Merge branch 'trunk' into wasm_arrays

This commit is contained in:
rvcas 2021-12-17 11:54:23 -05:00
commit b9bd75d643
14 changed files with 490 additions and 336 deletions

View file

@ -1,13 +1,16 @@
use bumpalo::{self, collections::Vec};
use code_builder::Align;
use roc_builtins::bitcode::IntWidth;
use roc_builtins::bitcode::{self, IntWidth};
use roc_collections::all::MutMap;
use roc_module::ident::Ident;
use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, Symbol};
use roc_mono::gen_refcount::{RefcountProcGenerator, REFCOUNT_MAX};
use roc_mono::ir::{CallType, Expr, JoinPointId, ListLiteralElement, Literal, Proc, Stmt};
use roc_mono::code_gen_help::{CodeGenHelp, REFCOUNT_MAX};
use roc_mono::ir::{
CallType, Expr, JoinPointId, ListLiteralElement, Literal, Proc, ProcLayout, Stmt,
};
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
use roc_reporting::internal_error;
@ -50,7 +53,7 @@ pub struct WasmBackend<'a> {
builtin_sym_index_map: MutMap<&'a str, usize>,
proc_symbols: Vec<'a, (Symbol, u32)>,
linker_symbols: Vec<'a, SymInfo>,
refcount_proc_gen: RefcountProcGenerator<'a>,
helper_proc_gen: CodeGenHelp<'a>,
// Function-level data
code_builder: CodeBuilder<'a>,
@ -72,7 +75,7 @@ impl<'a> WasmBackend<'a> {
proc_symbols: Vec<'a, (Symbol, u32)>,
mut linker_symbols: Vec<'a, SymInfo>,
mut exports: Vec<'a, Export>,
refcount_proc_gen: RefcountProcGenerator<'a>,
helper_proc_gen: CodeGenHelp<'a>,
) -> Self {
const MEMORY_INIT_SIZE: u32 = 1024 * 1024;
let arena = env.arena;
@ -145,7 +148,7 @@ impl<'a> WasmBackend<'a> {
builtin_sym_index_map: MutMap::default(),
proc_symbols,
linker_symbols,
refcount_proc_gen,
helper_proc_gen,
// Function-level data
block_depth: 0,
@ -158,15 +161,34 @@ impl<'a> WasmBackend<'a> {
}
}
pub fn generate_refcount_procs(&mut self) -> Vec<'a, Proc<'a>> {
pub fn generate_helpers(&mut self) -> Vec<'a, Proc<'a>> {
let ident_ids = self
.interns
.all_ident_ids
.get_mut(&self.env.module_id)
.unwrap();
self.refcount_proc_gen
.generate_refcount_procs(self.env.arena, ident_ids)
self.helper_proc_gen
.generate_procs(self.env.arena, ident_ids)
}
fn register_helper_proc(&mut self, new_proc_info: (Symbol, ProcLayout<'a>)) {
let (new_proc_sym, new_proc_layout) = new_proc_info;
let wasm_fn_index = self.proc_symbols.len() as u32;
let linker_sym_index = self.linker_symbols.len() as u32;
let name = self
.layout_ids
.get_toplevel(new_proc_sym, &new_proc_layout)
.to_symbol_string(new_proc_sym, self.interns);
self.proc_symbols.push((new_proc_sym, linker_sym_index));
self.linker_symbols
.push(SymInfo::Function(WasmObjectSymbol::Defined {
flags: 0,
index: wasm_fn_index,
name,
}));
}
pub fn finalize_module(mut self) -> WasmModule<'a> {
@ -523,8 +545,8 @@ impl<'a> WasmBackend<'a> {
.get_mut(&self.env.module_id)
.unwrap();
let (rc_stmt, new_proc_info) = self
.refcount_proc_gen
let (rc_stmt, new_specializations) = self
.helper_proc_gen
.expand_refcount_stmt(ident_ids, *layout, modify, *following);
if false {
@ -532,24 +554,9 @@ impl<'a> WasmBackend<'a> {
println!("## rc_stmt:\n{}\n{:?}", rc_stmt.to_pretty(200), rc_stmt);
}
// If we're creating a new RC procedure, we need to store its symbol data,
// so that we can correctly generate calls to it.
if let Some((rc_proc_sym, rc_proc_layout)) = new_proc_info {
let wasm_fn_index = self.proc_symbols.len() as u32;
let linker_sym_index = self.linker_symbols.len() as u32;
let name = self
.layout_ids
.get_toplevel(rc_proc_sym, &rc_proc_layout)
.to_symbol_string(rc_proc_sym, self.interns);
self.proc_symbols.push((rc_proc_sym, linker_sym_index));
self.linker_symbols
.push(SymInfo::Function(WasmObjectSymbol::Defined {
flags: 0,
index: wasm_fn_index,
name,
}));
// If any new specializations were created, register their symbol data
for spec in new_specializations.into_iter() {
self.register_helper_proc(spec);
}
self.build_stmt(rc_stmt, ret_layout);
@ -583,7 +590,13 @@ impl<'a> WasmBackend<'a> {
CallType::ByName { name: func_sym, .. } => {
// If this function is just a lowlevel wrapper, then inline it
if let Some(lowlevel) = LowLevel::from_inlined_wrapper(*func_sym) {
return self.build_low_level(lowlevel, arguments, *sym, wasm_layout);
return self.build_low_level(
lowlevel,
arguments,
*sym,
wasm_layout,
storage,
);
}
let (param_types, ret_type) = self.storage.load_symbols_for_call(
@ -619,7 +632,7 @@ impl<'a> WasmBackend<'a> {
}
CallType::LowLevel { op: lowlevel, .. } => {
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout)
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout, storage)
}
x => todo!("call type {:?}", x),
@ -1009,6 +1022,7 @@ impl<'a> WasmBackend<'a> {
arguments: &'a [Symbol],
return_sym: Symbol,
return_layout: WasmLayout,
storage: &StoredValue,
) {
let (param_types, ret_type) = self.storage.load_symbols_for_call(
self.env.arena,
@ -1033,6 +1047,37 @@ impl<'a> WasmBackend<'a> {
BuiltinCall(name) => {
self.call_zig_builtin(name, param_types, ret_type);
}
SpecializedEq | SpecializedNotEq => {
let layout = self.symbol_layouts[&arguments[0]];
if layout == Layout::Builtin(Builtin::Str) {
self.call_zig_builtin(bitcode::STR_EQUAL, param_types, ret_type);
} else {
let ident_ids = self
.interns
.all_ident_ids
.get_mut(&self.env.module_id)
.unwrap();
let (replacement_expr, new_specializations) = self
.helper_proc_gen
.specialize_equals(ident_ids, &layout, arguments);
// If any new specializations were created, register their symbol data
for spec in new_specializations.into_iter() {
self.register_helper_proc(spec);
}
self.build_expr(&return_sym, replacement_expr, &layout, storage);
}
if matches!(build_result, SpecializedNotEq) {
self.code_builder.i32_eqz();
}
}
SpecializedHash => {
todo!("Specialized hash functions")
}
NotImplemented => {
todo!("Low level operation {:?}", lowlevel)
}

View file

@ -10,7 +10,7 @@ use roc_builtins::bitcode::IntWidth;
use roc_collections::all::{MutMap, MutSet};
use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::gen_refcount::RefcountProcGenerator;
use roc_mono::code_gen_help::CodeGenHelp;
use roc_mono::ir::{Proc, ProcLayout};
use roc_mono::layout::LayoutIds;
use roc_reporting::internal_error;
@ -94,7 +94,7 @@ pub fn build_module_help<'a>(
proc_symbols,
linker_symbols,
exports,
RefcountProcGenerator::new(env.arena, IntWidth::I32, env.module_id),
CodeGenHelp::new(env.arena, IntWidth::I32, env.module_id),
);
if false {
@ -110,21 +110,21 @@ pub fn build_module_help<'a>(
backend.build_proc(proc);
}
// Generate IR for refcounting procs
let refcount_procs = backend.generate_refcount_procs();
// Generate specialized helpers for refcounting & equality
let helper_procs = backend.generate_helpers();
backend.register_symbol_debug_names();
if false {
println!("## refcount_procs");
for proc in refcount_procs.iter() {
println!("## helper_procs");
for proc in helper_procs.iter() {
println!("{}", proc.to_pretty(200));
println!("{:#?}", proc);
}
}
// Generate Wasm for refcounting procs
for proc in refcount_procs.iter() {
for proc in helper_procs.iter() {
backend.build_proc(proc);
}

View file

@ -10,6 +10,9 @@ use crate::wasm_module::{Align, CodeBuilder, ValueType::*};
pub enum LowlevelBuildResult {
Done,
BuiltinCall(&'static str),
SpecializedEq,
SpecializedNotEq,
SpecializedHash,
NotImplemented,
}
@ -360,8 +363,7 @@ pub fn decode_low_level<'a>(
match storage.get(&args[0]) {
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
match value_type {
I32 => code_builder.i32_const(1),
I64 => code_builder.i32_const(1),
I32 | I64 => code_builder.i32_const(1), // always true for integers
F32 => {
code_builder.i32_reinterpret_f32();
code_builder.i32_const(0x7f80_0000);
@ -573,7 +575,8 @@ pub fn decode_low_level<'a>(
code_builder.i32_and();
}
Int128 => compare_bytes(code_builder),
Float128 | DataStructure => return NotImplemented,
Float128 => return NotImplemented,
DataStructure => return SpecializedEq,
}
}
}
@ -587,15 +590,19 @@ pub fn decode_low_level<'a>(
F32 => code_builder.f32_ne(),
F64 => code_builder.f64_ne(),
},
StoredValue::StackMemory { .. } => {
decode_low_level(code_builder, storage, LowLevel::Eq, args, ret_layout);
code_builder.i32_eqz();
StoredValue::StackMemory { format, .. } => {
if matches!(format, DataStructure) {
return SpecializedNotEq;
} else {
decode_low_level(code_builder, storage, LowLevel::Eq, args, ret_layout);
code_builder.i32_eqz();
}
}
},
And => code_builder.i32_and(),
Or => code_builder.i32_or(),
Not => code_builder.i32_eqz(),
Hash => return NotImplemented,
Hash => return SpecializedHash,
ExpectTrue => return NotImplemented,
RefCountGetPtr => {
code_builder.i32_const(4);