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:
parent
1a177b6a5d
commit
7a14a010a8
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(const_fn)]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate quickcheck;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue