1 //! Data structures for representing decoded wasm modules. 2 3 use crate::tunables::Tunables; 4 use crate::WASM_MAX_PAGES; 5 use cranelift_codegen::ir; 6 use cranelift_entity::{EntityRef, PrimaryMap}; 7 use cranelift_wasm::{ 8 DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, 9 ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, 10 TableIndex, WasmFuncType, 11 }; 12 use indexmap::IndexMap; 13 use more_asserts::assert_ge; 14 use std::collections::HashMap; 15 use std::sync::{ 16 atomic::{AtomicUsize, Ordering::SeqCst}, 17 Arc, 18 }; 19 20 /// A WebAssembly table initializer. 21 #[derive(Clone, Debug, Hash)] 22 pub struct TableElements { 23 /// The index of a table to initialize. 24 pub table_index: TableIndex, 25 /// Optionally, a global variable giving a base index. 26 pub base: Option<GlobalIndex>, 27 /// The offset to add to the base. 28 pub offset: usize, 29 /// The values to write into the table elements. 30 pub elements: Box<[FuncIndex]>, 31 } 32 33 /// An index of an entity. 34 #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] 35 pub enum EntityIndex { 36 /// Function index. 37 Function(FuncIndex), 38 /// Table index. 39 Table(TableIndex), 40 /// Memory index. 41 Memory(MemoryIndex), 42 /// Global index. 43 Global(GlobalIndex), 44 } 45 46 /// Implemenation styles for WebAssembly linear memory. 47 #[derive(Debug, Clone, Hash)] 48 pub enum MemoryStyle { 49 /// The actual memory can be resized and moved. 50 Dynamic, 51 /// Addresss space is allocated up front. 52 Static { 53 /// The number of mapped and unmapped pages. 54 bound: u32, 55 }, 56 } 57 58 impl MemoryStyle { 59 /// Decide on an implementation style for the given `Memory`. for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64)60 pub fn for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64) { 61 // A heap with a maximum that doesn't exceed the static memory bound specified by the 62 // tunables make it static. 63 // 64 // If the module doesn't declare an explicit maximum treat it as 4GiB. 65 let maximum = memory.maximum.unwrap_or(WASM_MAX_PAGES); 66 if maximum <= tunables.static_memory_bound { 67 assert_ge!(tunables.static_memory_bound, memory.minimum); 68 return ( 69 Self::Static { 70 bound: tunables.static_memory_bound, 71 }, 72 tunables.static_memory_offset_guard_size, 73 ); 74 } 75 76 // Otherwise, make it dynamic. 77 (Self::Dynamic, tunables.dynamic_memory_offset_guard_size) 78 } 79 } 80 81 /// A WebAssembly linear memory description along with our chosen style for 82 /// implementing it. 83 #[derive(Debug, Clone, Hash)] 84 pub struct MemoryPlan { 85 /// The WebAssembly linear memory description. 86 pub memory: Memory, 87 /// Our chosen implementation style. 88 pub style: MemoryStyle, 89 /// Our chosen offset-guard size. 90 pub offset_guard_size: u64, 91 } 92 93 impl MemoryPlan { 94 /// Draw up a plan for implementing a `Memory`. for_memory(memory: Memory, tunables: &Tunables) -> Self95 pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self { 96 let (style, offset_guard_size) = MemoryStyle::for_memory(memory, tunables); 97 Self { 98 memory, 99 style, 100 offset_guard_size, 101 } 102 } 103 } 104 105 /// Implemenation styles for WebAssembly tables. 106 #[derive(Debug, Clone, Hash)] 107 pub enum TableStyle { 108 /// Signatures are stored in the table and checked in the caller. 109 CallerChecksSignature, 110 } 111 112 impl TableStyle { 113 /// Decide on an implementation style for the given `Table`. for_table(_table: Table, _tunables: &Tunables) -> Self114 pub fn for_table(_table: Table, _tunables: &Tunables) -> Self { 115 Self::CallerChecksSignature 116 } 117 } 118 119 /// A WebAssembly table description along with our chosen style for 120 /// implementing it. 121 #[derive(Debug, Clone, Hash)] 122 pub struct TablePlan { 123 /// The WebAssembly table description. 124 pub table: cranelift_wasm::Table, 125 /// Our chosen implementation style. 126 pub style: TableStyle, 127 } 128 129 impl TablePlan { 130 /// Draw up a plan for implementing a `Table`. for_table(table: Table, tunables: &Tunables) -> Self131 pub fn for_table(table: Table, tunables: &Tunables) -> Self { 132 let style = TableStyle::for_table(table, tunables); 133 Self { table, style } 134 } 135 } 136 137 /// A translated WebAssembly module, excluding the function bodies and 138 /// memory initializers. 139 #[derive(Debug)] 140 pub struct Module { 141 /// A unique identifier (within this process) for this module. 142 pub id: usize, 143 144 /// The name of this wasm module, often found in the wasm file. 145 pub name: Option<String>, 146 147 /// Local information about a module which is the bare minimum necessary to 148 /// translate a function body. This is derived as `Hash` whereas this module 149 /// isn't, since it contains too much information needed to translate a 150 /// function. 151 pub local: ModuleLocal, 152 153 /// All import records, in the order they are declared in the module. 154 pub imports: Vec<(String, String, EntityIndex)>, 155 156 /// Exported entities. 157 pub exports: IndexMap<String, EntityIndex>, 158 159 /// The module "start" function, if present. 160 pub start_func: Option<FuncIndex>, 161 162 /// WebAssembly table initializers. 163 pub table_elements: Vec<TableElements>, 164 165 /// WebAssembly passive elements. 166 pub passive_elements: HashMap<ElemIndex, Box<[FuncIndex]>>, 167 168 /// WebAssembly passive data segments. 169 pub passive_data: HashMap<DataIndex, Arc<[u8]>>, 170 171 /// WebAssembly table initializers. 172 pub func_names: HashMap<FuncIndex, String>, 173 } 174 175 /// Local information known about a wasm module, the bare minimum necessary to 176 /// translate function bodies. 177 /// 178 /// This is stored within a `Module` and it implements `Hash`, unlike `Module`, 179 /// and is used as part of the cache key when we load compiled modules from the 180 /// global cache. 181 #[derive(Debug, Hash)] 182 pub struct ModuleLocal { 183 /// Unprocessed signatures exactly as provided by `declare_signature()`. 184 pub signatures: PrimaryMap<SignatureIndex, (WasmFuncType, ir::Signature)>, 185 186 /// Number of imported functions in the module. 187 pub num_imported_funcs: usize, 188 189 /// Number of imported tables in the module. 190 pub num_imported_tables: usize, 191 192 /// Number of imported memories in the module. 193 pub num_imported_memories: usize, 194 195 /// Number of imported globals in the module. 196 pub num_imported_globals: usize, 197 198 /// Types of functions, imported and local. 199 pub functions: PrimaryMap<FuncIndex, SignatureIndex>, 200 201 /// WebAssembly tables. 202 pub table_plans: PrimaryMap<TableIndex, TablePlan>, 203 204 /// WebAssembly linear memory plans. 205 pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>, 206 207 /// WebAssembly global variables. 208 pub globals: PrimaryMap<GlobalIndex, Global>, 209 } 210 211 impl Module { 212 /// Allocates the module data structures. new() -> Self213 pub fn new() -> Self { 214 static NEXT_ID: AtomicUsize = AtomicUsize::new(0); 215 216 Self { 217 id: NEXT_ID.fetch_add(1, SeqCst), 218 name: None, 219 imports: Vec::new(), 220 exports: IndexMap::new(), 221 start_func: None, 222 table_elements: Vec::new(), 223 passive_elements: HashMap::new(), 224 passive_data: HashMap::new(), 225 func_names: HashMap::new(), 226 local: ModuleLocal { 227 num_imported_funcs: 0, 228 num_imported_tables: 0, 229 num_imported_memories: 0, 230 num_imported_globals: 0, 231 signatures: PrimaryMap::new(), 232 functions: PrimaryMap::new(), 233 table_plans: PrimaryMap::new(), 234 memory_plans: PrimaryMap::new(), 235 globals: PrimaryMap::new(), 236 }, 237 } 238 } 239 240 /// Get the given passive element, if it exists. get_passive_element(&self, index: ElemIndex) -> Option<&[FuncIndex]>241 pub fn get_passive_element(&self, index: ElemIndex) -> Option<&[FuncIndex]> { 242 self.passive_elements.get(&index).map(|es| &**es) 243 } 244 } 245 246 impl ModuleLocal { 247 /// Convert a `DefinedFuncIndex` into a `FuncIndex`. func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex248 pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex { 249 FuncIndex::new(self.num_imported_funcs + defined_func.index()) 250 } 251 252 /// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the 253 /// index is an imported function. defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex>254 pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> { 255 if func.index() < self.num_imported_funcs { 256 None 257 } else { 258 Some(DefinedFuncIndex::new( 259 func.index() - self.num_imported_funcs, 260 )) 261 } 262 } 263 264 /// Test whether the given function index is for an imported function. is_imported_function(&self, index: FuncIndex) -> bool265 pub fn is_imported_function(&self, index: FuncIndex) -> bool { 266 index.index() < self.num_imported_funcs 267 } 268 269 /// Convert a `DefinedTableIndex` into a `TableIndex`. table_index(&self, defined_table: DefinedTableIndex) -> TableIndex270 pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex { 271 TableIndex::new(self.num_imported_tables + defined_table.index()) 272 } 273 274 /// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the 275 /// index is an imported table. defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex>276 pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> { 277 if table.index() < self.num_imported_tables { 278 None 279 } else { 280 Some(DefinedTableIndex::new( 281 table.index() - self.num_imported_tables, 282 )) 283 } 284 } 285 286 /// Test whether the given table index is for an imported table. is_imported_table(&self, index: TableIndex) -> bool287 pub fn is_imported_table(&self, index: TableIndex) -> bool { 288 index.index() < self.num_imported_tables 289 } 290 291 /// Convert a `DefinedMemoryIndex` into a `MemoryIndex`. memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex292 pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex { 293 MemoryIndex::new(self.num_imported_memories + defined_memory.index()) 294 } 295 296 /// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the 297 /// index is an imported memory. defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex>298 pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> { 299 if memory.index() < self.num_imported_memories { 300 None 301 } else { 302 Some(DefinedMemoryIndex::new( 303 memory.index() - self.num_imported_memories, 304 )) 305 } 306 } 307 308 /// Test whether the given memory index is for an imported memory. is_imported_memory(&self, index: MemoryIndex) -> bool309 pub fn is_imported_memory(&self, index: MemoryIndex) -> bool { 310 index.index() < self.num_imported_memories 311 } 312 313 /// Convert a `DefinedGlobalIndex` into a `GlobalIndex`. global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex314 pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex { 315 GlobalIndex::new(self.num_imported_globals + defined_global.index()) 316 } 317 318 /// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the 319 /// index is an imported global. defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex>320 pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> { 321 if global.index() < self.num_imported_globals { 322 None 323 } else { 324 Some(DefinedGlobalIndex::new( 325 global.index() - self.num_imported_globals, 326 )) 327 } 328 } 329 330 /// Test whether the given global index is for an imported global. is_imported_global(&self, index: GlobalIndex) -> bool331 pub fn is_imported_global(&self, index: GlobalIndex) -> bool { 332 index.index() < self.num_imported_globals 333 } 334 335 /// Convenience method for looking up the native signature of a compiled 336 /// Wasm function. native_func_signature(&self, func_index: FuncIndex) -> &ir::Signature337 pub fn native_func_signature(&self, func_index: FuncIndex) -> &ir::Signature { 338 &self.signatures[self.functions[func_index]].1 339 } 340 341 /// Convenience method for looking up the original Wasm signature of a 342 /// function. wasm_func_type(&self, func_index: FuncIndex) -> &WasmFuncType343 pub fn wasm_func_type(&self, func_index: FuncIndex) -> &WasmFuncType { 344 &self.signatures[self.functions[func_index]].0 345 } 346 } 347