Improve handling of locals and add tests

- adds alloc_local and free_local
- slightly better wasmgen tests
- caller of gen_safe_write32 is responsible for allocating
  and freeing locals for address and value
- updates expect-tests
This commit is contained in:
Awal Garg 2018-07-24 16:38:01 +05:30 committed by Fabian
parent 1a177b6a5d
commit 7a14a010a8
16 changed files with 345 additions and 270 deletions

View file

@ -1,10 +1,9 @@
use global_pointers;
use jit::JitContext;
use jit::{GEN_LOCAL_SCRATCH0, GEN_LOCAL_SCRATCH1, GEN_LOCAL_SCRATCH2};
use modrm;
use regs;
use tlb::{TLB_GLOBAL, TLB_NO_USER, TLB_READONLY, TLB_VALID};
use wasmgen::module_init::WasmBuilder;
use wasmgen::module_init::{WasmBuilder, WasmLocal};
use wasmgen::{module_init, wasm_util};
pub fn gen_set_previous_eip_offset_from_eip(builder: &mut WasmBuilder, n: u32) {
@ -193,8 +192,8 @@ pub fn gen_safe_read32(ctx: &mut JitContext) {
//let instruction_body = &mut ctx.builder.instruction_body;
//let cpu = &mut ctx.cpu;
let address_local = GEN_LOCAL_SCRATCH0;
wasm_util::tee_local(&mut builder.instruction_body, address_local);
let address_local = builder.alloc_local();
wasm_util::tee_local(&mut builder.instruction_body, &address_local);
// Pseudo: base_on_stack = (uint32_t)address >> 12;
wasm_util::push_i32(&mut builder.instruction_body, 12);
@ -205,12 +204,12 @@ pub fn gen_safe_read32(ctx: &mut JitContext) {
wasm_util::shl_i32(&mut builder.instruction_body);
// Pseudo: entry = tlb_data[base_on_stack];
let entry_local = GEN_LOCAL_SCRATCH1;
let entry_local = builder.alloc_local();
wasm_util::load_aligned_i32_from_stack(
&mut builder.instruction_body,
global_pointers::TLB_DATA,
);
wasm_util::tee_local(&mut builder.instruction_body, entry_local);
wasm_util::tee_local(&mut builder.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));
@ -224,7 +223,7 @@ pub fn gen_safe_read32(ctx: &mut JitContext) {
wasm_util::push_i32(&mut builder.instruction_body, TLB_VALID as i32);
wasm_util::eq_i32(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, address_local);
wasm_util::get_local(&mut builder.instruction_body, &address_local);
wasm_util::push_i32(&mut builder.instruction_body, 0xFFF);
wasm_util::and_i32(&mut builder.instruction_body);
wasm_util::push_i32(&mut builder.instruction_body, 0x1000 - 4);
@ -235,10 +234,10 @@ pub fn gen_safe_read32(ctx: &mut JitContext) {
// Pseudo:
// if(can_use_fast_path) leave_on_stack(mem8[entry & ~0xFFF ^ address]);
wasm_util::if_i32(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, entry_local);
wasm_util::get_local(&mut builder.instruction_body, &entry_local);
wasm_util::push_i32(&mut builder.instruction_body, !0xFFF);
wasm_util::and_i32(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, address_local);
wasm_util::get_local(&mut builder.instruction_body, &address_local);
wasm_util::xor_i32(&mut builder.instruction_body);
wasm_util::load_unaligned_i32_from_stack(
@ -249,31 +248,22 @@ pub fn gen_safe_read32(ctx: &mut JitContext) {
// Pseudo:
// else { leave_on_stack(safe_read32s_slow(address)); }
wasm_util::else_(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, address_local);
wasm_util::get_local(&mut builder.instruction_body, &address_local);
gen_call_fn1_ret(builder, "safe_read32s_slow");
wasm_util::block_end(&mut builder.instruction_body);
builder.free_local(address_local);
builder.free_local(entry_local);
}
pub fn gen_safe_write32(ctx: &mut JitContext, local_for_address: u32, local_for_value: u32) {
pub fn gen_safe_write32(ctx: &mut JitContext, address_local: &WasmLocal, value_local: &WasmLocal) {
// Generates safe_write32' fast-path inline, bailing to safe_write32_slow if necessary.
// local_for_{address,value} are the numbers of the local variables which contain the virtual
// address and value for safe_write32
// Usage:
// set_local(0, value);
// set_local(1, v_addr);
// gen_safe_write32(0, 1);
// Since this function clobbers other variables, we confirm that the caller uses the local
// variables we expect them to
dbg_assert!(local_for_address == GEN_LOCAL_SCRATCH0);
dbg_assert!(local_for_value == GEN_LOCAL_SCRATCH1);
let builder = &mut ctx.builder;
//let instruction_body = &mut ctx.builder.instruction_body;
//let cpu = &mut ctx.cpu;
wasm_util::get_local(&mut builder.instruction_body, local_for_address);
wasm_util::get_local(&mut builder.instruction_body, &address_local);
// Pseudo: base_on_stack = (uint32_t)address >> 12;
wasm_util::push_i32(&mut builder.instruction_body, 12);
@ -283,65 +273,65 @@ pub fn gen_safe_write32(ctx: &mut JitContext, local_for_address: u32, local_for_
wasm_util::push_i32(&mut builder.instruction_body, 2);
wasm_util::shl_i32(&mut builder.instruction_body);
// entry_local is only used in the following block, so the scratch variable can be reused later
{
// Pseudo: entry = tlb_data[base_on_stack];
let entry_local = GEN_LOCAL_SCRATCH2;
wasm_util::load_aligned_i32_from_stack(
&mut builder.instruction_body,
global_pointers::TLB_DATA,
);
wasm_util::tee_local(&mut builder.instruction_body, entry_local);
// Pseudo: entry = tlb_data[base_on_stack];
let entry_local = builder.alloc_local();
wasm_util::load_aligned_i32_from_stack(
&mut builder.instruction_body,
global_pointers::TLB_DATA,
);
wasm_util::tee_local(&mut builder.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));
wasm_util::push_i32(
&mut builder.instruction_body,
(0xFFF & !TLB_GLOBAL & !(if ctx.cpu.cpl3() { 0 } else { TLB_NO_USER })) as i32,
);
wasm_util::and_i32(&mut builder.instruction_body);
// Pseudo: bool can_use_fast_path = (entry & 0xFFF & ~TLB_GLOBAL & ~(cpl == 3 ? 0 : TLB_NO_USER) == TLB_VALID &&
// (address & 0xFFF) <= (0x1000 - 4));
wasm_util::push_i32(
&mut builder.instruction_body,
(0xFFF & !TLB_GLOBAL & !(if ctx.cpu.cpl3() { 0 } else { TLB_NO_USER })) as i32,
);
wasm_util::and_i32(&mut builder.instruction_body);
wasm_util::push_i32(&mut builder.instruction_body, TLB_VALID as i32);
wasm_util::eq_i32(&mut builder.instruction_body);
wasm_util::push_i32(&mut builder.instruction_body, TLB_VALID as i32);
wasm_util::eq_i32(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, local_for_address);
wasm_util::push_i32(&mut builder.instruction_body, 0xFFF);
wasm_util::and_i32(&mut builder.instruction_body);
wasm_util::push_i32(&mut builder.instruction_body, 0x1000 - 4);
wasm_util::le_i32(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, &address_local);
wasm_util::push_i32(&mut builder.instruction_body, 0xFFF);
wasm_util::and_i32(&mut builder.instruction_body);
wasm_util::push_i32(&mut builder.instruction_body, 0x1000 - 4);
wasm_util::le_i32(&mut builder.instruction_body);
wasm_util::and_i32(&mut builder.instruction_body);
wasm_util::and_i32(&mut builder.instruction_body);
// Pseudo:
// if(can_use_fast_path)
// {
// phys_addr = entry & ~0xFFF ^ address;
wasm_util::if_void(&mut builder.instruction_body);
// Pseudo:
// if(can_use_fast_path)
// {
// phys_addr = entry & ~0xFFF ^ address;
wasm_util::if_void(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, entry_local);
wasm_util::push_i32(&mut builder.instruction_body, !0xFFF);
wasm_util::and_i32(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, local_for_address);
wasm_util::xor_i32(&mut builder.instruction_body);
}
wasm_util::get_local(&mut builder.instruction_body, &entry_local);
wasm_util::push_i32(&mut builder.instruction_body, !0xFFF);
wasm_util::and_i32(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, &address_local);
wasm_util::xor_i32(&mut builder.instruction_body);
// entry_local isn't needed anymore, so we overwrite it
let phys_addr_local = GEN_LOCAL_SCRATCH2;
builder.free_local(entry_local);
let phys_addr_local = builder.alloc_local();
// Pseudo:
// /* continued within can_use_fast_path branch */
// mem8[phys_addr] = value;
wasm_util::tee_local(&mut builder.instruction_body, phys_addr_local);
wasm_util::get_local(&mut builder.instruction_body, local_for_value);
wasm_util::tee_local(&mut builder.instruction_body, &phys_addr_local);
wasm_util::get_local(&mut builder.instruction_body, &value_local);
wasm_util::store_unaligned_i32(&mut builder.instruction_body, global_pointers::MEMORY);
// Pseudo:
// else { safe_read32_slow(address, value); }
wasm_util::else_(&mut builder.instruction_body);
wasm_util::get_local(&mut builder.instruction_body, local_for_address);
wasm_util::get_local(&mut builder.instruction_body, local_for_value);
wasm_util::get_local(&mut builder.instruction_body, &address_local);
wasm_util::get_local(&mut builder.instruction_body, &value_local);
gen_call_fn2(builder, "safe_write32_slow");
wasm_util::block_end(&mut builder.instruction_body);
builder.free_local(phys_addr_local);
}
pub fn gen_fn1_reg16(ctx: &mut JitContext, name: &str, r: u32) {
@ -384,69 +374,77 @@ pub fn gen_add_prefix_bits(ctx: &mut JitContext, mask: u32) {
pub fn gen_jmp_rel16(ctx: &mut JitContext, rel16: u16) {
let cs_offset_addr = global_pointers::get_seg_offset(regs::CS);
let local = ctx.builder.alloc_local();
// generate:
// *instruction_pointer = cs_offset + ((*instruction_pointer - cs_offset + rel16) & 0xFFFF);
{
let instruction_body = &mut ctx.builder.instruction_body;
let instruction_body = &mut ctx.builder.instruction_body;
wasm_util::load_aligned_i32(instruction_body, cs_offset_addr);
wasm_util::set_local(instruction_body, &local);
wasm_util::load_aligned_i32(instruction_body, cs_offset_addr);
wasm_util::set_local(instruction_body, GEN_LOCAL_SCRATCH0);
wasm_util::push_i32(
instruction_body,
global_pointers::INSTRUCTION_POINTER as i32,
);
wasm_util::push_i32(
instruction_body,
global_pointers::INSTRUCTION_POINTER as i32,
);
wasm_util::load_aligned_i32(instruction_body, global_pointers::INSTRUCTION_POINTER);
wasm_util::get_local(instruction_body, &local);
wasm_util::sub_i32(instruction_body);
wasm_util::load_aligned_i32(instruction_body, global_pointers::INSTRUCTION_POINTER);
wasm_util::get_local(instruction_body, GEN_LOCAL_SCRATCH0);
wasm_util::sub_i32(instruction_body);
wasm_util::push_i32(instruction_body, rel16 as i32);
wasm_util::add_i32(instruction_body);
wasm_util::push_i32(instruction_body, rel16 as i32);
wasm_util::add_i32(instruction_body);
wasm_util::push_i32(instruction_body, 0xFFFF);
wasm_util::and_i32(instruction_body);
wasm_util::push_i32(instruction_body, 0xFFFF);
wasm_util::and_i32(instruction_body);
wasm_util::get_local(instruction_body, &local);
wasm_util::add_i32(instruction_body);
wasm_util::get_local(instruction_body, GEN_LOCAL_SCRATCH0);
wasm_util::add_i32(instruction_body);
wasm_util::store_aligned_i32(instruction_body);
wasm_util::store_aligned_i32(instruction_body);
}
ctx.builder.free_local(local);
}
pub fn gen_pop16_ss16(ctx: &mut JitContext) {
let safe_read16_idx = ctx
.builder
.get_fn_idx("safe_read16", module_init::FN1_RET_TYPE_INDEX);
let instruction_body = &mut ctx.builder.instruction_body;
let sp_local = ctx.builder.alloc_local();
{
let instruction_body = &mut ctx.builder.instruction_body;
let sp_local = GEN_LOCAL_SCRATCH0;
// sp = segment_offsets[SS] + reg16[SP] (or just reg16[SP] if has_flat_segmentation)
wasm_util::load_aligned_i32(
instruction_body,
global_pointers::get_reg16_offset(regs::SP),
);
wasm_util::tee_local(instruction_body, &sp_local);
// sp = segment_offsets[SS] + reg16[SP] (or just reg16[SP] if has_flat_segmentation)
wasm_util::load_aligned_i32(
instruction_body,
global_pointers::get_reg16_offset(regs::SP),
);
wasm_util::tee_local(instruction_body, sp_local);
if !ctx.cpu.has_flat_segmentation() {
wasm_util::load_aligned_i32(
instruction_body,
global_pointers::get_seg_offset(regs::SS),
);
wasm_util::add_i32(instruction_body);
}
if !ctx.cpu.has_flat_segmentation() {
wasm_util::load_aligned_i32(instruction_body, global_pointers::get_seg_offset(regs::SS));
// result = safe_read16(sp)
// XXX: inline safe_read16
wasm_util::call_fn(instruction_body, safe_read16_idx);
// reg16[SP] += 2;
wasm_util::push_i32(
instruction_body,
global_pointers::get_reg16_offset(regs::SP) as i32,
);
wasm_util::get_local(instruction_body, &sp_local);
wasm_util::push_i32(instruction_body, 2);
wasm_util::add_i32(instruction_body);
wasm_util::store_aligned_i32(instruction_body);
}
// result = safe_read16(sp)
// XXX: inline safe_read16
wasm_util::call_fn(instruction_body, safe_read16_idx);
// reg16[SP] += 2;
wasm_util::push_i32(
instruction_body,
global_pointers::get_reg16_offset(regs::SP) as i32,
);
wasm_util::get_local(instruction_body, sp_local);
wasm_util::push_i32(instruction_body, 2);
wasm_util::add_i32(instruction_body);
wasm_util::store_aligned_i32(instruction_body);
ctx.builder.free_local(sp_local);
// return value is already on stack
}
@ -455,35 +453,39 @@ pub fn gen_pop16_ss32(ctx: &mut JitContext) {
let safe_read16_idx = ctx
.builder
.get_fn_idx("safe_read16", module_init::FN1_RET_TYPE_INDEX);
let instruction_body = &mut ctx.builder.instruction_body;
let esp_local = ctx.builder.alloc_local();
{
let instruction_body = &mut ctx.builder.instruction_body;
// esp = segment_offsets[SS] + reg32s[ESP] (or just reg32s[ESP] if has_flat_segmentation)
wasm_util::load_aligned_i32(
instruction_body,
global_pointers::get_reg32_offset(regs::ESP),
);
wasm_util::tee_local(instruction_body, &esp_local);
let esp_local = GEN_LOCAL_SCRATCH0;
if !ctx.cpu.has_flat_segmentation() {
wasm_util::load_aligned_i32(
instruction_body,
global_pointers::get_seg_offset(regs::SS),
);
wasm_util::add_i32(instruction_body);
}
// esp = segment_offsets[SS] + reg32s[ESP] (or just reg32s[ESP] if has_flat_segmentation)
wasm_util::load_aligned_i32(
instruction_body,
global_pointers::get_reg32_offset(regs::ESP),
);
wasm_util::tee_local(instruction_body, esp_local);
// result = safe_read16(esp)
// XXX: inline safe_read16
wasm_util::call_fn(instruction_body, safe_read16_idx);
if !ctx.cpu.has_flat_segmentation() {
wasm_util::load_aligned_i32(instruction_body, global_pointers::get_seg_offset(regs::SS));
// reg32s[ESP] += 2;
wasm_util::push_i32(
instruction_body,
global_pointers::get_reg32_offset(regs::ESP) as i32,
);
wasm_util::get_local(instruction_body, &esp_local);
wasm_util::push_i32(instruction_body, 2);
wasm_util::add_i32(instruction_body);
wasm_util::store_aligned_i32(instruction_body);
}
// result = safe_read16(esp)
// XXX: inline safe_read16
wasm_util::call_fn(instruction_body, safe_read16_idx);
// reg32s[ESP] += 2;
wasm_util::push_i32(
instruction_body,
global_pointers::get_reg32_offset(regs::ESP) as i32,
);
wasm_util::get_local(instruction_body, esp_local);
wasm_util::push_i32(instruction_body, 2);
wasm_util::add_i32(instruction_body);
wasm_util::store_aligned_i32(instruction_body);
ctx.builder.free_local(esp_local);
// return value is already on stack
}
@ -498,14 +500,14 @@ pub fn gen_pop16(ctx: &mut JitContext) {
}
pub fn gen_pop32s_ss16(ctx: &mut JitContext) {
let local_sp = GEN_LOCAL_SCRATCH2; // gen_safe_read32 uses local0 and local1
let local_sp = ctx.builder.alloc_local();
// sp = reg16[SP]
wasm_util::load_aligned_i32(
&mut ctx.builder.instruction_body,
global_pointers::get_reg16_offset(regs::SP),
);
wasm_util::tee_local(&mut ctx.builder.instruction_body, local_sp);
wasm_util::tee_local(&mut ctx.builder.instruction_body, &local_sp);
// result = safe_read32s(segment_offsets[SS] + sp) (or just sp if has_flat_segmentation)
if !ctx.cpu.has_flat_segmentation() {
@ -523,23 +525,25 @@ pub fn gen_pop32s_ss16(ctx: &mut JitContext) {
&mut ctx.builder.instruction_body,
global_pointers::get_reg16_offset(regs::SP) as i32,
);
wasm_util::get_local(&mut ctx.builder.instruction_body, local_sp);
wasm_util::get_local(&mut ctx.builder.instruction_body, &local_sp);
wasm_util::push_i32(&mut ctx.builder.instruction_body, 4);
wasm_util::add_i32(&mut ctx.builder.instruction_body);
wasm_util::store_aligned_i32(&mut ctx.builder.instruction_body);
ctx.builder.free_local(local_sp);
// return value is already on stack
}
pub fn gen_pop32s_ss32(ctx: &mut JitContext) {
let local_esp = GEN_LOCAL_SCRATCH2; // gen_safe_read32 uses local0 and local1
let local_esp = ctx.builder.alloc_local();
// esp = reg32s[ESP]
wasm_util::load_aligned_i32(
&mut ctx.builder.instruction_body,
global_pointers::get_reg32_offset(regs::ESP),
);
wasm_util::tee_local(&mut ctx.builder.instruction_body, local_esp);
wasm_util::tee_local(&mut ctx.builder.instruction_body, &local_esp);
// result = safe_read32s(segment_offsets[SS] + esp) (or just esp if has_flat_segmentation)
if !ctx.cpu.has_flat_segmentation() {
@ -556,11 +560,13 @@ pub fn gen_pop32s_ss32(ctx: &mut JitContext) {
&mut ctx.builder.instruction_body,
global_pointers::get_reg32_offset(regs::ESP) as i32,
);
wasm_util::get_local(&mut ctx.builder.instruction_body, local_esp);
wasm_util::get_local(&mut ctx.builder.instruction_body, &local_esp);
wasm_util::push_i32(&mut ctx.builder.instruction_body, 4);
wasm_util::add_i32(&mut ctx.builder.instruction_body);
wasm_util::store_aligned_i32(&mut ctx.builder.instruction_body);
ctx.builder.free_local(local_esp);
// return value is already on stack
}

View file

@ -380,16 +380,6 @@ pub struct JitContext<'a> {
pub builder: &'a mut WasmBuilder,
}
pub const GEN_LOCAL_ARG_INITIAL_STATE: u32 = 0;
pub const GEN_LOCAL_STATE: u32 = 1;
pub const GEN_LOCAL_ITERATION_COUNTER: u32 = 2;
// local scratch variables for use wherever required
pub const GEN_LOCAL_SCRATCH0: u32 = 3;
pub const GEN_LOCAL_SCRATCH1: u32 = 4;
pub const GEN_LOCAL_SCRATCH2: u32 = 5;
// Function arguments are not included in the local variable count
pub const GEN_NO_OF_LOCALS: u32 = 5;
pub const JIT_INSTR_BLOCK_BOUNDARY_FLAG: u32 = 1 << 0;
pub const JIT_INSTR_NONFAULTING_FLAG: u32 = 1 << 1;
@ -870,6 +860,14 @@ fn jit_generate_module(
let fn_get_seg_idx = builder.get_fn_idx("get_seg", module_init::FN1_RET_TYPE_INDEX);
dbg_assert!(fn_get_seg_idx == FN_GET_SEG_IDX);
let gen_local_state = builder.alloc_local();
let gen_local_iteration_counter = if JIT_ALWAYS_USE_LOOP_SAFETY || requires_loop_limit {
Some(builder.alloc_local())
}
else {
None
};
let basic_block_indices: HashMap<u32, u32> = basic_blocks
.iter()
.enumerate()
@ -877,16 +875,22 @@ fn jit_generate_module(
.collect();
// set state local variable to the initial state passed as the first argument
wasm_util::get_local(&mut builder.instruction_body, GEN_LOCAL_ARG_INITIAL_STATE);
wasm_util::set_local(&mut builder.instruction_body, GEN_LOCAL_STATE);
wasm_util::get_local(
&mut builder.instruction_body,
&builder.arg_local_initial_state,
);
wasm_util::set_local(&mut builder.instruction_body, &gen_local_state);
// initialise max_iterations
if JIT_ALWAYS_USE_LOOP_SAFETY || requires_loop_limit {
let gen_local_iteration_counter = gen_local_iteration_counter
.as_ref()
.expect("iteration counter");
wasm_util::push_i32(
&mut builder.instruction_body,
JIT_MAX_ITERATIONS_PER_FUNCTION as i32,
);
wasm_util::set_local(&mut builder.instruction_body, GEN_LOCAL_ITERATION_COUNTER);
wasm_util::set_local(&mut builder.instruction_body, gen_local_iteration_counter);
}
// main state machine loop
@ -894,15 +898,18 @@ fn jit_generate_module(
if JIT_ALWAYS_USE_LOOP_SAFETY || requires_loop_limit {
profiler::stat_increment(stat::S_COMPILE_WITH_LOOP_SAFETY);
let gen_local_iteration_counter = gen_local_iteration_counter
.as_ref()
.expect("iteration counter");
// decrement max_iterations
wasm_util::get_local(&mut builder.instruction_body, GEN_LOCAL_ITERATION_COUNTER);
wasm_util::get_local(&mut builder.instruction_body, gen_local_iteration_counter);
wasm_util::push_i32(&mut builder.instruction_body, -1);
wasm_util::add_i32(&mut builder.instruction_body);
wasm_util::set_local(&mut builder.instruction_body, GEN_LOCAL_ITERATION_COUNTER);
wasm_util::set_local(&mut builder.instruction_body, gen_local_iteration_counter);
// if max_iterations == 0: return
wasm_util::get_local(&mut builder.instruction_body, GEN_LOCAL_ITERATION_COUNTER);
wasm_util::get_local(&mut builder.instruction_body, gen_local_iteration_counter);
wasm_util::eqz_i32(&mut builder.instruction_body);
wasm_util::if_void(&mut builder.instruction_body);
wasm_util::return_(&mut builder.instruction_body);
@ -917,7 +924,7 @@ fn jit_generate_module(
wasm_util::block_void(&mut builder.instruction_body);
}
wasm_util::get_local(&mut builder.instruction_body, GEN_LOCAL_STATE);
wasm_util::get_local(&mut builder.instruction_body, &gen_local_state);
wasm_util::brtable_and_cases(&mut builder.instruction_body, basic_blocks.len() as u32);
for (i, block) in basic_blocks.iter().enumerate() {
@ -953,7 +960,7 @@ fn jit_generate_module(
// set state variable to next basic block
wasm_util::push_i32(&mut builder.instruction_body, next_bb_index as i32);
wasm_util::set_local(&mut builder.instruction_body, GEN_LOCAL_STATE);
wasm_util::set_local(&mut builder.instruction_body, &gen_local_state);
wasm_util::br(
&mut builder.instruction_body,
@ -1003,7 +1010,7 @@ fn jit_generate_module(
&mut builder.instruction_body,
next_basic_block_branch_taken_index as i32,
);
wasm_util::set_local(&mut builder.instruction_body, GEN_LOCAL_STATE);
wasm_util::set_local(&mut builder.instruction_body, &gen_local_state);
}
else {
// Jump to different page
@ -1022,7 +1029,7 @@ fn jit_generate_module(
&mut builder.instruction_body,
next_basic_block_index as i32,
);
wasm_util::set_local(&mut builder.instruction_body, GEN_LOCAL_STATE);
wasm_util::set_local(&mut builder.instruction_body, &gen_local_state);
}
wasm_util::block_end(&mut builder.instruction_body);
@ -1041,7 +1048,7 @@ fn jit_generate_module(
wasm_util::block_end(&mut builder.instruction_body); // loop
builder.commit_instruction_body_to_cs();
builder.finish(GEN_NO_OF_LOCALS as u8);
builder.finish();
}
fn jit_generate_basic_block(

View file

@ -4,7 +4,6 @@ use codegen;
use cpu_context::CpuContext;
use global_pointers;
use jit::JitContext;
use jit::{GEN_LOCAL_SCRATCH0, GEN_LOCAL_SCRATCH1};
use modrm;
use prefix::SEG_PREFIX_ZERO;
use prefix::{PREFIX_66, PREFIX_67, PREFIX_F2, PREFIX_F3};
@ -258,20 +257,22 @@ pub fn instr16_89_reg_jit(ctx: &mut JitContext, r1: u32, r2: u32) {
}
pub fn instr32_89_mem_jit(ctx: &mut JitContext, modrm_byte: u8, r: u32) {
// Pseudo: safe_write32(modrm_resolve(modrm_byte), reg32s[r]);
let address_local = GEN_LOCAL_SCRATCH0;
let value_local = GEN_LOCAL_SCRATCH1;
let address_local = ctx.builder.alloc_local();
let value_local = ctx.builder.alloc_local();
codegen::gen_modrm_resolve(ctx, modrm_byte);
wasm_util::set_local(&mut ctx.builder.instruction_body, address_local);
wasm_util::set_local(&mut ctx.builder.instruction_body, &address_local);
wasm_util::push_i32(
&mut ctx.builder.instruction_body,
global_pointers::get_reg32_offset(r) as i32,
);
wasm_util::load_aligned_i32_from_stack(&mut ctx.builder.instruction_body, 0);
wasm_util::set_local(&mut ctx.builder.instruction_body, value_local);
wasm_util::set_local(&mut ctx.builder.instruction_body, &value_local);
codegen::gen_safe_write32(ctx, address_local, value_local);
codegen::gen_safe_write32(ctx, &address_local, &value_local);
ctx.builder.free_local(address_local);
ctx.builder.free_local(value_local);
}
pub fn instr32_89_reg_jit(ctx: &mut JitContext, r1: u32, r2: u32) {
codegen::gen_set_reg32_r(ctx, r1, r2);
@ -497,10 +498,14 @@ pub fn instr32_C7_0_reg_jit(ctx: &mut JitContext, r: u32, imm: u32) {
}
pub fn instr32_C7_0_mem_jit(ctx: &mut JitContext, modrm_byte: u8) {
let address_local = ctx.builder.alloc_local();
let value_local = ctx.builder.alloc_local();
codegen::gen_modrm_resolve(ctx, modrm_byte);
wasm_util::set_local(&mut ctx.builder.instruction_body, GEN_LOCAL_SCRATCH0);
wasm_util::set_local(&mut ctx.builder.instruction_body, &address_local);
let imm = ctx.cpu.read_imm32();
wasm_util::push_i32(&mut ctx.builder.instruction_body, imm as i32);
wasm_util::set_local(&mut ctx.builder.instruction_body, GEN_LOCAL_SCRATCH1);
codegen::gen_safe_write32(ctx, GEN_LOCAL_SCRATCH0, GEN_LOCAL_SCRATCH1);
wasm_util::set_local(&mut ctx.builder.instruction_body, &value_local);
codegen::gen_safe_write32(ctx, &address_local, &value_local);
ctx.builder.free_local(address_local);
ctx.builder.free_local(value_local);
}

View file

@ -1,3 +1,5 @@
#![feature(const_fn)]
#[cfg(test)]
#[macro_use]
extern crate quickcheck;

View file

@ -33,6 +33,15 @@ pub struct WasmBuilder {
import_count: u16, // same as above
initial_static_size: usize, // size of module after initialization, rest is drained on reset
free_locals: Vec<WasmLocal>,
local_count: u8,
pub arg_local_initial_state: WasmLocal,
}
pub struct WasmLocal(u8);
impl WasmLocal {
pub fn idx(&self) -> u8 { self.0 }
}
impl WasmBuilder {
@ -50,6 +59,10 @@ impl WasmBuilder {
import_count: 0,
initial_static_size: 0,
free_locals: Vec::with_capacity(8),
local_count: 1, // for the argument
arg_local_initial_state: WasmLocal(0),
}
}
@ -75,11 +88,13 @@ impl WasmBuilder {
self.set_import_count(0);
self.code_section.clear();
self.instruction_body.clear();
self.free_locals.clear();
self.local_count = 0;
}
pub fn finish(&mut self, no_of_locals_i32: u8) -> usize {
pub fn finish(&mut self) -> usize {
self.write_memory_import();
self.write_function_section(1);
self.write_function_section();
self.write_export_section();
// write code section preamble
@ -101,8 +116,8 @@ impl WasmBuilder {
self.output.push(0);
self.output.push(1); // count of local blocks
dbg_assert!(no_of_locals_i32 < 128);
self.output.push(no_of_locals_i32);
dbg_assert!(self.local_count < 128);
self.output.push(self.local_count);
self.output.push(op::TYPE_I32);
self.output.append(&mut self.code_section);
@ -266,13 +281,11 @@ impl WasmBuilder {
self.import_count - 1
}
pub fn write_function_section(&mut self, count: u8) {
pub fn write_function_section(&mut self) {
self.output.push(op::SC_FUNCTION);
self.output.push(1 + count); // length of this section
self.output.push(count); // count of signature indices
for _ in 0..count {
self.output.push(FN1_TYPE_INDEX);
}
self.output.push(2); // length of this section
self.output.push(1); // count of signature indices
self.output.push(FN1_TYPE_INDEX);
}
pub fn write_export_section(&mut self) {
@ -310,6 +323,22 @@ impl WasmBuilder {
pub fn commit_instruction_body_to_cs(&mut self) {
self.code_section.append(&mut self.instruction_body);
}
pub fn alloc_local(&mut self) -> WasmLocal {
match self.free_locals.pop() {
Some(local) => local,
None => {
let new_idx = self.local_count;
self.local_count += 1;
WasmLocal(new_idx)
},
}
}
pub fn free_local(&mut self, local: WasmLocal) {
dbg_assert!(local.0 < self.local_count);
self.free_locals.push(local)
}
}
#[cfg(test)]
@ -340,7 +369,9 @@ mod tests {
let bar_index = m.get_fn_idx("bar", FN0_TYPE_INDEX);
call_fn(&mut m.code_section, bar_index);
m.finish(2);
let _ = m.alloc_local(); // for ensuring that reset clears previous locals
m.finish();
m.reset();
push_i32(&mut m.code_section, 2);
@ -350,9 +381,32 @@ mod tests {
foo_index = m.get_fn_idx("foo", FN1_TYPE_INDEX);
call_fn(&mut m.instruction_body, foo_index);
push_i32(&mut m.code_section, 10);
let local1 = m.alloc_local();
tee_local(&mut m.code_section, &local1); // local1 = 10
push_i32(&mut m.code_section, 20);
add_i32(&mut m.code_section);
let local2 = m.alloc_local();
tee_local(&mut m.code_section, &local2); // local2 = 30
m.free_local(local1);
let local3 = m.alloc_local();
assert_eq!(local3.idx(), 0);
m.free_local(local2);
m.free_local(local3);
push_i32(&mut m.code_section, 30);
ne_i32(&mut m.code_section);
if_void(&mut m.code_section);
unreachable(&mut m.code_section);
block_end(&mut m.code_section);
m.commit_instruction_body_to_cs();
m.finish(0);
m.finish();
let op_ptr = m.get_op_ptr();
let op_len = m.get_op_len();

View file

@ -1,4 +1,5 @@
use leb::{write_fixed_leb16_at_idx, write_leb_i32, write_leb_u32};
use wasmgen::module_init::WasmLocal;
use wasmgen::wasm_opcodes as op;
pub fn push_i32(buf: &mut Vec<u8>, v: i32) {
@ -143,19 +144,19 @@ pub fn br(buf: &mut Vec<u8>, depth: u32) {
write_leb_u32(buf, depth);
}
pub fn get_local(buf: &mut Vec<u8>, idx: u32) {
pub fn get_local(buf: &mut Vec<u8>, local: &WasmLocal) {
buf.push(op::OP_GETLOCAL);
write_leb_u32(buf, idx);
buf.push(local.idx());
}
pub fn set_local(buf: &mut Vec<u8>, idx: u32) {
pub fn set_local(buf: &mut Vec<u8>, local: &WasmLocal) {
buf.push(op::OP_SETLOCAL);
write_leb_u32(buf, idx);
buf.push(local.idx());
}
pub fn tee_local(buf: &mut Vec<u8>, idx: u32) {
pub fn tee_local(buf: &mut Vec<u8>, local: &WasmLocal) {
buf.push(op::OP_TEELOCAL);
write_leb_u32(buf, idx);
buf.push(local.idx());
}
pub fn unreachable(buf: &mut Vec<u8>) { buf.push(op::OP_UNREACHABLE); }

View file

@ -13,8 +13,8 @@
(import "e" "safe_read32s_slow" (func $e.safe_read32s_slow (type $t5)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
@ -22,7 +22,7 @@
(block $B3
(block $B4
(br_table $B4 $B3 $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.load
@ -41,7 +41,7 @@
(i32.load
(i32.const 664))
(i32.const 1)))
(set_local $l0
(set_local $p0
(i32.const 2))
(br $L0))
(i32.store
@ -82,13 +82,13 @@
(i32.and
(i32.eq
(i32.and
(tee_local $l3
(tee_local $l2
(i32.load offset=4194304
(i32.shl
(i32.shr_u
(tee_local $l2
(tee_local $l1
(i32.add
(tee_local $l4
(tee_local $l0
(i32.load
(i32.const 20)))
(i32.load
@ -99,23 +99,23 @@
(i32.const 1))
(i32.le_s
(i32.and
(get_local $l2)
(get_local $l1)
(i32.const 4095))
(i32.const 4092)))
(then
(i32.load offset=8388608 align=1
(i32.xor
(i32.and
(get_local $l3)
(get_local $l2)
(i32.const -4096))
(get_local $l2))))
(get_local $l1))))
(else
(call $e.safe_read32s_slow
(get_local $l2))))
(get_local $l1))))
(i32.store
(i32.const 20)
(i32.add
(get_local $l4)
(get_local $l0)
(i32.const 4)))
(i32.add)
(i32.store)

View file

@ -13,26 +13,26 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32) (local $l1 i32)
(set_local $p0
(get_local $p0))
(set_local $l1
(set_local $l0
(i32.const 10000))
(loop $L0
(set_local $l1
(set_local $l0
(i32.add
(get_local $l1)
(get_local $l0)
(i32.const -1)))
(if $I1
(i32.eqz
(get_local $l1))
(get_local $l0))
(then
(return)))
(block $B2
(block $B3
(block $B4
(br_table $B4 $B3 $B2
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.add
@ -64,10 +64,10 @@
(i32.load
(i32.const 556))
(i32.const -6)))
(set_local $l0
(set_local $p0
(i32.const 0)))
(else
(set_local $l0
(set_local $p0
(i32.const 1))))
(br $L0))
(i32.store

View file

@ -14,8 +14,8 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
@ -23,7 +23,7 @@
(block $B3
(block $B4
(br_table $B4 $B3 $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.add
@ -54,10 +54,10 @@
(i32.load
(i32.const 556))
(i32.const 1)))
(set_local $l0
(set_local $p0
(i32.const 2)))
(else
(set_local $l0
(set_local $p0
(i32.const 1))))
(br $L0))
(call $e.instr32_41)
@ -73,7 +73,7 @@
(i32.load
(i32.const 664))
(i32.const 1)))
(set_local $l0
(set_local $p0
(i32.const 2))
(br $L0))
(i32.store

View file

@ -11,15 +11,15 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
(block $B2
(block $B3
(br_table $B3 $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.load

View file

@ -13,14 +13,14 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
(block $B2
(br_table $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.add

View file

@ -12,19 +12,19 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
(block $B2
(br_table $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.load
(i32.const 556)))
(set_local $l2
(set_local $l0
(i32.add
(i32.add
(i32.load
@ -32,47 +32,47 @@
(i32.const 32))
(call $e.get_seg
(i32.const 3))))
(set_local $l3
(set_local $l1
(i32.load
(i32.const 4)))
(if $I3
(i32.and
(i32.eq
(i32.and
(tee_local $l4
(tee_local $l2
(i32.load offset=4194304
(i32.shl
(i32.shr_u
(get_local $l2)
(get_local $l0)
(i32.const 12))
(i32.const 2))))
(i32.const 4075))
(i32.const 1))
(i32.le_s
(i32.and
(get_local $l2)
(get_local $l0)
(i32.const 4095))
(i32.const 4092)))
(then
(i32.store offset=8388608 align=1
(tee_local $l4
(tee_local $l2
(i32.xor
(i32.and
(get_local $l4)
(get_local $l2)
(i32.const -4096))
(get_local $l2)))
(get_local $l3)))
(get_local $l0)))
(get_local $l1)))
(else
(call $e.safe_write32_slow
(get_local $l2)
(get_local $l3))))
(get_local $l0)
(get_local $l1))))
(i32.store
(i32.const 560)
(i32.add
(i32.load
(i32.const 556))
(i32.const 6)))
(set_local $l2
(set_local $l1
(i32.add
(i32.add
(i32.load
@ -80,40 +80,40 @@
(i32.const 28))
(call $e.get_seg
(i32.const 3))))
(set_local $l3
(set_local $l0
(i32.load
(i32.const 8)))
(if $I4
(i32.and
(i32.eq
(i32.and
(tee_local $l4
(tee_local $l2
(i32.load offset=4194304
(i32.shl
(i32.shr_u
(get_local $l2)
(get_local $l1)
(i32.const 12))
(i32.const 2))))
(i32.const 4075))
(i32.const 1))
(i32.le_s
(i32.and
(get_local $l2)
(get_local $l1)
(i32.const 4095))
(i32.const 4092)))
(then
(i32.store offset=8388608 align=1
(tee_local $l4
(tee_local $l2
(i32.xor
(i32.and
(get_local $l4)
(get_local $l2)
(i32.const -4096))
(get_local $l2)))
(get_local $l3)))
(get_local $l1)))
(get_local $l0)))
(else
(call $e.safe_write32_slow
(get_local $l2)
(get_local $l3))))
(get_local $l1)
(get_local $l0))))
(i32.store
(i32.const 560)
(i32.add
@ -126,11 +126,11 @@
(i32.and
(i32.eq
(i32.and
(tee_local $l3
(tee_local $l1
(i32.load offset=4194304
(i32.shl
(i32.shr_u
(tee_local $l2
(tee_local $l0
(i32.add
(i32.add
(i32.load
@ -144,19 +144,19 @@
(i32.const 1))
(i32.le_s
(i32.and
(get_local $l2)
(get_local $l0)
(i32.const 4095))
(i32.const 4092)))
(then
(i32.load offset=8388608 align=1
(i32.xor
(i32.and
(get_local $l3)
(get_local $l1)
(i32.const -4096))
(get_local $l2))))
(get_local $l0))))
(else
(call $e.safe_read32s_slow
(get_local $l2)))))
(get_local $l0)))))
(i32.store
(i32.const 560)
(i32.add
@ -169,11 +169,11 @@
(i32.and
(i32.eq
(i32.and
(tee_local $l3
(tee_local $l0
(i32.load offset=4194304
(i32.shl
(i32.shr_u
(tee_local $l2
(tee_local $l1
(i32.add
(i32.add
(i32.load
@ -187,19 +187,19 @@
(i32.const 1))
(i32.le_s
(i32.and
(get_local $l2)
(get_local $l1)
(i32.const 4095))
(i32.const 4092)))
(then
(i32.load offset=8388608 align=1
(i32.xor
(i32.and
(get_local $l3)
(get_local $l0)
(i32.const -4096))
(get_local $l2))))
(get_local $l1))))
(else
(call $e.safe_read32s_slow
(get_local $l2)))))
(get_local $l1)))))
(i32.store
(i32.const 560)
(i32.add

View file

@ -14,14 +14,14 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
(block $B2
(br_table $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.add

View file

@ -11,14 +11,14 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
(block $B2
(br_table $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.load

View file

@ -11,14 +11,14 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
(block $B2
(br_table $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.load

View file

@ -13,8 +13,8 @@
(import "e" "instr_F4" (func $e.instr_F4 (type $t0)))
(import "e" "m" (memory $e.m 256))
(func $f (export "f") (type $t1) (param $p0 i32)
(local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32)
(set_local $l0
(local $l0 i32)
(set_local $p0
(get_local $p0))
(loop $L0
(block $B1
@ -22,7 +22,7 @@
(block $B3
(block $B4
(br_table $B4 $B3 $B2 $B1
(get_local $l0)))
(get_local $p0)))
(i32.store
(i32.const 560)
(i32.add
@ -53,10 +53,10 @@
(i32.load
(i32.const 556))
(i32.const 3)))
(set_local $l0
(set_local $p0
(i32.const 2)))
(else
(set_local $l0
(set_local $p0
(i32.const 1))))
(br $L0))
(i32.store
@ -84,7 +84,7 @@
(i32.load
(i32.const 664))
(i32.const 2)))
(set_local $l0
(set_local $p0
(i32.const 0))
(br $L0))
(i32.store