Move the wasmtime crate directories form lib/* to wasmtime-*.

This follows a similar change to Cranelift made here:

https://github.com/CraneStation/cranelift/pull/660
This commit is contained in:
Dan Gohman
2019-03-20 10:33:21 -07:00
parent 7b9761f4a2
commit db0abe8431
74 changed files with 21 additions and 21 deletions

View File

@@ -0,0 +1,97 @@
//! A `Compilation` contains the compiled function bodies for a WebAssembly
//! module.
use cranelift_codegen::binemit;
use cranelift_codegen::ir;
use cranelift_codegen::CodegenError;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError};
use std::vec::Vec;
/// The result of compiling a WebAssembly module's functions.
#[derive(Debug)]
pub struct Compilation {
/// Compiled machine code for the function bodies.
pub functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>,
}
impl Compilation {
/// Allocates the compilation result with the given function bodies.
pub fn new(functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>) -> Self {
Self { functions }
}
}
/// A record of a relocation to perform.
#[derive(Debug, Clone)]
pub struct Relocation {
/// The relocation code.
pub reloc: binemit::Reloc,
/// Relocation target.
pub reloc_target: RelocationTarget,
/// The offset where to apply the relocation.
pub offset: binemit::CodeOffset,
/// The addend to add to the relocation value.
pub addend: binemit::Addend,
}
/// Destination function. Can be either user function or some special one, like `memory.grow`.
#[derive(Debug, Copy, Clone)]
pub enum RelocationTarget {
/// The user function index.
UserFunc(FuncIndex),
/// A compiler-generated libcall.
LibCall(ir::LibCall),
/// Function for growing a locally-defined 32-bit memory by the specified amount of pages.
Memory32Grow,
/// Function for growing an imported 32-bit memory by the specified amount of pages.
ImportedMemory32Grow,
/// Function for query current size of a locally-defined 32-bit linear memory.
Memory32Size,
/// Function for query current size of an imported 32-bit linear memory.
ImportedMemory32Size,
}
/// Relocations to apply to function bodies.
pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>;
/// An error while compiling WebAssembly to machine code.
#[derive(Fail, Debug)]
pub enum CompileError {
/// A wasm translation error occured.
#[fail(display = "WebAssembly translation error: {}", _0)]
Wasm(WasmError),
/// A compilation error occured.
#[fail(display = "Compilation error: {}", _0)]
Codegen(CodegenError),
}
/// Single address point transform.
#[derive(Debug)]
pub struct InstructionAddressTransform {
/// Original source location.
pub srcloc: ir::SourceLoc,
/// Generated instructions offset.
pub code_offset: usize,
/// Generated instructions length.
pub code_len: usize,
}
/// Function and its instructions transforms.
#[derive(Debug)]
pub struct FunctionAddressTransform {
/// Instructions transforms
pub locations: Vec<InstructionAddressTransform>,
/// Generated function body offset if applicable, otherwise 0.
pub body_offset: usize,
/// Generated function body length.
pub body_len: usize,
}
/// Function AddressTransforms collection.
pub type AddressTransforms = PrimaryMap<DefinedFuncIndex, FunctionAddressTransform>;

View File

@@ -0,0 +1,177 @@
//! Support for compiling with Cranelift.
use crate::compilation::{
AddressTransforms, Compilation, CompileError, FunctionAddressTransform,
InstructionAddressTransform, Relocation, RelocationTarget, Relocations,
};
use crate::func_environ::{
get_func_name, get_imported_memory32_grow_name, get_imported_memory32_size_name,
get_memory32_grow_name, get_memory32_size_name, FuncEnvironment,
};
use crate::module::Module;
use crate::module_environ::FunctionBodyData;
use cranelift_codegen::binemit;
use cranelift_codegen::ir;
use cranelift_codegen::ir::ExternalName;
use cranelift_codegen::isa;
use cranelift_codegen::Context;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator};
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use std::vec::Vec;
/// Implementation of a relocation sink that just saves all the information for later
struct RelocSink {
/// Relocations recorded for the function.
func_relocs: Vec<Relocation>,
}
impl binemit::RelocSink for RelocSink {
fn reloc_ebb(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_ebb_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("ebb headers not yet implemented");
}
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
reloc: binemit::Reloc,
name: &ExternalName,
addend: binemit::Addend,
) {
let reloc_target = if *name == get_memory32_grow_name() {
RelocationTarget::Memory32Grow
} else if *name == get_imported_memory32_grow_name() {
RelocationTarget::ImportedMemory32Grow
} else if *name == get_memory32_size_name() {
RelocationTarget::Memory32Size
} else if *name == get_imported_memory32_size_name() {
RelocationTarget::ImportedMemory32Size
} else if let ExternalName::User { namespace, index } = *name {
debug_assert!(namespace == 0);
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
self.func_relocs.push(Relocation {
reloc,
reloc_target,
offset,
addend,
});
}
fn reloc_jt(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_jt: ir::JumpTable,
) {
panic!("jump tables not yet implemented");
}
}
impl RelocSink {
/// Return a new `RelocSink` instance.
pub fn new() -> Self {
Self {
func_relocs: Vec::new(),
}
}
}
fn get_address_transform(
context: &Context,
isa: &isa::TargetIsa,
) -> Vec<InstructionAddressTransform> {
let mut result = Vec::new();
let func = &context.func;
let mut ebbs = func.layout.ebbs().collect::<Vec<_>>();
ebbs.sort_by_key(|ebb| func.offsets[*ebb]); // Ensure inst offsets always increase
let encinfo = isa.encoding_info();
for ebb in ebbs {
for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) {
let srcloc = func.srclocs[inst];
result.push(InstructionAddressTransform {
srcloc,
code_offset: offset as usize,
code_len: size as usize,
});
}
}
result
}
/// Compile the module using Cranelift, producing a compilation result with
/// associated relocations.
pub fn compile_module<'data, 'module>(
module: &'module Module,
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
isa: &dyn isa::TargetIsa,
generate_debug_info: bool,
) -> Result<(Compilation, Relocations, AddressTransforms), CompileError> {
let mut functions = PrimaryMap::with_capacity(function_body_inputs.len());
let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len());
let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len());
function_body_inputs
.into_iter()
.collect::<Vec<(DefinedFuncIndex, &FunctionBodyData<'data>)>>()
.par_iter()
.map(|(i, input)| {
let func_index = module.func_index(*i);
let mut context = Context::new();
context.func.name = get_func_name(func_index);
context.func.signature = module.signatures[module.functions[func_index]].clone();
let mut trans = FuncTranslator::new();
trans
.translate(
input.data,
input.module_offset,
&mut context.func,
&mut FuncEnvironment::new(isa.frontend_config(), module),
)
.map_err(CompileError::Wasm)?;
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink::new();
let mut trap_sink = binemit::NullTrapSink {};
context
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
.map_err(CompileError::Codegen)?;
let address_transform = if generate_debug_info {
let body_len = code_buf.len();
let at = get_address_transform(&context, isa);
Some(FunctionAddressTransform {
locations: at,
body_offset: 0,
body_len,
})
} else {
None
};
Ok((code_buf, reloc_sink.func_relocs, address_transform))
})
.collect::<Result<Vec<_>, CompileError>>()?
.into_iter()
.for_each(|(function, relocs, address_transform)| {
functions.push(function);
relocations.push(relocs);
if let Some(address_transform) = address_transform {
address_transforms.push(address_transform);
}
});
// TODO: Reorganize where we create the Vec for the resolved imports.
Ok((Compilation::new(functions), relocations, address_transforms))
}

View File

@@ -0,0 +1,539 @@
use crate::module::{MemoryPlan, MemoryStyle, Module, TableStyle};
use crate::vmoffsets::VMOffsets;
use crate::WASM_PAGE_SIZE;
use cast;
use core::clone::Clone;
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir;
use cranelift_codegen::ir::condcodes::*;
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{
AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature,
};
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_entity::EntityRef;
use cranelift_wasm::{
self, FuncIndex, GlobalIndex, GlobalVariable, MemoryIndex, SignatureIndex, TableIndex,
WasmResult,
};
use std::vec::Vec;
/// Compute an `ir::ExternalName` for a given wasm function index.
pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
ir::ExternalName::user(0, func_index.as_u32())
}
/// Compute an `ir::ExternalName` for the `memory.grow` libcall for
/// 32-bit locally-defined memories.
pub fn get_memory32_grow_name() -> ir::ExternalName {
ir::ExternalName::user(1, 0)
}
/// Compute an `ir::ExternalName` for the `memory.grow` libcall for
/// 32-bit imported memories.
pub fn get_imported_memory32_grow_name() -> ir::ExternalName {
ir::ExternalName::user(1, 1)
}
/// Compute an `ir::ExternalName` for the `memory.size` libcall for
/// 32-bit locally-defined memories.
pub fn get_memory32_size_name() -> ir::ExternalName {
ir::ExternalName::user(1, 2)
}
/// Compute an `ir::ExternalName` for the `memory.size` libcall for
/// 32-bit imported memories.
pub fn get_imported_memory32_size_name() -> ir::ExternalName {
ir::ExternalName::user(1, 3)
}
/// The `FuncEnvironment` implementation for use by the `ModuleEnvironment`.
pub struct FuncEnvironment<'module_environment> {
/// Target-specified configuration.
target_config: TargetFrontendConfig,
/// The module-level environment which this function-level environment belongs to.
module: &'module_environment Module,
/// The Cranelift global holding the vmctx address.
vmctx: Option<ir::GlobalValue>,
/// The external function declaration for implementing wasm's `memory.size`
/// for locally-defined 32-bit memories.
memory32_size_extfunc: Option<FuncRef>,
/// The external function declaration for implementing wasm's `memory.size`
/// for imported 32-bit memories.
imported_memory32_size_extfunc: Option<FuncRef>,
/// The external function declaration for implementing wasm's `memory.grow`
/// for locally-defined memories.
memory_grow_extfunc: Option<FuncRef>,
/// The external function declaration for implementing wasm's `memory.grow`
/// for imported memories.
imported_memory_grow_extfunc: Option<FuncRef>,
/// Offsets to struct fields accessed by JIT code.
offsets: VMOffsets,
}
impl<'module_environment> FuncEnvironment<'module_environment> {
pub fn new(target_config: TargetFrontendConfig, module: &'module_environment Module) -> Self {
Self {
target_config,
module,
vmctx: None,
memory32_size_extfunc: None,
imported_memory32_size_extfunc: None,
memory_grow_extfunc: None,
imported_memory_grow_extfunc: None,
offsets: VMOffsets::new(target_config.pointer_bytes(), module),
}
}
fn pointer_type(&self) -> ir::Type {
self.target_config.pointer_type()
}
fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue {
self.vmctx.unwrap_or_else(|| {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
self.vmctx = Some(vmctx);
vmctx
})
}
fn get_memory_grow_sig(&self, func: &mut Function) -> ir::SigRef {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
AbiParam::new(I32),
AbiParam::new(I32),
],
returns: vec![AbiParam::new(I32)],
call_conv: self.target_config.default_call_conv,
})
}
/// Return the memory.grow function to call for the given index, along with the
/// translated index value to pass to it.
fn get_memory_grow_func(
&mut self,
func: &mut Function,
index: MemoryIndex,
) -> (FuncRef, usize) {
if self.module.is_imported_memory(index) {
let extfunc = self.imported_memory_grow_extfunc.unwrap_or_else(|| {
let sig_ref = self.get_memory_grow_sig(func);
func.import_function(ExtFuncData {
name: get_imported_memory32_grow_name(),
signature: sig_ref,
// We currently allocate all code segments independently, so nothing
// is colocated.
colocated: false,
})
});
self.imported_memory_grow_extfunc = Some(extfunc);
(extfunc, index.index())
} else {
let extfunc = self.memory_grow_extfunc.unwrap_or_else(|| {
let sig_ref = self.get_memory_grow_sig(func);
func.import_function(ExtFuncData {
name: get_memory32_grow_name(),
signature: sig_ref,
// We currently allocate all code segments independently, so nothing
// is colocated.
colocated: false,
})
});
self.memory_grow_extfunc = Some(extfunc);
(
extfunc,
self.module.defined_memory_index(index).unwrap().index(),
)
}
}
fn get_memory32_size_sig(&self, func: &mut Function) -> ir::SigRef {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
AbiParam::new(I32),
],
returns: vec![AbiParam::new(I32)],
call_conv: self.target_config.default_call_conv,
})
}
/// Return the memory.size function to call for the given index, along with the
/// translated index value to pass to it.
fn get_memory_size_func(
&mut self,
func: &mut Function,
index: MemoryIndex,
) -> (FuncRef, usize) {
if self.module.is_imported_memory(index) {
let extfunc = self.imported_memory32_size_extfunc.unwrap_or_else(|| {
let sig_ref = self.get_memory32_size_sig(func);
func.import_function(ExtFuncData {
name: get_imported_memory32_size_name(),
signature: sig_ref,
// We currently allocate all code segments independently, so nothing
// is colocated.
colocated: false,
})
});
self.imported_memory32_size_extfunc = Some(extfunc);
(extfunc, index.index())
} else {
let extfunc = self.memory32_size_extfunc.unwrap_or_else(|| {
let sig_ref = self.get_memory32_size_sig(func);
func.import_function(ExtFuncData {
name: get_memory32_size_name(),
signature: sig_ref,
// We currently allocate all code segments independently, so nothing
// is colocated.
colocated: false,
})
});
self.memory32_size_extfunc = Some(extfunc);
(
extfunc,
self.module.defined_memory_index(index).unwrap().index(),
)
}
}
}
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
fn target_config(&self) -> TargetFrontendConfig {
self.target_config
}
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table {
let pointer_type = self.pointer_type();
let (ptr, base_offset, current_elements_offset) = {
let vmctx = self.vmctx(func);
if let Some(def_index) = self.module.defined_table_index(index) {
let base_offset =
cast::i32(self.offsets.vmctx_vmtable_definition_base(def_index)).unwrap();
let current_elements_offset = cast::i32(
self.offsets
.vmctx_vmtable_definition_current_elements(def_index),
)
.unwrap();
(vmctx, base_offset, current_elements_offset)
} else {
let from_offset = self.offsets.vmctx_vmtable_import_from(index);
let table = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(cast::i32(from_offset).unwrap()),
global_type: pointer_type,
readonly: true,
});
let base_offset = i32::from(self.offsets.vmtable_definition_base());
let current_elements_offset =
i32::from(self.offsets.vmtable_definition_current_elements());
(table, base_offset, current_elements_offset)
}
};
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
base: ptr,
offset: Offset32::new(base_offset),
global_type: pointer_type,
readonly: false,
});
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
base: ptr,
offset: Offset32::new(current_elements_offset),
global_type: self.offsets.type_of_vmtable_definition_current_elements(),
readonly: false,
});
let element_size = match self.module.table_plans[index].style {
TableStyle::CallerChecksSignature => {
u64::from(self.offsets.size_of_vmcaller_checked_anyfunc())
}
};
func.create_table(ir::TableData {
base_gv,
min_size: Uimm64::new(0),
bound_gv,
element_size: Uimm64::new(element_size),
index_type: I32,
})
}
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap {
let pointer_type = self.pointer_type();
let (ptr, base_offset, current_length_offset) = {
let vmctx = self.vmctx(func);
if let Some(def_index) = self.module.defined_memory_index(index) {
let base_offset =
cast::i32(self.offsets.vmctx_vmmemory_definition_base(def_index)).unwrap();
let current_length_offset = cast::i32(
self.offsets
.vmctx_vmmemory_definition_current_length(def_index),
)
.unwrap();
(vmctx, base_offset, current_length_offset)
} else {
let from_offset = self.offsets.vmctx_vmmemory_import_from(index);
let memory = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(cast::i32(from_offset).unwrap()),
global_type: pointer_type,
readonly: true,
});
let base_offset = i32::from(self.offsets.vmmemory_definition_base());
let current_length_offset =
i32::from(self.offsets.vmmemory_definition_current_length());
(memory, base_offset, current_length_offset)
}
};
// If we have a declared maximum, we can make this a "static" heap, which is
// allocated up front and never moved.
let (offset_guard_size, heap_style, readonly_base) = match self.module.memory_plans[index] {
MemoryPlan {
memory: _,
style: MemoryStyle::Dynamic,
offset_guard_size,
} => {
let heap_bound = func.create_global_value(ir::GlobalValueData::Load {
base: ptr,
offset: Offset32::new(current_length_offset),
global_type: self.offsets.type_of_vmmemory_definition_current_length(),
readonly: false,
});
(
Uimm64::new(offset_guard_size),
ir::HeapStyle::Dynamic {
bound_gv: heap_bound,
},
false,
)
}
MemoryPlan {
memory: _,
style: MemoryStyle::Static { bound },
offset_guard_size,
} => (
Uimm64::new(offset_guard_size),
ir::HeapStyle::Static {
bound: Uimm64::new(u64::from(bound) * u64::from(WASM_PAGE_SIZE)),
},
true,
),
};
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
base: ptr,
offset: Offset32::new(base_offset),
global_type: pointer_type,
readonly: readonly_base,
});
func.create_heap(ir::HeapData {
base: heap_base,
min_size: 0.into(),
offset_guard_size,
style: heap_style,
index_type: I32,
})
}
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
let pointer_type = self.pointer_type();
let (ptr, offset) = {
let vmctx = self.vmctx(func);
if let Some(def_index) = self.module.defined_global_index(index) {
let offset = cast::i32(self.offsets.vmctx_vmglobal_definition(def_index)).unwrap();
(vmctx, offset)
} else {
let from_offset = self.offsets.vmctx_vmglobal_import_from(index);
let global = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(cast::i32(from_offset).unwrap()),
global_type: pointer_type,
readonly: true,
});
(global, 0)
}
};
GlobalVariable::Memory {
gv: ptr,
offset: offset.into(),
ty: self.module.globals[index].ty,
}
}
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
func.import_signature(self.module.signatures[index].clone())
}
fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef {
let sigidx = self.module.functions[index];
let signature = func.import_signature(self.module.signatures[sigidx].clone());
let name = get_func_name(index);
func.import_function(ir::ExtFuncData {
name,
signature,
// We currently allocate all code segments independently, so nothing
// is colocated.
colocated: false,
})
}
fn translate_call_indirect(
&mut self,
mut pos: FuncCursor<'_>,
table_index: TableIndex,
table: ir::Table,
sig_index: SignatureIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {
let pointer_type = self.pointer_type();
let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0);
// If necessary, check the signature.
match self.module.table_plans[table_index].style {
TableStyle::CallerChecksSignature => {
let sig_id_size = self.offsets.size_of_vmshared_signature_index();
let sig_id_type = Type::int(u16::from(sig_id_size) * 8).unwrap();
let vmctx = self.vmctx(pos.func);
let base = pos.ins().global_value(pointer_type, vmctx);
let offset =
cast::i32(self.offsets.vmctx_vmshared_signature_id(sig_index)).unwrap();
// Load the caller ID.
let mut mem_flags = ir::MemFlags::trusted();
mem_flags.set_readonly();
let caller_sig_id = pos.ins().load(sig_id_type, mem_flags, base, offset);
// Load the callee ID.
let mem_flags = ir::MemFlags::trusted();
let callee_sig_id = pos.ins().load(
sig_id_type,
mem_flags,
table_entry_addr,
i32::from(self.offsets.vmcaller_checked_anyfunc_type_index()),
);
// Check that they match.
let cmp = pos.ins().icmp(IntCC::Equal, callee_sig_id, caller_sig_id);
pos.ins().trapz(cmp, ir::TrapCode::BadSignature);
}
}
// Dereference table_entry_addr to get the function address.
let mem_flags = ir::MemFlags::trusted();
let func_addr = pos.ins().load(
pointer_type,
mem_flags,
table_entry_addr,
i32::from(self.offsets.vmcaller_checked_anyfunc_func_ptr()),
);
let mut real_call_args = Vec::with_capacity(call_args.len() + 1);
// First append the callee vmctx address.
let vmctx = pos.ins().load(
pointer_type,
mem_flags,
table_entry_addr,
i32::from(self.offsets.vmcaller_checked_anyfunc_vmctx()),
);
real_call_args.push(vmctx);
// Then append the regular call arguments.
real_call_args.extend_from_slice(call_args);
Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args))
}
fn translate_call(
&mut self,
mut pos: FuncCursor<'_>,
callee_index: FuncIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {
let mut real_call_args = Vec::with_capacity(call_args.len() + 1);
// Handle direct calls to locally-defined functions.
if !self.module.is_imported_function(callee_index) {
// First append the callee vmctx address.
real_call_args.push(pos.func.special_param(ArgumentPurpose::VMContext).unwrap());
// Then append the regular call arguments.
real_call_args.extend_from_slice(call_args);
return Ok(pos.ins().call(callee, &real_call_args));
}
// Handle direct calls to imported functions. We use an indirect call
// so that we don't have to patch the code at runtime.
let pointer_type = self.pointer_type();
let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
let vmctx = self.vmctx(&mut pos.func);
let base = pos.ins().global_value(pointer_type, vmctx);
let mem_flags = ir::MemFlags::trusted();
// Load the callee address.
let body_offset =
cast::i32(self.offsets.vmctx_vmfunction_import_body(callee_index)).unwrap();
let func_addr = pos.ins().load(pointer_type, mem_flags, base, body_offset);
// First append the callee vmctx address.
let vmctx_offset =
cast::i32(self.offsets.vmctx_vmfunction_import_vmctx(callee_index)).unwrap();
let vmctx = pos.ins().load(pointer_type, mem_flags, base, vmctx_offset);
real_call_args.push(vmctx);
// Then append the regular call arguments.
real_call_args.extend_from_slice(call_args);
Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args))
}
fn translate_memory_grow(
&mut self,
mut pos: FuncCursor<'_>,
index: MemoryIndex,
_heap: ir::Heap,
val: ir::Value,
) -> WasmResult<ir::Value> {
let (memory_grow_func, index_arg) = self.get_memory_grow_func(&mut pos.func, index);
let memory_index = pos.ins().iconst(I32, index_arg as i64);
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
let call_inst = pos
.ins()
.call(memory_grow_func, &[vmctx, val, memory_index]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
fn translate_memory_size(
&mut self,
mut pos: FuncCursor<'_>,
index: MemoryIndex,
_heap: ir::Heap,
) -> WasmResult<ir::Value> {
let (memory_size_func, index_arg) = self.get_memory_size_func(&mut pos.func, index);
let memory_index = pos.ins().iconst(I32, index_arg as i64);
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
let call_inst = pos.ins().call(memory_size_func, &[vmctx, memory_index]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
}

View File

@@ -0,0 +1,70 @@
//! Standalone environment for WebAssembly using Cranelift. Provides functions to translate
//! `get_global`, `set_global`, `memory.size`, `memory.grow`, `call_indirect` that hardcode in
//! the translation the base addresses of regions of memory that will hold the globals, tables and
//! linear memories.
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
#![warn(unused_import_braces)]
#![cfg_attr(feature = "std", deny(unstable_features))]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
#![cfg_attr(
feature = "cargo-clippy",
allow(clippy::new_without_default, clippy::new_without_default_derive)
)]
#![cfg_attr(
feature = "cargo-clippy",
warn(
clippy::float_arithmetic,
clippy::mut_mut,
clippy::nonminimal_bool,
clippy::option_map_unwrap_or,
clippy::option_map_unwrap_or_else,
clippy::print_stdout,
clippy::unicode_not_nfc,
clippy::use_self
)
)]
#![no_std]
#![cfg_attr(not(feature = "std"), feature(alloc))]
#[cfg(not(feature = "std"))]
#[macro_use]
extern crate alloc as std;
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
#[macro_use]
extern crate failure_derive;
mod compilation;
mod func_environ;
mod module;
mod module_environ;
mod tunables;
mod vmoffsets;
pub mod cranelift;
pub use crate::compilation::{
AddressTransforms, Compilation, CompileError, InstructionAddressTransform, Relocation,
RelocationTarget, Relocations,
};
pub use crate::module::{
Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
};
pub use crate::module_environ::{
translate_signature, DataInitializer, DataInitializerLocation, FunctionBodyData,
ModuleEnvironment, ModuleTranslation,
};
pub use crate::tunables::Tunables;
pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets};
/// WebAssembly page sizes are defined to be 64KiB.
pub const WASM_PAGE_SIZE: u32 = 0x10000;
/// The number of pages we can have before we run out of byte index space.
pub const WASM_MAX_PAGES: u32 = 0x10000;
/// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -0,0 +1,281 @@
//! Data structures for representing decoded wasm modules.
use crate::tunables::Tunables;
use cranelift_codegen::ir;
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::{
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, Global,
GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex,
};
use indexmap::IndexMap;
use std::boxed::Box;
use std::string::String;
use std::vec::Vec;
/// A WebAssembly table initializer.
#[derive(Clone, Debug)]
pub struct TableElements {
/// The index of a table to initialize.
pub table_index: TableIndex,
/// Optionally, a global variable giving a base index.
pub base: Option<GlobalIndex>,
/// The offset to add to the base.
pub offset: usize,
/// The values to write into the table elements.
pub elements: Box<[FuncIndex]>,
}
/// An entity to export.
#[derive(Clone, Debug)]
pub enum Export {
/// Function export.
Function(FuncIndex),
/// Table export.
Table(TableIndex),
/// Memory export.
Memory(MemoryIndex),
/// Global export.
Global(GlobalIndex),
}
/// Implemenation styles for WebAssembly linear memory.
#[derive(Debug, Clone)]
pub enum MemoryStyle {
/// The actual memory can be resized and moved.
Dynamic,
/// Addresss space is allocated up front.
Static {
/// The number of mapped and unmapped pages.
bound: u32,
},
}
impl MemoryStyle {
/// Decide on an implementation style for the given `Memory`.
pub fn for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64) {
if let Some(maximum) = memory.maximum {
if maximum <= tunables.static_memory_bound {
// A heap with a declared maximum can be immovable, so make
// it static.
assert!(tunables.static_memory_bound >= memory.minimum);
return (
MemoryStyle::Static {
bound: tunables.static_memory_bound,
},
tunables.static_memory_offset_guard_size,
);
}
}
// Otherwise, make it dynamic.
(
MemoryStyle::Dynamic,
tunables.dynamic_memory_offset_guard_size,
)
}
}
/// A WebAssembly linear memory description along with our chosen style for
/// implementing it.
#[derive(Debug, Clone)]
pub struct MemoryPlan {
/// The WebAssembly linear memory description.
pub memory: Memory,
/// Our chosen implementation style.
pub style: MemoryStyle,
/// Our chosen offset-guard size.
pub offset_guard_size: u64,
}
impl MemoryPlan {
/// Draw up a plan for implementing a `Memory`.
pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self {
let (style, offset_guard_size) = MemoryStyle::for_memory(memory, tunables);
Self {
memory,
style,
offset_guard_size,
}
}
}
/// Implemenation styles for WebAssembly tables.
#[derive(Debug, Clone)]
pub enum TableStyle {
/// Signatures are stored in the table and checked in the caller.
CallerChecksSignature,
}
impl TableStyle {
/// Decide on an implementation style for the given `Table`.
pub fn for_table(_table: Table, _tunables: &Tunables) -> Self {
TableStyle::CallerChecksSignature
}
}
/// A WebAssembly table description along with our chosen style for
/// implementing it.
#[derive(Debug, Clone)]
pub struct TablePlan {
/// The WebAssembly table description.
pub table: cranelift_wasm::Table,
/// Our chosen implementation style.
pub style: TableStyle,
}
impl TablePlan {
/// Draw up a plan for implementing a `Table`.
pub fn for_table(table: Table, tunables: &Tunables) -> Self {
let style = TableStyle::for_table(table, tunables);
Self { table, style }
}
}
/// A translated WebAssembly module, excluding the function bodies and
/// memory initializers.
#[derive(Debug)]
pub struct Module {
/// Unprocessed signatures exactly as provided by `declare_signature()`.
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
/// Names of imported functions.
pub imported_funcs: PrimaryMap<FuncIndex, (String, String)>,
/// Names of imported tables.
pub imported_tables: PrimaryMap<TableIndex, (String, String)>,
/// Names of imported memories.
pub imported_memories: PrimaryMap<MemoryIndex, (String, String)>,
/// Names of imported globals.
pub imported_globals: PrimaryMap<GlobalIndex, (String, String)>,
/// Types of functions, imported and local.
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
/// WebAssembly tables.
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
/// WebAssembly linear memory plans.
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
/// WebAssembly global variables.
pub globals: PrimaryMap<GlobalIndex, Global>,
/// Exported entities.
pub exports: IndexMap<String, Export>,
/// The module "start" function, if present.
pub start_func: Option<FuncIndex>,
/// WebAssembly table initializers.
pub table_elements: Vec<TableElements>,
}
impl Module {
/// Allocates the module data structures.
pub fn new() -> Self {
Self {
signatures: PrimaryMap::new(),
imported_funcs: PrimaryMap::new(),
imported_tables: PrimaryMap::new(),
imported_memories: PrimaryMap::new(),
imported_globals: PrimaryMap::new(),
functions: PrimaryMap::new(),
table_plans: PrimaryMap::new(),
memory_plans: PrimaryMap::new(),
globals: PrimaryMap::new(),
exports: IndexMap::new(),
start_func: None,
table_elements: Vec::new(),
}
}
/// Convert a `DefinedFuncIndex` into a `FuncIndex`.
pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
FuncIndex::new(self.imported_funcs.len() + defined_func.index())
}
/// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the
/// index is an imported function.
pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> {
if func.index() < self.imported_funcs.len() {
None
} else {
Some(DefinedFuncIndex::new(
func.index() - self.imported_funcs.len(),
))
}
}
/// Test whether the given function index is for an imported function.
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
index.index() < self.imported_funcs.len()
}
/// Convert a `DefinedTableIndex` into a `TableIndex`.
pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
TableIndex::new(self.imported_tables.len() + defined_table.index())
}
/// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the
/// index is an imported table.
pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
if table.index() < self.imported_tables.len() {
None
} else {
Some(DefinedTableIndex::new(
table.index() - self.imported_tables.len(),
))
}
}
/// Test whether the given table index is for an imported table.
pub fn is_imported_table(&self, index: TableIndex) -> bool {
index.index() < self.imported_tables.len()
}
/// Convert a `DefinedMemoryIndex` into a `MemoryIndex`.
pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
MemoryIndex::new(self.imported_memories.len() + defined_memory.index())
}
/// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the
/// index is an imported memory.
pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
if memory.index() < self.imported_memories.len() {
None
} else {
Some(DefinedMemoryIndex::new(
memory.index() - self.imported_memories.len(),
))
}
}
/// Test whether the given memory index is for an imported memory.
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
index.index() < self.imported_memories.len()
}
/// Convert a `DefinedGlobalIndex` into a `GlobalIndex`.
pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
GlobalIndex::new(self.imported_globals.len() + defined_global.index())
}
/// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the
/// index is an imported global.
pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
if global.index() < self.imported_globals.len() {
None
} else {
Some(DefinedGlobalIndex::new(
global.index() - self.imported_globals.len(),
))
}
}
/// Test whether the given global index is for an imported global.
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
index.index() < self.imported_globals.len()
}
}

View File

@@ -0,0 +1,339 @@
use crate::func_environ::FuncEnvironment;
use crate::module::{Export, MemoryPlan, Module, TableElements, TablePlan};
use crate::tunables::Tunables;
use cranelift_codegen::ir;
use cranelift_codegen::ir::{AbiParam, ArgumentPurpose};
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{
self, translate_module, DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex,
SignatureIndex, Table, TableIndex, WasmResult,
};
use std::boxed::Box;
use std::string::String;
use std::vec::Vec;
/// Contains function data: byte code and its offset in the module.
pub struct FunctionBodyData<'a> {
/// Body byte code.
pub data: &'a [u8],
/// Body offset in the module file.
pub module_offset: usize,
}
/// The result of translating via `ModuleEnvironment`. Function bodies are not
/// yet translated, and data initializers have not yet been copied out of the
/// original buffer.
pub struct ModuleTranslation<'data> {
/// Compilation setting flags.
pub target_config: TargetFrontendConfig,
/// Module information.
pub module: Module,
/// References to the function bodies.
pub function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
/// References to the data initializers.
pub data_initializers: Vec<DataInitializer<'data>>,
/// Tunable parameters.
pub tunables: Tunables,
}
impl<'data> ModuleTranslation<'data> {
/// Return a new `FuncEnvironment` for translating a function.
pub fn func_env(&self) -> FuncEnvironment<'_> {
FuncEnvironment::new(self.target_config, &self.module)
}
}
/// Object containing the standalone environment information.
pub struct ModuleEnvironment<'data> {
/// The result to be filled in.
result: ModuleTranslation<'data>,
}
impl<'data> ModuleEnvironment<'data> {
/// Allocates the enironment data structures.
pub fn new(target_config: TargetFrontendConfig, tunables: Tunables) -> Self {
Self {
result: ModuleTranslation {
target_config,
module: Module::new(),
function_body_inputs: PrimaryMap::new(),
data_initializers: Vec::new(),
tunables,
},
}
}
fn pointer_type(&self) -> ir::Type {
self.result.target_config.pointer_type()
}
/// Translate a wasm module using this environment. This consumes the
/// `ModuleEnvironment` and produces a `ModuleTranslation`.
pub fn translate(mut self, data: &'data [u8]) -> WasmResult<ModuleTranslation<'data>> {
translate_module(data, &mut self)?;
Ok(self.result)
}
}
/// This trait is useful for `translate_module` because it tells how to translate
/// enironment-dependent wasm instructions. These functions should not be called by the user.
impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data> {
fn target_config(&self) -> TargetFrontendConfig {
self.result.target_config
}
fn reserve_signatures(&mut self, num: u32) {
self.result
.module
.signatures
.reserve_exact(cast::usize(num));
}
fn declare_signature(&mut self, sig: ir::Signature) {
let sig = translate_signature(sig, self.pointer_type());
// TODO: Deduplicate signatures.
self.result.module.signatures.push(sig);
}
fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &str, field: &str) {
debug_assert_eq!(
self.result.module.functions.len(),
self.result.module.imported_funcs.len(),
"Imported functions must be declared first"
);
self.result.module.functions.push(sig_index);
self.result
.module
.imported_funcs
.push((String::from(module), String::from(field)));
}
fn declare_table_import(&mut self, table: Table, module: &str, field: &str) {
debug_assert_eq!(
self.result.module.table_plans.len(),
self.result.module.imported_tables.len(),
"Imported tables must be declared first"
);
let plan = TablePlan::for_table(table, &self.result.tunables);
self.result.module.table_plans.push(plan);
self.result
.module
.imported_tables
.push((String::from(module), String::from(field)));
}
fn declare_memory_import(&mut self, memory: Memory, module: &str, field: &str) {
debug_assert_eq!(
self.result.module.memory_plans.len(),
self.result.module.imported_memories.len(),
"Imported memories must be declared first"
);
let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
self.result.module.memory_plans.push(plan);
self.result
.module
.imported_memories
.push((String::from(module), String::from(field)));
}
fn declare_global_import(&mut self, global: Global, module: &str, field: &str) {
debug_assert_eq!(
self.result.module.globals.len(),
self.result.module.imported_globals.len(),
"Imported globals must be declared first"
);
self.result.module.globals.push(global);
self.result
.module
.imported_globals
.push((String::from(module), String::from(field)));
}
fn finish_imports(&mut self) {
self.result.module.imported_funcs.shrink_to_fit();
self.result.module.imported_tables.shrink_to_fit();
self.result.module.imported_memories.shrink_to_fit();
self.result.module.imported_globals.shrink_to_fit();
}
fn reserve_func_types(&mut self, num: u32) {
self.result.module.functions.reserve_exact(cast::usize(num));
self.result
.function_body_inputs
.reserve_exact(cast::usize(num));
}
fn declare_func_type(&mut self, sig_index: SignatureIndex) {
self.result.module.functions.push(sig_index);
}
fn reserve_tables(&mut self, num: u32) {
self.result
.module
.table_plans
.reserve_exact(cast::usize(num));
}
fn declare_table(&mut self, table: Table) {
let plan = TablePlan::for_table(table, &self.result.tunables);
self.result.module.table_plans.push(plan);
}
fn reserve_memories(&mut self, num: u32) {
self.result
.module
.memory_plans
.reserve_exact(cast::usize(num));
}
fn declare_memory(&mut self, memory: Memory) {
let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
self.result.module.memory_plans.push(plan);
}
fn reserve_globals(&mut self, num: u32) {
self.result.module.globals.reserve_exact(cast::usize(num));
}
fn declare_global(&mut self, global: Global) {
self.result.module.globals.push(global);
}
fn reserve_exports(&mut self, num: u32) {
self.result.module.exports.reserve(cast::usize(num));
}
fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) {
self.result
.module
.exports
.insert(String::from(name), Export::Function(func_index));
}
fn declare_table_export(&mut self, table_index: TableIndex, name: &str) {
self.result
.module
.exports
.insert(String::from(name), Export::Table(table_index));
}
fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) {
self.result
.module
.exports
.insert(String::from(name), Export::Memory(memory_index));
}
fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) {
self.result
.module
.exports
.insert(String::from(name), Export::Global(global_index));
}
fn declare_start_func(&mut self, func_index: FuncIndex) {
debug_assert!(self.result.module.start_func.is_none());
self.result.module.start_func = Some(func_index);
}
fn reserve_table_elements(&mut self, num: u32) {
self.result
.module
.table_elements
.reserve_exact(cast::usize(num));
}
fn declare_table_elements(
&mut self,
table_index: TableIndex,
base: Option<GlobalIndex>,
offset: usize,
elements: Box<[FuncIndex]>,
) {
self.result.module.table_elements.push(TableElements {
table_index,
base,
offset,
elements,
});
}
fn define_function_body(
&mut self,
body_bytes: &'data [u8],
body_offset: usize,
) -> WasmResult<()> {
self.result.function_body_inputs.push(FunctionBodyData {
data: body_bytes,
module_offset: body_offset,
});
Ok(())
}
fn reserve_data_initializers(&mut self, num: u32) {
self.result
.data_initializers
.reserve_exact(cast::usize(num));
}
fn declare_data_initialization(
&mut self,
memory_index: MemoryIndex,
base: Option<GlobalIndex>,
offset: usize,
data: &'data [u8],
) {
self.result.data_initializers.push(DataInitializer {
location: DataInitializerLocation {
memory_index,
base,
offset,
},
data,
});
}
}
/// Add environment-specific function parameters.
pub fn translate_signature(mut sig: ir::Signature, pointer_type: ir::Type) -> ir::Signature {
// Prepend the vmctx argument.
sig.params.insert(
0,
AbiParam::special(pointer_type, ArgumentPurpose::VMContext),
);
sig
}
/// A memory index and offset within that memory where a data initialization
/// should is to be performed.
#[derive(Clone)]
pub struct DataInitializerLocation {
/// The index of the memory to initialize.
pub memory_index: MemoryIndex,
/// Optionally a globalvar base to initialize at.
pub base: Option<GlobalIndex>,
/// A constant offset to initialize at.
pub offset: usize,
}
/// A data initializer for linear memory.
pub struct DataInitializer<'data> {
/// The location where the initialization is to be performed.
pub location: DataInitializerLocation,
/// The initialization data.
pub data: &'data [u8],
}

View File

@@ -0,0 +1,44 @@
/// Tunable parameters for WebAssembly compilation.
#[derive(Clone)]
pub struct Tunables {
/// For static heaps, the size of the heap protected by bounds checking.
pub static_memory_bound: u32,
/// The size of the offset guard for static heaps.
pub static_memory_offset_guard_size: u64,
/// The size of the offset guard for dynamic heaps.
pub dynamic_memory_offset_guard_size: u64,
}
impl Default for Tunables {
fn default() -> Self {
Self {
#[cfg(target_pointer_width = "32")]
/// Size in wasm pages of the bound for static memories.
static_memory_bound: 0x4000,
#[cfg(target_pointer_width = "64")]
/// Size in wasm pages of the bound for static memories.
///
/// When we allocate 4 GiB of address space, we can avoid the
/// need for explicit bounds checks.
static_memory_bound: 0x1_0000,
#[cfg(target_pointer_width = "32")]
/// Size in bytes of the offset guard for static memories.
static_memory_offset_guard_size: 0x1_0000,
#[cfg(target_pointer_width = "64")]
/// Size in bytes of the offset guard for static memories.
///
/// Allocating 2 GiB of address space lets us translate wasm
/// offsets into x86 offsets as aggressively as we can.
static_memory_offset_guard_size: 0x8000_0000,
/// Size in bytes of the offset guard for dynamic memories.
///
/// Allocate a small guard to optimize common cases but without
/// wasting too much memor.
dynamic_memory_offset_guard_size: 0x1_0000,
}
}
}

View File

@@ -0,0 +1,536 @@
//! Offsets and sizes of various structs in wasmtime-runtime's vmcontext
//! module.
use crate::module::Module;
use cast;
use cranelift_codegen::ir;
use cranelift_wasm::{
DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, GlobalIndex, MemoryIndex,
SignatureIndex, TableIndex,
};
#[cfg(target_pointer_width = "32")]
fn cast_to_u32(sz: usize) -> u32 {
cast::u32(sz)
}
#[cfg(target_pointer_width = "64")]
fn cast_to_u32(sz: usize) -> u32 {
match cast::u32(sz) {
Ok(x) => x,
Err(_) => panic!("overflow in cast from usize to u32"),
}
}
/// This class computes offsets to fields within `VMContext` and other
/// related structs that JIT code accesses directly.
pub struct VMOffsets {
/// The size in bytes of a pointer on the target.
pub pointer_size: u8,
/// The number of signature declarations in the module.
pub num_signature_ids: u32,
/// The number of imported functions in the module.
pub num_imported_functions: u32,
/// The number of imported tables in the module.
pub num_imported_tables: u32,
/// The number of imported memories in the module.
pub num_imported_memories: u32,
/// The number of imported globals in the module.
pub num_imported_globals: u32,
/// The number of defined tables in the module.
pub num_defined_tables: u32,
/// The number of defined memories in the module.
pub num_defined_memories: u32,
/// The number of defined globals in the module.
pub num_defined_globals: u32,
}
impl VMOffsets {
/// Return a new `VMOffsets` instance, for a given pointer size.
pub fn new(pointer_size: u8, module: &Module) -> Self {
Self {
pointer_size,
num_signature_ids: cast_to_u32(module.signatures.len()),
num_imported_functions: cast_to_u32(module.imported_funcs.len()),
num_imported_tables: cast_to_u32(module.imported_tables.len()),
num_imported_memories: cast_to_u32(module.imported_memories.len()),
num_imported_globals: cast_to_u32(module.imported_globals.len()),
num_defined_tables: cast_to_u32(module.table_plans.len()),
num_defined_memories: cast_to_u32(module.memory_plans.len()),
num_defined_globals: cast_to_u32(module.globals.len()),
}
}
}
/// Offsets for `VMFunctionImport`.
impl VMOffsets {
/// The offset of the `body` field.
#[allow(clippy::erasing_op)]
pub fn vmfunction_import_body(&self) -> u8 {
0 * self.pointer_size
}
/// The offset of the `vmctx` field.
#[allow(clippy::identity_op)]
pub fn vmfunction_import_vmctx(&self) -> u8 {
1 * self.pointer_size
}
/// Return the size of `VMFunctionImport`.
pub fn size_of_vmfunction_import(&self) -> u8 {
2 * self.pointer_size
}
}
/// Offsets for `*const VMFunctionBody`.
impl VMOffsets {
/// The size of the `current_elements` field.
#[allow(clippy::identity_op)]
pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
1 * self.pointer_size
}
}
/// Offsets for `VMTableImport`.
impl VMOffsets {
/// The offset of the `from` field.
#[allow(clippy::erasing_op)]
pub fn vmtable_import_from(&self) -> u8 {
0 * self.pointer_size
}
/// The offset of the `vmctx` field.
#[allow(clippy::identity_op)]
pub fn vmtable_import_vmctx(&self) -> u8 {
1 * self.pointer_size
}
/// Return the size of `VMTableImport`.
pub fn size_of_vmtable_import(&self) -> u8 {
2 * self.pointer_size
}
}
/// Offsets for `VMTableDefinition`.
impl VMOffsets {
/// The offset of the `base` field.
#[allow(clippy::erasing_op)]
pub fn vmtable_definition_base(&self) -> u8 {
0 * self.pointer_size
}
/// The offset of the `current_elements` field.
#[allow(clippy::identity_op)]
pub fn vmtable_definition_current_elements(&self) -> u8 {
1 * self.pointer_size
}
/// The size of the `current_elements` field.
pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
4
}
/// Return the size of `VMTableDefinition`.
pub fn size_of_vmtable_definition(&self) -> u8 {
2 * self.pointer_size
}
/// The type of the `current_elements` field.
pub fn type_of_vmtable_definition_current_elements(&self) -> ir::Type {
ir::Type::int(u16::from(self.size_of_vmtable_definition_current_elements()) * 8).unwrap()
}
}
/// Offsets for `VMMemoryImport`.
impl VMOffsets {
/// The offset of the `from` field.
#[allow(clippy::erasing_op)]
pub fn vmmemory_import_from(&self) -> u8 {
0 * self.pointer_size
}
/// The offset of the `vmctx` field.
#[allow(clippy::identity_op)]
pub fn vmmemory_import_vmctx(&self) -> u8 {
1 * self.pointer_size
}
/// Return the size of `VMMemoryImport`.
pub fn size_of_vmmemory_import(&self) -> u8 {
2 * self.pointer_size
}
}
/// Offsets for `VMMemoryDefinition`.
impl VMOffsets {
/// The offset of the `base` field.
#[allow(clippy::erasing_op)]
pub fn vmmemory_definition_base(&self) -> u8 {
0 * self.pointer_size
}
/// The offset of the `current_length` field.
#[allow(clippy::identity_op)]
pub fn vmmemory_definition_current_length(&self) -> u8 {
1 * self.pointer_size
}
/// The size of the `current_length` field.
pub fn size_of_vmmemory_definition_current_length(&self) -> u8 {
4
}
/// Return the size of `VMMemoryDefinition`.
pub fn size_of_vmmemory_definition(&self) -> u8 {
2 * self.pointer_size
}
/// The type of the `current_length` field.
pub fn type_of_vmmemory_definition_current_length(&self) -> ir::Type {
ir::Type::int(u16::from(self.size_of_vmmemory_definition_current_length()) * 8).unwrap()
}
}
/// Offsets for `VMGlobalImport`.
impl VMOffsets {
/// The offset of the `from` field.
#[allow(clippy::erasing_op)]
pub fn vmglobal_import_from(&self) -> u8 {
0 * self.pointer_size
}
/// Return the size of `VMGlobalImport`.
#[allow(clippy::identity_op)]
pub fn size_of_vmglobal_import(&self) -> u8 {
1 * self.pointer_size
}
}
/// Offsets for `VMGlobalDefinition`.
impl VMOffsets {
/// Return the size of `VMGlobalDefinition`.
pub fn size_of_vmglobal_definition(&self) -> u8 {
8
}
}
/// Offsets for `VMSharedSignatureIndex`.
impl VMOffsets {
/// Return the size of `VMSharedSignatureIndex`.
pub fn size_of_vmshared_signature_index(&self) -> u8 {
4
}
}
/// Offsets for `VMCallerCheckedAnyfunc`.
impl VMOffsets {
/// The offset of the `func_ptr` field.
#[allow(clippy::erasing_op)]
pub fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
0 * self.pointer_size
}
/// The offset of the `type_index` field.
#[allow(clippy::identity_op)]
pub fn vmcaller_checked_anyfunc_type_index(&self) -> u8 {
1 * self.pointer_size
}
/// The offset of the `vmctx` field.
pub fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
2 * self.pointer_size
}
/// Return the size of `VMCallerCheckedAnyfunc`.
pub fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
3 * self.pointer_size
}
}
/// Offsets for `VMContext`.
impl VMOffsets {
/// The offset of the `signature_ids` array.
pub fn vmctx_signature_ids_begin(&self) -> u32 {
0
}
/// The offset of the `tables` array.
#[allow(clippy::erasing_op)]
pub fn vmctx_imported_functions_begin(&self) -> u32 {
self.vmctx_signature_ids_begin()
.checked_add(
self.num_signature_ids
.checked_mul(u32::from(self.size_of_vmshared_signature_index()))
.unwrap(),
)
.unwrap()
}
/// The offset of the `tables` array.
#[allow(clippy::identity_op)]
pub fn vmctx_imported_tables_begin(&self) -> u32 {
self.vmctx_imported_functions_begin()
.checked_add(
self.num_imported_functions
.checked_mul(u32::from(self.size_of_vmfunction_import()))
.unwrap(),
)
.unwrap()
}
/// The offset of the `memories` array.
pub fn vmctx_imported_memories_begin(&self) -> u32 {
self.vmctx_imported_tables_begin()
.checked_add(
self.num_imported_tables
.checked_mul(u32::from(self.size_of_vmtable_import()))
.unwrap(),
)
.unwrap()
}
/// The offset of the `globals` array.
pub fn vmctx_imported_globals_begin(&self) -> u32 {
self.vmctx_imported_memories_begin()
.checked_add(
self.num_imported_memories
.checked_mul(u32::from(self.size_of_vmmemory_import()))
.unwrap(),
)
.unwrap()
}
/// The offset of the `tables` array.
pub fn vmctx_tables_begin(&self) -> u32 {
self.vmctx_imported_globals_begin()
.checked_add(
self.num_imported_globals
.checked_mul(u32::from(self.size_of_vmglobal_import()))
.unwrap(),
)
.unwrap()
}
/// The offset of the `memories` array.
pub fn vmctx_memories_begin(&self) -> u32 {
self.vmctx_tables_begin()
.checked_add(
self.num_defined_tables
.checked_mul(u32::from(self.size_of_vmtable_definition()))
.unwrap(),
)
.unwrap()
}
/// The offset of the `globals` array.
pub fn vmctx_globals_begin(&self) -> u32 {
self.vmctx_memories_begin()
.checked_add(
self.num_defined_memories
.checked_mul(u32::from(self.size_of_vmmemory_definition()))
.unwrap(),
)
.unwrap()
}
/// Return the size of the `VMContext` allocation.
#[allow(dead_code)]
pub fn size_of_vmctx(&self) -> u32 {
self.vmctx_globals_begin()
.checked_add(
self.num_defined_globals
.checked_mul(u32::from(self.size_of_vmglobal_definition()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to `VMSharedSignatureId` index `index`.
pub fn vmctx_vmshared_signature_id(&self, index: SignatureIndex) -> u32 {
assert!(index.as_u32() < self.num_signature_ids);
self.vmctx_signature_ids_begin()
.checked_add(
index
.as_u32()
.checked_mul(u32::from(self.size_of_vmshared_signature_index()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to `VMFunctionImport` index `index`.
pub fn vmctx_vmfunction_import(&self, index: FuncIndex) -> u32 {
assert!(index.as_u32() < self.num_imported_functions);
self.vmctx_imported_functions_begin()
.checked_add(
index
.as_u32()
.checked_mul(u32::from(self.size_of_vmfunction_import()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to `VMTableImport` index `index`.
pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
assert!(index.as_u32() < self.num_imported_tables);
self.vmctx_imported_tables_begin()
.checked_add(
index
.as_u32()
.checked_mul(u32::from(self.size_of_vmtable_import()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to `VMMemoryImport` index `index`.
pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
assert!(index.as_u32() < self.num_imported_memories);
self.vmctx_imported_memories_begin()
.checked_add(
index
.as_u32()
.checked_mul(u32::from(self.size_of_vmmemory_import()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to `VMGlobalImport` index `index`.
pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
assert!(index.as_u32() < self.num_imported_globals);
self.vmctx_imported_globals_begin()
.checked_add(
index
.as_u32()
.checked_mul(u32::from(self.size_of_vmglobal_import()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to `VMTableDefinition` index `index`.
pub fn vmctx_vmtable_definition(&self, index: DefinedTableIndex) -> u32 {
assert!(index.as_u32() < self.num_defined_tables);
self.vmctx_tables_begin()
.checked_add(
index
.as_u32()
.checked_mul(u32::from(self.size_of_vmtable_definition()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to `VMMemoryDefinition` index `index`.
pub fn vmctx_vmmemory_definition(&self, index: DefinedMemoryIndex) -> u32 {
assert!(index.as_u32() < self.num_defined_memories);
self.vmctx_memories_begin()
.checked_add(
index
.as_u32()
.checked_mul(u32::from(self.size_of_vmmemory_definition()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to the `VMGlobalDefinition` index `index`.
pub fn vmctx_vmglobal_definition(&self, index: DefinedGlobalIndex) -> u32 {
assert!(index.as_u32() < self.num_defined_globals);
self.vmctx_globals_begin()
.checked_add(
index
.as_u32()
.checked_mul(u32::from(self.size_of_vmglobal_definition()))
.unwrap(),
)
.unwrap()
}
/// Return the offset to the `body` field in `*const VMFunctionBody` index `index`.
pub fn vmctx_vmfunction_import_body(&self, index: FuncIndex) -> u32 {
self.vmctx_vmfunction_import(index)
.checked_add(u32::from(self.vmfunction_import_body()))
.unwrap()
}
/// Return the offset to the `vmctx` field in `*const VMFunctionBody` index `index`.
pub fn vmctx_vmfunction_import_vmctx(&self, index: FuncIndex) -> u32 {
self.vmctx_vmfunction_import(index)
.checked_add(u32::from(self.vmfunction_import_vmctx()))
.unwrap()
}
/// Return the offset to the `from` field in `VMTableImport` index `index`.
pub fn vmctx_vmtable_import_from(&self, index: TableIndex) -> u32 {
self.vmctx_vmtable_import(index)
.checked_add(u32::from(self.vmtable_import_from()))
.unwrap()
}
/// Return the offset to the `base` field in `VMTableDefinition` index `index`.
pub fn vmctx_vmtable_definition_base(&self, index: DefinedTableIndex) -> u32 {
self.vmctx_vmtable_definition(index)
.checked_add(u32::from(self.vmtable_definition_base()))
.unwrap()
}
/// Return the offset to the `current_elements` field in `VMTableDefinition` index `index`.
pub fn vmctx_vmtable_definition_current_elements(&self, index: DefinedTableIndex) -> u32 {
self.vmctx_vmtable_definition(index)
.checked_add(u32::from(self.vmtable_definition_current_elements()))
.unwrap()
}
/// Return the offset to the `from` field in `VMMemoryImport` index `index`.
pub fn vmctx_vmmemory_import_from(&self, index: MemoryIndex) -> u32 {
self.vmctx_vmmemory_import(index)
.checked_add(u32::from(self.vmmemory_import_from()))
.unwrap()
}
/// Return the offset to the `vmctx` field in `VMMemoryImport` index `index`.
pub fn vmctx_vmmemory_import_vmctx(&self, index: MemoryIndex) -> u32 {
self.vmctx_vmmemory_import(index)
.checked_add(u32::from(self.vmmemory_import_vmctx()))
.unwrap()
}
/// Return the offset to the `base` field in `VMMemoryDefinition` index `index`.
pub fn vmctx_vmmemory_definition_base(&self, index: DefinedMemoryIndex) -> u32 {
self.vmctx_vmmemory_definition(index)
.checked_add(u32::from(self.vmmemory_definition_base()))
.unwrap()
}
/// Return the offset to the `current_length` field in `VMMemoryDefinition` index `index`.
pub fn vmctx_vmmemory_definition_current_length(&self, index: DefinedMemoryIndex) -> u32 {
self.vmctx_vmmemory_definition(index)
.checked_add(u32::from(self.vmmemory_definition_current_length()))
.unwrap()
}
/// Return the offset to the `from` field in `VMGlobalImport` index `index`.
pub fn vmctx_vmglobal_import_from(&self, index: GlobalIndex) -> u32 {
self.vmctx_vmglobal_import(index)
.checked_add(u32::from(self.vmglobal_import_from()))
.unwrap()
}
}
/// Target specific type for shared signature index.
#[derive(Debug, Copy, Clone)]
pub struct TargetSharedSignatureIndex(u32);
impl TargetSharedSignatureIndex {
/// Constructs `TargetSharedSignatureIndex`.
pub fn new(value: u32) -> Self {
TargetSharedSignatureIndex(value)
}
/// Returns index value.
pub fn index(&self) -> u32 {
self.0
}
}