Move code_builder from wasm_module to gen_wasm

This commit is contained in:
Brian Carroll 2022-11-14 09:18:53 +00:00
parent c8f949d546
commit 4dea82b2f5
No known key found for this signature in database
GPG key ID: 5C7B2EC4101703C0
8 changed files with 169 additions and 192 deletions

View file

@ -1,7 +1,6 @@
use bitvec::vec::BitVec; use bitvec::vec::BitVec;
use bumpalo::collections::{String, Vec}; use bumpalo::collections::{String, Vec};
use code_builder::Align;
use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
@ -15,21 +14,22 @@ use roc_mono::ir::{
use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout}; use roc_mono::layout::{Builtin, Layout, LayoutIds, TagIdIntType, UnionLayout};
use roc_std::RocDec; use roc_std::RocDec;
use crate::layout::{CallConv, ReturnMethod, WasmLayout};
use crate::low_level::{call_higher_order_lowlevel, LowLevelCall};
use crate::storage::{AddressValue, Storage, StoredValue, StoredVarKind};
use crate::{
copy_memory, CopyMemoryConfig, Env, DEBUG_SETTINGS, MEMORY_NAME, PTR_SIZE, PTR_TYPE,
TARGET_INFO,
};
use roc_wasm_module::linking::{DataSymbol, WasmObjectSymbol}; use roc_wasm_module::linking::{DataSymbol, WasmObjectSymbol};
use roc_wasm_module::sections::{ use roc_wasm_module::sections::{
ConstExpr, DataMode, DataSegment, Export, Global, GlobalType, Import, ImportDesc, Limits, ConstExpr, DataMode, DataSegment, Export, Global, GlobalType, Import, ImportDesc, Limits,
MemorySection, NameSection, MemorySection, NameSection,
}; };
use roc_wasm_module::{ use roc_wasm_module::{
code_builder, round_up_to_alignment, CodeBuilder, ExportType, LocalId, Signature, SymInfo, round_up_to_alignment, Align, ExportType, LocalId, Signature, SymInfo, ValueType, WasmModule,
ValueType, WasmModule, };
use crate::code_builder::CodeBuilder;
use crate::layout::{CallConv, ReturnMethod, WasmLayout};
use crate::low_level::{call_higher_order_lowlevel, LowLevelCall};
use crate::storage::{AddressValue, Storage, StoredValue, StoredVarKind};
use crate::{
copy_memory, CopyMemoryConfig, Env, DEBUG_SETTINGS, MEMORY_NAME, PTR_SIZE, PTR_TYPE,
TARGET_INFO,
}; };
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -104,10 +104,9 @@ impl<'a> WasmBackend<'a> {
} }
module.link_host_to_app_calls(env.arena, host_to_app_map); module.link_host_to_app_calls(env.arena, host_to_app_map);
module.code.code_builders.reserve(proc_lookup.len());
let host_function_count = module.import.imports.len() let host_function_count = module.import.imports.len()
+ (module.code.dead_import_dummy_count + module.code.preloaded_count) as usize; + (module.code.dead_import_dummy_count + module.code.function_count) as usize;
WasmBackend { WasmBackend {
env, env,
@ -212,7 +211,7 @@ impl<'a> WasmBackend<'a> {
self.module.data.end_addr += PTR_SIZE; self.module.data.end_addr += PTR_SIZE;
self.module.reloc_code.apply_relocs_u32( self.module.reloc_code.apply_relocs_u32(
&mut self.module.code.preloaded_bytes, &mut self.module.code.bytes,
sym_index as u32, sym_index as u32,
global_value_addr, global_value_addr,
); );
@ -320,7 +319,7 @@ impl<'a> WasmBackend<'a> {
self.module.export.append(Export { self.module.export.append(Export {
name: START, name: START,
ty: ExportType::Func, ty: ExportType::Func,
index: self.fn_index_offset + self.module.code.code_builders.len() as u32, index: self.module.code.function_count,
}); });
self.code_builder.i32_const(0); // argc=0 self.code_builder.i32_const(0); // argc=0
@ -367,7 +366,8 @@ impl<'a> WasmBackend<'a> {
// Push the completed CodeBuilder into the module and swap it for a new empty one // Push the completed CodeBuilder into the module and swap it for a new empty one
let mut swap_code_builder = CodeBuilder::new(self.env.arena); let mut swap_code_builder = CodeBuilder::new(self.env.arena);
std::mem::swap(&mut swap_code_builder, &mut self.code_builder); std::mem::swap(&mut swap_code_builder, &mut self.code_builder);
self.module.code.code_builders.push(swap_code_builder);
swap_code_builder.serialize_without_relocs(&mut self.module.code.bytes);
self.storage.clear(); self.storage.clear();
self.joinpoint_label_map.clear(); self.joinpoint_label_map.clear();
@ -1343,7 +1343,7 @@ impl<'a> WasmBackend<'a> {
self.called_preload_fns.set(*fn_index as usize, true); self.called_preload_fns.set(*fn_index as usize, true);
let host_import_count = self.fn_index_offset - self.module.code.preloaded_count; let host_import_count = self.fn_index_offset - self.module.code.function_count;
if *fn_index < host_import_count { if *fn_index < host_import_count {
self.code_builder self.code_builder
.call_import(*fn_index, num_wasm_args, has_return_val); .call_import(*fn_index, num_wasm_args, has_return_val);

View file

@ -1,53 +1,24 @@
use bumpalo::collections::vec::Vec; use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use core::panic; use core::panic;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_wasm_module::opcodes::{OpCode, OpCode::*};
use super::opcodes::{OpCode, OpCode::*}; use roc_wasm_module::serialize::{SerialBuffer, Serialize};
use super::serialize::{SerialBuffer, Serialize}; use roc_wasm_module::{
use crate::{ round_up_to_alignment, Align, LocalId, ValueType, FRAME_ALIGNMENT_BYTES,
round_up_to_alignment, DEBUG_SETTINGS, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID, STACK_POINTER_GLOBAL_ID,
}; };
use crate::DEBUG_SETTINGS;
macro_rules! log_instruction { macro_rules! log_instruction {
($($x: expr),+) => { ($($x: expr),+) => {
if DEBUG_SETTINGS.instructions { println!($($x,)*); } if DEBUG_SETTINGS.instructions { println!($($x,)*); }
}; };
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LocalId(pub u32);
/// Wasm value type. (Rust representation matches Wasm encoding)
#[repr(u8)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum ValueType {
I32 = 0x7f,
I64 = 0x7e,
F32 = 0x7d,
F64 = 0x7c,
}
impl Serialize for ValueType {
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
buffer.append_u8(*self as u8);
}
}
impl From<u8> for ValueType {
fn from(x: u8) -> Self {
match x {
0x7f => Self::I32,
0x7e => Self::I64,
0x7d => Self::F32,
0x7c => Self::F64,
_ => internal_error!("Invalid ValueType 0x{:02x}", x),
}
}
}
const BLOCK_NO_RESULT: u8 = 0x40; const BLOCK_NO_RESULT: u8 = 0x40;
/// A control block in our model of the VM /// A control block in our model of the VM
@ -65,52 +36,6 @@ impl std::fmt::Debug for VmBlock<'_> {
} }
} }
/// Wasm memory alignment for load/store instructions.
/// Rust representation matches Wasm encoding.
/// It's an error to specify alignment higher than the "natural" alignment of the instruction
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
pub enum Align {
Bytes1 = 0,
Bytes2 = 1,
Bytes4 = 2,
Bytes8 = 3,
}
impl Align {
/// Calculate the largest possible alignment for a load/store at a given stack frame offset
/// Assumes the stack frame is aligned to at least 8 bytes
pub fn from_stack_offset(max_align: Align, offset: u32) -> Align {
if (max_align == Align::Bytes8) && (offset & 7 == 0) {
return Align::Bytes8;
}
if (max_align >= Align::Bytes4) && (offset & 3 == 0) {
return Align::Bytes4;
}
if (max_align >= Align::Bytes2) && (offset & 1 == 0) {
return Align::Bytes2;
}
Align::Bytes1
}
}
impl From<u32> for Align {
fn from(x: u32) -> Align {
match x {
1 => Align::Bytes1,
2 => Align::Bytes2,
4 => Align::Bytes4,
_ => {
if x.count_ones() == 1 {
Align::Bytes8 // Max value supported by any Wasm instruction
} else {
internal_error!("Cannot align to {} bytes", x);
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)] #[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum VmSymbolState { pub enum VmSymbolState {
/// Value doesn't exist yet /// Value doesn't exist yet

View file

@ -1,5 +1,6 @@
//! Provides the WASM backend to generate Roc binaries. //! Provides the WASM backend to generate Roc binaries.
mod backend; mod backend;
mod code_builder;
mod layout; mod layout;
mod low_level; mod low_level;
mod storage; mod storage;
@ -19,9 +20,10 @@ use roc_mono::ir::{Proc, ProcLayout};
use roc_mono::layout::{LayoutIds, STLayoutInterner}; use roc_mono::layout::{LayoutIds, STLayoutInterner};
use roc_target::TargetInfo; use roc_target::TargetInfo;
use roc_wasm_module::parse::ParseError; use roc_wasm_module::parse::ParseError;
use roc_wasm_module::{Align, LocalId, ValueType, WasmModule};
use crate::backend::{ProcLookupData, ProcSource, WasmBackend}; use crate::backend::{ProcLookupData, ProcSource, WasmBackend};
use roc_wasm_module::{Align, CodeBuilder, LocalId, ValueType, WasmModule}; use crate::code_builder::CodeBuilder;
const TARGET_INFO: TargetInfo = TargetInfo::default_wasm32(); const TARGET_INFO: TargetInfo = TargetInfo::default_wasm32();
const PTR_SIZE: u32 = { const PTR_SIZE: u32 = {
@ -96,7 +98,7 @@ pub fn build_app_module<'a>(
// Adjust Wasm function indices to account for functions from the object file // Adjust Wasm function indices to account for functions from the object file
let fn_index_offset: u32 = let fn_index_offset: u32 =
host_module.import.function_count() as u32 + host_module.code.preloaded_count; host_module.import.function_count() as u32 + host_module.code.function_count;
// Pre-pass over the procedure names & layouts // Pre-pass over the procedure names & layouts
// Create a lookup to tell us the final index of each proc in the output file // Create a lookup to tell us the final index of each proc in the output file

View file

@ -6,11 +6,10 @@ use roc_error_macros::internal_error;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::layout::{Layout, STLayoutInterner}; use roc_mono::layout::{Layout, STLayoutInterner};
use crate::code_builder::{CodeBuilder, VmSymbolState};
use crate::layout::{CallConv, ReturnMethod, StackMemoryFormat, WasmLayout}; use crate::layout::{CallConv, ReturnMethod, StackMemoryFormat, WasmLayout};
use crate::{copy_memory, CopyMemoryConfig, PTR_TYPE}; use crate::{copy_memory, CopyMemoryConfig, PTR_TYPE};
use roc_wasm_module::{ use roc_wasm_module::{round_up_to_alignment, Align, LocalId, ValueType};
round_up_to_alignment, Align, CodeBuilder, LocalId, ValueType, VmSymbolState,
};
pub enum StoredVarKind { pub enum StoredVarKind {
Variable, Variable,

View file

@ -5,18 +5,20 @@ The user needs to analyse the Wasm module's memory to decode the result.
*/ */
use bumpalo::{collections::Vec, Bump}; use bumpalo::{collections::Vec, Bump};
use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_intern::Interner; use roc_intern::Interner;
use roc_mono::layout::{Builtin, Layout, UnionLayout}; use roc_mono::layout::{Builtin, Layout, UnionLayout};
use roc_target::TargetInfo;
use crate::wasm32_sized::Wasm32Sized;
use roc_std::{RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128}; use roc_std::{RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
use roc_target::TargetInfo;
use roc_wasm_module::{ use roc_wasm_module::{
linking::SymInfo, linking::WasmObjectSymbol, Align, CodeBuilder, Export, ExportType, LocalId, linking::SymInfo, linking::WasmObjectSymbol, Align, Export, ExportType, LocalId, Serialize,
Signature, ValueType, WasmModule, Signature, ValueType, WasmModule,
}; };
use crate::code_builder::CodeBuilder;
use crate::wasm32_sized::Wasm32Sized;
/// Type-driven wrapper generation /// Type-driven wrapper generation
pub trait Wasm32Result { pub trait Wasm32Result {
fn insert_wrapper<'a>( fn insert_wrapper<'a>(
@ -28,7 +30,7 @@ pub trait Wasm32Result {
insert_wrapper_metadata(arena, module, wrapper_name); insert_wrapper_metadata(arena, module, wrapper_name);
let mut code_builder = CodeBuilder::new(arena); let mut code_builder = CodeBuilder::new(arena);
Self::build_wrapper_body(&mut code_builder, main_function_index); Self::build_wrapper_body(&mut code_builder, main_function_index);
module.code.code_builders.push(code_builder); code_builder.serialize(&mut module.code.bytes);
} }
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32); fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32);
@ -51,7 +53,7 @@ pub fn insert_wrapper_for_layout<'a>(
insert_wrapper_metadata(arena, module, wrapper_name); insert_wrapper_metadata(arena, module, wrapper_name);
let mut code_builder = CodeBuilder::new(arena); let mut code_builder = CodeBuilder::new(arena);
build_wrapper_body_stack_memory(&mut code_builder, main_fn_index, size as usize); build_wrapper_body_stack_memory(&mut code_builder, main_fn_index, size as usize);
module.code.code_builders.push(code_builder); code_builder.serialize(&mut module.code.bytes);
} }
}; };
@ -92,8 +94,7 @@ fn insert_wrapper_metadata<'a>(
) { ) {
let index = (module.import.function_count() as u32) let index = (module.import.function_count() as u32)
+ module.code.dead_import_dummy_count + module.code.dead_import_dummy_count
+ module.code.preloaded_count + module.code.function_count;
+ module.code.code_builders.len() as u32;
module.add_function_signature(Signature { module.add_function_signature(Signature {
param_types: Vec::with_capacity_in(0, arena), param_types: Vec::with_capacity_in(0, arena),

View file

@ -1,4 +1,3 @@
pub mod code_builder;
pub mod linking; pub mod linking;
pub mod opcodes; pub mod opcodes;
pub mod parse; pub mod parse;
@ -7,8 +6,9 @@ pub mod serialize;
use std::iter::repeat; use std::iter::repeat;
pub use code_builder::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState};
pub use linking::{OffsetRelocType, RelocationEntry, SymInfo}; pub use linking::{OffsetRelocType, RelocationEntry, SymInfo};
use opcodes::OpCode;
use roc_error_macros::internal_error;
pub use sections::{ConstExpr, Export, ExportType, Global, GlobalType, Signature}; pub use sections::{ConstExpr, Export, ExportType, Global, GlobalType, Signature};
use bitvec::vec::BitVec; use bitvec::vec::BitVec;
@ -21,7 +21,7 @@ use self::sections::{
ImportDesc, ImportSection, MemorySection, NameSection, OpaqueSection, Section, SectionId, ImportDesc, ImportSection, MemorySection, NameSection, OpaqueSection, Section, SectionId,
TableSection, TypeSection, TableSection, TypeSection,
}; };
use self::serialize::{SerialBuffer, Serialize}; pub use self::serialize::{SerialBuffer, Serialize};
pub const STACK_POINTER_GLOBAL_ID: u32 = 0; pub const STACK_POINTER_GLOBAL_ID: u32 = 0;
pub const FRAME_ALIGNMENT_BYTES: i32 = 16; pub const FRAME_ALIGNMENT_BYTES: i32 = 16;
@ -132,7 +132,7 @@ impl<'a> WasmModule<'a> {
if function.signatures.is_empty() { if function.signatures.is_empty() {
module_errors.push_str("Missing Function section\n"); module_errors.push_str("Missing Function section\n");
} }
if code.preloaded_bytes.is_empty() { if code.bytes.is_empty() {
module_errors.push_str("Missing Code section\n"); module_errors.push_str("Missing Code section\n");
} }
if linking.symbol_table.is_empty() { if linking.symbol_table.is_empty() {
@ -187,7 +187,7 @@ impl<'a> WasmModule<'a> {
let import_count = self.import.imports.len(); let import_count = self.import.imports.len();
let host_fn_min = import_count as u32 + self.code.dead_import_dummy_count; let host_fn_min = import_count as u32 + self.code.dead_import_dummy_count;
let host_fn_max = host_fn_min + self.code.preloaded_count; let host_fn_max = host_fn_min + self.code.function_count;
// All functions exported to JS must be kept alive // All functions exported to JS must be kept alive
let exported_fns = self let exported_fns = self
@ -278,39 +278,27 @@ impl<'a> WasmModule<'a> {
.linking .linking
.find_and_reindex_imported_fn(old_index as u32, new_index as u32) .find_and_reindex_imported_fn(old_index as u32, new_index as u32)
.unwrap(); .unwrap();
self.reloc_code.apply_relocs_u32( self.reloc_code
&mut self.code.preloaded_bytes, .apply_relocs_u32(&mut self.code.bytes, sym_index, new_index as u32);
sym_index,
new_index as u32,
);
}
// Relocate calls from Roc app to JS imports
for code_builder in self.code.code_builders.iter_mut() {
code_builder.apply_import_relocs(&live_import_fns);
} }
// //
// Dead code elimination. Replace dead functions with tiny dummies. // Dead code elimination. Replace dead functions with tiny dummies.
// Live function indices are unchanged, so no relocations are needed. // Live function indices are unchanged, so no relocations are needed.
// //
let dummy = CodeBuilder::dummy(arena); let mut buffer = Vec::with_capacity_in(self.code.bytes.len(), arena);
let mut dummy_bytes = Vec::with_capacity_in(dummy.size(), arena); self.code.function_count.serialize(&mut buffer);
dummy.serialize(&mut dummy_bytes);
let mut buffer = Vec::with_capacity_in(self.code.preloaded_bytes.len(), arena);
self.code.preloaded_count.serialize(&mut buffer);
for (i, fn_index) in (host_fn_min..host_fn_max).enumerate() { for (i, fn_index) in (host_fn_min..host_fn_max).enumerate() {
if live_flags[fn_index as usize] { if live_flags[fn_index as usize] {
let code_start = self.code.preloaded_offsets[i] as usize; let code_start = self.code.function_offsets[i] as usize;
let code_end = self.code.preloaded_offsets[i + 1] as usize; let code_end = self.code.function_offsets[i + 1] as usize;
buffer.extend_from_slice(&self.code.preloaded_bytes[code_start..code_end]); buffer.extend_from_slice(&self.code.bytes[code_start..code_end]);
} else { } else {
buffer.extend_from_slice(&dummy_bytes); buffer.extend_from_slice(&DUMMY_FUNCTION);
} }
} }
self.code.preloaded_bytes = buffer; self.code.bytes = buffer;
} }
fn trace_live_host_functions<I: Iterator<Item = u32>>( fn trace_live_host_functions<I: Iterator<Item = u32>>(
@ -378,8 +366,8 @@ impl<'a> WasmModule<'a> {
// Find where the function body is // Find where the function body is
let offset_index = fn_index - host_fn_min as usize; let offset_index = fn_index - host_fn_min as usize;
let code_start = self.code.preloaded_offsets[offset_index]; let code_start = self.code.function_offsets[offset_index];
let code_end = self.code.preloaded_offsets[offset_index + 1]; let code_end = self.code.function_offsets[offset_index + 1];
// For each call in the body // For each call in the body
for (offset, symbol) in call_offsets_and_symbols.iter() { for (offset, symbol) in call_offsets_and_symbols.iter() {
@ -423,11 +411,8 @@ impl<'a> WasmModule<'a> {
self.linking self.linking
.find_internal_symbol(sym_name) .find_internal_symbol(sym_name)
.map(|sym_index| { .map(|sym_index| {
self.reloc_code.apply_relocs_u32( self.reloc_code
&mut self.code.preloaded_bytes, .apply_relocs_u32(&mut self.code.bytes, sym_index as u32, value);
sym_index as u32,
value,
);
sym_index as u32 sym_index as u32
}) })
@ -494,11 +479,8 @@ impl<'a> WasmModule<'a> {
.unwrap(); .unwrap();
// Update calls to use the app function instead of the host import // Update calls to use the app function instead of the host import
self.reloc_code.apply_relocs_u32( self.reloc_code
&mut self.code.preloaded_bytes, .apply_relocs_u32(&mut self.code.bytes, host_sym_index, app_fn_index);
host_sym_index,
app_fn_index,
);
if swap_import_index != host_import_index { if swap_import_index != host_import_index {
// get the name using the old host import index because we already swapped it! // get the name using the old host import index because we already swapped it!
@ -512,7 +494,7 @@ impl<'a> WasmModule<'a> {
// Update calls to the swapped JS import // Update calls to the swapped JS import
self.reloc_code.apply_relocs_u32( self.reloc_code.apply_relocs_u32(
&mut self.code.preloaded_bytes, &mut self.code.bytes,
swap_sym_index, swap_sym_index,
host_fn_index as u32, host_fn_index as u32,
); );
@ -595,6 +577,89 @@ impl<'a> WasmModule<'a> {
} }
} }
/*******************************************************************
*
* Common types & utility functions
*
*******************************************************************/
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LocalId(pub u32);
/// Wasm value type. (Rust representation matches Wasm encoding)
#[repr(u8)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum ValueType {
I32 = 0x7f,
I64 = 0x7e,
F32 = 0x7d,
F64 = 0x7c,
}
impl Serialize for ValueType {
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
buffer.append_u8(*self as u8);
}
}
impl From<u8> for ValueType {
fn from(x: u8) -> Self {
match x {
0x7f => Self::I32,
0x7e => Self::I64,
0x7d => Self::F32,
0x7c => Self::F64,
_ => internal_error!("Invalid ValueType 0x{:02x}", x),
}
}
}
/// Wasm memory alignment for load/store instructions.
/// Rust representation matches Wasm encoding.
/// It's an error to specify alignment higher than the "natural" alignment of the instruction
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
pub enum Align {
Bytes1 = 0,
Bytes2 = 1,
Bytes4 = 2,
Bytes8 = 3,
}
impl Align {
/// Calculate the largest possible alignment for a load/store at a given stack frame offset
/// Assumes the stack frame is aligned to at least 8 bytes
pub fn from_stack_offset(max_align: Align, offset: u32) -> Align {
if (max_align == Align::Bytes8) && (offset & 7 == 0) {
return Align::Bytes8;
}
if (max_align >= Align::Bytes4) && (offset & 3 == 0) {
return Align::Bytes4;
}
if (max_align >= Align::Bytes2) && (offset & 1 == 0) {
return Align::Bytes2;
}
Align::Bytes1
}
}
impl From<u32> for Align {
fn from(x: u32) -> Align {
match x {
1 => Align::Bytes1,
2 => Align::Bytes2,
4 => Align::Bytes4,
_ => {
if x.count_ones() == 1 {
Align::Bytes8 // Max value supported by any Wasm instruction
} else {
internal_error!("Cannot align to {} bytes", x);
}
}
}
}
}
/// Round up to alignment_bytes (which must be a power of 2) /// Round up to alignment_bytes (which must be a power of 2)
#[macro_export] #[macro_export]
macro_rules! round_up_to_alignment { macro_rules! round_up_to_alignment {
@ -615,24 +680,20 @@ macro_rules! round_up_to_alignment {
}; };
} }
/// Bytes for a dummy function with just a single `unreachable` instruction.
/// Used in dead code elimination to replace unused functions.
const DUMMY_FUNCTION: [u8; 4] = [
3, // inner byte length
0, // number of local variable declarations
OpCode::UNREACHABLE as u8, // panic if we were wrong to eliminate!
OpCode::END as u8, // end of function (required for validation)
];
// TODO: make this an environment variable
pub struct WasmDebugSettings { pub struct WasmDebugSettings {
proc_start_end: bool,
user_procs_ir: bool,
helper_procs_ir: bool,
let_stmt_ir: bool,
instructions: bool,
storage_map: bool,
pub keep_test_binary: bool,
pub skip_dead_code_elim: bool, pub skip_dead_code_elim: bool,
} }
pub const DEBUG_SETTINGS: WasmDebugSettings = WasmDebugSettings { pub const DEBUG_SETTINGS: WasmDebugSettings = WasmDebugSettings {
proc_start_end: false && cfg!(debug_assertions),
user_procs_ir: false && cfg!(debug_assertions), // Note: we also have `ROC_PRINT_IR_AFTER_REFCOUNT=1 cargo test-gen-wasm`
helper_procs_ir: false && cfg!(debug_assertions),
let_stmt_ir: false && cfg!(debug_assertions),
instructions: false && cfg!(debug_assertions),
storage_map: false && cfg!(debug_assertions),
keep_test_binary: false && cfg!(debug_assertions),
skip_dead_code_elim: false && cfg!(debug_assertions), skip_dead_code_elim: false && cfg!(debug_assertions),
}; };

View file

@ -4,11 +4,13 @@ use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use crate::DUMMY_FUNCTION;
use super::linking::{LinkingSection, SymInfo, WasmObjectSymbol}; use super::linking::{LinkingSection, SymInfo, WasmObjectSymbol};
use super::opcodes::OpCode; use super::opcodes::OpCode;
use super::parse::{Parse, ParseError, SkipBytes}; use super::parse::{Parse, ParseError, SkipBytes};
use super::serialize::{SerialBuffer, Serialize, MAX_SIZE_ENCODED_U32}; use super::serialize::{SerialBuffer, Serialize, MAX_SIZE_ENCODED_U32};
use super::{CodeBuilder, ValueType}; use super::ValueType;
/******************************************************************* /*******************************************************************
* *
@ -1168,20 +1170,17 @@ impl<'a> Serialize for ElementSection<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct CodeSection<'a> { pub struct CodeSection<'a> {
pub preloaded_count: u32, pub function_count: u32,
pub preloaded_bytes: Vec<'a, u8>, pub bytes: Vec<'a, u8>,
/// The start of each preloaded function /// The start of each preloaded function
pub preloaded_offsets: Vec<'a, u32>, pub function_offsets: Vec<'a, u32>,
/// Dead imports are replaced with dummy functions in CodeSection /// Dead imports are replaced with dummy functions in CodeSection
pub dead_import_dummy_count: u32, pub dead_import_dummy_count: u32,
pub code_builders: Vec<'a, CodeBuilder<'a>>,
} }
impl<'a> CodeSection<'a> { impl<'a> CodeSection<'a> {
pub fn size(&self) -> usize { pub fn size(&self) -> usize {
let builders_size: usize = self.code_builders.iter().map(|cb| cb.size()).sum(); MAX_SIZE_SECTION_HEADER + self.bytes.len()
MAX_SIZE_SECTION_HEADER + self.preloaded_bytes.len() + builders_size
} }
pub fn parse( pub fn parse(
@ -1224,11 +1223,10 @@ impl<'a> CodeSection<'a> {
debug_assert_eq!(preloaded_offsets.len(), 1 + count as usize); debug_assert_eq!(preloaded_offsets.len(), 1 + count as usize);
Ok(CodeSection { Ok(CodeSection {
preloaded_count: count, function_count: count,
preloaded_bytes, bytes: preloaded_bytes,
preloaded_offsets, function_offsets: preloaded_offsets,
dead_import_dummy_count: 0, dead_import_dummy_count: 0,
code_builders: Vec::with_capacity_in(0, arena),
}) })
} }
} }
@ -1236,26 +1234,17 @@ impl<'a> CodeSection<'a> {
impl<'a> Serialize for CodeSection<'a> { impl<'a> Serialize for CodeSection<'a> {
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) { fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
let header_indices = write_section_header(buffer, SectionId::Code); let header_indices = write_section_header(buffer, SectionId::Code);
buffer.encode_u32( buffer.encode_u32(self.dead_import_dummy_count + self.function_count);
self.dead_import_dummy_count + self.preloaded_count + self.code_builders.len() as u32,
);
// Insert dummy functions, requested by our linking logic. // Insert dummy functions, requested by our linking logic.
// This helps to minimise the number of functions we need to move around during linking. // This helps to minimise the number of functions we need to move around during linking.
let arena = self.code_builders[0].arena;
let dummy = CodeBuilder::dummy(arena);
for _ in 0..self.dead_import_dummy_count { for _ in 0..self.dead_import_dummy_count {
dummy.serialize(buffer); DUMMY_FUNCTION.serialize(buffer);
} }
// host + builtin functions // real functions
let first_fn_start = self.preloaded_offsets[0] as usize; let first_fn_start = self.function_offsets[0] as usize;
buffer.append_slice(&self.preloaded_bytes[first_fn_start..]); buffer.append_slice(&self.bytes[first_fn_start..]);
// Roc functions
for code_builder in self.code_builders.iter() {
code_builder.serialize(buffer);
}
update_section_size(buffer, header_indices); update_section_size(buffer, header_indices);
} }

View file

@ -7,7 +7,7 @@ use std::fmt::Debug;
/// Of course there is a price for this - an encoded U32 can be up to 5 bytes wide. /// Of course there is a price for this - an encoded U32 can be up to 5 bytes wide.
pub const MAX_SIZE_ENCODED_U32: usize = 5; pub const MAX_SIZE_ENCODED_U32: usize = 5;
pub(super) trait Serialize { pub trait Serialize {
fn serialize<T: SerialBuffer>(&self, buffer: &mut T); fn serialize<T: SerialBuffer>(&self, buffer: &mut T);
} }