Re-structure and decouple AST from generator
This commit is contained in:
parent
9d2d8aa69b
commit
22ea8910ad
@ -1,2 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["fuzz", "wasm"]
|
members = [
|
||||||
|
"codegen-luajit",
|
||||||
|
"codegen-luau",
|
||||||
|
"fuzz",
|
||||||
|
"wasm-ast",
|
||||||
|
"wasm-synth"
|
||||||
|
]
|
||||||
|
13
codegen-luajit/Cargo.toml
Normal file
13
codegen-luajit/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "codegen-luajit"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies.wasm-ast]
|
||||||
|
path = "../wasm-ast"
|
||||||
|
|
||||||
|
[dependencies.parity-wasm]
|
||||||
|
git = "https://github.com/paritytech/parity-wasm.git"
|
||||||
|
features = ["multi_value", "sign_ext"]
|
@ -1,8 +1,9 @@
|
|||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use crate::ast::node::{AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Function};
|
use wasm_ast::{
|
||||||
|
node::{AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Function},
|
||||||
use super::visit::{Driver, Visitor};
|
visit::{Driver, Visitor},
|
||||||
|
};
|
||||||
|
|
||||||
struct Visit {
|
struct Visit {
|
||||||
result: BTreeSet<(&'static str, &'static str)>,
|
result: BTreeSet<(&'static str, &'static str)>,
|
@ -1,8 +1,9 @@
|
|||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use crate::ast::node::{AnyLoad, AnyStore, Function, MemoryGrow, MemorySize};
|
use wasm_ast::{
|
||||||
|
node::{AnyLoad, AnyStore, Function, MemoryGrow, MemorySize},
|
||||||
use super::visit::{Driver, Visitor};
|
visit::{Driver, Visitor},
|
||||||
|
};
|
||||||
|
|
||||||
struct Visit {
|
struct Visit {
|
||||||
result: BTreeSet<u8>,
|
result: BTreeSet<u8>,
|
@ -1,3 +1,2 @@
|
|||||||
pub mod localize;
|
pub mod localize;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
mod visit;
|
|
@ -1,27 +1,151 @@
|
|||||||
use std::{collections::BTreeSet, io::Result};
|
use std::{collections::BTreeSet, io::Result, ops::Range};
|
||||||
|
|
||||||
use parity_wasm::elements::{External, ImportCountType, Instruction, Internal, Module};
|
use parity_wasm::elements::{
|
||||||
|
External, ImportCountType, Instruction, Internal, Module, NameSection, ResizableLimits,
|
||||||
use crate::{
|
|
||||||
analyzer::{localize, memory},
|
|
||||||
ast::{
|
|
||||||
builder::{Arities, Builder},
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use wasm_ast::{
|
||||||
base::{Transpiler, Writer},
|
builder::{Arities, Builder},
|
||||||
shared::{
|
node::{
|
||||||
aux_internal_index, write_f32, write_f64, write_func_name, write_memory_init,
|
AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call,
|
||||||
write_parameter_list, write_result_list, write_table_init, write_variable_list,
|
CallIndirect, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize,
|
||||||
|
MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement, Value,
|
||||||
},
|
},
|
||||||
|
writer::{Transpiler, Writer},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::analyzer::{localize, memory};
|
||||||
|
|
||||||
|
fn aux_internal_index(internal: Internal) -> u32 {
|
||||||
|
match internal {
|
||||||
|
Internal::Function(v) | Internal::Table(v) | Internal::Memory(v) | Internal::Global(v) => v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_limit_max(limits: &ResizableLimits) -> String {
|
||||||
|
match limits.maximum() {
|
||||||
|
Some(v) => v.to_string(),
|
||||||
|
None => "0xFFFF".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_table_init(limit: &ResizableLimits, w: Writer) -> Result<()> {
|
||||||
|
let a = limit.initial();
|
||||||
|
let b = new_limit_max(limit);
|
||||||
|
|
||||||
|
write!(w, "{{ min = {}, max = {}, data = {{}} }}", a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_memory_init(limit: &ResizableLimits, w: Writer) -> Result<()> {
|
||||||
|
let a = limit.initial();
|
||||||
|
let b = new_limit_max(limit);
|
||||||
|
|
||||||
|
write!(w, "rt.allocator.new({}, {})", a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_func_name(wasm: &Module, index: u32, offset: u32, w: Writer) -> Result<()> {
|
||||||
|
let opt = wasm
|
||||||
|
.names_section()
|
||||||
|
.and_then(NameSection::functions)
|
||||||
|
.and_then(|v| v.names().get(index));
|
||||||
|
|
||||||
|
write!(w, "FUNC_LIST")?;
|
||||||
|
|
||||||
|
if let Some(name) = opt {
|
||||||
|
write!(w, "--[[{}]]", name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "[{}] =", index + offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_in_order(prefix: &str, len: u32, w: Writer) -> Result<()> {
|
||||||
|
if len == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "{}_{}", prefix, 0)?;
|
||||||
|
(1..len).try_for_each(|i| write!(w, ", {}_{}", prefix, i))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_f32(f: f32, w: Writer) -> Result<()> {
|
||||||
|
let sign = if f.is_sign_negative() { "-" } else { "" };
|
||||||
|
|
||||||
|
if f.is_infinite() {
|
||||||
|
write!(w, "{}math.huge ", sign)
|
||||||
|
} else if f.is_nan() {
|
||||||
|
write!(w, "{}0/0 ", sign)
|
||||||
|
} else {
|
||||||
|
write!(w, "{:e} ", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_f64(f: f64, w: Writer) -> Result<()> {
|
||||||
|
let sign = if f.is_sign_negative() { "-" } else { "" };
|
||||||
|
|
||||||
|
if f.is_infinite() {
|
||||||
|
write!(w, "{}math.huge ", sign)
|
||||||
|
} else if f.is_nan() {
|
||||||
|
write!(w, "{}0/0 ", sign)
|
||||||
|
} else {
|
||||||
|
write!(w, "{:e} ", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_list(name: &str, len: usize, w: Writer) -> Result<()> {
|
||||||
|
let hash = len.min(1);
|
||||||
|
let len = len.saturating_sub(1);
|
||||||
|
|
||||||
|
write!(w, "local {} = table_new({}, {})", name, len, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_parameter_list(func: &Function, w: Writer) -> Result<()> {
|
||||||
|
write!(w, "function(")?;
|
||||||
|
write_in_order("param", func.num_param, w)?;
|
||||||
|
write!(w, ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_result_list(range: Range<u32>, w: Writer) -> Result<()> {
|
||||||
|
if range.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
range.clone().try_for_each(|i| {
|
||||||
|
if i != range.start {
|
||||||
|
write!(w, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "reg_{}", i)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
write!(w, " = ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_variable_list(func: &Function, w: Writer) -> Result<()> {
|
||||||
|
if !func.local_list.is_empty() {
|
||||||
|
let num_local = func.local_list.len().try_into().unwrap();
|
||||||
|
|
||||||
|
write!(w, "local ")?;
|
||||||
|
write_in_order("loc", num_local, w)?;
|
||||||
|
write!(w, " = ")?;
|
||||||
|
|
||||||
|
for (i, t) in func.local_list.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(w, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "ZERO_{} ", t)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if func.num_stack != 0 {
|
||||||
|
write!(w, "local ")?;
|
||||||
|
write_in_order("reg", func.num_stack, w)?;
|
||||||
|
write!(w, " ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn write_expression(code: &[Instruction], w: Writer) -> Result<()> {
|
fn write_expression(code: &[Instruction], w: Writer) -> Result<()> {
|
||||||
// FIXME: Badly generated WASM will produce the wrong constant.
|
// FIXME: Badly generated WASM will produce the wrong constant.
|
||||||
for inst in code {
|
for inst in code {
|
||||||
@ -479,12 +603,48 @@ impl Driver for Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LuaJIT<'a> {
|
pub struct Generator<'a> {
|
||||||
wasm: &'a Module,
|
wasm: &'a Module,
|
||||||
arity: Arities,
|
arity: Arities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LuaJIT<'a> {
|
static RUNTIME: &str = include_str!("../runtime/runtime.lua");
|
||||||
|
|
||||||
|
impl<'a> Transpiler<'a> for Generator<'a> {
|
||||||
|
fn new(wasm: &'a Module) -> Self {
|
||||||
|
let arity = Arities::new(wasm);
|
||||||
|
|
||||||
|
Self { wasm, arity }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime(w: Writer) -> Result<()> {
|
||||||
|
write!(w, "{}", RUNTIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transpile(&self, w: Writer) -> Result<()> {
|
||||||
|
write!(w, "local rt = require(\"luajit\")")?;
|
||||||
|
|
||||||
|
let func_list = self.build_func_list();
|
||||||
|
|
||||||
|
Self::gen_localize(&func_list, w)?;
|
||||||
|
|
||||||
|
write!(w, "local ZERO_i32 = 0 ")?;
|
||||||
|
write!(w, "local ZERO_i64 = 0LL ")?;
|
||||||
|
write!(w, "local ZERO_f32 = 0.0 ")?;
|
||||||
|
write!(w, "local ZERO_f64 = 0.0 ")?;
|
||||||
|
|
||||||
|
write!(w, "local table_new = require(\"table.new\")")?;
|
||||||
|
write_list("FUNC_LIST", self.wasm.functions_space(), w)?;
|
||||||
|
write_list("TABLE_LIST", self.wasm.table_space(), w)?;
|
||||||
|
write_list("MEMORY_LIST", self.wasm.memory_space(), w)?;
|
||||||
|
write_list("GLOBAL_LIST", self.wasm.globals_space(), w)?;
|
||||||
|
|
||||||
|
self.gen_func_list(&func_list, w)?;
|
||||||
|
self.gen_start_point(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Generator<'a> {
|
||||||
fn gen_import_of<T>(&self, w: Writer, lower: &str, cond: T) -> Result<()>
|
fn gen_import_of<T>(&self, w: Writer, lower: &str, cond: T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Fn(&External) -> bool,
|
T: Fn(&External) -> bool,
|
||||||
@ -712,46 +872,3 @@ impl<'a> LuaJIT<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_list(name: &str, len: usize, w: Writer) -> Result<()> {
|
|
||||||
let hash = len.min(1);
|
|
||||||
let len = len.saturating_sub(1);
|
|
||||||
|
|
||||||
write!(w, "local {} = table_new({}, {})", name, len, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
static LUAJIT_RUNTIME: &str = include_str!("../../runtime/luajit.lua");
|
|
||||||
|
|
||||||
impl<'a> Transpiler<'a> for LuaJIT<'a> {
|
|
||||||
fn new(wasm: &'a Module) -> Self {
|
|
||||||
let arity = Arities::new(wasm);
|
|
||||||
|
|
||||||
Self { wasm, arity }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn runtime(writer: Writer) -> Result<()> {
|
|
||||||
write!(writer, "{}", LUAJIT_RUNTIME)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transpile(&self, w: Writer) -> Result<()> {
|
|
||||||
write!(w, "local rt = require(\"luajit\")")?;
|
|
||||||
|
|
||||||
let func_list = self.build_func_list();
|
|
||||||
|
|
||||||
Self::gen_localize(&func_list, w)?;
|
|
||||||
|
|
||||||
write!(w, "local ZERO_i32 = 0 ")?;
|
|
||||||
write!(w, "local ZERO_i64 = 0LL ")?;
|
|
||||||
write!(w, "local ZERO_f32 = 0.0 ")?;
|
|
||||||
write!(w, "local ZERO_f64 = 0.0 ")?;
|
|
||||||
|
|
||||||
write!(w, "local table_new = require(\"table.new\")")?;
|
|
||||||
write_list("FUNC_LIST", self.wasm.functions_space(), w)?;
|
|
||||||
write_list("TABLE_LIST", self.wasm.table_space(), w)?;
|
|
||||||
write_list("MEMORY_LIST", self.wasm.memory_space(), w)?;
|
|
||||||
write_list("GLOBAL_LIST", self.wasm.globals_space(), w)?;
|
|
||||||
|
|
||||||
self.gen_func_list(&func_list, w)?;
|
|
||||||
self.gen_start_point(w)
|
|
||||||
}
|
|
||||||
}
|
|
4
codegen-luajit/src/lib.rs
Normal file
4
codegen-luajit/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub static RUNTIME: &str = include_str!("../runtime/runtime.lua");
|
||||||
|
|
||||||
|
mod analyzer;
|
||||||
|
pub mod gen;
|
13
codegen-luau/Cargo.toml
Normal file
13
codegen-luau/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "codegen-luau"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies.wasm-ast]
|
||||||
|
path = "../wasm-ast"
|
||||||
|
|
||||||
|
[dependencies.parity-wasm]
|
||||||
|
git = "https://github.com/paritytech/parity-wasm.git"
|
||||||
|
features = ["multi_value", "sign_ext"]
|
60
codegen-luau/src/analyzer/localize.rs
Normal file
60
codegen-luau/src/analyzer/localize.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use wasm_ast::{
|
||||||
|
node::{AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Function},
|
||||||
|
visit::{Driver, Visitor},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Visit {
|
||||||
|
result: BTreeSet<(&'static str, &'static str)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visitor for Visit {
|
||||||
|
fn visit_any_load(&mut self, v: &AnyLoad) {
|
||||||
|
let name = v.op.as_name();
|
||||||
|
|
||||||
|
self.result.insert(("load", name));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_any_store(&mut self, v: &AnyStore) {
|
||||||
|
let name = v.op.as_name();
|
||||||
|
|
||||||
|
self.result.insert(("store", name));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_any_unop(&mut self, v: &AnyUnOp) {
|
||||||
|
let name = v.op.as_name();
|
||||||
|
|
||||||
|
self.result.insert(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_any_binop(&mut self, v: &AnyBinOp) {
|
||||||
|
if v.op.as_operator().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = v.op.as_name();
|
||||||
|
|
||||||
|
self.result.insert(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_any_cmpop(&mut self, v: &AnyCmpOp) {
|
||||||
|
if v.op.as_operator().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = v.op.as_name();
|
||||||
|
|
||||||
|
self.result.insert(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn visit(func: &Function) -> BTreeSet<(&'static str, &'static str)> {
|
||||||
|
let mut visit = Visit {
|
||||||
|
result: BTreeSet::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
func.accept(&mut visit);
|
||||||
|
|
||||||
|
visit.result
|
||||||
|
}
|
38
codegen-luau/src/analyzer/memory.rs
Normal file
38
codegen-luau/src/analyzer/memory.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use wasm_ast::{
|
||||||
|
node::{AnyLoad, AnyStore, Function, MemoryGrow, MemorySize},
|
||||||
|
visit::{Driver, Visitor},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Visit {
|
||||||
|
result: BTreeSet<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visitor for Visit {
|
||||||
|
fn visit_any_store(&mut self, _: &AnyStore) {
|
||||||
|
self.result.insert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_any_load(&mut self, _: &AnyLoad) {
|
||||||
|
self.result.insert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_memory_size(&mut self, m: &MemorySize) {
|
||||||
|
self.result.insert(m.memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_memory_grow(&mut self, m: &MemoryGrow) {
|
||||||
|
self.result.insert(m.memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn visit(func: &Function) -> BTreeSet<u8> {
|
||||||
|
let mut visit = Visit {
|
||||||
|
result: BTreeSet::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
func.accept(&mut visit);
|
||||||
|
|
||||||
|
visit.result
|
||||||
|
}
|
2
codegen-luau/src/analyzer/mod.rs
Normal file
2
codegen-luau/src/analyzer/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod localize;
|
||||||
|
pub mod memory;
|
@ -1,27 +1,150 @@
|
|||||||
use std::{collections::BTreeSet, io::Result};
|
use std::{collections::BTreeSet, io::Result, ops::Range};
|
||||||
|
|
||||||
use parity_wasm::elements::{External, ImportCountType, Instruction, Internal, Module};
|
use parity_wasm::elements::{
|
||||||
|
External, ImportCountType, Instruction, Internal, Module, NameSection, ResizableLimits,
|
||||||
use crate::{
|
|
||||||
analyzer::{localize, memory},
|
|
||||||
ast::{
|
|
||||||
builder::{Arities, Builder},
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use wasm_ast::{
|
||||||
base::{Transpiler, Writer},
|
builder::{Arities, Builder},
|
||||||
shared::{
|
node::{
|
||||||
aux_internal_index, write_f32, write_f64, write_func_name, write_memory_init,
|
AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call,
|
||||||
write_parameter_list, write_result_list, write_table_init, write_variable_list,
|
CallIndirect, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize,
|
||||||
|
MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement, Value,
|
||||||
},
|
},
|
||||||
|
writer::{Transpiler, Writer},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::analyzer::{localize, memory};
|
||||||
|
|
||||||
|
fn aux_internal_index(internal: Internal) -> u32 {
|
||||||
|
match internal {
|
||||||
|
Internal::Function(v) | Internal::Table(v) | Internal::Memory(v) | Internal::Global(v) => v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_limit_max(limits: &ResizableLimits) -> String {
|
||||||
|
match limits.maximum() {
|
||||||
|
Some(v) => v.to_string(),
|
||||||
|
None => "0xFFFF".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_table_init(limit: &ResizableLimits, w: Writer) -> Result<()> {
|
||||||
|
let a = limit.initial();
|
||||||
|
let b = new_limit_max(limit);
|
||||||
|
|
||||||
|
write!(w, "{{ min = {}, max = {}, data = {{}} }}", a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_memory_init(limit: &ResizableLimits, w: Writer) -> Result<()> {
|
||||||
|
let a = limit.initial();
|
||||||
|
let b = new_limit_max(limit);
|
||||||
|
|
||||||
|
write!(w, "rt.allocator.new({}, {})", a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_func_name(wasm: &Module, index: u32, offset: u32, w: Writer) -> Result<()> {
|
||||||
|
let opt = wasm
|
||||||
|
.names_section()
|
||||||
|
.and_then(NameSection::functions)
|
||||||
|
.and_then(|v| v.names().get(index));
|
||||||
|
|
||||||
|
write!(w, "FUNC_LIST")?;
|
||||||
|
|
||||||
|
if let Some(name) = opt {
|
||||||
|
write!(w, "--[[{}]]", name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "[{}] =", index + offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_in_order(prefix: &str, len: u32, w: Writer) -> Result<()> {
|
||||||
|
if len == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "{}_{}", prefix, 0)?;
|
||||||
|
(1..len).try_for_each(|i| write!(w, ", {}_{}", prefix, i))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_f32(f: f32, w: Writer) -> Result<()> {
|
||||||
|
let sign = if f.is_sign_negative() { "-" } else { "" };
|
||||||
|
|
||||||
|
if f.is_infinite() {
|
||||||
|
write!(w, "{}math.huge ", sign)
|
||||||
|
} else if f.is_nan() {
|
||||||
|
write!(w, "{}0/0 ", sign)
|
||||||
|
} else {
|
||||||
|
write!(w, "{:e} ", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_f64(f: f64, w: Writer) -> Result<()> {
|
||||||
|
let sign = if f.is_sign_negative() { "-" } else { "" };
|
||||||
|
|
||||||
|
if f.is_infinite() {
|
||||||
|
write!(w, "{}math.huge ", sign)
|
||||||
|
} else if f.is_nan() {
|
||||||
|
write!(w, "{}0/0 ", sign)
|
||||||
|
} else {
|
||||||
|
write!(w, "{:e} ", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_list(name: &str, len: usize, w: Writer) -> Result<()> {
|
||||||
|
let len = len.saturating_sub(1);
|
||||||
|
|
||||||
|
write!(w, "local {} = table.create({})", name, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_parameter_list(func: &Function, w: Writer) -> Result<()> {
|
||||||
|
write!(w, "function(")?;
|
||||||
|
write_in_order("param", func.num_param, w)?;
|
||||||
|
write!(w, ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_result_list(range: Range<u32>, w: Writer) -> Result<()> {
|
||||||
|
if range.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
range.clone().try_for_each(|i| {
|
||||||
|
if i != range.start {
|
||||||
|
write!(w, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "reg_{}", i)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
write!(w, " = ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_variable_list(func: &Function, w: Writer) -> Result<()> {
|
||||||
|
if !func.local_list.is_empty() {
|
||||||
|
let num_local = func.local_list.len().try_into().unwrap();
|
||||||
|
|
||||||
|
write!(w, "local ")?;
|
||||||
|
write_in_order("loc", num_local, w)?;
|
||||||
|
write!(w, " = ")?;
|
||||||
|
|
||||||
|
for (i, t) in func.local_list.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(w, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "ZERO_{} ", t)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if func.num_stack != 0 {
|
||||||
|
write!(w, "local ")?;
|
||||||
|
write_in_order("reg", func.num_stack, w)?;
|
||||||
|
write!(w, " ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn write_expression(code: &[Instruction], w: Writer) -> Result<()> {
|
fn write_expression(code: &[Instruction], w: Writer) -> Result<()> {
|
||||||
// FIXME: Badly generated WASM will produce the wrong constant.
|
// FIXME: Badly generated WASM will produce the wrong constant.
|
||||||
for inst in code {
|
for inst in code {
|
||||||
@ -476,12 +599,47 @@ impl Driver for Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Luau<'a> {
|
pub struct Generator<'a> {
|
||||||
wasm: &'a Module,
|
wasm: &'a Module,
|
||||||
arity: Arities,
|
arity: Arities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Luau<'a> {
|
static RUNTIME: &str = include_str!("../runtime/runtime.lua");
|
||||||
|
|
||||||
|
impl<'a> Transpiler<'a> for Generator<'a> {
|
||||||
|
fn new(wasm: &'a Module) -> Self {
|
||||||
|
let arity = Arities::new(wasm);
|
||||||
|
|
||||||
|
Self { wasm, arity }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime(w: Writer) -> Result<()> {
|
||||||
|
write!(w, "{}", RUNTIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transpile(&self, w: Writer) -> Result<()> {
|
||||||
|
write!(w, "local rt = require(script.Runtime)")?;
|
||||||
|
|
||||||
|
let func_list = self.build_func_list();
|
||||||
|
|
||||||
|
Self::gen_localize(&func_list, w)?;
|
||||||
|
|
||||||
|
write!(w, "local ZERO_i32 = 0 ")?;
|
||||||
|
write!(w, "local ZERO_i64 = 0 ")?;
|
||||||
|
write!(w, "local ZERO_f32 = 0.0 ")?;
|
||||||
|
write!(w, "local ZERO_f64 = 0.0 ")?;
|
||||||
|
|
||||||
|
write_list("FUNC_LIST", self.wasm.functions_space(), w)?;
|
||||||
|
write_list("TABLE_LIST", self.wasm.table_space(), w)?;
|
||||||
|
write_list("MEMORY_LIST", self.wasm.memory_space(), w)?;
|
||||||
|
write_list("GLOBAL_LIST", self.wasm.globals_space(), w)?;
|
||||||
|
|
||||||
|
self.gen_func_list(&func_list, w)?;
|
||||||
|
self.gen_start_point(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Generator<'a> {
|
||||||
fn gen_import_of<T>(&self, w: Writer, lower: &str, cond: T) -> Result<()>
|
fn gen_import_of<T>(&self, w: Writer, lower: &str, cond: T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: Fn(&External) -> bool,
|
T: Fn(&External) -> bool,
|
||||||
@ -702,44 +860,3 @@ impl<'a> Luau<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_list(name: &str, len: usize, w: Writer) -> Result<()> {
|
|
||||||
let len = len.saturating_sub(1);
|
|
||||||
|
|
||||||
write!(w, "local {} = table.create({})", name, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
static LUAU_RUNTIME: &str = include_str!("../../runtime/luau.lua");
|
|
||||||
|
|
||||||
impl<'a> Transpiler<'a> for Luau<'a> {
|
|
||||||
fn new(wasm: &'a Module) -> Self {
|
|
||||||
let arity = Arities::new(wasm);
|
|
||||||
|
|
||||||
Self { wasm, arity }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn runtime(writer: Writer) -> Result<()> {
|
|
||||||
write!(writer, "{}", LUAU_RUNTIME)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transpile(&self, w: Writer) -> Result<()> {
|
|
||||||
write!(w, "local rt = require(script.Runtime)")?;
|
|
||||||
|
|
||||||
let func_list = self.build_func_list();
|
|
||||||
|
|
||||||
Self::gen_localize(&func_list, w)?;
|
|
||||||
|
|
||||||
write!(w, "local ZERO_i32 = 0 ")?;
|
|
||||||
write!(w, "local ZERO_i64 = 0 ")?;
|
|
||||||
write!(w, "local ZERO_f32 = 0.0 ")?;
|
|
||||||
write!(w, "local ZERO_f64 = 0.0 ")?;
|
|
||||||
|
|
||||||
write_list("FUNC_LIST", self.wasm.functions_space(), w)?;
|
|
||||||
write_list("TABLE_LIST", self.wasm.table_space(), w)?;
|
|
||||||
write_list("MEMORY_LIST", self.wasm.memory_space(), w)?;
|
|
||||||
write_list("GLOBAL_LIST", self.wasm.globals_space(), w)?;
|
|
||||||
|
|
||||||
self.gen_func_list(&func_list, w)?;
|
|
||||||
self.gen_start_point(w)
|
|
||||||
}
|
|
||||||
}
|
|
2
codegen-luau/src/lib.rs
Normal file
2
codegen-luau/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mod analyzer;
|
||||||
|
pub mod gen;
|
@ -15,8 +15,11 @@ wasm-smith = "0.8.0"
|
|||||||
git = "https://github.com/paritytech/parity-wasm.git"
|
git = "https://github.com/paritytech/parity-wasm.git"
|
||||||
features = ["multi_value", "sign_ext"]
|
features = ["multi_value", "sign_ext"]
|
||||||
|
|
||||||
[dependencies.wasm]
|
[dependencies.wasm-ast]
|
||||||
path = "../wasm"
|
path = "../wasm-ast"
|
||||||
|
|
||||||
|
[dependencies.codegen-luajit]
|
||||||
|
path = "../codegen-luajit"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "full_transpile"
|
name = "full_transpile"
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
use wasm_smith::Module;
|
use wasm_smith::Module;
|
||||||
|
|
||||||
use wasm::writer::{base::Transpiler, luajit::LuaJIT};
|
use codegen_luajit::gen::Generator;
|
||||||
|
use wasm_ast::writer::Transpiler;
|
||||||
|
|
||||||
// We are not interested in parity_wasm errors.
|
// We are not interested in parity_wasm errors.
|
||||||
// Only 1 edition should need to be tested too.
|
// Only 1 edition should need to be tested too.
|
||||||
@ -13,7 +14,7 @@ libfuzzer_sys::fuzz_target!(|module: Module| {
|
|||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
LuaJIT::new(&wasm)
|
Generator::new(&wasm)
|
||||||
.transpile(&mut std::io::sink())
|
.transpile(&mut std::io::sink())
|
||||||
.expect("LuaJIT should succeed");
|
.expect("LuaJIT should succeed");
|
||||||
});
|
});
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
use parity_wasm::elements::Module as WasmModule;
|
use parity_wasm::elements::Module as WasmModule;
|
||||||
use wasm_smith::Module as SmModule;
|
use wasm_smith::Module as SmModule;
|
||||||
|
|
||||||
use wasm::writer::{base::Transpiler, luajit::LuaJIT};
|
use codegen_luajit::gen::Generator;
|
||||||
|
use wasm_ast::writer::Transpiler;
|
||||||
|
|
||||||
fn fuzz_transformer(wasm: &WasmModule) {
|
fn fuzz_transformer(wasm: &WasmModule) {
|
||||||
let trans = LuaJIT::new(wasm);
|
let trans = Generator::new(wasm);
|
||||||
let _func = trans.build_func_list();
|
let _func = trans.build_func_list();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,10 +5,11 @@ use std::io::Result;
|
|||||||
use parity_wasm::elements::Module as WasmModule;
|
use parity_wasm::elements::Module as WasmModule;
|
||||||
use wasm_smith::Module as SmModule;
|
use wasm_smith::Module as SmModule;
|
||||||
|
|
||||||
use wasm::writer::{base::Transpiler, luajit::LuaJIT};
|
use codegen_luajit::gen::Generator;
|
||||||
|
use wasm_ast::writer::Transpiler;
|
||||||
|
|
||||||
fn fuzz_writer(wasm: &WasmModule) -> Result<()> {
|
fn fuzz_writer(wasm: &WasmModule) -> Result<()> {
|
||||||
let trans = LuaJIT::new(wasm);
|
let trans = Generator::new(wasm);
|
||||||
let list = trans.build_func_list();
|
let list = trans.build_func_list();
|
||||||
|
|
||||||
trans.gen_func_list(&list, &mut std::io::sink())
|
trans.gen_func_list(&list, &mut std::io::sink())
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wasm"
|
name = "wasm-ast"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
@ -3,13 +3,11 @@ use parity_wasm::elements::{
|
|||||||
ValueType,
|
ValueType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use crate::node::{
|
||||||
node::{
|
AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, BinOp, Br, BrIf, BrTable, Call,
|
||||||
AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call,
|
CallIndirect, CmpOp, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Load,
|
||||||
CallIndirect, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize,
|
Memorize, MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement,
|
||||||
MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement, Value,
|
Store, UnOp, Value,
|
||||||
},
|
|
||||||
tag::{BinOp, CmpOp, Load, Store, UnOp},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arity {
|
struct Arity {
|
||||||
@ -73,6 +71,7 @@ pub struct Arities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Arities {
|
impl Arities {
|
||||||
|
#[must_use]
|
||||||
pub fn new(parent: &Module) -> Self {
|
pub fn new(parent: &Module) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ex_arity: Arity::new_ex_list(parent),
|
ex_arity: Arity::new_ex_list(parent),
|
||||||
@ -80,10 +79,12 @@ impl Arities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn len_in(&self) -> usize {
|
pub fn len_in(&self) -> usize {
|
||||||
self.in_arity.len()
|
self.in_arity.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn len_ex(&self) -> usize {
|
pub fn len_ex(&self) -> usize {
|
||||||
self.ex_arity.len()
|
self.ex_arity.len()
|
||||||
}
|
}
|
||||||
@ -138,6 +139,7 @@ fn load_func_at(wasm: &Module, index: usize) -> &FuncBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Builder<'a> {
|
impl<'a> Builder<'a> {
|
||||||
|
#[must_use]
|
||||||
pub fn new(wasm: &'a Module, other: &'a Arities) -> Builder<'a> {
|
pub fn new(wasm: &'a Module, other: &'a Arities) -> Builder<'a> {
|
||||||
Builder {
|
Builder {
|
||||||
wasm,
|
wasm,
|
||||||
@ -149,6 +151,7 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn consume(mut self, index: usize) -> Function {
|
pub fn consume(mut self, index: usize) -> Function {
|
||||||
let func = load_func_at(self.wasm, index);
|
let func = load_func_at(self.wasm, index);
|
||||||
let arity = &self.other.in_arity[index];
|
let arity = &self.other.in_arity[index];
|
@ -1,3 +1,4 @@
|
|||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod node;
|
pub mod node;
|
||||||
mod tag;
|
pub mod visit;
|
||||||
|
pub mod writer;
|
@ -1,3 +1,7 @@
|
|||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use parity_wasm::elements::{BrTableData, ValueType};
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use parity_wasm::elements::{Instruction, SignExtInstruction};
|
use parity_wasm::elements::{Instruction, SignExtInstruction};
|
||||||
@ -22,6 +26,7 @@ pub enum Load {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Load {
|
impl Load {
|
||||||
|
#[must_use]
|
||||||
pub fn as_name(self) -> &'static str {
|
pub fn as_name(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::I32 => "i32",
|
Self::I32 => "i32",
|
||||||
@ -85,6 +90,7 @@ pub enum Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
|
#[must_use]
|
||||||
pub fn as_name(self) -> &'static str {
|
pub fn as_name(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::I32 => "i32",
|
Self::I32 => "i32",
|
||||||
@ -173,6 +179,7 @@ pub enum UnOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl UnOp {
|
impl UnOp {
|
||||||
|
#[must_use]
|
||||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||||
match self {
|
match self {
|
||||||
Self::Clz_I32 => ("clz", "i32"),
|
Self::Clz_I32 => ("clz", "i32"),
|
||||||
@ -323,6 +330,7 @@ pub enum BinOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BinOp {
|
impl BinOp {
|
||||||
|
#[must_use]
|
||||||
pub fn as_operator(self) -> Option<&'static str> {
|
pub fn as_operator(self) -> Option<&'static str> {
|
||||||
let op = match self {
|
let op = match self {
|
||||||
Self::Add_FN => "+",
|
Self::Add_FN => "+",
|
||||||
@ -336,6 +344,7 @@ impl BinOp {
|
|||||||
Some(op)
|
Some(op)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||||
match self {
|
match self {
|
||||||
Self::Add_I32 => ("add", "i32"),
|
Self::Add_I32 => ("add", "i32"),
|
||||||
@ -464,6 +473,7 @@ pub enum CmpOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CmpOp {
|
impl CmpOp {
|
||||||
|
#[must_use]
|
||||||
pub fn as_operator(self) -> Option<&'static str> {
|
pub fn as_operator(self) -> Option<&'static str> {
|
||||||
let op = match self {
|
let op = match self {
|
||||||
Self::Eq_I32 | Self::Eq_I64 | Self::Eq_FN => "==",
|
Self::Eq_I32 | Self::Eq_I64 | Self::Eq_FN => "==",
|
||||||
@ -478,6 +488,7 @@ impl CmpOp {
|
|||||||
Some(op)
|
Some(op)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||||
match self {
|
match self {
|
||||||
Self::Eq_I32 => ("eq", "i32"),
|
Self::Eq_I32 => ("eq", "i32"),
|
||||||
@ -551,3 +562,189 @@ impl TryFrom<&Instruction> for CmpOp {
|
|||||||
Ok(result)
|
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,
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use crate::ast::node::{
|
use crate::node::{
|
||||||
AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call,
|
AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call,
|
||||||
CallIndirect, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize,
|
CallIndirect, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize,
|
||||||
MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement, Value,
|
MemoryGrow, MemorySize, Recall, Return, Select, SetGlobal, SetLocal, Statement, Value,
|
19
wasm-synth/Cargo.toml
Normal file
19
wasm-synth/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm-synth"
|
||||||
|
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"]
|
||||||
|
|
||||||
|
[dependencies.wasm-ast]
|
||||||
|
path = "../wasm-ast"
|
||||||
|
|
||||||
|
[dependencies.codegen-luajit]
|
||||||
|
path = "../codegen-luajit"
|
||||||
|
|
||||||
|
[dependencies.codegen-luau]
|
||||||
|
path = "../codegen-luau"
|
9
wasm/src/main.rs → wasm-synth/src/main.rs
Executable file → Normal file
9
wasm/src/main.rs → wasm-synth/src/main.rs
Executable file → Normal file
@ -1,9 +1,8 @@
|
|||||||
use parity_wasm::{deserialize_file, elements::Module};
|
use parity_wasm::{deserialize_file, elements::Module};
|
||||||
use writer::{base::Transpiler, luajit::LuaJIT, luau::Luau};
|
|
||||||
|
|
||||||
mod analyzer;
|
use codegen_luajit::gen::Generator as LuaJIT;
|
||||||
mod ast;
|
use codegen_luau::gen::Generator as Luau;
|
||||||
mod writer;
|
use wasm_ast::writer::Transpiler;
|
||||||
|
|
||||||
fn parse_module(name: &str) -> Module {
|
fn parse_module(name: &str) -> Module {
|
||||||
let wasm = deserialize_file(name).expect("Failed to parse Wasm file");
|
let wasm = deserialize_file(name).expect("Failed to parse Wasm file");
|
||||||
@ -33,8 +32,8 @@ fn do_translate(name: &str, file: &str) {
|
|||||||
let wasm = &parse_module(file);
|
let wasm = &parse_module(file);
|
||||||
|
|
||||||
match name.to_lowercase().as_str() {
|
match name.to_lowercase().as_str() {
|
||||||
"luau" => run_translator::<Luau>(wasm),
|
|
||||||
"luajit" => run_translator::<LuaJIT>(wasm),
|
"luajit" => run_translator::<LuaJIT>(wasm),
|
||||||
|
"luau" => run_translator::<Luau>(wasm),
|
||||||
_ => panic!("Bad language: {}", name),
|
_ => panic!("Bad language: {}", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,189 +0,0 @@
|
|||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use parity_wasm::elements::{BrTableData, ValueType};
|
|
||||||
|
|
||||||
use super::tag::{BinOp, CmpOp, Load, Store, UnOp};
|
|
||||||
|
|
||||||
#[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 {
|
|
||||||
pub fn is_recalling(&self, wanted: usize) -> bool {
|
|
||||||
match self {
|
|
||||||
Expression::Recall(v) => v.var == wanted,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
mod analyzer;
|
|
||||||
mod ast;
|
|
||||||
pub mod writer;
|
|
@ -1,4 +0,0 @@
|
|||||||
pub mod base;
|
|
||||||
pub mod luajit;
|
|
||||||
pub mod luau;
|
|
||||||
mod shared;
|
|
@ -1,130 +0,0 @@
|
|||||||
use std::{io::Result, ops::Range};
|
|
||||||
|
|
||||||
use parity_wasm::elements::{Internal, Module, NameSection, ResizableLimits};
|
|
||||||
|
|
||||||
use crate::ast::node::Function;
|
|
||||||
|
|
||||||
use super::base::Writer;
|
|
||||||
|
|
||||||
pub fn aux_internal_index(internal: Internal) -> u32 {
|
|
||||||
match internal {
|
|
||||||
Internal::Function(v) | Internal::Table(v) | Internal::Memory(v) | Internal::Global(v) => v,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_limit_max(limits: &ResizableLimits) -> String {
|
|
||||||
match limits.maximum() {
|
|
||||||
Some(v) => v.to_string(),
|
|
||||||
None => "0xFFFF".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_table_init(limit: &ResizableLimits, w: Writer) -> Result<()> {
|
|
||||||
let a = limit.initial();
|
|
||||||
let b = new_limit_max(limit);
|
|
||||||
|
|
||||||
write!(w, "{{ min = {}, max = {}, data = {{}} }}", a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_memory_init(limit: &ResizableLimits, w: Writer) -> Result<()> {
|
|
||||||
let a = limit.initial();
|
|
||||||
let b = new_limit_max(limit);
|
|
||||||
|
|
||||||
write!(w, "rt.allocator.new({}, {})", a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_func_name(wasm: &Module, index: u32, offset: u32, w: Writer) -> Result<()> {
|
|
||||||
let opt = wasm
|
|
||||||
.names_section()
|
|
||||||
.and_then(NameSection::functions)
|
|
||||||
.and_then(|v| v.names().get(index));
|
|
||||||
|
|
||||||
write!(w, "FUNC_LIST")?;
|
|
||||||
|
|
||||||
if let Some(name) = opt {
|
|
||||||
write!(w, "--[[{}]]", name)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(w, "[{}] =", index + offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_in_order(prefix: &str, len: u32, w: Writer) -> Result<()> {
|
|
||||||
if len == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(w, "{}_{}", prefix, 0)?;
|
|
||||||
(1..len).try_for_each(|i| write!(w, ", {}_{}", prefix, i))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_f32(f: f32, w: Writer) -> Result<()> {
|
|
||||||
let sign = if f.is_sign_negative() { "-" } else { "" };
|
|
||||||
|
|
||||||
if f.is_infinite() {
|
|
||||||
write!(w, "{}math.huge ", sign)
|
|
||||||
} else if f.is_nan() {
|
|
||||||
write!(w, "{}0/0 ", sign)
|
|
||||||
} else {
|
|
||||||
write!(w, "{:e} ", f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_f64(f: f64, w: Writer) -> Result<()> {
|
|
||||||
let sign = if f.is_sign_negative() { "-" } else { "" };
|
|
||||||
|
|
||||||
if f.is_infinite() {
|
|
||||||
write!(w, "{}math.huge ", sign)
|
|
||||||
} else if f.is_nan() {
|
|
||||||
write!(w, "{}0/0 ", sign)
|
|
||||||
} else {
|
|
||||||
write!(w, "{:e} ", f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_parameter_list(func: &Function, w: Writer) -> Result<()> {
|
|
||||||
write!(w, "function(")?;
|
|
||||||
write_in_order("param", func.num_param, w)?;
|
|
||||||
write!(w, ")")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_result_list(range: Range<u32>, w: Writer) -> Result<()> {
|
|
||||||
if range.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
range.clone().try_for_each(|i| {
|
|
||||||
if i != range.start {
|
|
||||||
write!(w, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(w, "reg_{}", i)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
write!(w, " = ")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_variable_list(func: &Function, w: Writer) -> Result<()> {
|
|
||||||
if !func.local_list.is_empty() {
|
|
||||||
let num_local = func.local_list.len().try_into().unwrap();
|
|
||||||
|
|
||||||
write!(w, "local ")?;
|
|
||||||
write_in_order("loc", num_local, w)?;
|
|
||||||
write!(w, " = ")?;
|
|
||||||
|
|
||||||
for (i, t) in func.local_list.iter().enumerate() {
|
|
||||||
if i != 0 {
|
|
||||||
write!(w, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(w, "ZERO_{} ", t)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if func.num_stack != 0 {
|
|
||||||
write!(w, "local ")?;
|
|
||||||
write_in_order("reg", func.num_stack, w)?;
|
|
||||||
write!(w, " ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user