use crate::module_environ::FunctionBodyData;
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 more_asserts::assert_ge;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
#[derive(Clone, Debug, Hash)]
pub struct TableElements {
pub table_index: TableIndex,
pub base: Option<GlobalIndex>,
pub offset: usize,
pub elements: Box<[FuncIndex]>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Export {
Function(FuncIndex),
Table(TableIndex),
Memory(MemoryIndex),
Global(GlobalIndex),
}
#[derive(Debug, Clone, Hash)]
pub enum MemoryStyle {
Dynamic,
Static {
bound: u32,
},
}
impl MemoryStyle {
pub fn for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64) {
if let Some(maximum) = memory.maximum {
if maximum <= tunables.static_memory_bound {
assert_ge!(tunables.static_memory_bound, memory.minimum);
return (
Self::Static {
bound: tunables.static_memory_bound,
},
tunables.static_memory_offset_guard_size,
);
}
}
(Self::Dynamic, tunables.dynamic_memory_offset_guard_size)
}
}
#[derive(Debug, Clone, Hash)]
pub struct MemoryPlan {
pub memory: Memory,
pub style: MemoryStyle,
pub offset_guard_size: u64,
}
impl MemoryPlan {
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,
}
}
}
#[derive(Debug, Clone, Hash)]
pub enum TableStyle {
CallerChecksSignature,
}
impl TableStyle {
pub fn for_table(_table: Table, _tunables: &Tunables) -> Self {
Self::CallerChecksSignature
}
}
#[derive(Debug, Clone, Hash)]
pub struct TablePlan {
pub table: cranelift_wasm::Table,
pub style: TableStyle,
}
impl TablePlan {
pub fn for_table(table: Table, tunables: &Tunables) -> Self {
let style = TableStyle::for_table(table, tunables);
Self { table, style }
}
}
#[derive(Debug)]
pub struct Module {
pub id: usize,
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
pub imported_funcs: PrimaryMap<FuncIndex, (String, String, u32)>,
pub imported_tables: PrimaryMap<TableIndex, (String, String, u32)>,
pub imported_memories: PrimaryMap<MemoryIndex, (String, String, u32)>,
pub imported_globals: PrimaryMap<GlobalIndex, (String, String, u32)>,
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
pub globals: PrimaryMap<GlobalIndex, Global>,
pub exports: IndexMap<String, Export>,
pub start_func: Option<FuncIndex>,
pub table_elements: Vec<TableElements>,
pub func_names: HashMap<FuncIndex, String>,
}
impl Module {
pub fn new() -> Self {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
Self {
id: NEXT_ID.fetch_add(1, SeqCst),
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(),
func_names: HashMap::new(),
}
}
pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
FuncIndex::new(self.imported_funcs.len() + defined_func.index())
}
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(),
))
}
}
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
index.index() < self.imported_funcs.len()
}
pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
TableIndex::new(self.imported_tables.len() + defined_table.index())
}
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(),
))
}
}
pub fn is_imported_table(&self, index: TableIndex) -> bool {
index.index() < self.imported_tables.len()
}
pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
MemoryIndex::new(self.imported_memories.len() + defined_memory.index())
}
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(),
))
}
}
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
index.index() < self.imported_memories.len()
}
pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
GlobalIndex::new(self.imported_globals.len() + defined_global.index())
}
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(),
))
}
}
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
index.index() < self.imported_globals.len()
}
pub fn hash_for_cache<'data, H>(
&self,
function_body_inputs: &PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
state: &mut H,
) where
H: Hasher,
{
self.signatures.hash(state);
self.functions.hash(state);
self.table_plans.hash(state);
self.memory_plans.hash(state);
self.globals.hash(state);
let mut exports = self.exports.values().collect::<Vec<_>>();
exports.sort();
for val in exports {
val.hash(state);
}
function_body_inputs.hash(state);
}
}