mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Merge pull request #2268 from rtfeldman/update_zig_09
Update to Zig 0.9 + LLVM 13
This commit is contained in:
commit
5d3b83f5a6
43 changed files with 1037 additions and 555 deletions
|
@ -21,12 +21,11 @@ use crate::storage::{Storage, StoredValue, StoredValueKind};
|
|||
use crate::wasm_module::linking::{DataSymbol, LinkingSegment, WasmObjectSymbol};
|
||||
use crate::wasm_module::sections::{DataMode, DataSegment, Limits};
|
||||
use crate::wasm_module::{
|
||||
code_builder, CodeBuilder, Export, ExportType, LocalId, Signature, SymInfo, ValueType,
|
||||
WasmModule,
|
||||
code_builder, CodeBuilder, ExportType, LocalId, Signature, SymInfo, ValueType, WasmModule,
|
||||
};
|
||||
use crate::{
|
||||
copy_memory, round_up_to_alignment, CopyMemoryConfig, Env, DEBUG_LOG_SETTINGS, MEMORY_NAME,
|
||||
PTR_SIZE, PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, TARGET_INFO,
|
||||
copy_memory, round_up_to_alignment, CopyMemoryConfig, Env, DEBUG_LOG_SETTINGS, PTR_SIZE,
|
||||
PTR_TYPE, TARGET_INFO,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -77,22 +76,32 @@ impl<'a> WasmBackend<'a> {
|
|||
fn_index_offset: u32,
|
||||
helper_proc_gen: CodeGenHelp<'a>,
|
||||
) -> Self {
|
||||
module.export.append(Export {
|
||||
name: MEMORY_NAME.as_bytes(),
|
||||
ty: ExportType::Mem,
|
||||
index: 0,
|
||||
});
|
||||
module.export.append(Export {
|
||||
name: STACK_POINTER_NAME.as_bytes(),
|
||||
ty: ExportType::Global,
|
||||
index: STACK_POINTER_GLOBAL_ID,
|
||||
});
|
||||
// The preloaded builtins object file exports all functions, but the final app binary doesn't.
|
||||
// Remove the function exports and use them to populate the Name section (debug info)
|
||||
let platform_and_builtins_exports =
|
||||
std::mem::replace(&mut module.export.exports, bumpalo::vec![in env.arena]);
|
||||
let mut app_exports = Vec::with_capacity_in(32, env.arena);
|
||||
for ex in platform_and_builtins_exports.into_iter() {
|
||||
match ex.ty {
|
||||
ExportType::Func => module.names.append_function(ex.index, ex.name),
|
||||
_ => app_exports.push(ex),
|
||||
}
|
||||
}
|
||||
|
||||
// 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!
|
||||
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);
|
||||
|
||||
module.export.exports = app_exports;
|
||||
|
||||
WasmBackend {
|
||||
env,
|
||||
interns,
|
||||
|
@ -224,15 +233,19 @@ impl<'a> WasmBackend<'a> {
|
|||
}
|
||||
|
||||
fn start_proc(&mut self, proc: &Proc<'a>) {
|
||||
use ReturnMethod::*;
|
||||
let ret_layout = WasmLayout::new(&proc.ret_layout);
|
||||
|
||||
let ret_type = match ret_layout.return_method() {
|
||||
ReturnMethod::Primitive(ty, _) => Some(ty),
|
||||
ReturnMethod::NoReturnValue => None,
|
||||
ReturnMethod::WriteToPointerArg => {
|
||||
let ret_type = match ret_layout.return_method(CallConv::C) {
|
||||
Primitive(ty, _) => Some(ty),
|
||||
NoReturnValue => None,
|
||||
WriteToPointerArg => {
|
||||
self.storage.arg_types.push(PTR_TYPE);
|
||||
None
|
||||
}
|
||||
ZigPackedStruct => {
|
||||
internal_error!("C calling convention does not return Zig packed structs")
|
||||
}
|
||||
};
|
||||
|
||||
// Create a block so we can exit the function without skipping stack frame "pop" code.
|
||||
|
@ -323,7 +336,7 @@ impl<'a> WasmBackend<'a> {
|
|||
};
|
||||
|
||||
let mut n_inner_wasm_args = 0;
|
||||
let ret_type_and_size = match inner_ret_layout.return_method() {
|
||||
let ret_type_and_size = match inner_ret_layout.return_method(CallConv::C) {
|
||||
ReturnMethod::NoReturnValue => None,
|
||||
ReturnMethod::Primitive(ty, size) => {
|
||||
// If the inner function returns a primitive, load the address to store it at
|
||||
|
@ -337,6 +350,7 @@ impl<'a> WasmBackend<'a> {
|
|||
n_inner_wasm_args += 1;
|
||||
None
|
||||
}
|
||||
x => internal_error!("A Roc function should never use ReturnMethod {:?}", x),
|
||||
};
|
||||
|
||||
// Load all the arguments for the inner function
|
||||
|
@ -1020,14 +1034,16 @@ impl<'a> WasmBackend<'a> {
|
|||
return self.expr_call_low_level(lowlevel, arguments, ret_sym, ret_layout, ret_storage);
|
||||
}
|
||||
|
||||
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
||||
self.env.arena,
|
||||
&mut self.code_builder,
|
||||
arguments,
|
||||
ret_sym,
|
||||
&wasm_layout,
|
||||
CallConv::C,
|
||||
);
|
||||
let (num_wasm_args, has_return_val, ret_zig_packed_struct) =
|
||||
self.storage.load_symbols_for_call(
|
||||
self.env.arena,
|
||||
&mut self.code_builder,
|
||||
arguments,
|
||||
ret_sym,
|
||||
&wasm_layout,
|
||||
CallConv::C,
|
||||
);
|
||||
debug_assert!(!ret_zig_packed_struct);
|
||||
|
||||
for (roc_proc_index, lookup) in self.proc_lookup.iter().enumerate() {
|
||||
let ProcLookupData {
|
||||
|
@ -1038,8 +1054,6 @@ impl<'a> WasmBackend<'a> {
|
|||
} = lookup;
|
||||
if *ir_sym == func_sym && pl == proc_layout {
|
||||
let wasm_fn_index = self.fn_index_offset + roc_proc_index as u32;
|
||||
let num_wasm_args = param_types.len();
|
||||
let has_return_val = ret_type.is_some();
|
||||
self.code_builder.call(
|
||||
wasm_fn_index,
|
||||
*linker_sym_index,
|
||||
|
|
|
@ -4,9 +4,6 @@ use roc_mono::layout::{Layout, UnionLayout};
|
|||
use crate::wasm_module::ValueType;
|
||||
use crate::{PTR_SIZE, PTR_TYPE, TARGET_INFO};
|
||||
|
||||
/// Manually keep up to date with the Zig version we are using for builtins
|
||||
pub const BUILTINS_ZIG_VERSION: ZigVersion = ZigVersion::Zig8;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ReturnMethod {
|
||||
/// This layout is returned from a Wasm function "normally" as a Primitive
|
||||
|
@ -15,6 +12,8 @@ pub enum ReturnMethod {
|
|||
WriteToPointerArg,
|
||||
/// This layout is empty and requires no return value or argument (e.g. refcount helpers)
|
||||
NoReturnValue,
|
||||
/// This layout is returned as a packed struct in an integer. Only used by Zig, not C.
|
||||
ZigPackedStruct,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -124,33 +123,23 @@ impl WasmLayout {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn return_method(&self) -> ReturnMethod {
|
||||
pub fn return_method(&self, conv: CallConv) -> ReturnMethod {
|
||||
match self {
|
||||
Self::Primitive(ty, size) => ReturnMethod::Primitive(*ty, *size),
|
||||
Self::StackMemory { size, .. } => {
|
||||
if *size == 0 {
|
||||
ReturnMethod::NoReturnValue
|
||||
} else {
|
||||
ReturnMethod::WriteToPointerArg
|
||||
}
|
||||
Self::StackMemory { size, format, .. } => {
|
||||
conv.stack_memory_return_method(*size, *format)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum ZigVersion {
|
||||
Zig8,
|
||||
Zig9,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CallConv {
|
||||
/// The C calling convention, as defined here:
|
||||
/// https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
|
||||
C,
|
||||
/// The calling convention that Zig 0.8 or 0.9 generates for Wasm when we *ask* it
|
||||
/// for the .C calling convention, due to bugs in both versions of the Zig compiler.
|
||||
/// The calling convention that Zig 0.9 generates for Wasm when we *ask* it
|
||||
/// for the .C calling convention, due to bugs in the Zig compiler.
|
||||
Zig,
|
||||
}
|
||||
|
||||
|
@ -182,7 +171,7 @@ impl CallConv {
|
|||
&[I32] // Small struct: pass by value
|
||||
} else if size <= 8 {
|
||||
&[I64] // Small struct: pass by value
|
||||
} else if size <= 12 && BUILTINS_ZIG_VERSION == ZigVersion::Zig9 {
|
||||
} else if size <= 12 {
|
||||
&[I64, I32] // Medium struct: pass by value, as two Wasm arguments
|
||||
} else if size <= 16 {
|
||||
&[I64, I64] // Medium struct: pass by value, as two Wasm arguments
|
||||
|
@ -194,4 +183,30 @@ impl CallConv {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stack_memory_return_method(&self, size: u32, format: StackMemoryFormat) -> ReturnMethod {
|
||||
use ReturnMethod::*;
|
||||
use StackMemoryFormat::*;
|
||||
|
||||
match format {
|
||||
Int128 | Float128 | Decimal => WriteToPointerArg,
|
||||
|
||||
DataStructure => {
|
||||
if size == 0 {
|
||||
return NoReturnValue;
|
||||
}
|
||||
match self {
|
||||
CallConv::C => WriteToPointerArg,
|
||||
|
||||
CallConv::Zig => {
|
||||
if size <= 8 {
|
||||
ZigPackedStruct
|
||||
} else {
|
||||
WriteToPointerArg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
/// For numerical ops, this just pushes the arguments to the Wasm VM's value stack
|
||||
/// It implements the calling convention used by Zig for both numbers and structs
|
||||
/// Result is the type signature of the call
|
||||
fn load_args(&self, backend: &mut WasmBackend<'a>) -> (Vec<'a, ValueType>, Option<ValueType>) {
|
||||
fn load_args(&self, backend: &mut WasmBackend<'a>) -> (usize, bool, bool) {
|
||||
backend.storage.load_symbols_for_call(
|
||||
backend.env.arena,
|
||||
&mut backend.code_builder,
|
||||
|
@ -148,8 +148,29 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
|
||||
fn load_args_and_call_zig(&self, backend: &mut WasmBackend<'a>, name: &'a str) {
|
||||
let (param_types, ret_type) = self.load_args(backend);
|
||||
backend.call_zig_builtin_after_loading_args(name, param_types.len(), ret_type.is_some());
|
||||
let (num_wasm_args, has_return_val, ret_zig_packed_struct) = self.load_args(backend);
|
||||
backend.call_zig_builtin_after_loading_args(name, num_wasm_args, has_return_val);
|
||||
|
||||
if ret_zig_packed_struct {
|
||||
match self.ret_storage {
|
||||
StoredValue::StackMemory {
|
||||
size,
|
||||
alignment_bytes,
|
||||
..
|
||||
} => {
|
||||
// The address of the return value was already loaded before the call
|
||||
let align = Align::from(alignment_bytes);
|
||||
if size > 4 {
|
||||
backend.code_builder.i64_store(align, 0);
|
||||
} else {
|
||||
backend.code_builder.i32_store(align, 0);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
internal_error!("Zig packed struct should always be stored to StackMemory")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap an integer whose Wasm representation is i32
|
||||
|
|
|
@ -6,9 +6,7 @@ use roc_error_macros::internal_error;
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::Layout;
|
||||
|
||||
use crate::layout::{
|
||||
CallConv, ReturnMethod, StackMemoryFormat, WasmLayout, ZigVersion, BUILTINS_ZIG_VERSION,
|
||||
};
|
||||
use crate::layout::{CallConv, ReturnMethod, StackMemoryFormat, WasmLayout};
|
||||
use crate::wasm_module::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState};
|
||||
use crate::{copy_memory, round_up_to_alignment, CopyMemoryConfig, PTR_TYPE};
|
||||
|
||||
|
@ -327,7 +325,7 @@ impl<'a> Storage<'a> {
|
|||
code_builder.i32_load(align, offset);
|
||||
} else if *size <= 8 {
|
||||
code_builder.i64_load(align, offset);
|
||||
} else if *size <= 12 && BUILTINS_ZIG_VERSION == ZigVersion::Zig9 {
|
||||
} else if *size <= 12 {
|
||||
code_builder.i64_load(align, offset);
|
||||
code_builder.get_local(local_id);
|
||||
code_builder.i32_load(align, offset + 8);
|
||||
|
@ -397,37 +395,47 @@ impl<'a> Storage<'a> {
|
|||
return_symbol: Symbol,
|
||||
return_layout: &WasmLayout,
|
||||
call_conv: CallConv,
|
||||
) -> (Vec<'a, ValueType>, Option<ValueType>) {
|
||||
let mut wasm_arg_types = Vec::with_capacity_in(arguments.len() * 2 + 1, arena);
|
||||
let mut wasm_args = Vec::with_capacity_in(arguments.len() * 2 + 1, arena);
|
||||
) -> (usize, bool, bool) {
|
||||
use ReturnMethod::*;
|
||||
|
||||
let return_method = return_layout.return_method();
|
||||
let return_type = match return_method {
|
||||
ReturnMethod::Primitive(ty, _) => Some(ty),
|
||||
ReturnMethod::NoReturnValue => None,
|
||||
ReturnMethod::WriteToPointerArg => {
|
||||
wasm_arg_types.push(PTR_TYPE);
|
||||
wasm_args.push(return_symbol);
|
||||
None
|
||||
let mut num_wasm_args = 0;
|
||||
let mut symbols_to_load = Vec::with_capacity_in(arguments.len() * 2 + 1, arena);
|
||||
|
||||
let return_method = return_layout.return_method(call_conv);
|
||||
let has_return_val = match return_method {
|
||||
Primitive(..) => true,
|
||||
NoReturnValue => false,
|
||||
WriteToPointerArg => {
|
||||
num_wasm_args += 1;
|
||||
symbols_to_load.push(return_symbol);
|
||||
false
|
||||
}
|
||||
ZigPackedStruct => {
|
||||
// Workaround for Zig's incorrect implementation of the C calling convention.
|
||||
// We need to copy the packed struct into the stack frame
|
||||
// Load the address before the call so that afterward, it will be 2nd on the value stack,
|
||||
// ready for the store instruction.
|
||||
symbols_to_load.push(return_symbol);
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
for arg in arguments {
|
||||
let stored = self.symbol_storage_map.get(arg).unwrap();
|
||||
let arg_types = stored.arg_types(call_conv);
|
||||
wasm_arg_types.extend_from_slice(arg_types);
|
||||
num_wasm_args += arg_types.len();
|
||||
match arg_types.len() {
|
||||
0 => {}
|
||||
1 => wasm_args.push(*arg),
|
||||
2 => wasm_args.extend_from_slice(&[*arg, *arg]),
|
||||
1 => symbols_to_load.push(*arg),
|
||||
2 => symbols_to_load.extend_from_slice(&[*arg, *arg]),
|
||||
n => internal_error!("Cannot have {} Wasm arguments for 1 Roc argument", n),
|
||||
}
|
||||
}
|
||||
|
||||
// If the symbols were already at the top of the stack, do nothing!
|
||||
// Should be common for simple cases, due to the structure of the Mono IR
|
||||
if !code_builder.verify_stack_match(&wasm_args) {
|
||||
if return_method == ReturnMethod::WriteToPointerArg {
|
||||
if !code_builder.verify_stack_match(&symbols_to_load) {
|
||||
if matches!(return_method, WriteToPointerArg | ZigPackedStruct) {
|
||||
self.load_return_address_ccc(code_builder, return_symbol);
|
||||
};
|
||||
|
||||
|
@ -439,7 +447,11 @@ impl<'a> Storage<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
(wasm_arg_types, return_type)
|
||||
(
|
||||
num_wasm_args,
|
||||
has_return_val,
|
||||
return_method == ZigPackedStruct,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate code to copy a StoredValue to an arbitrary memory location
|
||||
|
|
|
@ -5,7 +5,7 @@ pub mod opcodes;
|
|||
pub mod sections;
|
||||
pub mod serialize;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use bumpalo::{collections::Vec, Bump};
|
||||
pub use code_builder::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState};
|
||||
pub use linking::SymInfo;
|
||||
use roc_error_macros::internal_error;
|
||||
|
@ -145,7 +145,7 @@ impl<'a> WasmModule<'a> {
|
|||
|
||||
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);
|
||||
|
||||
|
@ -191,10 +191,18 @@ impl<'a> WasmModule<'a> {
|
|||
arena: &'a Bump,
|
||||
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(
|
||||
arena,
|
||||
self.import.function_count,
|
||||
&self.export.function_indices,
|
||||
&function_indices,
|
||||
called_preload_fns,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ fn section_size(bytes: &[u8]) -> usize {
|
|||
}
|
||||
|
||||
fn parse_section<'a>(id: SectionId, module_bytes: &'a [u8], cursor: &mut usize) -> (u32, &'a [u8]) {
|
||||
if module_bytes[*cursor] != id as u8 {
|
||||
if (*cursor >= module_bytes.len()) || (module_bytes[*cursor] != id as u8) {
|
||||
return (0, &[]);
|
||||
}
|
||||
*cursor += 1;
|
||||
|
@ -809,56 +809,35 @@ impl Serialize for Export<'_> {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct ExportSection<'a> {
|
||||
pub count: u32,
|
||||
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>,
|
||||
pub exports: Vec<'a, Export<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> ExportSection<'a> {
|
||||
const ID: SectionId = SectionId::Export;
|
||||
|
||||
pub fn append(&mut self, export: Export) {
|
||||
export.serialize(&mut self.bytes);
|
||||
self.count += 1;
|
||||
if matches!(export.ty, ExportType::Func) {
|
||||
self.function_indices.push(export.index);
|
||||
}
|
||||
pub fn append(&mut self, export: Export<'a>) {
|
||||
self.exports.push(export);
|
||||
}
|
||||
|
||||
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 {
|
||||
ExportSection {
|
||||
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 {
|
||||
/// Preload from object file.
|
||||
pub fn preload(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self {
|
||||
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;
|
||||
for _ in 0..num_exports {
|
||||
let export_start = body_cursor;
|
||||
while body_cursor < body_bytes.len() {
|
||||
let export = Export::parse(arena, body_bytes, &mut body_cursor);
|
||||
if matches!(export.ty, ExportType::Global) {
|
||||
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.exports.push(export);
|
||||
}
|
||||
|
||||
export_section
|
||||
|
@ -867,10 +846,9 @@ impl<'a> ExportSection<'a> {
|
|||
|
||||
impl<'a> Serialize for ExportSection<'a> {
|
||||
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);
|
||||
buffer.encode_u32(self.count);
|
||||
buffer.append_slice(&self.bytes);
|
||||
self.exports.serialize(buffer);
|
||||
update_section_size(buffer, header_indices);
|
||||
}
|
||||
}
|
||||
|
@ -1291,6 +1269,14 @@ impl<'a> NameSection<'a> {
|
|||
}
|
||||
|
||||
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() {
|
||||
return NameSection {
|
||||
bytes: bumpalo::vec![in arena],
|
||||
functions: MutMap::default(),
|
||||
};
|
||||
}
|
||||
|
||||
// Custom section ID
|
||||
let section_id_byte = module_bytes[*cursor];
|
||||
if section_id_byte != Self::ID as u8 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue