Add more store instructions
This commit is contained in:
228
src/backend.rs
228
src/backend.rs
@@ -47,7 +47,7 @@ impl From<SignlessType> for Option<GPRType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GPR {
|
impl GPR {
|
||||||
fn type_(&self) -> GPRType {
|
fn type_(self) -> GPRType {
|
||||||
match self {
|
match self {
|
||||||
GPR::Rq(_) => GPRType::Rq,
|
GPR::Rq(_) => GPRType::Rq,
|
||||||
GPR::Rx(_) => GPRType::Rx,
|
GPR::Rx(_) => GPRType::Rx,
|
||||||
@@ -73,8 +73,8 @@ pub fn arg_locs(types: impl IntoIterator<Item = SignlessType>) -> Vec<CCLoc> {
|
|||||||
let types = types.into_iter();
|
let types = types.into_iter();
|
||||||
let mut out = Vec::with_capacity(types.size_hint().0);
|
let mut out = Vec::with_capacity(types.size_hint().0);
|
||||||
// TODO: VmCtx is in the first register
|
// TODO: VmCtx is in the first register
|
||||||
let mut int_gpr_iter = INTEGER_ARGS_IN_GPRS.into_iter();
|
let mut int_gpr_iter = INTEGER_ARGS_IN_GPRS.iter();
|
||||||
let mut float_gpr_iter = FLOAT_ARGS_IN_GPRS.into_iter();
|
let mut float_gpr_iter = FLOAT_ARGS_IN_GPRS.iter();
|
||||||
let mut stack_idx = 0;
|
let mut stack_idx = 0;
|
||||||
|
|
||||||
for ty in types {
|
for ty in types {
|
||||||
@@ -102,8 +102,8 @@ pub fn ret_locs(types: impl IntoIterator<Item = SignlessType>) -> Vec<CCLoc> {
|
|||||||
let types = types.into_iter();
|
let types = types.into_iter();
|
||||||
let mut out = Vec::with_capacity(types.size_hint().0);
|
let mut out = Vec::with_capacity(types.size_hint().0);
|
||||||
// TODO: VmCtx is in the first register
|
// TODO: VmCtx is in the first register
|
||||||
let mut int_gpr_iter = INTEGER_RETURN_GPRS.into_iter();
|
let mut int_gpr_iter = INTEGER_RETURN_GPRS.iter();
|
||||||
let mut float_gpr_iter = FLOAT_RETURN_GPRS.into_iter();
|
let mut float_gpr_iter = FLOAT_RETURN_GPRS.iter();
|
||||||
|
|
||||||
for ty in types {
|
for ty in types {
|
||||||
match ty {
|
match ty {
|
||||||
@@ -1224,40 +1224,43 @@ macro_rules! binop {
|
|||||||
};
|
};
|
||||||
($name:ident, $instr:ident, $const_fallback:expr, $reg_ty:ident, $reg_fn:ident, $ty:expr, $imm_fn:ident, $direct_imm:expr, $map_op:expr) => {
|
($name:ident, $instr:ident, $const_fallback:expr, $reg_ty:ident, $reg_fn:ident, $ty:expr, $imm_fn:ident, $direct_imm:expr, $map_op:expr) => {
|
||||||
pub fn $name(&mut self) {
|
pub fn $name(&mut self) {
|
||||||
let op0 = self.pop();
|
let right = self.pop();
|
||||||
let op1 = self.pop();
|
let left = self.pop();
|
||||||
|
|
||||||
if let Some(i1) = op1.$imm_fn() {
|
if let Some(i1) = left.$imm_fn() {
|
||||||
if let Some(i0) = op0.$imm_fn() {
|
if let Some(i0) = right.$imm_fn() {
|
||||||
self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0).into()));
|
self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0).into()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (op1, op0) = $map_op(op1, op0);
|
let (left, mut right) = $map_op(left, right);
|
||||||
let op1 = self.into_temp_reg($ty, op1);
|
let left = self.into_temp_reg($ty, left);
|
||||||
|
|
||||||
match op0 {
|
match right {
|
||||||
ValueLocation::Reg(reg) => {
|
ValueLocation::Reg(_) => {
|
||||||
|
// This handles the case where we (for example) have a float in an `Rq` reg
|
||||||
|
let right_reg = self.into_reg($ty, right);
|
||||||
|
right = ValueLocation::Reg(right_reg);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr $reg_ty(op1.$reg_fn().unwrap()), $reg_ty(reg.$reg_fn().unwrap())
|
; $instr $reg_ty(left.$reg_fn().unwrap()), $reg_ty(right_reg.$reg_fn().unwrap())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let offset = self.adjusted_offset(offset);
|
let offset = self.adjusted_offset(offset);
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr $reg_ty(op1.$reg_fn().unwrap()), [rsp + offset]
|
; $instr $reg_ty(left.$reg_fn().unwrap()), [rsp + offset]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
if let Some(i) = i.as_int().and_then(|i| i.try_into()) {
|
if let Some(i) = i.as_int().and_then(|i| i.try_into()) {
|
||||||
$direct_imm(self, op1, i);
|
$direct_imm(self, left, i);
|
||||||
} else {
|
} else {
|
||||||
let scratch = self.block_state.regs.take($ty);
|
let scratch = self.block_state.regs.take($ty);
|
||||||
self.immediate_to_reg(scratch, i);
|
self.immediate_to_reg(scratch, i);
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr $reg_ty(op1.$reg_fn().unwrap()), $reg_ty(scratch.$reg_fn().unwrap())
|
; $instr $reg_ty(left.$reg_fn().unwrap()), $reg_ty(scratch.$reg_fn().unwrap())
|
||||||
);
|
);
|
||||||
|
|
||||||
self.block_state.regs.release(scratch);
|
self.block_state.regs.release(scratch);
|
||||||
@@ -1265,15 +1268,15 @@ macro_rules! binop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.free_value(op0);
|
self.free_value(right);
|
||||||
self.push(ValueLocation::Reg(op1));
|
self.push(ValueLocation::Reg(left));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! load {
|
macro_rules! load {
|
||||||
($name:ident, $reg_ty:ident, $instruction_name:expr, $out_ty:expr) => {
|
(@inner $name:ident, $reg_ty:ident, $emit_fn:expr) => {
|
||||||
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
|
pub fn $name(&mut self, ty: impl Into<GPRType>, offset: u32) -> Result<(), Error> {
|
||||||
fn load_to_reg<_M: ModuleContext>(
|
fn load_to_reg<_M: ModuleContext>(
|
||||||
ctx: &mut Context<_M>,
|
ctx: &mut Context<_M>,
|
||||||
dst: GPR,
|
dst: GPR,
|
||||||
@@ -1284,18 +1287,7 @@ macro_rules! load {
|
|||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
|
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
|
||||||
);
|
);
|
||||||
match runtime_offset {
|
$emit_fn(ctx, dst, mem_ptr_reg, runtime_offset, offset);
|
||||||
Ok(imm) => {
|
|
||||||
dynasm!(ctx.asm
|
|
||||||
; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(offset_reg) => {
|
|
||||||
dynasm!(ctx.asm
|
|
||||||
; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.block_state.regs.release(mem_ptr_reg);
|
ctx.block_state.regs.release(mem_ptr_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1303,7 +1295,7 @@ macro_rules! load {
|
|||||||
|
|
||||||
let base = self.pop();
|
let base = self.pop();
|
||||||
|
|
||||||
let temp = self.block_state.regs.take($out_ty);
|
let temp = self.block_state.regs.take(ty);
|
||||||
|
|
||||||
match base {
|
match base {
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
@@ -1320,11 +1312,61 @@ macro_rules! load {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
($name:ident, $reg_ty:ident, NONE) => {
|
||||||
|
load!(@inner
|
||||||
|
$name,
|
||||||
|
$reg_ty,
|
||||||
|
|ctx: &mut Context<_>, dst: GPR, mem_ptr_reg: GPR, runtime_offset: Result<i32, GPR>, offset: i32| {
|
||||||
|
match runtime_offset {
|
||||||
|
Ok(imm) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Err(offset_reg) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
($name:ident, $reg_ty:ident, $xmm_instr:ident) => {
|
||||||
|
load!(@inner
|
||||||
|
$name,
|
||||||
|
$reg_ty,
|
||||||
|
|ctx: &mut Context<_>, dst: GPR, mem_ptr_reg: GPR, runtime_offset: Result<i32, GPR>, offset: i32| {
|
||||||
|
match (dst, runtime_offset) {
|
||||||
|
(GPR::Rq(r), Ok(imm)) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov $reg_ty(r), [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(GPR::Rx(r), Ok(imm)) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; $xmm_instr Rx(r), [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(GPR::Rq(r), Err(offset_reg)) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov $reg_ty(r), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(GPR::Rx(r), Err(offset_reg)) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; $xmm_instr Rx(r), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! store {
|
macro_rules! store {
|
||||||
($name:ident, $reg_ty:ident, $size:ident, $instruction_name:expr, $in_ty:expr) => {
|
(@inner $name:ident, $int_reg_ty:ident, $match_offset:expr, $size:ident) => {
|
||||||
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
|
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
|
||||||
fn store_from_reg<_M: ModuleContext>(
|
fn store_from_reg<_M: ModuleContext>(
|
||||||
ctx: &mut Context<_M>,
|
ctx: &mut Context<_M>,
|
||||||
@@ -1332,23 +1374,14 @@ macro_rules! store {
|
|||||||
(offset, runtime_offset): (i32, Result<i32, GPR>)
|
(offset, runtime_offset): (i32, Result<i32, GPR>)
|
||||||
) {
|
) {
|
||||||
let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32;
|
let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32;
|
||||||
let mem_ptr_reg = ctx.block_state.regs.take(I64);
|
let mem_ptr_reg = ctx.block_state.regs.take(GPRType::Rq);
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
|
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
|
||||||
);
|
);
|
||||||
match runtime_offset {
|
let src = $match_offset(ctx, mem_ptr_reg, runtime_offset, offset, src);
|
||||||
Ok(imm) => {
|
|
||||||
dynasm!(ctx.asm
|
|
||||||
; mov [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm], $reg_ty(src.rq().unwrap())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(offset_reg) => {
|
|
||||||
dynasm!(ctx.asm
|
|
||||||
; mov [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], $reg_ty(src.rq().unwrap())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.block_state.regs.release(mem_ptr_reg);
|
ctx.block_state.regs.release(mem_ptr_reg);
|
||||||
|
|
||||||
|
ctx.block_state.regs.release(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(offset <= i32::max_value() as u32);
|
assert!(offset <= i32::max_value() as u32);
|
||||||
@@ -1356,9 +1389,7 @@ macro_rules! store {
|
|||||||
let src = self.pop();
|
let src = self.pop();
|
||||||
let base = self.pop();
|
let base = self.pop();
|
||||||
|
|
||||||
let src_reg = self.into_reg($in_ty, src);
|
let src_reg = self.into_reg(None, src);
|
||||||
// TODO
|
|
||||||
debug_assert!(stringify!($reg_ty) == "Rq" || stringify!($reg_ty) == "Rd");
|
|
||||||
|
|
||||||
match base {
|
match base {
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
@@ -1371,11 +1402,67 @@ macro_rules! store {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.block_state.regs.release(src_reg);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
($name:ident, $int_reg_ty:ident, NONE, $size:ident) => {
|
||||||
|
store!(@inner
|
||||||
|
$name,
|
||||||
|
$int_reg_ty,
|
||||||
|
|ctx: &mut Context<_>, mem_ptr_reg: GPR, runtime_offset: Result<i32, GPR>, offset: i32, src| {
|
||||||
|
let src_reg = ctx.into_temp_reg(GPRType::Rq, ValueLocation::Reg(src));
|
||||||
|
|
||||||
|
match runtime_offset {
|
||||||
|
Ok(imm) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm], $int_reg_ty(src_reg.rq().unwrap())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Err(offset_reg) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], $int_reg_ty(src_reg.rq().unwrap())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src_reg
|
||||||
|
},
|
||||||
|
$size
|
||||||
|
);
|
||||||
|
};
|
||||||
|
($name:ident, $int_reg_ty:ident, $xmm_instr:ident, $size:ident) => {
|
||||||
|
store!(@inner
|
||||||
|
$name,
|
||||||
|
$int_reg_ty,
|
||||||
|
|ctx: &mut Context<_>, mem_ptr_reg: GPR, runtime_offset: Result<i32, GPR>, offset: i32, src| {
|
||||||
|
match (runtime_offset, src) {
|
||||||
|
(Ok(imm), GPR::Rq(r)) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm], $int_reg_ty(r)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Ok(imm), GPR::Rx(r)) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; $xmm_instr [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm], Rx(r)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Err(offset_reg), GPR::Rq(r)) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], $int_reg_ty(r)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Err(offset_reg), GPR::Rx(r)) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; $xmm_instr [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], Rx(r)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src
|
||||||
|
},
|
||||||
|
$size
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TryInto<O> {
|
trait TryInto<O> {
|
||||||
@@ -1442,6 +1529,21 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
(self.block_state.depth.0 as i32 + offset) * WORD_SIZE as i32
|
(self.block_state.depth.0 as i32 + offset) * WORD_SIZE as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zero_reg(&mut self, reg: GPR) {
|
||||||
|
match reg {
|
||||||
|
GPR::Rq(r) => {
|
||||||
|
dynasm!(self.asm
|
||||||
|
; xor Rq(r), Rq(r)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
GPR::Rx(r) => {
|
||||||
|
dynasm!(self.asm
|
||||||
|
; pxor Rx(r), Rx(r)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmp_i32!(i32_eq, sete, sete, |a, b| a == b);
|
cmp_i32!(i32_eq, sete, sete, |a, b| a == b);
|
||||||
cmp_i32!(i32_neq, setne, setne, |a, b| a != b);
|
cmp_i32!(i32_neq, setne, setne, |a, b| a != b);
|
||||||
// `dynasm-rs` inexplicably doesn't support setb but `setnae` (and `setc`) are synonymous
|
// `dynasm-rs` inexplicably doesn't support setb but `setnae` (and `setc`) are synonymous
|
||||||
@@ -1888,10 +1990,15 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
self.block_state.depth = cc.stack_depth;
|
self.block_state.depth = cc.stack_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
load!(i32_load, Rd, "i32.load", I32);
|
load!(load8, Rb, NONE);
|
||||||
load!(i64_load, Rq, "i64.load", I64);
|
load!(load16, Rw, NONE);
|
||||||
store!(i32_store, Rd, DWORD, "i32.store", I32);
|
load!(load32, Rd, movd);
|
||||||
store!(i64_store, Rq, QWORD, "i64.store", I64);
|
load!(load64, Rq, movq);
|
||||||
|
|
||||||
|
store!(store8, Rb, NONE, DWORD);
|
||||||
|
store!(store16, Rw, NONE, QWORD);
|
||||||
|
store!(store32, Rd, movd, DWORD);
|
||||||
|
store!(store64, Rq, movq, QWORD);
|
||||||
|
|
||||||
fn push_physical(&mut self, value: ValueLocation) -> ValueLocation {
|
fn push_physical(&mut self, value: ValueLocation) -> ValueLocation {
|
||||||
self.block_state.depth.reserve(1);
|
self.block_state.depth.reserve(1);
|
||||||
@@ -2166,11 +2273,7 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.free_value(quotient);
|
self.free_value(quotient);
|
||||||
let should_save_rax = if self.block_state.regs.is_free(RAX) {
|
let should_save_rax = !self.block_state.regs.is_free(RAX);
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
if let ValueLocation::Reg(r) = quotient {
|
if let ValueLocation::Reg(r) = quotient {
|
||||||
self.block_state.regs.mark_used(r);
|
self.block_state.regs.mark_used(r);
|
||||||
@@ -2874,4 +2977,3 @@ impl<M: ModuleContext> Context<'_, M> {
|
|||||||
label
|
label
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,13 +113,7 @@ where
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
loop {
|
while let Some(op) = body.next() {
|
||||||
let op = if let Some(op) = body.next() {
|
|
||||||
op
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(Operator::Label(label)) = body.peek() {
|
if let Some(Operator::Label(label)) = body.peek() {
|
||||||
let block = blocks
|
let block = blocks
|
||||||
.get_mut(&BrTarget::Label(label.clone()))
|
.get_mut(&BrTarget::Label(label.clone()))
|
||||||
@@ -313,19 +307,16 @@ where
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
let (def, params) = {
|
let (def, params) = {
|
||||||
let def = blocks.get(&default).unwrap();
|
let def = &blocks[&default];
|
||||||
(
|
(
|
||||||
if def.is_next {
|
if def.is_next { None } else { Some(def.label) },
|
||||||
None
|
def.params,
|
||||||
} else {
|
|
||||||
Some(def.label)
|
|
||||||
},
|
|
||||||
def.params.clone()
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_labels = targets.iter()
|
let target_labels = targets
|
||||||
.map(|target| blocks.get(target).unwrap().label)
|
.iter()
|
||||||
|
.map(|target| blocks[target].label)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
ctx.br_table(target_labels, def, |ctx| {
|
ctx.br_table(target_labels, def, |ctx| {
|
||||||
@@ -438,10 +429,25 @@ where
|
|||||||
Operator::Le(SF64) => ctx.f64_le(),
|
Operator::Le(SF64) => ctx.f64_le(),
|
||||||
Operator::Drop(range) => ctx.drop(range),
|
Operator::Drop(range) => ctx.drop(range),
|
||||||
Operator::Const(val) => ctx.const_(val),
|
Operator::Const(val) => ctx.const_(val),
|
||||||
Operator::Load { ty: I32, memarg } => ctx.i32_load(memarg.offset)?,
|
Operator::Load8 { ty: _, memarg } => ctx.load8(GPRType::Rq, memarg.offset)?,
|
||||||
Operator::Load { ty: I64, memarg } => ctx.i64_load(memarg.offset)?,
|
Operator::Load16 { ty: _, memarg } => ctx.load16(GPRType::Rq, memarg.offset)?,
|
||||||
Operator::Store { ty: I32, memarg } => ctx.i32_store(memarg.offset)?,
|
Operator::Load { ty: ty @ I32, memarg } | Operator::Load { ty: ty @ F32, memarg } => ctx.load32(ty, memarg.offset)?,
|
||||||
Operator::Store { ty: I64, memarg } => ctx.i64_store(memarg.offset)?,
|
Operator::Load { ty: ty @ I64, memarg } | Operator::Load { ty: ty @ F64, memarg } => ctx.load64(ty, memarg.offset)?,
|
||||||
|
Operator::Store8 { ty: _, memarg } => {
|
||||||
|
ctx.store8(memarg.offset)?
|
||||||
|
}
|
||||||
|
Operator::Store16 { ty: _, memarg } => {
|
||||||
|
ctx.store16(memarg.offset)?
|
||||||
|
}
|
||||||
|
Operator::Store32 { memarg } => {
|
||||||
|
ctx.store32(memarg.offset)?
|
||||||
|
}
|
||||||
|
Operator::Store { ty: I32, memarg } | Operator::Store { ty: F32, memarg } => {
|
||||||
|
ctx.store32(memarg.offset)?
|
||||||
|
}
|
||||||
|
Operator::Store { ty: I64, memarg } | Operator::Store { ty: F64, memarg } => {
|
||||||
|
ctx.store64(memarg.offset)?
|
||||||
|
}
|
||||||
Operator::Select => {
|
Operator::Select => {
|
||||||
ctx.select();
|
ctx.select();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
never_type,
|
never_type,
|
||||||
alloc_layout_extra,
|
alloc_layout_extra,
|
||||||
try_from,
|
try_from,
|
||||||
try_trait,
|
try_trait
|
||||||
)]
|
)]
|
||||||
#![plugin(dynasm)]
|
#![plugin(dynasm)]
|
||||||
|
|
||||||
|
|||||||
@@ -657,7 +657,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "], {}", default)
|
write!(f, "], {}", default)
|
||||||
},
|
}
|
||||||
Operator::Call { function_index } => write!(f, "call {}", function_index),
|
Operator::Call { function_index } => write!(f, "call {}", function_index),
|
||||||
Operator::CallIndirect { .. } => write!(f, "call_indirect"),
|
Operator::CallIndirect { .. } => write!(f, "call_indirect"),
|
||||||
Operator::Drop(range) => {
|
Operator::Drop(range) => {
|
||||||
@@ -803,9 +803,8 @@ impl ControlFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mark_branched_to(&mut self) {
|
fn mark_branched_to(&mut self) {
|
||||||
match &mut self.kind {
|
if let ControlFrameKind::Block { needs_end_label } = &mut self.kind {
|
||||||
ControlFrameKind::Block { needs_end_label } => *needs_end_label = true,
|
*needs_end_label = true
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1318,10 +1317,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(consts) = self.consts_to_emit.take() {
|
if let Some(consts) = self.consts_to_emit.take() {
|
||||||
return Some(Ok(consts
|
return Some(Ok(consts.into_iter().map(Operator::Const).collect()));
|
||||||
.into_iter()
|
|
||||||
.map(|value| Operator::Const(value))
|
|
||||||
.collect()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.unreachable {
|
if self.unreachable {
|
||||||
@@ -1564,7 +1560,6 @@ where
|
|||||||
.map(Operator::Drop)
|
.map(Operator::Drop)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(None)
|
.chain(None)
|
||||||
.into_iter()
|
|
||||||
.chain(None)
|
.chain(None)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1617,7 +1612,7 @@ where
|
|||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
};
|
};
|
||||||
let targets = entries
|
let targets = entries
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|depth| {
|
.map(|depth| {
|
||||||
let block = self.nth_block_mut(*depth as _);
|
let block = self.nth_block_mut(*depth as _);
|
||||||
block.mark_branched_to();
|
block.mark_branched_to();
|
||||||
|
|||||||
145
src/tests.rs
145
src/tests.rs
@@ -1202,19 +1202,144 @@ fn br_table() {
|
|||||||
translated.disassemble();
|
translated.disassemble();
|
||||||
|
|
||||||
println!("as-block-first");
|
println!("as-block-first");
|
||||||
assert_eq!(
|
assert_eq!(translated.execute_func::<_, ()>(0, ()), Ok(()),);
|
||||||
translated.execute_func::<_, ()>(0, ()),
|
|
||||||
Ok(()),
|
|
||||||
);
|
|
||||||
println!("as-block-mid");
|
println!("as-block-mid");
|
||||||
assert_eq!(
|
assert_eq!(translated.execute_func::<_, ()>(1, ()), Ok(()),);
|
||||||
translated.execute_func::<_, ()>(1, ()),
|
|
||||||
Ok(()),
|
|
||||||
);
|
|
||||||
println!("as-block-last");
|
println!("as-block-last");
|
||||||
|
assert_eq!(translated.execute_func::<_, ()>(2, ()), Ok(()),);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f32_storage() {
|
||||||
|
const CODE: &str = r#"
|
||||||
|
(module
|
||||||
|
(memory (data "\00\00\a0\7f"))
|
||||||
|
|
||||||
|
(func (result f32)
|
||||||
|
(f32.load (i32.const 0))
|
||||||
|
)
|
||||||
|
(func (result i32)
|
||||||
|
(i32.load (i32.const 0))
|
||||||
|
)
|
||||||
|
(func
|
||||||
|
(f32.store (i32.const 0) (f32.const nan:0x200000))
|
||||||
|
)
|
||||||
|
(func
|
||||||
|
(i32.store (i32.const 0) (i32.const 0x7fa00000))
|
||||||
|
)
|
||||||
|
(func
|
||||||
|
(i32.store (i32.const 0) (i32.const 0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
const EXPECTED: u32 = 0x7fa00000;
|
||||||
|
|
||||||
|
let translated = translate_wat(CODE);
|
||||||
|
translated.disassemble();
|
||||||
|
|
||||||
|
// TODO: We don't support the data section with Lightbeam's test runtime
|
||||||
|
assert!(translated.execute_func::<(), ()>(2, ()).is_ok());
|
||||||
|
|
||||||
|
assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(EXPECTED));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
translated.execute_func::<_, ()>(2, ()),
|
translated
|
||||||
Ok(()),
|
.execute_func::<(), f32>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(EXPECTED)
|
||||||
|
);
|
||||||
|
assert!(translated.execute_func::<(), ()>(4, ()).is_ok());
|
||||||
|
assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(0));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f32>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(0)
|
||||||
|
);
|
||||||
|
assert!(translated.execute_func::<(), ()>(2, ()).is_ok());
|
||||||
|
assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(EXPECTED));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f32>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(EXPECTED)
|
||||||
|
);
|
||||||
|
assert!(translated.execute_func::<(), ()>(4, ()).is_ok());
|
||||||
|
assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(0));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f32>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(0)
|
||||||
|
);
|
||||||
|
assert!(translated.execute_func::<(), ()>(3, ()).is_ok());
|
||||||
|
assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(EXPECTED));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f32>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(EXPECTED)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f64_storage() {
|
||||||
|
const CODE: &str = r#"
|
||||||
|
(module
|
||||||
|
(memory (data "\00\00\00\00\00\00\f4\7f"))
|
||||||
|
|
||||||
|
(func (export "f64.load") (result f64) (f64.load (i32.const 0)))
|
||||||
|
(func (export "i64.load") (result i64) (i64.load (i32.const 0)))
|
||||||
|
(func (export "f64.store") (f64.store (i32.const 0) (f64.const nan:0x4000000000000)))
|
||||||
|
(func (export "i64.store") (i64.store (i32.const 0) (i64.const 0x7ff4000000000000)))
|
||||||
|
(func (export "reset") (i64.store (i32.const 0) (i64.const 0)))
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
const EXPECTED: u64 = 0x7ff4000000000000;
|
||||||
|
|
||||||
|
let translated = translate_wat(CODE);
|
||||||
|
translated.disassemble();
|
||||||
|
|
||||||
|
// TODO: We don't support the data section with Lightbeam's test runtime
|
||||||
|
assert!(translated.execute_func::<(), ()>(2, ()).is_ok());
|
||||||
|
|
||||||
|
assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(EXPECTED));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f64>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(EXPECTED)
|
||||||
|
);
|
||||||
|
assert!(translated.execute_func::<(), ()>(4, ()).is_ok());
|
||||||
|
assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(0));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f64>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(0)
|
||||||
|
);
|
||||||
|
assert!(translated.execute_func::<(), ()>(2, ()).is_ok());
|
||||||
|
assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(EXPECTED));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f64>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(EXPECTED)
|
||||||
|
);
|
||||||
|
assert!(translated.execute_func::<(), ()>(4, ()).is_ok());
|
||||||
|
assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(0));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f64>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(0)
|
||||||
|
);
|
||||||
|
assert!(translated.execute_func::<(), ()>(3, ()).is_ok());
|
||||||
|
assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(EXPECTED));
|
||||||
|
assert_eq!(
|
||||||
|
translated
|
||||||
|
.execute_func::<(), f64>(0, ())
|
||||||
|
.map(|f| f.to_bits()),
|
||||||
|
Ok(EXPECTED)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,11 +116,7 @@ pub fn code(
|
|||||||
for (idx, body) in code.into_iter().enumerate() {
|
for (idx, body) in code.into_iter().enumerate() {
|
||||||
let body = body?;
|
let body = body?;
|
||||||
|
|
||||||
function_body::translate_wasm(
|
function_body::translate_wasm(&mut session, idx as u32, &body)?;
|
||||||
&mut session,
|
|
||||||
idx as u32,
|
|
||||||
&body,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(session.into_translated_code_section()?)
|
Ok(session.into_translated_code_section()?)
|
||||||
|
|||||||
Reference in New Issue
Block a user