From c52170ba6eacd5b370660fad2e0568dc15adcedd Mon Sep 17 00:00:00 2001 From: Rerumu Date: Sat, 25 Jun 2022 02:00:39 -0400 Subject: [PATCH] Refactor LuaJIT codegen to use `wasmparser` --- codegen/luajit/Cargo.toml | 7 +- codegen/luajit/src/backend/statement.rs | 13 +- codegen/luajit/src/bin/wasm2luajit.rs | 21 +- codegen/luajit/src/translator.rs | 255 ++++++++++------------ dev-test/fuzz_targets/luajit_translate.rs | 10 +- dev-test/tests/luajit_translate.rs | 3 +- 6 files changed, 135 insertions(+), 174 deletions(-) diff --git a/codegen/luajit/Cargo.toml b/codegen/luajit/Cargo.toml index 51c7c6f..5efdbd3 100644 --- a/codegen/luajit/Cargo.toml +++ b/codegen/luajit/Cargo.toml @@ -3,12 +3,11 @@ name = "codegen-luajit" version = "0.9.0" edition = "2021" +[dependencies] +wasmparser = "0.86.0" + [dependencies.wasm-ast] path = "../../wasm-ast" -[dependencies.parity-wasm] -git = "https://github.com/paritytech/parity-wasm.git" -features = ["multi_value", "sign_ext"] - [[bin]] name = "wasm2luajit" diff --git a/codegen/luajit/src/backend/statement.rs b/codegen/luajit/src/backend/statement.rs index 83edbce..d3d86dc 100644 --- a/codegen/luajit/src/backend/statement.rs +++ b/codegen/luajit/src/backend/statement.rs @@ -3,11 +3,11 @@ use std::{ ops::Range, }; -use parity_wasm::elements::ValueType; use wasm_ast::node::{ Backward, Br, BrIf, BrTable, Call, CallIndirect, Forward, FuncData, If, MemoryGrow, SetGlobal, SetLocal, SetTemporary, Statement, StoreAt, Terminator, }; +use wasmparser::ValType; use crate::analyzer::br_table; @@ -292,14 +292,9 @@ fn write_parameter_list(ast: &FuncData, w: &mut dyn Write) -> Result<()> { fn write_variable_list(ast: &FuncData, w: &mut dyn Write) -> Result<()> { let mut total = 0; - for data in ast.local_data().iter().filter(|v| v.count() != 0) { - let range = total..total + usize::try_from(data.count()).unwrap(); - let typed = if data.value_type() == ValueType::I64 { - "0LL" - } else { - "0" - } - .as_bytes(); + for data in ast.local_data().iter().filter(|v| v.0 != 0) { + let range = total..total + usize::try_from(data.0).unwrap(); + let typed = if data.1 == ValType::I64 { "0LL" } else { "0" }.as_bytes(); total = range.end; diff --git a/codegen/luajit/src/bin/wasm2luajit.rs b/codegen/luajit/src/bin/wasm2luajit.rs index 8a040a5..e08684f 100644 --- a/codegen/luajit/src/bin/wasm2luajit.rs +++ b/codegen/luajit/src/bin/wasm2luajit.rs @@ -1,12 +1,11 @@ use std::io::{Result, Write}; -use parity_wasm::{deserialize_file, elements::Module}; +use wasm_ast::module::Module; -fn load_module(name: &str) -> Module { - deserialize_file(name) - .expect("Failed to parse WebAssembly file") - .parse_names() - .unwrap_or_else(|v| v.1) +fn load_arg_source() -> Result> { + let name = std::env::args().nth(1).expect("usage: wasm2luajit "); + + std::fs::read(name) } fn do_runtime(lock: &mut dyn Write) -> Result<()> { @@ -18,14 +17,8 @@ fn do_runtime(lock: &mut dyn Write) -> Result<()> { } fn main() -> Result<()> { - let wasm = match std::env::args().nth(1) { - Some(name) => load_module(&name), - None => { - eprintln!("usage: wasm2luajit "); - - return Ok(()); - } - }; + let data = load_arg_source()?; + let wasm = Module::from_data(&data); let lock = &mut std::io::stdout().lock(); diff --git a/codegen/luajit/src/translator.rs b/codegen/luajit/src/translator.rs index d9067ad..26606d7 100644 --- a/codegen/luajit/src/translator.rs +++ b/codegen/luajit/src/translator.rs @@ -3,42 +3,41 @@ use std::{ io::{Result, Write}, }; -use parity_wasm::elements::{ - External, ImportCountType, Instruction, Internal, Module, NameSection, ResizableLimits, -}; - use wasm_ast::{ - builder::{Builder, TypeInfo}, + factory::Factory, + module::{External, Module, TypeInfo}, node::{FuncData, Statement}, }; +use wasmparser::{ + Data, DataKind, Element, ElementItem, ElementKind, Export, FunctionBody, Import, InitExpr, + Operator, OperatorsReader, +}; use crate::{ analyzer::localize, backend::manager::{Driver, Manager}, }; -fn to_internal_index(internal: Internal) -> u32 { - match internal { - Internal::Function(v) | Internal::Table(v) | Internal::Memory(v) | Internal::Global(v) => v, +trait AsIEName { + fn as_ie_name(&self) -> &str; +} + +impl AsIEName for External { + fn as_ie_name(&self) -> &str { + match self { + External::Func => "func_list", + External::Table => "table_list", + External::Memory => "memory_list", + External::Global => "global_list", + External::Tag => unimplemented!(), + } } } -fn limit_data_of(limits: &ResizableLimits) -> (u32, u32) { - let max = limits.maximum().unwrap_or(0xFFFF); +fn reader_to_code(reader: OperatorsReader) -> Vec { + let parsed: std::result::Result<_, _> = reader.into_iter().collect(); - (limits.initial(), max) -} - -fn write_table_init(limit: &ResizableLimits, w: &mut dyn Write) -> Result<()> { - let (a, b) = limit_data_of(limit); - - write!(w, "{{ min = {a}, max = {b}, data = {{}} }}") -} - -fn write_memory_init(limit: &ResizableLimits, w: &mut dyn Write) -> Result<()> { - let (a, b) = limit_data_of(limit); - - write!(w, "rt.allocator.new({a}, {b})") + parsed.unwrap() } fn write_named_array(name: &str, len: usize, w: &mut dyn Write) -> Result<()> { @@ -48,8 +47,9 @@ fn write_named_array(name: &str, len: usize, w: &mut dyn Write) -> Result<()> { write!(w, "local {name} = table_new({len}, {hash})") } -fn write_constant(code: &[Instruction], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { - let func = Builder::from_type_info(type_info).build_anonymous(code); +fn write_constant(init: &InitExpr, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { + let code = reader_to_code(init.get_operators_reader()); + let func = Factory::from_type_info(type_info).create_anonymous(&code); if let Some(Statement::SetTemporary(stat)) = func.code().code().last() { stat.value().write(&mut Manager::default(), w) @@ -58,128 +58,118 @@ fn write_constant(code: &[Instruction], type_info: &TypeInfo, w: &mut dyn Write) } } -fn write_import_of(wasm: &Module, lower: &str, cond: T, w: &mut dyn Write) -> Result<()> -where - T: Fn(&External) -> bool, -{ - let import = match wasm.import_section() { - Some(v) => v.entries(), - None => return Ok(()), - }; +fn write_import_of(list: &[Import], wanted: External, w: &mut dyn Write) -> Result<()> { + let lower = wanted.as_ie_name(); let upper = lower.to_uppercase(); - for (i, v) in import.iter().filter(|v| cond(v.external())).enumerate() { - let field = v.field(); - let module = v.module(); - - write!(w, r#"{upper}[{i}] = wasm["{module}"].{lower}["{field}"]"#)?; + for (i, Import { name, module, .. }) in list + .iter() + .filter(|v| External::from(v.ty) == wanted) + .enumerate() + { + write!(w, r#"{upper}[{i}] = wasm["{module}"].{lower}["{name}"]"#)?; } Ok(()) } -fn write_export_of(wasm: &Module, lower: &str, cond: T, w: &mut dyn Write) -> Result<()> -where - T: Fn(&Internal) -> bool, -{ - let export = match wasm.export_section() { - Some(v) => v.entries(), - None => return Ok(()), - }; +fn write_export_of(list: &[Export], wanted: External, w: &mut dyn Write) -> Result<()> { + let lower = wanted.as_ie_name(); let upper = lower.to_uppercase(); write!(w, "{lower} = {{")?; - for v in export.iter().filter(|v| cond(v.internal())) { - let field = v.field(); - let index = to_internal_index(*v.internal()); - - write!(w, r#"["{field}"] = {upper}[{index}],"#)?; + for Export { name, index, .. } in list.iter().filter(|v| External::from(v.kind) == wanted) { + write!(w, r#"["{name}"] = {upper}[{index}],"#)?; } write!(w, "}},") } -fn write_import_list(wasm: &Module, w: &mut dyn Write) -> Result<()> { - write_import_of(wasm, "func_list", |v| matches!(v, External::Function(_)), w)?; - write_import_of(wasm, "table_list", |v| matches!(v, External::Table(_)), w)?; - write_import_of(wasm, "memory_list", |v| matches!(v, External::Memory(_)), w)?; - write_import_of(wasm, "global_list", |v| matches!(v, External::Global(_)), w) +fn write_import_list(list: &[Import], w: &mut dyn Write) -> Result<()> { + write_import_of(list, External::Func, w)?; + write_import_of(list, External::Table, w)?; + write_import_of(list, External::Memory, w)?; + write_import_of(list, External::Global, w) } -fn write_export_list(wasm: &Module, w: &mut dyn Write) -> Result<()> { - write_export_of(wasm, "func_list", |v| matches!(v, Internal::Function(_)), w)?; - write_export_of(wasm, "table_list", |v| matches!(v, Internal::Table(_)), w)?; - write_export_of(wasm, "memory_list", |v| matches!(v, Internal::Memory(_)), w)?; - write_export_of(wasm, "global_list", |v| matches!(v, Internal::Global(_)), w) +fn write_export_list(list: &[Export], w: &mut dyn Write) -> Result<()> { + write_export_of(list, External::Func, w)?; + write_export_of(list, External::Table, w)?; + write_export_of(list, External::Memory, w)?; + write_export_of(list, External::Global, w) } fn write_table_list(wasm: &Module, w: &mut dyn Write) -> Result<()> { - let table = match wasm.table_section() { - Some(v) => v.entries(), - None => return Ok(()), - }; - let offset = wasm.import_count(ImportCountType::Table); + let offset = wasm.import_count(External::Table); + let table = wasm.table_section(); - for (i, v) in table.iter().enumerate() { - write!(w, "TABLE_LIST[{}] =", i + offset)?; - write_table_init(v.limits(), w)?; + for (i, table) in table.iter().enumerate() { + let index = offset + i; + let min = table.initial; + let max = table.maximum.unwrap_or(0xFFFF); + + write!( + w, + "TABLE_LIST[{index}] = {{ min = {min}, max = {max}, data = {{}} }}" + )?; } Ok(()) } fn write_memory_list(wasm: &Module, w: &mut dyn Write) -> Result<()> { - let memory = match wasm.memory_section() { - Some(v) => v.entries(), - None => return Ok(()), - }; - let offset = wasm.import_count(ImportCountType::Memory); + let offset = wasm.import_count(External::Memory); + let memory = wasm.memory_section(); for (i, v) in memory.iter().enumerate() { - write!(w, "MEMORY_LIST[{}] =", i + offset)?; - write_memory_init(v.limits(), w)?; + let index = offset + i; + let min = v.initial; + let max = v.maximum.unwrap_or(0xFFFF); + + write!(w, "MEMORY_LIST[{index}] = rt.allocator.new({min}, {max})")?; } Ok(()) } fn write_global_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { - let global = match wasm.global_section() { - Some(v) => v, - None => return Ok(()), - }; - let offset = wasm.import_count(ImportCountType::Global); + let offset = wasm.import_count(External::Global); + let global = wasm.global_section(); - for (i, v) in global.entries().iter().enumerate() { + for (i, v) in global.iter().enumerate() { write!(w, "GLOBAL_LIST[{}] = {{ value =", i + offset)?; - write_constant(v.init_expr().code(), type_info, w)?; + write_constant(&v.init_expr, type_info, w)?; write!(w, "}}")?; } Ok(()) } -fn write_element_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { - let element = match wasm.elements_section() { - Some(v) => v.entries(), - None => return Ok(()), - }; - - for v in element { - let code = v.offset().as_ref().unwrap().code(); +fn write_element_list(list: &[Element], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { + for v in list { + let (index, init) = match v.kind { + ElementKind::Active { + table_index, + init_expr, + } => (table_index, init_expr), + _ => unimplemented!(), + }; write!(w, "do ")?; - write!(w, "local target = TABLE_LIST[{}].data ", v.index())?; + write!(w, "local target = TABLE_LIST[{index}].data ")?; write!(w, "local offset =")?; - write_constant(code, type_info, w)?; + write_constant(&init, type_info, w)?; write!(w, "local data = {{")?; - v.members() - .iter() - .try_for_each(|v| write!(w, "FUNC_LIST[{v}],"))?; + for item in v.items.get_items_reader().unwrap() { + match item.unwrap() { + ElementItem::Func(index) => write!(w, "FUNC_LIST[{index}],"), + ElementItem::Expr(init) => write_constant(&init, type_info, w), + }?; + } write!(w, "}}")?; @@ -191,36 +181,31 @@ fn write_element_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Ok(()) } -fn write_data_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { - let data = match wasm.data_section() { - Some(v) => v.entries(), - None => return Ok(()), - }; - - for v in data { - let code = v.offset().as_ref().unwrap().code(); - let index = v.index(); +fn write_data_list(list: &[Data], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { + for data in list { + let (index, init) = match data.kind { + DataKind::Passive => unimplemented!(), + DataKind::Active { + memory_index, + init_expr, + } => (memory_index, init_expr), + }; write!(w, "rt.store.string(")?; write!(w, "MEMORY_LIST[{index}],")?; - write_constant(code, type_info, w)?; - write!(w, r#","{}")"#, v.value().escape_ascii())?; + write_constant(&init, type_info, w)?; + write!(w, r#","{}")"#, data.data.escape_ascii())?; } Ok(()) } -fn build_func_list(wasm: &Module, type_info: &TypeInfo) -> Vec { - let list = match wasm.code_section() { - Some(v) => v.bodies(), - None => return Vec::new(), - }; - - let mut builder = Builder::from_type_info(type_info); +fn build_func_list(list: &[FunctionBody], type_info: &TypeInfo) -> Vec { + let mut builder = Factory::from_type_info(type_info); list.iter() .enumerate() - .map(|f| builder.build_indexed(f.0, f.1)) + .map(|f| builder.create_indexed(f.0, f.1)) .collect() } @@ -260,28 +245,20 @@ fn write_localize_used(func_list: &[FuncData], w: &mut dyn Write) -> Result 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 { + if let Some(name) = wasm.name_section().get(&index) { write!(w, "--[[ {name} ]]")?; } write!(w, "[{index}] =") } -fn write_func_list( - wasm: &Module, - type_info: &TypeInfo, - func_list: &[FuncData], - w: &mut dyn Write, -) -> Result<()> { +fn write_func_list(wasm: &Module, func_list: &[FuncData], w: &mut dyn Write) -> Result<()> { + let offset = wasm.import_count(External::Func); + func_list.iter().enumerate().try_for_each(|(i, v)| { - let index = (type_info.len_ex() + i).try_into().unwrap(); + let index = (offset + i).try_into().unwrap(); write_func_start(wasm, index, w)?; @@ -299,12 +276,12 @@ fn write_module_start( write_table_list(wasm, w)?; write_memory_list(wasm, w)?; write_global_list(wasm, type_info, w)?; - write_element_list(wasm, type_info, w)?; - write_data_list(wasm, type_info, w)?; + write_element_list(wasm.element_section(), type_info, w)?; + write_data_list(wasm.data_section(), type_info, w)?; write!(w, "end ")?; write!(w, "return function(wasm)")?; - write_import_list(wasm, w)?; + write_import_list(wasm.import_section(), w)?; write!(w, "run_init_code()")?; for mem in mem_set { @@ -316,31 +293,31 @@ fn write_module_start( } write!(w, "return {{")?; - write_export_list(wasm, w)?; + write_export_list(wasm.export_section(), w)?; write!(w, "}} end ") } /// # Errors /// Returns `Err` if writing to `Write` failed. -pub fn from_inst_list(code: &[Instruction], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { - Builder::from_type_info(type_info) - .build_anonymous(code) +pub fn from_inst_list(code: &[Operator], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { + Factory::from_type_info(type_info) + .create_anonymous(code) .write(&mut Manager::default(), w) } /// # Errors /// Returns `Err` if writing to `Write` failed. pub fn from_module_typed(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { - let func_list = build_func_list(wasm, type_info); + let func_list = build_func_list(wasm.code_section(), type_info); let mem_set = write_localize_used(&func_list, w)?; write!(w, "local table_new = require(\"table.new\")")?; - write_named_array("FUNC_LIST", wasm.functions_space(), w)?; + write_named_array("FUNC_LIST", wasm.function_space(), w)?; write_named_array("TABLE_LIST", wasm.table_space(), w)?; write_named_array("MEMORY_LIST", wasm.memory_space(), w)?; - write_named_array("GLOBAL_LIST", wasm.globals_space(), w)?; + write_named_array("GLOBAL_LIST", wasm.global_space(), w)?; - write_func_list(wasm, type_info, &func_list, w)?; + write_func_list(wasm, &func_list, w)?; write_module_start(wasm, type_info, &mem_set, w) } diff --git a/dev-test/fuzz_targets/luajit_translate.rs b/dev-test/fuzz_targets/luajit_translate.rs index 0d569a5..ef5ec14 100644 --- a/dev-test/fuzz_targets/luajit_translate.rs +++ b/dev-test/fuzz_targets/luajit_translate.rs @@ -1,14 +1,12 @@ #![no_main] -use wasm_smith::Module; +use wasm_ast::module::Module; +use wasm_smith::Module as RngModule; // We are not interested in parity_wasm errors. -libfuzzer_sys::fuzz_target!(|module: Module| { +libfuzzer_sys::fuzz_target!(|module: RngModule| { let data = module.to_bytes(); - let wasm = match parity_wasm::deserialize_buffer(&data) { - Ok(v) => v, - Err(_) => return, - }; + let wasm = Module::from_data(&data); let sink = &mut std::io::sink(); diff --git a/dev-test/tests/luajit_translate.rs b/dev-test/tests/luajit_translate.rs index 475b315..96ce7da 100644 --- a/dev-test/tests/luajit_translate.rs +++ b/dev-test/tests/luajit_translate.rs @@ -3,8 +3,7 @@ use std::{ path::PathBuf, }; -use parity_wasm::elements::Module; -use wasm_ast::builder::TypeInfo; +use wasm_ast::module::{Module, TypeInfo}; use wast::{ core::{Expression, Instruction}, AssertExpression, WastExecute, WastInvoke,