Propagate module-linking types to wasmtime (#2115)

This commit adds lots of plumbing to get the type section from the
module linking proposal plumbed all the way through to the `wasmtime`
crate and the `wasmtime-c-api` crate. This isn't all that useful right
now because Wasmtime doesn't support imported/exported
modules/instances, but this is all necessary groundwork to getting that
exported at some point. I've added some light tests but I suspect the
bulk of the testing will come in a future commit.

One major change in this commit is that `SignatureIndex` no longer
follows type type index space in a wasm module. Instead a new
`TypeIndex` type is used to track that. Function signatures, still
indexed by `SignatureIndex`, are then packed together tightly.
This commit is contained in:
Alex Crichton
2020-11-06 14:48:09 -06:00
committed by GitHub
parent 77827a48a9
commit 73cda83548
27 changed files with 782 additions and 213 deletions

View File

@@ -77,7 +77,7 @@ use crate::state::{ControlStackFrame, ElseData, FuncTranslationState};
use crate::translation_utils::{
block_with_params, blocktype_params_results, f32_translation, f64_translation,
};
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex};
use crate::wasm_unsupported;
use core::convert::TryInto;
use core::{i32, u32};
@@ -587,7 +587,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
builder.cursor(),
TableIndex::from_u32(*table_index),
table,
SignatureIndex::from_u32(*index),
TypeIndex::from_u32(*index),
sigref,
callee,
state.peekn(num_args),

View File

