mirror of
https://github.com/roc-lang/roc.git
synced 2025-11-02 05:48:17 +00:00
Move code_builder from wasm_module to gen_wasm
This commit is contained in:
parent
c8f949d546
commit
4dea82b2f5
8 changed files with 169 additions and 192 deletions
|
|
@ -1,7 +1,6 @@
|
|||
use bitvec::vec::BitVec;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
|
||||
use code_builder::Align;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_collections::all::MutMap;
|
||||
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_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::sections::{
|
||||
ConstExpr, DataMode, DataSegment, Export, Global, GlobalType, Import, ImportDesc, Limits,
|
||||
MemorySection, NameSection,
|
||||
};
|
||||
use roc_wasm_module::{
|
||||
code_builder, round_up_to_alignment, CodeBuilder, ExportType, LocalId, Signature, SymInfo,
|
||||
ValueType, WasmModule,
|
||||
round_up_to_alignment, Align, ExportType, LocalId, Signature, SymInfo, 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)]
|
||||
|
|
@ -104,10 +104,9 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
|
||||
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()
|
||||
+ (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 {
|
||||
env,
|
||||
|
|
@ -212,7 +211,7 @@ impl<'a> WasmBackend<'a> {
|
|||
self.module.data.end_addr += PTR_SIZE;
|
||||
|
||||
self.module.reloc_code.apply_relocs_u32(
|
||||
&mut self.module.code.preloaded_bytes,
|
||||
&mut self.module.code.bytes,
|
||||
sym_index as u32,
|
||||
global_value_addr,
|
||||
);
|
||||
|
|
@ -320,7 +319,7 @@ impl<'a> WasmBackend<'a> {
|
|||
self.module.export.append(Export {
|
||||
name: START,
|
||||
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
|
||||
|
|
@ -367,7 +366,8 @@ impl<'a> WasmBackend<'a> {
|
|||
// 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);
|
||||
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.joinpoint_label_map.clear();
|
||||
|
|
@ -1343,7 +1343,7 @@ impl<'a> WasmBackend<'a> {
|
|||
|
||||
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 {
|
||||
self.code_builder
|
||||
.call_import(*fn_index, num_wasm_args, has_return_val);
|
||||
|
|
|
|||
|
|
@ -1,53 +1,24 @@
|
|||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
use core::panic;
|
||||
|
||||
use roc_error_macros::internal_error;
|
||||
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use super::opcodes::{OpCode, OpCode::*};
|
||||
use super::serialize::{SerialBuffer, Serialize};
|
||||
use crate::{
|
||||
round_up_to_alignment, DEBUG_SETTINGS, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID,
|
||||
use roc_wasm_module::opcodes::{OpCode, OpCode::*};
|
||||
use roc_wasm_module::serialize::{SerialBuffer, Serialize};
|
||||
use roc_wasm_module::{
|
||||
round_up_to_alignment, Align, LocalId, ValueType, FRAME_ALIGNMENT_BYTES,
|
||||
STACK_POINTER_GLOBAL_ID,
|
||||
};
|
||||
|
||||
use crate::DEBUG_SETTINGS;
|
||||
|
||||
macro_rules! log_instruction {
|
||||
($($x: expr),+) => {
|
||||
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;
|
||||
|
||||
/// 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)]
|
||||
pub enum VmSymbolState {
|
||||
/// Value doesn't exist yet
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//! Provides the WASM backend to generate Roc binaries.
|
||||
mod backend;
|
||||
mod code_builder;
|
||||
mod layout;
|
||||
mod low_level;
|
||||
mod storage;
|
||||
|
|
@ -19,9 +20,10 @@ use roc_mono::ir::{Proc, ProcLayout};
|
|||
use roc_mono::layout::{LayoutIds, STLayoutInterner};
|
||||
use roc_target::TargetInfo;
|
||||
use roc_wasm_module::parse::ParseError;
|
||||
use roc_wasm_module::{Align, LocalId, ValueType, WasmModule};
|
||||
|
||||
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 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
|
||||
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
|
||||
// Create a lookup to tell us the final index of each proc in the output file
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ use roc_error_macros::internal_error;
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Layout, STLayoutInterner};
|
||||
|
||||
use crate::code_builder::{CodeBuilder, VmSymbolState};
|
||||
use crate::layout::{CallConv, ReturnMethod, StackMemoryFormat, WasmLayout};
|
||||
use crate::{copy_memory, CopyMemoryConfig, PTR_TYPE};
|
||||
use roc_wasm_module::{
|
||||
round_up_to_alignment, Align, CodeBuilder, LocalId, ValueType, VmSymbolState,
|
||||
};
|
||||
use roc_wasm_module::{round_up_to_alignment, Align, LocalId, ValueType};
|
||||
|
||||
pub enum StoredVarKind {
|
||||
Variable,
|
||||
|
|
|
|||
|
|
@ -5,18 +5,20 @@ The user needs to analyse the Wasm module's memory to decode the result.
|
|||
*/
|
||||
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_intern::Interner;
|
||||
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_target::TargetInfo;
|
||||
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,
|
||||
};
|
||||
|
||||
use crate::code_builder::CodeBuilder;
|
||||
use crate::wasm32_sized::Wasm32Sized;
|
||||
|
||||
/// Type-driven wrapper generation
|
||||
pub trait Wasm32Result {
|
||||
fn insert_wrapper<'a>(
|
||||
|
|
@ -28,7 +30,7 @@ pub trait Wasm32Result {
|
|||
insert_wrapper_metadata(arena, module, wrapper_name);
|
||||
let mut code_builder = CodeBuilder::new(arena);
|
||||
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);
|
||||
|
|
@ -51,7 +53,7 @@ pub fn insert_wrapper_for_layout<'a>(
|
|||
insert_wrapper_metadata(arena, module, wrapper_name);
|
||||
let mut code_builder = CodeBuilder::new(arena);
|
||||
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)
|
||||
+ module.code.dead_import_dummy_count
|
||||
+ module.code.preloaded_count
|
||||
+ module.code.code_builders.len() as u32;
|
||||
+ module.code.function_count;
|
||||
|
||||
module.add_function_signature(Signature {
|
||||
param_types: Vec::with_capacity_in(0, arena),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
pub mod code_builder;
|
||||
pub mod linking;
|
||||
pub mod opcodes;
|
||||
pub mod parse;
|
||||
|
|
@ -7,8 +6,9 @@ pub mod serialize;
|
|||
|
||||
use std::iter::repeat;
|
||||
|
||||
pub use code_builder::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState};
|
||||
pub use linking::{OffsetRelocType, RelocationEntry, SymInfo};
|
||||
use opcodes::OpCode;
|
||||
use roc_error_macros::internal_error;
|
||||
pub use sections::{ConstExpr, Export, ExportType, Global, GlobalType, Signature};
|
||||
|
||||
use bitvec::vec::BitVec;
|
||||
|
|
@ -21,7 +21,7 @@ use self::sections::{
|
|||
ImportDesc, ImportSection, MemorySection, NameSection, OpaqueSection, Section, SectionId,
|
||||
TableSection, TypeSection,
|
||||
};
|
||||
use self::serialize::{SerialBuffer, Serialize};
|
||||
pub use self::serialize::{SerialBuffer, Serialize};
|
||||
|
||||
pub const STACK_POINTER_GLOBAL_ID: u32 = 0;
|
||||
pub const FRAME_ALIGNMENT_BYTES: i32 = 16;
|
||||
|
|
@ -132,7 +132,7 @@ impl<'a> WasmModule<'a> {
|
|||
if function.signatures.is_empty() {
|
||||
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");
|
||||
}
|
||||
if linking.symbol_table.is_empty() {
|
||||
|
|
@ -187,7 +187,7 @@ impl<'a> WasmModule<'a> {
|
|||
|
||||
let import_count = self.import.imports.len();
|
||||
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
|
||||
let exported_fns = self
|
||||
|
|
@ -278,39 +278,27 @@ impl<'a> WasmModule<'a> {
|
|||
.linking
|
||||
.find_and_reindex_imported_fn(old_index as u32, new_index as u32)
|
||||
.unwrap();
|
||||
self.reloc_code.apply_relocs_u32(
|
||||
&mut self.code.preloaded_bytes,
|
||||
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);
|
||||
self.reloc_code
|
||||
.apply_relocs_u32(&mut self.code.bytes, sym_index, new_index as u32);
|
||||
}
|
||||
|
||||
//
|
||||
// Dead code elimination. Replace dead functions with tiny dummies.
|
||||
// Live function indices are unchanged, so no relocations are needed.
|
||||
//
|
||||
let dummy = CodeBuilder::dummy(arena);
|
||||
let mut dummy_bytes = Vec::with_capacity_in(dummy.size(), arena);
|
||||
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);
|
||||
let mut buffer = Vec::with_capacity_in(self.code.bytes.len(), arena);
|
||||
self.code.function_count.serialize(&mut buffer);
|
||||
for (i, fn_index) in (host_fn_min..host_fn_max).enumerate() {
|
||||
if live_flags[fn_index as usize] {
|
||||
let code_start = self.code.preloaded_offsets[i] as usize;
|
||||
let code_end = self.code.preloaded_offsets[i + 1] as usize;
|
||||
buffer.extend_from_slice(&self.code.preloaded_bytes[code_start..code_end]);
|
||||
let code_start = self.code.function_offsets[i] as usize;
|
||||
let code_end = self.code.function_offsets[i + 1] as usize;
|
||||
buffer.extend_from_slice(&self.code.bytes[code_start..code_end]);
|
||||
} 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>>(
|
||||
|
|
@ -378,8 +366,8 @@ impl<'a> WasmModule<'a> {
|
|||
|
||||
// Find where the function body is
|
||||
let offset_index = fn_index - host_fn_min as usize;
|
||||
let code_start = self.code.preloaded_offsets[offset_index];
|
||||
let code_end = self.code.preloaded_offsets[offset_index + 1];
|
||||
let code_start = self.code.function_offsets[offset_index];
|
||||
let code_end = self.code.function_offsets[offset_index + 1];
|
||||
|
||||
// For each call in the body
|
||||
for (offset, symbol) in call_offsets_and_symbols.iter() {
|
||||
|
|
@ -423,11 +411,8 @@ impl<'a> WasmModule<'a> {
|
|||
self.linking
|
||||
.find_internal_symbol(sym_name)
|
||||
.map(|sym_index| {
|
||||
self.reloc_code.apply_relocs_u32(
|
||||
&mut self.code.preloaded_bytes,
|
||||
sym_index as u32,
|
||||
value,
|
||||
);
|
||||
self.reloc_code
|
||||
.apply_relocs_u32(&mut self.code.bytes, sym_index as u32, value);
|
||||
|
||||
sym_index as u32
|
||||
})
|
||||
|
|
@ -494,11 +479,8 @@ impl<'a> WasmModule<'a> {
|
|||
.unwrap();
|
||||
|
||||
// Update calls to use the app function instead of the host import
|
||||
self.reloc_code.apply_relocs_u32(
|
||||
&mut self.code.preloaded_bytes,
|
||||
host_sym_index,
|
||||
app_fn_index,
|
||||
);
|
||||
self.reloc_code
|
||||
.apply_relocs_u32(&mut self.code.bytes, host_sym_index, app_fn_index);
|
||||
|
||||
if swap_import_index != host_import_index {
|
||||
// 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
|
||||
self.reloc_code.apply_relocs_u32(
|
||||
&mut self.code.preloaded_bytes,
|
||||
&mut self.code.bytes,
|
||||
swap_sym_index,
|
||||
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)
|
||||
#[macro_export]
|
||||
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 {
|
||||
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 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),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ use bumpalo::collections::vec::Vec;
|
|||
use bumpalo::Bump;
|
||||
use roc_error_macros::internal_error;
|
||||
|
||||
use crate::DUMMY_FUNCTION;
|
||||
|
||||
use super::linking::{LinkingSection, SymInfo, WasmObjectSymbol};
|
||||
use super::opcodes::OpCode;
|
||||
use super::parse::{Parse, ParseError, SkipBytes};
|
||||
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)]
|
||||
pub struct CodeSection<'a> {
|
||||
pub preloaded_count: u32,
|
||||
pub preloaded_bytes: Vec<'a, u8>,
|
||||
pub function_count: u32,
|
||||
pub bytes: Vec<'a, u8>,
|
||||
/// 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
|
||||
pub dead_import_dummy_count: u32,
|
||||
pub code_builders: Vec<'a, CodeBuilder<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> CodeSection<'a> {
|
||||
pub fn size(&self) -> usize {
|
||||
let builders_size: usize = self.code_builders.iter().map(|cb| cb.size()).sum();
|
||||
|
||||
MAX_SIZE_SECTION_HEADER + self.preloaded_bytes.len() + builders_size
|
||||
MAX_SIZE_SECTION_HEADER + self.bytes.len()
|
||||
}
|
||||
|
||||
pub fn parse(
|
||||
|
|
@ -1224,11 +1223,10 @@ impl<'a> CodeSection<'a> {
|
|||
debug_assert_eq!(preloaded_offsets.len(), 1 + count as usize);
|
||||
|
||||
Ok(CodeSection {
|
||||
preloaded_count: count,
|
||||
preloaded_bytes,
|
||||
preloaded_offsets,
|
||||
function_count: count,
|
||||
bytes: preloaded_bytes,
|
||||
function_offsets: preloaded_offsets,
|
||||
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> {
|
||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||
let header_indices = write_section_header(buffer, SectionId::Code);
|
||||
buffer.encode_u32(
|
||||
self.dead_import_dummy_count + self.preloaded_count + self.code_builders.len() as u32,
|
||||
);
|
||||
buffer.encode_u32(self.dead_import_dummy_count + self.function_count);
|
||||
|
||||
// Insert dummy functions, requested by our linking logic.
|
||||
// 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 {
|
||||
dummy.serialize(buffer);
|
||||
DUMMY_FUNCTION.serialize(buffer);
|
||||
}
|
||||
|
||||
// host + builtin functions
|
||||
let first_fn_start = self.preloaded_offsets[0] as usize;
|
||||
buffer.append_slice(&self.preloaded_bytes[first_fn_start..]);
|
||||
|
||||
// Roc functions
|
||||
for code_builder in self.code_builders.iter() {
|
||||
code_builder.serialize(buffer);
|
||||
}
|
||||
// real functions
|
||||
let first_fn_start = self.function_offsets[0] as usize;
|
||||
buffer.append_slice(&self.bytes[first_fn_start..]);
|
||||
|
||||
update_section_size(buffer, header_indices);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
pub const MAX_SIZE_ENCODED_U32: usize = 5;
|
||||
|
||||
pub(super) trait Serialize {
|
||||
pub trait Serialize {
|
||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue