Remove unused code
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::microwasm::{BrTarget, SignlessType, Type, Value, F32, F64, I32, I64};
|
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 cranelift_codegen::{binemit, ir};
|
||||||
use dynasmrt::x64::Assembler;
|
use dynasmrt::x64::Assembler;
|
||||||
use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer};
|
use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer};
|
||||||
@@ -2296,7 +2296,7 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
|||||||
|
|
||||||
self.block_state.regs.release(tmp);
|
self.block_state.regs.release(tmp);
|
||||||
|
|
||||||
for (i, target) in targets.enumerate() {
|
for target in targets {
|
||||||
let label = target
|
let label = target
|
||||||
.map(|target| self.target_to_label(target))
|
.map(|target| self.target_to_label(target))
|
||||||
.unwrap_or(end_label);
|
.unwrap_or(end_label);
|
||||||
@@ -3163,6 +3163,7 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
|||||||
|
|
||||||
self.push(out_val);
|
self.push(out_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn i32_truncate_f32_u(&mut self) {
|
pub fn i32_truncate_f32_u(&mut self) {
|
||||||
let mut val = self.pop();
|
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 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 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();
|
let trap_label = self.trap_label();
|
||||||
|
|
||||||
dynasm!(self.asm
|
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 sign_mask = self.aligned_label(4, LabelValue::I32(SIGN_MASK_F32 as i32));
|
||||||
let float_cmp_mask =
|
let float_cmp_mask =
|
||||||
self.aligned_label(16, LabelValue::I64(0x41e0000000000000u64 as i64));
|
self.aligned_label(16, LabelValue::I64(0x41e0000000000000u64 as i64));
|
||||||
let zero = self.aligned_label(16, LabelValue::I64(0));
|
|
||||||
let trap_label = self.trap_label();
|
let trap_label = self.trap_label();
|
||||||
|
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
@@ -4720,7 +4719,6 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
|||||||
let locs = arg_locs(arg_types);
|
let locs = arg_locs(arg_types);
|
||||||
|
|
||||||
self.save_volatile(locs.len()..);
|
self.save_volatile(locs.len()..);
|
||||||
let depth = self.block_state.depth.clone();
|
|
||||||
|
|
||||||
let (_, label) = self.func_starts[defined_index as usize];
|
let (_, label) = self.func_starts[defined_index as usize];
|
||||||
|
|
||||||
@@ -4846,10 +4844,8 @@ impl<'this, M: ModuleContext> Context<'this, M> {
|
|||||||
where
|
where
|
||||||
F: IntoLabel,
|
F: IntoLabel,
|
||||||
{
|
{
|
||||||
use std::collections::hash_map::Entry;
|
|
||||||
|
|
||||||
let key = fun.key();
|
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;
|
return *label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::backend::*;
|
use crate::backend::*;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::microwasm::*;
|
use crate::microwasm::*;
|
||||||
use crate::module::{quickhash, ModuleContext, SigType, Signature};
|
use crate::module::{ModuleContext, SigType, Signature};
|
||||||
use cranelift_codegen::binemit;
|
use cranelift_codegen::binemit;
|
||||||
use either::{Either, Left, Right};
|
use either::{Either, Left, Right};
|
||||||
use multi_mut::HashMapMultiMut;
|
use multi_mut::HashMapMultiMut;
|
||||||
use std::{collections::HashMap, convert::TryInto, hash::Hash};
|
use std::{collections::HashMap, hash::Hash};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Block {
|
struct Block {
|
||||||
@@ -49,7 +49,7 @@ where
|
|||||||
body,
|
body,
|
||||||
);
|
);
|
||||||
|
|
||||||
crate::microwasm::dis(
|
let _ = crate::microwasm::dis(
|
||||||
std::io::stdout(),
|
std::io::stdout(),
|
||||||
func_idx,
|
func_idx,
|
||||||
microwasm_conv.flat_map(|ops| ops.unwrap()),
|
microwasm_conv.flat_map(|ops| ops.unwrap()),
|
||||||
@@ -81,7 +81,6 @@ where
|
|||||||
M: ModuleContext,
|
M: ModuleContext,
|
||||||
I: IntoIterator<Item = Operator<L>>,
|
I: IntoIterator<Item = Operator<L>>,
|
||||||
L: Hash + Clone + Eq,
|
L: Hash + Clone + Eq,
|
||||||
Operator<L>: std::fmt::Display,
|
|
||||||
{
|
{
|
||||||
fn drop_elements<T>(stack: &mut Vec<T>, depths: std::ops::RangeInclusive<u32>) {
|
fn drop_elements<T>(stack: &mut Vec<T>, depths: std::ops::RangeInclusive<u32>) {
|
||||||
let _ = (|| {
|
let _ = (|| {
|
||||||
@@ -140,12 +139,7 @@ where
|
|||||||
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()))
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| panic!("Label defined before being declared"));
|
||||||
panic!(
|
|
||||||
"Block definition should be before label definition: {}",
|
|
||||||
Operator::Label(label.clone())
|
|
||||||
)
|
|
||||||
});
|
|
||||||
block.is_next = true;
|
block.is_next = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,10 +221,7 @@ where
|
|||||||
entry.remove_entry();
|
entry.remove_entry();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!("Label defined before being declared");
|
||||||
"Label defined before being declared: {}",
|
|
||||||
Operator::Label(label)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::Block {
|
Operator::Block {
|
||||||
@@ -320,7 +311,8 @@ where
|
|||||||
) {
|
) {
|
||||||
((Some(Left(ref cc)), to_drop), ref mut other @ (None, _))
|
((Some(Left(ref cc)), to_drop), ref mut other @ (None, _))
|
||||||
| (ref mut other @ (None, _), (Some(Left(ref cc)), to_drop)) => {
|
| (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 {
|
if let Some(to_drop) = other.1 {
|
||||||
drop_elements(&mut cc.arguments, to_drop.clone());
|
drop_elements(&mut cc.arguments, to_drop.clone());
|
||||||
}
|
}
|
||||||
@@ -710,8 +702,6 @@ where
|
|||||||
ctx.memory_grow();
|
ctx.memory_grow();
|
||||||
}
|
}
|
||||||
Operator::Call { function_index } => {
|
Operator::Call { function_index } => {
|
||||||
use cranelift_codegen::ir;
|
|
||||||
|
|
||||||
let callee_ty = module_context.func_type(function_index);
|
let callee_ty = module_context.func_type(function_index);
|
||||||
|
|
||||||
if let Some(defined_index) = module_context.defined_func_index(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()),
|
callee_ty.returns().iter().map(|t| t.to_microwasm_type()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
op => {
|
|
||||||
unimplemented!("{}", op);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ where
|
|||||||
BrTarget<L>: fmt::Display,
|
BrTarget<L>: fmt::Display,
|
||||||
L: Clone,
|
L: Clone,
|
||||||
{
|
{
|
||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
const DISASSEMBLE_BLOCK_DEFS: bool = true;
|
|
||||||
|
|
||||||
writeln!(out, ".fn_{}:", function_name)?;
|
writeln!(out, ".fn_{}:", function_name)?;
|
||||||
|
|
||||||
let p = " ";
|
let p = " ";
|
||||||
@@ -508,8 +504,6 @@ pub enum Operator<Label> {
|
|||||||
reserved: u32,
|
reserved: u32,
|
||||||
},
|
},
|
||||||
Const(Value),
|
Const(Value),
|
||||||
RefNull,
|
|
||||||
RefIsNull,
|
|
||||||
Eq(SignlessType),
|
Eq(SignlessType),
|
||||||
Ne(SignlessType),
|
Ne(SignlessType),
|
||||||
/// `eqz` on integers
|
/// `eqz` on integers
|
||||||
@@ -565,30 +559,6 @@ pub enum Operator<Label> {
|
|||||||
Extend {
|
Extend {
|
||||||
sign: Signedness,
|
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> {
|
impl<L> Operator<L> {
|
||||||
@@ -743,8 +713,6 @@ where
|
|||||||
Operator::MemorySize { .. } => write!(f, "memory.size"),
|
Operator::MemorySize { .. } => write!(f, "memory.size"),
|
||||||
Operator::MemoryGrow { .. } => write!(f, "memory.grow"),
|
Operator::MemoryGrow { .. } => write!(f, "memory.grow"),
|
||||||
Operator::Const(val) => write!(f, "const {}", val),
|
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::Eq(ty) => write!(f, "{}.eq", ty),
|
||||||
Operator::Ne(ty) => write!(f, "{}.ne", ty),
|
Operator::Ne(ty) => write!(f, "{}.ne", ty),
|
||||||
Operator::Eqz(ty) => write!(f, "{}.eqz", SignfulInt(Signedness::Unsigned, *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::I64ReinterpretFromF64 => write!(f, "i64.reinterpret_from.f64"),
|
||||||
Operator::F32ReinterpretFromI32 => write!(f, "f32.reinterpret_from.i32"),
|
Operator::F32ReinterpretFromI32 => write!(f, "f32.reinterpret_from.i32"),
|
||||||
Operator::F64ReinterpretFromI64 => write!(f, "f64.reinterpret_from.i64"),
|
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 {
|
Operator::FConvertFromI {
|
||||||
input_ty,
|
input_ty,
|
||||||
output_ty,
|
output_ty,
|
||||||
@@ -807,25 +772,12 @@ where
|
|||||||
output_ty,
|
output_ty,
|
||||||
Type::<Int>::Float(*input_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!(
|
Operator::Extend { sign } => write!(
|
||||||
f,
|
f,
|
||||||
"{}.extend_from.{}",
|
"{}.extend_from.{}",
|
||||||
SignfulInt(*sign, Size::_64),
|
SignfulInt(*sign, Size::_64),
|
||||||
SignfulInt(*sign, Size::_32)
|
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]
|
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!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_type(&self, global_index: u32) -> &Self::GlobalType {
|
fn global_type(&self, _global_index: u32) -> &Self::GlobalType {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,15 +496,15 @@ impl ModuleContext for SimpleContext {
|
|||||||
&self.types[index as usize]
|
&self.types[index as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmglobal_definition(&self, index: u32) -> u32 {
|
fn vmctx_vmglobal_definition(&self, _index: u32) -> u32 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmglobal_import_from(&self, index: u32) -> u32 {
|
fn vmctx_vmglobal_import_from(&self, _index: u32) -> u32 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn defined_memory_index(&self, index: u32) -> Option<u32> {
|
fn defined_memory_index(&self, _index: u32) -> Option<u32> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,21 +512,21 @@ impl ModuleContext for SimpleContext {
|
|||||||
Some(index)
|
Some(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmfunction_import_body(&self, func_index: u32) -> u32 {
|
fn vmctx_vmfunction_import_body(&self, _func_index: u32) -> u32 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn vmctx_vmfunction_import_vmctx(&self, func_index: u32) -> u32 {
|
fn vmctx_vmfunction_import_vmctx(&self, _func_index: u32) -> u32 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmtable_import_from(&self, table_index: u32) -> u32 {
|
fn vmctx_vmtable_import_from(&self, _table_index: u32) -> u32 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmmemory_definition(&self, defined_memory_index: u32) -> u32 {
|
fn vmctx_vmmemory_definition(&self, _defined_memory_index: u32) -> u32 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn vmctx_vmmemory_import_from(&self, memory_index: u32) -> u32 {
|
fn vmctx_vmmemory_import_from(&self, _memory_index: u32) -> u32 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn vmmemory_definition_base(&self) -> u8 {
|
fn vmmemory_definition_base(&self) -> u8 {
|
||||||
@@ -536,22 +536,27 @@ impl ModuleContext for SimpleContext {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32 {
|
fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32 {
|
||||||
|
assert_eq!(defined_memory_index, 0);
|
||||||
VmCtx::offset_of_memory_ptr()
|
VmCtx::offset_of_memory_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32 {
|
fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32 {
|
||||||
|
assert_eq!(defined_memory_index, 0);
|
||||||
VmCtx::offset_of_memory_len()
|
VmCtx::offset_of_memory_len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmtable_definition(&self, defined_table_index: u32) -> u32 {
|
fn vmctx_vmtable_definition(&self, defined_table_index: u32) -> u32 {
|
||||||
|
assert_eq!(defined_table_index, 0);
|
||||||
VmCtx::offset_of_funcs_ptr() as _
|
VmCtx::offset_of_funcs_ptr() as _
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmtable_definition_base(&self, defined_table_index: u32) -> u32 {
|
fn vmctx_vmtable_definition_base(&self, defined_table_index: u32) -> u32 {
|
||||||
|
assert_eq!(defined_table_index, 0);
|
||||||
VmCtx::offset_of_funcs_ptr() as _
|
VmCtx::offset_of_funcs_ptr() as _
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vmctx_vmtable_definition_current_elements(&self, defined_table_index: u32) -> u32 {
|
fn vmctx_vmtable_definition_current_elements(&self, defined_table_index: u32) -> u32 {
|
||||||
|
assert_eq!(defined_table_index, 0);
|
||||||
VmCtx::offset_of_funcs_len() as _
|
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);
|
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]
|
#[test]
|
||||||
fn block() {
|
fn block() {
|
||||||
let code = r#"
|
let code = r#"
|
||||||
@@ -801,148 +632,6 @@ fn brif_block() {
|
|||||||
assert_eq!(execute_wat(code, 5, 7), 12);
|
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
|
// Tests that br_if keeps values in the case if the branch
|
||||||
// hasn't been taken.
|
// hasn't been taken.
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1284,309 +973,6 @@ fn br_table() {
|
|||||||
assert_eq!(translated.execute_func::<_, u32>(0, (8u32,)), Ok(126));
|
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 {
|
macro_rules! test_select {
|
||||||
($name:ident, $ty:ident) => {
|
($name:ident, $ty:ident) => {
|
||||||
mod $name {
|
mod $name {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use crate::backend::{CodeGenSession, TranslatedCodeSection};
|
use crate::backend::{CodeGenSession, TranslatedCodeSection};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::function_body;
|
use crate::function_body;
|
||||||
use crate::microwasm::{MicrowasmConv, Type as MWType};
|
use crate::module::SimpleContext;
|
||||||
use crate::module::{ModuleContext, SimpleContext};
|
|
||||||
use cranelift_codegen::{binemit, ir};
|
use cranelift_codegen::{binemit, ir};
|
||||||
#[allow(unused_imports)] // for now
|
#[allow(unused_imports)] // for now
|
||||||
use wasmparser::{
|
use wasmparser::{
|
||||||
|
|||||||
Reference in New Issue
Block a user