Merge branch 'master' of github.com:paritytech/wasmi into common-bounded-stack
This commit is contained in:
commit
74b32aaebd
|
@ -6,6 +6,9 @@ language:
|
||||||
rust:
|
rust:
|
||||||
- nightly
|
- nightly
|
||||||
- stable
|
- stable
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- rust: nightly
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "wasmi"
|
name = "wasmi"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Svyatoslav Nikolsky <svyatonik@yandex.ru>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Svyatoslav Nikolsky <svyatonik@yandex.ru>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
463
src/isa.rs
463
src/isa.rs
|
@ -74,7 +74,6 @@ use alloc::prelude::*;
|
||||||
///
|
///
|
||||||
/// Note that this is a `enum` since Wasm doesn't support multiple return
|
/// Note that this is a `enum` since Wasm doesn't support multiple return
|
||||||
/// values at the moment.
|
/// values at the moment.
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Keep {
|
pub enum Keep {
|
||||||
None,
|
None,
|
||||||
|
@ -90,30 +89,46 @@ pub struct DropKeep {
|
||||||
pub keep: Keep,
|
pub keep: Keep,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Target {
|
pub struct Target {
|
||||||
pub dst_pc: u32,
|
pub dst_pc: u32,
|
||||||
pub drop_keep: DropKeep,
|
pub drop_keep: DropKeep,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A relocation entry that specifies.
|
/// A relocation entry that specifies.
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Reloc {
|
pub enum Reloc {
|
||||||
/// Patch the destination of the branch instruction (br, br_eqz, br_nez)
|
/// Patch the destination of the branch instruction (br, br_eqz, br_nez)
|
||||||
/// at the specified pc.
|
/// at the specified pc.
|
||||||
Br {
|
Br { pc: u32 },
|
||||||
pc: u32,
|
|
||||||
},
|
|
||||||
/// Patch the specified destination index inside of br_table instruction at
|
/// Patch the specified destination index inside of br_table instruction at
|
||||||
/// the specified pc.
|
/// the specified pc.
|
||||||
BrTable {
|
BrTable { pc: u32, idx: usize },
|
||||||
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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Instruction {
|
pub enum Instruction<'a> {
|
||||||
/// Push a local variable or an argument from the specified depth.
|
/// Push a local variable or an argument from the specified depth.
|
||||||
GetLocal(u32),
|
GetLocal(u32),
|
||||||
|
|
||||||
|
@ -138,7 +153,202 @@ pub enum Instruction {
|
||||||
/// is greater than length of the branch table, then the last index will be used.
|
/// 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.
|
/// 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,
|
Unreachable,
|
||||||
Return(DropKeep),
|
Return(DropKeep),
|
||||||
|
@ -318,7 +528,7 @@ pub enum Instruction {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Instructions {
|
pub struct Instructions {
|
||||||
vec: Vec<Instruction>,
|
vec: Vec<InstructionInternal>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instructions {
|
impl Instructions {
|
||||||
|
@ -332,27 +542,27 @@ impl Instructions {
|
||||||
self.vec.len() as u32
|
self.vec.len() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, instruction: Instruction) {
|
pub(crate) fn push(&mut self, instruction: InstructionInternal) {
|
||||||
self.vec.push(instruction);
|
self.vec.push(instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn patch_relocation(&mut self, reloc: Reloc, dst_pc: u32) {
|
pub fn patch_relocation(&mut self, reloc: Reloc, dst_pc: u32) {
|
||||||
match reloc {
|
match reloc {
|
||||||
Reloc::Br { pc } => match self.vec[pc as usize] {
|
Reloc::Br { pc } => match self.vec[pc as usize] {
|
||||||
Instruction::Br(ref mut target)
|
InstructionInternal::Br(ref mut target)
|
||||||
| Instruction::BrIfEqz(ref mut target)
|
| InstructionInternal::BrIfEqz(ref mut target)
|
||||||
| Instruction::BrIfNez(ref mut target) => target.dst_pc = dst_pc,
|
| InstructionInternal::BrIfNez(ref mut target) => target.dst_pc = dst_pc,
|
||||||
_ => panic!("branch relocation points to a non-branch instruction"),
|
_ => panic!("branch relocation points to a non-branch instruction"),
|
||||||
},
|
},
|
||||||
Reloc::BrTable { pc, idx } => match self.vec[pc as usize] {
|
Reloc::BrTable { pc, idx } => match &mut self.vec[pc as usize + idx + 1] {
|
||||||
Instruction::BrTable(ref mut targets) => targets[idx].dst_pc = dst_pc,
|
InstructionInternal::BrTableTarget(target) => target.dst_pc = dst_pc,
|
||||||
_ => panic!("brtable relocation points to not brtable instruction"),
|
_ => panic!("brtable relocation points to not brtable instruction"),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iterate_from(&self, position: u32) -> InstructionIter {
|
pub fn iterate_from(&self, position: u32) -> InstructionIter {
|
||||||
InstructionIter{
|
InstructionIter {
|
||||||
instructions: &self.vec,
|
instructions: &self.vec,
|
||||||
position,
|
position,
|
||||||
}
|
}
|
||||||
|
@ -360,7 +570,7 @@ impl Instructions {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstructionIter<'a> {
|
pub struct InstructionIter<'a> {
|
||||||
instructions: &'a [Instruction],
|
instructions: &'a [InstructionInternal],
|
||||||
position: u32,
|
position: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,13 +582,212 @@ impl<'a> InstructionIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for InstructionIter<'a> {
|
impl<'a> Iterator for InstructionIter<'a> {
|
||||||
type Item = &'a Instruction;
|
type Item = Instruction<'a>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.instructions.get(self.position as usize).map(|instruction| {
|
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;
|
self.position += 1;
|
||||||
instruction
|
|
||||||
})
|
Some(out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,10 +338,15 @@ impl Interpreter {
|
||||||
instructions: &isa::Instructions,
|
instructions: &isa::Instructions,
|
||||||
) -> Result<RunResult, TrapKind> {
|
) -> Result<RunResult, TrapKind> {
|
||||||
let mut iter = instructions.iterate_from(function_context.position);
|
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::RunNextInstruction => {}
|
||||||
InstructionOutcome::Branch(target) => {
|
InstructionOutcome::Branch(target) => {
|
||||||
iter = instructions.iterate_from(target.dst_pc);
|
iter = instructions.iterate_from(target.dst_pc);
|
||||||
|
@ -370,10 +375,10 @@ impl Interpreter {
|
||||||
match instruction {
|
match instruction {
|
||||||
&isa::Instruction::Unreachable => self.run_unreachable(context),
|
&isa::Instruction::Unreachable => self.run_unreachable(context),
|
||||||
|
|
||||||
&isa::Instruction::Br(ref target) => self.run_br(context, target.clone()),
|
&isa::Instruction::Br(target) => self.run_br(context, target.clone()),
|
||||||
&isa::Instruction::BrIfEqz(ref target) => self.run_br_eqz(target.clone()),
|
&isa::Instruction::BrIfEqz(target) => self.run_br_eqz(target.clone()),
|
||||||
&isa::Instruction::BrIfNez(ref target) => self.run_br_nez(target.clone()),
|
&isa::Instruction::BrIfNez(target) => self.run_br_nez(target.clone()),
|
||||||
&isa::Instruction::BrTable(ref targets) => self.run_br_table(targets),
|
&isa::Instruction::BrTable(targets) => self.run_br_table(targets),
|
||||||
&isa::Instruction::Return(drop_keep) => self.run_return(drop_keep),
|
&isa::Instruction::Return(drop_keep) => self.run_return(drop_keep),
|
||||||
|
|
||||||
&isa::Instruction::Call(index) => self.run_call(context, index),
|
&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 index: u32 = self.value_stack.pop_as();
|
||||||
|
|
||||||
let dst = if (index as usize) < table.len() - 1 {
|
let dst = targets.get(index);
|
||||||
table[index as usize].clone()
|
|
||||||
} else {
|
|
||||||
table
|
|
||||||
.last()
|
|
||||||
.expect("Due to validation there should be at least one label")
|
|
||||||
.clone()
|
|
||||||
};
|
|
||||||
Ok(InstructionOutcome::Branch(dst))
|
Ok(InstructionOutcome::Branch(dst))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1261,8 +1260,8 @@ struct FunctionContext {
|
||||||
|
|
||||||
impl FunctionContext {
|
impl FunctionContext {
|
||||||
pub fn new(function: FuncRef) -> Self {
|
pub fn new(function: FuncRef) -> Self {
|
||||||
let module = match *function.as_internal() {
|
let module = match function.as_internal() {
|
||||||
FuncInstanceInternal::Internal { ref module, .. } => module.upgrade().expect("module deallocated"),
|
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"),
|
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);
|
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 super::{validate_module, ValidatedModule};
|
||||||
|
use isa;
|
||||||
use parity_wasm::builder::module;
|
use parity_wasm::builder::module;
|
||||||
use parity_wasm::elements::{
|
use parity_wasm::elements::{
|
||||||
External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType,
|
deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr,
|
||||||
Instruction, Instructions, TableType, ValueType, BlockType, deserialize_buffer,
|
Instruction, Instructions, MemoryType, Module, TableType, ValueType,
|
||||||
Module,
|
|
||||||
};
|
};
|
||||||
use isa;
|
|
||||||
use wabt;
|
use wabt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -27,24 +26,16 @@ fn limits() {
|
||||||
|
|
||||||
for (min, max, is_valid) in test_cases {
|
for (min, max, is_valid) in test_cases {
|
||||||
// defined table
|
// defined table
|
||||||
let m = module()
|
let m = module().table().with_min(min).with_max(max).build().build();
|
||||||
.table()
|
|
||||||
.with_min(min)
|
|
||||||
.with_max(max)
|
|
||||||
.build()
|
|
||||||
.build();
|
|
||||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||||
|
|
||||||
// imported table
|
// imported table
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_import(
|
.with_import(ImportEntry::new(
|
||||||
ImportEntry::new(
|
|
||||||
"core".into(),
|
"core".into(),
|
||||||
"table".into(),
|
"table".into(),
|
||||||
External::Table(TableType::new(min, max))
|
External::Table(TableType::new(min, max)),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||||
|
|
||||||
// defined memory
|
// defined memory
|
||||||
|
@ -58,14 +49,11 @@ fn limits() {
|
||||||
|
|
||||||
// imported table
|
// imported table
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_import(
|
.with_import(ImportEntry::new(
|
||||||
ImportEntry::new(
|
|
||||||
"core".into(),
|
"core".into(),
|
||||||
"memory".into(),
|
"memory".into(),
|
||||||
External::Memory(MemoryType::new(min, max))
|
External::Memory(MemoryType::new(min, max)),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,92 +61,63 @@ fn limits() {
|
||||||
#[test]
|
#[test]
|
||||||
fn global_init_const() {
|
fn global_init_const() {
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_global(
|
.with_global(GlobalEntry::new(
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, true),
|
GlobalType::new(ValueType::I32, true),
|
||||||
InitExpr::new(
|
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||||
vec![Instruction::I32Const(42), Instruction::End]
|
)).build();
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_ok());
|
assert!(validate_module(m).is_ok());
|
||||||
|
|
||||||
// init expr type differs from declared global type
|
// init expr type differs from declared global type
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_global(
|
.with_global(GlobalEntry::new(
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I64, true),
|
GlobalType::new(ValueType::I64, true),
|
||||||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End])
|
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_err());
|
assert!(validate_module(m).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn global_init_global() {
|
fn global_init_global() {
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_import(
|
.with_import(ImportEntry::new(
|
||||||
ImportEntry::new(
|
|
||||||
"env".into(),
|
"env".into(),
|
||||||
"ext_global".into(),
|
"ext_global".into(),
|
||||||
External::Global(GlobalType::new(ValueType::I32, false))
|
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||||
)
|
)).with_global(GlobalEntry::new(
|
||||||
)
|
|
||||||
.with_global(
|
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, true),
|
GlobalType::new(ValueType::I32, true),
|
||||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_ok());
|
assert!(validate_module(m).is_ok());
|
||||||
|
|
||||||
// get_global can reference only previously defined globals
|
// get_global can reference only previously defined globals
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_global(
|
.with_global(GlobalEntry::new(
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, true),
|
GlobalType::new(ValueType::I32, true),
|
||||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_err());
|
assert!(validate_module(m).is_err());
|
||||||
|
|
||||||
// get_global can reference only const globals
|
// get_global can reference only const globals
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_import(
|
.with_import(ImportEntry::new(
|
||||||
ImportEntry::new(
|
|
||||||
"env".into(),
|
"env".into(),
|
||||||
"ext_global".into(),
|
"ext_global".into(),
|
||||||
External::Global(GlobalType::new(ValueType::I32, true))
|
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||||
)
|
)).with_global(GlobalEntry::new(
|
||||||
)
|
|
||||||
.with_global(
|
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, true),
|
GlobalType::new(ValueType::I32, true),
|
||||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_err());
|
assert!(validate_module(m).is_err());
|
||||||
|
|
||||||
// get_global in init_expr can only refer to imported globals.
|
// get_global in init_expr can only refer to imported globals.
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_global(
|
.with_global(GlobalEntry::new(
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, false),
|
GlobalType::new(ValueType::I32, false),
|
||||||
InitExpr::new(vec![Instruction::I32Const(0), Instruction::End])
|
InitExpr::new(vec![Instruction::I32Const(0), Instruction::End]),
|
||||||
)
|
)).with_global(GlobalEntry::new(
|
||||||
)
|
|
||||||
.with_global(
|
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, true),
|
GlobalType::new(ValueType::I32, true),
|
||||||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End])
|
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_err());
|
assert!(validate_module(m).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,35 +125,26 @@ fn global_init_global() {
|
||||||
fn global_init_misc() {
|
fn global_init_misc() {
|
||||||
// without delimiting End opcode
|
// without delimiting End opcode
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_global(
|
.with_global(GlobalEntry::new(
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, true),
|
GlobalType::new(ValueType::I32, true),
|
||||||
InitExpr::new(vec![Instruction::I32Const(42)])
|
InitExpr::new(vec![Instruction::I32Const(42)]),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_err());
|
assert!(validate_module(m).is_err());
|
||||||
|
|
||||||
// empty init expr
|
// empty init expr
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_global(
|
.with_global(GlobalEntry::new(
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, true),
|
GlobalType::new(ValueType::I32, true),
|
||||||
InitExpr::new(vec![Instruction::End])
|
InitExpr::new(vec![Instruction::End]),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_err());
|
assert!(validate_module(m).is_err());
|
||||||
|
|
||||||
// not an constant opcode used
|
// not an constant opcode used
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_global(
|
.with_global(GlobalEntry::new(
|
||||||
GlobalEntry::new(
|
|
||||||
GlobalType::new(ValueType::I32, true),
|
GlobalType::new(ValueType::I32, true),
|
||||||
InitExpr::new(vec![Instruction::Unreachable, Instruction::End])
|
InitExpr::new(vec![Instruction::Unreachable, Instruction::End]),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_err());
|
assert!(validate_module(m).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,14 +152,11 @@ fn global_init_misc() {
|
||||||
fn module_limits_validity() {
|
fn module_limits_validity() {
|
||||||
// module cannot contain more than 1 memory atm.
|
// module cannot contain more than 1 memory atm.
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_import(
|
.with_import(ImportEntry::new(
|
||||||
ImportEntry::new(
|
|
||||||
"core".into(),
|
"core".into(),
|
||||||
"memory".into(),
|
"memory".into(),
|
||||||
External::Memory(MemoryType::new(10, None))
|
External::Memory(MemoryType::new(10, None)),
|
||||||
)
|
)).memory()
|
||||||
)
|
|
||||||
.memory()
|
|
||||||
.with_min(10)
|
.with_min(10)
|
||||||
.build()
|
.build()
|
||||||
.build();
|
.build();
|
||||||
|
@ -217,14 +164,11 @@ fn module_limits_validity() {
|
||||||
|
|
||||||
// module cannot contain more than 1 table atm.
|
// module cannot contain more than 1 table atm.
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_import(
|
.with_import(ImportEntry::new(
|
||||||
ImportEntry::new(
|
|
||||||
"core".into(),
|
"core".into(),
|
||||||
"table".into(),
|
"table".into(),
|
||||||
External::Table(TableType::new(10, None))
|
External::Table(TableType::new(10, None)),
|
||||||
)
|
)).table()
|
||||||
)
|
|
||||||
.table()
|
|
||||||
.with_min(10)
|
.with_min(10)
|
||||||
.build()
|
.build()
|
||||||
.build();
|
.build();
|
||||||
|
@ -236,15 +180,23 @@ fn funcs() {
|
||||||
// recursive function calls is legal.
|
// recursive function calls is legal.
|
||||||
let m = module()
|
let m = module()
|
||||||
.function()
|
.function()
|
||||||
.signature().return_type().i32().build()
|
.signature()
|
||||||
.body().with_instructions(Instructions::new(vec![
|
.return_type()
|
||||||
|
.i32()
|
||||||
|
.build()
|
||||||
|
.body()
|
||||||
|
.with_instructions(Instructions::new(vec![
|
||||||
Instruction::Call(1),
|
Instruction::Call(1),
|
||||||
Instruction::End,
|
Instruction::End,
|
||||||
])).build()
|
])).build()
|
||||||
.build()
|
.build()
|
||||||
.function()
|
.function()
|
||||||
.signature().return_type().i32().build()
|
.signature()
|
||||||
.body().with_instructions(Instructions::new(vec![
|
.return_type()
|
||||||
|
.i32()
|
||||||
|
.build()
|
||||||
|
.body()
|
||||||
|
.with_instructions(Instructions::new(vec![
|
||||||
Instruction::Call(0),
|
Instruction::Call(0),
|
||||||
Instruction::End,
|
Instruction::End,
|
||||||
])).build()
|
])).build()
|
||||||
|
@ -257,26 +209,20 @@ fn funcs() {
|
||||||
fn globals() {
|
fn globals() {
|
||||||
// import immutable global is legal.
|
// import immutable global is legal.
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_import(
|
.with_import(ImportEntry::new(
|
||||||
ImportEntry::new(
|
|
||||||
"env".into(),
|
"env".into(),
|
||||||
"ext_global".into(),
|
"ext_global".into(),
|
||||||
External::Global(GlobalType::new(ValueType::I32, false))
|
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_ok());
|
assert!(validate_module(m).is_ok());
|
||||||
|
|
||||||
// import mutable global is invalid.
|
// import mutable global is invalid.
|
||||||
let m = module()
|
let m = module()
|
||||||
.with_import(
|
.with_import(ImportEntry::new(
|
||||||
ImportEntry::new(
|
|
||||||
"env".into(),
|
"env".into(),
|
||||||
"ext_global".into(),
|
"ext_global".into(),
|
||||||
External::Global(GlobalType::new(ValueType::I32, true))
|
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||||
)
|
)).build();
|
||||||
)
|
|
||||||
.build();
|
|
||||||
assert!(validate_module(m).is_err());
|
assert!(validate_module(m).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,8 +230,10 @@ fn globals() {
|
||||||
fn if_else_with_return_type_validation() {
|
fn if_else_with_return_type_validation() {
|
||||||
let m = module()
|
let m = module()
|
||||||
.function()
|
.function()
|
||||||
.signature().build()
|
.signature()
|
||||||
.body().with_instructions(Instructions::new(vec![
|
.build()
|
||||||
|
.body()
|
||||||
|
.with_instructions(Instructions::new(vec![
|
||||||
Instruction::I32Const(1),
|
Instruction::I32Const(1),
|
||||||
Instruction::If(BlockType::NoResult),
|
Instruction::If(BlockType::NoResult),
|
||||||
Instruction::I32Const(1),
|
Instruction::I32Const(1),
|
||||||
|
@ -310,10 +258,8 @@ fn validate(wat: &str) -> ValidatedModule {
|
||||||
validated_module
|
validated_module
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
|
fn compile(module: &ValidatedModule) -> (Vec<isa::Instruction>, Vec<u32>) {
|
||||||
let validated_module = validate(wat);
|
let code = &module.code_map[0];
|
||||||
let code = &validated_module.code_map[0];
|
|
||||||
|
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let mut pcs = Vec::new();
|
let mut pcs = Vec::new();
|
||||||
let mut iter = code.iterate_from(0);
|
let mut iter = code.iterate_from(0);
|
||||||
|
@ -323,41 +269,56 @@ fn compile(wat: &str) -> (Vec<isa::Instruction>, Vec<u32>) {
|
||||||
instructions.push(instruction.clone());
|
instructions.push(instruction.clone());
|
||||||
pcs.push(pc);
|
pcs.push(pc);
|
||||||
} else {
|
} else {
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(instructions, pcs)
|
(instructions, pcs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! targets {
|
||||||
|
($($target:expr),*) => {
|
||||||
|
::isa::BrTargets::from_internal(
|
||||||
|
&[$($target,)*]
|
||||||
|
.iter()
|
||||||
|
.map(|&target| ::isa::InstructionInternal::BrTableTarget(target))
|
||||||
|
.collect::<Vec<_>>()[..]
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn implicit_return_no_value() {
|
fn implicit_return_no_value() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![isa::Instruction::Return(isa::DropKeep {
|
||||||
isa::Instruction::Return(isa::DropKeep {
|
|
||||||
drop: 0,
|
drop: 0,
|
||||||
keep: isa::Keep::None,
|
keep: isa::Keep::None,
|
||||||
})
|
})]
|
||||||
]
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn implicit_return_with_value() {
|
fn implicit_return_with_value() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call") (result i32)
|
(func (export "call") (result i32)
|
||||||
i32.const 0
|
i32.const 0
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -372,32 +333,36 @@ fn implicit_return_with_value() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn implicit_return_param() {
|
fn implicit_return_param() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call") (param i32)
|
(func (export "call") (param i32)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![isa::Instruction::Return(isa::DropKeep {
|
||||||
isa::Instruction::Return(isa::DropKeep {
|
|
||||||
drop: 1,
|
drop: 1,
|
||||||
keep: isa::Keep::None,
|
keep: isa::Keep::None,
|
||||||
}),
|
}),]
|
||||||
]
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_local() {
|
fn get_local() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call") (param i32) (result i32)
|
(func (export "call") (param i32) (result i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -412,14 +377,17 @@ fn get_local() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn explicit_return() {
|
fn explicit_return() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call") (param i32) (result i32)
|
(func (export "call") (param i32) (result i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
return
|
return
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -438,7 +406,8 @@ fn explicit_return() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_params() {
|
fn add_params() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call") (param i32) (param i32) (result i32)
|
(func (export "call") (param i32) (param i32) (result i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
|
@ -446,7 +415,9 @@ fn add_params() {
|
||||||
i32.add
|
i32.add
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -468,7 +439,8 @@ fn add_params() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn drop_locals() {
|
fn drop_locals() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call") (param i32)
|
(func (export "call") (param i32)
|
||||||
(local i32)
|
(local i32)
|
||||||
|
@ -476,7 +448,9 @@ fn drop_locals() {
|
||||||
set_local 1
|
set_local 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -492,7 +466,8 @@ fn drop_locals() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_without_else() {
|
fn if_without_else() {
|
||||||
let (code, pcs) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call") (param i32) (result i32)
|
(func (export "call") (param i32) (result i32)
|
||||||
i32.const 1
|
i32.const 1
|
||||||
|
@ -503,7 +478,9 @@ fn if_without_else() {
|
||||||
i32.const 3
|
i32.const 3
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, pcs) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -531,7 +508,8 @@ fn if_without_else() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_else() {
|
fn if_else() {
|
||||||
let (code, pcs) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
(local i32)
|
(local i32)
|
||||||
|
@ -545,7 +523,9 @@ fn if_else() {
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, pcs) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -578,7 +558,8 @@ fn if_else() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_else_returns_result() {
|
fn if_else_returns_result() {
|
||||||
let (code, pcs) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
i32.const 1
|
i32.const 1
|
||||||
|
@ -590,7 +571,9 @@ fn if_else_returns_result() {
|
||||||
drop
|
drop
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, pcs) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -622,7 +605,8 @@ fn if_else_returns_result() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_else_branch_from_true_branch() {
|
fn if_else_branch_from_true_branch() {
|
||||||
let (code, pcs) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
i32.const 1
|
i32.const 1
|
||||||
|
@ -638,7 +622,9 @@ fn if_else_branch_from_true_branch() {
|
||||||
drop
|
drop
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, pcs) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -680,7 +666,8 @@ fn if_else_branch_from_true_branch() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_else_branch_from_false_branch() {
|
fn if_else_branch_from_false_branch() {
|
||||||
let (code, pcs) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
i32.const 1
|
i32.const 1
|
||||||
|
@ -696,7 +683,9 @@ fn if_else_branch_from_false_branch() {
|
||||||
drop
|
drop
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, pcs) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -738,7 +727,8 @@ fn if_else_branch_from_false_branch() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn loop_() {
|
fn loop_() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
loop (result i32)
|
loop (result i32)
|
||||||
|
@ -749,7 +739,9 @@ fn loop_() {
|
||||||
drop
|
drop
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
@ -773,28 +765,30 @@ fn loop_() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn loop_empty() {
|
fn loop_empty() {
|
||||||
let (code, _) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
loop
|
loop
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, _) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![isa::Instruction::Return(isa::DropKeep {
|
||||||
isa::Instruction::Return(isa::DropKeep {
|
|
||||||
drop: 0,
|
drop: 0,
|
||||||
keep: isa::Keep::None,
|
keep: isa::Keep::None,
|
||||||
}),
|
}),]
|
||||||
]
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn brtable() {
|
fn brtable() {
|
||||||
let (code, pcs) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
block $1
|
block $1
|
||||||
|
@ -805,13 +799,14 @@ fn brtable() {
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, pcs) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
isa::Instruction::I32Const(0),
|
isa::Instruction::I32Const(0),
|
||||||
isa::Instruction::BrTable(
|
isa::Instruction::BrTable(targets![
|
||||||
vec![
|
|
||||||
isa::Target {
|
isa::Target {
|
||||||
dst_pc: 0,
|
dst_pc: 0,
|
||||||
drop_keep: isa::DropKeep {
|
drop_keep: isa::DropKeep {
|
||||||
|
@ -825,9 +820,8 @@ fn brtable() {
|
||||||
drop: 0,
|
drop: 0,
|
||||||
keep: isa::Keep::None,
|
keep: isa::Keep::None,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
].into_boxed_slice()
|
]),
|
||||||
),
|
|
||||||
isa::Instruction::Return(isa::DropKeep {
|
isa::Instruction::Return(isa::DropKeep {
|
||||||
drop: 0,
|
drop: 0,
|
||||||
keep: isa::Keep::None,
|
keep: isa::Keep::None,
|
||||||
|
@ -838,7 +832,8 @@ fn brtable() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn brtable_returns_result() {
|
fn brtable_returns_result() {
|
||||||
let (code, pcs) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call")
|
(func (export "call")
|
||||||
block $1 (result i32)
|
block $1 (result i32)
|
||||||
|
@ -852,14 +847,16 @@ fn brtable_returns_result() {
|
||||||
drop
|
drop
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, pcs) = compile(&module);
|
||||||
|
println!("{:?}", (&code, &pcs));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
isa::Instruction::I32Const(0),
|
isa::Instruction::I32Const(0),
|
||||||
isa::Instruction::I32Const(1),
|
isa::Instruction::I32Const(1),
|
||||||
isa::Instruction::BrTable(
|
isa::Instruction::BrTable(targets![
|
||||||
vec![
|
|
||||||
isa::Target {
|
isa::Target {
|
||||||
dst_pc: pcs[3],
|
dst_pc: pcs[3],
|
||||||
drop_keep: isa::DropKeep {
|
drop_keep: isa::DropKeep {
|
||||||
|
@ -873,9 +870,8 @@ fn brtable_returns_result() {
|
||||||
keep: isa::Keep::Single,
|
keep: isa::Keep::Single,
|
||||||
drop: 0,
|
drop: 0,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
].into_boxed_slice()
|
]),
|
||||||
),
|
|
||||||
isa::Instruction::Unreachable,
|
isa::Instruction::Unreachable,
|
||||||
isa::Instruction::Drop,
|
isa::Instruction::Drop,
|
||||||
isa::Instruction::Return(isa::DropKeep {
|
isa::Instruction::Return(isa::DropKeep {
|
||||||
|
@ -888,7 +884,8 @@ fn brtable_returns_result() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wabt_example() {
|
fn wabt_example() {
|
||||||
let (code, pcs) = compile(r#"
|
let module = validate(
|
||||||
|
r#"
|
||||||
(module
|
(module
|
||||||
(func (export "call") (param i32) (result i32)
|
(func (export "call") (param i32) (result i32)
|
||||||
block $exit
|
block $exit
|
||||||
|
@ -901,7 +898,9 @@ fn wabt_example() {
|
||||||
return
|
return
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#);
|
"#,
|
||||||
|
);
|
||||||
|
let (code, pcs) = compile(&module);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
|
|
Loading…
Reference in New Issue