Implement the module linking alias section (#2451)

This commit is intended to do almost everything necessary for processing
the alias section of module linking. Most of this is internal
refactoring, the highlights being:

* Type contents are now stored separately from a `wasmtime_env::Module`.
  Given that modules can freely alias types and have them used all over
  the place, it seemed best to have one canonical location to type
  storage which everywhere else points to (with indices). A new
  `TypeTables` structure is produced during compilation which is shared
  amongst all member modules in a wasm blob.

* Instantiation is heavily refactored to account for module linking. The
  main gotcha here is that imports are now listed as "initializers". We
  have a sort of pseudo-bytecode-interpreter which interprets the
  initialization of a module. This is more complicated than just
  matching imports at this point because in the module linking proposal
  the module, alias, import, and instance sections may all be
  interleaved. This means that imports aren't guaranteed to show up at
  the beginning of the address space for modules/instances.

Otherwise most of the changes here largely fell out from these two
design points. Aliases are recorded as initializers in this scheme.
Copying around type information and/or just knowing type information
during compilation is also pretty easy since everything is just a
pointer into a `TypeTables` and we don't have to actually copy any types
themselves. Lots of various refactorings were necessary to accomodate
these changes.

Tests are hoped to cover a breadth of functionality here, but not
necessarily a depth. There's still one more piece of the module linking
proposal missing which is exporting instances/modules, which will come
in a future PR.

It's also worth nothing that there's one large TODO which isn't
implemented in this change that I plan on opening an issue for.
With module linking when a set of modules comes back from compilation
each modules has all the trampolines for the entire set of modules. This
is quite a lot of duplicate trampolines across module-linking modules.
We'll want to refactor this at some point to instead have only one set
of trampolines per set of module linking modules and have them shared
from there. I figured it was best to separate out this change, however,
since it's purely related to resource usage, and doesn't impact
non-module-linking modules at all.

cc #2094
This commit is contained in:
Alex Crichton
2020-12-02 17:24:06 -06:00
committed by GitHub
parent a548516f97
commit 9ac7d01288
34 changed files with 1322 additions and 521 deletions

View File

@@ -6,6 +6,6 @@ mod spec;
pub use crate::environ::dummy::DummyEnvironment; pub use crate::environ::dummy::DummyEnvironment;
pub use crate::environ::spec::{ pub use crate::environ::spec::{
FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment, WasmError, Alias, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment,
WasmFuncType, WasmResult, WasmType, WasmError, WasmFuncType, WasmResult, WasmType,
}; };

View File

@@ -9,7 +9,8 @@
use crate::state::FuncTranslationState; use crate::state::FuncTranslationState;
use crate::translation_utils::{ use crate::translation_utils::{
DataIndex, ElemIndex, EntityIndex, EntityType, Event, EventIndex, FuncIndex, Global, DataIndex, ElemIndex, EntityIndex, EntityType, Event, EventIndex, FuncIndex, Global,
GlobalIndex, Memory, MemoryIndex, ModuleIndex, Table, TableIndex, TypeIndex, GlobalIndex, InstanceIndex, InstanceTypeIndex, Memory, MemoryIndex, ModuleIndex,
ModuleTypeIndex, SignatureIndex, Table, TableIndex, TypeIndex,
}; };
use core::convert::From; use core::convert::From;
use core::convert::TryFrom; use core::convert::TryFrom;
@@ -202,6 +203,30 @@ pub enum ReturnMode {
FallthroughReturn, FallthroughReturn,
} }
/// An entry in the alias section of a wasm module (from the module linking
/// proposal)
pub enum Alias {
/// A parent's module is being aliased into our own index space.
///
/// Note that the index here is in the parent's index space, not our own.
ParentModule(ModuleIndex),
/// A parent's type is being aliased into our own index space
///
/// Note that the index here is in the parent's index space, not our own.
ParentType(TypeIndex),
/// A previously created instance is having one of its exports aliased into
/// our index space.
Child {
/// The index we're aliasing.
instance: InstanceIndex,
/// The nth export that we're inserting into our own index space
/// locally.
export: usize,
},
}
/// Environment affecting the translation of a WebAssembly. /// Environment affecting the translation of a WebAssembly.
pub trait TargetEnvironment { pub trait TargetEnvironment {
/// Get the information needed to produce Cranelift IR for the given target. /// Get the information needed to produce Cranelift IR for the given target.
@@ -684,6 +709,27 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
Err(WasmError::Unsupported("module linking".to_string())) Err(WasmError::Unsupported("module linking".to_string()))
} }
/// Translates a type index to its signature index, only called for type
/// indices which point to functions.
fn type_to_signature(&self, index: TypeIndex) -> WasmResult<SignatureIndex> {
drop(index);
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Translates a type index to its module type index, only called for type
/// indices which point to modules.
fn type_to_module_type(&self, index: TypeIndex) -> WasmResult<ModuleTypeIndex> {
drop(index);
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Translates a type index to its instance type index, only called for type
/// indices which point to instances.
fn type_to_instance_type(&self, index: TypeIndex) -> WasmResult<InstanceTypeIndex> {
drop(index);
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Provides the number of imports up front. By default this does nothing, but /// Provides the number of imports up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired. /// implementations can use this to preallocate memory if desired.
fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> { fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> {
@@ -845,6 +891,22 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
name: &'data str, name: &'data str,
) -> WasmResult<()>; ) -> WasmResult<()>;
/// Declares an instance export to the environment.
fn declare_instance_export(
&mut self,
index: InstanceIndex,
name: &'data str,
) -> WasmResult<()> {
drop((index, name));
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Declares an instance export to the environment.
fn declare_module_export(&mut self, index: ModuleIndex, name: &'data str) -> WasmResult<()> {
drop((index, name));
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Notifies the implementation that all exports have been declared. /// Notifies the implementation that all exports have been declared.
fn finish_exports(&mut self) -> WasmResult<()> { fn finish_exports(&mut self) -> WasmResult<()> {
Ok(()) Ok(())
@@ -952,6 +1014,12 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
drop(amount); drop(amount);
} }
/// Declares that a module will come later with the type signature provided.
fn declare_module(&mut self, ty: TypeIndex) -> WasmResult<()> {
drop(ty);
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Called at the beginning of translating a module. /// Called at the beginning of translating a module.
/// ///
/// The `index` argument is a monotonically increasing index which /// The `index` argument is a monotonically increasing index which
@@ -982,4 +1050,14 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
drop((module, args)); drop((module, args));
Err(WasmError::Unsupported("wasm instance".to_string())) Err(WasmError::Unsupported("wasm instance".to_string()))
} }
/// Declares a new alias being added to this module.
///
/// The alias comes from the `instance` specified (or the parent if `None`
/// is supplied) and the index is either in the module's own index spaces
/// for the parent or an index into the exports for nested instances.
fn declare_alias(&mut self, alias: Alias) -> WasmResult<()> {
drop(alias);
Err(WasmError::Unsupported("wasm alias".to_string()))
}
} }

View File

@@ -57,7 +57,7 @@ mod state;
mod translation_utils; mod translation_utils;
pub use crate::environ::{ pub use crate::environ::{
DummyEnvironment, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, Alias, DummyEnvironment, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode,
TargetEnvironment, WasmError, WasmFuncType, WasmResult, WasmType, TargetEnvironment, WasmError, WasmFuncType, WasmResult, WasmType,
}; };
pub use crate::func_translator::FuncTranslator; pub use crate::func_translator::FuncTranslator;

View File

@@ -2,10 +2,10 @@
//! to deal with each part of it. //! to deal with each part of it.
use crate::environ::{ModuleEnvironment, WasmResult}; use crate::environ::{ModuleEnvironment, WasmResult};
use crate::sections_translator::{ use crate::sections_translator::{
parse_data_section, parse_element_section, parse_event_section, parse_export_section, parse_alias_section, parse_data_section, parse_element_section, parse_event_section,
parse_function_section, parse_global_section, parse_import_section, parse_instance_section, parse_export_section, parse_function_section, parse_global_section, parse_import_section,
parse_memory_section, parse_name_section, parse_start_section, parse_table_section, parse_instance_section, parse_memory_section, parse_module_section, parse_name_section,
parse_type_section, parse_start_section, parse_table_section, parse_type_section,
}; };
use crate::state::ModuleTranslationState; use crate::state::ModuleTranslationState;
use cranelift_codegen::timing; use cranelift_codegen::timing;
@@ -113,7 +113,7 @@ pub fn translate_module<'data>(
Payload::ModuleSection(s) => { Payload::ModuleSection(s) => {
validator.module_section(&s)?; validator.module_section(&s)?;
environ.reserve_modules(s.get_count()); parse_module_section(s, environ)?;
} }
Payload::InstanceSection(s) => { Payload::InstanceSection(s) => {
validator.instance_section(&s)?; validator.instance_section(&s)?;
@@ -121,7 +121,7 @@ pub fn translate_module<'data>(
} }
Payload::AliasSection(s) => { Payload::AliasSection(s) => {
validator.alias_section(&s)?; validator.alias_section(&s)?;
unimplemented!("module linking not implemented yet") parse_alias_section(s, environ)?;
} }
Payload::ModuleCodeSectionStart { Payload::ModuleCodeSectionStart {
count, count,

View File

@@ -7,7 +7,7 @@
//! The special case of the initialize expressions for table elements offsets or global variables //! The special case of the initialize expressions for table elements offsets or global variables
//! is handled, according to the semantics of WebAssembly, to only specific expressions that are //! is handled, according to the semantics of WebAssembly, to only specific expressions that are
//! interpreted on the fly. //! interpreted on the fly.
use crate::environ::{ModuleEnvironment, WasmError, WasmResult}; use crate::environ::{Alias, ModuleEnvironment, WasmError, WasmResult};
use crate::state::ModuleTranslationState; use crate::state::ModuleTranslationState;
use crate::translation_utils::{ use crate::translation_utils::{
tabletype_to_type, type_to_type, DataIndex, ElemIndex, EntityIndex, EntityType, Event, tabletype_to_type, type_to_type, DataIndex, ElemIndex, EntityIndex, EntityType, Event,
@@ -36,9 +36,15 @@ fn entity_type(
environ: &mut dyn ModuleEnvironment<'_>, environ: &mut dyn ModuleEnvironment<'_>,
) -> WasmResult<EntityType> { ) -> WasmResult<EntityType> {
Ok(match ty { Ok(match ty {
ImportSectionEntryType::Function(sig) => EntityType::Function(TypeIndex::from_u32(sig)), ImportSectionEntryType::Function(sig) => {
ImportSectionEntryType::Module(sig) => EntityType::Module(TypeIndex::from_u32(sig)), EntityType::Function(environ.type_to_signature(TypeIndex::from_u32(sig))?)
ImportSectionEntryType::Instance(sig) => EntityType::Instance(TypeIndex::from_u32(sig)), }
ImportSectionEntryType::Module(sig) => {
EntityType::Module(environ.type_to_module_type(TypeIndex::from_u32(sig))?)
}
ImportSectionEntryType::Instance(sig) => {
EntityType::Instance(environ.type_to_instance_type(TypeIndex::from_u32(sig))?)
}
ImportSectionEntryType::Memory(ty) => EntityType::Memory(memory(ty)), ImportSectionEntryType::Memory(ty) => EntityType::Memory(memory(ty)),
ImportSectionEntryType::Event(evt) => EntityType::Event(event(evt)), ImportSectionEntryType::Event(evt) => EntityType::Event(event(evt)),
ImportSectionEntryType::Global(ty) => { ImportSectionEntryType::Global(ty) => {
@@ -156,24 +162,40 @@ pub fn parse_import_section<'data>(
for entry in imports { for entry in imports {
let import = entry?; let import = entry?;
match entity_type(import.ty, environ)? { match import.ty {
EntityType::Function(idx) => { ImportSectionEntryType::Function(sig) => {
environ.declare_func_import(idx, import.module, import.field)?; environ.declare_func_import(
TypeIndex::from_u32(sig),
import.module,
import.field,
)?;
} }
EntityType::Module(idx) => { ImportSectionEntryType::Module(sig) => {
environ.declare_module_import(idx, import.module, import.field)?; environ.declare_module_import(
TypeIndex::from_u32(sig),
import.module,
import.field,
)?;
} }
EntityType::Instance(idx) => { ImportSectionEntryType::Instance(sig) => {
environ.declare_instance_import(idx, import.module, import.field)?; environ.declare_instance_import(
TypeIndex::from_u32(sig),
import.module,
import.field,
)?;
} }
EntityType::Memory(ty) => { ImportSectionEntryType::Memory(ty) => {
environ.declare_memory_import(ty, import.module, import.field)?; environ.declare_memory_import(memory(ty), import.module, import.field)?;
} }
EntityType::Event(e) => environ.declare_event_import(e, import.module, import.field)?, ImportSectionEntryType::Event(e) => {
EntityType::Global(ty) => { environ.declare_event_import(event(e), import.module, import.field)?;
}
ImportSectionEntryType::Global(ty) => {
let ty = global(ty, environ, GlobalInit::Import)?;
environ.declare_global_import(ty, import.module, import.field)?; environ.declare_global_import(ty, import.module, import.field)?;
} }
EntityType::Table(ty) => { ImportSectionEntryType::Table(ty) => {
let ty = table(ty, environ)?;
environ.declare_table_import(ty, import.module, import.field)?; environ.declare_table_import(ty, import.module, import.field)?;
} }
} }
@@ -316,9 +338,15 @@ pub fn parse_export_section<'data>(
ExternalKind::Global => { ExternalKind::Global => {
environ.declare_global_export(GlobalIndex::new(index), field)? environ.declare_global_export(GlobalIndex::new(index), field)?
} }
ExternalKind::Type | ExternalKind::Module | ExternalKind::Instance => { ExternalKind::Module => {
unimplemented!("module linking not implemented yet") environ.declare_module_export(ModuleIndex::new(index), field)?
} }
ExternalKind::Instance => {
environ.declare_instance_export(InstanceIndex::new(index), field)?
}
// this never gets past validation
ExternalKind::Type => unreachable!(),
} }
} }
@@ -476,12 +504,25 @@ pub fn parse_name_section<'data>(
Ok(()) Ok(())
} }
/// Parses the Module section of the wasm module.
pub fn parse_module_section<'data>(
section: wasmparser::ModuleSectionReader<'data>,
environ: &mut dyn ModuleEnvironment<'data>,
) -> WasmResult<()> {
environ.reserve_modules(section.get_count());
for module_ty in section {
environ.declare_module(TypeIndex::from_u32(module_ty?))?;
}
Ok(())
}
/// Parses the Instance section of the wasm module. /// Parses the Instance section of the wasm module.
pub fn parse_instance_section<'data>( pub fn parse_instance_section<'data>(
section: wasmparser::InstanceSectionReader<'data>, section: wasmparser::InstanceSectionReader<'data>,
environ: &mut dyn ModuleEnvironment<'data>, environ: &mut dyn ModuleEnvironment<'data>,
) -> WasmResult<()> { ) -> WasmResult<()> {
environ.reserve_types(section.get_count())?; environ.reserve_instances(section.get_count());
for instance in section { for instance in section {
let instance = instance?; let instance = instance?;
@@ -509,3 +550,29 @@ pub fn parse_instance_section<'data>(
} }
Ok(()) Ok(())
} }
/// Parses the Alias section of the wasm module.
pub fn parse_alias_section<'data>(
section: wasmparser::AliasSectionReader<'data>,
environ: &mut dyn ModuleEnvironment<'data>,
) -> WasmResult<()> {
for alias in section {
let alias = alias?;
let alias = match alias.instance {
wasmparser::AliasedInstance::Parent => {
match alias.kind {
ExternalKind::Module => Alias::ParentModule(ModuleIndex::from_u32(alias.index)),
ExternalKind::Type => Alias::ParentType(TypeIndex::from_u32(alias.index)),
// shouldn't get past validation
_ => unreachable!(),
}
}
wasmparser::AliasedInstance::Child(i) => Alias::Child {
instance: InstanceIndex::from_u32(i),
export: alias.index as usize,
},
};
environ.declare_alias(alias)?;
}
Ok(())
}

View File

@@ -97,6 +97,18 @@ entity_impl!(InstanceIndex);
pub struct EventIndex(u32); pub struct EventIndex(u32);
entity_impl!(EventIndex); entity_impl!(EventIndex);
/// Specialized index for just module types.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ModuleTypeIndex(u32);
entity_impl!(ModuleTypeIndex);
/// Specialized index for just instance types.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct InstanceTypeIndex(u32);
entity_impl!(InstanceTypeIndex);
/// An index of an entity. /// An index of an entity.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
@@ -131,13 +143,13 @@ pub enum EntityType {
Table(Table), Table(Table),
/// A function type where the index points to the type section and records a /// A function type where the index points to the type section and records a
/// function signature. /// function signature.
Function(TypeIndex), Function(SignatureIndex),
/// An instance where the index points to the type section and records a /// An instance where the index points to the type section and records a
/// instance's exports. /// instance's exports.
Instance(TypeIndex), Instance(InstanceTypeIndex),
/// A module where the index points to the type section and records a /// A module where the index points to the type section and records a
/// module's imports and exports. /// module's imports and exports.
Module(TypeIndex), Module(ModuleTypeIndex),
} }
/// A WebAssembly global. /// A WebAssembly global.

View File

@@ -1039,7 +1039,6 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
callee: ir::Value, callee: ir::Value,
call_args: &[ir::Value], call_args: &[ir::Value],
) -> WasmResult<ir::Inst> { ) -> WasmResult<ir::Inst> {
let sig_index = self.module.types[ty_index].unwrap_function();
let pointer_type = self.pointer_type(); let pointer_type = self.pointer_type();
let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0); let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0);
@@ -1071,7 +1070,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
let vmctx = self.vmctx(pos.func); let vmctx = self.vmctx(pos.func);
let base = pos.ins().global_value(pointer_type, vmctx); let base = pos.ins().global_value(pointer_type, vmctx);
let offset = let offset =
i32::try_from(self.offsets.vmctx_vmshared_signature_id(sig_index)).unwrap(); i32::try_from(self.offsets.vmctx_vmshared_signature_id(ty_index)).unwrap();
// Load the caller ID. // Load the caller ID.
let mut mem_flags = ir::MemFlags::trusted(); let mut mem_flags = ir::MemFlags::trusted();

View File

@@ -99,7 +99,7 @@ use std::sync::Mutex;
use wasmtime_environ::{ use wasmtime_environ::{
CompileError, CompiledFunction, Compiler, FunctionAddressMap, FunctionBodyData, CompileError, CompiledFunction, Compiler, FunctionAddressMap, FunctionBodyData,
InstructionAddressMap, ModuleTranslation, Relocation, RelocationTarget, StackMapInformation, InstructionAddressMap, ModuleTranslation, Relocation, RelocationTarget, StackMapInformation,
TrapInformation, Tunables, TrapInformation, Tunables, TypeTables,
}; };
mod func_environ; mod func_environ;
@@ -348,13 +348,14 @@ impl Compiler for Cranelift {
mut input: FunctionBodyData<'_>, mut input: FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
tunables: &Tunables, tunables: &Tunables,
types: &TypeTables,
) -> Result<CompiledFunction, CompileError> { ) -> Result<CompiledFunction, CompileError> {
let module = &translation.module; let module = &translation.module;
let func_index = module.func_index(func_index); let func_index = module.func_index(func_index);
let mut context = Context::new(); let mut context = Context::new();
context.func.name = get_func_name(func_index); context.func.name = get_func_name(func_index);
let sig_index = module.functions[func_index]; let sig_index = module.functions[func_index];
context.func.signature = translation.native_signatures[sig_index].clone(); context.func.signature = types.native_signatures[sig_index].clone();
if tunables.generate_native_debuginfo { if tunables.generate_native_debuginfo {
context.func.collect_debug_info(); context.func.collect_debug_info();
} }
@@ -362,7 +363,7 @@ impl Compiler for Cranelift {
let mut func_env = FuncEnvironment::new( let mut func_env = FuncEnvironment::new(
isa.frontend_config(), isa.frontend_config(),
module, module,
&translation.native_signatures, &types.native_signatures,
tunables, tunables,
); );

View File

@@ -1,7 +1,7 @@
//! A `Compilation` contains the compiled function bodies for a WebAssembly //! A `Compilation` contains the compiled function bodies for a WebAssembly
//! module. //! module.
use crate::{FunctionAddressMap, FunctionBodyData, ModuleTranslation, Tunables}; use crate::{FunctionAddressMap, FunctionBodyData, ModuleTranslation, Tunables, TypeTables};
use cranelift_codegen::{binemit, ir, isa, isa::unwind::UnwindInfo}; use cranelift_codegen::{binemit, ir, isa, isa::unwind::UnwindInfo};
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError}; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError};
@@ -104,5 +104,6 @@ pub trait Compiler: Send + Sync {
data: FunctionBodyData<'_>, data: FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
tunables: &Tunables, tunables: &Tunables,
types: &TypeTables,
) -> Result<CompiledFunction, CompileError>; ) -> Result<CompiledFunction, CompileError>;
} }

