Remove unused code

This commit is contained in:
Jef
2019-03-26 17:40:57 +01:00
parent 84b4fa0208
commit 96df539554
6 changed files with 27 additions and 702 deletions

View File

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

View File

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

View File

@@ -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!(),
}
}
}

View File

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

View File

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

View File

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