use crate::codegen::{control, BlockSig, BuiltinFunction, OperandSize};
use std::collections::{
hash_map::Entry::{Occupied, Vacant},
HashMap,
};
use wasmparser::BlockType;
use wasmtime_environ::{
FuncIndex, GlobalIndex, MemoryIndex, ModuleTranslation, ModuleTypesBuilder, PtrSize,
TableIndex, TablePlan, TypeConvert, TypeIndex, VMOffsets, WasmFuncType, WasmHeapType,
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 struct HeapData {
pub offset: u32,
pub current_length_offset: u32,
pub import_from: Option<u32>,
pub ty: WasmValType,
}
#[derive(Clone)]
pub enum Callee {
Local(CalleeInfo),
Import(CalleeInfo),
FuncRef(WasmFuncType),
Builtin(BuiltinFunction),
}
#[derive(Clone)]
pub struct CalleeInfo {
pub ty: WasmFuncType,
pub index: FuncIndex,
}
pub struct FuncEnv<'a, 'translation: 'a, 'data: 'translation, P: PtrSize> {
pub vmoffsets: &'a VMOffsets<P>,
pub translation: &'translation ModuleTranslation<'data>,
pub types: &'translation ModuleTypesBuilder,
resolved_tables: HashMap<TableIndex, TableData>,
resolved_heaps: HashMap<MemoryIndex, HeapData>,
}
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,
) -> Self {
Self {
vmoffsets,
translation,
types,
resolved_tables: HashMap::new(),
resolved_heaps: HashMap::new(),
}
}
pub(crate) fn ptr_type(&self) -> WasmValType {
ptr_type_from_ptr_size(self.ptr_size())
}
fn ptr_size(&self) -> u8 {
self.vmoffsets.ptr.size()
}
pub fn funcref(&self, idx: TypeIndex) -> Callee {
let sig_index = self.translation.module.types[idx].unwrap_function();
let ty = self.types[sig_index].clone();
Callee::FuncRef(ty)
}
pub fn callee_from_index(&self, idx: FuncIndex) -> Callee {
let types = &self.translation.get_types();
let ty = types[types.core_function_at(idx.as_u32())].unwrap_func();
let ty = self.convert_func_type(ty);
let import = self.translation.module.is_imported_function(idx);
let info = CalleeInfo { ty, index: idx };
if import {
Callee::Import(info)
} else {
Callee::Local(info)
}
}
pub(crate) fn resolve_block_sig(&self, ty: BlockType) -> BlockSig {
use BlockType::*;
match ty {
Empty => BlockSig::new(control::BlockType::void()),
Type(ty) => {
let ty = self.convert_valtype(ty);
BlockSig::new(control::BlockType::single(ty))
}
FuncType(idx) => {
let sig_index =
self.translation.module.types[TypeIndex::from_u32(idx)].unwrap_function();
let sig = &self.types[sig_index];
BlockSig::new(control::BlockType::func(sig.clone()))
}
}
}
pub fn resolve_global_type_and_offset(&self, index: GlobalIndex) -> (WasmValType, u32) {
let ty = self.translation.module.globals[index].wasm_ty;
let offset = match self.translation.module.defined_global_index(index) {
Some(defined_index) => self.vmoffsets.vmctx_vmglobal_definition(defined_index),
None => self.vmoffsets.vmctx_vmglobal_import_from(index),
};
(ty, offset)
}
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(),
),
};
*entry.insert(HeapData {
offset: base_offset,
import_from,
current_length_offset,
ty: if self.translation.module.memory_plans[index].memory.memory64 {
WasmValType::I64
} else {
WasmValType::I32
},
})
}
}
}
pub fn table_plan(&mut self, index: TableIndex) -> &TablePlan {
&self.translation.module.table_plans[index]
}
}
impl<P: PtrSize> TypeConvert for FuncEnv<'_, '_, '_, P> {
fn lookup_heap_type(&self, idx: wasmparser::UnpackedIndex) -> WasmHeapType {
wasmtime_environ::WasmparserTypeConverter {
module: &self.translation.module,
types: self.types,
}
.lookup_heap_type(idx)
}
}