Refactor LuaJIT codegen to use wasmparser

This commit is contained in:
Rerumu 2022-06-25 02:00:39 -04:00
parent 9f8cf3814b
commit c52170ba6e
6 changed files with 135 additions and 174 deletions

View File

@ -3,12 +3,11 @@ name = "codegen-luajit"
version = "0.9.0" version = "0.9.0"
edition = "2021" edition = "2021"
[dependencies]
wasmparser = "0.86.0"
[dependencies.wasm-ast] [dependencies.wasm-ast]
path = "../../wasm-ast" path = "../../wasm-ast"
[dependencies.parity-wasm]
git = "https://github.com/paritytech/parity-wasm.git"
features = ["multi_value", "sign_ext"]
[[bin]] [[bin]]
name = "wasm2luajit" name = "wasm2luajit"

View File

@ -3,11 +3,11 @@ use std::{
ops::Range, ops::Range,
}; };
use parity_wasm::elements::ValueType;
use wasm_ast::node::{ use wasm_ast::node::{
Backward, Br, BrIf, BrTable, Call, CallIndirect, Forward, FuncData, If, MemoryGrow, SetGlobal, Backward, Br, BrIf, BrTable, Call, CallIndirect, Forward, FuncData, If, MemoryGrow, SetGlobal,
SetLocal, SetTemporary, Statement, StoreAt, Terminator, SetLocal, SetTemporary, Statement, StoreAt, Terminator,
}; };
use wasmparser::ValType;
use crate::analyzer::br_table; 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<()> { fn write_variable_list(ast: &FuncData, w: &mut dyn Write) -> Result<()> {
let mut total = 0; let mut total = 0;
for data in ast.local_data().iter().filter(|v| v.count() != 0) { for data in ast.local_data().iter().filter(|v| v.0 != 0) {
let range = total..total + usize::try_from(data.count()).unwrap(); let range = total..total + usize::try_from(data.0).unwrap();
let typed = if data.value_type() == ValueType::I64 { let typed = if data.1 == ValType::I64 { "0LL" } else { "0" }.as_bytes();
"0LL"
} else {
"0"
}
.as_bytes();
total = range.end; total = range.end;

View File

@ -1,12 +1,11 @@
use std::io::{Result, Write}; use std::io::{Result, Write};
use parity_wasm::{deserialize_file, elements::Module}; use wasm_ast::module::Module;
fn load_module(name: &str) -> Module { fn load_arg_source() -> Result<Vec<u8>> {
deserialize_file(name) let name = std::env::args().nth(1).expect("usage: wasm2luajit <file>");
.expect("Failed to parse WebAssembly file")
.parse_names() std::fs::read(name)
.unwrap_or_else(|v| v.1)
} }
fn do_runtime(lock: &mut dyn Write) -> Result<()> { fn do_runtime(lock: &mut dyn Write) -> Result<()> {
@ -18,14 +17,8 @@ fn do_runtime(lock: &mut dyn Write) -> Result<()> {
} }
fn main() -> Result<()> { fn main() -> Result<()> {
let wasm = match std::env::args().nth(1) { let data = load_arg_source()?;
Some(name) => load_module(&name), let wasm = Module::from_data(&data);
None => {
eprintln!("usage: wasm2luajit <file>");
return Ok(());
}
};
let lock = &mut std::io::stdout().lock(); let lock = &mut std::io::stdout().lock();

View File

@ -3,42 +3,41 @@ use std::{
io::{Result, Write}, io::{Result, Write},
}; };
use parity_wasm::elements::{
External, ImportCountType, Instruction, Internal, Module, NameSection, ResizableLimits,
};
use wasm_ast::{ use wasm_ast::{
builder::{Builder, TypeInfo}, factory::Factory,
module::{External, Module, TypeInfo},
node::{FuncData, Statement}, node::{FuncData, Statement},
}; };
use wasmparser::{
Data, DataKind, Element, ElementItem, ElementKind, Export, FunctionBody, Import, InitExpr,
Operator, OperatorsReader,
};
use crate::{ use crate::{
analyzer::localize, analyzer::localize,
backend::manager::{Driver, Manager}, backend::manager::{Driver, Manager},
}; };
fn to_internal_index(internal: Internal) -> u32 { trait AsIEName {
match internal { fn as_ie_name(&self) -> &str;
Internal::Function(v) | Internal::Table(v) | Internal::Memory(v) | Internal::Global(v) => v, }
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) { fn reader_to_code(reader: OperatorsReader) -> Vec<Operator> {
let max = limits.maximum().unwrap_or(0xFFFF); let parsed: std::result::Result<_, _> = reader.into_iter().collect();
(limits.initial(), max) parsed.unwrap()
}
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})")
} }
fn write_named_array(name: &str, len: usize, w: &mut dyn Write) -> Result<()> { 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})") write!(w, "local {name} = table_new({len}, {hash})")
} }
fn write_constant(code: &[Instruction], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { fn write_constant(init: &InitExpr, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> {
let func = Builder::from_type_info(type_info).build_anonymous(code); 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() { if let Some(Statement::SetTemporary(stat)) = func.code().code().last() {
stat.value().write(&mut Manager::default(), w) 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<T>(wasm: &Module, lower: &str, cond: T, w: &mut dyn Write) -> Result<()> fn write_import_of(list: &[Import], wanted: External, w: &mut dyn Write) -> Result<()> {
where let lower = wanted.as_ie_name();
T: Fn(&External) -> bool,
{
let import = match wasm.import_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let upper = lower.to_uppercase(); let upper = lower.to_uppercase();
for (i, v) in import.iter().filter(|v| cond(v.external())).enumerate() { for (i, Import { name, module, .. }) in list
let field = v.field(); .iter()
let module = v.module(); .filter(|v| External::from(v.ty) == wanted)
.enumerate()
write!(w, r#"{upper}[{i}] = wasm["{module}"].{lower}["{field}"]"#)?; {
write!(w, r#"{upper}[{i}] = wasm["{module}"].{lower}["{name}"]"#)?;
} }
Ok(()) Ok(())
} }
fn write_export_of<T>(wasm: &Module, lower: &str, cond: T, w: &mut dyn Write) -> Result<()> fn write_export_of(list: &[Export], wanted: External, w: &mut dyn Write) -> Result<()> {
where let lower = wanted.as_ie_name();
T: Fn(&Internal) -> bool,
{
let export = match wasm.export_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let upper = lower.to_uppercase(); let upper = lower.to_uppercase();
write!(w, "{lower} = {{")?; write!(w, "{lower} = {{")?;
for v in export.iter().filter(|v| cond(v.internal())) { for Export { name, index, .. } in list.iter().filter(|v| External::from(v.kind) == wanted) {
let field = v.field(); write!(w, r#"["{name}"] = {upper}[{index}],"#)?;
let index = to_internal_index(*v.internal());
write!(w, r#"["{field}"] = {upper}[{index}],"#)?;
} }
write!(w, "}},") write!(w, "}},")
} }
fn write_import_list(wasm: &Module, w: &mut dyn Write) -> Result<()> { fn write_import_list(list: &[Import], w: &mut dyn Write) -> Result<()> {
write_import_of(wasm, "func_list", |v| matches!(v, External::Function(_)), w)?; write_import_of(list, External::Func, w)?;
write_import_of(wasm, "table_list", |v| matches!(v, External::Table(_)), w)?; write_import_of(list, External::Table, w)?;
write_import_of(wasm, "memory_list", |v| matches!(v, External::Memory(_)), w)?; write_import_of(list, External::Memory, w)?;
write_import_of(wasm, "global_list", |v| matches!(v, External::Global(_)), w) write_import_of(list, External::Global, w)
} }
fn write_export_list(wasm: &Module, w: &mut dyn Write) -> Result<()> { fn write_export_list(list: &[Export], w: &mut dyn Write) -> Result<()> {
write_export_of(wasm, "func_list", |v| matches!(v, Internal::Function(_)), w)?; write_export_of(list, External::Func, w)?;
write_export_of(wasm, "table_list", |v| matches!(v, Internal::Table(_)), w)?; write_export_of(list, External::Table, w)?;
write_export_of(wasm, "memory_list", |v| matches!(v, Internal::Memory(_)), w)?; write_export_of(list, External::Memory, w)?;
write_export_of(wasm, "global_list", |v| matches!(v, Internal::Global(_)), w) write_export_of(list, External::Global, w)
} }
fn write_table_list(wasm: &Module, w: &mut dyn Write) -> Result<()> { fn write_table_list(wasm: &Module, w: &mut dyn Write) -> Result<()> {
let table = match wasm.table_section() { let offset = wasm.import_count(External::Table);
Some(v) => v.entries(), let table = wasm.table_section();
None => return Ok(()),
};
let offset = wasm.import_count(ImportCountType::Table);
for (i, v) in table.iter().enumerate() { for (i, table) in table.iter().enumerate() {
write!(w, "TABLE_LIST[{}] =", i + offset)?; let index = offset + i;
write_table_init(v.limits(), w)?; let min = table.initial;
let max = table.maximum.unwrap_or(0xFFFF);
write!(
w,
"TABLE_LIST[{index}] = {{ min = {min}, max = {max}, data = {{}} }}"
)?;
} }
Ok(()) Ok(())
} }
fn write_memory_list(wasm: &Module, w: &mut dyn Write) -> Result<()> { fn write_memory_list(wasm: &Module, w: &mut dyn Write) -> Result<()> {
let memory = match wasm.memory_section() { let offset = wasm.import_count(External::Memory);
Some(v) => v.entries(), let memory = wasm.memory_section();
None => return Ok(()),
};
let offset = wasm.import_count(ImportCountType::Memory);
for (i, v) in memory.iter().enumerate() { for (i, v) in memory.iter().enumerate() {
write!(w, "MEMORY_LIST[{}] =", i + offset)?; let index = offset + i;
write_memory_init(v.limits(), w)?; let min = v.initial;
let max = v.maximum.unwrap_or(0xFFFF);
write!(w, "MEMORY_LIST[{index}] = rt.allocator.new({min}, {max})")?;
} }
Ok(()) Ok(())
} }
fn write_global_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { fn write_global_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> {
let global = match wasm.global_section() { let offset = wasm.import_count(External::Global);
Some(v) => v, let global = wasm.global_section();
None => return Ok(()),
};
let offset = wasm.import_count(ImportCountType::Global);
for (i, v) in global.entries().iter().enumerate() { for (i, v) in global.iter().enumerate() {
write!(w, "GLOBAL_LIST[{}] = {{ value =", i + offset)?; 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, "}}")?; write!(w, "}}")?;
} }
Ok(()) Ok(())
} }
fn write_element_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { fn write_element_list(list: &[Element], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> {
let element = match wasm.elements_section() { for v in list {
Some(v) => v.entries(), let (index, init) = match v.kind {
None => return Ok(()), ElementKind::Active {
}; table_index,
init_expr,
for v in element { } => (table_index, init_expr),
let code = v.offset().as_ref().unwrap().code(); _ => unimplemented!(),
};
write!(w, "do ")?; 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!(w, "local offset =")?;
write_constant(code, type_info, w)?; write_constant(&init, type_info, w)?;
write!(w, "local data = {{")?; write!(w, "local data = {{")?;
v.members() for item in v.items.get_items_reader().unwrap() {
.iter() match item.unwrap() {
.try_for_each(|v| write!(w, "FUNC_LIST[{v}],"))?; ElementItem::Func(index) => write!(w, "FUNC_LIST[{index}],"),
ElementItem::Expr(init) => write_constant(&init, type_info, w),
}?;
}
write!(w, "}}")?; write!(w, "}}")?;
@ -191,36 +181,31 @@ fn write_element_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) ->
Ok(()) Ok(())
} }
fn write_data_list(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { fn write_data_list(list: &[Data], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> {
let data = match wasm.data_section() { for data in list {
Some(v) => v.entries(), let (index, init) = match data.kind {
None => return Ok(()), DataKind::Passive => unimplemented!(),
}; DataKind::Active {
memory_index,
for v in data { init_expr,
let code = v.offset().as_ref().unwrap().code(); } => (memory_index, init_expr),
let index = v.index(); };
write!(w, "rt.store.string(")?; write!(w, "rt.store.string(")?;
write!(w, "MEMORY_LIST[{index}],")?; write!(w, "MEMORY_LIST[{index}],")?;
write_constant(code, type_info, w)?; write_constant(&init, type_info, w)?;
write!(w, r#","{}")"#, v.value().escape_ascii())?; write!(w, r#","{}")"#, data.data.escape_ascii())?;
} }
Ok(()) Ok(())
} }
fn build_func_list(wasm: &Module, type_info: &TypeInfo) -> Vec<FuncData> { fn build_func_list(list: &[FunctionBody], type_info: &TypeInfo) -> Vec<FuncData> {
let list = match wasm.code_section() { let mut builder = Factory::from_type_info(type_info);
Some(v) => v.bodies(),
None => return Vec::new(),
};
let mut builder = Builder::from_type_info(type_info);
list.iter() list.iter()
.enumerate() .enumerate()
.map(|f| builder.build_indexed(f.0, f.1)) .map(|f| builder.create_indexed(f.0, f.1))
.collect() .collect()
} }
@ -260,28 +245,20 @@ fn write_localize_used(func_list: &[FuncData], w: &mut dyn Write) -> Result<BTre
} }
fn write_func_start(wasm: &Module, index: u32, w: &mut dyn Write) -> Result<()> { fn write_func_start(wasm: &Module, index: u32, w: &mut dyn Write) -> Result<()> {
let opt = wasm
.names_section()
.and_then(NameSection::functions)
.and_then(|v| v.names().get(index));
write!(w, "FUNC_LIST")?; write!(w, "FUNC_LIST")?;
if let Some(name) = opt { if let Some(name) = wasm.name_section().get(&index) {
write!(w, "--[[ {name} ]]")?; write!(w, "--[[ {name} ]]")?;
} }
write!(w, "[{index}] =") write!(w, "[{index}] =")
} }
fn write_func_list( fn write_func_list(wasm: &Module, func_list: &[FuncData], w: &mut dyn Write) -> Result<()> {
wasm: &Module, let offset = wasm.import_count(External::Func);
type_info: &TypeInfo,
func_list: &[FuncData],
w: &mut dyn Write,
) -> Result<()> {
func_list.iter().enumerate().try_for_each(|(i, v)| { 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)?; write_func_start(wasm, index, w)?;
@ -299,12 +276,12 @@ fn write_module_start(
write_table_list(wasm, w)?; write_table_list(wasm, w)?;
write_memory_list(wasm, w)?; write_memory_list(wasm, w)?;
write_global_list(wasm, type_info, w)?; write_global_list(wasm, type_info, w)?;
write_element_list(wasm, type_info, w)?; write_element_list(wasm.element_section(), type_info, w)?;
write_data_list(wasm, type_info, w)?; write_data_list(wasm.data_section(), type_info, w)?;
write!(w, "end ")?; write!(w, "end ")?;
write!(w, "return function(wasm)")?; write!(w, "return function(wasm)")?;
write_import_list(wasm, w)?; write_import_list(wasm.import_section(), w)?;
write!(w, "run_init_code()")?; write!(w, "run_init_code()")?;
for mem in mem_set { for mem in mem_set {
@ -316,31 +293,31 @@ fn write_module_start(
} }
write!(w, "return {{")?; write!(w, "return {{")?;
write_export_list(wasm, w)?; write_export_list(wasm.export_section(), w)?;
write!(w, "}} end ") write!(w, "}} end ")
} }
/// # Errors /// # Errors
/// Returns `Err` if writing to `Write` failed. /// Returns `Err` if writing to `Write` failed.
pub fn from_inst_list(code: &[Instruction], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { pub fn from_inst_list(code: &[Operator], type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> {
Builder::from_type_info(type_info) Factory::from_type_info(type_info)
.build_anonymous(code) .create_anonymous(code)
.write(&mut Manager::default(), w) .write(&mut Manager::default(), w)
} }
/// # Errors /// # Errors
/// Returns `Err` if writing to `Write` failed. /// Returns `Err` if writing to `Write` failed.
pub fn from_module_typed(wasm: &Module, type_info: &TypeInfo, w: &mut dyn Write) -> Result<()> { 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)?; let mem_set = write_localize_used(&func_list, w)?;
write!(w, "local table_new = require(\"table.new\")")?; 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("TABLE_LIST", wasm.table_space(), w)?;
write_named_array("MEMORY_LIST", wasm.memory_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) write_module_start(wasm, type_info, &mem_set, w)
} }

View File

@ -1,14 +1,12 @@
#![no_main] #![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. // 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 data = module.to_bytes();
let wasm = match parity_wasm::deserialize_buffer(&data) { let wasm = Module::from_data(&data);
Ok(v) => v,
Err(_) => return,
};
let sink = &mut std::io::sink(); let sink = &mut std::io::sink();

View File

@ -3,8 +3,7 @@ use std::{
path::PathBuf, path::PathBuf,
}; };
use parity_wasm::elements::Module; use wasm_ast::module::{Module, TypeInfo};
use wasm_ast::builder::TypeInfo;
use wast::{ use wast::{
core::{Expression, Instruction}, core::{Expression, Instruction},
AssertExpression, WastExecute, WastInvoke, AssertExpression, WastExecute, WastInvoke,