Re-structure and decouple AST from generator
This commit is contained in:
parent
9d2d8aa69b
commit
22ea8910ad
@ -1,2 +1,8 @@
|
||||
[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 crate::ast::node::{AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Function};
|
||||
|
||||
use super::visit::{Driver, Visitor};
|
||||
use wasm_ast::{
|
||||
node::{AnyBinOp, AnyCmpOp, AnyLoad, AnyStore, AnyUnOp, Function},
|
||||
visit::{Driver, Visitor},
|
||||
};
|
||||
|
||||
struct Visit {
|
||||
result: BTreeSet<(&'static str, &'static str)>,
|
@ -1,8 +1,9 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::ast::node::{AnyLoad, AnyStore, Function, MemoryGrow, MemorySize};
|
||||
|
||||
use super::visit::{Driver, Visitor};
|
||||
use wasm_ast::{
|
||||
node::{AnyLoad, AnyStore, Function, MemoryGrow, MemorySize},
|
||||
visit::{Driver, Visitor},
|
||||
};
|
||||
|
||||
struct Visit {
|
||||
result: BTreeSet<u8>,
|
@ -1,3 +1,2 @@
|
||||
pub mod localize;
|
||||
pub mod memory;
|
||||
mod visit;
|
@ -1,26 +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::{
|
||||
use wasm_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,
|
||||
},
|
||||
},
|
||||
writer::{Transpiler, Writer},
|
||||
};
|
||||
|
||||
use super::{
|
||||
base::{Transpiler, Writer},
|
||||
shared::{
|
||||
aux_internal_index, write_f32, write_f64, write_func_name, write_memory_init,
|
||||
write_parameter_list, write_result_list, write_table_init, write_variable_list,
|
||||
},
|
||||
};
|
||||
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<()> {
|
||||
// FIXME: Badly generated WASM will produce the wrong constant.
|
||||
@ -479,12 +603,48 @@ impl Driver for Function {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LuaJIT<'a> {
|
||||
pub struct Generator<'a> {
|
||||
wasm: &'a Module,
|
||||
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<()>
|
||||
where
|
||||
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,26 +1,149 @@
|
||||
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::{
|
||||
use wasm_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,
|
||||
},
|
||||
},
|
||||
writer::{Transpiler, Writer},
|
||||
};
|
||||
|
||||
use super::{
|
||||
base::{Transpiler, Writer},
|
||||
shared::{
|
||||
aux_internal_index, write_f32, write_f64, write_func_name, write_memory_init,
|
||||
write_parameter_list, write_result_list, write_table_init, write_variable_list,
|
||||
},
|
||||
};
|
||||
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<()> {
|
||||
// FIXME: Badly generated WASM will produce the wrong constant.
|
||||
@ -476,12 +599,47 @@ impl Driver for Function {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Luau<'a> {
|
||||
pub struct Generator<'a> {
|
||||
wasm: &'a Module,
|
||||
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<()>
|
||||
where
|
||||
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"
|
||||
features = ["multi_value", "sign_ext"]
|
||||
|
||||
[dependencies.wasm]
|
||||
path = "../wasm"
|
||||
[dependencies.wasm-ast]
|
||||
path = "../wasm-ast"
|
||||
|
||||
[dependencies.codegen-luajit]
|
||||
path = "../codegen-luajit"
|
||||
|
||||
[[bin]]
|
||||
name = "full_transpile"
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
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.
|
||||
// Only 1 edition should need to be tested too.
|
||||
@ -13,7 +14,7 @@ libfuzzer_sys::fuzz_target!(|module: Module| {
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
LuaJIT::new(&wasm)
|
||||
Generator::new(&wasm)
|
||||
.transpile(&mut std::io::sink())
|
||||
.expect("LuaJIT should succeed");
|
||||
});
|
||||
|
@ -3,10 +3,11 @@
|
||||
use parity_wasm::elements::Module as WasmModule;
|
||||
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) {
|
||||
let trans = LuaJIT::new(wasm);
|
||||
let trans = Generator::new(wasm);
|
||||
let _func = trans.build_func_list();
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,11 @@ use std::io::Result;
|
||||
use parity_wasm::elements::Module as WasmModule;
|
||||
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<()> {
|
||||
let trans = LuaJIT::new(wasm);
|
||||
let trans = Generator::new(wasm);
|
||||
let list = trans.build_func_list();
|
||||
|
||||
trans.gen_func_list(&list, &mut std::io::sink())
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "wasm"
|
||||
name = "wasm-ast"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
@ -3,13 +3,11 @@ use parity_wasm::elements::{
|
||||
ValueType,
|
||||
};
|
||||
|
||||
use super::{
|
||||
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,
|
||||
},
|
||||
tag::{BinOp, CmpOp, Load, Store, UnOp},
|
||||
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 {
|
||||
@ -73,6 +71,7 @@ pub struct Arities {
|
||||
}
|
||||
|
||||
impl Arities {
|
||||
#[must_use]
|
||||
pub fn new(parent: &Module) -> Self {
|
||||
Self {
|
||||
ex_arity: Arity::new_ex_list(parent),
|
||||
@ -80,10 +79,12 @@ impl Arities {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn len_in(&self) -> usize {
|
||||
self.in_arity.len()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn len_ex(&self) -> usize {
|
||||
self.ex_arity.len()
|
||||
}
|
||||
@ -138,6 +139,7 @@ fn load_func_at(wasm: &Module, index: usize) -> &FuncBody {
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
#[must_use]
|
||||
pub fn new(wasm: &'a Module, other: &'a Arities) -> Builder<'a> {
|
||||
Builder {
|
||||
wasm,
|
||||
@ -149,6 +151,7 @@ impl<'a> Builder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[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];
|
@ -1,3 +1,4 @@
|
||||
pub mod builder;
|
||||
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 parity_wasm::elements::{Instruction, SignExtInstruction};
|
||||
@ -22,6 +26,7 @@ pub enum Load {
|
||||
}
|
||||
|
||||
impl Load {
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> &'static str {
|
||||
match self {
|
||||
Self::I32 => "i32",
|
||||
@ -85,6 +90,7 @@ pub enum Store {
|
||||
}
|
||||
|
||||
impl Store {
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> &'static str {
|
||||
match self {
|
||||
Self::I32 => "i32",
|
||||
@ -173,6 +179,7 @@ pub enum UnOp {
|
||||
}
|
||||
|
||||
impl UnOp {
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
Self::Clz_I32 => ("clz", "i32"),
|
||||
@ -323,6 +330,7 @@ pub enum BinOp {
|
||||
}
|
||||
|
||||
impl BinOp {
|
||||
#[must_use]
|
||||
pub fn as_operator(self) -> Option<&'static str> {
|
||||
let op = match self {
|
||||
Self::Add_FN => "+",
|
||||
@ -336,6 +344,7 @@ impl BinOp {
|
||||
Some(op)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
Self::Add_I32 => ("add", "i32"),
|
||||
@ -464,6 +473,7 @@ pub enum CmpOp {
|
||||
}
|
||||
|
||||
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 => "==",
|
||||
@ -478,6 +488,7 @@ impl CmpOp {
|
||||
Some(op)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn as_name(self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
Self::Eq_I32 => ("eq", "i32"),
|
||||
@ -551,3 +562,189 @@ impl TryFrom<&Instruction> for CmpOp {
|
||||
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,
|
||||
CallIndirect, Else, Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize,
|
||||
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 writer::{base::Transpiler, luajit::LuaJIT, luau::Luau};
|
||||
|
||||
mod analyzer;
|
||||
mod ast;
|
||||
mod writer;
|
||||
use codegen_luajit::gen::Generator as LuaJIT;
|
||||
use codegen_luau::gen::Generator as Luau;
|
||||
use wasm_ast::writer::Transpiler;
|
||||
|
||||
fn parse_module(name: &str) -> Module {
|
||||
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);
|
||||
|
||||
match name.to_lowercase().as_str() {
|
||||
"luau" => run_translator::<Luau>(wasm),
|
||||
"luajit" => run_translator::<LuaJIT>(wasm),
|
||||
"luau" => run_translator::<Luau>(wasm),
|
||||
_ => 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