mod traversals;
pub use self::traversals::*;
use crate::encode::Encoder;
use crate::{DataId, FunctionId, GlobalId, LocalFunction, MemoryId, TableId, TypeId, ValType};
use id_arena::Id;
use std::fmt;
use std::ops::{Deref, DerefMut};
use walrus_macro::walrus_instr;
pub type LocalId = Id<Local>;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Local {
id: LocalId,
ty: ValType,
pub name: Option<String>,
}
impl Local {
pub fn new(id: LocalId, ty: ValType) -> Local {
Local { id, ty, name: None }
}
pub fn id(&self) -> LocalId {
self.id
}
pub fn ty(&self) -> ValType {
self.ty
}
}
pub type InstrSeqId = Id<InstrSeq>;
#[derive(Debug)]
pub struct InstrSeq {
id: InstrSeqId,
pub params: Box<[ValType]>,
pub results: Box<[ValType]>,
pub instrs: Vec<Instr>,
}
impl Deref for InstrSeq {
type Target = Vec<Instr>;
#[inline]
fn deref(&self) -> &Vec<Instr> {
&self.instrs
}
}
impl DerefMut for InstrSeq {
#[inline]
fn deref_mut(&mut self) -> &mut Vec<Instr> {
&mut self.instrs
}
}
impl InstrSeq {
pub(crate) fn new(id: InstrSeqId, params: Box<[ValType]>, results: Box<[ValType]>) -> InstrSeq {
let instrs = vec![];
InstrSeq {
id,
params,
results,
instrs,
}
}
#[inline]
pub fn id(&self) -> InstrSeqId {
self.id
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum BlockKind {
Block,
Loop,
If,
Else,
FunctionEntry,
}
#[walrus_instr]
#[derive(Clone, Debug)]
pub enum Instr {
#[walrus(skip_builder)]
Block {
seq: InstrSeqId,
},
#[walrus(skip_builder)]
Loop {
seq: InstrSeqId,
},
Call {
func: FunctionId,
},
CallIndirect {
ty: TypeId,
table: TableId,
},
LocalGet {
local: LocalId,
},
LocalSet {
local: LocalId,
},
LocalTee {
local: LocalId,
},
GlobalGet {
global: GlobalId,
},
GlobalSet {
global: GlobalId,
},
Const {
value: Value,
},
Binop {
#[walrus(skip_visit)]
op: BinaryOp,
},
Unop {
#[walrus(skip_visit)]
op: UnaryOp,
},
Select {},
Unreachable {},
Br {
#[walrus(skip_visit)]
block: InstrSeqId,
},
BrIf {
#[walrus(skip_visit)]
block: InstrSeqId,
},
#[walrus(skip_builder)]
IfElse {
consequent: InstrSeqId,
alternative: InstrSeqId,
},
BrTable {
#[walrus(skip_visit)]
blocks: Box<[InstrSeqId]>,
#[walrus(skip_visit)]
default: InstrSeqId,
},
Drop {},
Return {},
MemorySize {
memory: MemoryId,
},
MemoryGrow {
memory: MemoryId,
},
MemoryInit {
memory: MemoryId,
data: DataId,
},
DataDrop {
data: DataId,
},
MemoryCopy {
src: MemoryId,
dst: MemoryId,
},
MemoryFill {
memory: MemoryId,
},
Load {
memory: MemoryId,
#[walrus(skip_visit)]
kind: LoadKind,
#[walrus(skip_visit)]
arg: MemArg,
},
Store {
memory: MemoryId,
#[walrus(skip_visit)]
kind: StoreKind,
#[walrus(skip_visit)]
arg: MemArg,
},
AtomicRmw {
memory: MemoryId,
#[walrus(skip_visit)]
op: AtomicOp,
#[walrus(skip_visit)]
width: AtomicWidth,
#[walrus(skip_visit)]
arg: MemArg,
},
Cmpxchg {
memory: MemoryId,
#[walrus(skip_visit)]
width: AtomicWidth,
#[walrus(skip_visit)]
arg: MemArg,
},
AtomicNotify {
memory: MemoryId,
#[walrus(skip_visit)]
arg: MemArg,
},
AtomicWait {
memory: MemoryId,
#[walrus(skip_visit)]
arg: MemArg,
#[walrus(skip_visit)]
sixty_four: bool,
},
AtomicFence {},
TableGet {
table: TableId,
},
TableSet {
table: TableId,
},
TableGrow {
table: TableId,
},
TableSize {
table: TableId,
},
RefNull {},
RefIsNull {},
V128Bitselect {},
V128Swizzle {},
V128Shuffle {
#[walrus(skip_visit)]
indices: ShuffleIndices,
},
LoadSplat {
memory: MemoryId,
#[walrus(skip_visit)]
kind: LoadSplatKind,
#[walrus(skip_visit)]
arg: MemArg,
},
}
pub type ShuffleIndices = [u8; 16];
#[derive(Debug, Clone, Copy)]
pub enum Value {
I32(i32),
I64(i64),
F32(f32),
F64(f64),
V128(u128),
}
impl Value {
pub(crate) fn emit(&self, encoder: &mut Encoder) {
match *self {
Value::I32(n) => {
encoder.byte(0x41);
encoder.i32(n);
}
Value::I64(n) => {
encoder.byte(0x42);
encoder.i64(n);
}
Value::F32(n) => {
encoder.byte(0x43);
encoder.f32(n);
}
Value::F64(n) => {
encoder.byte(0x44);
encoder.f64(n);
}
Value::V128(n) => {
encoder.raw(&[0xfd, 0x02]);
for i in 0..16 {
encoder.byte((n >> (i * 8)) as u8);
}
}
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Value::I32(i) => i.fmt(f),
Value::I64(i) => i.fmt(f),
Value::F32(i) => i.fmt(f),
Value::F64(i) => i.fmt(f),
Value::V128(i) => i.fmt(f),
}
}
}
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug)]
pub enum BinaryOp {
I32Eq,
I32Ne,
I32LtS,
I32LtU,
I32GtS,
I32GtU,
I32LeS,
I32LeU,
I32GeS,
I32GeU,
I64Eq,
I64Ne,
I64LtS,
I64LtU,
I64GtS,
I64GtU,
I64LeS,
I64LeU,
I64GeS,
I64GeU,
F32Eq,
F32Ne,
F32Lt,
F32Gt,
F32Le,
F32Ge,
F64Eq,
F64Ne,
F64Lt,
F64Gt,
F64Le,
F64Ge,
I32Add,
I32Sub,
I32Mul,
I32DivS,
I32DivU,
I32RemS,
I32RemU,
I32And,
I32Or,
I32Xor,
I32Shl,
I32ShrS,
I32ShrU,
I32Rotl,
I32Rotr,
I64Add,
I64Sub,
I64Mul,
I64DivS,
I64DivU,
I64RemS,
I64RemU,
I64And,
I64Or,
I64Xor,
I64Shl,
I64ShrS,
I64ShrU,
I64Rotl,
I64Rotr,
F32Add,
F32Sub,
F32Mul,
F32Div,
F32Min,
F32Max,
F32Copysign,
F64Add,
F64Sub,
F64Mul,
F64Div,
F64Min,
F64Max,
F64Copysign,
I8x16ReplaceLane { idx: u8 },
I16x8ReplaceLane { idx: u8 },
I32x4ReplaceLane { idx: u8 },
I64x2ReplaceLane { idx: u8 },
F32x4ReplaceLane { idx: u8 },
F64x2ReplaceLane { idx: u8 },
I8x16Eq,
I8x16Ne,
I8x16LtS,
I8x16LtU,
I8x16GtS,
I8x16GtU,
I8x16LeS,
I8x16LeU,
I8x16GeS,
I8x16GeU,
I16x8Eq,
I16x8Ne,
I16x8LtS,
I16x8LtU,
I16x8GtS,
I16x8GtU,
I16x8LeS,
I16x8LeU,
I16x8GeS,
I16x8GeU,
I32x4Eq,
I32x4Ne,
I32x4LtS,
I32x4LtU,
I32x4GtS,
I32x4GtU,
I32x4LeS,
I32x4LeU,
I32x4GeS,
I32x4GeU,
F32x4Eq,
F32x4Ne,
F32x4Lt,
F32x4Gt,
F32x4Le,
F32x4Ge,
F64x2Eq,
F64x2Ne,
F64x2Lt,
F64x2Gt,
F64x2Le,
F64x2Ge,
V128And,
V128Or,
V128Xor,
I8x16Shl,
I8x16ShrS,
I8x16ShrU,
I8x16Add,
I8x16AddSaturateS,
I8x16AddSaturateU,
I8x16Sub,
I8x16SubSaturateS,
I8x16SubSaturateU,
I8x16Mul,
I16x8Shl,
I16x8ShrS,
I16x8ShrU,
I16x8Add,
I16x8AddSaturateS,
I16x8AddSaturateU,
I16x8Sub,
I16x8SubSaturateS,
I16x8SubSaturateU,
I16x8Mul,
I32x4Shl,
I32x4ShrS,
I32x4ShrU,
I32x4Add,
I32x4Sub,
I32x4Mul,
I64x2Shl,
I64x2ShrS,
I64x2ShrU,
I64x2Add,
I64x2Sub,
F32x4Add,
F32x4Sub,
F32x4Mul,
F32x4Div,
F32x4Min,
F32x4Max,
F64x2Add,
F64x2Sub,
F64x2Mul,
F64x2Div,
F64x2Min,
F64x2Max,
}
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug)]
pub enum UnaryOp {
I32Eqz,
I32Clz,
I32Ctz,
I32Popcnt,
I64Eqz,
I64Clz,
I64Ctz,
I64Popcnt,
F32Abs,
F32Neg,
F32Ceil,
F32Floor,
F32Trunc,
F32Nearest,
F32Sqrt,
F64Abs,
F64Neg,
F64Ceil,
F64Floor,
F64Trunc,
F64Nearest,
F64Sqrt,
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,
I32Extend8S,
I32Extend16S,
I64Extend8S,
I64Extend16S,
I64Extend32S,
I8x16Splat,
I8x16ExtractLaneS { idx: u8 },
I8x16ExtractLaneU { idx: u8 },
I16x8Splat,
I16x8ExtractLaneS { idx: u8 },
I16x8ExtractLaneU { idx: u8 },
I32x4Splat,
I32x4ExtractLane { idx: u8 },
I64x2Splat,
I64x2ExtractLane { idx: u8 },
F32x4Splat,
F32x4ExtractLane { idx: u8 },
F64x2Splat,
F64x2ExtractLane { idx: u8 },
V128Not,
I8x16Neg,
I8x16AnyTrue,
I8x16AllTrue,
I16x8Neg,
I16x8AnyTrue,
I16x8AllTrue,
I32x4Neg,
I32x4AnyTrue,
I32x4AllTrue,
I64x2Neg,
I64x2AnyTrue,
I64x2AllTrue,
F32x4Abs,
F32x4Neg,
F32x4Sqrt,
F64x2Abs,
F64x2Neg,
F64x2Sqrt,
I32x4TruncSF32x4Sat,
I32x4TruncUF32x4Sat,
I64x2TruncSF64x2Sat,
I64x2TruncUF64x2Sat,
F32x4ConvertSI32x4,
F32x4ConvertUI32x4,
F64x2ConvertSI64x2,
F64x2ConvertUI64x2,
I32TruncSSatF32,
I32TruncUSatF32,
I32TruncSSatF64,
I32TruncUSatF64,
I64TruncSSatF32,
I64TruncUSatF32,
I64TruncSSatF64,
I64TruncUSatF64,
}
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum LoadKind {
I32 { atomic: bool },
I64 { atomic: bool },
F32,
F64,
V128,
I32_8 { kind: ExtendedLoad },
I32_16 { kind: ExtendedLoad },
I64_8 { kind: ExtendedLoad },
I64_16 { kind: ExtendedLoad },
I64_32 { kind: ExtendedLoad },
}
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum LoadSplatKind {
I8,
I16,
I32,
I64,
}
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum ExtendedLoad {
SignExtend,
ZeroExtend,
ZeroExtendAtomic,
}
impl LoadKind {
pub fn width(&self) -> u32 {
use self::LoadKind::*;
match self {
I32_8 { .. } | I64_8 { .. } => 1,
I32_16 { .. } | I64_16 { .. } => 2,
I32 { .. } | F32 | I64_32 { .. } => 4,
I64 { .. } | F64 => 8,
V128 => 16,
}
}
pub fn atomic(&self) -> bool {
use self::LoadKind::*;
match self {
I32_8 { kind }
| I32_16 { kind }
| I64_8 { kind }
| I64_16 { kind }
| I64_32 { kind } => kind.atomic(),
I32 { atomic } | I64 { atomic } => *atomic,
F32 | F64 | V128 => false,
}
}
}
impl ExtendedLoad {
pub fn atomic(&self) -> bool {
match self {
ExtendedLoad::SignExtend | ExtendedLoad::ZeroExtend => false,
ExtendedLoad::ZeroExtendAtomic => true,
}
}
}
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum StoreKind {
I32 { atomic: bool },
I64 { atomic: bool },
F32,
F64,
V128,
I32_8 { atomic: bool },
I32_16 { atomic: bool },
I64_8 { atomic: bool },
I64_16 { atomic: bool },
I64_32 { atomic: bool },
}
impl StoreKind {
pub fn width(&self) -> u32 {
use self::StoreKind::*;
match self {
I32_8 { .. } | I64_8 { .. } => 1,
I32_16 { .. } | I64_16 { .. } => 2,
I32 { .. } | F32 | I64_32 { .. } => 4,
I64 { .. } | F64 => 8,
V128 => 16,
}
}
pub fn atomic(&self) -> bool {
use self::StoreKind::*;
match self {
I32 { atomic }
| I64 { atomic }
| I32_8 { atomic }
| I32_16 { atomic }
| I64_8 { atomic }
| I64_16 { atomic }
| I64_32 { atomic } => *atomic,
F32 | F64 | V128 => false,
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct MemArg {
pub align: u32,
pub offset: u32,
}
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum AtomicOp {
Add,
Sub,
And,
Or,
Xor,
Xchg,
}
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum AtomicWidth {
I32,
I32_8,
I32_16,
I64,
I64_8,
I64_16,
I64_32,
}
impl AtomicWidth {
pub fn bytes(&self) -> u32 {
use self::AtomicWidth::*;
match self {
I32_8 | I64_8 => 1,
I32_16 | I64_16 => 2,
I32 | I64_32 => 4,
I64 => 8,
}
}
}
impl Instr {
pub fn following_instructions_are_unreachable(&self) -> bool {
match *self {
Instr::Unreachable(..) | Instr::Br(..) | Instr::BrTable(..) | Instr::Return(..) => true,
Instr::Block(..)
| Instr::Loop(..)
| Instr::Call(..)
| Instr::LocalGet(..)
| Instr::LocalSet(..)
| Instr::LocalTee(..)
| Instr::GlobalGet(..)
| Instr::GlobalSet(..)
| Instr::Const(..)
| Instr::Binop(..)
| Instr::Unop(..)
| Instr::Select(..)
| Instr::BrIf(..)
| Instr::IfElse(..)
| Instr::MemorySize(..)
| Instr::MemoryGrow(..)
| Instr::MemoryInit(..)
| Instr::DataDrop(..)
| Instr::MemoryCopy(..)
| Instr::MemoryFill(..)
| Instr::CallIndirect(..)
| Instr::Load(..)
| Instr::Store(..)
| Instr::AtomicRmw(..)
| Instr::Cmpxchg(..)
| Instr::AtomicNotify(..)
| Instr::AtomicWait(..)
| Instr::TableGet(..)
| Instr::TableSet(..)
| Instr::TableGrow(..)
| Instr::TableSize(..)
| Instr::RefNull(..)
| Instr::RefIsNull(..)
| Instr::V128Bitselect(..)
| Instr::V128Swizzle(..)
| Instr::V128Shuffle(..)
| Instr::LoadSplat(..)
| Instr::AtomicFence(..)
| Instr::Drop(..) => false,
}
}
}
pub(crate) trait Visit<'expr> {
fn visit<V>(&self, visitor: &mut V)
where
V: Visitor<'expr>;
}
pub(crate) trait VisitMut {
fn visit_mut<V>(&mut self, visitor: &mut V)
where
V: VisitorMut;
}