Remove `Box<[Target]>` from `Instruction` (#141)
This also allows `Instruction` to be `Copy`, which massively speeds up `<Instructions as Clone>::clone` since it can now just `memcpy` the bytes using SIMD instead of having to switch on every single element. I haven't looked at the disassembly of `InstructionIter::next` yet, it could be that there are even more improvements yet to be gained from either: * Only doing work on `BrTable` (this might already be the case depending on the whims of the optimiser) * Using `unsafe` to make it a noop (we really don't want to do this, obviously, since it means that `Instructions` has to be immovable)
This commit is contained in:
parent
7b4c648acb
commit
e11ba15373
465
src/isa.rs
465
src/isa.rs
|
@ -74,7 +74,6 @@ use alloc::prelude::*;
|
|||
///
|
||||
/// Note that this is a `enum` since Wasm doesn't support multiple return
|
||||
/// values at the moment.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Keep {
|
||||
None,
|
||||
|
@ -90,30 +89,46 @@ pub struct DropKeep {
|
|||
pub keep: Keep,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Target {
|
||||
pub dst_pc: u32,
|
||||
pub drop_keep: DropKeep,
|
||||
}
|
||||
|
||||
/// A relocation entry that specifies.
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Reloc {
|
||||
/// Patch the destination of the branch instruction (br, br_eqz, br_nez)
|
||||
/// at the specified pc.
|
||||
Br {
|
||||
pc: u32,
|
||||
},
|
||||
Br { pc: u32 },
|
||||
/// Patch the specified destination index inside of br_table instruction at
|
||||
/// the specified pc.
|
||||
BrTable {
|
||||
pc: u32,
|
||||
idx: usize,
|
||||
},
|
||||
BrTable { pc: u32, idx: usize },
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct BrTargets<'a> {
|
||||
stream: &'a [InstructionInternal],
|
||||
}
|
||||
|
||||
impl<'a> BrTargets<'a> {
|
||||
pub(crate) fn from_internal(targets: &'a [InstructionInternal]) -> Self {
|
||||
BrTargets { stream: targets }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, index: u32) -> Target {
|
||||
match self.stream[index.min(self.stream.len() as u32 - 1) as usize] {
|
||||
InstructionInternal::BrTableTarget(target) => target,
|
||||
_ => panic!("BrTable has incorrect target count"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The main interpreted instruction type. This is what is returned by `InstructionIter`, but
|
||||
/// it is not what is stored internally. For that, see `InstructionInternal`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Instruction {
|
||||
pub enum Instruction<'a> {
|
||||
/// Push a local variable or an argument from the specified depth.
|
||||
GetLocal(u32),
|
||||
|
||||
|
@ -138,7 +153,202 @@ pub enum Instruction {
|
|||
/// is greater than length of the branch table, then the last index will be used.
|
||||
///
|
||||
/// Validation ensures that there should be at least one target.
|
||||
BrTable(Box<[Target]>),
|
||||
BrTable(BrTargets<'a>),
|
||||
|
||||
Unreachable,
|
||||
Return(DropKeep),
|
||||
|
||||
Call(u32),
|
||||
CallIndirect(u32),
|
||||
|
||||
Drop,
|
||||
Select,
|
||||
|
||||
GetGlobal(u32),
|
||||
SetGlobal(u32),
|
||||
|
||||
I32Load(u32),
|
||||
I64Load(u32),
|
||||
F32Load(u32),
|
||||
F64Load(u32),
|
||||
I32Load8S(u32),
|
||||
I32Load8U(u32),
|
||||
I32Load16S(u32),
|
||||
I32Load16U(u32),
|
||||
I64Load8S(u32),
|
||||
I64Load8U(u32),
|
||||
I64Load16S(u32),
|
||||
I64Load16U(u32),
|
||||
I64Load32S(u32),
|
||||
I64Load32U(u32),
|
||||
I32Store(u32),
|
||||
I64Store(u32),
|
||||
F32Store(u32),
|
||||
F64Store(u32),
|
||||
I32Store8(u32),
|
||||
I32Store16(u32),
|
||||
I64Store8(u32),
|
||||
I64Store16(u32),
|
||||
I64Store32(u32),
|
||||
|
||||
CurrentMemory,
|
||||
GrowMemory,
|
||||
|
||||
I32Const(i32),
|
||||
I64Const(i64),
|
||||
F32Const(u32),
|
||||
F64Const(u64),
|
||||
|
||||
I32Eqz,
|
||||
I32Eq,
|
||||
I32Ne,
|
||||
I32LtS,
|
||||
I32LtU,
|
||||
I32GtS,
|
||||
I32GtU,
|
||||
I32LeS,
|
||||
I32LeU,
|
||||
I32GeS,
|
||||
I32GeU,
|
||||
|
||||
I64Eqz,
|
||||
I64Eq,
|
||||
I64Ne,
|
||||
I64LtS,
|
||||
I64LtU,
|
||||
I64GtS,
|
||||
I64GtU,
|
||||
I64LeS,
|
||||
I64LeU,
|
||||
I64GeS,
|
||||
I64GeU,
|
||||
|
||||
F32Eq,
|
||||
F32Ne,
|
||||
F32Lt,
|
||||
F32Gt,
|
||||
F32Le,
|
||||
F32Ge,
|
||||
|
||||
F64Eq,
|
||||
F64Ne,
|
||||
F64Lt,
|
||||
F64Gt,
|
||||
F64Le,
|
||||
F64Ge,
|
||||
|
||||
I32Clz,
|
||||
I32Ctz,
|
||||
I32Popcnt,
|
||||
I32Add,
|
||||
I32Sub,
|
||||
I32Mul,
|
||||
I32DivS,
|
||||
I32DivU,
|
||||
I32RemS,
|
||||
I32RemU,
|
||||
I32And,
|
||||
I32Or,
|
||||
I32Xor,
|
||||
I32Shl,
|
||||
I32ShrS,
|
||||
I32ShrU,
|
||||
I32Rotl,
|
||||
I32Rotr,
|
||||
|
||||
I64Clz,
|
||||
I64Ctz,
|
||||
I64Popcnt,
|
||||
I64Add,
|
||||
I64Sub,
|
||||
I64Mul,
|
||||
I64DivS,
|
||||
I64DivU,
|
||||
I64RemS,
|
||||
I64RemU,
|
||||
I64And,
|
||||
I64Or,
|
||||
I64Xor,
|
||||
I64Shl,
|
||||
I64ShrS,
|
||||
I64ShrU,
|
||||
I64Rotl,
|
||||
I64Rotr,
|
||||
F32Abs,
|
||||
F32Neg,
|
||||
F32Ceil,
|
||||
F32Floor,
|
||||
F32Trunc,
|
||||
F32Nearest,
|
||||
F32Sqrt,
|
||||
F32Add,
|
||||
F32Sub,
|
||||
F32Mul,
|
||||
F32Div,
|
||||
F32Min,
|
||||
F32Max,
|
||||
F32Copysign,
|
||||
F64Abs,
|
||||
F64Neg,
|
||||
F64Ceil,
|
||||
F64Floor,
|
||||
F64Trunc,
|
||||
F64Nearest,
|
||||
F64Sqrt,
|
||||
F64Add,
|
||||
F64Sub,
|
||||
F64Mul,
|
||||
F64Div,
|
||||
F64Min,
|
||||
F64Max,
|
||||
F64Copysign,
|
||||
|
||||
I32WrapI64,
|
||||
I32TruncSF32,
|
||||
I32TruncUF32,
|
||||
I32TruncSF64,
|
||||
I32TruncUF64,
|
||||
I64ExtendSI32,
|
||||
I64ExtendUI32,
|
||||
I64TruncSF32,
|
||||
I64TruncUF32,
|
||||
I64TruncSF64,
|
||||
I64TruncUF64,
|
||||
F32ConvertSI32,
|
||||
F32ConvertUI32,
|
||||
F32ConvertSI64,
|
||||
F32ConvertUI64,
|
||||
F32DemoteF64,
|
||||
F64ConvertSI32,
|
||||
F64ConvertUI32,
|
||||
F64ConvertSI64,
|
||||
F64ConvertUI64,
|
||||
F64PromoteF32,
|
||||
|
||||
I32ReinterpretF32,
|
||||
I64ReinterpretF64,
|
||||
F32ReinterpretI32,
|
||||
F64ReinterpretI64,
|
||||
}
|
||||
|
||||
/// The internally-stored instruction type. This differs from `Instruction` in that the `BrTable`
|
||||
/// target list is "unrolled" into seperate instructions in order to be able to A) improve cache
|
||||
/// usage and B) allow this struct to be `Copy` and therefore allow `Instructions::clone` to be
|
||||
/// a `memcpy`. It also means that `Instructions::drop` is trivial. The overall speedup on some
|
||||
/// benchmarks is as high as 13%.
|
||||
///
|
||||
/// When returning instructions we convert to `Instruction`, whose `BrTable` variant internally
|
||||
/// borrows the list of instructions and returns targets by reading it.
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum InstructionInternal {
|
||||
GetLocal(u32),
|
||||
SetLocal(u32),
|
||||
TeeLocal(u32),
|
||||
Br(Target),
|
||||
BrIfEqz(Target),
|
||||
BrIfNez(Target),
|
||||
BrTable { count: u32 },
|
||||
BrTableTarget(Target),
|
||||
|
||||
Unreachable,
|
||||
Return(DropKeep),
|
||||
|
@ -318,7 +528,7 @@ pub enum Instruction {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Instructions {
|
||||
vec: Vec<Instruction>,
|
||||
vec: Vec<InstructionInternal>,
|
||||
}
|
||||
|
||||
impl Instructions {
|
||||
|
@ -332,27 +542,27 @@ impl Instructions {
|
|||
self.vec.len() as u32
|
||||
}
|
||||
|
||||
pub fn push(&mut self, instruction: Instruction) {
|
||||
pub(crate) fn push(&mut self, instruction: InstructionInternal) {
|
||||
self.vec.push(instruction);
|
||||
}
|
||||
|
||||
pub fn patch_relocation(&mut self, reloc: Reloc, dst_pc: u32) {
|
||||
match reloc {
|
||||
Reloc::Br { pc } => match self.vec[pc as usize] {
|
||||
Instruction::Br(ref mut target)
|
||||
| Instruction::BrIfEqz(ref mut target)
|
||||
| Instruction::BrIfNez(ref mut target) => target.dst_pc = dst_pc,
|
||||
InstructionInternal::Br(ref mut target)
|
||||
| InstructionInternal::BrIfEqz(ref mut target)
|
||||
| InstructionInternal::BrIfNez(ref mut target) => target.dst_pc = dst_pc,
|
||||
_ => panic!("branch relocation points to a non-branch instruction"),
|
||||
},
|
||||
Reloc::BrTable { pc, idx } => match self.vec[pc as usize] {
|
||||
Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc,
|
||||
Reloc::BrTable { pc, idx } => match &mut self.vec[pc as usize + idx + 1] {
|
||||
InstructionInternal::BrTableTarget(target) => target.dst_pc = dst_pc,
|
||||
_ => panic!("brtable relocation points to not brtable instruction"),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iterate_from(&self, position: u32) -> InstructionIter {
|
||||
InstructionIter{
|
||||
InstructionIter {
|
||||
instructions: &self.vec,
|
||||
position,
|
||||
}
|
||||
|
@ -360,7 +570,7 @@ impl Instructions {
|
|||
}
|
||||
|
||||
pub struct InstructionIter<'a> {
|
||||
instructions: &'a [Instruction],
|
||||
instructions: &'a [InstructionInternal],
|
||||
position: u32,
|
||||
}
|
||||
|
||||
|
@ -372,13 +582,212 @@ impl<'a> InstructionIter<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Iterator for InstructionIter<'a> {
|
||||
type Item = &'a Instruction;
|
||||
type Item = Instruction<'a>;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
||||
self.instructions.get(self.position as usize).map(|instruction| {
|
||||
self.position += 1;
|
||||
instruction
|
||||
})
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let internal = if let Some(i) = self.instructions.get(self.position as usize) {
|
||||
i
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let out = match *internal {
|
||||
InstructionInternal::GetLocal(x) => Instruction::GetLocal(x),
|
||||
InstructionInternal::SetLocal(x) => Instruction::SetLocal(x),
|
||||
InstructionInternal::TeeLocal(x) => Instruction::TeeLocal(x),
|
||||
InstructionInternal::Br(x) => Instruction::Br(x),
|
||||
InstructionInternal::BrIfEqz(x) => Instruction::BrIfEqz(x),
|
||||
InstructionInternal::BrIfNez(x) => Instruction::BrIfNez(x),
|
||||
InstructionInternal::BrTable { count } => {
|
||||
let start = self.position as usize + 1;
|
||||
|
||||
self.position += count;
|
||||
|
||||
Instruction::BrTable(BrTargets::from_internal(
|
||||
&self.instructions[start..start + count as usize],
|
||||
))
|
||||
}
|
||||
InstructionInternal::BrTableTarget(_) => panic!("Executed BrTableTarget"),
|
||||
|
||||
InstructionInternal::Unreachable => Instruction::Unreachable,
|
||||
InstructionInternal::Return(x) => Instruction::Return(x),
|
||||
|
||||
InstructionInternal::Call(x) => Instruction::Call(x),
|
||||
InstructionInternal::CallIndirect(x) => Instruction::CallIndirect(x),
|
||||
|
||||
InstructionInternal::Drop => Instruction::Drop,
|
||||
InstructionInternal::Select => Instruction::Select,
|
||||
|
||||
InstructionInternal::GetGlobal(x) => Instruction::GetGlobal(x),
|
||||
InstructionInternal::SetGlobal(x) => Instruction::SetGlobal(x),
|
||||
|
||||
InstructionInternal::I32Load(x) => Instruction::I32Load(x),
|
||||
InstructionInternal::I64Load(x) => Instruction::I64Load(x),
|
||||
InstructionInternal::F32Load(x) => Instruction::F32Load(x),
|
||||
InstructionInternal::F64Load(x) => Instruction::F64Load(x),
|
||||
InstructionInternal::I32Load8S(x) => Instruction::I32Load8S(x),
|
||||
InstructionInternal::I32Load8U(x) => Instruction::I32Load8U(x),
|
||||
InstructionInternal::I32Load16S(x) => Instruction::I32Load16S(x),
|
||||
InstructionInternal::I32Load16U(x) => Instruction::I32Load16U(x),
|
||||
InstructionInternal::I64Load8S(x) => Instruction::I64Load8S(x),
|
||||
InstructionInternal::I64Load8U(x) => Instruction::I64Load8U(x),
|
||||
InstructionInternal::I64Load16S(x) => Instruction::I64Load16S(x),
|
||||
InstructionInternal::I64Load16U(x) => Instruction::I64Load16U(x),
|
||||
InstructionInternal::I64Load32S(x) => Instruction::I64Load32S(x),
|
||||
InstructionInternal::I64Load32U(x) => Instruction::I64Load32U(x),
|
||||
InstructionInternal::I32Store(x) => Instruction::I32Store(x),
|
||||
InstructionInternal::I64Store(x) => Instruction::I64Store(x),
|
||||
InstructionInternal::F32Store(x) => Instruction::F32Store(x),
|
||||
InstructionInternal::F64Store(x) => Instruction::F64Store(x),
|
||||
InstructionInternal::I32Store8(x) => Instruction::I32Store8(x),
|
||||
InstructionInternal::I32Store16(x) => Instruction::I32Store16(x),
|
||||
InstructionInternal::I64Store8(x) => Instruction::I64Store8(x),
|
||||
InstructionInternal::I64Store16(x) => Instruction::I64Store16(x),
|
||||
InstructionInternal::I64Store32(x) => Instruction::I64Store32(x),
|
||||
|
||||
InstructionInternal::CurrentMemory => Instruction::CurrentMemory,
|
||||
InstructionInternal::GrowMemory => Instruction::GrowMemory,
|
||||
|
||||
InstructionInternal::I32Const(x) => Instruction::I32Const(x),
|
||||
InstructionInternal::I64Const(x) => Instruction::I64Const(x),
|
||||
InstructionInternal::F32Const(x) => Instruction::F32Const(x),
|
||||
InstructionInternal::F64Const(x) => Instruction::F64Const(x),
|
||||
|
||||
InstructionInternal::I32Eqz => Instruction::I32Eqz,
|
||||
InstructionInternal::I32Eq => Instruction::I32Eq,
|
||||
InstructionInternal::I32Ne => Instruction::I32Ne,
|
||||
InstructionInternal::I32LtS => Instruction::I32LtS,
|
||||
InstructionInternal::I32LtU => Instruction::I32LtU,
|
||||
InstructionInternal::I32GtS => Instruction::I32GtS,
|
||||
InstructionInternal::I32GtU => Instruction::I32GtU,
|
||||
InstructionInternal::I32LeS => Instruction::I32LeS,
|
||||
InstructionInternal::I32LeU => Instruction::I32LeU,
|
||||
InstructionInternal::I32GeS => Instruction::I32GeS,
|
||||
InstructionInternal::I32GeU => Instruction::I32GeU,
|
||||
|
||||
InstructionInternal::I64Eqz => Instruction::I64Eqz,
|
||||
InstructionInternal::I64Eq => Instruction::I64Eq,
|
||||
InstructionInternal::I64Ne => Instruction::I64Ne,
|
||||
InstructionInternal::I64LtS => Instruction::I64LtS,
|
||||
InstructionInternal::I64LtU => Instruction::I64LtU,
|
||||
InstructionInternal::I64GtS => Instruction::I64GtS,
|
||||
InstructionInternal::I64GtU => Instruction::I64GtU,
|
||||
InstructionInternal::I64LeS => Instruction::I64LeS,
|
||||
InstructionInternal::I64LeU => Instruction::I64LeU,
|
||||
InstructionInternal::I64GeS => Instruction::I64GeS,
|
||||
InstructionInternal::I64GeU => Instruction::I64GeU,
|
||||
|
||||
InstructionInternal::F32Eq => Instruction::F32Eq,
|
||||
InstructionInternal::F32Ne => Instruction::F32Ne,
|
||||
InstructionInternal::F32Lt => Instruction::F32Lt,
|
||||
InstructionInternal::F32Gt => Instruction::F32Gt,
|
||||
InstructionInternal::F32Le => Instruction::F32Le,
|
||||
InstructionInternal::F32Ge => Instruction::F32Ge,
|
||||
|
||||
InstructionInternal::F64Eq => Instruction::F64Eq,
|
||||
InstructionInternal::F64Ne => Instruction::F64Ne,
|
||||
InstructionInternal::F64Lt => Instruction::F64Lt,
|
||||
InstructionInternal::F64Gt => Instruction::F64Gt,
|
||||
InstructionInternal::F64Le => Instruction::F64Le,
|
||||
InstructionInternal::F64Ge => Instruction::F64Ge,
|
||||
|
||||
InstructionInternal::I32Clz => Instruction::I32Clz,
|
||||
InstructionInternal::I32Ctz => Instruction::I32Ctz,
|
||||
InstructionInternal::I32Popcnt => Instruction::I32Popcnt,
|
||||
InstructionInternal::I32Add => Instruction::I32Add,
|
||||
InstructionInternal::I32Sub => Instruction::I32Sub,
|
||||
InstructionInternal::I32Mul => Instruction::I32Mul,
|
||||
InstructionInternal::I32DivS => Instruction::I32DivS,
|
||||
InstructionInternal::I32DivU => Instruction::I32DivU,
|
||||
InstructionInternal::I32RemS => Instruction::I32RemS,
|
||||
InstructionInternal::I32RemU => Instruction::I32RemU,
|
||||
InstructionInternal::I32And => Instruction::I32And,
|
||||
InstructionInternal::I32Or => Instruction::I32Or,
|
||||
InstructionInternal::I32Xor => Instruction::I32Xor,
|
||||
InstructionInternal::I32Shl => Instruction::I32Shl,
|
||||
InstructionInternal::I32ShrS => Instruction::I32ShrS,
|
||||
InstructionInternal::I32ShrU => Instruction::I32ShrU,
|
||||
InstructionInternal::I32Rotl => Instruction::I32Rotl,
|
||||
InstructionInternal::I32Rotr => Instruction::I32Rotr,
|
||||
|
||||
InstructionInternal::I64Clz => Instruction::I64Clz,
|
||||
InstructionInternal::I64Ctz => Instruction::I64Ctz,
|
||||
InstructionInternal::I64Popcnt => Instruction::I64Popcnt,
|
||||
InstructionInternal::I64Add => Instruction::I64Add,
|
||||
InstructionInternal::I64Sub => Instruction::I64Sub,
|
||||
InstructionInternal::I64Mul => Instruction::I64Mul,
|
||||
InstructionInternal::I64DivS => Instruction::I64DivS,
|
||||
InstructionInternal::I64DivU => Instruction::I64DivU,
|
||||
InstructionInternal::I64RemS => Instruction::I64RemS,
|
||||
InstructionInternal::I64RemU => Instruction::I64RemU,
|
||||
InstructionInternal::I64And => Instruction::I64And,
|
||||
InstructionInternal::I64Or => Instruction::I64Or,
|
||||
InstructionInternal::I64Xor => Instruction::I64Xor,
|
||||
InstructionInternal::I64Shl => Instruction::I64Shl,
|
||||
InstructionInternal::I64ShrS => Instruction::I64ShrS,
|
||||
InstructionInternal::I64ShrU => Instruction::I64ShrU,
|
||||
InstructionInternal::I64Rotl => Instruction::I64Rotl,
|
||||
InstructionInternal::I64Rotr => Instruction::I64Rotr,
|
||||
InstructionInternal::F32Abs => Instruction::F32Abs,
|
||||
InstructionInternal::F32Neg => Instruction::F32Neg,
|
||||
InstructionInternal::F32Ceil => Instruction::F32Ceil,
|
||||
InstructionInternal::F32Floor => Instruction::F32Floor,
|
||||
InstructionInternal::F32Trunc => Instruction::F32Trunc,
|
||||
InstructionInternal::F32Nearest => Instruction::F32Nearest,
|
||||
InstructionInternal::F32Sqrt => Instruction::F32Sqrt,
|
||||
InstructionInternal::F32Add => Instruction::F32Add,
|
||||
InstructionInternal::F32Sub => Instruction::F32Sub,
|
||||
InstructionInternal::F32Mul => Instruction::F32Mul,
|
||||
InstructionInternal::F32Div => Instruction::F32Div,
|
||||
InstructionInternal::F32Min => Instruction::F32Min,
|
||||
InstructionInternal::F32Max => Instruction::F32Max,
|
||||
InstructionInternal::F32Copysign => Instruction::F32Copysign,
|
||||
InstructionInternal::F64Abs => Instruction::F64Abs,
|
||||
InstructionInternal::F64Neg => Instruction::F64Neg,
|
||||
InstructionInternal::F64Ceil => Instruction::F64Ceil,
|
||||
InstructionInternal::F64Floor => Instruction::F64Floor,
|
||||
InstructionInternal::F64Trunc => Instruction::F64Trunc,
|
||||
InstructionInternal::F64Nearest => Instruction::F64Nearest,
|
||||
InstructionInternal::F64Sqrt => Instruction::F64Sqrt,
|
||||
InstructionInternal::F64Add => Instruction::F64Add,
|
||||
InstructionInternal::F64Sub => Instruction::F64Sub,
|
||||
InstructionInternal::F64Mul => Instruction::F64Mul,
|
||||
InstructionInternal::F64Div => Instruction::F64Div,
|
||||
InstructionInternal::F64Min => Instruction::F64Min,
|
||||
InstructionInternal::F64Max => Instruction::F64Max,
|
||||
InstructionInternal::F64Copysign => Instruction::F64Copysign,
|
||||
|
||||
InstructionInternal::I32WrapI64 => Instruction::I32WrapI64,
|
||||
InstructionInternal::I32TruncSF32 => Instruction::I32TruncSF32,
|
||||
InstructionInternal::I32TruncUF32 => Instruction::I32TruncUF32,
|
||||
InstructionInternal::I32TruncSF64 => Instruction::I32TruncSF64,
|
||||
InstructionInternal::I32TruncUF64 => Instruction::I32TruncUF64,
|
||||
InstructionInternal::I64ExtendSI32 => Instruction::I64ExtendSI32,
|
||||
InstructionInternal::I64ExtendUI32 => Instruction::I64ExtendUI32,
|
||||
InstructionInternal::I64TruncSF32 => Instruction::I64TruncSF32,
|
||||
InstructionInternal::I64TruncUF32 => Instruction::I64TruncUF32,
|
||||
InstructionInternal::I64TruncSF64 => Instruction::I64TruncSF64,
|
||||
InstructionInternal::I64TruncUF64 => Instruction::I64TruncUF64,
|
||||
InstructionInternal::F32ConvertSI32 => Instruction::F32ConvertSI32,
|
||||
InstructionInternal::F32ConvertUI32 => Instruction::F32ConvertUI32,
|
||||
InstructionInternal::F32ConvertSI64 => Instruction::F32ConvertSI64,
|
||||
InstructionInternal::F32ConvertUI64 => Instruction::F32ConvertUI64,
|
||||
InstructionInternal::F32DemoteF64 => Instruction::F32DemoteF64,
|
||||
InstructionInternal::F64ConvertSI32 => Instruction::F64ConvertSI32,
|
||||
InstructionInternal::F64ConvertUI32 => Instruction::F64ConvertUI32,
|
||||
InstructionInternal::F64ConvertSI64 => Instruction::F64ConvertSI64,
|
||||
InstructionInternal::F64ConvertUI64 => Instruction::F64ConvertUI64,
|
||||
InstructionInternal::F64PromoteF32 => Instruction::F64PromoteF32,
|
||||
|
||||
InstructionInternal::I32ReinterpretF32 => Instruction::I32ReinterpretF32,
|
||||
InstructionInternal::I64ReinterpretF64 => Instruction::I64ReinterpretF64,
|
||||
InstructionInternal::F32ReinterpretI32 => Instruction::F32ReinterpretI32,
|
||||
InstructionInternal::F64ReinterpretI64 => Instruction::F64ReinterpretI64,
|
||||
};
|
||||
|
||||
self.position += 1;
|
||||
|
||||
Some(out)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -338,10 +338,15 @@ impl Interpreter {
|
|||
instructions: &isa::Instructions,
|
||||
) -> Result<RunResult, TrapKind> {
|
||||
let mut iter = instructions.iterate_from(function_context.position);
|
||||
loop {
|
||||
let instruction = iter.next().expect("instruction");
|
||||
|
||||
match self.run_instruction(function_context, instruction)? {
|
||||
loop {
|
||||
let instruction = iter.next().expect(
|
||||
"Ran out of instructions, this should be impossible \
|
||||
since validation ensures that we either have an explicit \
|
||||
return or an implicit block `end`.",
|
||||
);
|
||||
|
||||
match self.run_instruction(function_context, &instruction)? {
|
||||
InstructionOutcome::RunNextInstruction => {}
|
||||
InstructionOutcome::Branch(target) => {
|
||||
iter = instructions.iterate_from(target.dst_pc);
|
||||
|
@ -370,10 +375,10 @@ impl Interpreter {
|
|||
match instruction {
|
||||
&isa::Instruction::Unreachable => self.run_unreachable(context),
|
||||
|
||||
&isa::Instruction::Br(ref target) => self.run_br(context, target.clone()),
|
||||
&isa::Instruction::BrIfEqz(ref target) => self.run_br_eqz(target.clone()),
|
||||
&isa::Instruction::BrIfNez(ref target) => self.run_br_nez(target.clone()),
|
||||
&isa::Instruction::BrTable(ref targets) => self.run_br_table(targets),
|
||||
&isa::Instruction::Br(target) => self.run_br(context, target.clone()),
|
||||
&isa::Instruction::BrIfEqz(target) => self.run_br_eqz(target.clone()),
|
||||
&isa::Instruction::BrIfNez(target) => self.run_br_nez(target.clone()),
|
||||
&isa::Instruction::BrTable(targets) => self.run_br_table(targets),
|
||||
&isa::Instruction::Return(drop_keep) => self.run_return(drop_keep),
|
||||
|
||||
&isa::Instruction::Call(index) => self.run_call(context, index),
|
||||
|
@ -615,17 +620,11 @@ impl Interpreter {
|
|||
}
|
||||
}
|
||||
|
||||
fn run_br_table(&mut self, table: &[isa::Target]) -> Result<InstructionOutcome, TrapKind> {
|
||||
fn run_br_table(&mut self, targets: isa::BrTargets) -> Result<InstructionOutcome, TrapKind> {
|
||||
let index: u32 = self.value_stack.pop_as();
|
||||
|
||||
let dst = if (index as usize) < table.len() - 1 {
|
||||
table[index as usize].clone()
|
||||
} else {
|
||||
table
|
||||
.last()
|
||||
.expect("Due to validation there should be at least one label")
|
||||
.clone()
|
||||
};
|
||||
let dst = targets.get(index);
|
||||
|
||||
Ok(InstructionOutcome::Branch(dst))
|
||||
}
|
||||
|
||||
|
@ -1261,8 +1260,8 @@ struct FunctionContext {
|
|||
|
||||
impl FunctionContext {
|
||||
pub fn new(function: FuncRef) -> Self {
|
||||
let module = match *function.as_internal() {
|
||||
FuncInstanceInternal::Internal { ref module, .. } => module.upgrade().expect("module deallocated"),
|
||||
let module = match function.as_internal() {
|
||||
FuncInstanceInternal::Internal { module, .. } => module.upgrade().expect("module deallocated"),
|
||||
FuncInstanceInternal::Host { .. } => panic!("Host functions can't be called as internally defined functions; Thus FunctionContext can be created only with internally defined functions; qed"),
|
||||
};
|
||||
let memory = module.memory_by_index(DEFAULT_MEMORY_INDEX);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +1,10 @@
|
|||
use super::{validate_module, ValidatedModule};
|
||||
use isa;
|
||||
use parity_wasm::builder::module;
|
||||
use parity_wasm::elements::{
|
||||
External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType,
|
||||
Instruction, Instructions, TableType, ValueType, BlockType, deserialize_buffer,
|
||||
Module,
|
||||
deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr,
|
||||
Instruction, Instructions, MemoryType, Module, TableType, ValueType,
|
||||
};
|
||||
use isa;
|
||||
use wabt;
|
||||
|
||||
#[test]
|
||||
|
@ -27,45 +26,34 @@ fn limits() {
|
|||
|
||||
for (min, max, is_valid) in test_cases {
|
||||
// defined table
|
||||
let m = module()
|
||||
.table()
|
||||
.with_min(min)
|
||||
.with_max(max)
|
||||
.build()
|
||||
.build();
|
||||
let m = module().table().with_min(min).with_max(max).build().build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
|
||||
// imported table
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(min, max))
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(min, max)),
|
||||
)).build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
|
||||
// defined memory
|
||||
let m = module()
|
||||
.memory()
|
||||
.with_min(min)
|
||||
.with_max(max)
|
||||
.build()
|
||||
.with_min(min)
|
||||
.with_max(max)
|
||||
.build()
|
||||
.build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
|
||||
// imported table
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(min, max))
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(min, max)),
|
||||
)).build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
}
|
||||
}
|
||||
|
@ -73,92 +61,63 @@ fn limits() {
|
|||
#[test]
|
||||
fn global_init_const() {
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(
|
||||
vec![Instruction::I32Const(42), Instruction::End]
|
||||
)
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// init expr type differs from declared global type
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I64, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I64, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_init_global() {
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false))
|
||||
)
|
||||
)
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||
)).with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// get_global can reference only previously defined globals
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// get_global can reference only const globals
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true))
|
||||
)
|
||||
)
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||
)).with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// get_global in init_expr can only refer to imported globals.
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, false),
|
||||
InitExpr::new(vec![Instruction::I32Const(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, false),
|
||||
InitExpr::new(vec![Instruction::I32Const(0), Instruction::End]),
|
||||
)).with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -166,35 +125,26 @@ fn global_init_global() {
|
|||
fn global_init_misc() {
|
||||
// without delimiting End opcode
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42)])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::I32Const(42)]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// empty init expr
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// not an constant opcode used
|
||||
let m = module()
|
||||
.with_global(
|
||||
GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::Unreachable, Instruction::End])
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_global(GlobalEntry::new(
|
||||
GlobalType::new(ValueType::I32, true),
|
||||
InitExpr::new(vec![Instruction::Unreachable, Instruction::End]),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -202,31 +152,25 @@ fn global_init_misc() {
|
|||
fn module_limits_validity() {
|
||||
// module cannot contain more than 1 memory atm.
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(10, None))
|
||||
)
|
||||
)
|
||||
.memory()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.with_import(ImportEntry::new(
|
||||
"core".into(),
|
||||
"memory".into(),
|
||||
External::Memory(MemoryType::new(10, None)),
|
||||
)).memory()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
|
||||
// module cannot contain more than 1 table atm.
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(10, None))
|
||||
)
|
||||
)
|
||||
.table()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.with_import(ImportEntry::new(
|
||||
"core".into(),
|
||||
"table".into(),
|
||||
External::Table(TableType::new(10, None)),
|
||||
)).table()
|
||||
.with_min(10)
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
@ -236,19 +180,27 @@ fn funcs() {
|
|||
// recursive function calls is legal.
|
||||
let m = module()
|
||||
.function()
|
||||
.signature().return_type().i32().build()
|
||||
.body().with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(1),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.signature()
|
||||
.return_type()
|
||||
.i32()
|
||||
.build()
|
||||
.body()
|
||||
.with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(1),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.function()
|
||||
.signature().return_type().i32().build()
|
||||
.body().with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(0),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.signature()
|
||||
.return_type()
|
||||
.i32()
|
||||
.build()
|
||||
.body()
|
||||
.with_instructions(Instructions::new(vec![
|
||||
Instruction::Call(0),
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
}
|
||||
|
@ -257,26 +209,20 @@ fn funcs() {
|
|||
fn globals() {
|
||||
// import immutable global is legal.
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false))
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
|
||||
// import mutable global is invalid.
|
||||
let m = module()
|
||||
.with_import(
|
||||
ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true))
|
||||
)
|
||||
)
|
||||
.build();
|
||||
.with_import(ImportEntry::new(
|
||||
"env".into(),
|
||||
"ext_global".into(),
|
||||
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||
)).build();
|
||||
assert!(validate_module(m).is_err());
|
||||
}
|
||||
|
||||
|
@ -284,21 +230,23 @@ fn globals() {
|
|||
fn if_else_with_return_type_validation() {
|
||||
let m = module()
|
||||
.function()
|
||||
.signature().build()
|
||||
.body().with_instructions(Instructions::new(vec![
|
||||
Instruction::I32Const(1),
|
||||
Instruction::If(BlockType::NoResult),
|
||||
Instruction::I32Const(1),
|
||||
Instruction::If(BlockType::Value(ValueType::I32)),
|
||||
Instruction::I32Const(1),
|
||||
Instruction::Else,
|
||||
Instruction::I32Const(2),
|
||||
Instruction::End,
|
||||
Instruction::Drop,
|
||||
Instruction::End,
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.signature()
|
||||
.build()
|
||||
.body()
|
||||
.with_instructions(Instructions::new(vec![
|
||||
Instruction::I32Const(1),
|
||||
Instruction::If(BlockType::NoResult),
|
||||
Instruction::I32Const(1),
|
||||
Instruction::If(BlockType::Value(ValueType::I32)),
|
||||
Instruction::I32Const(1),
|
||||
Instruction::Else,
|
||||
Instruction::I32Const(2),
|
||||
Instruction::End,
|
||||
Instruction::Drop,
|
||||
Instruction::End,
|
||||
Instruction::End,
|
||||
])).build()
|
||||
.build()
|
||||
.build();
|
||||
validate_module(m).unwrap();
|
||||
}
|
||||
|
@ -310,10 +258,8 @@ fn validate(wat: &str) -> ValidatedModule {
|
|||
validated_module
|
||||
}
|
||||
|
||||
fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
|
||||
let validated_module = validate(wat);
|
||||
let code = &validated_module.code_map[0];
|
||||
|
||||
fn compile(module: &ValidatedModule) -> (Vec<isa::Instruction>, Vec<u32>) {
|
||||
let code = &module.code_map[0];
|
||||
let mut instructions = Vec::new();
|
||||
let mut pcs = Vec::new();
|
||||
let mut iter = code.iterate_from(0);
|
||||
|
@ -323,41 +269,56 @@ fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
|
|||
instructions.push(instruction.clone());
|
||||
pcs.push(pc);
|
||||
} else {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(instructions, pcs)
|
||||
}
|
||||
|
||||
macro_rules! targets {
|
||||
($($target:expr),*) => {
|
||||
::isa::BrTargets::from_internal(
|
||||
&[$($target,)*]
|
||||
.iter()
|
||||
.map(|&target| ::isa::InstructionInternal::BrTableTarget(target))
|
||||
.collect::<Vec<_>>()[..]
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_no_value() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
})
|
||||
]
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
})]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_with_value() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (result i32)
|
||||
i32.const 0
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -372,32 +333,36 @@ fn implicit_return_with_value() {
|
|||
|
||||
#[test]
|
||||
fn implicit_return_param() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::None,
|
||||
}),]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_local() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -412,14 +377,17 @@ fn get_local() {
|
|||
|
||||
#[test]
|
||||
fn explicit_return() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
return
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -438,7 +406,8 @@ fn explicit_return() {
|
|||
|
||||
#[test]
|
||||
fn add_params() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (param i32) (result i32)
|
||||
get_local 0
|
||||
|
@ -446,7 +415,9 @@ fn add_params() {
|
|||
i32.add
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -468,7 +439,8 @@ fn add_params() {
|
|||
|
||||
#[test]
|
||||
fn drop_locals() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
(local i32)
|
||||
|
@ -476,7 +448,9 @@ fn drop_locals() {
|
|||
set_local 1
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -492,7 +466,8 @@ fn drop_locals() {
|
|||
|
||||
#[test]
|
||||
fn if_without_else() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
i32.const 1
|
||||
|
@ -503,7 +478,9 @@ fn if_without_else() {
|
|||
i32.const 3
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -517,7 +494,7 @@ fn if_without_else() {
|
|||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1, // 1 param
|
||||
drop: 1, // 1 param
|
||||
keep: isa::Keep::Single, // 1 result
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
|
@ -531,7 +508,8 @@ fn if_without_else() {
|
|||
|
||||
#[test]
|
||||
fn if_else() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
(local i32)
|
||||
|
@ -545,7 +523,9 @@ fn if_else() {
|
|||
end
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -578,7 +558,8 @@ fn if_else() {
|
|||
|
||||
#[test]
|
||||
fn if_else_returns_result() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -590,7 +571,9 @@ fn if_else_returns_result() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -622,7 +605,8 @@ fn if_else_returns_result() {
|
|||
|
||||
#[test]
|
||||
fn if_else_branch_from_true_branch() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -638,7 +622,9 @@ fn if_else_branch_from_true_branch() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -680,7 +666,8 @@ fn if_else_branch_from_true_branch() {
|
|||
|
||||
#[test]
|
||||
fn if_else_branch_from_false_branch() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
|
@ -696,7 +683,9 @@ fn if_else_branch_from_false_branch() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -738,7 +727,8 @@ fn if_else_branch_from_false_branch() {
|
|||
|
||||
#[test]
|
||||
fn loop_() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop (result i32)
|
||||
|
@ -749,7 +739,9 @@ fn loop_() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
@ -773,28 +765,30 @@ fn loop_() {
|
|||
|
||||
#[test]
|
||||
fn loop_empty() {
|
||||
let (code, _) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop
|
||||
end
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brtable() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1
|
||||
|
@ -805,29 +799,29 @@ fn brtable() {
|
|||
end
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(0),
|
||||
isa::Instruction::BrTable(
|
||||
vec![
|
||||
isa::Target {
|
||||
dst_pc: 0,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
isa::Instruction::BrTable(targets![
|
||||
isa::Target {
|
||||
dst_pc: 0,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: pcs[2],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: pcs[2],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
].into_boxed_slice()
|
||||
),
|
||||
}
|
||||
]),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
|
@ -838,7 +832,8 @@ fn brtable() {
|
|||
|
||||
#[test]
|
||||
fn brtable_returns_result() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1 (result i32)
|
||||
|
@ -852,30 +847,31 @@ fn brtable_returns_result() {
|
|||
drop
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
println!("{:?}", (&code, &pcs));
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(0),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrTable(
|
||||
vec![
|
||||
isa::Target {
|
||||
dst_pc: pcs[3],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
isa::Instruction::BrTable(targets![
|
||||
isa::Target {
|
||||
dst_pc: pcs[3],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
keep: isa::Keep::Single,
|
||||
drop: 0,
|
||||
},
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
keep: isa::Keep::Single,
|
||||
drop: 0,
|
||||
},
|
||||
].into_boxed_slice()
|
||||
),
|
||||
}
|
||||
]),
|
||||
isa::Instruction::Unreachable,
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
|
@ -888,7 +884,8 @@ fn brtable_returns_result() {
|
|||
|
||||
#[test]
|
||||
fn wabt_example() {
|
||||
let (code, pcs) = compile(r#"
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
block $exit
|
||||
|
@ -901,7 +898,9 @@ fn wabt_example() {
|
|||
return
|
||||
)
|
||||
)
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
|
|
Loading…
Reference in New Issue