Re-structure and decouple AST from generator
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "wasm-ast"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies.parity-wasm]
|
||||
git = "https://github.com/paritytech/parity-wasm.git"
|
||||
features = ["multi_value", "sign_ext"]
|
||||
@@ -0,0 +1,613 @@
|
||||
use parity_wasm::elements::{
|
||||
BlockType, External, FuncBody, FunctionType, ImportEntry, Instruction, Local, Module, Type,
|
||||
ValueType,
|
||||
};
|
||||
|
||||
use crate::node::{
|
||||
AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, BinOp, Br, BrIf, BrTable, Call,
|
||||
CallIndirect, CmpOp, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Load,
|
||||
Memorize, MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement,
|
||||
Store, UnOp, Value,
|
||||
};
|
||||
|
||||
struct Arity {
|
||||
num_param: u32,
|
||||
num_result: u32,
|
||||
}
|
||||
|
||||
impl Arity {
|
||||
fn from_type(typ: &FunctionType) -> Self {
|
||||
let num_param = typ.params().len().try_into().unwrap();
|
||||
let num_result = typ.results().len().try_into().unwrap();
|
||||
|
||||
Self {
|
||||
num_param,
|
||||
num_result,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_index(types: &[Type], index: u32) -> Self {
|
||||
let Type::Function(typ) = &types[index as usize];
|
||||
|
||||
Self::from_type(typ)
|
||||
}
|
||||
|
||||
fn new_arity_ext(types: &[Type], import: &ImportEntry) -> Option<Arity> {
|
||||
if let External::Function(i) = import.external() {
|
||||
Some(Arity::from_index(types, *i))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn new_in_list(wasm: &Module) -> Vec<Self> {
|
||||
let (types, funcs) = match (wasm.type_section(), wasm.function_section()) {
|
||||
(Some(t), Some(f)) => (t.types(), f.entries()),
|
||||
_ => return Vec::new(),
|
||||
};
|
||||
|
||||
funcs
|
||||
.iter()
|
||||
.map(|i| Self::from_index(types, i.type_ref()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn new_ex_list(wasm: &Module) -> Vec<Self> {
|
||||
let (types, imports) = match (wasm.type_section(), wasm.import_section()) {
|
||||
(Some(t), Some(i)) => (t.types(), i.entries()),
|
||||
_ => return Vec::new(),
|
||||
};
|
||||
|
||||
imports
|
||||
.iter()
|
||||
.filter_map(|i| Self::new_arity_ext(types, i))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Arities {
|
||||
ex_arity: Vec<Arity>,
|
||||
in_arity: Vec<Arity>,
|
||||
}
|
||||
|
||||
impl Arities {
|
||||
#[must_use]
|
||||
pub fn new(parent: &Module) -> Self {
|
||||
Self {
|
||||
ex_arity: Arity::new_ex_list(parent),
|
||||
in_arity: Arity::new_in_list(parent),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn len_in(&self) -> usize {
|
||||
self.in_arity.len()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn len_ex(&self) -> usize {
|
||||
self.ex_arity.len()
|
||||
}
|
||||
|
||||
fn arity_of(&self, index: usize) -> &Arity {
|
||||
let offset = self.ex_arity.len();
|
||||
|
||||
self.ex_arity
|
||||
.get(index)
|
||||
.or_else(|| self.in_arity.get(index - offset))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Builder<'a> {
|
||||
// target state
|
||||
wasm: &'a Module,
|
||||
other: &'a Arities,
|
||||
num_result: u32,
|
||||
|
||||
// translation state
|
||||
pending: Vec<Vec<Expression>>,
|
||||
stack: Vec<Expression>,
|
||||
last_stack: usize,
|
||||
}
|
||||
|
||||
fn is_else_stat(inst: &Instruction) -> bool {
|
||||
inst == &Instruction::Else
|
||||
}
|
||||
|
||||
fn is_dead_precursor(inst: &Instruction) -> bool {
|
||||
matches!(
|
||||
inst,
|
||||
Instruction::Unreachable | Instruction::Br(_) | Instruction::Return
|
||||
)
|
||||
}
|
||||
|
||||
fn flat_local_list(local: Local) -> impl Iterator<Item = ValueType> {
|
||||
std::iter::repeat(local.value_type()).take(local.count().try_into().unwrap())
|
||||
}
|
||||
|
||||
fn load_local_list(func: &FuncBody) -> Vec<ValueType> {
|
||||
func.locals()
|
||||
.iter()
|
||||
.copied()
|
||||
.flat_map(flat_local_list)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn load_func_at(wasm: &Module, index: usize) -> &FuncBody {
|
||||
&wasm.code_section().unwrap().bodies()[index]
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
#[must_use]
|
||||
pub fn new(wasm: &'a Module, other: &'a Arities) -> Builder<'a> {
|
||||
Builder {
|
||||
wasm,
|
||||
other,
|
||||
num_result: 0,
|
||||
pending: Vec::new(),
|
||||
stack: Vec::new(),
|
||||
last_stack: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn consume(mut self, index: usize) -> Function {
|
||||
let func = load_func_at(self.wasm, index);
|
||||
let arity = &self.other.in_arity[index];
|
||||
|
||||
let local_list = load_local_list(func);
|
||||
let num_param = arity.num_param;
|
||||
|
||||
self.num_result = arity.num_result;
|
||||
|
||||
let body = self.new_forward(&mut func.code().elements());
|
||||
let num_stack = self.last_stack.try_into().unwrap();
|
||||
|
||||
Function {
|
||||
local_list,
|
||||
num_param,
|
||||
num_stack,
|
||||
body,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type_of(&self, index: u32) -> Arity {
|
||||
let types = self.wasm.type_section().unwrap().types();
|
||||
|
||||
Arity::from_index(types, index)
|
||||
}
|
||||
|
||||
fn push_recall(&mut self, num: u32) {
|
||||
let len = self.stack.len();
|
||||
|
||||
for var in len..len + num as usize {
|
||||
self.stack.push(Expression::Recall(Recall { var }));
|
||||
}
|
||||
}
|
||||
|
||||
fn push_block_result(&mut self, typ: BlockType) {
|
||||
let num = match typ {
|
||||
BlockType::NoResult => {
|
||||
return;
|
||||
}
|
||||
BlockType::Value(_) => 1,
|
||||
BlockType::TypeIndex(i) => self.get_type_of(i).num_result,
|
||||
};
|
||||
|
||||
self.push_recall(num);
|
||||
}
|
||||
|
||||
// If any expressions are still pending at the start of
|
||||
// statement, we leak them into variables.
|
||||
// Since expressions do not have set ordering rules, this is
|
||||
// safe and condenses code.
|
||||
fn gen_leak_pending(&mut self, stat: &mut Vec<Statement>) {
|
||||
self.last_stack = self.last_stack.max(self.stack.len());
|
||||
|
||||
for (i, v) in self
|
||||
.stack
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.filter(|v| !v.1.is_recalling(v.0))
|
||||
{
|
||||
let new = Expression::Recall(Recall { var: i });
|
||||
let mem = Memorize {
|
||||
var: i,
|
||||
value: std::mem::replace(v, new),
|
||||
};
|
||||
|
||||
stat.push(Statement::Memorize(mem));
|
||||
}
|
||||
}
|
||||
|
||||
// Pending expressions are put to sleep before entering
|
||||
// a control structure so that they are not lost.
|
||||
fn save_pending(&mut self) {
|
||||
let cloned = self.stack.iter().map(Expression::clone_recall).collect();
|
||||
|
||||
self.pending.push(cloned);
|
||||
}
|
||||
|
||||
fn load_pending(&mut self) {
|
||||
self.stack = self.pending.pop().unwrap();
|
||||
}
|
||||
|
||||
fn gen_return(&mut self, stat: &mut Vec<Statement>) {
|
||||
let num = self.num_result as usize;
|
||||
let list = self.stack.split_off(self.stack.len() - num);
|
||||
|
||||
self.gen_leak_pending(stat);
|
||||
stat.push(Statement::Return(Return { list }));
|
||||
}
|
||||
|
||||
fn gen_call(&mut self, func: u32, stat: &mut Vec<Statement>) {
|
||||
let arity = self.other.arity_of(func as usize);
|
||||
|
||||
let param_list = self
|
||||
.stack
|
||||
.split_off(self.stack.len() - arity.num_param as usize);
|
||||
|
||||
let len = u32::try_from(self.stack.len()).unwrap();
|
||||
let result = len..len + arity.num_result;
|
||||
|
||||
self.push_recall(arity.num_result);
|
||||
self.gen_leak_pending(stat);
|
||||
stat.push(Statement::Call(Call {
|
||||
func,
|
||||
result,
|
||||
param_list,
|
||||
}));
|
||||
}
|
||||
|
||||
fn gen_call_indirect(&mut self, typ: u32, table: u8, stat: &mut Vec<Statement>) {
|
||||
let arity = self.get_type_of(typ);
|
||||
|
||||
let index = self.stack.pop().unwrap();
|
||||
let param_list = self
|
||||
.stack
|
||||
.split_off(self.stack.len() - arity.num_param as usize);
|
||||
|
||||
let len = u32::try_from(self.stack.len()).unwrap();
|
||||
let result = len..len + arity.num_result;
|
||||
|
||||
self.push_recall(arity.num_result);
|
||||
self.gen_leak_pending(stat);
|
||||
stat.push(Statement::CallIndirect(CallIndirect {
|
||||
table,
|
||||
index,
|
||||
result,
|
||||
param_list,
|
||||
}));
|
||||
}
|
||||
|
||||
fn push_load(&mut self, op: Load, offset: u32) {
|
||||
let pointer = Box::new(self.stack.pop().unwrap());
|
||||
|
||||
self.stack.push(Expression::AnyLoad(AnyLoad {
|
||||
op,
|
||||
offset,
|
||||
pointer,
|
||||
}));
|
||||
}
|
||||
|
||||
fn gen_store(&mut self, op: Store, offset: u32, stat: &mut Vec<Statement>) {
|
||||
let value = self.stack.pop().unwrap();
|
||||
let pointer = self.stack.pop().unwrap();
|
||||
|
||||
self.gen_leak_pending(stat);
|
||||
stat.push(Statement::AnyStore(AnyStore {
|
||||
op,
|
||||
offset,
|
||||
pointer,
|
||||
value,
|
||||
}));
|
||||
}
|
||||
|
||||
fn push_constant(&mut self, value: Value) {
|
||||
self.stack.push(Expression::Value(value));
|
||||
}
|
||||
|
||||
fn push_un_op(&mut self, op: UnOp) {
|
||||
let rhs = Box::new(self.stack.pop().unwrap());
|
||||
|
||||
self.stack.push(Expression::AnyUnOp(AnyUnOp { op, rhs }));
|
||||
}
|
||||
|
||||
fn push_bin_op(&mut self, op: BinOp) {
|
||||
let rhs = Box::new(self.stack.pop().unwrap());
|
||||
let lhs = Box::new(self.stack.pop().unwrap());
|
||||
|
||||
self.stack
|
||||
.push(Expression::AnyBinOp(AnyBinOp { op, lhs, rhs }));
|
||||
}
|
||||
|
||||
fn push_cmp_op(&mut self, op: CmpOp) {
|
||||
let rhs = Box::new(self.stack.pop().unwrap());
|
||||
let lhs = Box::new(self.stack.pop().unwrap());
|
||||
|
||||
self.stack
|
||||
.push(Expression::AnyCmpOp(AnyCmpOp { op, lhs, rhs }));
|
||||
}
|
||||
|
||||
// Since Eqz is the only unary comparison it's cleaner to
|
||||
// generate a simple CmpOp
|
||||
fn from_equal_zero(&mut self, inst: &Instruction) -> bool {
|
||||
match inst {
|
||||
Instruction::I32Eqz => {
|
||||
self.push_constant(Value::I32(0));
|
||||
self.push_cmp_op(CmpOp::Eq_I32);
|
||||
|
||||
true
|
||||
}
|
||||
Instruction::I64Eqz => {
|
||||
self.push_constant(Value::I64(0));
|
||||
self.push_cmp_op(CmpOp::Eq_I64);
|
||||
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_operation(&mut self, inst: &Instruction) -> bool {
|
||||
if let Ok(op) = UnOp::try_from(inst) {
|
||||
self.push_un_op(op);
|
||||
|
||||
true
|
||||
} else if let Ok(op) = BinOp::try_from(inst) {
|
||||
self.push_bin_op(op);
|
||||
|
||||
true
|
||||
} else if let Ok(op) = CmpOp::try_from(inst) {
|
||||
self.push_cmp_op(op);
|
||||
|
||||
true
|
||||
} else {
|
||||
self.from_equal_zero(inst)
|
||||
}
|
||||
}
|
||||
|
||||
fn drop_unreachable(list: &mut &[Instruction]) {
|
||||
use Instruction as Inst;
|
||||
|
||||
let mut level = 1;
|
||||
|
||||
loop {
|
||||
let inst = &list[0];
|
||||
|
||||
*list = &list[1..];
|
||||
|
||||
match inst {
|
||||
Inst::Block(_) | Inst::Loop(_) | Inst::If(_) => {
|
||||
level += 1;
|
||||
}
|
||||
Inst::Else => {
|
||||
if level == 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Inst::End => {
|
||||
level -= 1;
|
||||
|
||||
if level == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn new_stored_body(&mut self, list: &mut &[Instruction]) -> Vec<Statement> {
|
||||
use Instruction as Inst;
|
||||
|
||||
let mut stat = Vec::new();
|
||||
|
||||
self.save_pending();
|
||||
|
||||
loop {
|
||||
let inst = &list[0];
|
||||
|
||||
*list = &list[1..];
|
||||
|
||||
if self.from_operation(inst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match inst {
|
||||
Inst::Nop => {}
|
||||
Inst::Unreachable => {
|
||||
stat.push(Statement::Unreachable);
|
||||
}
|
||||
Inst::Block(t) => {
|
||||
self.gen_leak_pending(&mut stat);
|
||||
|
||||
let data = self.new_forward(list);
|
||||
|
||||
self.push_block_result(*t);
|
||||
stat.push(Statement::Forward(data));
|
||||
}
|
||||
Inst::Loop(t) => {
|
||||
self.gen_leak_pending(&mut stat);
|
||||
|
||||
let data = self.new_backward(list);
|
||||
|
||||
self.push_block_result(*t);
|
||||
stat.push(Statement::Backward(data));
|
||||
}
|
||||
Inst::If(t) => {
|
||||
let cond = self.stack.pop().unwrap();
|
||||
|
||||
self.gen_leak_pending(&mut stat);
|
||||
|
||||
let data = self.new_if(cond, list);
|
||||
|
||||
self.push_block_result(*t);
|
||||
stat.push(Statement::If(data));
|
||||
}
|
||||
Inst::Else => {
|
||||
self.gen_leak_pending(&mut stat);
|
||||
|
||||
break;
|
||||
}
|
||||
Inst::End => {
|
||||
if list.is_empty() && self.num_result != 0 {
|
||||
self.gen_return(&mut stat);
|
||||
} else {
|
||||
self.gen_leak_pending(&mut stat);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
Inst::Br(i) => {
|
||||
self.gen_leak_pending(&mut stat);
|
||||
stat.push(Statement::Br(Br { target: *i }));
|
||||
}
|
||||
Inst::BrIf(i) => {
|
||||
let cond = self.stack.pop().unwrap();
|
||||
|
||||
self.gen_leak_pending(&mut stat);
|
||||
stat.push(Statement::BrIf(BrIf { cond, target: *i }));
|
||||
}
|
||||
Inst::BrTable(t) => {
|
||||
let cond = self.stack.pop().unwrap();
|
||||
|
||||
self.gen_leak_pending(&mut stat);
|
||||
stat.push(Statement::BrTable(BrTable {
|
||||
cond,
|
||||
data: *t.clone(),
|
||||
}));
|
||||
}
|
||||
Inst::Return => {
|
||||
self.gen_return(&mut stat);
|
||||
}
|
||||
Inst::Call(i) => {
|
||||
self.gen_call(*i, &mut stat);
|
||||
}
|
||||
Inst::CallIndirect(i, t) => {
|
||||
self.gen_call_indirect(*i, *t, &mut stat);
|
||||
}
|
||||
Inst::Drop => {
|
||||
self.stack.pop().unwrap();
|
||||
}
|
||||
Inst::Select => {
|
||||
let cond = Box::new(self.stack.pop().unwrap());
|
||||
let b = Box::new(self.stack.pop().unwrap());
|
||||
let a = Box::new(self.stack.pop().unwrap());
|
||||
|
||||
self.stack.push(Expression::Select(Select { cond, a, b }));
|
||||
}
|
||||
Inst::GetLocal(i) => {
|
||||
self.stack.push(Expression::GetLocal(GetLocal { var: *i }));
|
||||
}
|
||||
Inst::SetLocal(i) => {
|
||||
let value = self.stack.pop().unwrap();
|
||||
|
||||
self.gen_leak_pending(&mut stat);
|
||||
stat.push(Statement::SetLocal(SetLocal { var: *i, value }));
|
||||
}
|
||||
Inst::TeeLocal(i) => {
|
||||
self.gen_leak_pending(&mut stat);
|
||||
|
||||
let value = self.stack.last().unwrap().clone_recall();
|
||||
|
||||
stat.push(Statement::SetLocal(SetLocal { var: *i, value }));
|
||||
}
|
||||
Inst::GetGlobal(i) => {
|
||||
self.stack
|
||||
.push(Expression::GetGlobal(GetGlobal { var: *i }));
|
||||
}
|
||||
Inst::SetGlobal(i) => {
|
||||
let value = self.stack.pop().unwrap();
|
||||
|
||||
stat.push(Statement::SetGlobal(SetGlobal { var: *i, value }));
|
||||
}
|
||||
Inst::I32Load(_, o) => self.push_load(Load::I32, *o),
|
||||
Inst::I64Load(_, o) => self.push_load(Load::I64, *o),
|
||||
Inst::F32Load(_, o) => self.push_load(Load::F32, *o),
|
||||
Inst::F64Load(_, o) => self.push_load(Load::F64, *o),
|
||||
Inst::I32Load8S(_, o) => self.push_load(Load::I32_I8, *o),
|
||||
Inst::I32Load8U(_, o) => self.push_load(Load::I32_U8, *o),
|
||||
Inst::I32Load16S(_, o) => self.push_load(Load::I32_I16, *o),
|
||||
Inst::I32Load16U(_, o) => self.push_load(Load::I32_U16, *o),
|
||||
Inst::I64Load8S(_, o) => self.push_load(Load::I64_I8, *o),
|
||||
Inst::I64Load8U(_, o) => self.push_load(Load::I64_U8, *o),
|
||||
Inst::I64Load16S(_, o) => self.push_load(Load::I64_I16, *o),
|
||||
Inst::I64Load16U(_, o) => self.push_load(Load::I64_U16, *o),
|
||||
Inst::I64Load32S(_, o) => self.push_load(Load::I64_I32, *o),
|
||||
Inst::I64Load32U(_, o) => self.push_load(Load::I64_U32, *o),
|
||||
Inst::I32Store(_, o) => self.gen_store(Store::I32, *o, &mut stat),
|
||||
Inst::I64Store(_, o) => self.gen_store(Store::I64, *o, &mut stat),
|
||||
Inst::F32Store(_, o) => self.gen_store(Store::F32, *o, &mut stat),
|
||||
Inst::F64Store(_, o) => self.gen_store(Store::F64, *o, &mut stat),
|
||||
Inst::I32Store8(_, o) => self.gen_store(Store::I32_N8, *o, &mut stat),
|
||||
Inst::I32Store16(_, o) => self.gen_store(Store::I32_N16, *o, &mut stat),
|
||||
Inst::I64Store8(_, o) => self.gen_store(Store::I64_N8, *o, &mut stat),
|
||||
Inst::I64Store16(_, o) => self.gen_store(Store::I64_N16, *o, &mut stat),
|
||||
Inst::I64Store32(_, o) => self.gen_store(Store::I64_N32, *o, &mut stat),
|
||||
Inst::CurrentMemory(i) => {
|
||||
self.stack
|
||||
.push(Expression::MemorySize(MemorySize { memory: *i }));
|
||||
}
|
||||
Inst::GrowMemory(i) => {
|
||||
let value = Box::new(self.stack.pop().unwrap());
|
||||
|
||||
// `MemoryGrow` is an expression *but* it has side effects
|
||||
self.stack
|
||||
.push(Expression::MemoryGrow(MemoryGrow { memory: *i, value }));
|
||||
|
||||
self.gen_leak_pending(&mut stat);
|
||||
}
|
||||
Inst::I32Const(v) => self.push_constant(Value::I32(*v)),
|
||||
Inst::I64Const(v) => self.push_constant(Value::I64(*v)),
|
||||
Inst::F32Const(v) => self.push_constant(Value::F32(f32::from_bits(*v))),
|
||||
Inst::F64Const(v) => self.push_constant(Value::F64(f64::from_bits(*v))),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
if is_dead_precursor(inst) {
|
||||
Self::drop_unreachable(list);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.load_pending();
|
||||
|
||||
stat
|
||||
}
|
||||
|
||||
fn new_else(&mut self, list: &mut &[Instruction]) -> Else {
|
||||
Else {
|
||||
body: self.new_stored_body(list),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_if(&mut self, cond: Expression, list: &mut &[Instruction]) -> If {
|
||||
let copied = <&[Instruction]>::clone(list);
|
||||
let truthy = self.new_stored_body(list);
|
||||
|
||||
let end = copied.len() - list.len() - 1;
|
||||
let falsey = is_else_stat(&copied[end]).then(|| self.new_else(list));
|
||||
|
||||
If {
|
||||
cond,
|
||||
truthy,
|
||||
falsey,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_backward(&mut self, list: &mut &[Instruction]) -> Backward {
|
||||
Backward {
|
||||
body: self.new_stored_body(list),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_forward(&mut self, list: &mut &[Instruction]) -> Forward {
|
||||
Forward {
|
||||
body: self.new_stored_body(list),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod builder;
|
||||
pub mod node;
|
||||
pub mod visit;
|
||||
pub mod writer;
|
||||
@@ -0,0 +1,750 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use parity_wasm::elements::{BrTableData, ValueType};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use parity_wasm::elements::{Instruction, SignExtInstruction};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Load {
|
||||
I32,
|
||||
I64,
|
||||
F32,
|
||||
F64,
|
||||
I32_I8,
|
||||
I32_U8,
|
||||
I32_I16,
|
||||
I32_U16,
|
||||
I64_I8,
|
||||
I64_U8,
|
||||
I64_I16,
|
||||
I64_U16,
|
||||
I64_I32,
|
||||
I64_U32,
|
||||
}
|
||||
|
||||
impl Load {
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> &'static str {
|
||||
match self {
|
||||
Self::I32 => "i32",
|
||||
Self::I64 => "i64",
|
||||
Self::F32 => "f32",
|
||||
Self::F64 => "f64",
|
||||
Self::I32_I8 => "i32_i8",
|
||||
Self::I32_U8 => "i32_u8",
|
||||
Self::I32_I16 => "i32_i16",
|
||||
Self::I32_U16 => "i32_u16",
|
||||
Self::I64_I8 => "i64_i8",
|
||||
Self::I64_U8 => "i64_u8",
|
||||
Self::I64_I16 => "i64_i16",
|
||||
Self::I64_U16 => "i64_u16",
|
||||
Self::I64_I32 => "i64_i32",
|
||||
Self::I64_U32 => "i64_u32",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Instruction> for Load {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
|
||||
use Instruction as Inst;
|
||||
|
||||
let result = match inst {
|
||||
Inst::I32Load(_, _) => Self::I32,
|
||||
Inst::I64Load(_, _) => Self::I64,
|
||||
Inst::F32Load(_, _) => Self::F32,
|
||||
Inst::F64Load(_, _) => Self::F64,
|
||||
Inst::I32Load8S(_, _) => Self::I32_I8,
|
||||
Inst::I32Load8U(_, _) => Self::I32_U8,
|
||||
Inst::I32Load16S(_, _) => Self::I32_I16,
|
||||
Inst::I32Load16U(_, _) => Self::I32_U16,
|
||||
Inst::I64Load8S(_, _) => Self::I64_I8,
|
||||
Inst::I64Load8U(_, _) => Self::I64_U8,
|
||||
Inst::I64Load16S(_, _) => Self::I64_I16,
|
||||
Inst::I64Load16U(_, _) => Self::I64_U16,
|
||||
Inst::I64Load32S(_, _) => Self::I64_I32,
|
||||
Inst::I64Load32U(_, _) => Self::I64_U32,
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Store {
|
||||
I32,
|
||||
I64,
|
||||
F32,
|
||||
F64,
|
||||
I32_N8,
|
||||
I32_N16,
|
||||
I64_N8,
|
||||
I64_N16,
|
||||
I64_N32,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> &'static str {
|
||||
match self {
|
||||
Self::I32 => "i32",
|
||||
Self::I64 => "i64",
|
||||
Self::F32 => "f32",
|
||||
Self::F64 => "f64",
|
||||
Self::I32_N8 => "i32_n8",
|
||||
Self::I32_N16 => "i32_n16",
|
||||
Self::I64_N8 => "i64_n8",
|
||||
Self::I64_N16 => "i64_n16",
|
||||
Self::I64_N32 => "i64_n32",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Instruction> for Store {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
|
||||
use Instruction as Inst;
|
||||
|
||||
let result = match inst {
|
||||
Inst::I32Store(_, _) => Self::I32,
|
||||
Inst::I64Store(_, _) => Self::I64,
|
||||
Inst::F32Store(_, _) => Self::F32,
|
||||
Inst::F64Store(_, _) => Self::F64,
|
||||
Inst::I32Store8(_, _) => Self::I32_N8,
|
||||
Inst::I32Store16(_, _) => Self::I32_N16,
|
||||
Inst::I64Store8(_, _) => Self::I64_N8,
|
||||
Inst::I64Store16(_, _) => Self::I64_N16,
|
||||
Inst::I64Store32(_, _) => Self::I64_N32,
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
// Order of mnemonics is:
|
||||
// operation_result_parameter
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum UnOp {
|
||||
Clz_I32,
|
||||
Ctz_I32,
|
||||
Popcnt_I32,
|
||||
Clz_I64,
|
||||
Ctz_I64,
|
||||
Popcnt_I64,
|
||||
Abs_FN,
|
||||
Neg_FN,
|
||||
Ceil_FN,
|
||||
Floor_FN,
|
||||
Trunc_FN,
|
||||
Nearest_FN,
|
||||
Sqrt_FN,
|
||||
Wrap_I32_I64,
|
||||
Trunc_I32_F32,
|
||||
Trunc_U32_F32,
|
||||
Trunc_I32_F64,
|
||||
Trunc_U32_F64,
|
||||
Extend_I32_I8,
|
||||
Extend_I32_I16,
|
||||
Extend_I64_I8,
|
||||
Extend_I64_I16,
|
||||
Extend_I64_I32,
|
||||
Extend_U64_I32,
|
||||
Trunc_I64_F32,
|
||||
Trunc_U64_F32,
|
||||
Trunc_I64_F64,
|
||||
Trunc_U64_F64,
|
||||
Convert_F32_I32,
|
||||
Convert_F32_U32,
|
||||
Convert_F32_I64,
|
||||
Convert_F32_U64,
|
||||
Demote_F32_F64,
|
||||
Convert_F64_I32,
|
||||
Convert_F64_U32,
|
||||
Convert_F64_I64,
|
||||
Convert_F64_U64,
|
||||
Promote_F64_F32,
|
||||
Reinterpret_I32_F32,
|
||||
Reinterpret_I64_F64,
|
||||
Reinterpret_F32_I32,
|
||||
Reinterpret_F64_I64,
|
||||
}
|
||||
|
||||
impl UnOp {
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
Self::Clz_I32 => ("clz", "i32"),
|
||||
Self::Ctz_I32 => ("ctz", "i32"),
|
||||
Self::Popcnt_I32 => ("popcnt", "i32"),
|
||||
Self::Clz_I64 => ("clz", "i64"),
|
||||
Self::Ctz_I64 => ("ctz", "i64"),
|
||||
Self::Popcnt_I64 => ("popcnt", "i64"),
|
||||
Self::Abs_FN => ("math", "abs"),
|
||||
Self::Neg_FN => ("neg", "num"),
|
||||
Self::Ceil_FN => ("math", "ceil"),
|
||||
Self::Floor_FN => ("math", "floor"),
|
||||
Self::Trunc_FN => ("trunc", "num"),
|
||||
Self::Nearest_FN => ("nearest", "num"),
|
||||
Self::Sqrt_FN => ("math", "sqrt"),
|
||||
Self::Wrap_I32_I64 => ("wrap", "i32_i64"),
|
||||
Self::Trunc_I32_F32 => ("trunc", "i32_f32"),
|
||||
Self::Trunc_U32_F32 => ("trunc", "u32_f32"),
|
||||
Self::Trunc_I32_F64 => ("trunc", "i32_f64"),
|
||||
Self::Trunc_U32_F64 => ("trunc", "u32_f64"),
|
||||
Self::Extend_I32_I8 => ("extend", "i32_i8"),
|
||||
Self::Extend_I32_I16 => ("extend", "i32_i16"),
|
||||
Self::Extend_I64_I8 => ("extend", "i64_i8"),
|
||||
Self::Extend_I64_I16 => ("extend", "i64_i16"),
|
||||
Self::Extend_I64_I32 => ("extend", "i64_i32"),
|
||||
Self::Extend_U64_I32 => ("extend", "u64_i32"),
|
||||
Self::Trunc_I64_F32 => ("trunc", "i64_f32"),
|
||||
Self::Trunc_U64_F32 => ("trunc", "u64_f32"),
|
||||
Self::Trunc_I64_F64 => ("trunc", "i64_f64"),
|
||||
Self::Trunc_U64_F64 => ("trunc", "u64_f64"),
|
||||
Self::Convert_F32_I32 => ("convert", "f32_i32"),
|
||||
Self::Convert_F32_U32 => ("convert", "f32_u32"),
|
||||
Self::Convert_F32_I64 => ("convert", "f32_i64"),
|
||||
Self::Convert_F32_U64 => ("convert", "f32_u64"),
|
||||
Self::Demote_F32_F64 => ("demote", "f32_f64"),
|
||||
Self::Convert_F64_I32 => ("convert", "f64_i32"),
|
||||
Self::Convert_F64_U32 => ("convert", "f64_u32"),
|
||||
Self::Convert_F64_I64 => ("convert", "f64_i64"),
|
||||
Self::Convert_F64_U64 => ("convert", "f64_u64"),
|
||||
Self::Promote_F64_F32 => ("promote", "f64_f32"),
|
||||
Self::Reinterpret_I32_F32 => ("reinterpret", "i32_f32"),
|
||||
Self::Reinterpret_I64_F64 => ("reinterpret", "i64_f64"),
|
||||
Self::Reinterpret_F32_I32 => ("reinterpret", "f32_i32"),
|
||||
Self::Reinterpret_F64_I64 => ("reinterpret", "f64_i64"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Instruction> for UnOp {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
|
||||
use Instruction as Inst;
|
||||
|
||||
let result = match inst {
|
||||
Inst::SignExt(ext) => match ext {
|
||||
SignExtInstruction::I32Extend8S => Self::Extend_I32_I8,
|
||||
SignExtInstruction::I32Extend16S => Self::Extend_I32_I16,
|
||||
SignExtInstruction::I64Extend8S => Self::Extend_I64_I8,
|
||||
SignExtInstruction::I64Extend16S => Self::Extend_I64_I16,
|
||||
SignExtInstruction::I64Extend32S => Self::Extend_I64_I32,
|
||||
},
|
||||
Inst::I32Clz => Self::Clz_I32,
|
||||
Inst::I32Ctz => Self::Ctz_I32,
|
||||
Inst::I32Popcnt => Self::Popcnt_I32,
|
||||
Inst::I64Clz => Self::Clz_I64,
|
||||
Inst::I64Ctz => Self::Ctz_I64,
|
||||
Inst::I64Popcnt => Self::Popcnt_I64,
|
||||
Inst::F32Abs | Inst::F64Abs => Self::Abs_FN,
|
||||
Inst::F32Neg | Inst::F64Neg => Self::Neg_FN,
|
||||
Inst::F32Ceil | Inst::F64Ceil => Self::Ceil_FN,
|
||||
Inst::F32Floor | Inst::F64Floor => Self::Floor_FN,
|
||||
Inst::F32Trunc | Inst::F64Trunc => Self::Trunc_FN,
|
||||
Inst::F32Nearest | Inst::F64Nearest => Self::Nearest_FN,
|
||||
Inst::F32Sqrt | Inst::F64Sqrt => Self::Sqrt_FN,
|
||||
Inst::I32WrapI64 => Self::Wrap_I32_I64,
|
||||
Inst::I32TruncSF32 => Self::Trunc_I32_F32,
|
||||
Inst::I32TruncUF32 => Self::Trunc_U32_F32,
|
||||
Inst::I32TruncSF64 => Self::Trunc_I32_F64,
|
||||
Inst::I32TruncUF64 => Self::Trunc_U32_F64,
|
||||
Inst::I64ExtendSI32 => Self::Extend_I64_I32,
|
||||
Inst::I64ExtendUI32 => Self::Extend_U64_I32,
|
||||
Inst::I64TruncSF32 => Self::Trunc_I64_F32,
|
||||
Inst::I64TruncUF32 => Self::Trunc_U64_F32,
|
||||
Inst::I64TruncSF64 => Self::Trunc_I64_F64,
|
||||
Inst::I64TruncUF64 => Self::Trunc_U64_F64,
|
||||
Inst::F32ConvertSI32 => Self::Convert_F32_I32,
|
||||
Inst::F32ConvertUI32 => Self::Convert_F32_U32,
|
||||
Inst::F32ConvertSI64 => Self::Convert_F32_I64,
|
||||
Inst::F32ConvertUI64 => Self::Convert_F32_U64,
|
||||
Inst::F32DemoteF64 => Self::Demote_F32_F64,
|
||||
Inst::F64ConvertSI32 => Self::Convert_F64_I32,
|
||||
Inst::F64ConvertUI32 => Self::Convert_F64_U32,
|
||||
Inst::F64ConvertSI64 => Self::Convert_F64_I64,
|
||||
Inst::F64ConvertUI64 => Self::Convert_F64_U64,
|
||||
Inst::F64PromoteF32 => Self::Promote_F64_F32,
|
||||
Inst::I32ReinterpretF32 => Self::Reinterpret_I32_F32,
|
||||
Inst::I64ReinterpretF64 => Self::Reinterpret_I64_F64,
|
||||
Inst::F32ReinterpretI32 => Self::Reinterpret_F32_I32,
|
||||
Inst::F64ReinterpretI64 => Self::Reinterpret_F64_I64,
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum BinOp {
|
||||
Add_I32,
|
||||
Sub_I32,
|
||||
Mul_I32,
|
||||
DivS_I32,
|
||||
DivU_I32,
|
||||
RemS_I32,
|
||||
RemU_I32,
|
||||
And_I32,
|
||||
Or_I32,
|
||||
Xor_I32,
|
||||
Shl_I32,
|
||||
ShrS_I32,
|
||||
ShrU_I32,
|
||||
Rotl_I32,
|
||||
Rotr_I32,
|
||||
Add_I64,
|
||||
Sub_I64,
|
||||
Mul_I64,
|
||||
DivS_I64,
|
||||
DivU_I64,
|
||||
RemS_I64,
|
||||
RemU_I64,
|
||||
And_I64,
|
||||
Or_I64,
|
||||
Xor_I64,
|
||||
Shl_I64,
|
||||
ShrS_I64,
|
||||
ShrU_I64,
|
||||
Rotl_I64,
|
||||
Rotr_I64,
|
||||
Add_FN,
|
||||
Sub_FN,
|
||||
Mul_FN,
|
||||
Div_FN,
|
||||
Min_FN,
|
||||
Max_FN,
|
||||
Copysign_FN,
|
||||
}
|
||||
|
||||
impl BinOp {
|
||||
#[must_use]
|
||||
pub fn as_operator(self) -> Option<&'static str> {
|
||||
let op = match self {
|
||||
Self::Add_FN => "+",
|
||||
Self::Sub_FN => "-",
|
||||
Self::Mul_FN => "*",
|
||||
Self::Div_FN => "/",
|
||||
Self::RemS_I32 | Self::RemU_I32 | Self::RemS_I64 | Self::RemU_I64 => "%",
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(op)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
Self::Add_I32 => ("add", "i32"),
|
||||
Self::Sub_I32 => ("sub", "i32"),
|
||||
Self::Mul_I32 => ("mul", "i32"),
|
||||
Self::DivS_I32 => ("div", "i32"),
|
||||
Self::DivU_I32 => ("div", "u32"),
|
||||
Self::RemS_I32 => ("rem", "i32"),
|
||||
Self::RemU_I32 => ("rem", "u32"),
|
||||
Self::And_I32 => ("band", "i32"),
|
||||
Self::Or_I32 => ("bor", "i32"),
|
||||
Self::Xor_I32 => ("bxor", "i32"),
|
||||
Self::Shl_I32 => ("shl", "i32"),
|
||||
Self::ShrS_I32 => ("shr", "i32"),
|
||||
Self::ShrU_I32 => ("shr", "u32"),
|
||||
Self::Rotl_I32 => ("rotl", "i32"),
|
||||
Self::Rotr_I32 => ("rotr", "i32"),
|
||||
Self::Add_I64 => ("add", "i64"),
|
||||
Self::Sub_I64 => ("sub", "i64"),
|
||||
Self::Mul_I64 => ("mul", "i64"),
|
||||
Self::DivS_I64 => ("div", "i64"),
|
||||
Self::DivU_I64 => ("div", "u64"),
|
||||
Self::RemS_I64 => ("rem", "i64"),
|
||||
Self::RemU_I64 => ("rem", "u64"),
|
||||
Self::And_I64 => ("band", "i64"),
|
||||
Self::Or_I64 => ("bor", "i64"),
|
||||
Self::Xor_I64 => ("bxor", "i64"),
|
||||
Self::Shl_I64 => ("shl", "i64"),
|
||||
Self::ShrS_I64 => ("shr", "i64"),
|
||||
Self::ShrU_I64 => ("shr", "u64"),
|
||||
Self::Rotl_I64 => ("rotl", "i64"),
|
||||
Self::Rotr_I64 => ("rotr", "i64"),
|
||||
Self::Add_FN => ("add", "num"),
|
||||
Self::Sub_FN => ("sub", "num"),
|
||||
Self::Mul_FN => ("mul", "num"),
|
||||
Self::Div_FN => ("div", "num"),
|
||||
Self::Min_FN => ("math", "min"),
|
||||
Self::Max_FN => ("math", "max"),
|
||||
Self::Copysign_FN => ("copysign", "num"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Instruction> for BinOp {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
|
||||
use Instruction as Inst;
|
||||
|
||||
let result = match inst {
|
||||
Inst::I32Add => Self::Add_I32,
|
||||
Inst::I32Sub => Self::Sub_I32,
|
||||
Inst::I32Mul => Self::Mul_I32,
|
||||
Inst::I32DivS => Self::DivS_I32,
|
||||
Inst::I32DivU => Self::DivU_I32,
|
||||
Inst::I32RemS => Self::RemS_I32,
|
||||
Inst::I32RemU => Self::RemU_I32,
|
||||
Inst::I32And => Self::And_I32,
|
||||
Inst::I32Or => Self::Or_I32,
|
||||
Inst::I32Xor => Self::Xor_I32,
|
||||
Inst::I32Shl => Self::Shl_I32,
|
||||
Inst::I32ShrS => Self::ShrS_I32,
|
||||
Inst::I32ShrU => Self::ShrU_I32,
|
||||
Inst::I32Rotl => Self::Rotl_I32,
|
||||
Inst::I32Rotr => Self::Rotr_I32,
|
||||
Inst::I64Add => Self::Add_I64,
|
||||
Inst::I64Sub => Self::Sub_I64,
|
||||
Inst::I64Mul => Self::Mul_I64,
|
||||
Inst::I64DivS => Self::DivS_I64,
|
||||
Inst::I64DivU => Self::DivU_I64,
|
||||
Inst::I64RemS => Self::RemS_I64,
|
||||
Inst::I64RemU => Self::RemU_I64,
|
||||
Inst::I64And => Self::And_I64,
|
||||
Inst::I64Or => Self::Or_I64,
|
||||
Inst::I64Xor => Self::Xor_I64,
|
||||
Inst::I64Shl => Self::Shl_I64,
|
||||
Inst::I64ShrS => Self::ShrS_I64,
|
||||
Inst::I64ShrU => Self::ShrU_I64,
|
||||
Inst::I64Rotl => Self::Rotl_I64,
|
||||
Inst::I64Rotr => Self::Rotr_I64,
|
||||
Inst::F32Add | Inst::F64Add => Self::Add_FN,
|
||||
Inst::F32Sub | Inst::F64Sub => Self::Sub_FN,
|
||||
Inst::F32Mul | Inst::F64Mul => Self::Mul_FN,
|
||||
Inst::F32Div | Inst::F64Div => Self::Div_FN,
|
||||
Inst::F32Min | Inst::F64Min => Self::Min_FN,
|
||||
Inst::F32Max | Inst::F64Max => Self::Max_FN,
|
||||
Inst::F32Copysign | Inst::F64Copysign => Self::Copysign_FN,
|
||||
_ => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CmpOp {
|
||||
Eq_I32,
|
||||
Ne_I32,
|
||||
LtS_I32,
|
||||
LtU_I32,
|
||||
GtS_I32,
|
||||
GtU_I32,
|
||||
LeS_I32,
|
||||
LeU_I32,
|
||||
GeS_I32,
|
||||
GeU_I32,
|
||||
Eq_I64,
|
||||
Ne_I64,
|
||||
LtS_I64,
|
||||
LtU_I64,
|
||||
GtS_I64,
|
||||
GtU_I64,
|
||||
LeS_I64,
|
||||
LeU_I64,
|
||||
GeS_I64,
|
||||
GeU_I64,
|
||||
Eq_FN,
|
||||
Ne_FN,
|
||||
Lt_FN,
|
||||
Gt_FN,
|
||||
Le_FN,
|
||||
Ge_FN,
|
||||
}
|
||||
|
||||
impl CmpOp {
|
||||
#[must_use]
|
||||
pub fn as_operator(self) -> Option<&'static str> {
|
||||
let op = match self {
|
||||
Self::Eq_I32 | Self::Eq_I64 | Self::Eq_FN => "==",
|
||||
Self::Ne_I32 | Self::Ne_I64 | Self::Ne_FN => "~=",
|
||||
Self::LtS_I32 | Self::LtS_I64 | Self::Lt_FN => "<",
|
||||
Self::GtS_I32 | Self::GtS_I64 | Self::Gt_FN => ">",
|
||||
Self::LeS_I32 | Self::LeS_I64 | Self::Le_FN => "<=",
|
||||
Self::GeS_I32 | Self::GeS_I64 | Self::Ge_FN => ">=",
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(op)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
Self::Eq_I32 => ("eq", "i32"),
|
||||
Self::Ne_I32 => ("ne", "i32"),
|
||||
Self::LtS_I32 => ("lt", "i32"),
|
||||
Self::LtU_I32 => ("lt", "u32"),
|
||||
Self::GtS_I32 => ("gt", "i32"),
|
||||
Self::GtU_I32 => ("gt", "u32"),
|
||||
Self::LeS_I32 => ("le", "i32"),
|
||||
Self::LeU_I32 => ("le", "u32"),
|
||||
Self::GeS_I32 => ("ge", "i32"),
|
||||
Self::GeU_I32 => ("ge", "u32"),
|
||||
Self::Eq_I64 => ("eq", "i64"),
|
||||
Self::Ne_I64 => ("ne", "i64"),
|
||||
Self::LtS_I64 => ("lt", "i64"),
|
||||
Self::LtU_I64 => ("lt", "u64"),
|
||||
Self::GtS_I64 => ("gt", "i64"),
|
||||
Self::GtU_I64 => ("gt", "u64"),
|
||||
Self::LeS_I64 => ("le", "i64"),
|
||||
Self::LeU_I64 => ("le", "u64"),
|
||||
Self::GeS_I64 => ("ge", "i64"),
|
||||
Self::GeU_I64 => ("ge", "u64"),
|
||||
Self::Eq_FN => ("eq", "num"),
|
||||
Self::Ne_FN => ("ne", "num"),
|
||||
Self::Lt_FN => ("lt", "num"),
|
||||
Self::Gt_FN => ("gt", "num"),
|
||||
Self::Le_FN => ("le", "num"),
|
||||
Self::Ge_FN => ("ge", "num"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Instruction> for CmpOp {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
|
||||
use Instruction as Inst;
|
||||
|
||||
let result = match inst {
|
||||
Inst::I32Eq => Self::Eq_I32,
|
||||
Inst::I32Ne => Self::Ne_I32,
|
||||
Inst::I32LtS => Self::LtS_I32,
|
||||
Inst::I32LtU => Self::LtU_I32,
|
||||
Inst::I32GtS => Self::GtS_I32,
|
||||
Inst::I32GtU => Self::GtU_I32,
|
||||
Inst::I32LeS => Self::LeS_I32,
|
||||
Inst::I32LeU => Self::LeU_I32,
|
||||
Inst::I32GeS => Self::GeS_I32,
|
||||
Inst::I32GeU => Self::GeU_I32,
|
||||
Inst::I64Eq => Self::Eq_I64,
|
||||
Inst::I64Ne => Self::Ne_I64,
|
||||
Inst::I64LtS => Self::LtS_I64,
|
||||
Inst::I64LtU => Self::LtU_I64,
|
||||
Inst::I64GtS => Self::GtS_I64,
|
||||
Inst::I64GtU => Self::GtU_I64,
|
||||
Inst::I64LeS => Self::LeS_I64,
|
||||
Inst::I64LeU => Self::LeU_I64,
|
||||
Inst::I64GeS => Self::GeS_I64,
|
||||
Inst::I64GeU => Self::GeU_I64,
|
||||
Inst::F32Eq | Inst::F64Eq => Self::Eq_FN,
|
||||
Inst::F32Ne | Inst::F64Ne => Self::Ne_FN,
|
||||
Inst::F32Lt | Inst::F64Lt => Self::Lt_FN,
|
||||
Inst::F32Gt | Inst::F64Gt => Self::Gt_FN,
|
||||
Inst::F32Le | Inst::F64Le => Self::Le_FN,
|
||||
Inst::F32Ge | Inst::F64Ge => Self::Ge_FN,
|
||||
_ => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Recall {
|
||||
pub var: usize,
|
||||
}
|
||||
|
||||
pub struct Select {
|
||||
pub cond: Box<Expression>,
|
||||
pub a: Box<Expression>,
|
||||
pub b: Box<Expression>,
|
||||
}
|
||||
|
||||
pub struct GetLocal {
|
||||
pub var: u32,
|
||||
}
|
||||
|
||||
pub struct GetGlobal {
|
||||
pub var: u32,
|
||||
}
|
||||
|
||||
pub struct AnyLoad {
|
||||
pub op: Load,
|
||||
pub offset: u32,
|
||||
pub pointer: Box<Expression>,
|
||||
}
|
||||
|
||||
pub struct MemorySize {
|
||||
pub memory: u8,
|
||||
}
|
||||
|
||||
pub struct MemoryGrow {
|
||||
pub memory: u8,
|
||||
pub value: Box<Expression>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Value {
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
}
|
||||
|
||||
pub struct AnyUnOp {
|
||||
pub op: UnOp,
|
||||
pub rhs: Box<Expression>,
|
||||
}
|
||||
|
||||
pub struct AnyBinOp {
|
||||
pub op: BinOp,
|
||||
pub lhs: Box<Expression>,
|
||||
pub rhs: Box<Expression>,
|
||||
}
|
||||
|
||||
pub struct AnyCmpOp {
|
||||
pub op: CmpOp,
|
||||
pub lhs: Box<Expression>,
|
||||
pub rhs: Box<Expression>,
|
||||
}
|
||||
|
||||
pub enum Expression {
|
||||
Recall(Recall),
|
||||
Select(Select),
|
||||
GetLocal(GetLocal),
|
||||
GetGlobal(GetGlobal),
|
||||
AnyLoad(AnyLoad),
|
||||
MemorySize(MemorySize),
|
||||
MemoryGrow(MemoryGrow),
|
||||
Value(Value),
|
||||
AnyUnOp(AnyUnOp),
|
||||
AnyBinOp(AnyBinOp),
|
||||
AnyCmpOp(AnyCmpOp),
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
#[must_use]
|
||||
pub fn is_recalling(&self, wanted: usize) -> bool {
|
||||
match self {
|
||||
Expression::Recall(v) => v.var == wanted,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn clone_recall(&self) -> Self {
|
||||
match self {
|
||||
Expression::Recall(v) => Expression::Recall(v.clone()),
|
||||
_ => unreachable!("clone_recall called on non-recall"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Memorize {
|
||||
pub var: usize,
|
||||
pub value: Expression,
|
||||
}
|
||||
|
||||
pub struct Forward {
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
pub struct Backward {
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
pub struct Else {
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
pub struct If {
|
||||
pub cond: Expression,
|
||||
pub truthy: Vec<Statement>,
|
||||
pub falsey: Option<Else>,
|
||||
}
|
||||
|
||||
pub struct Br {
|
||||
pub target: u32,
|
||||
}
|
||||
|
||||
pub struct BrIf {
|
||||
pub cond: Expression,
|
||||
pub target: u32,
|
||||
}
|
||||
|
||||
pub struct BrTable {
|
||||
pub cond: Expression,
|
||||
pub data: BrTableData,
|
||||
}
|
||||
|
||||
pub struct Return {
|
||||
pub list: Vec<Expression>,
|
||||
}
|
||||
|
||||
pub struct Call {
|
||||
pub func: u32,
|
||||
pub result: Range<u32>,
|
||||
pub param_list: Vec<Expression>,
|
||||
}
|
||||
|
||||
pub struct CallIndirect {
|
||||
pub table: u8,
|
||||
pub index: Expression,
|
||||
pub result: Range<u32>,
|
||||
pub param_list: Vec<Expression>,
|
||||
}
|
||||
|
||||
pub struct SetLocal {
|
||||
pub var: u32,
|
||||
pub value: Expression,
|
||||
}
|
||||
|
||||
pub struct SetGlobal {
|
||||
pub var: u32,
|
||||
pub value: Expression,
|
||||
}
|
||||
|
||||
pub struct AnyStore {
|
||||
pub op: Store,
|
||||
pub offset: u32,
|
||||
pub pointer: Expression,
|
||||
pub value: Expression,
|
||||
}
|
||||
|
||||
pub enum Statement {
|
||||
Unreachable,
|
||||
Memorize(Memorize),
|
||||
Forward(Forward),
|
||||
Backward(Backward),
|
||||
If(If),
|
||||
Br(Br),
|
||||
BrIf(BrIf),
|
||||
BrTable(BrTable),
|
||||
Return(Return),
|
||||
Call(Call),
|
||||
CallIndirect(CallIndirect),
|
||||
SetLocal(SetLocal),
|
||||
SetGlobal(SetGlobal),
|
||||
AnyStore(AnyStore),
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub local_list: Vec<ValueType>,
|
||||
pub num_param: u32,
|
||||
pub num_stack: u32,
|
||||
pub body: Forward,
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
use crate::node::{
|
||||
AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call,
|
||||
CallIndirect, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize,
|
||||
MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement, Value,
|
||||
};
|
||||
|
||||
pub trait Visitor {
|
||||
fn visit_recall(&mut self, _: &Recall) {}
|
||||
|
||||
fn visit_select(&mut self, _: &Select) {}
|
||||
|
||||
fn visit_get_local(&mut self, _: &GetLocal) {}
|
||||
|
||||
fn visit_get_global(&mut self, _: &GetGlobal) {}
|
||||
|
||||
fn visit_any_load(&mut self, _: &AnyLoad) {}
|
||||
|
||||
fn visit_memory_size(&mut self, _: &MemorySize) {}
|
||||
|
||||
fn visit_memory_grow(&mut self, _: &MemoryGrow) {}
|
||||
|
||||
fn visit_value(&mut self, _: &Value) {}
|
||||
|
||||
fn visit_any_unop(&mut self, _: &AnyUnOp) {}
|
||||
|
||||
fn visit_any_binop(&mut self, _: &AnyBinOp) {}
|
||||
|
||||
fn visit_any_cmpop(&mut self, _: &AnyCmpOp) {}
|
||||
|
||||
fn visit_expression(&mut self, _: &Expression) {}
|
||||
|
||||
fn visit_unreachable(&mut self) {}
|
||||
|
||||
fn visit_memorize(&mut self, _: &Memorize) {}
|
||||
|
||||
fn visit_forward(&mut self, _: &Forward) {}
|
||||
|
||||
fn visit_backward(&mut self, _: &Backward) {}
|
||||
|
||||
fn visit_else(&mut self, _: &Else) {}
|
||||
|
||||
fn visit_if(&mut self, _: &If) {}
|
||||
|
||||
fn visit_br(&mut self, _: &Br) {}
|
||||
|
||||
fn visit_br_if(&mut self, _: &BrIf) {}
|
||||
|
||||
fn visit_br_table(&mut self, _: &BrTable) {}
|
||||
|
||||
fn visit_return(&mut self, _: &Return) {}
|
||||
|
||||
fn visit_call(&mut self, _: &Call) {}
|
||||
|
||||
fn visit_call_indirect(&mut self, _: &CallIndirect) {}
|
||||
|
||||
fn visit_set_local(&mut self, _: &SetLocal) {}
|
||||
|
||||
fn visit_set_global(&mut self, _: &SetGlobal) {}
|
||||
|
||||
fn visit_any_store(&mut self, _: &AnyStore) {}
|
||||
|
||||
fn visit_statement(&mut self, _: &Statement) {}
|
||||
}
|
||||
|
||||
pub trait Driver<T: Visitor> {
|
||||
fn accept(&self, visitor: &mut T);
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Recall {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
visitor.visit_recall(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Select {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.cond.accept(visitor);
|
||||
self.a.accept(visitor);
|
||||
self.b.accept(visitor);
|
||||
|
||||
visitor.visit_select(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for GetLocal {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
visitor.visit_get_local(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for GetGlobal {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
visitor.visit_get_global(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for AnyLoad {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.pointer.accept(visitor);
|
||||
|
||||
visitor.visit_any_load(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for MemorySize {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
visitor.visit_memory_size(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for MemoryGrow {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.value.accept(visitor);
|
||||
|
||||
visitor.visit_memory_grow(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Value {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
visitor.visit_value(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for AnyUnOp {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.rhs.accept(visitor);
|
||||
|
||||
visitor.visit_any_unop(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for AnyBinOp {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.lhs.accept(visitor);
|
||||
self.rhs.accept(visitor);
|
||||
|
||||
visitor.visit_any_binop(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for AnyCmpOp {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.lhs.accept(visitor);
|
||||
self.rhs.accept(visitor);
|
||||
|
||||
visitor.visit_any_cmpop(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Expression {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
match self {
|
||||
Self::Recall(v) => v.accept(visitor),
|
||||
Self::Select(v) => v.accept(visitor),
|
||||
Self::GetLocal(v) => v.accept(visitor),
|
||||
Self::GetGlobal(v) => v.accept(visitor),
|
||||
Self::AnyLoad(v) => v.accept(visitor),
|
||||
Self::MemorySize(v) => v.accept(visitor),
|
||||
Self::MemoryGrow(v) => v.accept(visitor),
|
||||
Self::Value(v) => v.accept(visitor),
|
||||
Self::AnyUnOp(v) => v.accept(visitor),
|
||||
Self::AnyBinOp(v) => v.accept(visitor),
|
||||
Self::AnyCmpOp(v) => v.accept(visitor),
|
||||
}
|
||||
|
||||
visitor.visit_expression(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Memorize {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.value.accept(visitor);
|
||||
|
||||
visitor.visit_memorize(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Forward {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
for v in &self.body {
|
||||
v.accept(visitor);
|
||||
}
|
||||
|
||||
visitor.visit_forward(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Backward {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
for v in &self.body {
|
||||
v.accept(visitor);
|
||||
}
|
||||
|
||||
visitor.visit_backward(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Else {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
for v in &self.body {
|
||||
v.accept(visitor);
|
||||
}
|
||||
|
||||
visitor.visit_else(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for If {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.cond.accept(visitor);
|
||||
|
||||
for v in &self.truthy {
|
||||
v.accept(visitor);
|
||||
}
|
||||
|
||||
if let Some(v) = &self.falsey {
|
||||
v.accept(visitor);
|
||||
}
|
||||
|
||||
visitor.visit_if(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Br {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
visitor.visit_br(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for BrIf {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.cond.accept(visitor);
|
||||
|
||||
visitor.visit_br_if(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for BrTable {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.cond.accept(visitor);
|
||||
|
||||
visitor.visit_br_table(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Return {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
for v in &self.list {
|
||||
v.accept(visitor);
|
||||
}
|
||||
|
||||
visitor.visit_return(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Call {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
for v in &self.param_list {
|
||||
v.accept(visitor);
|
||||
}
|
||||
|
||||
visitor.visit_call(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for CallIndirect {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.index.accept(visitor);
|
||||
|
||||
for v in &self.param_list {
|
||||
v.accept(visitor);
|
||||
}
|
||||
|
||||
visitor.visit_call_indirect(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for SetLocal {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.value.accept(visitor);
|
||||
|
||||
visitor.visit_set_local(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for SetGlobal {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.value.accept(visitor);
|
||||
|
||||
visitor.visit_set_global(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for AnyStore {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.pointer.accept(visitor);
|
||||
self.value.accept(visitor);
|
||||
|
||||
visitor.visit_any_store(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Statement {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
match self {
|
||||
Self::Unreachable => visitor.visit_unreachable(),
|
||||
Self::Memorize(v) => v.accept(visitor),
|
||||
Self::Forward(v) => v.accept(visitor),
|
||||
Self::Backward(v) => v.accept(visitor),
|
||||
Self::If(v) => v.accept(visitor),
|
||||
Self::Br(v) => v.accept(visitor),
|
||||
Self::BrIf(v) => v.accept(visitor),
|
||||
Self::BrTable(v) => v.accept(visitor),
|
||||
Self::Return(v) => v.accept(visitor),
|
||||
Self::Call(v) => v.accept(visitor),
|
||||
Self::CallIndirect(v) => v.accept(visitor),
|
||||
Self::SetLocal(v) => v.accept(visitor),
|
||||
Self::SetGlobal(v) => v.accept(visitor),
|
||||
Self::AnyStore(v) => v.accept(visitor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Visitor> Driver<T> for Function {
|
||||
fn accept(&self, visitor: &mut T) {
|
||||
self.body.accept(visitor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
use std::io::{Result, Write};
|
||||
|
||||
use parity_wasm::elements::Module;
|
||||
|
||||
pub type Writer<'a> = &'a mut dyn Write;
|
||||
|
||||
pub trait Transpiler<'a> {
|
||||
fn new(wasm: &'a Module) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// # Errors
|
||||
/// Returns `Err` if writing to `Writer` failed.
|
||||
fn runtime(writer: Writer) -> Result<()>;
|
||||
|
||||
/// # Errors
|
||||
/// Returns `Err` if writing to `Writer` failed.
|
||||
fn transpile(&self, writer: Writer) -> Result<()>;
|
||||
}
|
||||
Reference in New Issue
Block a user