mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
wasm: Use Export section to initialise Name section
Works even if the preloaded Name section is missing.
This commit is contained in:
parent
1dd374a9ab
commit
1e5f659be1
3 changed files with 56 additions and 57 deletions
|
@ -22,12 +22,11 @@ use crate::storage::{Storage, StoredValue, StoredValueKind};
|
||||||
use crate::wasm_module::linking::{DataSymbol, LinkingSegment, WasmObjectSymbol};
|
use crate::wasm_module::linking::{DataSymbol, LinkingSegment, WasmObjectSymbol};
|
||||||
use crate::wasm_module::sections::{DataMode, DataSegment};
|
use crate::wasm_module::sections::{DataMode, DataSegment};
|
||||||
use crate::wasm_module::{
|
use crate::wasm_module::{
|
||||||
code_builder, CodeBuilder, Export, ExportType, LocalId, Signature, SymInfo, ValueType,
|
code_builder, CodeBuilder, ExportType, LocalId, Signature, SymInfo, ValueType, WasmModule,
|
||||||
WasmModule,
|
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
copy_memory, round_up_to_alignment, CopyMemoryConfig, Env, DEBUG_LOG_SETTINGS, MEMORY_NAME,
|
copy_memory, round_up_to_alignment, CopyMemoryConfig, Env, DEBUG_LOG_SETTINGS, PTR_SIZE,
|
||||||
PTR_SIZE, PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, TARGET_INFO,
|
PTR_TYPE, TARGET_INFO,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct WasmBackend<'a> {
|
pub struct WasmBackend<'a> {
|
||||||
|
@ -62,22 +61,32 @@ impl<'a> WasmBackend<'a> {
|
||||||
fn_index_offset: u32,
|
fn_index_offset: u32,
|
||||||
helper_proc_gen: CodeGenHelp<'a>,
|
helper_proc_gen: CodeGenHelp<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
module.export.append(Export {
|
// The preloaded builtins object file exports all functions, but the final app binary doesn't.
|
||||||
name: MEMORY_NAME.as_bytes(),
|
// Remove the function exports and use them to populate the Name section (debug info)
|
||||||
ty: ExportType::Mem,
|
let platform_and_builtins_exports =
|
||||||
index: 0,
|
std::mem::replace(&mut module.export.exports, bumpalo::vec![in env.arena]);
|
||||||
});
|
let mut app_exports = Vec::with_capacity_in(32, env.arena);
|
||||||
module.export.append(Export {
|
for ex in platform_and_builtins_exports.into_iter() {
|
||||||
name: STACK_POINTER_NAME.as_bytes(),
|
match ex.ty {
|
||||||
ty: ExportType::Global,
|
ExportType::Func => module.names.append_function(ex.index, ex.name),
|
||||||
index: STACK_POINTER_GLOBAL_ID,
|
_ => app_exports.push(ex),
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The preloaded binary has a global to tell us where its data section ends
|
// The preloaded binary has a global to tell us where its data section ends
|
||||||
// Note: We need this to account for zero data (.bss), which doesn't have an explicit DataSegment!
|
// Note: We need this to account for zero data (.bss), which doesn't have an explicit DataSegment!
|
||||||
let data_end_idx = module.export.globals_lookup["__data_end".as_bytes()];
|
let data_end_name = "__data_end".as_bytes();
|
||||||
|
let data_end_idx = app_exports
|
||||||
|
.iter()
|
||||||
|
.find(|ex| ex.name == data_end_name)
|
||||||
|
.map(|ex| ex.index)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
internal_error!("Preloaded Wasm binary must export global constant `__data_end`")
|
||||||
|
});
|
||||||
let next_constant_addr = module.global.parse_u32_at_index(data_end_idx);
|
let next_constant_addr = module.global.parse_u32_at_index(data_end_idx);
|
||||||
|
|
||||||
|
module.export.exports = app_exports;
|
||||||
|
|
||||||
WasmBackend {
|
WasmBackend {
|
||||||
env,
|
env,
|
||||||
interns,
|
interns,
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub mod opcodes;
|
||||||
pub mod sections;
|
pub mod sections;
|
||||||
pub mod serialize;
|
pub mod serialize;
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::{collections::Vec, Bump};
|
||||||
pub use code_builder::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState};
|
pub use code_builder::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState};
|
||||||
pub use linking::SymInfo;
|
pub use linking::SymInfo;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
|
@ -144,7 +144,7 @@ impl<'a> WasmModule<'a> {
|
||||||
|
|
||||||
let global = GlobalSection::preload(arena, bytes, &mut cursor);
|
let global = GlobalSection::preload(arena, bytes, &mut cursor);
|
||||||
|
|
||||||
let export = ExportSection::preload_globals(arena, bytes, &mut cursor);
|
let export = ExportSection::preload(arena, bytes, &mut cursor);
|
||||||
|
|
||||||
let start = OpaqueSection::preload(SectionId::Start, arena, bytes, &mut cursor);
|
let start = OpaqueSection::preload(SectionId::Start, arena, bytes, &mut cursor);
|
||||||
|
|
||||||
|
@ -190,10 +190,18 @@ impl<'a> WasmModule<'a> {
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
called_preload_fns: T,
|
called_preload_fns: T,
|
||||||
) {
|
) {
|
||||||
|
let exported_fn_iter = self
|
||||||
|
.export
|
||||||
|
.exports
|
||||||
|
.iter()
|
||||||
|
.filter(|ex| ex.ty == ExportType::Func)
|
||||||
|
.map(|ex| ex.index);
|
||||||
|
let function_indices = Vec::from_iter_in(exported_fn_iter, arena);
|
||||||
|
|
||||||
self.code.remove_dead_preloads(
|
self.code.remove_dead_preloads(
|
||||||
arena,
|
arena,
|
||||||
self.import.function_count,
|
self.import.function_count,
|
||||||
&self.export.function_indices,
|
&function_indices,
|
||||||
called_preload_fns,
|
called_preload_fns,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -716,56 +716,35 @@ impl Serialize for Export<'_> {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ExportSection<'a> {
|
pub struct ExportSection<'a> {
|
||||||
pub count: u32,
|
pub exports: Vec<'a, Export<'a>>,
|
||||||
pub bytes: Vec<'a, u8>,
|
|
||||||
/// List of exported functions to keep during dead-code-elimination
|
|
||||||
pub function_indices: Vec<'a, u32>,
|
|
||||||
/// name -> index
|
|
||||||
pub globals_lookup: MutMap<&'a [u8], u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExportSection<'a> {
|
impl<'a> ExportSection<'a> {
|
||||||
const ID: SectionId = SectionId::Export;
|
const ID: SectionId = SectionId::Export;
|
||||||
|
|
||||||
pub fn append(&mut self, export: Export) {
|
pub fn append(&mut self, export: Export<'a>) {
|
||||||
export.serialize(&mut self.bytes);
|
self.exports.push(export);
|
||||||
self.count += 1;
|
|
||||||
if matches!(export.ty, ExportType::Func) {
|
|
||||||
self.function_indices.push(export.index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
pub fn size(&self) -> usize {
|
||||||
section_size(&self.bytes)
|
self.exports
|
||||||
|
.iter()
|
||||||
|
.map(|ex| ex.name.len() + 1 + MAX_SIZE_ENCODED_U32)
|
||||||
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty(arena: &'a Bump) -> Self {
|
/// Preload from object file.
|
||||||
ExportSection {
|
pub fn preload(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self {
|
||||||
count: 0,
|
|
||||||
bytes: Vec::with_capacity_in(256, arena),
|
|
||||||
function_indices: Vec::with_capacity_in(4, arena),
|
|
||||||
globals_lookup: MutMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Preload from object file. Keep only the Global exports, ignore the rest.
|
|
||||||
pub fn preload_globals(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self {
|
|
||||||
let (num_exports, body_bytes) = parse_section(Self::ID, module_bytes, cursor);
|
let (num_exports, body_bytes) = parse_section(Self::ID, module_bytes, cursor);
|
||||||
|
|
||||||
let mut export_section = ExportSection::empty(arena);
|
let mut export_section = ExportSection {
|
||||||
|
exports: Vec::with_capacity_in(num_exports as usize, arena),
|
||||||
|
};
|
||||||
|
|
||||||
let mut body_cursor = 0;
|
let mut body_cursor = 0;
|
||||||
for _ in 0..num_exports {
|
while body_cursor < body_bytes.len() {
|
||||||
let export_start = body_cursor;
|
|
||||||
let export = Export::parse(arena, body_bytes, &mut body_cursor);
|
let export = Export::parse(arena, body_bytes, &mut body_cursor);
|
||||||
if matches!(export.ty, ExportType::Global) {
|
export_section.exports.push(export);
|
||||||
let global_bytes = &body_bytes[export_start..body_cursor];
|
|
||||||
export_section.bytes.extend_from_slice(global_bytes);
|
|
||||||
export_section.count += 1;
|
|
||||||
export_section
|
|
||||||
.globals_lookup
|
|
||||||
.insert(export.name, export.index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export_section
|
export_section
|
||||||
|
@ -774,10 +753,9 @@ impl<'a> ExportSection<'a> {
|
||||||
|
|
||||||
impl<'a> Serialize for ExportSection<'a> {
|
impl<'a> Serialize for ExportSection<'a> {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
if !self.bytes.is_empty() {
|
if !self.exports.is_empty() {
|
||||||
let header_indices = write_section_header(buffer, Self::ID);
|
let header_indices = write_section_header(buffer, Self::ID);
|
||||||
buffer.encode_u32(self.count);
|
self.exports.serialize(buffer);
|
||||||
buffer.append_slice(&self.bytes);
|
|
||||||
update_section_size(buffer, header_indices);
|
update_section_size(buffer, header_indices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1155,8 +1133,12 @@ impl<'a> NameSection<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self {
|
pub fn parse(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self {
|
||||||
|
// If we're already past the end of the preloaded file then there is no Name section
|
||||||
if *cursor >= module_bytes.len() {
|
if *cursor >= module_bytes.len() {
|
||||||
internal_error!("NameSection not found in preloaded object file");
|
return NameSection {
|
||||||
|
bytes: bumpalo::vec![in arena],
|
||||||
|
functions: MutMap::default(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom section ID
|
// Custom section ID
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue