Create a high-level structure for Wasm backend

This commit is contained in:
Brian Carroll 2021-08-29 10:52:22 +01:00
parent 863f449048
commit a1102222dd
2 changed files with 121 additions and 5 deletions

View file

@ -6,3 +6,23 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
roc_collections = { path = "../collections" }
# roc_region = { path = "../region" }
# roc_load = { path = "../load" }
roc_module = { path = "../module" }
# roc_problem = { path = "../problem" }
# roc_types = { path = "../types" }
# roc_builtins = { path = "../builtins" }
# roc_constrain = { path = "../constrain" }
# roc_unify = { path = "../unify" }
# roc_solve = { path = "../solve" }
roc_mono = { path = "../mono" }
# im = "14" # im and im-rc should always have the same version!
# im-rc = "14" # im and im-rc should always have the same version!
bumpalo = { version = "3.6.1", features = ["collections"] }
# target-lexicon = "0.12.2"
# libloading = "0.6"
parity-wasm = "0.42"
[dev-dependencies]
wasmer = "2.0.0"

View file

@ -1,7 +1,103 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
use bumpalo::{collections::Vec, Bump};
use parity_wasm::{elements, builder};
use parity_wasm::elements::{ValueType, Internal};
use parity_wasm::builder::{ModuleBuilder, FunctionDefinition, FunctionBuilder, CodeLocation};
// use roc_builtins::bitcode;
use roc_collections::all::{MutMap, MutSet};
// use roc_module::ident::{ModuleName, TagName};
// use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, Symbol};
use roc_mono::ir::{BranchInfo, CallType, Expr, JoinPointId, Literal, Proc, ProcLayout, Stmt};
use roc_mono::layout::{Builtin, Layout, LayoutIds};
pub struct Env<'a> {
pub arena: &'a Bump,
pub interns: Interns,
pub exposed_to_host: MutSet<Symbol>,
}
pub enum SymbolStorage {
Local(ValueType, u32),
LocalAndBase(ValueType, u32, u32),
}
pub struct LabelId(u32);
// Don't allocate any constant data at the address zero or anywhere near it.
// These addresses are not special in Wasm, but putting something there seems bug-prone.
// Emscripten leaves 1kB free so let's do the same for now, although 4 bytes would probably do.
const UNUSED_DATA_SECTION_BYTES: u32 = 1024;
pub struct BackendWasm<'a> {
// module-level state
env: &'a Env<'a>,
module_builder: ModuleBuilder,
data_offset_map: MutMap<Literal<'a>, u32>,
data_offset_next: u32,
// procedure-level state
symbol_storage_map: MutMap<Symbol, SymbolStorage>,
joinpoint_label_map: MutMap<JoinPointId, LabelId>,
}
pub fn build_module<'a>(env: &'a Env, procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>) -> Result<elements::Module, String> {
let mut backend = BackendWasm::new(env);
let mut layout_ids = LayoutIds::default();
for ((sym, layout), proc) in procedures {
let location = backend.build_proc(proc)?;
if env.exposed_to_host.contains(&sym) {
let fn_name = layout_ids
.get_toplevel(sym, &layout)
.to_symbol_string(sym, &env.interns);
let export = builder::export()
.field(fn_name.as_str())
.with_internal(Internal::Function(location.body))
.build();
backend.module_builder.push_export(export);
}
}
Ok(backend.module_builder.build())
}
impl <'a>BackendWasm<'a> {
pub fn new(env: &'a Env) -> Self {
BackendWasm {
env,
module_builder: builder::module(),
data_offset_map: MutMap::default(),
data_offset_next: UNUSED_DATA_SECTION_BYTES,
symbol_storage_map: MutMap::default(),
joinpoint_label_map: MutMap::default(),
}
}
fn build_proc(&mut self, proc: Proc<'a>) -> Result<CodeLocation, String> {
let mut function_builder = builder::function();
//
// ... generate stuff ...
//
let def = function_builder.build();
let location = self.module_builder.push_function(def);
Ok(location)
}
fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &Layout<'a>) -> Result<(), String> {
Err("todo: everything".to_string())
}
fn build_expr(
&mut self,
sym: &Symbol,
expr: &Expr<'a>,
layout: &Layout<'a>,
) -> Result<(), String> {
Err("todo: everything".to_string())
}
}