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