From 9e5d2fae2683c30ed81ed14c6bc45091a0f5c921 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 21 Nov 2022 21:35:23 -0800 Subject: [PATCH] WIP. --- src/backend/binaryen.rs | 254 ++++++++++++++++++++++++++++++++++++++++ src/backend/lower.rs | 74 ++++++++++++ src/backend/mod.rs | 1 + src/frontend.rs | 13 +- src/ir/module.rs | 20 ++-- 5 files changed, 350 insertions(+), 12 deletions(-) diff --git a/src/backend/binaryen.rs b/src/backend/binaryen.rs index cb8a438..9ebe451 100644 --- a/src/backend/binaryen.rs +++ b/src/backend/binaryen.rs @@ -43,6 +43,14 @@ impl Module { Ok(slice.to_vec()) } + pub fn new() -> Result { + let ptr = unsafe { BinaryenModuleCreate() }; + if ptr.is_null() { + bail!("Failed to allocate module"); + } + Ok(Module(ptr)) + } + pub fn num_funcs(&self) -> usize { unsafe { BinaryenGetNumFunctions(self.0) as usize } } @@ -88,6 +96,178 @@ impl Module { pub fn module(&self) -> BinaryenModule { self.0 } + + pub fn add_global(&self, ty: ir::Type, mutable: bool, value: Option) -> ir::Global { + let b_ty = Type::from(ty).to_binaryen(); + let value = value.unwrap_or(0); + let init = match ty { + ir::Type::I32 => Expression::const_i32(self, value as i32), + ir::Type::I64 => Expression::const_i64(self, value as i64), + ir::Type::F32 => Expression::const_f32(self, Ieee32::from_bits(value as u32)), + ir::Type::F64 => Expression::const_f64(self, Ieee64::from_bits(value)), + _ => panic!("Unsupported type"), + }; + + let num = unsafe { BinaryenGetNumGlobals(self.0) }; + let global = unsafe { BinaryenAddGlobal(self.0, std::ptr::null(), b_ty, mutable, init.1) }; + assert!(!global.is_null()); + ir::Global::from(num) + } + + pub fn add_table(&self, ty: ir::Type, init: usize, max: Option) -> ir::Table { + let ty = Type::from(ty).to_binaryen(); + let num = unsafe { BinaryenGetNumTables(self.0) }; + let max = max.unwrap_or(0); + let table = unsafe { + BinaryenAddTable( + self.0, + std::ptr::null(), + init as BinaryenIndex, + max as BinaryenIndex, + ty, + ) + }; + assert!(!table.is_null()); + ir::Table::from(num) + } + + pub fn add_table_elem(&self, table: ir::Table, index: usize, elt: ir::Func) { + let table_name = unsafe { + BinaryenTableGetName(BinaryenGetTableByIndex( + self.0, + table.index() as BinaryenIndex, + )) + }; + let func_name = unsafe { + BinaryenFunctionGetName(BinaryenGetFunctionByIndex( + self.0, + elt.index() as BinaryenIndex, + )) + }; + let offset = Expression::const_i32(self, index as i32); + let seg = unsafe { + BinaryenAddActiveElementSegment( + self.0, + table_name, + std::ptr::null(), + &func_name as *const *const c_char, + 1, + offset.1, + ) + }; + assert!(!seg.is_null()); + } + + pub fn add_mem( + &self, + init_pages: usize, + max_pages: Option, + segments: &[ir::MemorySegment], + ) -> ir::Memory { + let seg_passive = vec![false; segments.len()]; + let seg_offset = segments + .iter() + .map(|seg| Expression::const_i32(self, seg.offset as i32).1) + .collect::>(); + let seg_data = segments + .iter() + .map(|seg| seg.data.as_ptr() as *const c_char) + .collect::>(); + let seg_size = segments + .iter() + .map(|seg| seg.data.len() as BinaryenIndex) + .collect::>(); + + // Binaryen does not support multi-memory. + unsafe { + BinaryenSetMemory( + self.0, + init_pages as BinaryenIndex, + max_pages.unwrap_or(0) as BinaryenIndex, + std::ptr::null(), + seg_data.as_ptr(), + seg_passive.as_ptr(), + seg_offset.as_ptr(), + seg_size.as_ptr(), + segments.len() as BinaryenIndex, + false, + ); + } + ir::Memory::from(0) + } + + pub fn add_table_import(&self, table: ir::Table, module: &str, name: &str) { + let table_name = unsafe { + BinaryenTableGetName(BinaryenGetTableByIndex( + self.0, + table.index() as BinaryenIndex, + )) + }; + let c_module = std::ffi::CString::new(module).unwrap(); + let c_name = std::ffi::CString::new(name).unwrap(); + unsafe { + BinaryenAddTableImport(self.0, table_name, c_module.as_ptr(), c_name.as_ptr()); + } + } + + pub fn add_func_import( + &self, + func: ir::Func, + module: &str, + name: &str, + params: &[ir::Type], + results: &[ir::Type], + ) { + let func_name = unsafe { + BinaryenFunctionGetName(BinaryenGetFunctionByIndex( + self.0, + func.index() as BinaryenIndex, + )) + }; + let c_module = std::ffi::CString::new(module).unwrap(); + let c_name = std::ffi::CString::new(name).unwrap(); + let params = tys_to_binaryen(params.iter().copied()); + let results = tys_to_binaryen(results.iter().copied()); + unsafe { + BinaryenAddFunctionImport( + self.0, + func_name, + c_module.as_ptr(), + c_name.as_ptr(), + params, + results, + ); + } + } + + pub fn add_global_import( + &self, + global: ir::Global, + module: &str, + name: &str, + ty: ir::Type, + mutable: bool, + ) { + let global_name = unsafe { + BinaryenGlobalGetName(BinaryenGetGlobalByIndex( + self.0, + global.index() as BinaryenIndex, + )) + }; + let c_module = std::ffi::CString::new(module).unwrap(); + let c_name = std::ffi::CString::new(name).unwrap(); + let ty = Type::from(ty).to_binaryen(); + unsafe { + BinaryenAddGlobalImport( + self.0, + global_name, + c_module.as_ptr(), + c_name.as_ptr(), + ty, + mutable, + ); + } + } } impl Drop for Module { @@ -708,6 +888,8 @@ type BinaryenExport = *const c_void; type BinaryenRelooper = *const c_void; type BinaryenRelooperBlock = *const c_void; type BinaryenTable = *const c_void; +type BinaryenGlobal = *const c_void; +type BinaryenElementSegment = *const c_void; #[repr(C)] struct BinaryenModuleAllocateAndWriteResult { @@ -787,6 +969,7 @@ impl Drop for Relooper { #[link(name = "binaryen")] extern "C" { fn BinaryenModuleRead(data: *const u8, len: usize) -> BinaryenModule; + fn BinaryenModuleCreate() -> BinaryenModule; fn BinaryenModuleDispose(ptr: BinaryenModule); fn BinaryenModuleAllocateAndWrite( ptr: BinaryenModule, @@ -1373,6 +1556,77 @@ extern "C" { fn BinaryenRefAsData() -> BinaryenOp; fn BinaryenRefAsI31() -> BinaryenOp; + fn BinaryenAddGlobal( + module: BinaryenModule, + name: *const c_char, + ty: BinaryenType, + mutable: bool, + init: BinaryenExpression, + ) -> BinaryenGlobal; + fn BinaryenGetNumGlobals(module: BinaryenModule) -> BinaryenIndex; + + fn BinaryenAddTable( + module: BinaryenModule, + name: *const c_char, + initial: BinaryenIndex, + max: BinaryenIndex, + ty: BinaryenType, + ) -> BinaryenTable; + fn BinaryenGetNumTables(module: BinaryenModule) -> BinaryenIndex; + + fn BinaryenAddActiveElementSegment( + module: BinaryenModule, + table: *const c_char, + name: *const c_char, + func_names: *const *const c_char, + num_funcs: BinaryenIndex, + offset: BinaryenExpression, + ) -> BinaryenElementSegment; + + fn BinaryenSetMemory( + module: BinaryenModule, + init: BinaryenIndex, + max: BinaryenIndex, + export_name: *const c_char, + segments: *const *const c_char, + seg_passive: *const bool, + seg_offsets: *const BinaryenExpression, + sizes: *const BinaryenIndex, + n_segments: BinaryenIndex, + shared: bool, + ); + + fn BinaryenAddTableImport( + module: BinaryenModule, + name: *const c_char, + extern_module: *const c_char, + extern_name: *const c_char, + ); + fn BinaryenAddMemoryImport( + module: BinaryenModule, + name: *const c_char, + extern_module: *const c_char, + extern_name: *const c_char, + ); + fn BinaryenAddGlobalImport( + module: BinaryenModule, + name: *const c_char, + extern_module: *const c_char, + extern_name: *const c_char, + ty: BinaryenType, + mutable: bool, + ); + fn BinaryenAddFunctionImport( + module: BinaryenModule, + name: *const c_char, + extern_module: *const c_char, + extern_name: *const c_char, + params: BinaryenType, + results: BinaryenType, + ); + + fn BinaryenGlobalGetName(global: BinaryenGlobal) -> *const c_char; + fn BinaryenGetGlobalByIndex(module: BinaryenModule, index: BinaryenIndex) -> BinaryenGlobal; } #[repr(C)] diff --git a/src/backend/lower.rs b/src/backend/lower.rs index 8860a7f..6d79759 100644 --- a/src/backend/lower.rs +++ b/src/backend/lower.rs @@ -1,10 +1,84 @@ use crate::backend::binaryen; use crate::entity::EntityRef; +use crate::ir::ImportKind; use crate::ir::*; use crate::Operator; +use anyhow::Result; use fxhash::FxHashMap; use std::collections::BTreeMap; +pub(crate) fn lower(module: &Module) -> Result { + let into_mod = binaryen::Module::new()?; + + // Create globals. + for (global, data) in module.globals() { + let new_global = into_mod.add_global(data.ty, data.mutable, data.value); + assert_eq!(new_global, global); + } + + // Create tables. + for (table, data) in module.tables() { + let new_table = into_mod.add_table( + data.ty, + data.func_elements + .as_ref() + .map(|elems| elems.len()) + .unwrap_or(0), + data.max, + ); + assert_eq!(new_table, table); + if let Some(elts) = data.func_elements.as_ref() { + for (i, &elt) in elts.iter().enumerate() { + if elt.is_valid() { + into_mod.add_table_elem(new_table, i, elt); + } + } + } + } + + // Create memories. + for (mem, data) in module.memories() { + let new_mem = into_mod.add_mem(data.initial_pages, data.maximum_pages, &data.segments[..]); + assert_eq!(new_mem, mem); + } + + // Create function bodies. + + // Create imports. + for import in module.imports() { + match &import.kind { + &ImportKind::Table(table) => { + into_mod.add_table_import(table, &import.module[..], &import.name[..]); + } + &ImportKind::Func(func) => { + let sig = module.func(func).sig(); + let sigdata = module.signature(sig); + into_mod.add_func_import( + func, + &import.module[..], + &import.name[..], + &sigdata.params[..], + &sigdata.returns[..], + ); + } + &ImportKind::Global(global) => { + let globdata = module.global(global); + into_mod.add_global_import( + global, + &import.module[..], + &import.name[..], + globdata.ty, + globdata.mutable, + ); + } + } + } + + // Create exports. + + Ok(into_mod) +} + /// Creates a body expression for a function. Returns that expression, /// and new locals (as their types) that were created as temporaries /// and need to be appended to `body.locals`. diff --git a/src/backend/mod.rs b/src/backend/mod.rs index d75646a..7020fb8 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -2,3 +2,4 @@ pub mod binaryen; pub mod lower; + diff --git a/src/frontend.rs b/src/frontend.rs index a4cc719..d569b14 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -78,12 +78,17 @@ fn handle_payload<'a>( Some(ImportKind::Func(func)) } ImportSectionEntryType::Global(ty) => { + let mutable = ty.mutable; let ty = ty.content_type.into(); - let global = module.frontend_add_global(GlobalData { ty, value: None }); + let global = module.frontend_add_global(GlobalData { + ty, + value: None, + mutable, + }); Some(ImportKind::Global(global)) } ImportSectionEntryType::Table(ty) => { - let table = module.frontend_add_table(ty.element_type.into()); + let table = module.frontend_add_table(ty.element_type.into(), None); Some(ImportKind::Table(table)) } _ => None, @@ -100,18 +105,20 @@ fn handle_payload<'a>( Payload::GlobalSection(reader) => { for global in reader { let global = global?; + let mutable = global.ty.mutable; let ty = global.ty.content_type.into(); let init_expr = parse_init_expr(&global.init_expr)?; module.frontend_add_global(GlobalData { ty, value: init_expr, + mutable, }); } } Payload::TableSection(reader) => { for table in reader { let table = table?; - module.frontend_add_table(table.element_type.into()); + module.frontend_add_table(table.element_type.into(), table.maximum); } } Payload::FunctionSection(reader) => { diff --git a/src/ir/module.rs b/src/ir/module.rs index 4dcdc3c..eb9ada3 100644 --- a/src/ir/module.rs +++ b/src/ir/module.rs @@ -1,9 +1,8 @@ use super::{Func, FuncDecl, Global, Memory, ModuleDisplay, Signature, Table, Type}; use crate::entity::EntityVec; -use crate::frontend; use crate::ir::FunctionBody; +use crate::{backend, frontend}; use anyhow::Result; -use fxhash::FxHashSet; #[derive(Clone, Debug)] pub struct Module<'a> { @@ -15,8 +14,6 @@ pub struct Module<'a> { imports: Vec, exports: Vec, memories: EntityVec, - - dirty_funcs: FxHashSet, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -41,6 +38,7 @@ pub struct MemorySegment { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TableData { pub ty: Type, + pub max: Option, pub func_elements: Option>, } @@ -48,6 +46,7 @@ pub struct TableData { pub struct GlobalData { pub ty: Type, pub value: Option, + pub mutable: bool, } impl From<&wasmparser::FuncType> for SignatureData { @@ -134,7 +133,6 @@ impl<'a> Module<'a> { imports: vec![], exports: vec![], memories: EntityVec::default(), - dirty_funcs: FxHashSet::default(), } } } @@ -144,7 +142,6 @@ impl<'a> Module<'a> { &self.funcs[id] } pub fn func_mut<'b>(&'b mut self, id: Func) -> &'b mut FuncDecl { - self.dirty_funcs.insert(id); &mut self.funcs[id] } pub fn funcs<'b>(&'b self) -> impl Iterator { @@ -190,13 +187,17 @@ impl<'a> Module<'a> { pub(crate) fn frontend_add_func(&mut self, body: FuncDecl) -> Func { self.funcs.push(body) } - pub(crate) fn frontend_add_table(&mut self, ty: Type) -> Table { + pub(crate) fn frontend_add_table(&mut self, ty: Type, max: Option) -> Table { let func_elements = if ty == Type::FuncRef { Some(vec![]) } else { None }; - self.tables.push(TableData { ty, func_elements }) + self.tables.push(TableData { + ty, + func_elements, + max, + }) } pub(crate) fn frontend_add_global(&mut self, global: GlobalData) -> Global { self.globals.push(global) @@ -225,7 +226,8 @@ impl<'a> Module<'a> { } pub fn to_wasm_bytes(&self) -> Result> { - todo!() + let module = backend::lower::lower(self)?; + module.write() } pub fn display<'b>(&'b self) -> ModuleDisplay<'b>