diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index 726fc62268..5e29ced9eb 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -422,8 +422,21 @@ pub struct EncodingState<'a> { /// Maps from original export name to task initialization wrapper function index. /// Used to wrap exports with __wasm_init_(async_)task calls. export_task_initialization_wrappers: HashMap, + + /// The index of the instance of the synthesized module which stores the TLS + /// base pointer in a `global`. + /// + /// This is only used, and only created, when a module imports + /// `__wasm_{get,set}_tls_base` but the program doesn't use cooperative + /// threading. See `materialize_tls_base_import`. + tls_base_instance_index: Option<(u32, ValType)>, } +/// Name of the export of the synthesized TLS-base module which reads the base. +const TLS_BASE_GET: &str = "get"; +/// Name of the export of the synthesized TLS-base module which writes the base. +const TLS_BASE_SET: &str = "set"; + impl<'a> EncodingState<'a> { fn encode_core_modules(&mut self) { assert!(self.module_index.is_none()); @@ -1991,6 +2004,14 @@ impl<'a> EncodingState<'a> { let index = self.component.context_set((*ty).try_into()?, *slot); Ok((ExportKind::Func, index)) } + Import::TlsBaseGet { ty } => Ok(( + ExportKind::Func, + self.materialize_tls_base_import(false, (*ty).try_into()?), + )), + Import::TlsBaseSet { ty } => Ok(( + ExportKind::Func, + self.materialize_tls_base_import(true, (*ty).try_into()?), + )), Import::ExportedTaskCancel => { let index = self.component.task_cancel(); Ok((ExportKind::Func, index)) @@ -2037,6 +2058,93 @@ impl<'a> EncodingState<'a> { } } + /// Helper to satisfy `__wasm_{get,set}_tls_base` imports. + /// + /// For more information on this see WebAssembly/wasi-libc#857 + fn materialize_tls_base_import(&mut self, set: bool, ty: ValType) -> u32 { + if self.info.uses_cooperative_threading() { + return if set { + self.component.context_set(ty, 1) + } else { + self.component.context_get(ty, 1) + }; + } + + let instance = match self.tls_base_instance_index { + Some((index, prev_ty)) => { + assert_eq!(prev_ty, ty, "conflicting TLS base pointer types"); + index + } + None => { + let index = self.encode_tls_base_module(ty); + self.tls_base_instance_index = Some((index, ty)); + index + } + }; + let name = if set { TLS_BASE_SET } else { TLS_BASE_GET }; + self.core_alias_export( + Some(&format!("tls-base-{name}")), + instance, + name, + ExportKind::Func, + ) + } + + /// Synthesizes and instantiates a module which stores the TLS base pointer + /// in a mutable `global`, exporting accessors for it. + fn encode_tls_base_module(&mut self, ty: ValType) -> u32 { + let mut types = TypeSection::new(); + types.ty().function([], [ty]); + types.ty().function([ty], []); + + let mut globals = GlobalSection::new(); + globals.global( + wasm_encoder::GlobalType { + val_type: ty, + mutable: true, + shared: false, + }, + &match ty { + ValType::I64 => ConstExpr::i64_const(0), + ValType::I32 => ConstExpr::i32_const(0), + _ => unreachable!(), + }, + ); + + let mut functions = FunctionSection::new(); + let mut code = CodeSection::new(); + + functions.function(0); + let mut get = wasm_encoder::Function::new([]); + get.instruction(&Instruction::GlobalGet(0)); + get.instruction(&Instruction::End); + code.function(&get); + + functions.function(1); + let mut set = wasm_encoder::Function::new([]); + set.instruction(&Instruction::LocalGet(0)); + set.instruction(&Instruction::GlobalSet(0)); + set.instruction(&Instruction::End); + code.function(&set); + + let mut exports = ExportSection::new(); + exports.export(TLS_BASE_GET, ExportKind::Func, 0); + exports.export(TLS_BASE_SET, ExportKind::Func, 1); + + let mut module = Module::new(); + module.section(&types); + module.section(&functions); + module.section(&globals); + module.section(&exports); + module.section(&code); + + let module_index = self + .component + .core_module(Some("wit-component:tls-base"), &module); + self.component + .core_instantiate(Some("wit-component:tls-base"), module_index, []) + } + /// Helper for `materialize_import` above for materializing functions that /// are part of the "shim module" generated. fn materialize_shim_import(&mut self, shims: &Shims<'_>, kind: &ShimKind) -> (ExportKind, u32) { @@ -2686,6 +2794,8 @@ impl<'a> Shims<'a> { | Import::WaitableJoin | Import::ContextGet { .. } | Import::ContextSet { .. } + | Import::TlsBaseGet { .. } + | Import::TlsBaseSet { .. } | Import::ThreadIndex | Import::ThreadResumeLater | Import::ThreadSuspend { .. } @@ -3411,6 +3521,7 @@ impl ComponentEncoder { aliased_core_items: Default::default(), info: &world, export_task_initialization_wrappers: HashMap::new(), + tls_base_instance_index: None, }; state.encode_imports(&self.import_name_map)?; state.encode_core_modules(); diff --git a/crates/wit-component/src/encoding/world.rs b/crates/wit-component/src/encoding/world.rs index dfb56b2795..f5c6d32e69 100644 --- a/crates/wit-component/src/encoding/world.rs +++ b/crates/wit-component/src/encoding/world.rs @@ -85,6 +85,21 @@ impl<'a> ComponentWorld<'a> { Ok(ret) } + /// Returns whether any module in this component may spawn a thread, and + /// thus whether the program is using cooperative threading. + /// + /// This is a heuristic which should go away once component-model-threading + /// has been stable for awhile and the return value of this function should + /// be const-propagated as `true`. + pub fn uses_cooperative_threading(&self) -> bool { + let uses = |info: &ValidatedModule| { + info.imports + .imports() + .any(|(_, _, import)| matches!(import, Import::ThreadNewIndirect)) + }; + uses(&self.info) || self.adapters.values().any(|a| uses(&a.info)) + } + /// Process adapters which are required here. Iterate over all /// adapters and figure out what functions are required from the /// adapter itself, either because the functions are imported by the @@ -432,6 +447,8 @@ impl<'a> ComponentWorld<'a> { | Import::Item(_) | Import::ContextGet { .. } | Import::ContextSet { .. } + | Import::TlsBaseGet { .. } + | Import::TlsBaseSet { .. } | Import::BackpressureInc | Import::BackpressureDec | Import::WaitableSetNew diff --git a/crates/wit-component/src/linking.rs b/crates/wit-component/src/linking.rs index 0077d3711f..dda280eef7 100644 --- a/crates/wit-component/src/linking.rs +++ b/crates/wit-component/src/linking.rs @@ -28,6 +28,7 @@ use { indexmap::{IndexMap, IndexSet, map::Entry}, metadata::{Export, ExportKey, FunctionType, GlobalType, Metadata, Type, ValueType}, std::{ + cmp, collections::{BTreeMap, HashMap, HashSet}, fmt::Debug, hash::Hash, @@ -35,9 +36,9 @@ use { }, wasm_encoder::{ CodeSection, ConstExpr, DataSection, ElementSection, Elements, EntityType, ExportKind, - ExportSection, Function, FunctionSection, GlobalSection, ImportSection, Instruction as Ins, - MemArg, MemorySection, MemoryType, Module, RawCustomSection, RefType, StartSection, - TableSection, TableType, TypeSection, ValType, + ExportSection, Function, FunctionSection, GlobalSection, ImportSection, MemArg, + MemorySection, MemoryType, Module, RawCustomSection, RefType, StartSection, TableSection, + TableType, TypeSection, ValType, }, wasmparser::SymbolFlags, }; @@ -183,6 +184,134 @@ impl<'a> DlOpenables<'a> { } } +/// The layout of the whole program's thread-local storage bookkeeping. +/// +/// This generates a C structure that matches this layout: +/// +/// ```c +/// struct { +/// size_t num_libraries; +/// struct { +/// size_t __tls_size; +/// size_t __tls_align; +/// void (*__wasm_init_tls)(void*); +/// } *library_info; +/// void **main_thread_tls_base; +/// } __wasm_program_tls_info; +/// ``` +/// +/// where `main_thread_tls_base` is placed first, then `library_info`, then this +/// structure itself. +#[derive(Default)] +struct TlsLayout { + /// Address of the `main_thread_tls_base` array. + /// + /// This is left zero-initialized; no data segment covers it. + main_thread_tls_base: u32, + + /// Address of the `library_info` array. + library_info: u32, + + /// Address of the `__wasm_program_tls_info` struct itself. + program_info: u32, + + /// The libraries which have thread-local storage, in the order they appear + /// in `library_info`, paired with the table index reserved for each one's + /// `__wasm_init_tls`. + init_tls_functions: Vec<(usize, u32)>, + + /// For each library, the slot it uses in the array of TLS base pointers, or + /// `None` if it has no thread-local storage of its own. + slots: Vec>, + + /// Static contents of the `library_info` array and the + /// `__wasm_program_tls_info` struct, which live contiguously starting at + /// `library_info`. + buffer: Vec, +} + +impl TlsLayout { + /// Reserve linear memory and table space for the layout described above, + /// advancing `memory_offset` and `table_offset` past what's used. + /// + /// Nothing is reserved for a program which doesn't use thread-local storage + /// at all, and the `__wasm_program_tls_info` half is skipped unless some + /// library actually asks for it, which is only the case when the program + /// might spawn a thread. + fn new(metadata: &[Metadata], memory_offset: &mut u32, table_offset: &mut u32) -> Self { + let needs_tls_base = metadata + .iter() + .any(|m| m.needs_get_tls_base || m.needs_set_tls_base); + let needs_program_info = metadata.iter().any(|m| m.needs_program_tls_info); + if !needs_tls_base && !needs_program_info { + return Self::default(); + } + + // Filter out libraries that don't have TLS, and then sort this by + // biggest alignment first to help minimize the size of TLS blocks + // allocated. + let mut libraries = metadata + .iter() + .enumerate() + .filter(|(_, metadata)| metadata.has_tls_info) + .map(|(index, _)| index) + .collect::>(); + libraries.sort_by_key(|&index| cmp::Reverse(metadata[index].tls_align)); + + let mut slots = vec![None; metadata.len()]; + for (slot, index) in libraries.iter().enumerate() { + slots[*index] = Some(u32::try_from(slot).unwrap()); + } + let count = u32::try_from(libraries.len()).unwrap(); + + // Allocate space for `main_thread_tls_base` + *memory_offset = align(*memory_offset, 4); + let main_thread_tls_base = *memory_offset; + *memory_offset += count * 4; + + let mut library_info = 0; + let mut program_info = 0; + let mut init_tls_functions = Vec::new(); + let mut buffer = Vec::new(); + if needs_program_info { + // Allocate space for `library_info` + library_info = *memory_offset; + *memory_offset += count * 12; + // Allocate space for `__wasm_program_tls_info` + program_info = *memory_offset; + *memory_offset += 12; + + init_tls_functions = libraries + .iter() + .map(|&index| (index, get_and_increment(table_offset))) + .collect::>(); + + for &(index, table_index) in &init_tls_functions { + write_u32(&mut buffer, metadata[index].tls_size); + write_u32(&mut buffer, metadata[index].tls_align); + write_u32(&mut buffer, table_index); + } + write_u32(&mut buffer, count); + write_u32(&mut buffer, library_info); + write_u32(&mut buffer, main_thread_tls_base); + } + + Self { + main_thread_tls_base, + library_info, + program_info, + init_tls_functions, + slots, + buffer, + } + } + + /// The slot library `index` uses in the array of TLS base pointers. + fn slot(&self, index: usize) -> Option { + self.slots.get(index).copied().flatten() + } +} + fn write_u32(buffer: &mut Vec, value: u32) { buffer.extend(value.to_le_bytes()); } @@ -286,7 +415,7 @@ fn make_env_module<'a>( env_exports: &[EnvExport<'_>], cabi_realloc_exporter: Option<&str>, stack_size_bytes: u32, -) -> (Vec, DlOpenables<'a>, u32) { +) -> (Vec, DlOpenables<'a>, TlsLayout, u32) { // TODO: deduplicate types let mut types = TypeSection::new(); let mut imports = ImportSection::new(); @@ -354,6 +483,25 @@ fn make_env_module<'a>( exports.export(CABI_REALLOC, ExportKind::Func, index); } + // If tls base shims are being generated, and something might spawn a + // thread, then the shims generated will need access to `context.get 1`. + let indirect_tls_base = metadata + .iter() + .any(|m| m.needs_get_tls_base || m.needs_set_tls_base) + && metadata.iter().any(|m| m.uses_thread_new_indirect); + let tls_context_get = if indirect_tls_base { + let index = get_and_increment(&mut function_count); + types.ty().function([], [ValType::I32]); + imports.import( + metadata::ROOT, + metadata::CONTEXT_GET_1, + EntityType::Function(index), + ); + Some(index) + } else { + None + }; + let mut add_global_export = |name: &str, value, mutable| { let index = globals.len(); globals.global( @@ -380,6 +528,12 @@ fn make_env_module<'a>( table_offset += dl_openables.function_count; memory_offset += u32::try_from(dl_openables.buffer.len()).unwrap(); + let tls = TlsLayout::new(metadata, &mut memory_offset, &mut table_offset); + + if metadata.iter().any(|m| m.needs_program_tls_info) { + add_global_export(metadata::PROGRAM_TLS_INFO, tls.program_info, true); + } + let memory_size = { if metadata.iter().any(|m| m.needs_stack_pointer) { add_global_export(metadata::STACK_POINTER, stack_size_bytes, true); @@ -486,20 +640,141 @@ fn make_env_module<'a>( functions.function(u32::try_from(index).unwrap()); let mut function = Function::new([]); for local in 0..export.ty.parameters.len() { - function.instruction(&Ins::LocalGet(u32::try_from(local).unwrap())); + function + .instructions() + .local_get(u32::try_from(local).unwrap()); } - function.instruction(&Ins::I32Const(i32::try_from(table_offset).unwrap())); - function.instruction(&Ins::CallIndirect { - type_index: u32::try_from(index).unwrap(), - table_index: 0, - }); - function.instruction(&Ins::End); + function + .instructions() + .i32_const(i32::try_from(table_offset).unwrap()) + .call_indirect(0, u32::try_from(index).unwrap()) + .end(); code.function(&function); exports.export(export.name, ExportKind::Func, index); table_offset += 1; } + // Define a distinct `__wasm_{get,set}_tls_base` pair for each library that + // needs one. Each pair reads and writes that library's slot of the array of + // pointers described by `TlsLayout`. + for (index, metadata) in metadata.iter().enumerate() { + // A library with no thread-local storage of its own has no slot in the + // array. If it still imports these then synthesize functions that trap + // since they shouldn't ever be called. + let Some(slot) = tls.slot(index) else { + for (needed, name, params, results) in [ + ( + metadata.needs_get_tls_base, + metadata::GET_TLS_BASE, + &[][..], + &[ValType::I32][..], + ), + ( + metadata.needs_set_tls_base, + metadata::SET_TLS_BASE, + &[ValType::I32][..], + &[][..], + ), + ] { + if !needed { + continue; + } + let func = get_and_increment(&mut function_count); + types + .ty() + .function(params.iter().copied(), results.iter().copied()); + functions.function(func); + let mut function = Function::new([]); + function.instructions().unreachable().end(); + code.function(&function); + exports.export(&format!("{}:{name}", metadata.name), ExportKind::Func, func); + } + continue; + }; + + let mem_arg = MemArg { + offset: u64::from(slot * 4), + align: 2, + memory_index: 0, + }; + + if metadata.needs_get_tls_base { + let func = get_and_increment(&mut function_count); + types.ty().function([], [ValType::I32]); + functions.function(func); + let mut function = Function::new([]); + // With coop threads the base pointer is in `context.get 1`. Without + // coop threads the base pointer is `main_thread_tls_base` itself. + match tls_context_get { + Some(get) => { + function.instructions().call(get); + } + None => { + function + .instructions() + .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap()); + } + } + function.instructions().i32_load(mem_arg).end(); + code.function(&function); + exports.export( + &format!("{}:{}", metadata.name, metadata::GET_TLS_BASE), + ExportKind::Func, + func, + ); + } + + if metadata.needs_set_tls_base { + let func = get_and_increment(&mut function_count); + types.ty().function([ValType::I32], []); + functions.function(func); + let mut function = Function::new_with_locals_types(if tls_context_get.is_some() { + vec![ValType::I32] + } else { + vec![] + }); + // With coop threads this intrinsic conditionally initializes + // `main_thread_tls_base` based on `context.get 1`. Otherwise it + // writes through to it if it's set. + // + // Without coop threads this is updating `main_thread_tls_base`. + match tls_context_get { + Some(get) => { + function + .instructions() + .call(get) + .local_tee(1) + .i32_eqz() + .if_(wasm_encoder::BlockType::Empty) + .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap()) + .local_get(0) + .i32_store(mem_arg) + .else_() + .local_get(1) + .local_get(0) + .i32_store(mem_arg) + .end() + .end(); + } + None => { + function + .instructions() + .i32_const(i32::try_from(tls.main_thread_tls_base).unwrap()) + .local_get(0) + .i32_store(mem_arg) + .end(); + } + } + code.function(&function); + exports.export( + &format!("{}:{}", metadata.name, metadata::SET_TLS_BASE), + ExportKind::Func, + func, + ); + } + } + for (import, offset) in import_map { exports.export( &format!("{}:{}", import.module, import.name), @@ -553,7 +828,7 @@ fn make_env_module<'a>( let module = module.finish(); wasmparser::validate(&module).unwrap(); - (module, dl_openables, indirection_table_base) + (module, dl_openables, tls, indirection_table_base) } /// Synthesize the "init" module, responsible for initializing global variables per the dynamic linking tool @@ -565,6 +840,7 @@ fn make_init_module( exporters: &IndexMap<&ExportKey, (&str, &Export)>, env_exports: &[EnvExport<'_>], dl_openables: DlOpenables, + tls: TlsLayout, indirection_table_base: u32, ) -> Result> { let mut module = Module::new(); @@ -574,6 +850,7 @@ fn make_init_module( types.ty().function([], []); let thunk_ty = 0; types.ty().function([ValType::I32], []); + let init_tls_ty = 1; let mut type_offset = 2; for metadata in metadata { @@ -650,52 +927,80 @@ fn make_init_module( }) }; - let mut memory_address_inits = Vec::new(); - let mut reloc_calls = Vec::new(); - let mut ctor_calls = Vec::new(); + let mut start = Function::new([]); + let mut names = HashMap::new(); + for (index, metadata) in metadata.iter().enumerate() { + names.insert_unique(index, metadata.name); + } for (exporter, export, address) in dl_openables.global_addresses.iter() { - memory_address_inits.push(Ins::I32Const(i32::try_from(*address).unwrap())); - memory_address_inits.push(Ins::GlobalGet(add_global_import( + let memory_base = add_global_import( &mut imports, metadata::ENV, &format!("{exporter}:memory_base"), false, - ))); - memory_address_inits.push(Ins::GlobalGet(add_global_import( - &mut imports, - exporter, - export, - false, - ))); - memory_address_inits.push(Ins::I32Add); - memory_address_inits.push(Ins::I32Store(MemArg { - offset: 0, - align: 2, - memory_index: 0, - })); + ); + let export = add_global_import(&mut imports, exporter, export, false); + start + .instructions() + .i32_const(i32::try_from(*address).unwrap()) + .global_get(memory_base) + .global_get(export) + .i32_add() + .i32_store(MemArg { + offset: 0, + align: 2, + memory_index: 0, + }); } - let mut init_task_exporter = exporters - .get(&ExportKey { - name: metadata::INIT_TASK, - ty: Type::Function(EMPTY_FUNCTION_TYPE.clone()), - }) - .map(|(name, _)| name); + for metadata in metadata { + for import in &metadata.memory_address_imports { + let (exporter, _) = find_offset_exporter(import, exporters)?; - for (index, metadata) in metadata.iter().enumerate() { - names.insert_unique(index, metadata.name); + let memory_base = add_global_import( + &mut imports, + metadata::ENV, + &format!("{exporter}:memory_base"), + false, + ); + let offset = add_global_import(&mut imports, exporter, import, false); + let address = add_global_import( + &mut imports, + metadata::ENV, + &format!("{}:{import}", metadata.name), + true, + ); + start + .instructions() + .global_get(memory_base) + .global_get(offset) + .i32_add() + .global_set(address); + } + } + for metadata in metadata { if metadata.has_data_relocs { - reloc_calls.push(Ins::Call(add_function_import( + let func = add_function_import( &mut imports, metadata.name, metadata::APPLY_DATA_RELOCS, thunk_ty, - ))); + ); + start.instructions().call(func); } + } + + let mut init_task_exporter = exporters + .get(&ExportKey { + name: metadata::INIT_TASK, + ty: Type::Function(EMPTY_FUNCTION_TYPE.clone()), + }) + .map(|(name, _)| name); + for metadata in metadata { if metadata.has_ctors && metadata.has_initialize { bail!( "library {} exports both `{}` and `{}`; \ @@ -717,54 +1022,20 @@ fn make_init_module( // `init_task_exporter` to `None` to avoid calling it again: init_task_exporter = None; - ctor_calls.push(Ins::Call(add_function_import( - &mut imports, - exporter, - metadata::INIT_TASK, - thunk_ty, - ))); + let func = add_function_import(&mut imports, exporter, metadata::INIT_TASK, thunk_ty); + start.instructions().call(func); } if metadata.has_ctors { - ctor_calls.push(Ins::Call(add_function_import( - &mut imports, - metadata.name, - metadata::CALL_CTORS, - thunk_ty, - ))); + let func = + add_function_import(&mut imports, metadata.name, metadata::CALL_CTORS, thunk_ty); + start.instructions().call(func); } if metadata.has_initialize { - ctor_calls.push(Ins::Call(add_function_import( - &mut imports, - metadata.name, - metadata::INITIALIZE, - thunk_ty, - ))); - } - - for import in &metadata.memory_address_imports { - let (exporter, _) = find_offset_exporter(import, exporters)?; - - memory_address_inits.push(Ins::GlobalGet(add_global_import( - &mut imports, - metadata::ENV, - &format!("{exporter}:memory_base"), - false, - ))); - memory_address_inits.push(Ins::GlobalGet(add_global_import( - &mut imports, - exporter, - import, - false, - ))); - memory_address_inits.push(Ins::I32Add); - memory_address_inits.push(Ins::GlobalSet(add_global_import( - &mut imports, - metadata::ENV, - &format!("{}:{import}", metadata.name), - true, - ))); + let func = + add_function_import(&mut imports, metadata.name, metadata::INITIALIZE, thunk_ty); + start.instructions().call(func); } } @@ -796,6 +1067,22 @@ fn make_init_module( }) .collect::>(); + // Each library's `__wasm_init_tls` needs a table slot so that wasi-libc + // can call it through the function pointer in `library_info`. Everything + // else about the layout is known statically and lives in a data segment. + let init_tls_functions = tls + .init_tls_functions + .iter() + .map(|&(index, _)| { + add_function_import( + &mut imports, + metadata[index].name, + metadata::INIT_TLS, + init_tls_ty, + ) + }) + .collect::>(); + module.section(&imports); { @@ -820,26 +1107,28 @@ fn make_init_module( &const_u32(indirection_table_base), Elements::Functions(indirections.into()), ); + if let Some((_, table_base)) = tls.init_tls_functions.first() { + elements.active( + None, + &const_u32(*table_base), + Elements::Functions(init_tls_functions.into()), + ); + } module.section(&elements); } { let mut code = CodeSection::new(); - let mut function = Function::new([]); - for ins in memory_address_inits - .iter() - .chain(&reloc_calls) - .chain(&ctor_calls) - { - function.instruction(ins); - } - function.instruction(&Ins::End); - code.function(&function); + start.instructions().end(); + code.function(&start); module.section(&code); } let mut data = DataSection::new(); data.active(0, &const_u32(dl_openables.memory_base), dl_openables.buffer); + if !tls.buffer.is_empty() { + data.active(0, &const_u32(tls.library_info), tls.buffer); + } module.section(&data); module.section(&RawCustomSection( @@ -1313,8 +1602,7 @@ fn make_stubs_module(missing: &[(&str, Export)]) -> Vec { ); functions.function(offset); let mut function = Function::new([]); - function.instruction(&Ins::Unreachable); - function.instruction(&Ins::End); + function.instructions().unreachable().end(); code.function(&function); exports.export(name, ExportKind::Func, offset); } @@ -1591,7 +1879,7 @@ impl Linker { reexport_cabi_realloc, } = env_exports(&metadata, &exporters, &topo_sorted)?; - let (env_module, dl_openables, table_base) = make_env_module( + let (env_module, dl_openables, tls, table_base) = make_env_module( &metadata, &env_exports, if reexport_cabi_realloc { @@ -1665,6 +1953,20 @@ impl Linker { name: format!("{name}:table_base"), }, ]) + .chain( + [ + (metadata.needs_get_tls_base, metadata::GET_TLS_BASE), + (metadata.needs_set_tls_base, metadata::SET_TLS_BASE), + ] + .into_iter() + .filter(|(needed, _)| *needed) + .map(|(_, intrinsic)| Item { + alias: intrinsic.into(), + kind: ExportKind::Func, + which: MainOrAdapter::Main, + name: format!("{name}:{intrinsic}"), + }), + ) .chain(metadata.env_imports.iter().map(|(name, (ty, _))| { let (exporter, _) = find_function_exporter(name, ty, &exporters).unwrap(); @@ -1752,6 +2054,7 @@ impl Linker { metadata::STACK_HIGH, metadata::STACK_LOW, metadata::LIBDL_LIBRARIES, + metadata::PROGRAM_TLS_INFO, ] .into_iter() .map(|name| Item { @@ -1811,6 +2114,7 @@ impl Linker { &exporters, &env_exports, dl_openables, + tls, table_base, )?, LibraryInfo { diff --git a/crates/wit-component/src/linking/metadata.rs b/crates/wit-component/src/linking/metadata.rs index 4dcfcb6ae4..de687ba47a 100644 --- a/crates/wit-component/src/linking/metadata.rs +++ b/crates/wit-component/src/linking/metadata.rs @@ -8,8 +8,8 @@ use { fmt, }, wasmparser::{ - Dylink0Subsection, ExternalKind, FuncType, KnownCustom, MemInfo, Parser, Payload, RefType, - SymbolFlags, TableType, TagKind, TagType, TypeRef, ValType, + Dylink0Subsection, ExternalKind, FuncType, KnownCustom, MemInfo, Operator, Parser, Payload, + RefType, SymbolFlags, TableType, TagKind, TagType, TypeRef, ValType, }, }; @@ -35,6 +35,17 @@ pub const START: &str = "_start"; pub const LIBDL_LIBRARIES: &str = "__wasm_libdl_libraries"; pub const INIT_TASK: &str = "__wasm_init_task"; pub const INIT_ASYNC_TASK: &str = "__wasm_init_async_task"; +pub const ROOT: &str = "$root"; +pub const THREAD_NEW_INDIRECT: &str = "[thread-new-indirect-v0]"; +pub const CONTEXT_GET_1: &str = "[context-get-1]"; +pub const GET_STACK_POINTER: &str = "__wasm_get_stack_pointer"; +pub const SET_STACK_POINTER: &str = "__wasm_set_stack_pointer"; +pub const GET_TLS_BASE: &str = "__wasm_get_tls_base"; +pub const SET_TLS_BASE: &str = "__wasm_set_tls_base"; +pub const TLS_SIZE: &str = "__tls_size"; +pub const TLS_ALIGN: &str = "__tls_align"; +pub const INIT_TLS: &str = "__wasm_init_tls"; +pub const PROGRAM_TLS_INFO: &str = "__wasm_program_tls_info"; /// Represents a core Wasm value type (not including V128 or reference types, which are not yet supported) #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -241,6 +252,36 @@ pub struct Metadata<'a> { /// Whether this module imports `__stack_low` pub needs_stack_low: bool, + /// Whether this module imports `env::__wasm_get_tls_base` + pub needs_get_tls_base: bool, + + /// Whether this module imports `env::__wasm_set_tls_base` + pub needs_set_tls_base: bool, + + /// Whether this module imports the address of `__wasm_program_tls_info` + pub needs_program_tls_info: bool, + + /// Whether this module imports `$root::[thread-new-indirect-v0]`, meaning + /// the program may spawn threads and is thus using cooperative threading. + pub uses_thread_new_indirect: bool, + + /// Whether this module has thread-local storage of its own which needs + /// allocating and initializing on a freshly spawned thread. + /// + /// This requires the module to export all of `__tls_size`, `__tls_align`, + /// and `__wasm_init_tls`, and to have a non-zero `tls_size`. + pub has_tls_info: bool, + + /// The size of this module's thread-local storage, i.e. the value of the + /// `__tls_size` global that `wasm-ld` synthesized for it. + /// + /// Zero if the module has no thread-local storage at all. + pub tls_size: u32, + + /// The alignment of this module's thread-local storage, i.e. the value of + /// the `__tls_align` global. At least one whenever `has_tls_info` is set. + pub tls_align: u32, + /// The functions imported from the `env` module, if any pub env_imports: BTreeSet<(&'a str, (FunctionType, SymbolFlags))>, @@ -296,6 +337,13 @@ impl<'a> Metadata<'a> { needs_heap_end: false, needs_stack_high: false, needs_stack_low: false, + needs_get_tls_base: false, + needs_set_tls_base: false, + needs_program_tls_info: false, + uses_thread_new_indirect: false, + has_tls_info: false, + tls_size: 0, + tls_align: 0, env_imports: BTreeSet::new(), memory_address_imports: BTreeSet::new(), table_address_imports: BTreeSet::new(), @@ -306,6 +354,10 @@ impl<'a> Metadata<'a> { let mut types = Vec::new(); let mut function_types = Vec::new(); let mut global_types = Vec::new(); + let mut global_values = Vec::new(); + let mut tls_size = None; + let mut tls_align = None; + let mut has_init_tls = false; let mut tag_types = Vec::new(); let mut import_info = HashMap::new(); let mut export_info = HashMap::new(); @@ -353,7 +405,10 @@ impl<'a> Metadata<'a> { match import.ty { TypeRef::Func(ty) => function_types.push(usize::try_from(ty).unwrap()), - TypeRef::Global(ty) => global_types.push(ty), + TypeRef::Global(ty) => { + global_types.push(ty); + global_values.push(None); + } TypeRef::Tag(ty) => tag_types.push(ty), _ => (), } @@ -424,6 +479,22 @@ impl<'a> Metadata<'a> { return type_error(); } } + ( + self::ENV, + name @ (self::GET_STACK_POINTER + | self::SET_STACK_POINTER + | self::GET_TLS_BASE + | self::SET_TLS_BASE), + ) => { + if !matches!(import.ty, TypeRef::Func(_)) { + return type_error(); + } + match name { + self::GET_TLS_BASE => result.needs_get_tls_base = true, + self::SET_TLS_BASE => result.needs_set_tls_base = true, + _ => {} + } + } (self::ENV, name) => match import.ty { TypeRef::Func(ty) => { result.env_imports.insert(( @@ -466,6 +537,9 @@ impl<'a> Metadata<'a> { self::LIBDL_LIBRARIES => { result.needs_libdl_libraries = true; } + self::PROGRAM_TLS_INFO => { + result.needs_program_tls_info = true; + } _ => { result.memory_address_imports.insert(name); @@ -486,6 +560,9 @@ impl<'a> Metadata<'a> { return type_error(); } } + (self::ROOT, self::THREAD_NEW_INDIRECT) => { + result.uses_thread_new_indirect = true; + } (module, name) if adapter_names.contains(module) => { let ty = match import.ty { TypeRef::Global(wasmparser::GlobalType { @@ -532,7 +609,15 @@ impl<'a> Metadata<'a> { Payload::GlobalSection(reader) => { for global in reader { - global_types.push(global?.ty); + let global = global?; + global_types.push(global.ty); + let mut ops = global.init_expr.get_operators_reader(); + global_values.push(match (ops.read(), ops.read()) { + (Ok(Operator::I32Const { value }), Ok(Operator::End)) => { + Some(value as u32) + } + _ => None, + }); } } @@ -546,11 +631,20 @@ impl<'a> Metadata<'a> { for export in reader { let export = export?; + let global_value = || { + global_values + .get(usize::try_from(export.index).unwrap()) + .copied() + .flatten() + }; match export.name { self::APPLY_DATA_RELOCS => result.has_data_relocs = true, self::CALL_CTORS => result.has_ctors = true, self::INITIALIZE => result.has_initialize = true, self::START => result.has_wasi_start = true, + self::TLS_SIZE => tls_size = global_value(), + self::TLS_ALIGN => tls_align = global_value(), + self::INIT_TLS => has_init_tls = true, _ => { if export.name == self::INIT_TASK { result.has_init_task = true; @@ -601,6 +695,32 @@ impl<'a> Metadata<'a> { } } + // A module only participates in whole-program TLS setup if the linker + // driver arranged for these to be exported and it actually has some + // thread-local storage. Anything else (a library built without those + // exports, or the synthesized stubs module) is left out of + // `__wasm_program_tls_info`: it never reads a TLS base. + result.tls_size = tls_size.unwrap_or(0); + result.tls_align = tls_align.unwrap_or(0); + result.has_tls_info = result.tls_size > 0; + if result.has_tls_info { + if !has_init_tls { + bail!( + "{} has {} bytes of thread-local storage but does not export `{}`", + result.name, + result.tls_size, + self::INIT_TLS, + ); + } + if result.tls_align == 0 { + bail!( + "{} has {} bytes of thread-local storage but no alignment", + result.name, + result.tls_size + ); + } + } + Ok(result) } } diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs index 898343d48b..eeddc597dc 100644 --- a/crates/wit-component/src/validation.rs +++ b/crates/wit-component/src/validation.rs @@ -290,6 +290,24 @@ pub enum Import { slot: u32, }, + /// The `__wasm_get_tls_base` function that LLVM emits to read the base + /// pointer of this module's thread-local storage. + /// + /// Unlike [`Import::ContextGet`] this is not tied to a particular storage + /// mechanism: how it's satisfied depends on whether the program uses + /// cooperative threading. See + /// `EncodingState::materialize_tls_base_import` for the details. + TlsBaseGet { + /// The type of the base pointer (`i32` or `i64`). + ty: ValType, + }, + + /// The `__wasm_set_tls_base` counterpart to [`Import::TlsBaseGet`]. + TlsBaseSet { + /// The type of the base pointer (`i32` or `i64`). + ty: ValType, + }, + /// A `canon backpressure.inc` intrinsic. BackpressureInc, @@ -2608,13 +2626,16 @@ impl NameMangling for Legacy { let ty = *ty.params().get(0)?; Some(Import::ContextSet { ty, slot: 0 }) } + // TLS handling is slightly different than above to handle + // coop-threading-vs-not, so the exact resolution of this import is + // deferred to later. "__wasm_get_tls_base" => { let ty = *ty.results().get(0)?; - Some(Import::ContextGet { ty, slot: 1 }) + Some(Import::TlsBaseGet { ty }) } "__wasm_set_tls_base" => { let ty = *ty.params().get(0)?; - Some(Import::ContextSet { ty, slot: 1 }) + Some(Import::TlsBaseSet { ty }) } _ => None, } diff --git a/crates/wit-component/tests/components/error-link-tls-without-init/error.txt b/crates/wit-component/tests/components/error-link-tls-without-init/error.txt new file mode 100644 index 0000000000..49630a8de8 --- /dev/null +++ b/crates/wit-component/tests/components/error-link-tls-without-init/error.txt @@ -0,0 +1 @@ +failed to extract linking metadata from foo: foo has 32 bytes of thread-local storage but does not export `__wasm_init_tls` \ No newline at end of file diff --git a/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wat b/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wat new file mode 100644 index 0000000000..73ad41a810 --- /dev/null +++ b/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wat @@ -0,0 +1,20 @@ +;; A library with thread-local storage which doesn't export the +;; `__wasm_init_tls` that initializes it. Nothing could set this module's TLS up +;; on a spawned thread, so linking has to fail rather than silently leave it +;; uninitialized. +(module + (@dylink.0 + (mem-info (memory 8 4)) + ) + (type $void (func)) + (type $get (func (result i32))) + + (global $__tls_size i32 i32.const 32) + (global $__tls_align i32 i32.const 16) + + (func $foo (type $get) i32.const 7) + + (export "__tls_size" (global $__tls_size)) + (export "__tls_align" (global $__tls_align)) + (export "test:test/test#foo" (func $foo)) +) diff --git a/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wit b/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wit new file mode 100644 index 0000000000..be962ede03 --- /dev/null +++ b/crates/wit-component/tests/components/error-link-tls-without-init/lib-foo.wit @@ -0,0 +1,9 @@ +package test:test; + +interface test { + foo: func() -> s32; +} + +world lib-foo { + export test; +} diff --git a/crates/wit-component/tests/components/link-initialize/component.wat b/crates/wit-component/tests/components/link-initialize/component.wat index 31abefee85..48def58f7f 100644 --- a/crates/wit-component/tests/components/link-initialize/component.wat +++ b/crates/wit-component/tests/components/link-initialize/component.wat @@ -148,16 +148,16 @@ (type (;1;) (func (param i32))) (import "env" "memory" (memory (;0;) 0)) (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) - (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) - (import "bar" "_initialize" (func (;1;) (type 0))) (import "env" "foo:memory_base" (global (;0;) i32)) (import "foo" "well" (global (;1;) i32)) (import "env" "bar:well" (global (;2;) (mut i32))) - (import "foo" "__wasm_apply_data_relocs" (func (;2;) (type 0))) - (import "foo" "_initialize" (func (;3;) (type 0))) (import "env" "bar:memory_base" (global (;3;) i32)) (import "bar" "um" (global (;4;) i32)) (import "env" "foo:um" (global (;5;) (mut i32))) + (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) + (import "foo" "__wasm_apply_data_relocs" (func (;1;) (type 0))) + (import "bar" "_initialize" (func (;2;) (type 0))) + (import "foo" "_initialize" (func (;3;) (type 0))) (start 4) (elem (;0;) (i32.const 1) func) (elem (;1;) (i32.const 1) func) @@ -171,8 +171,8 @@ i32.add global.set 5 call 0 - call 2 call 1 + call 2 call 3 ) (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") @@ -243,8 +243,8 @@ ) (core instance $__init (;10;) (instantiate $__init (with "env" (instance $main)) - (with "bar" (instance $bar)) (with "foo" (instance $foo)) + (with "bar" (instance $bar)) ) ) (type (;1;) (func (param "v" s32) (result s32))) diff --git a/crates/wit-component/tests/components/link-stub-wasip2/component.wat b/crates/wit-component/tests/components/link-stub-wasip2/component.wat index 47436ff025..49c9c2b44f 100644 --- a/crates/wit-component/tests/components/link-stub-wasip2/component.wat +++ b/crates/wit-component/tests/components/link-stub-wasip2/component.wat @@ -160,16 +160,16 @@ (type (;1;) (func (param i32))) (import "env" "memory" (memory (;0;) 0)) (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) - (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) - (import "bar" "__wasm_call_ctors" (func (;1;) (type 0))) (import "env" "foo:memory_base" (global (;0;) i32)) (import "foo" "well" (global (;1;) i32)) (import "env" "bar:well" (global (;2;) (mut i32))) - (import "foo" "__wasm_apply_data_relocs" (func (;2;) (type 0))) - (import "foo" "__wasm_call_ctors" (func (;3;) (type 0))) (import "env" "bar:memory_base" (global (;3;) i32)) (import "bar" "um" (global (;4;) i32)) (import "env" "foo:um" (global (;5;) (mut i32))) + (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) + (import "foo" "__wasm_apply_data_relocs" (func (;1;) (type 0))) + (import "bar" "__wasm_call_ctors" (func (;2;) (type 0))) + (import "foo" "__wasm_call_ctors" (func (;3;) (type 0))) (start 4) (elem (;0;) (i32.const 1) func) (elem (;1;) (i32.const 1) func) @@ -183,8 +183,8 @@ i32.add global.set 5 call 0 - call 2 call 1 + call 2 call 3 ) (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") @@ -302,8 +302,8 @@ ) (core instance $__init (;16;) (instantiate $__init (with "env" (instance $main)) - (with "bar" (instance $bar)) (with "foo" (instance $foo)) + (with "bar" (instance $bar)) ) ) (type (;1;) (func (param "v" s32) (result s32))) diff --git a/crates/wit-component/tests/components/link-wasip3-abi/component.wat b/crates/wit-component/tests/components/link-wasip3-abi/component.wat index d0ee5128eb..f0eb6f6c2c 100644 --- a/crates/wit-component/tests/components/link-wasip3-abi/component.wat +++ b/crates/wit-component/tests/components/link-wasip3-abi/component.wat @@ -178,21 +178,21 @@ (type (;3;) (func)) (import "env" "memory" (memory (;0;) 0)) (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) - (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) - (import "c" "__wasm_init_task" (func (;1;) (type 0))) - (import "bar" "__wasm_call_ctors" (func (;2;) (type 0))) (import "env" "foo:memory_base" (global (;0;) i32)) (import "foo" "well" (global (;1;) i32)) (import "env" "bar:well" (global (;2;) (mut i32))) - (import "foo" "__wasm_apply_data_relocs" (func (;3;) (type 0))) - (import "foo" "__wasm_call_ctors" (func (;4;) (type 0))) (import "env" "bar:memory_base" (global (;3;) i32)) (import "bar" "um" (global (;4;) i32)) (import "env" "foo:um" (global (;5;) (mut i32))) + (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) + (import "foo" "__wasm_apply_data_relocs" (func (;1;) (type 0))) + (import "c" "__wasm_init_task" (func (;2;) (type 0))) + (import "bar" "__wasm_call_ctors" (func (;3;) (type 0))) + (import "foo" "__wasm_call_ctors" (func (;4;) (type 0))) (import "c" "__wasm_init_async_task" (func (;5;) (type 3))) (start 6) (elem (;0;) (i32.const 1) func) - (elem (;1;) (i32.const 1) func 1 5) + (elem (;1;) (i32.const 1) func 2 5) (func (;6;) (type 0) global.get 0 global.get 1 @@ -203,9 +203,9 @@ i32.add global.set 5 call 0 - call 3 call 1 call 2 + call 3 call 4 ) (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") @@ -228,8 +228,8 @@ (alias core export $main "__indirect_function_table" (core table $__indirect_function_table (;0;))) (alias core export $main "foo:memory_base" (core global $foo:memory_base (;2;))) (alias core export $main "foo:table_base" (core global $foo:table_base (;3;))) - (alias core export $c "__wasm_get_stack_pointer" (core func $__wasm_get_stack_pointer (;0;))) - (alias core export $c "__wasm_set_stack_pointer" (core func $__wasm_set_stack_pointer (;1;))) + (core func $"context.get 0" (;0;) (canon context.get i32 0)) + (core func $"context.set 0" (;1;) (canon context.set i32 0)) (alias core export $c "malloc" (core func $malloc (;2;))) (alias core export $c "abort" (core func $abort (;3;))) (core instance $env (;3;) @@ -237,8 +237,8 @@ (export "__indirect_function_table" (table $__indirect_function_table)) (export "__memory_base" (global $foo:memory_base)) (export "__table_base" (global $foo:table_base)) - (export "__wasm_get_stack_pointer" (func $__wasm_get_stack_pointer)) - (export "__wasm_set_stack_pointer" (func $__wasm_set_stack_pointer)) + (export "__wasm_get_stack_pointer" (func $"context.get 0")) + (export "__wasm_set_stack_pointer" (func $"context.set 0")) (export "malloc" (func $malloc)) (export "abort" (func $abort)) ) @@ -278,9 +278,9 @@ ) (core instance $__init (;10;) (instantiate $__init (with "env" (instance $main)) + (with "foo" (instance $foo)) (with "bar" (instance $bar)) (with "c" (instance $c)) - (with "foo" (instance $foo)) ) ) (core module $init-task-wrappers (;5;) diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wat b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wat new file mode 100644 index 0000000000..1fcffc47cc --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wat @@ -0,0 +1,182 @@ +(component + (core module $main (;0;) + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (type (;2;) (func (result i32))) + (type (;3;) (func (param i32))) + (table (;0;) 1 funcref) + (memory (;0;) 17) + (global (;0;) i32 i32.const 1048592) + (global (;1;) i32 i32.const 1) + (global (;2;) i32 i32.const 1048608) + (global (;3;) i32 i32.const 1) + (export "c:memory_base" (global 0)) + (export "c:table_base" (global 1)) + (export "foo:memory_base" (global 2)) + (export "foo:table_base" (global 3)) + (export "c:__wasm_get_tls_base" (func 0)) + (export "c:__wasm_set_tls_base" (func 1)) + (export "foo:__wasm_get_tls_base" (func 2)) + (export "foo:__wasm_set_tls_base" (func 3)) + (export "__indirect_function_table" (table 0)) + (export "memory" (memory 0)) + (func (;0;) (type 0) (result i32) + i32.const 1048584 + i32.load offset=4 + ) + (func (;1;) (type 1) (param i32) + i32.const 1048584 + local.get 0 + i32.store offset=4 + ) + (func (;2;) (type 2) (result i32) + i32.const 1048584 + i32.load + ) + (func (;3;) (type 3) (param i32) + i32.const 1048584 + local.get 0 + i32.store + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $c (;1;) + (@dylink.0 + (mem-info (memory 16 4)) + ) + (type $void (;0;) (func)) + (type $get (;1;) (func (result i32))) + (type $set (;2;) (func (param i32))) + (type (;3;) (func (param i32) (result i32))) + (import "env" "memory" (memory (;0;) 1)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "env" "__memory_base" (global $__memory_base (;0;) i32)) + (import "env" "__table_base" (global $__table_base (;1;) i32)) + (import "env" "__wasm_get_stack_pointer" (func $get_sp (;0;) (type $get))) + (import "env" "__wasm_set_stack_pointer" (func $set_sp (;1;) (type $set))) + (import "env" "__wasm_get_tls_base" (func $get_tls (;2;) (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (;3;) (type $set))) + (global (;2;) i32 i32.const 148) + (global (;3;) i32 i32.const 4) + (export "__tls_size" (global 2)) + (export "__tls_align" (global 3)) + (export "__wasm_init_tls" (func 4)) + (export "abort" (func $abort)) + (func (;4;) (type $set) (param i32) + unreachable + ) + (func $malloc (;5;) (type 3) (param i32) (result i32) + unreachable + ) + (func $abort (;6;) (type $void) + unreachable + ) + ) + (core module $foo (;2;) + (@dylink.0 + (mem-info (memory 8 4)) + (needed "c") + ) + (type $void (;0;) (func)) + (type $get (;1;) (func (result i32))) + (type $set (;2;) (func (param i32))) + (import "env" "memory" (memory (;0;) 1)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "env" "__memory_base" (global $__memory_base (;0;) i32)) + (import "env" "__table_base" (global $__table_base (;1;) i32)) + (import "env" "__wasm_get_tls_base" (func $get_tls (;0;) (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (;1;) (type $set))) + (import "env" "abort" (func $abort (;2;) (type $void))) + (global (;2;) i32 i32.const 32) + (global (;3;) i32 i32.const 16) + (export "__tls_size" (global 2)) + (export "__tls_align" (global 3)) + (export "__wasm_init_tls" (func 3)) + (export "test:test/test#foo" (func $foo)) + (func (;3;) (type $set) (param i32) + unreachable + ) + (func $foo (;4;) (type $get) (result i32) + call $get_tls + i32.load + ) + ) + (core module $__init (;3;) + (type (;0;) (func)) + (type (;1;) (func (param i32))) + (import "env" "memory" (memory (;0;) 0)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (start 0) + (elem (;0;) (i32.const 1) func) + (elem (;1;) (i32.const 1) func) + (func (;0;) (type 0)) + (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core instance $main (;0;) (instantiate $main)) + (alias core export $main "memory" (core memory $memory (;0;))) + (alias core export $main "__indirect_function_table" (core table $__indirect_function_table (;0;))) + (alias core export $main "c:memory_base" (core global $c:memory_base (;0;))) + (alias core export $main "c:table_base" (core global $c:table_base (;1;))) + (core func $"context.get 0" (;0;) (canon context.get i32 0)) + (core func $"context.set 0" (;1;) (canon context.set i32 0)) + (alias core export $main "c:__wasm_get_tls_base" (core func $c:__wasm_get_tls_base (;2;))) + (alias core export $main "c:__wasm_set_tls_base" (core func $c:__wasm_set_tls_base (;3;))) + (core instance $env (;1;) + (export "memory" (memory $memory)) + (export "__indirect_function_table" (table $__indirect_function_table)) + (export "__memory_base" (global $c:memory_base)) + (export "__table_base" (global $c:table_base)) + (export "__wasm_get_stack_pointer" (func $"context.get 0")) + (export "__wasm_set_stack_pointer" (func $"context.set 0")) + (export "__wasm_get_tls_base" (func $c:__wasm_get_tls_base)) + (export "__wasm_set_tls_base" (func $c:__wasm_set_tls_base)) + ) + (core instance $c (;2;) (instantiate $c + (with "env" (instance $env)) + ) + ) + (alias core export $main "foo:memory_base" (core global $foo:memory_base (;2;))) + (alias core export $main "foo:table_base" (core global $foo:table_base (;3;))) + (alias core export $main "foo:__wasm_get_tls_base" (core func $foo:__wasm_get_tls_base (;4;))) + (alias core export $main "foo:__wasm_set_tls_base" (core func $foo:__wasm_set_tls_base (;5;))) + (alias core export $c "abort" (core func $abort (;6;))) + (core instance $"#core-instance3 env" (@name "env") (;3;) + (export "memory" (memory $memory)) + (export "__indirect_function_table" (table $__indirect_function_table)) + (export "__memory_base" (global $foo:memory_base)) + (export "__table_base" (global $foo:table_base)) + (export "__wasm_get_tls_base" (func $foo:__wasm_get_tls_base)) + (export "__wasm_set_tls_base" (func $foo:__wasm_set_tls_base)) + (export "abort" (func $abort)) + ) + (core instance $foo (;4;) (instantiate $foo + (with "env" (instance $"#core-instance3 env")) + ) + ) + (core instance $__init (;5;) (instantiate $__init + (with "env" (instance $main)) + ) + ) + (type (;0;) (func (result s32))) + (alias core export $foo "test:test/test#foo" (core func $test:test/test#foo (;7;))) + (func $foo (;0;) (type 0) (canon lift (core func $test:test/test#foo))) + (component $test:test/test-shim-component (;0;) + (type (;0;) (func (result s32))) + (import "import-func-foo" (func (;0;) (type 0))) + (type (;1;) (func (result s32))) + (export (;1;) "foo" (func 0) (func (type 1))) + ) + (instance $test:test/test-shim-instance (;0;) (instantiate $test:test/test-shim-component + (with "import-func-foo" (func $foo)) + ) + ) + (export $test:test/test (;1;) "test:test/test" (instance $test:test/test-shim-instance)) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wit.print b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wit.print new file mode 100644 index 0000000000..ad829fd0b8 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/component.wit.print @@ -0,0 +1,5 @@ +package root:component; + +world root { + export test:test/test; +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wat b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wat new file mode 100644 index 0000000000..7ac22ca3b2 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wat @@ -0,0 +1,26 @@ +(module + (@dylink.0 + (mem-info (memory 16 4)) + ) + (type $void (func)) + (type $get (func (result i32))) + (type $set (func (param i32))) + + (import "env" "memory" (memory 1)) + (import "env" "__indirect_function_table" (table 0 funcref)) + (import "env" "__memory_base" (global $__memory_base i32)) + (import "env" "__table_base" (global $__table_base i32)) + (import "env" "__wasm_get_stack_pointer" (func $get_sp (type $get))) + (import "env" "__wasm_set_stack_pointer" (func $set_sp (type $set))) + (import "env" "__wasm_get_tls_base" (func $get_tls (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (type $set))) + + (global (export "__tls_size") i32 i32.const 148) + (global (export "__tls_align") i32 i32.const 4) + (func (export "__wasm_init_tls") (param i32) unreachable) + + (func $malloc (param i32) (result i32) unreachable) + (func $abort unreachable) + + (export "abort" (func $abort)) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wit b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wit new file mode 100644 index 0000000000..65ff1f36b2 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-c.wit @@ -0,0 +1,4 @@ +package test:test; + +world lib-c { +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wat b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wat new file mode 100644 index 0000000000..c58fbaaa1a --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wat @@ -0,0 +1,28 @@ +(module + (@dylink.0 + (mem-info (memory 8 4)) + (needed "c") + ) + (type $void (func)) + (type $get (func (result i32))) + (type $set (func (param i32))) + + (import "env" "memory" (memory 1)) + (import "env" "__indirect_function_table" (table 0 funcref)) + (import "env" "__memory_base" (global $__memory_base i32)) + (import "env" "__table_base" (global $__table_base i32)) + (import "env" "__wasm_get_tls_base" (func $get_tls (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (type $set))) + (import "env" "abort" (func $abort (type $void))) + + (global (export "__tls_size") i32 i32.const 32) + (global (export "__tls_align") i32 i32.const 16) + (func (export "__wasm_init_tls") (param i32) unreachable) + + (func $foo (result i32) + call $get_tls + i32.load + ) + + (export "test:test/test#foo" (func $foo)) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wit b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wit new file mode 100644 index 0000000000..be962ede03 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls-no-threads/lib-foo.wit @@ -0,0 +1,9 @@ +package test:test; + +interface test { + foo: func() -> s32; +} + +world lib-foo { + export test; +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls/component.wat b/crates/wit-component/tests/components/link-wasip3-tls/component.wat new file mode 100644 index 0000000000..a2bb4bcd70 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/component.wat @@ -0,0 +1,294 @@ +(component + (core module $main (;0;) + (type (;0;) (func (result i32))) + (type (;1;) (func (result i32))) + (type (;2;) (func (param i32))) + (type (;3;) (func (result i32))) + (type (;4;) (func (param i32))) + (import "$root" "[context-get-1]" (func (;0;) (type 0))) + (table (;0;) 3 funcref) + (memory (;0;) 17) + (global (;0;) (mut i32) i32.const 1048616) + (global (;1;) i32 i32.const 1048640) + (global (;2;) i32 i32.const 3) + (global (;3;) i32 i32.const 1048656) + (global (;4;) i32 i32.const 3) + (global (;5;) i32 i32.const 1048672) + (global (;6;) i32 i32.const 3) + (export "__wasm_program_tls_info" (global 0)) + (export "c:memory_base" (global 1)) + (export "c:table_base" (global 2)) + (export "foo:memory_base" (global 3)) + (export "foo:table_base" (global 4)) + (export "none:memory_base" (global 5)) + (export "none:table_base" (global 6)) + (export "c:__wasm_get_tls_base" (func 1)) + (export "c:__wasm_set_tls_base" (func 2)) + (export "foo:__wasm_get_tls_base" (func 3)) + (export "foo:__wasm_set_tls_base" (func 4)) + (export "__indirect_function_table" (table 0)) + (export "memory" (memory 0)) + (func (;1;) (type 1) (result i32) + call 0 + i32.load offset=4 + ) + (func (;2;) (type 2) (param i32) + (local i32) + call 0 + local.tee 1 + i32.eqz + if ;; label = @1 + i32.const 1048584 + local.get 0 + i32.store offset=4 + else + local.get 1 + local.get 0 + i32.store offset=4 + end + ) + (func (;3;) (type 3) (result i32) + call 0 + i32.load + ) + (func (;4;) (type 4) (param i32) + (local i32) + call 0 + local.tee 1 + i32.eqz + if ;; label = @1 + i32.const 1048584 + local.get 0 + i32.store + else + local.get 1 + local.get 0 + i32.store + end + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $c (;1;) + (@dylink.0 + (mem-info (memory 16 4)) + ) + (type $void (;0;) (func)) + (type $get (;1;) (func (result i32))) + (type $set (;2;) (func (param i32))) + (type $spawn (;3;) (func (param i32 i32) (result i32))) + (import "env" "memory" (memory (;0;) 1)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "env" "__memory_base" (global $__memory_base (;0;) i32)) + (import "env" "__table_base" (global $__table_base (;1;) i32)) + (import "env" "__wasm_get_stack_pointer" (func $get_sp (;0;) (type $get))) + (import "env" "__wasm_set_stack_pointer" (func $set_sp (;1;) (type $set))) + (import "env" "__wasm_get_tls_base" (func $get_tls (;2;) (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (;3;) (type $set))) + (import "$root" "[context-get-1]" (func $get_ctx (;4;) (type $get))) + (import "$root" "[context-set-1]" (func $set_ctx (;5;) (type $set))) + (import "GOT.mem" "__wasm_program_tls_info" (global $tls_info (;2;) (mut i32))) + (import "$root" "[thread-new-indirect-v0]" (func $spawn (;6;) (type $spawn))) + (global (;3;) i32 i32.const 148) + (global (;4;) i32 i32.const 4) + (export "__tls_size" (global 3)) + (export "__tls_align" (global 4)) + (export "__wasm_init_tls" (func 7)) + (export "abort" (func 8)) + (func (;7;) (type $set) (param i32) + local.get 0 + call $set_tls + ) + (func (;8;) (type $void) + unreachable + ) + ) + (core module $none (;2;) + (@dylink.0 + (mem-info (memory 4 4)) + (needed "c") + ) + (type (;0;) (func (result i32))) + (export "none_helper" (func 0)) + (func (;0;) (type 0) (result i32) + i32.const 7 + ) + ) + (core module $foo (;3;) + (@dylink.0 + (mem-info (memory 8 4)) + (needed "c") + (needed "none") + ) + (type $void (;0;) (func)) + (type $get (;1;) (func (result i32))) + (type $set (;2;) (func (param i32))) + (import "env" "memory" (memory (;0;) 1)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "env" "__memory_base" (global $__memory_base (;0;) i32)) + (import "env" "__table_base" (global $__table_base (;1;) i32)) + (import "env" "__wasm_get_tls_base" (func $get_tls (;0;) (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (;1;) (type $set))) + (import "env" "abort" (func $abort (;2;) (type $void))) + (import "env" "none_helper" (func $none_helper (;3;) (type $get))) + (global (;2;) i32 i32.const 32) + (global (;3;) i32 i32.const 16) + (export "__tls_size" (global 2)) + (export "__tls_align" (global 3)) + (export "__wasm_init_tls" (func 4)) + (export "test:test/test#foo" (func 5)) + (func (;4;) (type $set) (param i32) + local.get 0 + call $set_tls + ) + (func (;5;) (type $get) (result i32) + call $get_tls + i32.load + call $none_helper + i32.add + ) + ) + (core module $__init (;4;) + (type (;0;) (func)) + (type (;1;) (func (param i32))) + (import "env" "memory" (memory (;0;) 0)) + (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) + (import "foo" "__wasm_init_tls" (func (;0;) (type 1))) + (import "c" "__wasm_init_tls" (func (;1;) (type 1))) + (start 2) + (elem (;0;) (i32.const 1) func) + (elem (;1;) (i32.const 3) func) + (elem (;2;) (i32.const 1) func 0 1) + (func (;2;) (type 0)) + (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") + (data (;1;) (i32.const 1048592) " \00\00\00\10\00\00\00\01\00\00\00\94\00\00\00\04\00\00\00\02\00\00\00\02\00\00\00\10\00\10\00\08\00\10\00") + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $wit-component-shim-module (;5;) + (type (;0;) (func (param i32 i32) (result i32))) + (table (;0;) 1 1 funcref) + (export "0" (func $thread.new-indirect)) + (export "$imports" (table 0)) + (func $thread.new-indirect (;0;) (type 0) (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.const 0 + call_indirect (type 0) + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $wit-component-fixup (;6;) + (type (;0;) (func (param i32 i32) (result i32))) + (import "" "0" (func (;0;) (type 0))) + (import "" "$imports" (table (;0;) 1 1 funcref)) + (elem (;0;) (i32.const 0) func 0) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module)) + (core func $"context.get 1" (;0;) (canon context.get i32 1)) + (core instance $$root (;1;) + (export "[context-get-1]" (func $"context.get 1")) + ) + (core instance $main (;2;) (instantiate $main + (with "$root" (instance $$root)) + ) + ) + (alias core export $main "memory" (core memory $memory (;0;))) + (alias core export $main "__indirect_function_table" (core table $__indirect_function_table (;0;))) + (alias core export $main "c:memory_base" (core global $c:memory_base (;0;))) + (alias core export $main "c:table_base" (core global $c:table_base (;1;))) + (core func $"context.get 0" (;1;) (canon context.get i32 0)) + (core func $"context.set 0" (;2;) (canon context.set i32 0)) + (alias core export $main "c:__wasm_get_tls_base" (core func $c:__wasm_get_tls_base (;3;))) + (alias core export $main "c:__wasm_set_tls_base" (core func $c:__wasm_set_tls_base (;4;))) + (core instance $env (;3;) + (export "memory" (memory $memory)) + (export "__indirect_function_table" (table $__indirect_function_table)) + (export "__memory_base" (global $c:memory_base)) + (export "__table_base" (global $c:table_base)) + (export "__wasm_get_stack_pointer" (func $"context.get 0")) + (export "__wasm_set_stack_pointer" (func $"context.set 0")) + (export "__wasm_get_tls_base" (func $c:__wasm_get_tls_base)) + (export "__wasm_set_tls_base" (func $c:__wasm_set_tls_base)) + ) + (core func $"#core-func5 context.get 1" (@name "context.get 1") (;5;) (canon context.get i32 1)) + (core func $"context.set 1" (;6;) (canon context.set i32 1)) + (alias core export $wit-component-shim-instance "0" (core func $thread.new-indirect (;7;))) + (core instance $"#core-instance4 $root" (@name "$root") (;4;) + (export "[context-get-1]" (func $"#core-func5 context.get 1")) + (export "[context-set-1]" (func $"context.set 1")) + (export "[thread-new-indirect-v0]" (func $thread.new-indirect)) + ) + (alias core export $main "__wasm_program_tls_info" (core global $__wasm_program_tls_info (;2;))) + (core instance $GOT.mem (;5;) + (export "__wasm_program_tls_info" (global $__wasm_program_tls_info)) + ) + (core instance $c (;6;) (instantiate $c + (with "env" (instance $env)) + (with "$root" (instance $"#core-instance4 $root")) + (with "GOT.mem" (instance $GOT.mem)) + ) + ) + (core instance $none (;7;) (instantiate $none)) + (alias core export $main "foo:memory_base" (core global $foo:memory_base (;3;))) + (alias core export $main "foo:table_base" (core global $foo:table_base (;4;))) + (alias core export $main "foo:__wasm_get_tls_base" (core func $foo:__wasm_get_tls_base (;8;))) + (alias core export $main "foo:__wasm_set_tls_base" (core func $foo:__wasm_set_tls_base (;9;))) + (alias core export $c "abort" (core func $abort (;10;))) + (alias core export $none "none_helper" (core func $none_helper (;11;))) + (core instance $"#core-instance8 env" (@name "env") (;8;) + (export "memory" (memory $memory)) + (export "__indirect_function_table" (table $__indirect_function_table)) + (export "__memory_base" (global $foo:memory_base)) + (export "__table_base" (global $foo:table_base)) + (export "__wasm_get_tls_base" (func $foo:__wasm_get_tls_base)) + (export "__wasm_set_tls_base" (func $foo:__wasm_set_tls_base)) + (export "abort" (func $abort)) + (export "none_helper" (func $none_helper)) + ) + (core instance $foo (;9;) (instantiate $foo + (with "env" (instance $"#core-instance8 env")) + ) + ) + (alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;1;))) + (core type $thread-start (;0;) (func (param i32))) + (core func $"#core-func12 thread.new-indirect" (@name "thread.new-indirect") (;12;) (canon thread.new-indirect $thread-start $__indirect_function_table)) + (core instance $fixup-args (;10;) + (export "$imports" (table $"shim table")) + (export "0" (func $"#core-func12 thread.new-indirect")) + ) + (core instance $fixup (;11;) (instantiate $wit-component-fixup + (with "" (instance $fixup-args)) + ) + ) + (core instance $__init (;12;) (instantiate $__init + (with "env" (instance $main)) + (with "foo" (instance $foo)) + (with "c" (instance $c)) + ) + ) + (type (;0;) (func (result s32))) + (alias core export $foo "test:test/test#foo" (core func $test:test/test#foo (;13;))) + (func $foo (;0;) (type 0) (canon lift (core func $test:test/test#foo))) + (component $test:test/test-shim-component (;0;) + (type (;0;) (func (result s32))) + (import "import-func-foo" (func (;0;) (type 0))) + (type (;1;) (func (result s32))) + (export (;1;) "foo" (func 0) (func (type 1))) + ) + (instance $test:test/test-shim-instance (;0;) (instantiate $test:test/test-shim-component + (with "import-func-foo" (func $foo)) + ) + ) + (export $test:test/test (;1;) "test:test/test" (instance $test:test/test-shim-instance)) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls/component.wit.print b/crates/wit-component/tests/components/link-wasip3-tls/component.wit.print new file mode 100644 index 0000000000..ad829fd0b8 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/component.wit.print @@ -0,0 +1,5 @@ +package root:component; + +world root { + export test:test/test; +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wat b/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wat new file mode 100644 index 0000000000..07e7070df0 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wat @@ -0,0 +1,31 @@ +(module + (@dylink.0 + (mem-info (memory 16 4)) + ) + (type $void (func)) + (type $get (func (result i32))) + (type $set (func (param i32))) + (type $spawn (func (param i32 i32) (result i32))) + + (import "env" "memory" (memory 1)) + (import "env" "__indirect_function_table" (table 0 funcref)) + (import "env" "__memory_base" (global $__memory_base i32)) + (import "env" "__table_base" (global $__table_base i32)) + (import "env" "__wasm_get_stack_pointer" (func $get_sp (type $get))) + (import "env" "__wasm_set_stack_pointer" (func $set_sp (type $set))) + (import "env" "__wasm_get_tls_base" (func $get_tls (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (type $set))) + (import "$root" "[context-get-1]" (func $get_ctx (type $get))) + (import "$root" "[context-set-1]" (func $set_ctx (type $set))) + (import "GOT.mem" "__wasm_program_tls_info" (global $tls_info (mut i32))) + (import "$root" "[thread-new-indirect-v0]" (func $spawn (type $spawn))) + + (global (export "__tls_size") i32 i32.const 148) + (global (export "__tls_align") i32 i32.const 4) + (func (export "__wasm_init_tls") (param i32) + local.get 0 + call $set_tls + ) + + (func (export "abort") unreachable) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wit b/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wit new file mode 100644 index 0000000000..65ff1f36b2 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-c.wit @@ -0,0 +1,4 @@ +package test:test; + +world lib-c { +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wat b/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wat new file mode 100644 index 0000000000..bd1ec391c8 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wat @@ -0,0 +1,33 @@ +(module + (@dylink.0 + (mem-info (memory 8 4)) + (needed "c") + (needed "none") + ) + (type $void (func)) + (type $get (func (result i32))) + (type $set (func (param i32))) + + (import "env" "memory" (memory 1)) + (import "env" "__indirect_function_table" (table 0 funcref)) + (import "env" "__memory_base" (global $__memory_base i32)) + (import "env" "__table_base" (global $__table_base i32)) + (import "env" "__wasm_get_tls_base" (func $get_tls (type $get))) + (import "env" "__wasm_set_tls_base" (func $set_tls (type $set))) + (import "env" "abort" (func $abort (type $void))) + (import "env" "none_helper" (func $none_helper (type $get))) + + (global (export "__tls_size") i32 i32.const 32) + (global (export "__tls_align") i32 i32.const 16) + (func (export "__wasm_init_tls") (param i32) + local.get 0 + call $set_tls + ) + + (func (export "test:test/test#foo") (result i32) + call $get_tls + i32.load + call $none_helper + i32.add + ) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wit b/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wit new file mode 100644 index 0000000000..be962ede03 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-foo.wit @@ -0,0 +1,9 @@ +package test:test; + +interface test { + foo: func() -> s32; +} + +world lib-foo { + export test; +} diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wat b/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wat new file mode 100644 index 0000000000..3f3813b6a1 --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wat @@ -0,0 +1,8 @@ +(module + (@dylink.0 + (mem-info (memory 4 4)) + (needed "c") + ) + + (func (export "none_helper") (result i32) i32.const 7) +) diff --git a/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wit b/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wit new file mode 100644 index 0000000000..1f34c7a78e --- /dev/null +++ b/crates/wit-component/tests/components/link-wasip3-tls/lib-none.wit @@ -0,0 +1,4 @@ +package test:test; + +world lib-none { +} diff --git a/crates/wit-component/tests/components/link/component.wat b/crates/wit-component/tests/components/link/component.wat index 9cbc709107..5a66d8ab1d 100644 --- a/crates/wit-component/tests/components/link/component.wat +++ b/crates/wit-component/tests/components/link/component.wat @@ -148,16 +148,16 @@ (type (;1;) (func (param i32))) (import "env" "memory" (memory (;0;) 0)) (import "env" "__indirect_function_table" (table (;0;) 0 funcref)) - (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) - (import "bar" "__wasm_call_ctors" (func (;1;) (type 0))) (import "env" "foo:memory_base" (global (;0;) i32)) (import "foo" "well" (global (;1;) i32)) (import "env" "bar:well" (global (;2;) (mut i32))) - (import "foo" "__wasm_apply_data_relocs" (func (;2;) (type 0))) - (import "foo" "__wasm_call_ctors" (func (;3;) (type 0))) (import "env" "bar:memory_base" (global (;3;) i32)) (import "bar" "um" (global (;4;) i32)) (import "env" "foo:um" (global (;5;) (mut i32))) + (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0))) + (import "foo" "__wasm_apply_data_relocs" (func (;1;) (type 0))) + (import "bar" "__wasm_call_ctors" (func (;2;) (type 0))) + (import "foo" "__wasm_call_ctors" (func (;3;) (type 0))) (start 4) (elem (;0;) (i32.const 1) func) (elem (;1;) (i32.const 1) func) @@ -171,8 +171,8 @@ i32.add global.set 5 call 0 - call 2 call 1 + call 2 call 3 ) (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00") @@ -243,8 +243,8 @@ ) (core instance $__init (;10;) (instantiate $__init (with "env" (instance $main)) - (with "bar" (instance $bar)) (with "foo" (instance $foo)) + (with "bar" (instance $bar)) ) ) (type (;1;) (func (param "v" s32) (result s32))) diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wat b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wat new file mode 100644 index 0000000000..d188eb4733 --- /dev/null +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wat @@ -0,0 +1,77 @@ +(component + (core module $main (;0;) + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (type (;2;) (func (param i32 i32) (result i32))) + (import "env" "__wasm_get_stack_pointer" (func (;0;) (type 0))) + (import "env" "__wasm_set_stack_pointer" (func (;1;) (type 1))) + (import "env" "__wasm_get_tls_base" (func (;2;) (type 0))) + (import "env" "__wasm_set_tls_base" (func (;3;) (type 1))) + (import "$root" "[thread-new-indirect-v0]" (func (;4;) (type 2))) + (table (;0;) 1 funcref) + (export "__indirect_function_table" (table 0)) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + (processed-by "my-fake-bindgen" "123.45") + ) + ) + (core module $wit-component-shim-module (;1;) + (type (;0;) (func (param i32 i32) (result i32))) + (table (;0;) 1 1 funcref) + (export "0" (func $thread.new-indirect)) + (export "$imports" (table 0)) + (func $thread.new-indirect (;0;) (type 0) (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.const 0 + call_indirect (type 0) + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core module $wit-component-fixup (;2;) + (type (;0;) (func (param i32 i32) (result i32))) + (import "" "0" (func (;0;) (type 0))) + (import "" "$imports" (table (;0;) 1 1 funcref)) + (elem (;0;) (i32.const 0) func 0) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) + ) + (core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module)) + (core func $"context.get 0" (;0;) (canon context.get i32 0)) + (core func $"context.set 0" (;1;) (canon context.set i32 0)) + (core func $"context.get 1" (;2;) (canon context.get i32 1)) + (core func $"context.set 1" (;3;) (canon context.set i32 1)) + (core instance $env (;1;) + (export "__wasm_get_stack_pointer" (func $"context.get 0")) + (export "__wasm_set_stack_pointer" (func $"context.set 0")) + (export "__wasm_get_tls_base" (func $"context.get 1")) + (export "__wasm_set_tls_base" (func $"context.set 1")) + ) + (alias core export $wit-component-shim-instance "0" (core func $thread.new-indirect (;4;))) + (core instance $$root (;2;) + (export "[thread-new-indirect-v0]" (func $thread.new-indirect)) + ) + (core instance $main (;3;) (instantiate $main + (with "env" (instance $env)) + (with "$root" (instance $$root)) + ) + ) + (alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;))) + (core type $thread-start (;0;) (func (param i32))) + (alias core export $main "__indirect_function_table" (core table $indirect-function-table (;1;))) + (core func $"#core-func5 thread.new-indirect" (@name "thread.new-indirect") (;5;) (canon thread.new-indirect $thread-start $indirect-function-table)) + (core instance $fixup-args (;4;) + (export "$imports" (table $"shim table")) + (export "0" (func $"#core-func5 thread.new-indirect")) + ) + (core instance $fixup (;5;) (instantiate $wit-component-fixup + (with "" (instance $fixup-args)) + ) + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wit.print b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wit.print new file mode 100644 index 0000000000..b4548abf3d --- /dev/null +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/component.wit.print @@ -0,0 +1,4 @@ +package root:component; + +world root { +} diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wat b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wat new file mode 100644 index 0000000000..0ad52eb8ea --- /dev/null +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wat @@ -0,0 +1,8 @@ +(module + (import "env" "__wasm_get_stack_pointer" (func (result i32))) + (import "env" "__wasm_set_stack_pointer" (func (param i32))) + (import "env" "__wasm_get_tls_base" (func (result i32))) + (import "env" "__wasm_set_tls_base" (func (param i32))) + (import "$root" "[thread-new-indirect-v0]" (func (param i32 i32) (result i32))) + (table (export "__indirect_function_table") 1 funcref) +) diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wit b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wit new file mode 100644 index 0000000000..065a2959f4 --- /dev/null +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols-coop/module.wit @@ -0,0 +1,5 @@ +package foo:foo; + + +world module { +} diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols/component.wat b/crates/wit-component/tests/components/threading-wasm-ld-symbols/component.wat index 6bcc4c5674..a9b2c43544 100644 --- a/crates/wit-component/tests/components/threading-wasm-ld-symbols/component.wat +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols/component.wat @@ -13,15 +13,30 @@ ) (core func $"context.get 0" (;0;) (canon context.get i32 0)) (core func $"context.set 0" (;1;) (canon context.set i32 0)) - (core func $"context.get 1" (;2;) (canon context.get i32 1)) - (core func $"context.set 1" (;3;) (canon context.set i32 1)) - (core instance $env (;0;) + (core module $wit-component:tls-base (;1;) + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (global (;0;) (mut i32) i32.const 0) + (export "get" (func 0)) + (export "set" (func 1)) + (func (;0;) (type 0) (result i32) + global.get 0 + ) + (func (;1;) (type 1) (param i32) + local.get 0 + global.set 0 + ) + ) + (core instance $wit-component:tls-base (;0;) (instantiate $wit-component:tls-base)) + (alias core export $wit-component:tls-base "get" (core func $tls-base-get (;2;))) + (alias core export $wit-component:tls-base "set" (core func $tls-base-set (;3;))) + (core instance $env (;1;) (export "__wasm_get_stack_pointer" (func $"context.get 0")) (export "__wasm_set_stack_pointer" (func $"context.set 0")) - (export "__wasm_get_tls_base" (func $"context.get 1")) - (export "__wasm_set_tls_base" (func $"context.set 1")) + (export "__wasm_get_tls_base" (func $tls-base-get)) + (export "__wasm_set_tls_base" (func $tls-base-set)) ) - (core instance $main (;1;) (instantiate $main + (core instance $main (;2;) (instantiate $main (with "env" (instance $env)) ) ) diff --git a/crates/wit-component/tests/components/threading-wasm-ld-symbols64/component.wat b/crates/wit-component/tests/components/threading-wasm-ld-symbols64/component.wat index dd5690ae86..069f45b88b 100644 --- a/crates/wit-component/tests/components/threading-wasm-ld-symbols64/component.wat +++ b/crates/wit-component/tests/components/threading-wasm-ld-symbols64/component.wat @@ -13,15 +13,30 @@ ) (core func $"context.get 0" (;0;) (canon context.get i64 0)) (core func $"context.set 0" (;1;) (canon context.set i64 0)) - (core func $"context.get 1" (;2;) (canon context.get i64 1)) - (core func $"context.set 1" (;3;) (canon context.set i64 1)) - (core instance $env (;0;) + (core module $wit-component:tls-base (;1;) + (type (;0;) (func (result i64))) + (type (;1;) (func (param i64))) + (global (;0;) (mut i64) i64.const 0) + (export "get" (func 0)) + (export "set" (func 1)) + (func (;0;) (type 0) (result i64) + global.get 0 + ) + (func (;1;) (type 1) (param i64) + local.get 0 + global.set 0 + ) + ) + (core instance $wit-component:tls-base (;0;) (instantiate $wit-component:tls-base)) + (alias core export $wit-component:tls-base "get" (core func $tls-base-get (;2;))) + (alias core export $wit-component:tls-base "set" (core func $tls-base-set (;3;))) + (core instance $env (;1;) (export "__wasm_get_stack_pointer" (func $"context.get 0")) (export "__wasm_set_stack_pointer" (func $"context.set 0")) - (export "__wasm_get_tls_base" (func $"context.get 1")) - (export "__wasm_set_tls_base" (func $"context.set 1")) + (export "__wasm_get_tls_base" (func $tls-base-get)) + (export "__wasm_set_tls_base" (func $tls-base-set)) ) - (core instance $main (;1;) (instantiate $main + (core instance $main (;2;) (instantiate $main (with "env" (instance $env)) ) )