This commit is contained in:
Chris Fallin 2022-11-21 21:35:23 -08:00
parent 15d45e1443
commit 9e5d2fae26
No known key found for this signature in database
GPG key ID: 31649E4FE65EB465
5 changed files with 350 additions and 12 deletions

View file

@ -43,6 +43,14 @@ impl Module {
Ok(slice.to_vec())
}
pub fn new() -> Result<Module> {
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<u64>) -> 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<u32>) -> 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<usize>,
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::<Vec<_>>();
let seg_data = segments
.iter()
.map(|seg| seg.data.as_ptr() as *const c_char)
.collect::<Vec<_>>();
let seg_size = segments
.iter()
.map(|seg| seg.data.len() as BinaryenIndex)
.collect::<Vec<_>>();
// 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)]

View file

@ -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<binaryen::Module> {
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`.

View file

@ -2,3 +2,4 @@
pub mod binaryen;
pub mod lower;

View file

@ -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) => {

View file

@ -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<Import>,
exports: Vec<Export>,
memories: EntityVec<Memory, MemoryData>,
dirty_funcs: FxHashSet<Func>,
}
#[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<u32>,
pub func_elements: Option<Vec<Func>>,
}
@ -48,6 +46,7 @@ pub struct TableData {
pub struct GlobalData {
pub ty: Type,
pub value: Option<u64>,
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<Item = (Func, &'b FuncDecl)> {
@ -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<u32>) -> 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<Vec<u8>> {
todo!()
let module = backend::lower::lower(self)?;
module.write()
}
pub fn display<'b>(&'b self) -> ModuleDisplay<'b>