WIP: almost-complete Wasm backend.
This commit is contained in:
parent
8744965705
commit
e9d4fe89b1
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
use crate::cfg::CFGInfo;
|
use crate::cfg::CFGInfo;
|
||||||
use crate::entity::EntityRef;
|
use crate::entity::EntityRef;
|
||||||
use crate::ir::{FunctionBody, Value, ValueDef};
|
use crate::ir::{
|
||||||
|
ExportKind, Func, FuncDecl, FunctionBody, ImportKind, Module, Type, Value, ValueDef,
|
||||||
|
};
|
||||||
use crate::passes::rpo::RPO;
|
use crate::passes::rpo::RPO;
|
||||||
use crate::Operator;
|
use crate::Operator;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub mod stackify;
|
pub mod stackify;
|
||||||
use stackify::{Context as StackifyContext, WasmBlock};
|
use stackify::{Context as StackifyContext, WasmBlock};
|
||||||
|
@ -15,7 +18,7 @@ use treeify::Trees;
|
||||||
pub mod localify;
|
pub mod localify;
|
||||||
use localify::Localifier;
|
use localify::Localifier;
|
||||||
|
|
||||||
pub struct WasmBackend<'a> {
|
pub struct WasmFuncBackend<'a> {
|
||||||
body: &'a FunctionBody,
|
body: &'a FunctionBody,
|
||||||
rpo: RPO,
|
rpo: RPO,
|
||||||
trees: Trees,
|
trees: Trees,
|
||||||
|
@ -29,8 +32,8 @@ macro_rules! op {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WasmBackend<'a> {
|
impl<'a> WasmFuncBackend<'a> {
|
||||||
pub fn new(body: &'a FunctionBody) -> Result<WasmBackend<'a>> {
|
pub fn new(body: &'a FunctionBody) -> Result<WasmFuncBackend<'a>> {
|
||||||
log::debug!("Backend compiling:\n{}\n", body.display_verbose("| "));
|
log::debug!("Backend compiling:\n{}\n", body.display_verbose("| "));
|
||||||
let cfg = CFGInfo::new(body);
|
let cfg = CFGInfo::new(body);
|
||||||
let rpo = RPO::compute(body);
|
let rpo = RPO::compute(body);
|
||||||
|
@ -41,7 +44,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
log::debug!("Ctrl:\n{:?}\n", ctrl);
|
log::debug!("Ctrl:\n{:?}\n", ctrl);
|
||||||
let locals = Localifier::compute(body, &cfg, &trees);
|
let locals = Localifier::compute(body, &cfg, &trees);
|
||||||
log::debug!("Locals:\n{:?}\n", locals);
|
log::debug!("Locals:\n{:?}\n", locals);
|
||||||
Ok(WasmBackend {
|
Ok(WasmFuncBackend {
|
||||||
body,
|
body,
|
||||||
rpo,
|
rpo,
|
||||||
trees,
|
trees,
|
||||||
|
@ -55,6 +58,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
self.locals
|
self.locals
|
||||||
.locals
|
.locals
|
||||||
.values()
|
.values()
|
||||||
|
.skip(self.body.blocks[self.body.entry].params.len())
|
||||||
.map(|&ty| (1, wasm_encoder::ValType::from(ty)))
|
.map(|&ty| (1, wasm_encoder::ValType::from(ty)))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
|
@ -73,6 +77,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
func.instruction(&wasm_encoder::Instruction::End);
|
||||||
|
|
||||||
log::debug!("Compiled to:\n{:?}\n", func);
|
log::debug!("Compiled to:\n{:?}\n", func);
|
||||||
|
|
||||||
|
@ -503,3 +508,188 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compile(module: &Module<'_>) -> anyhow::Result<Vec<u8>> {
|
||||||
|
let mut into_mod = wasm_encoder::Module::new();
|
||||||
|
|
||||||
|
let mut types = wasm_encoder::TypeSection::new();
|
||||||
|
for (_sig, sig_data) in module.signatures() {
|
||||||
|
let params = sig_data
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.map(|&ty| wasm_encoder::ValType::from(ty));
|
||||||
|
let returns = sig_data
|
||||||
|
.returns
|
||||||
|
.iter()
|
||||||
|
.map(|&ty| wasm_encoder::ValType::from(ty));
|
||||||
|
types.function(params, returns);
|
||||||
|
}
|
||||||
|
into_mod.section(&types);
|
||||||
|
|
||||||
|
let mut imports = wasm_encoder::ImportSection::new();
|
||||||
|
let import_map = module
|
||||||
|
.imports()
|
||||||
|
.filter_map(|import| match &import.kind {
|
||||||
|
&ImportKind::Func(func) => Some((func, (&import.module[..], &import.name[..]))),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<HashMap<Func, (&str, &str)>>();
|
||||||
|
let mut num_imports = 0;
|
||||||
|
for (i, (func, func_decl)) in module.funcs().enumerate() {
|
||||||
|
match func_decl {
|
||||||
|
FuncDecl::Import(sig) => {
|
||||||
|
let (module_name, func_name) = import_map.get(&func).unwrap_or(&("", ""));
|
||||||
|
imports.import(
|
||||||
|
module_name,
|
||||||
|
func_name,
|
||||||
|
wasm_encoder::EntityType::Function(sig.index() as u32),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
FuncDecl::Body(..) => {
|
||||||
|
num_imports = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
into_mod.section(&imports);
|
||||||
|
|
||||||
|
let mut funcs = wasm_encoder::FunctionSection::new();
|
||||||
|
for (func, func_decl) in module.funcs().skip(num_imports) {
|
||||||
|
match func_decl {
|
||||||
|
FuncDecl::Import(_) => anyhow::bail!("Import comes after func with body: {}", func),
|
||||||
|
FuncDecl::Body(sig, _) => {
|
||||||
|
funcs.function(sig.index() as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
into_mod.section(&funcs);
|
||||||
|
|
||||||
|
let mut tables = wasm_encoder::TableSection::new();
|
||||||
|
for (_table, table_data) in module.tables() {
|
||||||
|
tables.table(wasm_encoder::TableType {
|
||||||
|
element_type: wasm_encoder::ValType::from(table_data.ty),
|
||||||
|
minimum: table_data
|
||||||
|
.func_elements
|
||||||
|
.as_ref()
|
||||||
|
.map(|elt| elt.len())
|
||||||
|
.unwrap_or(0) as u32,
|
||||||
|
maximum: table_data.max,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
into_mod.section(&tables);
|
||||||
|
|
||||||
|
let mut memories = wasm_encoder::MemorySection::new();
|
||||||
|
for (_mem, mem_data) in module.memories() {
|
||||||
|
memories.memory(wasm_encoder::MemoryType {
|
||||||
|
minimum: mem_data.initial_pages as u64,
|
||||||
|
maximum: mem_data.maximum_pages.map(|val| val as u64),
|
||||||
|
memory64: false,
|
||||||
|
shared: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
into_mod.section(&memories);
|
||||||
|
|
||||||
|
let mut globals = wasm_encoder::GlobalSection::new();
|
||||||
|
for (_global, global_data) in module.globals() {
|
||||||
|
globals.global(
|
||||||
|
wasm_encoder::GlobalType {
|
||||||
|
val_type: wasm_encoder::ValType::from(global_data.ty),
|
||||||
|
mutable: global_data.mutable,
|
||||||
|
},
|
||||||
|
&const_init(global_data.ty, global_data.value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
into_mod.section(&globals);
|
||||||
|
|
||||||
|
let mut exports = wasm_encoder::ExportSection::new();
|
||||||
|
for export in module.exports() {
|
||||||
|
match &export.kind {
|
||||||
|
&ExportKind::Table(table) => {
|
||||||
|
exports.export(
|
||||||
|
&export.name[..],
|
||||||
|
wasm_encoder::ExportKind::Table,
|
||||||
|
table.index() as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
&ExportKind::Func(func) => {
|
||||||
|
exports.export(
|
||||||
|
&export.name[..],
|
||||||
|
wasm_encoder::ExportKind::Func,
|
||||||
|
func.index() as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
&ExportKind::Memory(mem) => {
|
||||||
|
exports.export(
|
||||||
|
&export.name[..],
|
||||||
|
wasm_encoder::ExportKind::Memory,
|
||||||
|
mem.index() as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
&ExportKind::Global(global) => {
|
||||||
|
exports.export(
|
||||||
|
&export.name[..],
|
||||||
|
wasm_encoder::ExportKind::Global,
|
||||||
|
global.index() as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
into_mod.section(&exports);
|
||||||
|
|
||||||
|
if let Some(start) = module.start_func {
|
||||||
|
let start = wasm_encoder::StartSection {
|
||||||
|
function_index: start.index() as u32,
|
||||||
|
};
|
||||||
|
into_mod.section(&start);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut elem = wasm_encoder::ElementSection::new();
|
||||||
|
for (table, table_data) in module.tables() {
|
||||||
|
if let Some(elts) = &table_data.func_elements {
|
||||||
|
for (i, &elt) in elts.iter().enumerate() {
|
||||||
|
if elt.is_valid() {
|
||||||
|
elem.active(
|
||||||
|
Some(table.index() as u32),
|
||||||
|
&wasm_encoder::ConstExpr::i32_const(i as i32),
|
||||||
|
wasm_encoder::ValType::FuncRef,
|
||||||
|
wasm_encoder::Elements::Functions(&[elt.index() as u32]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
into_mod.section(&elem);
|
||||||
|
|
||||||
|
let mut code = wasm_encoder::CodeSection::new();
|
||||||
|
for (_func, func_decl) in module.funcs().skip(num_imports) {
|
||||||
|
let body = func_decl.body().unwrap();
|
||||||
|
let body = WasmFuncBackend::new(body)?.compile()?;
|
||||||
|
code.function(&body);
|
||||||
|
}
|
||||||
|
into_mod.section(&code);
|
||||||
|
|
||||||
|
let mut data = wasm_encoder::DataSection::new();
|
||||||
|
for (mem, mem_data) in module.memories() {
|
||||||
|
for segment in &mem_data.segments {
|
||||||
|
data.active(
|
||||||
|
mem.index() as u32,
|
||||||
|
&wasm_encoder::ConstExpr::i32_const(segment.offset as i32),
|
||||||
|
segment.data.iter().copied(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
into_mod.section(&data);
|
||||||
|
|
||||||
|
Ok(into_mod.finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn const_init(ty: Type, value: Option<u64>) -> wasm_encoder::ConstExpr {
|
||||||
|
let bits = value.unwrap_or(0);
|
||||||
|
match ty {
|
||||||
|
Type::I32 => wasm_encoder::ConstExpr::i32_const(bits as u32 as i32),
|
||||||
|
Type::I64 => wasm_encoder::ConstExpr::i64_const(bits as i64),
|
||||||
|
Type::F32 => wasm_encoder::ConstExpr::f32_const(f32::from_bits(bits as u32)),
|
||||||
|
Type::F64 => wasm_encoder::ConstExpr::f64_const(f64::from_bits(bits as u64)),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -221,6 +221,9 @@ fn handle_payload<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Payload::End(_) => {}
|
Payload::End(_) => {}
|
||||||
|
Payload::StartSection { func, .. } => {
|
||||||
|
module.start_func = Some(Func::from(func));
|
||||||
|
}
|
||||||
payload => {
|
payload => {
|
||||||
log::warn!("Skipping section: {:?}", payload);
|
log::warn!("Skipping section: {:?}", payload);
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,9 @@ pub struct ModuleDisplay<'a>(pub(crate) &'a Module<'a>);
|
||||||
impl<'a> Display for ModuleDisplay<'a> {
|
impl<'a> Display for ModuleDisplay<'a> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||||
writeln!(f, "module {{")?;
|
writeln!(f, "module {{")?;
|
||||||
|
if let Some(func) = self.0.start_func {
|
||||||
|
writeln!(f, " start = {}", func)?;
|
||||||
|
}
|
||||||
let mut sig_strs = HashMap::new();
|
let mut sig_strs = HashMap::new();
|
||||||
for (sig, sig_data) in self.0.signatures() {
|
for (sig, sig_data) in self.0.signatures() {
|
||||||
let arg_tys = sig_data
|
let arg_tys = sig_data
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub struct Module<'a> {
|
||||||
imports: Vec<Import>,
|
imports: Vec<Import>,
|
||||||
exports: Vec<Export>,
|
exports: Vec<Export>,
|
||||||
memories: EntityVec<Memory, MemoryData>,
|
memories: EntityVec<Memory, MemoryData>,
|
||||||
|
pub start_func: Option<Func>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -133,6 +134,7 @@ impl<'a> Module<'a> {
|
||||||
imports: vec![],
|
imports: vec![],
|
||||||
exports: vec![],
|
exports: vec![],
|
||||||
memories: EntityVec::default(),
|
memories: EntityVec::default(),
|
||||||
|
start_func: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,15 +228,7 @@ impl<'a> Module<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_wasm_bytes(&self) -> Result<Vec<u8>> {
|
pub fn to_wasm_bytes(&self) -> Result<Vec<u8>> {
|
||||||
for (func, func_decl) in self.funcs.entries() {
|
backend::compile(self)
|
||||||
log::debug!("Compiling: {}", func);
|
|
||||||
if let Some(body) = func_decl.body() {
|
|
||||||
let comp = backend::WasmBackend::new(body)?;
|
|
||||||
let _ = comp.compile()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display<'b>(&'b self) -> ModuleDisplay<'b>
|
pub fn display<'b>(&'b self) -> ModuleDisplay<'b>
|
||||||
|
|
Loading…
Reference in a new issue