From 22ea8910ad2e67c8cc64a6cf2ec886edf33ef526 Mon Sep 17 00:00:00 2001 From: Rerumu Date: Tue, 8 Feb 2022 17:39:14 -0500 Subject: [PATCH] Re-structure and decouple AST from generator --- Cargo.toml | 8 +- codegen-luajit/Cargo.toml | 13 + .../runtime/runtime.lua | 0 .../src/analyzer/localize.rs | 7 +- .../src/analyzer/memory.rs | 7 +- {wasm => codegen-luajit}/src/analyzer/mod.rs | 1 - .../luajit.rs => codegen-luajit/src/gen.rs | 243 +++++++++++++----- codegen-luajit/src/lib.rs | 4 + codegen-luau/Cargo.toml | 13 + {wasm => codegen-luau}/runtime/numeric.lua | 0 .../runtime/runtime.lua | 0 codegen-luau/src/analyzer/localize.rs | 60 +++++ codegen-luau/src/analyzer/memory.rs | 38 +++ codegen-luau/src/analyzer/mod.rs | 2 + .../writer/luau.rs => codegen-luau/src/gen.rs | 239 ++++++++++++----- codegen-luau/src/lib.rs | 2 + fuzz/Cargo.toml | 7 +- fuzz/fuzz_targets/full_transpile.rs | 5 +- fuzz/fuzz_targets/just_builder.rs | 5 +- fuzz/fuzz_targets/just_writer.rs | 5 +- {wasm => wasm-ast}/Cargo.toml | 2 +- {wasm/src/ast => wasm-ast/src}/builder.rs | 17 +- wasm/src/ast/mod.rs => wasm-ast/src/lib.rs | 3 +- wasm/src/ast/tag.rs => wasm-ast/src/node.rs | 197 ++++++++++++++ {wasm/src/analyzer => wasm-ast/src}/visit.rs | 2 +- .../writer/base.rs => wasm-ast/src/writer.rs | 0 wasm-synth/Cargo.toml | 19 ++ {wasm => wasm-synth}/src/main.rs | 9 +- wasm/src/ast/node.rs | 189 -------------- wasm/src/lib.rs | 3 - wasm/src/writer/mod.rs | 4 - wasm/src/writer/shared.rs | 130 ---------- 32 files changed, 753 insertions(+), 481 deletions(-) create mode 100644 codegen-luajit/Cargo.toml rename wasm/runtime/luajit.lua => codegen-luajit/runtime/runtime.lua (100%) rename {wasm => codegen-luajit}/src/analyzer/localize.rs (88%) rename {wasm => codegen-luajit}/src/analyzer/memory.rs (83%) rename {wasm => codegen-luajit}/src/analyzer/mod.rs (75%) rename wasm/src/writer/luajit.rs => codegen-luajit/src/gen.rs (82%) create mode 100644 codegen-luajit/src/lib.rs create mode 100644 codegen-luau/Cargo.toml rename {wasm => codegen-luau}/runtime/numeric.lua (100%) rename wasm/runtime/luau.lua => codegen-luau/runtime/runtime.lua (100%) create mode 100644 codegen-luau/src/analyzer/localize.rs create mode 100644 codegen-luau/src/analyzer/memory.rs create mode 100644 codegen-luau/src/analyzer/mod.rs rename wasm/src/writer/luau.rs => codegen-luau/src/gen.rs (82%) create mode 100644 codegen-luau/src/lib.rs rename {wasm => wasm-ast}/Cargo.toml (93%) rename {wasm/src/ast => wasm-ast/src}/builder.rs (97%) rename wasm/src/ast/mod.rs => wasm-ast/src/lib.rs (50%) rename wasm/src/ast/tag.rs => wasm-ast/src/node.rs (83%) rename {wasm/src/analyzer => wasm-ast/src}/visit.rs (99%) rename wasm/src/writer/base.rs => wasm-ast/src/writer.rs (100%) create mode 100644 wasm-synth/Cargo.toml rename {wasm => wasm-synth}/src/main.rs (93%) mode change 100755 => 100644 delete mode 100644 wasm/src/ast/node.rs delete mode 100644 wasm/src/lib.rs delete mode 100644 wasm/src/writer/mod.rs delete mode 100644 wasm/src/writer/shared.rs diff --git a/Cargo.toml b/Cargo.toml index c1c6c32..7967c11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,8 @@ [workspace] -members = ["fuzz", "wasm"] +members = [ + "codegen-luajit", + "codegen-luau", + "fuzz", + "wasm-ast", + "wasm-synth" +] diff --git a/codegen-luajit/Cargo.toml b/codegen-luajit/Cargo.toml new file mode 100644 index 0000000..6562983 --- /dev/null +++ b/codegen-luajit/Cargo.toml @@ -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"] diff --git a/wasm/runtime/luajit.lua b/codegen-luajit/runtime/runtime.lua similarity index 100% rename from wasm/runtime/luajit.lua rename to codegen-luajit/runtime/runtime.lua diff --git a/wasm/src/analyzer/localize.rs b/codegen-luajit/src/analyzer/localize.rs similarity index 88% rename from wasm/src/analyzer/localize.rs rename to codegen-luajit/src/analyzer/localize.rs index 22ebe76..ff75881 100644 --- a/wasm/src/analyzer/localize.rs +++ b/codegen-luajit/src/analyzer/localize.rs @@ -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)>, diff --git a/wasm/src/analyzer/memory.rs b/codegen-luajit/src/analyzer/memory.rs similarity index 83% rename from wasm/src/analyzer/memory.rs rename to codegen-luajit/src/analyzer/memory.rs index 44b38a8..f9c1f6b 100644 --- a/wasm/src/analyzer/memory.rs +++ b/codegen-luajit/src/analyzer/memory.rs @@ -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, diff --git a/wasm/src/analyzer/mod.rs b/codegen-luajit/src/analyzer/mod.rs similarity index 75% rename from wasm/src/analyzer/mod.rs rename to codegen-luajit/src/analyzer/mod.rs index bb93ed4..6e4ed64 100644 --- a/wasm/src/analyzer/mod.rs +++ b/codegen-luajit/src/analyzer/mod.rs @@ -1,3 +1,2 @@ pub mod localize; pub mod memory; -mod visit; diff --git a/wasm/src/writer/luajit.rs b/codegen-luajit/src/gen.rs similarity index 82% rename from wasm/src/writer/luajit.rs rename to codegen-luajit/src/gen.rs index 07fa115..f987675 100644 --- a/wasm/src/writer/luajit.rs +++ b/codegen-luajit/src/gen.rs @@ -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 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 parity_wasm::elements::{ + External, ImportCountType, Instruction, Internal, Module, NameSection, ResizableLimits, }; -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 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 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, 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. for inst in code { @@ -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(&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) - } -} diff --git a/codegen-luajit/src/lib.rs b/codegen-luajit/src/lib.rs new file mode 100644 index 0000000..7d1eb64 --- /dev/null +++ b/codegen-luajit/src/lib.rs @@ -0,0 +1,4 @@ +pub static RUNTIME: &str = include_str!("../runtime/runtime.lua"); + +mod analyzer; +pub mod gen; diff --git a/codegen-luau/Cargo.toml b/codegen-luau/Cargo.toml new file mode 100644 index 0000000..0311f0a --- /dev/null +++ b/codegen-luau/Cargo.toml @@ -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"] diff --git a/wasm/runtime/numeric.lua b/codegen-luau/runtime/numeric.lua similarity index 100% rename from wasm/runtime/numeric.lua rename to codegen-luau/runtime/numeric.lua diff --git a/wasm/runtime/luau.lua b/codegen-luau/runtime/runtime.lua similarity index 100% rename from wasm/runtime/luau.lua rename to codegen-luau/runtime/runtime.lua diff --git a/codegen-luau/src/analyzer/localize.rs b/codegen-luau/src/analyzer/localize.rs new file mode 100644 index 0000000..ff75881 --- /dev/null +++ b/codegen-luau/src/analyzer/localize.rs @@ -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 +} diff --git a/codegen-luau/src/analyzer/memory.rs b/codegen-luau/src/analyzer/memory.rs new file mode 100644 index 0000000..f9c1f6b --- /dev/null +++ b/codegen-luau/src/analyzer/memory.rs @@ -0,0 +1,38 @@ +use std::collections::BTreeSet; + +use wasm_ast::{ + node::{AnyLoad, AnyStore, Function, MemoryGrow, MemorySize}, + visit::{Driver, Visitor}, +}; + +struct Visit { + result: BTreeSet, +} + +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 { + let mut visit = Visit { + result: BTreeSet::new(), + }; + + func.accept(&mut visit); + + visit.result +} diff --git a/codegen-luau/src/analyzer/mod.rs b/codegen-luau/src/analyzer/mod.rs new file mode 100644 index 0000000..6e4ed64 --- /dev/null +++ b/codegen-luau/src/analyzer/mod.rs @@ -0,0 +1,2 @@ +pub mod localize; +pub mod memory; diff --git a/wasm/src/writer/luau.rs b/codegen-luau/src/gen.rs similarity index 82% rename from wasm/src/writer/luau.rs rename to codegen-luau/src/gen.rs index 1c15a89..5a9b4f8 100644 --- a/wasm/src/writer/luau.rs +++ b/codegen-luau/src/gen.rs @@ -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 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 parity_wasm::elements::{ + External, ImportCountType, Instruction, Internal, Module, NameSection, ResizableLimits, }; -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 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::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, 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. for inst in code { @@ -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(&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) - } -} diff --git a/codegen-luau/src/lib.rs b/codegen-luau/src/lib.rs new file mode 100644 index 0000000..38b9f4c --- /dev/null +++ b/codegen-luau/src/lib.rs @@ -0,0 +1,2 @@ +mod analyzer; +pub mod gen; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 875ec6a..2435da3 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -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" diff --git a/fuzz/fuzz_targets/full_transpile.rs b/fuzz/fuzz_targets/full_transpile.rs index 6c82408..77606d2 100644 --- a/fuzz/fuzz_targets/full_transpile.rs +++ b/fuzz/fuzz_targets/full_transpile.rs @@ -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"); }); diff --git a/fuzz/fuzz_targets/just_builder.rs b/fuzz/fuzz_targets/just_builder.rs index 59e8083..d1de985 100644 --- a/fuzz/fuzz_targets/just_builder.rs +++ b/fuzz/fuzz_targets/just_builder.rs @@ -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(); } diff --git a/fuzz/fuzz_targets/just_writer.rs b/fuzz/fuzz_targets/just_writer.rs index d843e39..458136e 100644 --- a/fuzz/fuzz_targets/just_writer.rs +++ b/fuzz/fuzz_targets/just_writer.rs @@ -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()) diff --git a/wasm/Cargo.toml b/wasm-ast/Cargo.toml similarity index 93% rename from wasm/Cargo.toml rename to wasm-ast/Cargo.toml index e9aee21..8684726 100644 --- a/wasm/Cargo.toml +++ b/wasm-ast/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wasm" +name = "wasm-ast" version = "0.1.0" edition = "2021" diff --git a/wasm/src/ast/builder.rs b/wasm-ast/src/builder.rs similarity index 97% rename from wasm/src/ast/builder.rs rename to wasm-ast/src/builder.rs index 5f14897..b76bb30 100644 --- a/wasm/src/ast/builder.rs +++ b/wasm-ast/src/builder.rs @@ -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]; diff --git a/wasm/src/ast/mod.rs b/wasm-ast/src/lib.rs similarity index 50% rename from wasm/src/ast/mod.rs rename to wasm-ast/src/lib.rs index 055c273..96401e5 100644 --- a/wasm/src/ast/mod.rs +++ b/wasm-ast/src/lib.rs @@ -1,3 +1,4 @@ pub mod builder; pub mod node; -mod tag; +pub mod visit; +pub mod writer; diff --git a/wasm/src/ast/tag.rs b/wasm-ast/src/node.rs similarity index 83% rename from wasm/src/ast/tag.rs rename to wasm-ast/src/node.rs index 0471977..753d125 100644 --- a/wasm/src/ast/tag.rs +++ b/wasm-ast/src/node.rs @@ -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, + pub a: Box, + pub b: Box, +} + +pub struct GetLocal { + pub var: u32, +} + +pub struct GetGlobal { + pub var: u32, +} + +pub struct AnyLoad { + pub op: Load, + pub offset: u32, + pub pointer: Box, +} + +pub struct MemorySize { + pub memory: u8, +} + +pub struct MemoryGrow { + pub memory: u8, + pub value: Box, +} + +#[derive(Clone, Copy)] +pub enum Value { + I32(i32), + I64(i64), + F32(f32), + F64(f64), +} + +pub struct AnyUnOp { + pub op: UnOp, + pub rhs: Box, +} + +pub struct AnyBinOp { + pub op: BinOp, + pub lhs: Box, + pub rhs: Box, +} + +pub struct AnyCmpOp { + pub op: CmpOp, + pub lhs: Box, + pub rhs: Box, +} + +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, +} + +pub struct Backward { + pub body: Vec, +} + +pub struct Else { + pub body: Vec, +} + +pub struct If { + pub cond: Expression, + pub truthy: Vec, + pub falsey: Option, +} + +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, +} + +pub struct Call { + pub func: u32, + pub result: Range, + pub param_list: Vec, +} + +pub struct CallIndirect { + pub table: u8, + pub index: Expression, + pub result: Range, + pub param_list: Vec, +} + +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, + pub num_param: u32, + pub num_stack: u32, + pub body: Forward, +} diff --git a/wasm/src/analyzer/visit.rs b/wasm-ast/src/visit.rs similarity index 99% rename from wasm/src/analyzer/visit.rs rename to wasm-ast/src/visit.rs index 948d280..463f48b 100644 --- a/wasm/src/analyzer/visit.rs +++ b/wasm-ast/src/visit.rs @@ -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, diff --git a/wasm/src/writer/base.rs b/wasm-ast/src/writer.rs similarity index 100% rename from wasm/src/writer/base.rs rename to wasm-ast/src/writer.rs diff --git a/wasm-synth/Cargo.toml b/wasm-synth/Cargo.toml new file mode 100644 index 0000000..b176160 --- /dev/null +++ b/wasm-synth/Cargo.toml @@ -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" diff --git a/wasm/src/main.rs b/wasm-synth/src/main.rs old mode 100755 new mode 100644 similarity index 93% rename from wasm/src/main.rs rename to wasm-synth/src/main.rs index e964215..05e54b9 --- a/wasm/src/main.rs +++ b/wasm-synth/src/main.rs @@ -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::(wasm), "luajit" => run_translator::(wasm), + "luau" => run_translator::(wasm), _ => panic!("Bad language: {}", name), } } diff --git a/wasm/src/ast/node.rs b/wasm/src/ast/node.rs deleted file mode 100644 index dd833bf..0000000 --- a/wasm/src/ast/node.rs +++ /dev/null @@ -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, - pub a: Box, - pub b: Box, -} - -pub struct GetLocal { - pub var: u32, -} - -pub struct GetGlobal { - pub var: u32, -} - -pub struct AnyLoad { - pub op: Load, - pub offset: u32, - pub pointer: Box, -} - -pub struct MemorySize { - pub memory: u8, -} - -pub struct MemoryGrow { - pub memory: u8, - pub value: Box, -} - -#[derive(Clone, Copy)] -pub enum Value { - I32(i32), - I64(i64), - F32(f32), - F64(f64), -} - -pub struct AnyUnOp { - pub op: UnOp, - pub rhs: Box, -} - -pub struct AnyBinOp { - pub op: BinOp, - pub lhs: Box, - pub rhs: Box, -} - -pub struct AnyCmpOp { - pub op: CmpOp, - pub lhs: Box, - pub rhs: Box, -} - -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, -} - -pub struct Backward { - pub body: Vec, -} - -pub struct Else { - pub body: Vec, -} - -pub struct If { - pub cond: Expression, - pub truthy: Vec, - pub falsey: Option, -} - -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, -} - -pub struct Call { - pub func: u32, - pub result: Range, - pub param_list: Vec, -} - -pub struct CallIndirect { - pub table: u8, - pub index: Expression, - pub result: Range, - pub param_list: Vec, -} - -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, - pub num_param: u32, - pub num_stack: u32, - pub body: Forward, -} diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs deleted file mode 100644 index b52a947..0000000 --- a/wasm/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod analyzer; -mod ast; -pub mod writer; diff --git a/wasm/src/writer/mod.rs b/wasm/src/writer/mod.rs deleted file mode 100644 index f253bdb..0000000 --- a/wasm/src/writer/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod base; -pub mod luajit; -pub mod luau; -mod shared; diff --git a/wasm/src/writer/shared.rs b/wasm/src/writer/shared.rs deleted file mode 100644 index fd7cc61..0000000 --- a/wasm/src/writer/shared.rs +++ /dev/null @@ -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, 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(()) -}