use crate::{
abi::{wasm_sig, ABISig, ABI},
codegen::{control, BlockSig, BuiltinFunction, BuiltinFunctions, OperandSize},
isa::TargetIsa,
};
use cranelift_codegen::ir::{UserExternalName, UserExternalNameRef};
use std::collections::{
hash_map::Entry::{Occupied, Vacant},
HashMap,
};
use std::mem;
use wasmparser::BlockType;
use wasmtime_environ::{
BuiltinFunctionIndex, FuncIndex, GlobalIndex, MemoryIndex, MemoryPlan, MemoryStyle,
ModuleTranslation, ModuleTypesBuilder, PrimaryMap, PtrSize, TableIndex, TablePlan, TypeConvert,
TypeIndex, VMOffsets, WasmHeapType, WasmValType,
};
#[derive(Debug, Clone, Copy)]
pub struct GlobalData {
pub offset: u32,
pub imported: bool,
pub ty: WasmValType,
}
#[derive(Debug, Copy, Clone)]
pub struct TableData {
pub offset: u32,
pub current_elems_offset: u32,
pub import_from: Option<u32>,
pub(crate) element_size: OperandSize,
pub(crate) current_elements_size: OperandSize,
}
#[derive(Debug, Copy, Clone)]
pub enum HeapStyle {
Static {
bound: u64,
},
Dynamic,
}
#[derive(Debug, Copy, Clone)]
pub struct HeapData {
pub offset: u32,
pub current_length_offset: u32,
pub import_from: Option<u32>,
pub ty: WasmValType,
pub style: HeapStyle,
pub min_size: u64,
pub max_size: Option<u64>,
pub page_size_log2: u8,
pub offset_guard_size: u64,
}
#[derive(Clone)]
pub(crate) enum Callee {
Local(FuncIndex),
Import(FuncIndex),
FuncRef(TypeIndex),
Builtin(BuiltinFunction),
}
pub struct FuncEnv<'a, 'translation: 'a, 'data: 'translation, P: PtrSize> {
pub vmoffsets: &'a VMOffsets<P>,
pub translation: &'translation ModuleTranslation<'data>,
pub types: &'translation ModuleTypesBuilder,
pub builtins: &'translation mut BuiltinFunctions,
resolved_tables: HashMap<TableIndex, TableData>,
resolved_heaps: HashMap<MemoryIndex, HeapData>,
resolved_callees: HashMap<FuncIndex, ABISig>,
resolved_sigs: HashMap<TypeIndex, ABISig>,
resolved_globals: HashMap<GlobalIndex, GlobalData>,
ptr_type: WasmValType,
heap_access_spectre_mitigation: bool,
table_access_spectre_mitigation: bool,
name_map: PrimaryMap<UserExternalNameRef, UserExternalName>,
name_intern: HashMap<UserExternalName, UserExternalNameRef>,
}
pub fn ptr_type_from_ptr_size(size: u8) -> WasmValType {
(size == 8)
.then(|| WasmValType::I64)
.unwrap_or_else(|| unimplemented!("Support for non-64-bit architectures"))
}
impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
pub fn new(
vmoffsets: &'a VMOffsets<P>,
translation: &'translation ModuleTranslation<'data>,
types: &'translation ModuleTypesBuilder,
builtins: &'translation mut BuiltinFunctions,
isa: &dyn TargetIsa,
ptr_type: WasmValType,
) -> Self {
Self {
vmoffsets,
translation,
types,
resolved_tables: HashMap::new(),
resolved_heaps: HashMap::new(),
resolved_callees: HashMap::new(),
resolved_sigs: HashMap::new(),
resolved_globals: HashMap::new(),
ptr_type,
heap_access_spectre_mitigation: isa.flags().enable_heap_access_spectre_mitigation(),
table_access_spectre_mitigation: isa.flags().enable_table_access_spectre_mitigation(),
builtins,
name_map: Default::default(),
name_intern: Default::default(),
}
}
pub(crate) fn ptr_type(&self) -> WasmValType {
self.ptr_type
}
pub(crate) fn funcref(&mut self, idx: TypeIndex) -> Callee {
Callee::FuncRef(idx)
}
pub(crate) fn callee_from_index(&mut self, idx: FuncIndex) -> Callee {
let import = self.translation.module.is_imported_function(idx);
if import {
Callee::Import(idx)
} else {
Callee::Local(idx)
}
}
pub(crate) fn resolve_block_sig(&self, ty: BlockType) -> BlockSig {
use BlockType::*;
match ty {
Empty => BlockSig::new(control::BlockType::void()),
Type(ty) => {
let ty = TypeConverter::new(self.translation, self.types).convert_valtype(ty);
BlockSig::new(control::BlockType::single(ty))
}
FuncType(idx) => {
let sig_index = self.translation.module.types[TypeIndex::from_u32(idx)];
let sig = self.types[sig_index].unwrap_func();
BlockSig::new(control::BlockType::func(sig.clone()))
}
}
}
pub fn resolve_global(&mut self, index: GlobalIndex) -> GlobalData {
let ty = self.translation.module.globals[index].wasm_ty;
let val = || match self.translation.module.defined_global_index(index) {
Some(defined_index) => GlobalData {
offset: self.vmoffsets.vmctx_vmglobal_definition(defined_index),
imported: false,
ty,
},
None => GlobalData {
offset: self.vmoffsets.vmctx_vmglobal_import_from(index),
imported: true,
ty,
},
};
*self.resolved_globals.entry(index).or_insert_with(val)
}
pub fn resolve_table_data(&mut self, index: TableIndex) -> TableData {
match self.resolved_tables.entry(index) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let (from_offset, base_offset, current_elems_offset) =
match self.translation.module.defined_table_index(index) {
Some(defined) => (
None,
self.vmoffsets.vmctx_vmtable_definition_base(defined),
self.vmoffsets
.vmctx_vmtable_definition_current_elements(defined),
),
None => (
Some(self.vmoffsets.vmctx_vmtable_import_from(index)),
self.vmoffsets.vmtable_definition_base().into(),
self.vmoffsets.vmtable_definition_current_elements().into(),
),
};
*entry.insert(TableData {
import_from: from_offset,
offset: base_offset,
current_elems_offset,
element_size: OperandSize::from_bytes(self.vmoffsets.ptr.size()),
current_elements_size: OperandSize::from_bytes(
self.vmoffsets.size_of_vmtable_definition_current_elements(),
),
})
}
}
}
pub fn resolve_heap(&mut self, index: MemoryIndex) -> HeapData {
match self.resolved_heaps.entry(index) {
Occupied(entry) => *entry.get(),
Vacant(entry) => {
let (import_from, base_offset, current_length_offset) =
match self.translation.module.defined_memory_index(index) {
Some(defined) => {
let owned = self.translation.module.owned_memory_index(defined);
(
None,
self.vmoffsets.vmctx_vmmemory_definition_base(owned),
self.vmoffsets
.vmctx_vmmemory_definition_current_length(owned),
)
}
None => (
Some(self.vmoffsets.vmctx_vmmemory_import_from(index)),
self.vmoffsets.ptr.vmmemory_definition_base().into(),
self.vmoffsets
.ptr
.vmmemory_definition_current_length()
.into(),
),
};
let plan = &self.translation.module.memory_plans[index];
let (min_size, max_size) = heap_limits(&plan);
let (style, offset_guard_size) = heap_style_and_offset_guard_size(&plan);
*entry.insert(HeapData {
offset: base_offset,
import_from,
current_length_offset,
style,
ty: if plan.memory.memory64 {
WasmValType::I64
} else {
WasmValType::I32
},
min_size,
max_size,
page_size_log2: plan.memory.page_size_log2,
offset_guard_size,
})
}
}
}
pub fn table_plan(&mut self, index: TableIndex) -> &TablePlan {
&self.translation.module.table_plans[index]
}
pub fn heap_access_spectre_mitigation(&self) -> bool {
self.heap_access_spectre_mitigation
}
pub fn table_access_spectre_mitigation(&self) -> bool {
self.table_access_spectre_mitigation
}
pub(crate) fn callee_sig<'b, A>(&'b mut self, callee: &'b Callee) -> &'b ABISig
where
A: ABI,
{
match callee {
Callee::Local(idx) | Callee::Import(idx) => {
let types = self.translation.get_types();
let ty = types[types.core_function_at(idx.as_u32())].unwrap_func();
let val = || {
let converter = TypeConverter::new(self.translation, self.types);
let ty = converter.convert_func_type(&ty);
wasm_sig::<A>(&ty)
};
self.resolved_callees.entry(*idx).or_insert_with(val)
}
Callee::FuncRef(idx) => {
let val = || {
let sig_index = self.translation.module.types[*idx];
let ty = self.types[sig_index].unwrap_func();
let sig = wasm_sig::<A>(ty);
sig
};
self.resolved_sigs.entry(*idx).or_insert_with(val)
}
Callee::Builtin(b) => b.sig(),
}
}
pub fn name_builtin(&mut self, builtin: BuiltinFunctionIndex) -> UserExternalNameRef {
self.intern_name(UserExternalName {
namespace: wasmtime_cranelift::NS_WASMTIME_BUILTIN,
index: builtin.index(),
})
}
pub fn name_wasm(&mut self, index: FuncIndex) -> UserExternalNameRef {
self.intern_name(UserExternalName {
namespace: wasmtime_cranelift::NS_WASM_FUNC,
index: index.as_u32(),
})
}
fn intern_name(&mut self, name: UserExternalName) -> UserExternalNameRef {
*self
.name_intern
.entry(name.clone())
.or_insert_with(|| self.name_map.push(name))
}
pub fn take_name_map(&mut self) -> PrimaryMap<UserExternalNameRef, UserExternalName> {
self.name_intern.clear();
mem::take(&mut self.name_map)
}
}
pub(crate) struct TypeConverter<'a, 'data: 'a> {
translation: &'a ModuleTranslation<'data>,
types: &'a ModuleTypesBuilder,
}
impl TypeConvert for TypeConverter<'_, '_> {
fn lookup_heap_type(&self, idx: wasmparser::UnpackedIndex) -> WasmHeapType {
wasmtime_environ::WasmparserTypeConverter::new(self.types, &self.translation.module)
.lookup_heap_type(idx)
}
fn lookup_type_index(
&self,
index: wasmparser::UnpackedIndex,
) -> wasmtime_environ::EngineOrModuleTypeIndex {
wasmtime_environ::WasmparserTypeConverter::new(self.types, &self.translation.module)
.lookup_type_index(index)
}
}
impl<'a, 'data> TypeConverter<'a, 'data> {
pub fn new(translation: &'a ModuleTranslation<'data>, types: &'a ModuleTypesBuilder) -> Self {
Self { translation, types }
}
}
fn heap_style_and_offset_guard_size(plan: &MemoryPlan) -> (HeapStyle, u64) {
match plan {
MemoryPlan {
style: MemoryStyle::Static { byte_reservation },
offset_guard_size,
..
} => (
HeapStyle::Static {
bound: *byte_reservation,
},
*offset_guard_size,
),
MemoryPlan {
style: MemoryStyle::Dynamic { .. },
offset_guard_size,
..
} => (HeapStyle::Dynamic, *offset_guard_size),
}
}
fn heap_limits(plan: &MemoryPlan) -> (u64, Option<u64>) {
(
plan.memory.minimum_byte_size().unwrap_or_else(|_| {
u64::MAX
}),
plan.memory.maximum_byte_size().ok(),
)
}