@@ -12,7 +12,7 @@ use crate::environ::{
use crate::func_translator::FuncTranslator;
use crate::translation_utils::{
DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex,
SignatureIndex, Table, TableIndex,
Table, TableIndex, TypeIndex,
};
use core::convert::TryFrom;
use cranelift_codegen::cursor::FuncCursor;
@@ -58,7 +58,7 @@ pub struct DummyModuleInfo {
config: TargetFrontendConfig,
/// Signatures as provided by `declare_signature`.
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
pub signatures: PrimaryMap<TypeIndex, ir::Signature>,
/// Module and field names of imported functions as provided by `declare_func_import`.
pub imported_funcs: Vec<(String, String)>,
@@ -73,7 +73,7 @@ pub struct DummyModuleInfo {
pub imported_memories: Vec<(String, String)>,
/// Functions, imported and local.
pub functions: PrimaryMap<FuncIndex, Exportable<SignatureIndex>>,
pub functions: PrimaryMap<FuncIndex, Exportable<TypeIndex>>,
/// Function bodies.
pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>,
@@ -157,7 +157,7 @@ impl DummyEnvironment {
DummyFuncEnvironment::new(&self.info, self.return_mode)
}
fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex {
fn get_func_type(&self, func_index: FuncIndex) -> TypeIndex {
self.info.functions[func_index].entity
}
@@ -190,7 +190,7 @@ impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
// Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm
// arguments.
fn vmctx_sig(&self, sigidx: SignatureIndex) -> ir::Signature {
fn vmctx_sig(&self, sigidx: TypeIndex) -> ir::Signature {
let mut sig = self.mod_info.signatures[sigidx].clone();
sig.params.push(ir::AbiParam::special(
self.pointer_type(),
@@ -283,7 +283,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
fn make_indirect_sig(
&mut self,
func: &mut ir::Function,
index: SignatureIndex,
index: TypeIndex,
) -> WasmResult<ir::SigRef> {
// A real implementation would probably change the calling convention and add `vmctx` and
// signature index arguments.
@@ -312,7 +312,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
mut pos: FuncCursor,
_table_index: TableIndex,
_table: ir::Table,
_sig_index: SignatureIndex,
_sig_index: TypeIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
@@ -572,14 +572,14 @@ impl TargetEnvironment for DummyEnvironment {
}
impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
fn declare_signature(&mut self, _wasm: WasmFuncType, sig: ir::Signature) -> WasmResult<()> {
fn declare_type_func(&mut self, _wasm: WasmFuncType, sig: ir::Signature) -> WasmResult<()> {
self.info.signatures.push(sig);
Ok(())
}
fn declare_func_import(
&mut self,
sig_index: SignatureIndex,
index: TypeIndex,
module: &'data str,
field: &'data str,
) -> WasmResult<()> {
@@ -588,15 +588,15 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
self.info.imported_funcs.len(),
"Imported functions must be declared first"
);
self.info.functions.push(Exportable::new(sig_index));
self.info.functions.push(Exportable::new(index));
self.info
.imported_funcs
.push((String::from(module), String::from(field)));
Ok(())
}
fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
self.info.functions.push(Exportable::new(sig_index));
fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()> {
self.info.functions.push(Exportable::new(index));
Ok(())
}

View File

@@ -8,8 +8,8 @@
use crate::state::FuncTranslationState;
use crate::translation_utils::{
DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
Table, TableIndex,
DataIndex, ElemIndex, EntityType, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, Table,
TableIndex, TypeIndex,
};
use core::convert::From;
use core::convert::TryFrom;
@@ -293,7 +293,7 @@ pub trait FuncEnvironment: TargetEnvironment {
fn make_indirect_sig(
&mut self,
func: &mut ir::Function,
index: SignatureIndex,
index: TypeIndex,
) -> WasmResult<ir::SigRef>;
/// Set up an external function definition in the preamble of `func` that can be used to
@@ -328,7 +328,7 @@ pub trait FuncEnvironment: TargetEnvironment {
pos: FuncCursor,
table_index: TableIndex,
table: ir::Table,
sig_index: SignatureIndex,
sig_index: TypeIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
@@ -630,19 +630,35 @@ pub trait FuncEnvironment: TargetEnvironment {
/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
/// by the user, they are only for `cranelift-wasm` internal use.
pub trait ModuleEnvironment<'data>: TargetEnvironment {
/// Provides the number of signatures up front. By default this does nothing, but
/// Provides the number of types up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_signatures(&mut self, _num: u32) -> WasmResult<()> {
fn reserve_types(&mut self, _num: u32) -> WasmResult<()> {
Ok(())
}
/// Declares a function signature to the environment.
fn declare_signature(
fn declare_type_func(
&mut self,
wasm_func_type: WasmFuncType,
sig: ir::Signature,
) -> WasmResult<()>;
/// Declares a module type signature to the environment.
fn declare_type_module(
&mut self,
imports: &[(&'data str, Option<&'data str>, EntityType)],
exports: &[(&'data str, EntityType)],
) -> WasmResult<()> {
drop((imports, exports));
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Declares an instance type signature to the environment.
fn declare_type_instance(&mut self, exports: &[(&'data str, EntityType)]) -> WasmResult<()> {
drop(exports);
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Provides the number of imports up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> {
@@ -652,7 +668,7 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
/// Declares a function import to the environment.
fn declare_func_import(
&mut self,
sig_index: SignatureIndex,
index: TypeIndex,
module: &'data str,
field: &'data str,
) -> WasmResult<()>;
@@ -681,6 +697,28 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
field: &'data str,
) -> WasmResult<()>;
/// Declares a module import to the environment.
fn declare_module_import(
&mut self,
ty_index: TypeIndex,
module: &'data str,
field: &'data str,
) -> WasmResult<()> {
drop((ty_index, module, field));
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Declares an instance import to the environment.
fn declare_instance_import(
&mut self,
ty_index: TypeIndex,
module: &'data str,
field: &'data str,
) -> WasmResult<()> {
drop((ty_index, module, field));
Err(WasmError::Unsupported("module linking".to_string()))
}
/// Notifies the implementation that all imports have been declared.
fn finish_imports(&mut self) -> WasmResult<()> {
Ok(())
@@ -693,7 +731,7 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
}
/// Declares the type (signature) of a local function in the module.
fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()>;
fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()>;
/// Provides the number of defined tables up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.

View File

@@ -65,11 +65,7 @@ pub use crate::func_translator::FuncTranslator;
pub use crate::module_translator::translate_module;
pub use crate::state::func_state::FuncTranslationState;
pub use crate::state::module_state::ModuleTranslationState;
pub use crate::translation_utils::{
get_vmctx_value_label, DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex,
DefinedTableIndex, ElemIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex,
SignatureIndex, Table, TableElementType, TableIndex,
};
pub use crate::translation_utils::*;
pub use cranelift_frontend::FunctionBuilder;
// Convenience reexport of the wasmparser crate that we're linking against,

View File

@@ -10,8 +10,8 @@
use crate::environ::{ModuleEnvironment, WasmError, WasmResult};
use crate::state::ModuleTranslationState;
use crate::translation_utils::{
tabletype_to_type, type_to_type, DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex,
GlobalInit, Memory, MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex,
tabletype_to_type, type_to_type, DataIndex, ElemIndex, EntityType, FuncIndex, Global,
GlobalIndex, GlobalInit, Memory, MemoryIndex, Table, TableElementType, TableIndex, TypeIndex,
};
use crate::wasm_unsupported;
use core::convert::TryFrom;
@@ -27,18 +27,71 @@ use wasmparser::{
ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader,
MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader,
TypeDef, TypeSectionReader,
TableType, TypeDef, TypeSectionReader,
};
fn entity_type(
ty: ImportSectionEntryType,
environ: &mut dyn ModuleEnvironment<'_>,
) -> WasmResult<EntityType> {
Ok(match ty {
ImportSectionEntryType::Function(sig) => EntityType::Function(TypeIndex::from_u32(sig)),
ImportSectionEntryType::Module(sig) => EntityType::Module(TypeIndex::from_u32(sig)),
ImportSectionEntryType::Instance(sig) => EntityType::Instance(TypeIndex::from_u32(sig)),
ImportSectionEntryType::Memory(ty) => EntityType::Memory(memory(ty)),
ImportSectionEntryType::Global(ty) => {
EntityType::Global(global(ty, environ, GlobalInit::Import)?)
}
ImportSectionEntryType::Table(ty) => EntityType::Table(table(ty, environ)?),
})
}
fn memory(ty: MemoryType) -> Memory {
match ty {
MemoryType::M32 { limits, shared } => Memory {
minimum: limits.initial,
maximum: limits.maximum,
shared: shared,
},
// FIXME(#2361)
MemoryType::M64 { .. } => unimplemented!(),
}
}
fn table(ty: TableType, environ: &mut dyn ModuleEnvironment<'_>) -> WasmResult<Table> {
Ok(Table {
wasm_ty: ty.element_type.try_into()?,
ty: match tabletype_to_type(ty.element_type, environ)? {
Some(t) => TableElementType::Val(t),
None => TableElementType::Func,
},
minimum: ty.limits.initial,
maximum: ty.limits.maximum,
})
}
fn global(
ty: GlobalType,
environ: &mut dyn ModuleEnvironment<'_>,
initializer: GlobalInit,
) -> WasmResult<Global> {
Ok(Global {
wasm_ty: ty.content_type.try_into()?,
ty: type_to_type(ty.content_type, environ).unwrap(),
mutability: ty.mutable,
initializer,
})
}
/// Parses the Type section of the wasm module.
pub fn parse_type_section(
types: TypeSectionReader,
pub fn parse_type_section<'a>(
types: TypeSectionReader<'a>,
module_translation_state: &mut ModuleTranslationState,
environ: &mut dyn ModuleEnvironment,
environ: &mut dyn ModuleEnvironment<'a>,
) -> WasmResult<()> {
let count = types.get_count();
module_translation_state.wasm_types.reserve(count as usize);
environ.reserve_signatures(count)?;
environ.reserve_types(count)?;
for entry in types {
match entry? {
@@ -55,28 +108,31 @@ pub fn parse_type_section(
.expect("only numeric types are supported in function signatures");
AbiParam::new(cret_arg)
}));
environ.declare_signature(wasm_func_ty.clone().try_into()?, sig)?;
environ.declare_type_func(wasm_func_ty.clone().try_into()?, sig)?;
module_translation_state
.wasm_types
.push((wasm_func_ty.params, wasm_func_ty.returns));
}
// Not implemented yet for module linking. Push dummy function types
// though to keep the function type index space consistent. We'll
// want an actual implementation here that handles this eventually.
TypeDef::Module(_) | TypeDef::Instance(_) => {
let sig =
Signature::new(ModuleEnvironment::target_config(environ).default_call_conv);
environ.declare_signature(
crate::environ::WasmFuncType {
params: Box::new([]),
returns: Box::new([]),
},
sig,
)?;
module_translation_state
.wasm_types
.push((Box::new([]), Box::new([])));
TypeDef::Module(t) => {
let imports = t
.imports
.iter()
.map(|i| Ok((i.module, i.field, entity_type(i.ty, environ)?)))
.collect::<WasmResult<Vec<_>>>()?;
let exports = t
.exports
.iter()
.map(|e| Ok((e.name, entity_type(e.ty, environ)?)))
.collect::<WasmResult<Vec<_>>>()?;
environ.declare_type_module(&imports, &exports)?;
}
TypeDef::Instance(t) => {
let exports = t
.exports
.iter()
.map(|e| Ok((e.name, entity_type(e.ty, environ)?)))
.collect::<WasmResult<Vec<_>>>()?;
environ.declare_type_instance(&exports)?;
}
}
}
@@ -94,61 +150,24 @@ pub fn parse_import_section<'data>(
let import = entry?;
let module_name = import.module;
let field_name = import.field.unwrap(); // TODO Handle error when module linking is implemented.
match import.ty {
ImportSectionEntryType::Function(sig) => {
environ.declare_func_import(
SignatureIndex::from_u32(sig),
module_name,
field_name,
)?;
match entity_type(import.ty, environ)? {
EntityType::Function(idx) => {
environ.declare_func_import(idx, module_name, field_name)?;
}
ImportSectionEntryType::Module(_sig) | ImportSectionEntryType::Instance(_sig) => {
unimplemented!("module linking not implemented yet")
EntityType::Module(idx) => {
environ.declare_module_import(idx, module_name, field_name)?;
}
ImportSectionEntryType::Memory(MemoryType::M32 {
limits: ref memlimits,
shared,
}) => {
environ.declare_memory_import(
Memory {
minimum: memlimits.initial,
maximum: memlimits.maximum,
shared,
},
module_name,
field_name,
)?;
EntityType::Instance(idx) => {
environ.declare_instance_import(idx, module_name, field_name)?;
}
ImportSectionEntryType::Memory(MemoryType::M64 { .. }) => {
unimplemented!();
EntityType::Memory(ty) => {
environ.declare_memory_import(ty, module_name, field_name)?;
}
ImportSectionEntryType::Global(ref ty) => {
environ.declare_global_import(
Global {
wasm_ty: ty.content_type.try_into()?,
ty: type_to_type(ty.content_type, environ).unwrap(),
mutability: ty.mutable,
initializer: GlobalInit::Import,
},
module_name,
field_name,
)?;
EntityType::Global(ty) => {
environ.declare_global_import(ty, module_name, field_name)?;
}
ImportSectionEntryType::Table(ref tab) => {
environ.declare_table_import(
Table {
wasm_ty: tab.element_type.try_into()?,
ty: match tabletype_to_type(tab.element_type, environ)? {
Some(t) => TableElementType::Val(t),
None => TableElementType::Func,
},
minimum: tab.limits.initial,
maximum: tab.limits.maximum,
},
module_name,
field_name,
)?;
EntityType::Table(ty) => {
environ.declare_table_import(ty, module_name, field_name)?;
}
}
}
@@ -172,7 +191,7 @@ pub fn parse_function_section(
for entry in functions {
let sigindex = entry?;
environ.declare_func_type(SignatureIndex::from_u32(sigindex))?;
environ.declare_func_type(TypeIndex::from_u32(sigindex))?;
}
Ok(())
@@ -186,16 +205,8 @@ pub fn parse_table_section(
environ.reserve_tables(tables.get_count())?;
for entry in tables {
let table = entry?;
environ.declare_table(Table {
wasm_ty: table.element_type.try_into()?,
ty: match tabletype_to_type(table.element_type, environ)? {
Some(t) => TableElementType::Val(t),
None => TableElementType::Func,
},
minimum: table.limits.initial,
maximum: table.limits.maximum,
})?;
let ty = table(entry?, environ)?;
environ.declare_table(ty)?;
}
Ok(())
@@ -209,17 +220,8 @@ pub fn parse_memory_section(
environ.reserve_memories(memories.get_count())?;
for entry in memories {
let memory = entry?;
match memory {
MemoryType::M32 { limits, shared } => {
environ.declare_memory(Memory {
minimum: limits.initial,
maximum: limits.maximum,
shared: shared,
})?;
}
MemoryType::M64 { .. } => unimplemented!(),
}
let memory = memory(entry?);
environ.declare_memory(memory)?;
}
Ok(())
@@ -233,13 +235,7 @@ pub fn parse_global_section(
environ.reserve_globals(globals.get_count())?;
for entry in globals {
let wasmparser::Global {
ty: GlobalType {
content_type,
mutable,
},
init_expr,
} = entry?;
let wasmparser::Global { ty, init_expr } = entry?;
let mut init_expr_reader = init_expr.get_binary_reader();
let initializer = match init_expr_reader.read_operator()? {
Operator::I32Const { value } => GlobalInit::I32Const(value),
@@ -263,13 +259,8 @@ pub fn parse_global_section(
));
}
};
let global = Global {
wasm_ty: content_type.try_into()?,
ty: type_to_type(content_type, environ).unwrap(),
mutability: mutable,
initializer,
};
environ.declare_global(global)?;
let ty = global(ty, environ, initializer)?;
environ.declare_global(ty)?;
}
Ok(())

View File

@@ -7,7 +7,7 @@
//! value and control stacks during the translation of a single function.
use crate::environ::{FuncEnvironment, GlobalVariable, WasmResult};
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex};
use crate::{HashMap, Occupied, Vacant};
use cranelift_codegen::ir::{self, Block, Inst, Value};
use std::vec::Vec;
@@ -236,7 +236,7 @@ pub struct FuncTranslationState {
// Map of indirect call signatures that have been created by
// `FuncEnvironment::make_indirect_sig()`.
// Stores both the signature reference and the number of WebAssembly arguments
signatures: HashMap<SignatureIndex, (ir::SigRef, usize)>,
signatures: HashMap<TypeIndex, (ir::SigRef, usize)>,
// Imported and local functions that have been created by
// `FuncEnvironment::make_direct_func()`.
@@ -498,7 +498,7 @@ impl FuncTranslationState {
index: u32,
environ: &mut FE,
) -> WasmResult<(ir::SigRef, usize)> {
let index = SignatureIndex::from_u32(index);
let index = TypeIndex::from_u32(index);
match self.signatures.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => {

View File

@@ -73,6 +73,65 @@ entity_impl!(DataIndex);
pub struct ElemIndex(u32);
entity_impl!(ElemIndex);
/// Index type of a type inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct TypeIndex(u32);
entity_impl!(TypeIndex);
/// Index type of a module inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct ModuleIndex(u32);
entity_impl!(ModuleIndex);
/// Index type of an instance inside the WebAssembly module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct InstanceIndex(u32);
entity_impl!(InstanceIndex);
/// An index of an entity.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum EntityIndex {
/// Function index.
Function(FuncIndex),
/// Table index.
Table(TableIndex),
/// Memory index.
Memory(MemoryIndex),
/// Global index.
Global(GlobalIndex),
/// Module index.
Module(ModuleIndex),
/// Instance index.
Instance(InstanceIndex),
}
/// A type of an item in a wasm module where an item is typically something that
/// can be exported.
#[allow(missing_docs)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum EntityType {
/// A global variable with the specified content type
Global(Global),
/// A linear memory with the specified limits
Memory(Memory),
/// A table with the specified element type and limits
Table(Table),
/// A function type where the index points to the type section and records a
/// function signature.
Function(TypeIndex),
/// An instance where the index points to the type section and records a
/// instance's exports.
Instance(TypeIndex),
/// A module where the index points to the type section and records a
/// module's imports and exports.
Module(TypeIndex),
}
/// A WebAssembly global.
///
/// Note that we record both the original Wasm type and the Cranelift IR type