Remove unused code
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::microwasm::{BrTarget, SignlessType, Type, Value, F32, F64, I32, I64};
|
||||
use crate::module::{ModuleContext, RuntimeFunc};
|
||||
use crate::module::ModuleContext;
|
||||
use cranelift_codegen::{binemit, ir};
|
||||
use dynasmrt::x64::Assembler;
|
||||
use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer};
|
||||
@@ -2296,7 +2296,7 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
||||
|
||||
self.block_state.regs.release(tmp);
|
||||
|
||||
for (i, target) in targets.enumerate() {
|
||||
for target in targets {
|
||||
let label = target
|
||||
.map(|target| self.target_to_label(target))
|
||||
.unwrap_or(end_label);
|
||||
@@ -3163,6 +3163,7 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
||||
|
||||
self.push(out_val);
|
||||
}
|
||||
|
||||
pub fn i32_truncate_f32_u(&mut self) {
|
||||
let mut val = self.pop();
|
||||
|
||||
@@ -3177,7 +3178,6 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
||||
|
||||
let sign_mask = self.aligned_label(4, LabelValue::I32(SIGN_MASK_F32 as i32));
|
||||
let float_cmp_mask = self.aligned_label(16, LabelValue::I32(0x4f000000u32 as i32));
|
||||
let zero = self.aligned_label(16, LabelValue::I32(0));
|
||||
let trap_label = self.trap_label();
|
||||
|
||||
dynasm!(self.asm
|
||||
@@ -3261,7 +3261,6 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
||||
let sign_mask = self.aligned_label(4, LabelValue::I32(SIGN_MASK_F32 as i32));
|
||||
let float_cmp_mask =
|
||||
self.aligned_label(16, LabelValue::I64(0x41e0000000000000u64 as i64));
|
||||
let zero = self.aligned_label(16, LabelValue::I64(0));
|
||||
let trap_label = self.trap_label();
|
||||
|
||||
dynasm!(self.asm
|
||||
@@ -4720,7 +4719,6 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
||||
let locs = arg_locs(arg_types);
|
||||
|
||||
self.save_volatile(locs.len()..);
|
||||
let depth = self.block_state.depth.clone();
|
||||
|
||||
let (_, label) = self.func_starts[defined_index as usize];
|
||||
|
||||
@@ -4846,10 +4844,8 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
||||
where
|
||||
F: IntoLabel,
|
||||
{
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let key = fun.key();
|
||||
if let Some((label, current_align, func)) = self.labels.get(&(align, key)) {
|
||||
if let Some((label, _, _)) = self.labels.get(&(align, key)) {
|
||||
return *label;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::backend::*;
|
||||
use crate::error::Error;
|
||||
use crate::microwasm::*;
|
||||
use crate::module::{quickhash, ModuleContext, SigType, Signature};
|
||||
use crate::module::{ModuleContext, SigType, Signature};
|
||||
use cranelift_codegen::binemit;
|
||||
use either::{Either, Left, Right};
|
||||
use multi_mut::HashMapMultiMut;
|
||||
use std::{collections::HashMap, convert::TryInto, hash::Hash};
|
||||
use std::{collections::HashMap, hash::Hash};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Block {
|
||||
@@ -49,7 +49,7 @@ where
|
||||
body,
|
||||
);
|
||||
|
||||
crate::microwasm::dis(
|
||||
let _ = crate::microwasm::dis(
|
||||
std::io::stdout(),
|
||||
func_idx,
|
||||
microwasm_conv.flat_map(|ops| ops.unwrap()),
|
||||
@@ -81,7 +81,6 @@ where
|
||||
M: ModuleContext,
|
||||
I: IntoIterator<Item = Operator<L>>,
|
||||
L: Hash + Clone + Eq,
|
||||
Operator<L>: std::fmt::Display,
|
||||
{
|
||||
fn drop_elements<T>(stack: &mut Vec<T>, depths: std::ops::RangeInclusive<u32>) {
|
||||
let _ = (|| {
|
||||
@@ -140,12 +139,7 @@ where
|
||||
if let Some(Operator::Label(label)) = body.peek() {
|
||||
let block = blocks
|
||||
.get_mut(&BrTarget::Label(label.clone()))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Block definition should be before label definition: {}",
|
||||
Operator::Label(label.clone())
|
||||
)
|
||||
});
|
||||
.unwrap_or_else(|| panic!("Label defined before being declared"));
|
||||
block.is_next = true;
|
||||
}
|
||||
|
||||
@@ -227,10 +221,7 @@ where
|
||||
entry.remove_entry();
|
||||
}
|
||||
} else {
|
||||
panic!(
|
||||
"Label defined before being declared: {}",
|
||||
Operator::Label(label)
|
||||
);
|
||||
panic!("Label defined before being declared");
|
||||
}
|
||||
}
|
||||
Operator::Block {
|
||||
@@ -320,7 +311,8 @@ where
|
||||
) {
|
||||
((Some(Left(ref cc)), to_drop), ref mut other @ (None, _))
|
||||
| (ref mut other @ (None, _), (Some(Left(ref cc)), to_drop)) => {
|
||||
let mut cc = ctx.serialize_block_args_preserve_flags(cc, to_drop.clone());
|
||||
let mut cc =
|
||||
ctx.serialize_block_args_preserve_flags(cc, to_drop.clone());
|
||||
if let Some(to_drop) = other.1 {
|
||||
drop_elements(&mut cc.arguments, to_drop.clone());
|
||||
}
|
||||
@@ -710,8 +702,6 @@ where
|
||||
ctx.memory_grow();
|
||||
}
|
||||
Operator::Call { function_index } => {
|
||||
use cranelift_codegen::ir;
|
||||
|
||||
let callee_ty = module_context.func_type(function_index);
|
||||
|
||||
if let Some(defined_index) = module_context.defined_func_index(function_index) {
|
||||
@@ -752,9 +742,6 @@ where
|
||||
callee_ty.returns().iter().map(|t| t.to_microwasm_type()),
|
||||
);
|
||||
}
|
||||
op => {
|
||||
unimplemented!("{}", op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,6 @@ where
|
||||
BrTarget<L>: fmt::Display,
|
||||
L: Clone,
|
||||
{
|
||||
use std::fmt::Write;
|
||||
|
||||
const DISASSEMBLE_BLOCK_DEFS: bool = true;
|
||||
|
||||
writeln!(out, ".fn_{}:", function_name)?;
|
||||
|
||||
let p = " ";
|
||||
@@ -508,8 +504,6 @@ pub enum Operator<Label> {
|
||||
reserved: u32,
|
||||
},
|
||||
Const(Value),
|
||||
RefNull,
|
||||
RefIsNull,
|
||||
Eq(SignlessType),
|
||||
Ne(SignlessType),
|
||||
/// `eqz` on integers
|
||||
@@ -565,30 +559,6 @@ pub enum Operator<Label> {
|
||||
Extend {
|
||||
sign: Signedness,
|
||||
},
|
||||
// 0xFC operators
|
||||
/// Non-trapping Float-to-int conversion
|
||||
ISatTruncFromF {
|
||||
input_ty: Float,
|
||||
output_ty: SignfulInt,
|
||||
},
|
||||
|
||||
// 0xFC operators
|
||||
// bulk memory https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md
|
||||
MemoryInit {
|
||||
segment: u32,
|
||||
},
|
||||
DataDrop {
|
||||
segment: u32,
|
||||
},
|
||||
MemoryCopy,
|
||||
MemoryFill,
|
||||
TableInit {
|
||||
segment: u32,
|
||||
},
|
||||
ElemDrop {
|
||||
segment: u32,
|
||||
},
|
||||
TableCopy,
|
||||
}
|
||||
|
||||
impl<L> Operator<L> {
|
||||
@@ -743,8 +713,6 @@ where
|
||||
Operator::MemorySize { .. } => write!(f, "memory.size"),
|
||||
Operator::MemoryGrow { .. } => write!(f, "memory.grow"),
|
||||
Operator::Const(val) => write!(f, "const {}", val),
|
||||
Operator::RefNull => write!(f, "refnull"),
|
||||
Operator::RefIsNull => write!(f, "refisnull"),
|
||||
Operator::Eq(ty) => write!(f, "{}.eq", ty),
|
||||
Operator::Ne(ty) => write!(f, "{}.ne", ty),
|
||||
Operator::Eqz(ty) => write!(f, "{}.eqz", SignfulInt(Signedness::Unsigned, *ty)),
|
||||
@@ -784,9 +752,6 @@ where
|
||||
Operator::I64ReinterpretFromF64 => write!(f, "i64.reinterpret_from.f64"),
|
||||
Operator::F32ReinterpretFromI32 => write!(f, "f32.reinterpret_from.i32"),
|
||||
Operator::F64ReinterpretFromI64 => write!(f, "f64.reinterpret_from.i64"),
|
||||
Operator::MemoryCopy => write!(f, "memory.copy"),
|
||||
Operator::MemoryFill => write!(f, "memory.fill"),
|
||||
Operator::TableCopy => write!(f, "table.copy"),
|
||||
Operator::FConvertFromI {
|
||||
input_ty,
|
||||
output_ty,
|
||||
@@ -807,25 +772,12 @@ where
|
||||
output_ty,
|
||||
Type::<Int>::Float(*input_ty)
|
||||
),
|
||||
Operator::ISatTruncFromF {
|
||||
input_ty,
|
||||
output_ty,
|
||||
} => write!(
|
||||
f,
|
||||
"{}.saturating_truncate_from.{}",
|
||||
output_ty,
|
||||
Type::<Int>::Float(*input_ty)
|
||||
),
|
||||
Operator::Extend { sign } => write!(
|
||||
f,
|
||||
"{}.extend_from.{}",
|
||||
SignfulInt(*sign, Size::_64),
|
||||
SignfulInt(*sign, Size::_32)
|
||||
),
|
||||
Operator::MemoryInit { .. } => unimplemented!(),
|
||||
Operator::TableInit { .. } => unimplemented!(),
|
||||
Operator::DataDrop { .. } => unimplemented!(),
|
||||
Operator::ElemDrop { .. } => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,11 +484,11 @@ impl ModuleContext for SimpleContext {
|
||||
self.func_ty_indicies[func_idx as usize]
|
||||
}
|
||||
|
||||
fn defined_global_index(&self, index: u32) -> Option<u32> {
|
||||
fn defined_global_index(&self, _index: u32) -> Option<u32> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn global_type(&self, global_index: u32) -> &Self::GlobalType {
|
||||
fn global_type(&self, _global_index: u32) -> &Self::GlobalType {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -496,15 +496,15 @@ impl ModuleContext for SimpleContext {
|
||||
&self.types[index as usize]
|
||||
}
|
||||
|
||||
fn vmctx_vmglobal_definition(&self, index: u32) -> u32 {
|
||||
fn vmctx_vmglobal_definition(&self, _index: u32) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn vmctx_vmglobal_import_from(&self, index: u32) -> u32 {
|
||||
fn vmctx_vmglobal_import_from(&self, _index: u32) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn defined_memory_index(&self, index: u32) -> Option<u32> {
|
||||
fn defined_memory_index(&self, _index: u32) -> Option<u32> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -512,21 +512,21 @@ impl ModuleContext for SimpleContext {
|
||||
Some(index)
|
||||
}
|
||||
|
||||
fn vmctx_vmfunction_import_body(&self, func_index: u32) -> u32 {
|
||||
fn vmctx_vmfunction_import_body(&self, _func_index: u32) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn vmctx_vmfunction_import_vmctx(&self, func_index: u32) -> u32 {
|
||||
fn vmctx_vmfunction_import_vmctx(&self, _func_index: u32) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn vmctx_vmtable_import_from(&self, table_index: u32) -> u32 {
|
||||
fn vmctx_vmtable_import_from(&self, _table_index: u32) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn vmctx_vmmemory_definition(&self, defined_memory_index: u32) -> u32 {
|
||||
fn vmctx_vmmemory_definition(&self, _defined_memory_index: u32) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn vmctx_vmmemory_import_from(&self, memory_index: u32) -> u32 {
|
||||
fn vmctx_vmmemory_import_from(&self, _memory_index: u32) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn vmmemory_definition_base(&self) -> u8 {
|
||||
@@ -536,22 +536,27 @@ impl ModuleContext for SimpleContext {
|
||||
unimplemented!()
|
||||
}
|
||||
fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32 {
|
||||
assert_eq!(defined_memory_index, 0);
|
||||
VmCtx::offset_of_memory_ptr()
|
||||
}
|
||||
|
||||
fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32 {
|
||||
assert_eq!(defined_memory_index, 0);
|
||||
VmCtx::offset_of_memory_len()
|
||||
}
|
||||
|
||||
fn vmctx_vmtable_definition(&self, defined_table_index: u32) -> u32 {
|
||||
assert_eq!(defined_table_index, 0);
|
||||
VmCtx::offset_of_funcs_ptr() as _
|
||||
}
|
||||
|
||||
fn vmctx_vmtable_definition_base(&self, defined_table_index: u32) -> u32 {
|
||||
assert_eq!(defined_table_index, 0);
|
||||
VmCtx::offset_of_funcs_ptr() as _
|
||||
}
|
||||
|
||||
fn vmctx_vmtable_definition_current_elements(&self, defined_table_index: u32) -> u32 {
|
||||
assert_eq!(defined_table_index, 0);
|
||||
VmCtx::offset_of_funcs_len() as _
|
||||
}
|
||||
|
||||
|
||||
614
src/tests.rs
614
src/tests.rs
@@ -571,175 +571,6 @@ fn if_without_result() {
|
||||
assert_eq!(execute_wat(code, 2, 3), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_call() {
|
||||
let code = r#"
|
||||
(module
|
||||
(func (param i32) (param i32) (result i32)
|
||||
(call $assert_zero
|
||||
(get_local 1)
|
||||
)
|
||||
(get_local 0)
|
||||
)
|
||||
|
||||
(func $assert_zero (param $v i32)
|
||||
(local i32)
|
||||
(if (get_local $v)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
assert_eq!(execute_wat(code, 2, 0), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_function() {
|
||||
let code = r#"
|
||||
(module
|
||||
(func (param i32) (param i32) (param i32) (param i32)
|
||||
(param i32) (param i32)
|
||||
(result i32)
|
||||
|
||||
(call $assert_zero
|
||||
(get_local 5)
|
||||
)
|
||||
(get_local 0)
|
||||
)
|
||||
|
||||
(func $assert_zero (param $v i32)
|
||||
(local i32)
|
||||
(if (get_local $v)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
{
|
||||
let translated = translate_wat(code);
|
||||
translated.disassemble();
|
||||
let out: Result<u32, _> = translated.execute_func(0, (5, 4, 3, 2, 1, 0));
|
||||
out
|
||||
},
|
||||
Ok(5)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_read_args_spill_to_stack() {
|
||||
let code = r#"
|
||||
(module
|
||||
(func (param i32) (param i32) (param i32) (param i32)
|
||||
(param i32) (param i32) (param i32) (param i32)
|
||||
(param i32) (param i32) (param i32) (param i32)
|
||||
(result i32)
|
||||
|
||||
(call $assert_zero
|
||||
(get_local 7)
|
||||
)
|
||||
(get_local 0)
|
||||
)
|
||||
|
||||
(func $assert_zero (param $v i32)
|
||||
(local i32)
|
||||
(if (get_local $v)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
{
|
||||
let translated = translate_wat(code);
|
||||
translated.disassemble();
|
||||
translated.execute_func(
|
||||
0,
|
||||
(
|
||||
7u32, 6u32, 5u32, 4u32, 3u32, 2u32, 1u32, 0u32, 1u32, 2u32, 3u32, 4u32,
|
||||
),
|
||||
)
|
||||
},
|
||||
Ok(7u32)
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! mk_function_write_args_spill_to_stack {
|
||||
($name:ident, $typ:ty) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let code = format!(
|
||||
"
|
||||
(module
|
||||
(func (param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||
(result {typ})
|
||||
|
||||
(call $called
|
||||
(get_local 0)
|
||||
(get_local 1)
|
||||
(get_local 2)
|
||||
(get_local 3)
|
||||
(get_local 4)
|
||||
(get_local 5)
|
||||
(get_local 6)
|
||||
(get_local 7)
|
||||
(get_local 8)
|
||||
(get_local 9)
|
||||
(get_local 10)
|
||||
(get_local 11)
|
||||
)
|
||||
)
|
||||
|
||||
(func $called
|
||||
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||
(result {typ})
|
||||
|
||||
(call $assert_zero
|
||||
(get_local 11)
|
||||
)
|
||||
(get_local 0)
|
||||
)
|
||||
|
||||
(func $assert_zero (param $v {typ})
|
||||
(local {typ})
|
||||
(if ({typ}.ne (get_local $v) ({typ}.const 0))
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
",
|
||||
typ = stringify!($typ)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
{
|
||||
let translated = translate_wat(&code);
|
||||
translated.disassemble();
|
||||
let out: Result<$typ, _> = translated.execute_func(
|
||||
0,
|
||||
(
|
||||
11 as $typ, 10 as $typ, 9 as $typ, 8 as $typ, 7 as $typ, 6 as $typ,
|
||||
5 as $typ, 4 as $typ, 3 as $typ, 2 as $typ, 1 as $typ, 0 as $typ,
|
||||
),
|
||||
);
|
||||
out
|
||||
},
|
||||
Ok(11)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mk_function_write_args_spill_to_stack!(function_write_args_spill_to_stack_i32, i32);
|
||||
mk_function_write_args_spill_to_stack!(function_write_args_spill_to_stack_i64, i64);
|
||||
|
||||
#[test]
|
||||
fn block() {
|
||||
let code = r#"
|
||||
@@ -801,148 +632,6 @@ fn brif_block() {
|
||||
assert_eq!(execute_wat(code, 5, 7), 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_loop() {
|
||||
let code = r#"
|
||||
(module
|
||||
(func
|
||||
(call $assert-eq (call $as-binary-operand) (i32.const 12))
|
||||
(call $assert-eq (call $break-bare) (i32.const 19))
|
||||
(call $assert-eq (call $break-value) (i32.const 18))
|
||||
(call $assert-eq (call $break-repeated) (i32.const 18))
|
||||
(call $assert-eq (call $break-inner) (i32.const 0x7))
|
||||
)
|
||||
(func $dummy)
|
||||
(func $as-binary-operand (result i32)
|
||||
(i32.mul
|
||||
(loop (result i32) (call $dummy) (i32.const 3))
|
||||
(loop (result i32) (call $dummy) (i32.const 4))
|
||||
)
|
||||
)
|
||||
(func $break-bare (result i32)
|
||||
(block (loop (br 1) (br 0) (unreachable)))
|
||||
(block (loop (br_if 1 (i32.const 1)) (unreachable)))
|
||||
(i32.const 19)
|
||||
)
|
||||
(func $break-value (result i32)
|
||||
(block (result i32)
|
||||
(loop (result i32) (br 1 (i32.const 18)) (br 0) (i32.const 19))
|
||||
)
|
||||
)
|
||||
(func $break-repeated (result i32)
|
||||
(block (result i32)
|
||||
(loop (result i32)
|
||||
(br 1 (i32.const 18))
|
||||
(br 1 (i32.const 19))
|
||||
(drop (br_if 1 (i32.const 20) (i32.const 0)))
|
||||
(drop (br_if 1 (i32.const 20) (i32.const 1)))
|
||||
(br 1 (i32.const 21))
|
||||
(i32.const 21)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $break-inner (result i32)
|
||||
(local i32)
|
||||
(set_local 0 (i32.const 0))
|
||||
(set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (block (result i32) (br 2 (i32.const 0x1)))))))
|
||||
(set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (loop (result i32) (br 2 (i32.const 0x2)))))))
|
||||
(set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (block (result i32) (loop (result i32) (br 1 (i32.const 0x4))))))))
|
||||
(get_local 0)
|
||||
)
|
||||
(func $assert-eq (param i32) (param i32)
|
||||
(if (i32.ne (get_local 0) (get_local 1))
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let translated = translate_wat(code);
|
||||
translated.disassemble();
|
||||
translated.execute_func::<(), ()>(0, ()).unwrap();
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn spec_fac(n: i8) -> bool {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(func (param i32) (result i32)
|
||||
(local i32)
|
||||
(set_local 1 (call $fac-iter (get_local 0)))
|
||||
(call $assert-eq (get_local 1) (call $fac-opt (get_local 0)))
|
||||
(get_local 1)
|
||||
)
|
||||
|
||||
(func $assert-eq (param i32) (param i32)
|
||||
(if (i32.ne (get_local 0) (get_local 1))
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
;; Iterative factorial
|
||||
(func $fac-iter (param i32) (result i32)
|
||||
(local i32 i32)
|
||||
(set_local 1 (get_local 0))
|
||||
(set_local 2 (i32.const 1))
|
||||
(block
|
||||
(loop
|
||||
(if
|
||||
(i32.lt_s (get_local 1) (i32.const 2))
|
||||
(then (br 2))
|
||||
(else
|
||||
(set_local 2 (i32.mul (get_local 1) (get_local 2)))
|
||||
(set_local 1 (i32.sub (get_local 1) (i32.const 1)))
|
||||
)
|
||||
)
|
||||
(br 0)
|
||||
)
|
||||
)
|
||||
(get_local 2)
|
||||
)
|
||||
|
||||
;; Optimized factorial.
|
||||
(func $fac-opt (param i32) (result i32)
|
||||
(local i32)
|
||||
(set_local 1 (i32.const 1))
|
||||
(block
|
||||
(br_if 0 (i32.lt_s (get_local 0) (i32.const 2)))
|
||||
(loop
|
||||
(set_local 1 (i32.mul (get_local 1) (get_local 0)))
|
||||
(set_local 0 (i32.add (get_local 0) (i32.const -1)))
|
||||
(br_if 0 (i32.gt_s (get_local 0) (i32.const 1)))
|
||||
)
|
||||
)
|
||||
(get_local 1)
|
||||
)
|
||||
)"#;
|
||||
|
||||
fn fac(mut n: i32) -> i32 {
|
||||
let mut a = 1i32;
|
||||
|
||||
while n > 1 {
|
||||
a = a.wrapping_mul(n);
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
a
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref TRANSLATED: ExecutableModule = {
|
||||
let out = translate_wat(CODE);
|
||||
out.disassemble();
|
||||
out
|
||||
};
|
||||
}
|
||||
|
||||
let n = n as i32;
|
||||
|
||||
assert_eq!(TRANSLATED.execute_func::<(i32,), i32>(2, (n,)), Ok(fac(n)));
|
||||
assert_eq!(TRANSLATED.execute_func::<(i32,), i32>(3, (n,)), Ok(fac(n)));
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that br_if keeps values in the case if the branch
|
||||
// hasn't been taken.
|
||||
#[test]
|
||||
@@ -1284,309 +973,6 @@ fn br_table() {
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (8u32,)), Ok(126));
|
||||
}
|
||||
|
||||
#[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!(
|
||||
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::<(), ()>(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)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(memory 1 1)
|
||||
|
||||
(func (result i32)
|
||||
(local i32 i32 i32)
|
||||
(set_local 0 (i32.const 10))
|
||||
(block
|
||||
(loop
|
||||
(if
|
||||
(i32.eq (get_local 0) (i32.const 0))
|
||||
(then (br 2))
|
||||
)
|
||||
(set_local 2 (i32.mul (get_local 0) (i32.const 4)))
|
||||
(i32.store (get_local 2) (get_local 0))
|
||||
(set_local 1 (i32.load (get_local 2)))
|
||||
(if
|
||||
(i32.ne (get_local 0) (get_local 1))
|
||||
(then (return (i32.const 0)))
|
||||
)
|
||||
(set_local 0 (i32.sub (get_local 0) (i32.const 1)))
|
||||
(br 0)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
)"#;
|
||||
|
||||
let translated = translate_wat(CODE);
|
||||
translated.disassemble();
|
||||
|
||||
assert_eq!(translated.execute_func::<(), i32>(0, ()), Ok(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_storage_calls() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(memory 1 1)
|
||||
|
||||
(func (result i32)
|
||||
(local i32 i32 i32)
|
||||
(set_local 0 (i32.const 10))
|
||||
(block
|
||||
(loop
|
||||
(if
|
||||
(i32.eq (get_local 0) (i32.const 0))
|
||||
(then (br 2))
|
||||
)
|
||||
(set_local 2 (i32.mul (get_local 0) (i32.const 4)))
|
||||
(call $assert_eq (call $inner) (i32.const 1))
|
||||
(i32.store (get_local 2) (get_local 0))
|
||||
(set_local 1 (i32.load (get_local 2)))
|
||||
(if
|
||||
(i32.ne (get_local 0) (get_local 1))
|
||||
(then (return (i32.const 0)))
|
||||
)
|
||||
(set_local 0 (i32.sub (get_local 0) (i32.const 1)))
|
||||
(br 0)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
|
||||
(func $assert_eq (param $a i32) (param $b i32)
|
||||
(if (i32.ne (get_local $a) (get_local $b))
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
(func $inner (result i32)
|
||||
(local i32 i32 i32)
|
||||
(set_local 0 (i32.const 10))
|
||||
(block
|
||||
(loop
|
||||
(if
|
||||
(i32.eq (get_local 0) (i32.const 0))
|
||||
(then (br 2))
|
||||
)
|
||||
(set_local 2 (i32.mul (get_local 0) (i32.const 4)))
|
||||
(i32.store (get_local 2) (get_local 0))
|
||||
(set_local 1 (i32.load (get_local 2)))
|
||||
(if
|
||||
(i32.ne (get_local 0) (get_local 1))
|
||||
(then (return (i32.const 0)))
|
||||
)
|
||||
(set_local 0 (i32.sub (get_local 0) (i32.const 1)))
|
||||
(br 0)
|
||||
)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
)"#;
|
||||
|
||||
let translated = translate_wat(CODE);
|
||||
translated.disassemble();
|
||||
|
||||
assert_eq!(translated.execute_func::<(), i32>(0, ()), Ok(1));
|
||||
}
|
||||
|
||||
// TODO: Signature mismatches correctly fail at time of writing this comment,
|
||||
// but we can't add a test for that until we implement traps properly.
|
||||
#[test]
|
||||
fn call_indirect() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(type $over-i64 (func (param i64) (result i64)))
|
||||
|
||||
(table anyfunc
|
||||
(elem
|
||||
$fac $fib
|
||||
)
|
||||
)
|
||||
|
||||
(func $dispatch (param i32 i64) (result i64)
|
||||
(call_indirect (type $over-i64) (get_local 1) (get_local 0))
|
||||
)
|
||||
|
||||
(func $fac (type $over-i64)
|
||||
(if (result i64) (i64.eqz (get_local 0))
|
||||
(then (i64.const 1))
|
||||
(else
|
||||
(i64.mul
|
||||
(get_local 0)
|
||||
(call_indirect (type $over-i64)
|
||||
(i64.sub (get_local 0) (i64.const 1))
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func $fib (type $over-i64)
|
||||
(if (result i64) (i64.le_u (get_local 0) (i64.const 1))
|
||||
(then (i64.const 1))
|
||||
(else
|
||||
(i64.add
|
||||
(call_indirect (type $over-i64)
|
||||
(i64.sub (get_local 0) (i64.const 2))
|
||||
(i32.const 1)
|
||||
)
|
||||
(call_indirect (type $over-i64)
|
||||
(i64.sub (get_local 0) (i64.const 1))
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)"#;
|
||||
|
||||
let wasm = wabt::wat2wasm(CODE).unwrap();
|
||||
let module = translate(&wasm).unwrap();
|
||||
|
||||
module.disassemble();
|
||||
|
||||
assert_eq!(
|
||||
module.execute_func::<(i32, i64), i64>(0, (0, 10)).unwrap(),
|
||||
3628800
|
||||
);
|
||||
assert_eq!(
|
||||
module.execute_func::<(i32, i64), i64>(0, (1, 10)).unwrap(),
|
||||
89
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! test_select {
|
||||
($name:ident, $ty:ident) => {
|
||||
mod $name {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::backend::{CodeGenSession, TranslatedCodeSection};
|
||||
use crate::error::Error;
|
||||
use crate::function_body;
|
||||
use crate::microwasm::{MicrowasmConv, Type as MWType};
|
||||
use crate::module::{ModuleContext, SimpleContext};
|
||||
use crate::module::SimpleContext;
|
||||
use cranelift_codegen::{binemit, ir};
|
||||
#[allow(unused_imports)] // for now
|
||||
use wasmparser::{
|
||||
|
||||
Reference in New Issue
Block a user