View File

@@ -2,20 +2,14 @@
use crate::tunables::Tunables; use crate::tunables::Tunables;
use crate::WASM_MAX_PAGES; use crate::WASM_MAX_PAGES;
use cranelift_codegen::ir;
use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::{ use cranelift_wasm::*;
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
ElemIndex, EntityIndex, EntityType, FuncIndex, Global, GlobalIndex, InstanceIndex, Memory,
MemoryIndex, ModuleIndex, SignatureIndex, Table, TableIndex, TypeIndex, WasmFuncType,
};
use indexmap::IndexMap; use indexmap::IndexMap;
use more_asserts::assert_ge; use more_asserts::assert_ge;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{ use std::sync::Arc;
atomic::{AtomicUsize, Ordering::SeqCst},
Arc,
};
/// A WebAssembly table initializer. /// A WebAssembly table initializer.
#[derive(Clone, Debug, Hash, Serialize, Deserialize)] #[derive(Clone, Debug, Hash, Serialize, Deserialize)]
@@ -121,23 +115,16 @@ impl TablePlan {
} }
} }
/// Different types that can appear in a module /// Different types that can appear in a module.
#[derive(Debug, Clone, Serialize, Deserialize)] ///
/// Note that each of these variants are intended to index further into a
/// separate table.
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum ModuleType { pub enum ModuleType {
/// A function type, indexed further into the `signatures` table.
Function(SignatureIndex), Function(SignatureIndex),
/// A module type Module(ModuleTypeIndex),
Module { Instance(InstanceTypeIndex),
/// The module's imports
imports: Vec<(String, Option<String>, EntityType)>,
/// The module's exports
exports: Vec<(String, EntityType)>,
},
/// An instance type
Instance {
/// the instance's exports
exports: Vec<(String, EntityType)>,
},
} }
impl ModuleType { impl ModuleType {
@@ -153,17 +140,19 @@ impl ModuleType {
/// A translated WebAssembly module, excluding the function bodies and /// A translated WebAssembly module, excluding the function bodies and
/// memory initializers. /// memory initializers.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct Module { pub struct Module {
/// A unique identifier (within this process) for this module. /// The parent index of this module, used for the module linking proposal.
#[serde(skip_serializing, skip_deserializing, default = "Module::next_id")] ///
pub id: usize, /// This index is into the list of modules returned from compilation of a
/// single wasm file with nested modules.
pub parent: Option<usize>,
/// The name of this wasm module, often found in the wasm file. /// The name of this wasm module, often found in the wasm file.
pub name: Option<String>, pub name: Option<String>,
/// All import records, in the order they are declared in the module. /// All import records, in the order they are declared in the module.
pub imports: Vec<(String, Option<String>, EntityIndex)>, pub initializers: Vec<Initializer>,
/// Exported entities. /// Exported entities.
pub exports: IndexMap<String, EntityIndex>, pub exports: IndexMap<String, EntityIndex>,
@@ -184,22 +173,19 @@ pub struct Module {
/// WebAssembly table initializers. /// WebAssembly table initializers.
pub func_names: HashMap<FuncIndex, String>, pub func_names: HashMap<FuncIndex, String>,
/// Unprocessed signatures exactly as provided by `declare_signature()`.
pub signatures: PrimaryMap<SignatureIndex, WasmFuncType>,
/// Types declared in the wasm module. /// Types declared in the wasm module.
pub types: PrimaryMap<TypeIndex, ModuleType>, pub types: PrimaryMap<TypeIndex, ModuleType>,
/// Number of imported functions in the module. /// Number of imported or aliased functions in the module.
pub num_imported_funcs: usize, pub num_imported_funcs: usize,
/// Number of imported tables in the module. /// Number of imported or aliased tables in the module.
pub num_imported_tables: usize, pub num_imported_tables: usize,
/// Number of imported memories in the module. /// Number of imported or aliased memories in the module.
pub num_imported_memories: usize, pub num_imported_memories: usize,
/// Number of imported globals in the module. /// Number of imported or aliased globals in the module.
pub num_imported_globals: usize, pub num_imported_globals: usize,
/// Types of functions, imported and local. /// Types of functions, imported and local.
@@ -214,54 +200,58 @@ pub struct Module {
/// WebAssembly global variables. /// WebAssembly global variables.
pub globals: PrimaryMap<GlobalIndex, Global>, pub globals: PrimaryMap<GlobalIndex, Global>,
/// WebAssembly instances. /// The type of each wasm instance this module defines.
pub instances: PrimaryMap<InstanceIndex, Instance>, pub instances: PrimaryMap<InstanceIndex, InstanceTypeIndex>,
/// WebAssembly modules. /// The type of each nested wasm module this module contains.
pub modules: PrimaryMap<ModuleIndex, TypeIndex>, pub modules: PrimaryMap<ModuleIndex, ModuleTypeIndex>,
} }
/// Different forms an instance can take in a wasm module /// Initialization routines for creating an instance, encompassing imports,
/// modules, instances, aliases, etc.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Instance { pub enum Initializer {
/// This is an imported instance with the specified type /// An imported item is required to be provided.
Import(TypeIndex), Import {
/// This is a locally created instance which instantiates the specified /// Module name of this import
/// module with the given list of entities. module: String,
/// Optional field name of this import
field: Option<String>,
/// Where this import will be placed, which also has type information
/// about the import.
index: EntityIndex,
},
/// A module from the parent's declared modules is inserted into our own
/// index space.
AliasParentModule(ModuleIndex),
/// A module from the parent's declared modules is inserted into our own
/// index space.
#[allow(missing_docs)]
AliasInstanceExport {
instance: InstanceIndex,
export: usize,
},
/// A module is being instantiated with previously configured intializers
/// as arguments.
Instantiate { Instantiate {
/// The module that this instance is instantiating. /// The module that this instance is instantiating.
module: ModuleIndex, module: ModuleIndex,
/// The arguments provided to instantiation. /// The arguments provided to instantiation.
args: Vec<EntityIndex>, args: Vec<EntityIndex>,
}, },
/// A module is defined into the module index space, and which module is
/// being defined is specified by the index payload.
DefineModule(usize),
} }
impl Module { impl Module {
/// Allocates the module data structures. /// Allocates the module data structures.
pub fn new() -> Self { pub fn new() -> Self {
Self { Module::default()
id: Self::next_id(),
name: None,
imports: Vec::new(),
exports: IndexMap::new(),
start_func: None,
table_elements: Vec::new(),
passive_elements: HashMap::new(),
passive_data: HashMap::new(),
func_names: HashMap::new(),
num_imported_funcs: 0,
num_imported_tables: 0,
num_imported_memories: 0,
num_imported_globals: 0,
signatures: PrimaryMap::new(),
functions: PrimaryMap::new(),
table_plans: PrimaryMap::new(),
memory_plans: PrimaryMap::new(),
globals: PrimaryMap::new(),
instances: PrimaryMap::new(),
modules: PrimaryMap::new(),
types: PrimaryMap::new(),
}
} }
/// Get the given passive element, if it exists. /// Get the given passive element, if it exists.
@@ -269,11 +259,6 @@ impl Module {
self.passive_elements.get(&index).map(|es| &**es) self.passive_elements.get(&index).map(|es| &**es)
} }
fn next_id() -> usize {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
NEXT_ID.fetch_add(1, SeqCst)
}
/// Convert a `DefinedFuncIndex` into a `FuncIndex`. /// Convert a `DefinedFuncIndex` into a `FuncIndex`.
pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex { pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
FuncIndex::new(self.num_imported_funcs + defined_func.index()) FuncIndex::new(self.num_imported_funcs + defined_func.index())
@@ -361,18 +346,37 @@ impl Module {
pub fn is_imported_global(&self, index: GlobalIndex) -> bool { pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
index.index() < self.num_imported_globals index.index() < self.num_imported_globals
} }
/// Convenience method for looking up the original Wasm signature of a
/// function.
pub fn wasm_func_type(&self, func_index: FuncIndex) -> &WasmFuncType {
&self.signatures[self.functions[func_index]]
}
} }
impl Default for Module { /// All types which are recorded for the entirety of a translation.
fn default() -> Module { ///
Module::new() /// Note that this is shared amongst all modules coming out of a translation
/// in the case of nested modules and the module linking proposal.
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct TypeTables {
pub wasm_signatures: PrimaryMap<SignatureIndex, WasmFuncType>,
pub native_signatures: PrimaryMap<SignatureIndex, ir::Signature>,
pub module_signatures: PrimaryMap<ModuleTypeIndex, ModuleSignature>,
pub instance_signatures: PrimaryMap<InstanceTypeIndex, InstanceSignature>,
} }
/// The type signature of known modules.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleSignature {
/// All imports in this module, listed in order with their module/name and
/// what type they're importing.
pub imports: Vec<(String, Option<String>, EntityType)>,
/// Exports are what an instance type conveys, so we go through an
/// indirection over there.
pub exports: InstanceTypeIndex,
}
/// The type signature of known instances.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstanceSignature {
/// The name of what's being exported as well as its type signature.
pub exports: Vec<(String, EntityType)>,
} }
mod passive_data_serde { mod passive_data_serde {

View File

@@ -1,13 +1,17 @@
use crate::module::{Instance, MemoryPlan, Module, ModuleType, TableElements, TablePlan}; use crate::module::{
Initializer, InstanceSignature, MemoryPlan, Module, ModuleSignature, ModuleType, TableElements,
TablePlan, TypeTables,
};
use crate::tunables::Tunables; use crate::tunables::Tunables;
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_codegen::ir::{AbiParam, ArgumentPurpose}; use cranelift_codegen::ir::{AbiParam, ArgumentPurpose};
use cranelift_codegen::isa::TargetFrontendConfig; use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{ use cranelift_wasm::{
self, translate_module, DataIndex, DefinedFuncIndex, ElemIndex, EntityIndex, EntityType, self, translate_module, Alias, DataIndex, DefinedFuncIndex, ElemIndex, EntityIndex, EntityType,
FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, ModuleIndex, SignatureIndex, Table, FuncIndex, Global, GlobalIndex, InstanceIndex, InstanceTypeIndex, Memory, MemoryIndex,
TableIndex, TargetEnvironment, TypeIndex, WasmError, WasmFuncType, WasmResult, ModuleIndex, ModuleTypeIndex, SignatureIndex, Table, TableIndex, TargetEnvironment, TypeIndex,
WasmError, WasmFuncType, WasmResult,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
@@ -27,10 +31,11 @@ pub struct ModuleEnvironment<'data> {
/// the module linking proposal. /// the module linking proposal.
results: Vec<ModuleTranslation<'data>>, results: Vec<ModuleTranslation<'data>>,
/// Modules which are in-progress for being translated (our parents) and /// Intern'd types for this entire translation, shared by all modules.
/// we'll resume once we finish the current module. This is only applicable types: TypeTables,
/// with the module linking proposal.
in_progress: Vec<ModuleTranslation<'data>>, /// Where our module will get pushed into `results` after it's finished.
cur: usize,
// Various bits and pieces of configuration // Various bits and pieces of configuration
features: WasmFeatures, features: WasmFeatures,
@@ -46,9 +51,6 @@ pub struct ModuleTranslation<'data> {
/// Module information. /// Module information.
pub module: Module, pub module: Module,
/// Map of native signatures
pub native_signatures: PrimaryMap<SignatureIndex, ir::Signature>,
/// References to the function bodies. /// References to the function bodies.
pub function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>, pub function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
@@ -58,15 +60,23 @@ pub struct ModuleTranslation<'data> {
/// DWARF debug information, if enabled, parsed from the module. /// DWARF debug information, if enabled, parsed from the module.
pub debuginfo: DebugInfoData<'data>, pub debuginfo: DebugInfoData<'data>,
/// Indexes into the returned list of translations that are submodules of
/// this module.
pub submodules: PrimaryMap<ModuleIndex, usize>,
/// Set if debuginfo was found but it was not parsed due to `Tunables` /// Set if debuginfo was found but it was not parsed due to `Tunables`
/// configuration. /// configuration.
pub has_unparsed_debuginfo: bool, pub has_unparsed_debuginfo: bool,
/// When we're parsing the code section this will be incremented so we know
/// which function is currently being defined.
code_index: u32, code_index: u32,
/// When local modules are declared an entry is pushed onto this list which
/// indicates that the initializer at the specified position needs to be
/// rewritten with the module's final index in the global list of compiled
/// modules.
module_initializer_indexes: Vec<usize>,
/// Used as a pointer into the above list as the module code section is
/// parsed.
num_modules_defined: usize,
} }
/// Contains function data: byte code and its offset in the module. /// Contains function data: byte code and its offset in the module.
@@ -128,7 +138,8 @@ impl<'data> ModuleEnvironment<'data> {
Self { Self {
result: ModuleTranslation::default(), result: ModuleTranslation::default(),
results: Vec::with_capacity(1), results: Vec::with_capacity(1),
in_progress: Vec::new(), cur: 0,
types: Default::default(),
target_config, target_config,
tunables: tunables.clone(), tunables: tunables.clone(),
features: *features, features: *features,
@@ -139,12 +150,28 @@ impl<'data> ModuleEnvironment<'data> {
self.target_config.pointer_type() self.target_config.pointer_type()
} }
/// Translate a wasm module using this environment. This consumes the /// Translate a wasm module using this environment.
/// `ModuleEnvironment` and produces a `ModuleTranslation`. ///
pub fn translate(mut self, data: &'data [u8]) -> WasmResult<Vec<ModuleTranslation<'data>>> { /// This consumes the `ModuleEnvironment` and produces a list of
/// `ModuleTranslation`s as well as a `TypeTables`. The list of module
/// translations corresponds to all wasm modules found in the input `data`.
/// Note that for MVP modules this will always be a list with one element,
/// but with the module linking proposal this may have many elements.
///
/// For the module linking proposal the top-level module is at index 0.
///
/// The `TypeTables` structure returned contains intern'd versions of types
/// referenced from each module translation. This primarily serves as the
/// source of truth for module-linking use cases where modules can refer to
/// other module's types. All `SignatureIndex`, `ModuleTypeIndex`, and
/// `InstanceTypeIndex` values are resolved through the returned tables.
pub fn translate(
mut self,
data: &'data [u8],
) -> WasmResult<(Vec<ModuleTranslation<'data>>, TypeTables)> {
translate_module(data, &mut self)?; translate_module(data, &mut self)?;
assert!(self.results.len() > 0); assert!(self.results.len() > 0);
Ok(self.results) Ok((self.results, self.types))
} }
fn declare_export(&mut self, export: EntityIndex, name: &str) -> WasmResult<()> { fn declare_export(&mut self, export: EntityIndex, name: &str) -> WasmResult<()> {
@@ -209,16 +236,23 @@ impl<'data> TargetEnvironment for ModuleEnvironment<'data> {
impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data> { impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data> {
fn reserve_types(&mut self, num: u32) -> WasmResult<()> { fn reserve_types(&mut self, num: u32) -> WasmResult<()> {
let num = usize::try_from(num).unwrap(); let num = usize::try_from(num).unwrap();
self.result.module.types.reserve_exact(num); self.result.module.types.reserve(num);
self.result.native_signatures.reserve_exact(num); self.types.native_signatures.reserve(num);
self.types.wasm_signatures.reserve(num);
Ok(()) Ok(())
} }
fn declare_type_func(&mut self, wasm: WasmFuncType, sig: ir::Signature) -> WasmResult<()> { fn declare_type_func(&mut self, wasm: WasmFuncType, sig: ir::Signature) -> WasmResult<()> {
let sig = translate_signature(sig, self.pointer_type()); let sig = translate_signature(sig, self.pointer_type());
// TODO: Deduplicate signatures.
self.result.native_signatures.push(sig); // FIXME(#2469): Signatures should be deduplicated in these two tables
let sig_index = self.result.module.signatures.push(wasm); // since `SignatureIndex` is already a index space separate from the
// module's index space. Note that this may get more urgent with
// module-linking modules where types are more likely to get repeated
// (across modules).
let sig_index = self.types.native_signatures.push(sig);
let sig_index2 = self.types.wasm_signatures.push(wasm);
debug_assert_eq!(sig_index, sig_index2);
self.result self.result
.module .module
.types .types
@@ -239,10 +273,19 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
.iter() .iter()
.map(|e| (e.0.to_string(), e.1.clone())) .map(|e| (e.0.to_string(), e.1.clone()))
.collect(); .collect();
self.result
.module // FIXME(#2469): Like signatures above we should probably deduplicate
// the listings of module types since with module linking it's possible
// you'll need to write down the module type in multiple locations.
let exports = self
.types .types
.push(ModuleType::Module { imports, exports }); .instance_signatures
.push(InstanceSignature { exports });
let idx = self
.types
.module_signatures
.push(ModuleSignature { imports, exports });
self.result.module.types.push(ModuleType::Module(idx));
Ok(()) Ok(())
} }
@@ -251,19 +294,46 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
.iter() .iter()
.map(|e| (e.0.to_string(), e.1.clone())) .map(|e| (e.0.to_string(), e.1.clone()))
.collect(); .collect();
self.result
.module // FIXME(#2469): Like signatures above we should probably deduplicate
// the listings of instance types since with module linking it's
// possible you'll need to write down the module type in multiple
// locations.
let idx = self
.types .types
.push(ModuleType::Instance { exports }); .instance_signatures
.push(InstanceSignature { exports });
self.result.module.types.push(ModuleType::Instance(idx));
Ok(()) Ok(())
} }
fn type_to_signature(&self, index: TypeIndex) -> WasmResult<SignatureIndex> {
match self.result.module.types[index] {
ModuleType::Function(sig) => Ok(sig),
_ => unreachable!(),
}
}
fn type_to_module_type(&self, index: TypeIndex) -> WasmResult<ModuleTypeIndex> {
match self.result.module.types[index] {
ModuleType::Module(sig) => Ok(sig),
_ => unreachable!(),
}
}
fn type_to_instance_type(&self, index: TypeIndex) -> WasmResult<InstanceTypeIndex> {
match self.result.module.types[index] {
ModuleType::Instance(sig) => Ok(sig),
_ => unreachable!(),
}
}
fn reserve_imports(&mut self, num: u32) -> WasmResult<()> { fn reserve_imports(&mut self, num: u32) -> WasmResult<()> {
Ok(self Ok(self
.result .result
.module .module
.imports .initializers
.reserve_exact(usize::try_from(num).unwrap())) .reserve(usize::try_from(num).unwrap()))
} }
fn declare_func_import( fn declare_func_import(
@@ -279,11 +349,11 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
); );
let sig_index = self.result.module.types[index].unwrap_function(); let sig_index = self.result.module.types[index].unwrap_function();
let func_index = self.result.module.functions.push(sig_index); let func_index = self.result.module.functions.push(sig_index);
self.result.module.imports.push(( self.result.module.initializers.push(Initializer::Import {
module.to_owned(), module: module.to_owned(),
field.map(|s| s.to_owned()), field: field.map(|s| s.to_owned()),
EntityIndex::Function(func_index), index: EntityIndex::Function(func_index),
)); });
self.result.module.num_imported_funcs += 1; self.result.module.num_imported_funcs += 1;
self.result.debuginfo.wasm_file.imported_func_count += 1; self.result.debuginfo.wasm_file.imported_func_count += 1;
Ok(()) Ok(())
@@ -302,11 +372,11 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
); );
let plan = TablePlan::for_table(table, &self.tunables); let plan = TablePlan::for_table(table, &self.tunables);
let table_index = self.result.module.table_plans.push(plan); let table_index = self.result.module.table_plans.push(plan);
self.result.module.imports.push(( self.result.module.initializers.push(Initializer::Import {
module.to_owned(), module: module.to_owned(),
field.map(|s| s.to_owned()), field: field.map(|s| s.to_owned()),
EntityIndex::Table(table_index), index: EntityIndex::Table(table_index),
)); });
self.result.module.num_imported_tables += 1; self.result.module.num_imported_tables += 1;
Ok(()) Ok(())
} }
@@ -327,11 +397,11 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
} }
let plan = MemoryPlan::for_memory(memory, &self.tunables); let plan = MemoryPlan::for_memory(memory, &self.tunables);
let memory_index = self.result.module.memory_plans.push(plan); let memory_index = self.result.module.memory_plans.push(plan);
self.result.module.imports.push(( self.result.module.initializers.push(Initializer::Import {
module.to_owned(), module: module.to_owned(),
field.map(|s| s.to_owned()), field: field.map(|s| s.to_owned()),
EntityIndex::Memory(memory_index), index: EntityIndex::Memory(memory_index),
)); });
self.result.module.num_imported_memories += 1; self.result.module.num_imported_memories += 1;
Ok(()) Ok(())
} }
@@ -348,15 +418,47 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
"Imported globals must be declared first" "Imported globals must be declared first"
); );
let global_index = self.result.module.globals.push(global); let global_index = self.result.module.globals.push(global);
self.result.module.imports.push(( self.result.module.initializers.push(Initializer::Import {
module.to_owned(), module: module.to_owned(),
field.map(|s| s.to_owned()), field: field.map(|s| s.to_owned()),
EntityIndex::Global(global_index), index: EntityIndex::Global(global_index),
)); });
self.result.module.num_imported_globals += 1; self.result.module.num_imported_globals += 1;
Ok(()) Ok(())
} }
fn declare_module_import(
&mut self,
ty_index: TypeIndex,
module: &'data str,
field: Option<&'data str>,
) -> WasmResult<()> {
let signature = self.type_to_module_type(ty_index)?;
let module_index = self.result.module.modules.push(signature);
self.result.module.initializers.push(Initializer::Import {
module: module.to_owned(),
field: field.map(|s| s.to_owned()),
index: EntityIndex::Module(module_index),
});
Ok(())
}
fn declare_instance_import(
&mut self,
ty_index: TypeIndex,
module: &'data str,
field: Option<&'data str>,
) -> WasmResult<()> {
let signature = self.type_to_instance_type(ty_index)?;
let instance_index = self.result.module.instances.push(signature);
self.result.module.initializers.push(Initializer::Import {
module: module.to_owned(),
field: field.map(|s| s.to_owned()),
index: EntityIndex::Instance(instance_index),
});
Ok(())
}
fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> { fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> {
self.result self.result
.module .module
@@ -442,6 +544,14 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
self.declare_export(EntityIndex::Global(global_index), name) self.declare_export(EntityIndex::Global(global_index), name)
} }
fn declare_module_export(&mut self, index: ModuleIndex, name: &str) -> WasmResult<()> {
self.declare_export(EntityIndex::Module(index), name)
}
fn declare_instance_export(&mut self, index: InstanceIndex, name: &str) -> WasmResult<()> {
self.declare_export(EntityIndex::Instance(index), name)
}
fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> { fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
debug_assert!(self.result.module.start_func.is_none()); debug_assert!(self.result.module.start_func.is_none());
self.result.module.start_func = Some(func_index); self.result.module.start_func = Some(func_index);
@@ -503,7 +613,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
let func_index = self.result.code_index + self.result.module.num_imported_funcs as u32; let func_index = self.result.code_index + self.result.module.num_imported_funcs as u32;
let func_index = FuncIndex::from_u32(func_index); let func_index = FuncIndex::from_u32(func_index);
let sig_index = self.result.module.functions[func_index]; let sig_index = self.result.module.functions[func_index];
let sig = &self.result.module.signatures[sig_index]; let sig = &self.types.wasm_signatures[sig_index];
let mut locals = Vec::new(); let mut locals = Vec::new();
for pair in body.get_locals_reader()? { for pair in body.get_locals_reader()? {
locals.push(pair?); locals.push(pair?);
@@ -629,42 +739,159 @@ and for re-adding support for interface types you can see this issue:
} }
fn reserve_modules(&mut self, amount: u32) { fn reserve_modules(&mut self, amount: u32) {
// Go ahead and reserve space in the final `results` array for `amount`
// more modules.
let extra = self.results.capacity() + (amount as usize) - self.results.len(); let extra = self.results.capacity() + (amount as usize) - self.results.len();
self.results.reserve(extra); self.results.reserve(extra);
self.result.submodules.reserve(amount as usize);
// Then also reserve space in our own local module's metadata fields
// we'll be adding to.
self.result.module.modules.reserve(amount as usize);
self.result.module.initializers.reserve(amount as usize);
}
fn declare_module(&mut self, ty: TypeIndex) -> WasmResult<()> {
// Record the type signature of this module ...
let signature = self.type_to_module_type(ty)?;
self.result.module.modules.push(signature);
// ... and then record that in the initialization steps of this module
// we're inserting this module into the module index space. At this
// point we don't know the final index of the module we're defining, so
// we leave a placeholder to get rewritten later.
let loc = self.result.module.initializers.len();
self.result
.module
.initializers
.push(Initializer::DefineModule(usize::max_value()));
self.result.module_initializer_indexes.push(loc);
Ok(())
} }
fn module_start(&mut self, index: usize) { fn module_start(&mut self, index: usize) {
// skip the first module since `self.result` is already empty and we'll // Reset the contents of `self.result` for a new module that's getting
// be translating into that. // translataed.
let mut prev = mem::replace(&mut self.result, ModuleTranslation::default());
// If this is a nested submodule then we record the final destination of
// the child in parent (we store `index` into `prev`) in the appropriate
// initialization slot as dicated by `num_modules_defined` (our index of
// iteration through the code section).
// Record that the `num_modules_defined`-th module is defined at index
// by updating the initializer entry.
if index > 0 { if index > 0 {
let in_progress = mem::replace(&mut self.result, ModuleTranslation::default()); let initializer_idx = prev.module_initializer_indexes[prev.num_modules_defined];
self.in_progress.push(in_progress); prev.num_modules_defined += 1;
debug_assert!(match &prev.module.initializers[initializer_idx] {
Initializer::DefineModule(usize::MAX) => true,
_ => false,
});
prev.module.initializers[initializer_idx] = Initializer::DefineModule(index);
self.result.module.parent = Some(self.cur);
} }
// Update our current index counter and save our parent's translation
// where this current translation will end up, which we'll swap back as
// part of `module_end`.
self.cur = index;
assert_eq!(index, self.results.len());
self.results.push(prev);
} }
fn module_end(&mut self, index: usize) { fn module_end(&mut self, index: usize) {
let to_continue = match self.in_progress.pop() { assert!(self.result.num_modules_defined == self.result.module_initializer_indexes.len());
Some(m) => m,
None => { // Move our finished module into its final location, swapping it with
assert_eq!(index, 0); // what was this module's parent.
ModuleTranslation::default() self.cur = self.result.module.parent.unwrap_or(0);
} mem::swap(&mut self.result, &mut self.results[index]);
};
let finished = mem::replace(&mut self.result, to_continue);
self.result.submodules.push(self.results.len());
self.results.push(finished);
} }
fn reserve_instances(&mut self, amt: u32) { fn reserve_instances(&mut self, amt: u32) {
self.result.module.instances.reserve(amt as usize); self.result.module.instances.reserve(amt as usize);
self.result.module.initializers.reserve(amt as usize);
} }
fn declare_instance(&mut self, module: ModuleIndex, args: Vec<EntityIndex>) -> WasmResult<()> { fn declare_instance(&mut self, module: ModuleIndex, args: Vec<EntityIndex>) -> WasmResult<()> {
// Record the type of this instance with the type signature of the
// module we're instantiating and then also add an initializer which
// records that we'll be adding to the instance index space here.
let module_ty = self.result.module.modules[module];
let instance_ty = self.types.module_signatures[module_ty].exports;
self.result.module.instances.push(instance_ty);
self.result self.result
.module .module
.instances .initializers
.push(Instance::Instantiate { module, args }); .push(Initializer::Instantiate { module, args });
Ok(())
}
fn declare_alias(&mut self, alias: Alias) -> WasmResult<()> {
match alias {
// Types are easy, we statically know everything so we're just
// copying some pointers from our parent module to our own module.
//
// Note that we don't add an initializer for this alias because
// we statically know where all types point to.
Alias::ParentType(parent_idx) => {
let ty = self.results[self.cur].module.types[parent_idx];
self.result.module.types.push(ty);
}
// This is similar to types in that it's easy for us to record the
// type of the module that's being aliased, but we also need to add
// an initializer so during instantiation we can prepare the index
// space appropriately.
Alias::ParentModule(parent_idx) => {
let module_idx = self.results[self.cur].module.modules[parent_idx];
self.result.module.modules.push(module_idx);
self.result
.module
.initializers
.push(Initializer::AliasParentModule(parent_idx));
}
// This case is slightly more involved, we'll be recording all the
// type information for each kind of entity, and then we also need
// to record an initialization step to get the export from the
// instance.
Alias::Child { instance, export } => {
let ty = self.result.module.instances[instance];
match &self.types.instance_signatures[ty].exports[export].1 {
EntityType::Global(g) => {
self.result.module.globals.push(g.clone());
self.result.module.num_imported_globals += 1;
}
EntityType::Memory(mem) => {
let plan = MemoryPlan::for_memory(*mem, &self.tunables);
self.result.module.memory_plans.push(plan);
self.result.module.num_imported_memories += 1;
}
EntityType::Table(t) => {
let plan = TablePlan::for_table(*t, &self.tunables);
self.result.module.table_plans.push(plan);
self.result.module.num_imported_tables += 1;
}
EntityType::Function(sig) => {
self.result.module.functions.push(*sig);
self.result.module.num_imported_funcs += 1;
self.result.debuginfo.wasm_file.imported_func_count += 1;
}
EntityType::Instance(sig) => {
self.result.module.instances.push(*sig);
}
EntityType::Module(sig) => {
self.result.module.modules.push(*sig);
}
EntityType::Event(_) => unimplemented!(),
}
self.result
.module
.initializers
.push(Initializer::AliasInstanceExport { instance, export })
}
}
Ok(()) Ok(())
} }
} }

View File

@@ -24,7 +24,7 @@ use crate::BuiltinFunctionIndex;
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_wasm::{ use cranelift_wasm::{
DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, GlobalIndex, MemoryIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, GlobalIndex, MemoryIndex,
SignatureIndex, TableIndex, TableIndex, TypeIndex,
}; };
use more_asserts::assert_lt; use more_asserts::assert_lt;
use std::convert::TryFrom; use std::convert::TryFrom;
@@ -78,7 +78,7 @@ impl VMOffsets {
pub fn new(pointer_size: u8, module: &Module) -> Self { pub fn new(pointer_size: u8, module: &Module) -> Self {
Self { Self {
pointer_size, pointer_size,
num_signature_ids: cast_to_u32(module.signatures.len()), num_signature_ids: cast_to_u32(module.types.len()),
num_imported_functions: cast_to_u32(module.num_imported_funcs), num_imported_functions: cast_to_u32(module.num_imported_funcs),
num_imported_tables: cast_to_u32(module.num_imported_tables), num_imported_tables: cast_to_u32(module.num_imported_tables),
num_imported_memories: cast_to_u32(module.num_imported_memories), num_imported_memories: cast_to_u32(module.num_imported_memories),
@@ -430,7 +430,7 @@ impl VMOffsets {
} }
/// Return the offset to `VMSharedSignatureId` index `index`. /// Return the offset to `VMSharedSignatureId` index `index`.
pub fn vmctx_vmshared_signature_id(&self, index: SignatureIndex) -> u32 { pub fn vmctx_vmshared_signature_id(&self, index: TypeIndex) -> u32 {
assert_lt!(index.as_u32(), self.num_signature_ids); assert_lt!(index.as_u32(), self.num_signature_ids);
self.vmctx_signature_ids_begin() self.vmctx_signature_ids_begin()
.checked_add( .checked_add(

View File

@@ -14,7 +14,7 @@ use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
use wasmtime_environ::wasm::{DefinedMemoryIndex, MemoryIndex}; use wasmtime_environ::wasm::{DefinedMemoryIndex, MemoryIndex};
use wasmtime_environ::{ use wasmtime_environ::{
CompiledFunctions, Compiler as EnvCompiler, DebugInfoData, Module, ModuleMemoryOffset, CompiledFunctions, Compiler as EnvCompiler, DebugInfoData, Module, ModuleMemoryOffset,
ModuleTranslation, Tunables, VMOffsets, ModuleTranslation, Tunables, TypeTables, VMOffsets,
}; };
/// Select which kind of compilation to use. /// Select which kind of compilation to use.
@@ -127,13 +127,20 @@ impl Compiler {
pub fn compile<'data>( pub fn compile<'data>(
&self, &self,
translation: &mut ModuleTranslation, translation: &mut ModuleTranslation,
types: &TypeTables,
) -> Result<Compilation, SetupError> { ) -> Result<Compilation, SetupError> {
let functions = mem::take(&mut translation.function_body_inputs); let functions = mem::take(&mut translation.function_body_inputs);
let functions = functions.into_iter().collect::<Vec<_>>(); let functions = functions.into_iter().collect::<Vec<_>>();
let funcs = maybe_parallel!(functions.(into_iter | into_par_iter)) let funcs = maybe_parallel!(functions.(into_iter | into_par_iter))
.map(|(index, func)| { .map(|(index, func)| {
self.compiler self.compiler.compile_function(
.compile_function(translation, index, func, &*self.isa, &self.tunables) translation,
index,
func,
&*self.isa,
&self.tunables,
types,
)
}) })
.collect::<Result<Vec<_>, _>>()? .collect::<Result<Vec<_>, _>>()?
.into_iter() .into_iter()
@@ -150,7 +157,8 @@ impl Compiler {
vec![] vec![]
}; };
let (obj, unwind_info) = build_object(&*self.isa, &translation, &funcs, dwarf_sections)?; let (obj, unwind_info) =
build_object(&*self.isa, &translation, types, &funcs, dwarf_sections)?;
Ok(Compilation { Ok(Compilation {
obj, obj,

View File

@@ -18,10 +18,13 @@ use thiserror::Error;
use wasmtime_debug::create_gdbjit_image; use wasmtime_debug::create_gdbjit_image;
use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::isa::TargetIsa; use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::wasm::{DefinedFuncIndex, ModuleIndex, SignatureIndex}; use wasmtime_environ::wasm::{
DefinedFuncIndex, InstanceTypeIndex, ModuleTypeIndex, SignatureIndex, WasmFuncType,
};
use wasmtime_environ::{ use wasmtime_environ::{
CompileError, DataInitializer, DataInitializerLocation, DebugInfoData, FunctionAddressMap, CompileError, DataInitializer, DataInitializerLocation, DebugInfoData, FunctionAddressMap,
Module, ModuleEnvironment, ModuleTranslation, StackMapInformation, TrapInformation, InstanceSignature, Module, ModuleEnvironment, ModuleSignature, ModuleTranslation,
StackMapInformation, TrapInformation,
}; };
use wasmtime_profiling::ProfilingAgent; use wasmtime_profiling::ProfilingAgent;
use wasmtime_runtime::{ use wasmtime_runtime::{
@@ -70,10 +73,6 @@ pub struct CompilationArtifacts {
/// Descriptions of compiled functions /// Descriptions of compiled functions
funcs: PrimaryMap<DefinedFuncIndex, FunctionInfo>, funcs: PrimaryMap<DefinedFuncIndex, FunctionInfo>,
/// Where to find this module's submodule code in the top-level list of
/// modules.
submodules: PrimaryMap<ModuleIndex, usize>,
/// Whether or not native debug information is available in `obj` /// Whether or not native debug information is available in `obj`
native_debug_info_present: bool, native_debug_info_present: bool,
@@ -106,8 +105,8 @@ impl CompilationArtifacts {
pub fn build( pub fn build(
compiler: &Compiler, compiler: &Compiler,
data: &[u8], data: &[u8],
) -> Result<Vec<CompilationArtifacts>, SetupError> { ) -> Result<(Vec<CompilationArtifacts>, TypeTables), SetupError> {
let translations = ModuleEnvironment::new( let (translations, types) = ModuleEnvironment::new(
compiler.frontend_config(), compiler.frontend_config(),
compiler.tunables(), compiler.tunables(),
compiler.features(), compiler.features(),
@@ -115,18 +114,17 @@ impl CompilationArtifacts {
.translate(data) .translate(data)
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?; .map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
maybe_parallel!(translations.(into_iter | into_par_iter)) let list = maybe_parallel!(translations.(into_iter | into_par_iter))
.map(|mut translation| { .map(|mut translation| {
let Compilation { let Compilation {
obj, obj,
unwind_info, unwind_info,
funcs, funcs,
} = compiler.compile(&mut translation)?; } = compiler.compile(&mut translation, &types)?;
let ModuleTranslation { let ModuleTranslation {
module, module,
data_initializers, data_initializers,
submodules,
debuginfo, debuginfo,
has_unparsed_debuginfo, has_unparsed_debuginfo,
.. ..
@@ -149,7 +147,6 @@ impl CompilationArtifacts {
obj: obj.into_boxed_slice(), obj: obj.into_boxed_slice(),
unwind_info: unwind_info.into_boxed_slice(), unwind_info: unwind_info.into_boxed_slice(),
data_initializers, data_initializers,
submodules,
funcs: funcs funcs: funcs
.into_iter() .into_iter()
.map(|(_, func)| FunctionInfo { .map(|(_, func)| FunctionInfo {
@@ -167,11 +164,21 @@ impl CompilationArtifacts {
has_unparsed_debuginfo, has_unparsed_debuginfo,
}) })
}) })
.collect::<Result<Vec<_>, SetupError>>() .collect::<Result<Vec<_>, SetupError>>()?;
Ok((
list,
TypeTables {
wasm_signatures: types.wasm_signatures,
module_signatures: types.module_signatures,
instance_signatures: types.instance_signatures,
},
))
} }
} }
struct FinishedFunctions(PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>); struct FinishedFunctions(PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>);
unsafe impl Send for FinishedFunctions {}
unsafe impl Sync for FinishedFunctions {}
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
struct FunctionInfo { struct FunctionInfo {
@@ -180,8 +187,15 @@ struct FunctionInfo {
stack_maps: Vec<StackMapInformation>, stack_maps: Vec<StackMapInformation>,
} }
unsafe impl Send for FinishedFunctions {} /// This is intended to mirror the type tables in `wasmtime_environ`, except that
unsafe impl Sync for FinishedFunctions {} /// it doesn't store the native signatures which are no longer needed past compilation.
#[derive(Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct TypeTables {
pub wasm_signatures: PrimaryMap<SignatureIndex, WasmFuncType>,
pub module_signatures: PrimaryMap<ModuleTypeIndex, ModuleSignature>,
pub instance_signatures: PrimaryMap<InstanceTypeIndex, InstanceSignature>,
}
/// Container for data needed for an Instance function to exist. /// Container for data needed for an Instance function to exist.
pub struct ModuleCode { pub struct ModuleCode {
@@ -375,12 +389,6 @@ impl CompiledModule {
&self.code &self.code
} }
/// Returns where the specified submodule lives in this module's
/// array-of-modules (store at the top-level)
pub fn submodule_idx(&self, idx: ModuleIndex) -> usize {
self.artifacts.submodules[idx]
}
/// Creates a new symbolication context which can be used to further /// Creates a new symbolication context which can be used to further
/// symbolicate stack traces. /// symbolicate stack traces.
/// ///

View File

@@ -47,7 +47,7 @@ pub mod trampoline;
pub use crate::code_memory::CodeMemory; pub use crate::code_memory::CodeMemory;
pub use crate::compiler::{Compilation, CompilationStrategy, Compiler}; pub use crate::compiler::{Compilation, CompilationStrategy, Compiler};
pub use crate::instantiate::{ pub use crate::instantiate::{
CompilationArtifacts, CompiledModule, ModuleCode, SetupError, SymbolizeContext, CompilationArtifacts, CompiledModule, ModuleCode, SetupError, SymbolizeContext, TypeTables,
}; };
pub use crate::link::link_module; pub use crate::link::link_module;

View File

@@ -8,7 +8,7 @@ use wasmtime_debug::DwarfSection;
use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa}; use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
use wasmtime_environ::wasm::{FuncIndex, SignatureIndex}; use wasmtime_environ::wasm::{FuncIndex, SignatureIndex};
use wasmtime_environ::{CompiledFunctions, ModuleTranslation}; use wasmtime_environ::{CompiledFunctions, ModuleTranslation, TypeTables};
use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget}; use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget};
pub use wasmtime_obj::utils; pub use wasmtime_obj::utils;
@@ -24,6 +24,7 @@ pub enum ObjectUnwindInfo {
pub(crate) fn build_object( pub(crate) fn build_object(
isa: &dyn TargetIsa, isa: &dyn TargetIsa,
translation: &ModuleTranslation, translation: &ModuleTranslation,
types: &TypeTables,
funcs: &CompiledFunctions, funcs: &CompiledFunctions,
dwarf_sections: Vec<DwarfSection>, dwarf_sections: Vec<DwarfSection>,
) -> Result<(Object, Vec<ObjectUnwindInfo>), anyhow::Error> { ) -> Result<(Object, Vec<ObjectUnwindInfo>), anyhow::Error> {
@@ -38,10 +39,16 @@ pub(crate) fn build_object(
.map(|info| ObjectUnwindInfo::Func(translation.module.func_index(index), info.clone())) .map(|info| ObjectUnwindInfo::Func(translation.module.func_index(index), info.clone()))
})); }));
let mut trampolines = PrimaryMap::with_capacity(translation.module.signatures.len()); let mut trampolines = PrimaryMap::with_capacity(types.native_signatures.len());
let mut cx = FunctionBuilderContext::new(); let mut cx = FunctionBuilderContext::new();
// Build trampolines for every signature. // Build trampolines for every signature.
for (i, native_sig) in translation.native_signatures.iter() { //
// TODO: for the module linking proposal this builds too many native
// signatures. This builds trampolines for all signatures for all modules
// for each module. That's a lot of trampolines! We should instead figure
// out a way to share trampolines amongst all modules when compiling
// module-linking modules.
for (i, native_sig) in types.native_signatures.iter() {
let func = build_trampoline(isa, &mut cx, native_sig, std::mem::size_of::<u128>())?; let func = build_trampoline(isa, &mut cx, native_sig, std::mem::size_of::<u128>())?;
// Preserve trampoline function unwind info. // Preserve trampoline function unwind info.
if let Some(info) = &func.unwind_info { if let Some(info) = &func.unwind_info {

View File

@@ -9,12 +9,12 @@ use cranelift_codegen::isa;
use lightbeam::{CodeGenSession, NullOffsetSink, Sinks}; use lightbeam::{CodeGenSession, NullOffsetSink, Sinks};
use wasmtime_environ::wasm::{ use wasmtime_environ::wasm::{
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex,
GlobalIndex, MemoryIndex, SignatureIndex, TableIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex, TypeIndex,
}; };
use wasmtime_environ::{ use wasmtime_environ::{
entity::PrimaryMap, BuiltinFunctionIndex, CompileError, CompiledFunction, Compiler, entity::PrimaryMap, BuiltinFunctionIndex, CompileError, CompiledFunction, Compiler,
FunctionBodyData, Module, ModuleTranslation, Relocation, RelocationTarget, TrapInformation, FunctionBodyData, Module, ModuleTranslation, Relocation, RelocationTarget, TrapInformation,
Tunables, VMOffsets, Tunables, TypeTables, VMOffsets,
}; };
/// A compiler that compiles a WebAssembly module with Lightbeam, directly translating the Wasm file. /// A compiler that compiles a WebAssembly module with Lightbeam, directly translating the Wasm file.
@@ -28,13 +28,14 @@ impl Compiler for Lightbeam {
function_body: FunctionBodyData<'_>, function_body: FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
tunables: &Tunables, tunables: &Tunables,
types: &TypeTables,
) -> Result<CompiledFunction, CompileError> { ) -> Result<CompiledFunction, CompileError> {
if tunables.generate_native_debuginfo { if tunables.generate_native_debuginfo {
return Err(CompileError::DebugInfoNotSupported); return Err(CompileError::DebugInfoNotSupported);
} }
let func_index = translation.module.func_index(i); let func_index = translation.module.func_index(i);
let env = FuncEnvironment::new(isa.frontend_config().pointer_bytes(), translation); let env = FuncEnvironment::new(isa.frontend_config().pointer_bytes(), translation, types);
let mut codegen_session: CodeGenSession<_> = CodeGenSession::new( let mut codegen_session: CodeGenSession<_> = CodeGenSession::new(
translation.function_body_inputs.len() as u32, translation.function_body_inputs.len() as u32,
&env, &env,
@@ -180,11 +181,15 @@ struct FuncEnvironment<'module_environment> {
} }
impl<'module_environment> FuncEnvironment<'module_environment> { impl<'module_environment> FuncEnvironment<'module_environment> {
fn new(pointer_bytes: u8, translation: &'module_environment ModuleTranslation<'_>) -> Self { fn new(
pointer_bytes: u8,
translation: &'module_environment ModuleTranslation<'_>,
types: &'module_environment TypeTables,
) -> Self {
Self { Self {
module: &translation.module, module: &translation.module,
offsets: VMOffsets::new(pointer_bytes, &translation.module), offsets: VMOffsets::new(pointer_bytes, &translation.module),
native_signatures: &translation.native_signatures, native_signatures: &types.native_signatures,
} }
} }
} }
@@ -322,7 +327,7 @@ impl lightbeam::ModuleContext for FuncEnvironment<'_> {
} }
fn vmctx_vmshared_signature_id(&self, signature_idx: u32) -> u32 { fn vmctx_vmshared_signature_id(&self, signature_idx: u32) -> u32 {
self.offsets self.offsets
.vmctx_vmshared_signature_id(SignatureIndex::from_u32(signature_idx)) .vmctx_vmshared_signature_id(TypeIndex::from_u32(signature_idx))
} }
// TODO: type of a global // TODO: type of a global

View File

@@ -7,7 +7,7 @@ use std::ptr;
use wasmtime_environ::entity::EntityRef; use wasmtime_environ::entity::EntityRef;
use wasmtime_environ::isa::TargetFrontendConfig; use wasmtime_environ::isa::TargetFrontendConfig;
use wasmtime_environ::wasm::GlobalInit; use wasmtime_environ::wasm::GlobalInit;
use wasmtime_environ::{Module, TargetSharedSignatureIndex, VMOffsets}; use wasmtime_environ::{Module, ModuleType, TargetSharedSignatureIndex, VMOffsets};
pub struct TableRelocation { pub struct TableRelocation {
pub index: usize, pub index: usize,
@@ -25,9 +25,10 @@ pub fn layout_vmcontext(
// Assign unique indices to unique signatures. // Assign unique indices to unique signatures.
let mut signature_registry = HashMap::new(); let mut signature_registry = HashMap::new();
let mut signature_registry_len = signature_registry.len(); let mut signature_registry_len = signature_registry.len();
for (index, sig) in module.signatures.iter() { for (index, sig) in module.types.iter() {
let offset = ofs.vmctx_vmshared_signature_id(index) as usize; let offset = ofs.vmctx_vmshared_signature_id(index) as usize;
let target_index = match signature_registry.entry(sig) { let target_index = match sig {
ModuleType::Function(sig) => match signature_registry.entry(sig) {
Entry::Occupied(o) => *o.get(), Entry::Occupied(o) => *o.get(),
Entry::Vacant(v) => { Entry::Vacant(v) => {
assert_le!(signature_registry_len, std::u32::MAX as usize); assert_le!(signature_registry_len, std::u32::MAX as usize);
@@ -35,6 +36,8 @@ pub fn layout_vmcontext(
signature_registry_len += 1; signature_registry_len += 1;
*v.insert(id) *v.insert(id)
} }
},
_ => TargetSharedSignatureIndex::new(u32::max_value()),
}; };
unsafe { unsafe {
let to = out.as_mut_ptr().add(offset) as *mut TargetSharedSignatureIndex; let to = out.as_mut_ptr().add(offset) as *mut TargetSharedSignatureIndex;

View File

@@ -31,7 +31,7 @@ use wasmtime_environ::wasm::{
ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex,
TableElementType, TableIndex, WasmType, TableElementType, TableIndex, WasmType,
}; };
use wasmtime_environ::{ir, DataInitializer, Module, TableElements, VMOffsets}; use wasmtime_environ::{ir, DataInitializer, Module, ModuleType, TableElements, VMOffsets};
/// A WebAssembly instance. /// A WebAssembly instance.
/// ///
@@ -78,12 +78,6 @@ impl Instance {
.cast() .cast()
} }
/// Return the indexed `VMSharedSignatureIndex`.
fn signature_id(&self, index: SignatureIndex) -> VMSharedSignatureIndex {
let index = usize::try_from(index.as_u32()).unwrap();
unsafe { *self.signature_ids_ptr().add(index) }
}
pub(crate) fn module(&self) -> &Module { pub(crate) fn module(&self) -> &Module {
&self.module &self.module
} }
@@ -868,8 +862,11 @@ impl InstanceHandle {
let instance = handle.instance(); let instance = handle.instance();
let mut ptr = instance.signature_ids_ptr(); let mut ptr = instance.signature_ids_ptr();
for (signature, _) in handle.module().signatures.iter() { for sig in handle.module().types.values() {
*ptr = lookup_shared_signature(signature); *ptr = match sig {
ModuleType::Function(sig) => lookup_shared_signature(*sig),
_ => VMSharedSignatureIndex::new(u32::max_value()),
};
ptr = ptr.add(1); ptr = ptr.add(1);
} }
@@ -924,7 +921,7 @@ impl InstanceHandle {
*instance.stack_map_registry() = stack_map_registry; *instance.stack_map_registry() = stack_map_registry;
for (index, sig) in instance.module.functions.iter() { for (index, sig) in instance.module.functions.iter() {
let type_index = instance.signature_id(*sig); let type_index = lookup_shared_signature(*sig);
let (func_ptr, vmctx) = let (func_ptr, vmctx) =
if let Some(def_index) = instance.module.defined_func_index(index) { if let Some(def_index) = instance.module.defined_func_index(index) {

View File

@@ -115,6 +115,15 @@ impl Extern {
}; };
Store::same(my_store, store) Store::same(my_store, store)
} }
pub(crate) fn desc(&self) -> &'static str {
match self {
Extern::Func(_) => "function",
Extern::Table(_) => "table",
Extern::Memory(_) => "memory",
Extern::Global(_) => "global",
}
}
} }
impl From<Func> for Extern { impl From<Func> for Extern {

View File

@@ -1,74 +1,158 @@
use crate::trampoline::StoreInstanceHandle; use crate::trampoline::StoreInstanceHandle;
use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap}; use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
use anyhow::{bail, Error, Result}; use anyhow::{bail, Context, Error, Result};
use std::mem; use std::mem;
use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::{EntityIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex}; use wasmtime_environ::wasm::{
use wasmtime_jit::CompiledModule; EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex,
};
use wasmtime_environ::Initializer;
use wasmtime_jit::{CompiledModule, TypeTables};
use wasmtime_runtime::{ use wasmtime_runtime::{
Imports, InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable, Imports, InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable,
VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport,
}; };
fn instantiate( /// Performs all low-level steps necessary for instantiation.
store: &Store, ///
compiled_module: &CompiledModule, /// This function will take all the arguments and attempt to do everything
all_modules: &[CompiledModule], /// necessary to instantiate the referenced instance. The trickiness of this
imports: &mut ImportsBuilder<'_>, /// function stems from the implementation of the module-linking proposal where
/// we're handling nested instances, interleaved imports/aliases, etc. That's
/// all an internal implementation here ideally though!
///
/// * `store` - the store we're instantiating into
/// * `compiled_module` - the module that we're instantiating
/// * `all_modules` - the list of all modules that were part of the compilation
/// of `compiled_module`. This is only applicable in the module linking
/// proposal, otherwise this will just be a list containing `compiled_module`
/// itself.
/// * `type` - the type tables produced during compilation which
/// `compiled_module`'s metadata references.
/// * `parent_modules` - this is the list of compiled modules the parent has.
/// This is only applicable on recursive instantiations.
/// * `define_import` - this function, like the name implies, defines an import
/// into the provided builder. The expected entity that it's defining is also
/// passed in for the top-level case where type-checking is performed. This is
/// fallible because type checks may fail.
fn instantiate<'a>(
store: &'a Store,
compiled_module: &'a CompiledModule,
all_modules: &'a [CompiledModule],
types: &'a TypeTables,
parent_modules: &PrimaryMap<ModuleIndex, &'a CompiledModule>,
define_import: &mut dyn FnMut(&EntityIndex, &mut ImportsBuilder<'a>) -> Result<()>,
) -> Result<StoreInstanceHandle, Error> { ) -> Result<StoreInstanceHandle, Error> {
let env_module = compiled_module.module(); let env_module = compiled_module.module();
// The first part of instantiating any module is to first follow any let mut imports = ImportsBuilder::new(env_module, types, store);
// `instantiate` instructions it has as part of the module linking for initializer in env_module.initializers.iter() {
// proposal. Here we iterate overall those instructions and create the match initializer {
// instances as necessary. // Definition of an import depends on how our parent is providing
for instance in env_module.instances.values() { // imports, so we delegate to our custom closure. This will resolve
let (module_idx, args) = match instance { // to fetching from the import list for the top-level module and
wasmtime_environ::Instance::Instantiate { module, args } => (*module, args), // otherwise fetching from each nested instance's argument list for
wasmtime_environ::Instance::Import(_) => continue, // submodules.
}; Initializer::Import {
// Translate the `module_idx` to a top-level module `usize` and then index,
// use that to extract the child `&CompiledModule` itself. Then we can module,
// iterate over each of the arguments provided to satisfy its imports. field,
} => {
define_import(index, &mut imports).with_context(|| match field {
Some(name) => format!("incompatible import type for `{}::{}`", module, name),
None => format!("incompatible import type for `{}`", module),
})?;
}
// This one's pretty easy, we're just picking up our parent's module
// and putting it into our own index space.
Initializer::AliasParentModule(idx) => {
imports.modules.push(parent_modules[*idx]);
}
// Turns out defining any kind of module is pretty easy, we're just
// slinging around pointers.
Initializer::DefineModule(idx) => {
imports.modules.push(&all_modules[*idx]);
}
// Here we lookup our instance handle, ask it for the nth export,
// and then push that item into our own index space. We eschew
// type-checking since only valid modules reach this point.
Initializer::AliasInstanceExport { instance, export } => {
let handle = &imports.instances[*instance];
let export_index = &handle.module().exports[*export];
let item = Extern::from_wasmtime_export(
handle.lookup_by_declaration(export_index),
handle.clone(),
);
imports.push_extern(&item);
}
// Oh boy a recursive instantiation! The recursive arguments here
// are pretty simple, and the only slightly-meaty one is how
// arguments are pulled from `args` and pushed directly into the
// builder specified, which should be an easy enough
// copy-the-pointer operation in all cases.
// //
// Note that we directly reach into `imports` below based on indexes // Note that this recursive call shouldn't result in an infinite
// and push raw value into how to instantiate our submodule. This should // loop because of wasm module validation which requires everything
// be safe due to wasm validation ensuring that all our indices are // to be a DAG. Additionally the recursion should also be bounded
// in-bounds and all the expected types and such line up. // due to validation. We may one day need to make this an iterative
let module_idx = compiled_module.submodule_idx(module_idx); // loop, however.
let compiled_module = &all_modules[module_idx]; Initializer::Instantiate { module, args } => {
let mut builder = ImportsBuilder::new(compiled_module.module(), store); let module_to_instantiate = imports.modules[*module];
for arg in args { let mut args = args.iter();
match *arg { let handle = instantiate(
store,
module_to_instantiate,
all_modules,
types,
&imports.modules,
&mut |_, builder| {
match *args.next().unwrap() {
EntityIndex::Global(i) => { EntityIndex::Global(i) => {
builder.globals.push(imports.globals[i]); builder.globals.push(imports.globals[i]);
} }
EntityIndex::Table(i) => {
builder.tables.push(imports.tables[i]);
}
EntityIndex::Function(i) => { EntityIndex::Function(i) => {
builder.functions.push(imports.functions[i]); builder.functions.push(imports.functions[i]);
} }
EntityIndex::Table(i) => {
builder.tables.push(imports.tables[i]);
}
EntityIndex::Memory(i) => { EntityIndex::Memory(i) => {
builder.memories.push(imports.memories[i]); builder.memories.push(imports.memories[i]);
} }
EntityIndex::Module(_) => unimplemented!(), EntityIndex::Module(i) => {
EntityIndex::Instance(_) => unimplemented!(), builder.modules.push(imports.modules[i]);
}
EntityIndex::Instance(i) => {
builder.instances.push(imports.instances[i].clone());
} }
} }
instantiate(store, compiled_module, all_modules, &mut builder)?; Ok(())
},
)?;
imports.instances.push(handle);
} }
}
}
// With the above initialization done we've now acquired the final set of
// imports in all the right index spaces and everything. Time to carry on
// with the creation of our own instance.
let imports = imports.imports();
// Register the module just before instantiation to ensure we have a // Register the module just before instantiation to ensure we have a
// trampoline registered for every signature and to preserve the module's // trampoline registered for every signature and to preserve the module's
// compiled JIT code within the `Store`. // compiled JIT code within the `Store`.
store.register_module(compiled_module); store.register_module(compiled_module, types);
let config = store.engine().config(); let config = store.engine().config();
let instance = unsafe { let instance = unsafe {
let instance = compiled_module.instantiate( let instance = compiled_module.instantiate(
imports.imports(), imports,
&store.lookup_shared_signature(compiled_module.module()), &store.lookup_shared_signature(types),
config.memory_creator.as_ref().map(|a| a as _), config.memory_creator.as_ref().map(|a| a as _),
store.interrupts(), store.interrupts(),
Box::new(()), Box::new(()),
@@ -208,26 +292,38 @@ impl Instance {
bail!("cross-`Engine` instantiation is not currently supported"); bail!("cross-`Engine` instantiation is not currently supported");
} }
let mut builder = ImportsBuilder::new(module.compiled_module().module(), store); // Perform some pre-flight checks before we get into the meat of
// instantiation.
let expected = module
.compiled_module()
.module()
.initializers
.iter()
.filter(|e| match e {
Initializer::Import { .. } => true,
_ => false,
})
.count();
if expected != imports.len() {
bail!("expected {} imports, found {}", expected, imports.len());
}
for import in imports { for import in imports {
// For now we have a restriction that the `Store` that we're working
// with is the same for everything involved here.
if !import.comes_from_same_store(store) { if !import.comes_from_same_store(store) {
bail!("cross-`Store` instantiation is not currently supported"); bail!("cross-`Store` instantiation is not currently supported");
} }
match import {
Extern::Global(e) => builder.global(e)?,
Extern::Func(e) => builder.func(e)?,
Extern::Table(e) => builder.table(e)?,
Extern::Memory(e) => builder.memory(e)?,
} }
}
builder.validate_all_imports_provided()?; let mut imports = imports.iter();
let handle = instantiate( let handle = instantiate(
store, store,
module.compiled_module(), module.compiled_module(),
&module.compiled, module.all_compiled_modules(),
&mut builder, module.types(),
&PrimaryMap::new(),
&mut |idx, builder| {
let import = imports.next().expect("already checked the length");
builder.define_extern(idx, import)
},
)?; )?;
Ok(Instance { Ok(Instance {
@@ -304,113 +400,105 @@ struct ImportsBuilder<'a> {
tables: PrimaryMap<TableIndex, VMTableImport>, tables: PrimaryMap<TableIndex, VMTableImport>,
memories: PrimaryMap<MemoryIndex, VMMemoryImport>, memories: PrimaryMap<MemoryIndex, VMMemoryImport>,
globals: PrimaryMap<GlobalIndex, VMGlobalImport>, globals: PrimaryMap<GlobalIndex, VMGlobalImport>,
instances: PrimaryMap<InstanceIndex, StoreInstanceHandle>,
modules: PrimaryMap<ModuleIndex, &'a CompiledModule>,
module: &'a wasmtime_environ::Module, module: &'a wasmtime_environ::Module,
imports: std::slice::Iter<'a, (String, Option<String>, EntityIndex)>,
store: &'a Store, store: &'a Store,
types: &'a TypeTables,
} }
impl<'a> ImportsBuilder<'a> { impl<'a> ImportsBuilder<'a> {
fn new(module: &'a wasmtime_environ::Module, store: &'a Store) -> ImportsBuilder<'a> { fn new(
module: &'a wasmtime_environ::Module,
types: &'a TypeTables,
store: &'a Store,
) -> ImportsBuilder<'a> {
ImportsBuilder { ImportsBuilder {
imports: module.imports.iter(),
module, module,
store, store,
types,
functions: PrimaryMap::with_capacity(module.num_imported_funcs), functions: PrimaryMap::with_capacity(module.num_imported_funcs),
tables: PrimaryMap::with_capacity(module.num_imported_tables), tables: PrimaryMap::with_capacity(module.num_imported_tables),
memories: PrimaryMap::with_capacity(module.num_imported_memories), memories: PrimaryMap::with_capacity(module.num_imported_memories),
globals: PrimaryMap::with_capacity(module.num_imported_globals), globals: PrimaryMap::with_capacity(module.num_imported_globals),
instances: PrimaryMap::with_capacity(module.instances.len()),
modules: PrimaryMap::with_capacity(module.modules.len()),
} }
} }
fn next_import( fn define_extern(&mut self, expected: &EntityIndex, actual: &Extern) -> Result<()> {
&mut self, match *expected {
found: &str, EntityIndex::Table(i) => {
get: impl FnOnce(&wasmtime_environ::Module, &EntityIndex) -> Option<bool>, self.tables.push(match actual {
) -> Result<()> { Extern::Table(e) if e.matches_expected(&self.module.table_plans[i]) => {
match self.imports.next() { e.vmimport()
Some((module, field, idx)) => {
let error = match get(self.module, idx) {
Some(true) => return Ok(()),
Some(false) => {
anyhow::anyhow!("{} types incompatible", found)
} }
None => { Extern::Table(_) => bail!("table types incompatible"),
let desc = match idx { _ => bail!("expected table, but found {}", actual.desc()),
EntityIndex::Table(_) => "table", });
EntityIndex::Function(_) => "func", }
EntityIndex::Memory(_) => "memory", EntityIndex::Memory(i) => {
EntityIndex::Global(_) => "global", self.memories.push(match actual {
EntityIndex::Instance(_) => "instance", Extern::Memory(e) if e.matches_expected(&self.module.memory_plans[i]) => {
EntityIndex::Module(_) => "module", e.vmimport()
}
Extern::Memory(_) => bail!("memory types incompatible"),
_ => bail!("expected memory, but found {}", actual.desc()),
});
}
EntityIndex::Global(i) => {
self.globals.push(match actual {
Extern::Global(e) if e.matches_expected(&self.module.globals[i]) => {
e.vmimport()
}
Extern::Global(_) => bail!("global types incompatible"),
_ => bail!("expected global, but found {}", actual.desc()),
});
}
EntityIndex::Function(i) => {
let func = match actual {
Extern::Func(e) => e,
_ => bail!("expected function, but found {}", actual.desc()),
}; };
anyhow::anyhow!("expected {}, but found {}", desc, found)
}
};
let import_name = match field {
Some(name) => format!("{}/{}", module, name),
None => module.to_string(),
};
Err(error.context(format!("incompatible import type for {}", import_name)))
}
None => bail!("too many imports provided"),
}
}
fn global(&mut self, global: &Global) -> Result<()> {
self.next_import("global", |m, e| match e {
EntityIndex::Global(i) => Some(global.matches_expected(&m.globals[*i])),
_ => None,
})?;
self.globals.push(global.vmimport());
Ok(())
}
fn memory(&mut self, mem: &Memory) -> Result<()> {
self.next_import("memory", |m, e| match e {
EntityIndex::Memory(i) => Some(mem.matches_expected(&m.memory_plans[*i])),
_ => None,
})?;
self.memories.push(mem.vmimport());
Ok(())
}
fn table(&mut self, table: &Table) -> Result<()> {
self.next_import("table", |m, e| match e {
EntityIndex::Table(i) => Some(table.matches_expected(&m.table_plans[*i])),
_ => None,
})?;
self.tables.push(table.vmimport());
Ok(())
}
fn func(&mut self, func: &Func) -> Result<()> {
let store = self.store;
self.next_import("func", |m, e| match e {
EntityIndex::Function(i) => Some(
// Look up the `i`th function's type from the module in our // Look up the `i`th function's type from the module in our
// signature registry. If it's not present then we have no // signature registry. If it's not present then we have no
// functions registered with that type, so `func` is guaranteed // functions registered with that type, so `func` is guaranteed
// to not match. // to not match.
match store let ty = self
.store
.signatures() .signatures()
.borrow() .borrow()
.lookup(&m.signatures[m.functions[*i]]) .lookup(&self.types.wasm_signatures[self.module.functions[i]])
{ .ok_or_else(|| anyhow::format_err!("function types incompatible"))?;
Some(ty) => func.matches_expected(ty), if !func.matches_expected(ty) {
None => false, bail!("function types incompatible");
}, }
),
_ => None,
})?;
self.functions.push(func.vmimport()); self.functions.push(func.vmimport());
}
// FIXME(#2094)
EntityIndex::Module(_i) => unimplemented!(),
EntityIndex::Instance(_i) => unimplemented!(),
}
Ok(()) Ok(())
} }
fn validate_all_imports_provided(&mut self) -> Result<()> { fn push_extern(&mut self, item: &Extern) {
if self.imports.next().is_some() { match item {
bail!("not enough imports provided"); Extern::Func(i) => {
self.functions.push(i.vmimport());
}
Extern::Global(i) => {
self.globals.push(i.vmimport());
}
Extern::Table(i) => {
self.tables.push(i.vmimport());
}
Extern::Memory(i) => {
self.memories.push(i.vmimport());
}
} }
Ok(())
} }
fn imports(&self) -> Imports<'_> { fn imports(&self) -> Imports<'_> {

View File

@@ -8,7 +8,7 @@ use std::sync::Arc;
use wasmparser::Validator; use wasmparser::Validator;
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
use wasmtime_cache::ModuleCacheEntry; use wasmtime_cache::ModuleCacheEntry;
use wasmtime_jit::{CompilationArtifacts, CompiledModule}; use wasmtime_jit::{CompilationArtifacts, CompiledModule, TypeTables};
/// A compiled WebAssembly module, ready to be instantiated. /// A compiled WebAssembly module, ready to be instantiated.
/// ///
@@ -81,10 +81,15 @@ use wasmtime_jit::{CompilationArtifacts, CompiledModule};
#[derive(Clone)] #[derive(Clone)]
pub struct Module { pub struct Module {
engine: Engine, engine: Engine,
pub(crate) compiled: Arc<[CompiledModule]>, data: Arc<ModuleData>,
index: usize, index: usize,
} }
pub(crate) struct ModuleData {
pub(crate) types: TypeTables,
pub(crate) modules: Vec<CompiledModule>,
}
impl Module { impl Module {
/// Creates a new WebAssembly `Module` from the given in-memory `bytes`. /// Creates a new WebAssembly `Module` from the given in-memory `bytes`.
/// ///
@@ -164,7 +169,7 @@ impl Module {
/// See [`Module::new`] for other details. /// See [`Module::new`] for other details.
pub fn new_with_name(engine: &Engine, bytes: impl AsRef<[u8]>, name: &str) -> Result<Module> { pub fn new_with_name(engine: &Engine, bytes: impl AsRef<[u8]>, name: &str) -> Result<Module> {
let mut module = Module::new(engine, bytes.as_ref())?; let mut module = Module::new(engine, bytes.as_ref())?;
Arc::get_mut(&mut module.compiled).unwrap()[module.index] Arc::get_mut(&mut module.data).unwrap().modules[module.index]
.module_mut() .module_mut()
.expect("mutable module") .expect("mutable module")
.name = Some(name.to_string()); .name = Some(name.to_string());
@@ -240,14 +245,14 @@ impl Module {
/// ``` /// ```
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> { pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
#[cfg(feature = "cache")] #[cfg(feature = "cache")]
let artifacts = ModuleCacheEntry::new("wasmtime", engine.cache_config()) let (artifacts, types) = ModuleCacheEntry::new("wasmtime", engine.cache_config())
.get_data((engine.compiler(), binary), |(compiler, binary)| { .get_data((engine.compiler(), binary), |(compiler, binary)| {
CompilationArtifacts::build(compiler, binary) CompilationArtifacts::build(compiler, binary)
})?; })?;
#[cfg(not(feature = "cache"))] #[cfg(not(feature = "cache"))]
let artifacts = CompilationArtifacts::build(engine.compiler(), binary)?; let (artifacts, types) = CompilationArtifacts::build(engine.compiler(), binary)?;
let compiled = CompiledModule::from_artifacts_list( let modules = CompiledModule::from_artifacts_list(
artifacts, artifacts,
engine.compiler().isa(), engine.compiler().isa(),
&*engine.config().profiler, &*engine.config().profiler,
@@ -255,8 +260,8 @@ impl Module {
Ok(Module { Ok(Module {
engine: engine.clone(), engine: engine.clone(),
index: compiled.len() - 1, index: 0,
compiled: compiled.into(), data: Arc::new(ModuleData { types, modules }),
}) })
} }
@@ -290,10 +295,12 @@ impl Module {
pub fn serialize(&self) -> Result<Vec<u8>> { pub fn serialize(&self) -> Result<Vec<u8>> {
let artifacts = ( let artifacts = (
compiler_fingerprint(&self.engine), compiler_fingerprint(&self.engine),
self.compiled self.data
.modules
.iter() .iter()
.map(|i| i.compilation_artifacts()) .map(|i| i.compilation_artifacts())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
&self.data.types,
self.index, self.index,
); );
@@ -313,14 +320,14 @@ impl Module {
pub fn deserialize(engine: &Engine, serialized: &[u8]) -> Result<Module> { pub fn deserialize(engine: &Engine, serialized: &[u8]) -> Result<Module> {
let expected_fingerprint = compiler_fingerprint(engine); let expected_fingerprint = compiler_fingerprint(engine);
let (fingerprint, artifacts, index) = bincode_options() let (fingerprint, artifacts, types, index) = bincode_options()
.deserialize::<(u64, _, _)>(serialized) .deserialize::<(u64, _, _, _)>(serialized)
.context("Deserialize compilation artifacts")?; .context("Deserialize compilation artifacts")?;
if fingerprint != expected_fingerprint { if fingerprint != expected_fingerprint {
bail!("Incompatible compilation artifact"); bail!("Incompatible compilation artifact");
} }
let compiled = CompiledModule::from_artifacts_list( let modules = CompiledModule::from_artifacts_list(
artifacts, artifacts,
engine.compiler().isa(), engine.compiler().isa(),
&*engine.config().profiler, &*engine.config().profiler,
@@ -329,12 +336,20 @@ impl Module {
Ok(Module { Ok(Module {
engine: engine.clone(), engine: engine.clone(),
index, index,
compiled: compiled.into(), data: Arc::new(ModuleData { modules, types }),
}) })
} }
pub(crate) fn compiled_module(&self) -> &CompiledModule { pub(crate) fn compiled_module(&self) -> &CompiledModule {
&self.compiled[self.index] &self.data.modules[self.index]
}
pub(crate) fn all_compiled_modules(&self) -> &[CompiledModule] {
&self.data.modules
}
pub(crate) fn types(&self) -> &TypeTables {
&self.data.types
} }
/// Returns identifier/name that this [`Module`] has. This name /// Returns identifier/name that this [`Module`] has. This name
@@ -419,12 +434,21 @@ impl Module {
) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module { ) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module {
let module = self.compiled_module().module(); let module = self.compiled_module().module();
module module
.imports .initializers
.iter() .iter()
.map(move |(module_name, name, entity_index)| { .filter_map(move |initializer| match initializer {
let r#type = EntityType::new(entity_index, module); wasmtime_environ::Initializer::Import {
ImportType::new(module_name, name.as_deref(), r#type) module,
field,
index,
} => {
let ty = EntityType::new(index, self);
Some(ImportType::new(module, field.as_deref(), ty))
}
_ => None,
}) })
.collect::<Vec<_>>()
.into_iter()
} }
/// Returns the list of exports that this [`Module`] has and will be /// Returns the list of exports that this [`Module`] has and will be
@@ -486,8 +510,8 @@ impl Module {
) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module { ) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module {
let module = self.compiled_module().module(); let module = self.compiled_module().module();
module.exports.iter().map(move |(name, entity_index)| { module.exports.iter().map(move |(name, entity_index)| {
let r#type = EntityType::new(entity_index, module); let ty = EntityType::new(entity_index, self);
ExportType::new(name, r#type) ExportType::new(name, ty)
}) })
} }
@@ -537,7 +561,7 @@ impl Module {
pub fn get_export<'module>(&'module self, name: &'module str) -> Option<ExternType> { pub fn get_export<'module>(&'module self, name: &'module str) -> Option<ExternType> {
let module = self.compiled_module().module(); let module = self.compiled_module().module();
let entity_index = module.exports.get(name)?; let entity_index = module.exports.get(name)?;
Some(EntityType::new(entity_index, module).extern_type()) Some(EntityType::new(entity_index, self).extern_type())
} }
/// Returns the [`Engine`] that this [`Module`] was compiled by. /// Returns the [`Engine`] that this [`Module`] was compiled by.

View File

@@ -11,7 +11,7 @@ use std::hash::{Hash, Hasher};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::Arc; use std::sync::Arc;
use wasmtime_environ::wasm; use wasmtime_environ::wasm;
use wasmtime_jit::{CompiledModule, ModuleCode}; use wasmtime_jit::{CompiledModule, ModuleCode, TypeTables};
use wasmtime_runtime::{ use wasmtime_runtime::{
InstanceHandle, RuntimeMemoryCreator, SignalHandler, StackMapRegistry, TrapInfo, VMExternRef, InstanceHandle, RuntimeMemoryCreator, SignalHandler, StackMapRegistry, TrapInfo, VMExternRef,
VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex, VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex,
@@ -137,17 +137,17 @@ impl Store {
pub(crate) fn lookup_shared_signature<'a>( pub(crate) fn lookup_shared_signature<'a>(
&'a self, &'a self,
module: &'a wasmtime_environ::Module, types: &'a TypeTables,
) -> impl Fn(wasm::SignatureIndex) -> VMSharedSignatureIndex + 'a { ) -> impl Fn(wasm::SignatureIndex) -> VMSharedSignatureIndex + 'a {
move |index| { move |index| {
self.signatures() self.signatures()
.borrow() .borrow()
.lookup(&module.signatures[index]) .lookup(&types.wasm_signatures[index])
.expect("signature not previously registered") .expect("signature not previously registered")
} }
} }
pub(crate) fn register_module(&self, module: &CompiledModule) { pub(crate) fn register_module(&self, module: &CompiledModule, types: &TypeTables) {
// All modules register their JIT code in a store for two reasons // All modules register their JIT code in a store for two reasons
// currently: // currently:
// //
@@ -169,7 +169,7 @@ impl Store {
// once-per-module (and once-per-signature). This allows us to create // once-per-module (and once-per-signature). This allows us to create
// a `Func` wrapper for any function in the module, which requires that // a `Func` wrapper for any function in the module, which requires that
// we know about the signature and trampoline for all instances. // we know about the signature and trampoline for all instances.
self.register_signatures(module); self.register_signatures(module, types);
// And finally with a module being instantiated into this `Store` we // And finally with a module being instantiated into this `Store` we
// need to preserve its jit-code. References to this module's code and // need to preserve its jit-code. References to this module's code and
@@ -205,11 +205,10 @@ impl Store {
})); }));
} }
fn register_signatures(&self, module: &CompiledModule) { fn register_signatures(&self, module: &CompiledModule, types: &TypeTables) {
let trampolines = module.trampolines(); let trampolines = module.trampolines();
let module = module.module();
let mut signatures = self.signatures().borrow_mut(); let mut signatures = self.signatures().borrow_mut();
for (index, wasm) in module.signatures.iter() { for (index, wasm) in types.wasm_signatures.iter() {
signatures.register(wasm, trampolines[index]); signatures.register(wasm, trampolines[index]);
} }
} }

View File

@@ -10,7 +10,7 @@ use wasmtime_environ::wasm::DefinedFuncIndex;
use wasmtime_environ::Module; use wasmtime_environ::Module;
use wasmtime_runtime::{ use wasmtime_runtime::{
Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody, Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
VMFunctionImport, VMFunctionImport, VMSharedSignatureIndex,
}; };
pub(crate) fn create_handle( pub(crate) fn create_handle(
@@ -19,11 +19,11 @@ pub(crate) fn create_handle(
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
state: Box<dyn Any>, state: Box<dyn Any>,
func_imports: &[VMFunctionImport], func_imports: &[VMFunctionImport],
shared_signature_id: Option<VMSharedSignatureIndex>,
) -> Result<StoreInstanceHandle> { ) -> Result<StoreInstanceHandle> {
let mut imports = Imports::default(); let mut imports = Imports::default();
imports.functions = func_imports; imports.functions = func_imports;
let module = Arc::new(module); let module = Arc::new(module);
let module2 = module.clone();
unsafe { unsafe {
let handle = InstanceHandle::new( let handle = InstanceHandle::new(
@@ -31,7 +31,7 @@ pub(crate) fn create_handle(
&finished_functions, &finished_functions,
imports, imports,
store.memory_creator(), store.memory_creator(),
&store.lookup_shared_signature(&module2), &|_| shared_signature_id.unwrap(),
state, state,
store.interrupts(), store.interrupts(),
store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _, store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,

View File

@@ -10,7 +10,8 @@ use std::mem;
use std::panic::{self, AssertUnwindSafe}; use std::panic::{self, AssertUnwindSafe};
use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::isa::TargetIsa; use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::{ir, wasm, CompiledFunction, Module}; use wasmtime_environ::wasm::SignatureIndex;
use wasmtime_environ::{ir, wasm, CompiledFunction, Module, ModuleType};
use wasmtime_jit::trampoline::ir::{ use wasmtime_jit::trampoline::ir::{
ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind, ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind,
}; };
@@ -223,7 +224,8 @@ pub fn create_handle_with_function(
// First up we manufacture a trampoline which has the ABI specified by `ft` // First up we manufacture a trampoline which has the ABI specified by `ft`
// and calls into `stub_fn`... // and calls into `stub_fn`...
let sig_id = module.signatures.push(wft.clone()); let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
module.types.push(ModuleType::Function(sig_id));
let func_id = module.functions.push(sig_id); let func_id = module.functions.push(sig_id);
module module
.exports .exports
@@ -241,7 +243,7 @@ pub fn create_handle_with_function(
&sig, &sig,
mem::size_of::<u128>(), mem::size_of::<u128>(),
)?; )?;
store.signatures().borrow_mut().register(wft, trampoline); let shared_signature_id = store.signatures().borrow_mut().register(wft, trampoline);
// Next up we wrap everything up into an `InstanceHandle` by publishing our // Next up we wrap everything up into an `InstanceHandle` by publishing our
// code memory (makes it executable) and ensuring all our various bits of // code memory (makes it executable) and ensuring all our various bits of
@@ -254,6 +256,7 @@ pub fn create_handle_with_function(
finished_functions, finished_functions,
Box::new(trampoline_state), Box::new(trampoline_state),
&[], &[],
Some(shared_signature_id),
) )
.map(|instance| (instance, trampoline)) .map(|instance| (instance, trampoline))
} }
@@ -270,13 +273,21 @@ pub unsafe fn create_handle_with_raw_function(
let mut module = Module::new(); let mut module = Module::new();
let mut finished_functions = PrimaryMap::new(); let mut finished_functions = PrimaryMap::new();
let sig_id = module.signatures.push(wft.clone()); let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
module.types.push(ModuleType::Function(sig_id));
let func_id = module.functions.push(sig_id); let func_id = module.functions.push(sig_id);
module module
.exports .exports
.insert(String::new(), wasm::EntityIndex::Function(func_id)); .insert(String::new(), wasm::EntityIndex::Function(func_id));
finished_functions.push(func); finished_functions.push(func);
store.signatures().borrow_mut().register(wft, trampoline); let shared_signature_id = store.signatures().borrow_mut().register(wft, trampoline);
create_handle(module, store, finished_functions, state, &[]) create_handle(
module,
store,
finished_functions,
state,
&[],
Some(shared_signature_id),
)
} }

View File

@@ -3,13 +3,17 @@ use crate::trampoline::StoreInstanceHandle;
use crate::{GlobalType, Mutability, Store, Val}; use crate::{GlobalType, Mutability, Store, Val};
use anyhow::Result; use anyhow::Result;
use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, Module}; use wasmtime_environ::{
wasm::{self, SignatureIndex},
Module, ModuleType,
};
use wasmtime_runtime::VMFunctionImport; use wasmtime_runtime::VMFunctionImport;
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> { pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
let mut module = Module::new(); let mut module = Module::new();
let mut func_imports = Vec::new(); let mut func_imports = Vec::new();
let mut externref_init = None; let mut externref_init = None;
let mut shared_signature_id = None;
let global = wasm::Global { let global = wasm::Global {
wasm_ty: gt.content().to_wasm_type(), wasm_ty: gt.content().to_wasm_type(),
@@ -35,17 +39,19 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
Val::FuncRef(Some(f)) => { Val::FuncRef(Some(f)) => {
// Add a function import to the stub module, and then initialize // Add a function import to the stub module, and then initialize
// our global with a `ref.func` to grab that imported function. // our global with a `ref.func` to grab that imported function.
let signatures = store.signatures().borrow();
let shared_sig_index = f.sig_index(); let shared_sig_index = f.sig_index();
let (wasm, _) = signatures shared_signature_id = Some(shared_sig_index);
.lookup_shared(shared_sig_index) let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
.expect("signature not registered"); module.types.push(ModuleType::Function(sig_id));
let local_sig_index = module.signatures.push(wasm.clone()); let func_index = module.functions.push(sig_id);
let func_index = module.functions.push(local_sig_index);
module.num_imported_funcs = 1; module.num_imported_funcs = 1;
module module
.imports .initializers
.push(("".into(), None, wasm::EntityIndex::Function(func_index))); .push(wasmtime_environ::Initializer::Import {
module: "".into(),
field: None,
index: wasm::EntityIndex::Function(func_index),
});
let f = f.caller_checked_anyfunc(); let f = f.caller_checked_anyfunc();
let f = unsafe { f.as_ref() }; let f = unsafe { f.as_ref() };
@@ -70,6 +76,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
PrimaryMap::new(), PrimaryMap::new(),
Box::new(()), Box::new(()),
&func_imports, &func_imports,
shared_signature_id,
)?; )?;
if let Some(x) = externref_init { if let Some(x) = externref_init {

View File

@@ -29,7 +29,7 @@ pub fn create_handle_with_memory(
.exports .exports
.insert(String::new(), wasm::EntityIndex::Memory(memory_id)); .insert(String::new(), wasm::EntityIndex::Memory(memory_id));
create_handle(module, store, PrimaryMap::new(), Box::new(()), &[]) create_handle(module, store, PrimaryMap::new(), Box::new(()), &[], None)
} }
struct LinearMemoryProxy { struct LinearMemoryProxy {

View File

@@ -27,5 +27,5 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Stor
.exports .exports
.insert(String::new(), wasm::EntityIndex::Table(table_id)); .insert(String::new(), wasm::EntityIndex::Table(table_id));
create_handle(module, store, PrimaryMap::new(), Box::new(()), &[]) create_handle(module, store, PrimaryMap::new(), Box::new(()), &[], None)
} }

View File

@@ -1,3 +1,4 @@
use crate::Module;
use std::fmt; use std::fmt;
use wasmtime_environ::wasm::WasmFuncType; use wasmtime_environ::wasm::WasmFuncType;
use wasmtime_environ::{ir, wasm}; use wasmtime_environ::{ir, wasm};
@@ -195,33 +196,23 @@ impl ExternType {
(Instance(InstanceType) instance unwrap_instance) (Instance(InstanceType) instance unwrap_instance)
} }
fn from_wasmtime( fn from_wasmtime(module: &Module, ty: &wasmtime_environ::wasm::EntityType) -> ExternType {
module: &wasmtime_environ::Module,
ty: &wasmtime_environ::wasm::EntityType,
) -> ExternType {
use wasmtime_environ::wasm::EntityType; use wasmtime_environ::wasm::EntityType;
match ty { match ty {
EntityType::Function(idx) => { EntityType::Function(idx) => {
let sig = module.types[*idx].unwrap_function(); let sig = &module.types().wasm_signatures[*idx];
let sig = &module.signatures[sig];
FuncType::from_wasm_func_type(sig).into() FuncType::from_wasm_func_type(sig).into()
} }
EntityType::Global(ty) => GlobalType::from_wasmtime_global(ty).into(), EntityType::Global(ty) => GlobalType::from_wasmtime_global(ty).into(),
EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(), EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(),
EntityType::Table(ty) => TableType::from_wasmtime_table(ty).into(), EntityType::Table(ty) => TableType::from_wasmtime_table(ty).into(),
EntityType::Module(ty) => { EntityType::Module(ty) => {
let (imports, exports) = match &module.types[*ty] { let ty = &module.types().module_signatures[*ty];
wasmtime_environ::ModuleType::Module { imports, exports } => (imports, exports), ModuleType::from_wasmtime(module, ty).into()
_ => unreachable!("not possible in valid wasm modules"),
};
ModuleType::from_wasmtime(module, imports, exports).into()
} }
EntityType::Instance(ty) => { EntityType::Instance(ty) => {
let exports = match &module.types[*ty] { let ty = &module.types().instance_signatures[*ty];
wasmtime_environ::ModuleType::Instance { exports } => exports, InstanceType::from_wasmtime(module, ty).into()
_ => unreachable!("not possible in valid wasm modules"),
};
InstanceType::from_wasmtime(module, exports).into()
} }
EntityType::Event(_) => unimplemented!("wasm event support"), EntityType::Event(_) => unimplemented!("wasm event support"),
} }
@@ -499,16 +490,17 @@ impl ModuleType {
} }
pub(crate) fn from_wasmtime( pub(crate) fn from_wasmtime(
module: &wasmtime_environ::Module, module: &Module,
imports: &[(String, Option<String>, wasmtime_environ::wasm::EntityType)], ty: &wasmtime_environ::ModuleSignature,
exports: &[(String, wasmtime_environ::wasm::EntityType)],
) -> ModuleType { ) -> ModuleType {
let exports = &module.types().instance_signatures[ty.exports].exports;
ModuleType { ModuleType {
exports: exports exports: exports
.iter() .iter()
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty))) .map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty)))
.collect(), .collect(),
imports: imports imports: ty
.imports
.iter() .iter()
.map(|(m, name, ty)| { .map(|(m, name, ty)| {
( (
@@ -556,11 +548,12 @@ impl InstanceType {
} }
pub(crate) fn from_wasmtime( pub(crate) fn from_wasmtime(
module: &wasmtime_environ::Module, module: &Module,
exports: &[(String, wasmtime_environ::wasm::EntityType)], ty: &wasmtime_environ::InstanceSignature,
) -> InstanceType { ) -> InstanceType {
InstanceType { InstanceType {
exports: exports exports: ty
.exports
.iter() .iter()
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty))) .map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty)))
.collect(), .collect(),
@@ -577,13 +570,12 @@ pub(crate) enum EntityType<'module> {
Memory(&'module wasm::Memory), Memory(&'module wasm::Memory),
Global(&'module wasm::Global), Global(&'module wasm::Global),
Module { Module {
imports: &'module [(String, Option<String>, wasmtime_environ::wasm::EntityType)], ty: &'module wasmtime_environ::ModuleSignature,
exports: &'module [(String, wasmtime_environ::wasm::EntityType)], module: &'module Module,
module: &'module wasmtime_environ::Module,
}, },
Instance { Instance {
exports: &'module [(String, wasmtime_environ::wasm::EntityType)], ty: &'module wasmtime_environ::InstanceSignature,
module: &'module wasmtime_environ::Module, module: &'module Module,
}, },
} }
@@ -591,51 +583,31 @@ impl<'module> EntityType<'module> {
/// Translate from a `EntityIndex` into an `ExternType`. /// Translate from a `EntityIndex` into an `ExternType`.
pub(crate) fn new( pub(crate) fn new(
entity_index: &wasm::EntityIndex, entity_index: &wasm::EntityIndex,
module: &'module wasmtime_environ::Module, module: &'module Module,
) -> EntityType<'module> { ) -> EntityType<'module> {
let env_module = module.compiled_module().module();
match entity_index { match entity_index {
wasm::EntityIndex::Function(func_index) => { wasm::EntityIndex::Function(func_index) => {
let sig = module.wasm_func_type(*func_index); let sig_index = env_module.functions[*func_index];
EntityType::Function(&sig) let sig = &module.types().wasm_signatures[sig_index];
EntityType::Function(sig)
} }
wasm::EntityIndex::Table(table_index) => { wasm::EntityIndex::Table(table_index) => {
EntityType::Table(&module.table_plans[*table_index].table) EntityType::Table(&env_module.table_plans[*table_index].table)
} }
wasm::EntityIndex::Memory(memory_index) => { wasm::EntityIndex::Memory(memory_index) => {
EntityType::Memory(&module.memory_plans[*memory_index].memory) EntityType::Memory(&env_module.memory_plans[*memory_index].memory)
} }
wasm::EntityIndex::Global(global_index) => { wasm::EntityIndex::Global(global_index) => {
EntityType::Global(&module.globals[*global_index]) EntityType::Global(&env_module.globals[*global_index])
} }
wasm::EntityIndex::Module(idx) => { wasm::EntityIndex::Module(idx) => {
let (imports, exports) = match &module.types[module.modules[*idx]] { let ty = &module.types().module_signatures[env_module.modules[*idx]];
wasmtime_environ::ModuleType::Module { imports, exports } => (imports, exports), EntityType::Module { ty, module }
_ => unreachable!("valid modules should never hit this"),
};
EntityType::Module {
imports,
exports,
module,
}
} }
wasm::EntityIndex::Instance(idx) => { wasm::EntityIndex::Instance(idx) => {
// Get the type, either a pointer to an instance for an import let ty = &module.types().instance_signatures[env_module.instances[*idx]];
// or a module for an instantiation. EntityType::Instance { ty, module }
let ty = match module.instances[*idx] {
wasmtime_environ::Instance::Import(ty) => ty,
wasmtime_environ::Instance::Instantiate { module: idx, .. } => {
module.modules[idx]
}
};
// Get the exports of whatever our type specifies, ignoring
// imports in the module case since we're instantiating the
// module.
let exports = match &module.types[ty] {
wasmtime_environ::ModuleType::Instance { exports } => exports,
wasmtime_environ::ModuleType::Module { exports, .. } => exports,
_ => unreachable!("valid modules should never hit this"),
};
EntityType::Instance { exports, module }
} }
} }
} }
@@ -647,14 +619,8 @@ impl<'module> EntityType<'module> {
EntityType::Table(table) => TableType::from_wasmtime_table(table).into(), EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),
EntityType::Memory(memory) => MemoryType::from_wasmtime_memory(memory).into(), EntityType::Memory(memory) => MemoryType::from_wasmtime_memory(memory).into(),
EntityType::Global(global) => GlobalType::from_wasmtime_global(global).into(), EntityType::Global(global) => GlobalType::from_wasmtime_global(global).into(),
EntityType::Instance { exports, module } => { EntityType::Instance { module, ty } => InstanceType::from_wasmtime(module, ty).into(),
InstanceType::from_wasmtime(module, exports).into() EntityType::Module { module, ty } => ModuleType::from_wasmtime(module, ty).into(),
}
EntityType::Module {
imports,
exports,
module,
} => ModuleType::from_wasmtime(module, imports, exports).into(),
} }
} }
} }

View File

@@ -226,7 +226,8 @@ impl WastContext {
for directive in ast.directives { for directive in ast.directives {
let sp = directive.span(); let sp = directive.span();
self.run_directive(directive).with_context(|| { self.run_directive(directive, &adjust_wast)
.with_context(|| {
let (line, col) = sp.linecol_in(wast); let (line, col) = sp.linecol_in(wast);
format!("failed directive on {}:{}:{}", filename, line + 1, col) format!("failed directive on {}:{}:{}", filename, line + 1, col)
})?; })?;
@@ -234,12 +235,16 @@ impl WastContext {
Ok(()) Ok(())
} }
fn run_directive(&mut self, directive: wast::WastDirective) -> Result<()> { fn run_directive(
&mut self,
directive: wast::WastDirective,
adjust: impl Fn(wast::Error) -> wast::Error,
) -> Result<()> {
use wast::WastDirective::*; use wast::WastDirective::*;
match directive { match directive {
Module(mut module) => { Module(mut module) => {
let binary = module.encode()?; let binary = module.encode().map_err(adjust)?;
self.module(module.id.map(|s| s.name()), &binary)?; self.module(module.id.map(|s| s.name()), &binary)?;
} }
QuoteModule { span: _, source } => { QuoteModule { span: _, source } => {
@@ -249,7 +254,10 @@ impl WastContext {
module.push_str(" "); module.push_str(" ");
} }
let buf = ParseBuffer::new(&module)?; let buf = ParseBuffer::new(&module)?;
let mut wat = parser::parse::<Wat>(&buf)?; let mut wat = parser::parse::<Wat>(&buf).map_err(|mut e| {
e.set_text(&module);
e
})?;
let binary = wat.module.encode()?; let binary = wat.module.encode()?;
self.module(wat.module.id.map(|s| s.name()), &binary)?; self.module(wat.module.id.map(|s| s.name()), &binary)?;
} }
@@ -317,7 +325,7 @@ impl WastContext {
// interested in. // interested in.
wast::QuoteModule::Quote(_) => return Ok(()), wast::QuoteModule::Quote(_) => return Ok(()),
}; };
let bytes = module.encode()?; let bytes = module.encode().map_err(adjust)?;
if let Ok(_) = self.module(None, &bytes) { if let Ok(_) = self.module(None, &bytes) {
bail!("expected malformed module to fail to instantiate"); bail!("expected malformed module to fail to instantiate");
} }
@@ -327,7 +335,7 @@ impl WastContext {
mut module, mut module,
message, message,
} => { } => {
let bytes = module.encode()?; let bytes = module.encode().map_err(adjust)?;
let err = match self.module(None, &bytes) { let err = match self.module(None, &bytes) {
Ok(()) => bail!("expected module to fail to link"), Ok(()) => bail!("expected module to fail to link"),
Err(e) => e, Err(e) => e,

View File

@@ -65,10 +65,10 @@ pub fn compile_to_obj(
); );
let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables, &features); let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables, &features);
let mut translation = environ let (mut translation, types) = environ
.translate(wasm) .translate(wasm)
.context("failed to translate module")?; .context("failed to translate module")?;
assert_eq!(translation.len(), 1); assert_eq!(translation.len(), 1);
let compilation = compiler.compile(&mut translation[0])?; let compilation = compiler.compile(&mut translation[0], &types)?;
Ok(compilation.obj) Ok(compilation.obj)
} }

View File

@@ -52,3 +52,126 @@ fn types() -> Result<()> {
Module::new(&engine, "(module (type (instance)))")?; Module::new(&engine, "(module (type (instance)))")?;
Ok(()) Ok(())
} }
#[test]
fn imports_exports() -> Result<()> {
let engine = engine();
// empty module type
let module = Module::new(&engine, "(module (module (export \"\")))")?;
let mut e = module.exports();
assert_eq!(e.len(), 1);
let export = e.next().unwrap();
assert_eq!(export.name(), "");
let module_ty = match export.ty() {
ExternType::Module(m) => m,
_ => panic!("unexpected type"),
};
assert_eq!(module_ty.imports().len(), 0);
assert_eq!(module_ty.exports().len(), 0);
// empty instance type
let module = Module::new(
&engine,
"
(module
(module)
(instance (export \"\") (instantiate 0)))
",
)?;
let mut e = module.exports();
assert_eq!(e.len(), 1);
let export = e.next().unwrap();
assert_eq!(export.name(), "");
let instance_ty = match export.ty() {
ExternType::Instance(i) => i,
_ => panic!("unexpected type"),
};
assert_eq!(instance_ty.exports().len(), 0);
// full module type
let module = Module::new(
&engine,
"
(module
(import \"\" \"a\" (module
(import \"a\" (func))
(export \"\" (global i32))
))
)
",
)?;
let mut i = module.imports();
assert_eq!(i.len(), 1);
let import = i.next().unwrap();
assert_eq!(import.module(), "");
assert_eq!(import.name(), Some("a"));
let module_ty = match import.ty() {
ExternType::Module(m) => m,
_ => panic!("unexpected type"),
};
assert_eq!(module_ty.imports().len(), 1);
assert_eq!(module_ty.exports().len(), 1);
let import = module_ty.imports().next().unwrap();
assert_eq!(import.module(), "a");
assert_eq!(import.name(), None);
match import.ty() {
ExternType::Func(f) => {
assert_eq!(f.results().len(), 0);
assert_eq!(f.params().len(), 0);
}
_ => panic!("unexpected type"),
}
let export = module_ty.exports().next().unwrap();
assert_eq!(export.name(), "");
match export.ty() {
ExternType::Global(g) => {
assert_eq!(*g.content(), ValType::I32);
assert_eq!(g.mutability(), Mutability::Const);
}
_ => panic!("unexpected type"),
}
// full instance type
let module = Module::new(
&engine,
"
(module
(import \"\" \"b\" (instance
(export \"m\" (memory 1))
(export \"t\" (table 1 funcref))
))
)
",
)?;
let mut i = module.imports();
assert_eq!(i.len(), 1);
let import = i.next().unwrap();
assert_eq!(import.module(), "");
assert_eq!(import.name(), Some("b"));
let instance_ty = match import.ty() {
ExternType::Instance(m) => m,
_ => panic!("unexpected type"),
};
assert_eq!(instance_ty.exports().len(), 2);
let mem_export = instance_ty.exports().nth(0).unwrap();
assert_eq!(mem_export.name(), "m");
match mem_export.ty() {
ExternType::Memory(m) => {
assert_eq!(m.limits().min(), 1);
assert_eq!(m.limits().max(), None);
}
_ => panic!("unexpected type"),
}
let table_export = instance_ty.exports().nth(1).unwrap();
assert_eq!(table_export.name(), "t");
match table_export.ty() {
ExternType::Table(t) => {
assert_eq!(t.limits().min(), 1);
assert_eq!(t.limits().max(), None);
assert_eq!(*t.element(), ValType::FuncRef);
}
_ => panic!("unexpected type"),
}
Ok(())
}

View File

@@ -0,0 +1,114 @@
;; functions
(module
(module $m
(func $foo (export "foo") (result i32)
i32.const 1)
)
(instance $a (instantiate $m))
(func (export "get") (result i32)
call $a.$foo)
)
(assert_return (invoke "get") (i32.const 1))
;; globals
(module
(module $m
(global $g (export "g") (mut i32) (i32.const 2))
)
(instance $a (instantiate $m))
(func (export "get") (result i32)
global.get $a.$g)
)
(assert_return (invoke "get") (i32.const 2))
;; memories
(module
(module $m
(memory $m (export "m") 1)
(data (i32.const 0) "\03\00\00\00")
)
(instance $a (instantiate $m))
(alias (instance $a) (memory $m))
(func (export "get") (result i32)
i32.const 0
i32.load)
)
(assert_return (invoke "get") (i32.const 3))
;; tables
(module
(module $m
(table $t (export "t") 1 funcref)
(func (result i32)
i32.const 4)
(elem (i32.const 0) 0)
)
(instance $a (instantiate $m))
(func (export "get") (result i32)
i32.const 0
call_indirect $a.$t (result i32))
)
(assert_return (invoke "get") (i32.const 4))
;; TODO instances/modules -- needs import/export of modules/instances to work
;; alias parent -- type
(module
(type $t (func))
(module $m
(func $f (type $t))
)
(instance $a (instantiate $m))
)
;; alias parent -- module
(module
(module $a)
(module $m
(instance (instantiate $a))
)
(instance (instantiate $m))
)
;; The alias, import, type, module, and instance sections can all be interleaved
(module
(module $a)
(type $t (func))
(module $m
;; alias
(alias $thunk parent (type $t))
;; import
(import "" "" (func (type $thunk)))
;; module (referencing parent type)
(module
(func (type $thunk))
)
;; type
(type $thunk2 (func))
;; module (referencing previous alias)
(module $m2
(func (export "") (type $thunk2))
)
;; instance
(instance $i (instantiate $m2))
;; alias that instance
(alias $my_f (instance $i) (func 0))
;; module
(module $m3
(import "" (func)))
;; use our aliased function to create the module
(instance $i2 (instantiate $m3 (func $my_f)))
;; module
(module $m4
(import "" (func)))
)
;; instantiate the above module
(module $smol (func $f (export "")))
(instance $smol (instantiate $smol))
(instance (instantiate $m (func $smol.$f)))
)

View File

@@ -74,13 +74,48 @@
(import "" (memory 1)) (import "" (memory 1))
(func (func
i32.const 0 i32.const 0
i32.const 4 i32.const 100
i32.store) i32.store)
(start 0)) (start 0))
(instance $a (instantiate 0 (memory $m))) (instance $a (instantiate 0 (memory $m)))
) )
(assert_return (invoke $a "load") (i32.const 4)) (assert_return (invoke $a "load") (i32.const 100))
;; Imported instances work
(module
(import "a" "inc" (func $set))
(module $m1
(import "" (instance (export "" (func))))
(alias (instance 0) (func 0))
(start 0))
(module $m2
(func (export "") (import "")))
(instance $i (instantiate $m2 (func $set)))
(instance (instantiate $m1 (instance $i)))
)
(assert_return (invoke $a "get") (i32.const 4))
;; Imported modules work
(module
(import "a" "inc" (func $set))
(module $m1
(import "" (module $m (export "" (func $f (result i32)))))
(instance $i (instantiate $m))
(func $get (export "") (result i32)
call $i.$f))
(module $m2
(func (export "") (result i32)
i32.const 5))
(instance $i (instantiate $m1 (module $m2)))
(func (export "get") (result i32)
call $i.$get)
)
(assert_return (invoke "get") (i32.const 5))
;; all at once ;; all at once
(module (module