1 use crate::{
2     Result,
3     abi::{ABI, ABISig, wasm_sig},
4     codegen::{BlockSig, BuiltinFunction, BuiltinFunctions, OperandSize, control},
5     isa::TargetIsa,
6 };
7 use cranelift_codegen::ir::{UserExternalName, UserExternalNameRef};
8 use std::collections::{
9     HashMap,
10     hash_map::Entry::{Occupied, Vacant},
11 };
12 use std::mem;
13 use wasmparser::BlockType;
14 use wasmtime_environ::{
15     BuiltinFunctionIndex, DefinedFuncIndex, FuncIndex, FuncKey, GlobalIndex, IndexType, Memory,
16     MemoryIndex, ModuleTranslation, ModuleTypesBuilder, PrimaryMap, PtrSize, Table, TableIndex,
17     TypeConvert, TypeIndex, VMOffsets, WasmHeapType, WasmValType, collections::TryClone as _,
18 };
19 
20 #[derive(Debug, Clone, Copy)]
21 pub struct GlobalData {
22     /// The offset of the global.
23     pub offset: u32,
24     /// True if the global is imported.
25     pub imported: bool,
26     /// The WebAssembly type of the global.
27     pub ty: WasmValType,
28 }
29 
30 /// Table metadata.
31 #[derive(Debug, Copy, Clone)]
32 pub struct TableData {
33     /// The offset to the base of the table.
34     pub offset: u32,
35     /// The offset to the current elements field.
36     pub current_elems_offset: u32,
37     /// If the table is imported, this field contains the offset to locate the
38     /// base of the table data.
39     pub import_from: Option<u32>,
40     /// The size of the table elements.
41     pub(crate) element_size: OperandSize,
42     /// The size of the current elements field.
43     pub(crate) current_elements_size: OperandSize,
44     /// The type of this table.
45     pub ty: Table,
46 }
47 
48 impl TableData {
index_type(&self) -> WasmValType49     pub fn index_type(&self) -> WasmValType {
50         match self.ty.idx_type {
51             IndexType::I32 => WasmValType::I32,
52             IndexType::I64 => WasmValType::I64,
53         }
54     }
55 }
56 
57 /// Heap metadata.
58 ///
59 /// Heaps represent a WebAssembly linear memory.
60 #[derive(Debug, Copy, Clone)]
61 pub struct HeapData {
62     /// The offset to the base of the heap.
63     /// Relative to the `VMContext` pointer if the WebAssembly memory is locally
64     /// defined. Else this is relative to the location of the imported WebAssembly
65     /// memory location.
66     pub offset: u32,
67     /// The offset to the current length field.
68     pub current_length_offset: u32,
69     /// If the WebAssembly memory is imported or shared, this field contains the offset to locate the
70     /// base of the heap.
71     pub import_from: Option<u32>,
72     /// The memory type this heap is associated with.
73     pub memory: Memory,
74 }
75 
76 impl HeapData {
index_type(&self) -> WasmValType77     pub fn index_type(&self) -> WasmValType {
78         match self.memory.idx_type {
79             IndexType::I32 => WasmValType::I32,
80             IndexType::I64 => WasmValType::I64,
81         }
82     }
83 }
84 
85 /// A function callee.
86 /// It categorizes how the callee should be treated
87 /// when performing the call.
88 #[derive(Clone)]
89 pub(crate) enum Callee {
90     /// Locally defined function.
91     Local(FuncIndex),
92     /// Imported function.
93     Import(FuncIndex),
94     /// Function reference.
95     FuncRef(TypeIndex),
96     /// A built-in function.
97     Builtin(BuiltinFunction),
98     /// A built-in function, but the vmctx argument is located at the static
99     /// offset provided from the current function's vmctx.
100     BuiltinWithDifferentVmctx(BuiltinFunction, u32),
101 }
102 
103 /// The function environment.
104 ///
105 /// Contains all information about the module and runtime that is accessible to
106 /// to a particular function during code generation.
107 pub struct FuncEnv<'a, 'translation: 'a, 'data: 'translation, P: PtrSize> {
108     /// Offsets to the fields within the `VMContext` ptr.
109     pub vmoffsets: &'a VMOffsets<P>,
110     /// Metadata about the translation process of a WebAssembly module.
111     pub translation: &'translation ModuleTranslation<'data>,
112     /// The module's function types.
113     pub types: &'translation ModuleTypesBuilder,
114     /// The built-in functions available to the JIT code.
115     pub builtins: &'translation mut BuiltinFunctions,
116     /// Track resolved table information.
117     resolved_tables: HashMap<TableIndex, TableData>,
118     /// Track resolved heap information.
119     resolved_heaps: HashMap<MemoryIndex, HeapData>,
120     /// A map from [FunctionIndex] to [ABISig], to keep track of the resolved
121     /// function callees.
122     resolved_callees: HashMap<FuncIndex, ABISig>,
123     /// A map from [TypeIndex] to [ABISig], to keep track of the resolved
124     /// indirect function signatures.
125     resolved_sigs: HashMap<TypeIndex, ABISig>,
126     /// A map from [GlobalIndex] to [GlobalData].
127     resolved_globals: HashMap<GlobalIndex, GlobalData>,
128     /// Pointer size represented as a WebAssembly type.
129     ptr_type: WasmValType,
130     /// Whether or not to enable Spectre mitigation on heap bounds checks.
131     heap_access_spectre_mitigation: bool,
132     /// Whether or not to enable Spectre mitigation on table element accesses.
133     table_access_spectre_mitigation: bool,
134     /// Size of pages on the compilation target.
135     pub page_size_log2: u8,
136     name_map: PrimaryMap<UserExternalNameRef, UserExternalName>,
137     name_intern: HashMap<UserExternalName, UserExternalNameRef>,
138 }
139 
ptr_type_from_ptr_size(size: u8) -> WasmValType140 pub fn ptr_type_from_ptr_size(size: u8) -> WasmValType {
141     (size == 8)
142         .then(|| WasmValType::I64)
143         .unwrap_or_else(|| unimplemented!("Support for non-64-bit architectures"))
144 }
145 
146 impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
147     /// Create a new function environment.
new( vmoffsets: &'a VMOffsets<P>, translation: &'translation ModuleTranslation<'data>, types: &'translation ModuleTypesBuilder, builtins: &'translation mut BuiltinFunctions, isa: &dyn TargetIsa, ptr_type: WasmValType, ) -> Self148     pub fn new(
149         vmoffsets: &'a VMOffsets<P>,
150         translation: &'translation ModuleTranslation<'data>,
151         types: &'translation ModuleTypesBuilder,
152         builtins: &'translation mut BuiltinFunctions,
153         isa: &dyn TargetIsa,
154         ptr_type: WasmValType,
155     ) -> Self {
156         Self {
157             vmoffsets,
158             translation,
159             types,
160             resolved_tables: HashMap::new(),
161             resolved_heaps: HashMap::new(),
162             resolved_callees: HashMap::new(),
163             resolved_sigs: HashMap::new(),
164             resolved_globals: HashMap::new(),
165             ptr_type,
166             heap_access_spectre_mitigation: isa.flags().enable_heap_access_spectre_mitigation(),
167             table_access_spectre_mitigation: isa.flags().enable_table_access_spectre_mitigation(),
168             page_size_log2: isa.page_size_align_log2(),
169             builtins,
170             name_map: Default::default(),
171             name_intern: Default::default(),
172         }
173     }
174 
175     /// Derive the [`WasmType`] from the pointer size.
ptr_type(&self) -> WasmValType176     pub(crate) fn ptr_type(&self) -> WasmValType {
177         self.ptr_type
178     }
179 
180     /// Resolves a [`Callee::FuncRef`] from a type index.
funcref(&mut self, idx: TypeIndex) -> Callee181     pub(crate) fn funcref(&mut self, idx: TypeIndex) -> Callee {
182         Callee::FuncRef(idx)
183     }
184 
185     /// Resolves a function [`Callee`] from an index.
callee_from_index(&mut self, idx: FuncIndex) -> Callee186     pub(crate) fn callee_from_index(&mut self, idx: FuncIndex) -> Callee {
187         let import = self.translation.module.is_imported_function(idx);
188         if import {
189             Callee::Import(idx)
190         } else {
191             Callee::Local(idx)
192         }
193     }
194 
195     /// Converts a [wasmparser::BlockType] into a [BlockSig].
resolve_block_sig(&self, ty: BlockType) -> Result<BlockSig>196     pub(crate) fn resolve_block_sig(&self, ty: BlockType) -> Result<BlockSig> {
197         use BlockType::*;
198         Ok(match ty {
199             Empty => BlockSig::new(control::BlockType::void()),
200             Type(ty) => {
201                 let ty = TypeConverter::new(self.translation, self.types).convert_valtype(ty)?;
202                 BlockSig::new(control::BlockType::single(ty))
203             }
204             FuncType(idx) => {
205                 let sig_index = self.translation.module.types[TypeIndex::from_u32(idx)]
206                     .unwrap_module_type_index();
207                 let sig = self.types[sig_index].unwrap_func();
208                 BlockSig::new(control::BlockType::func(sig.clone_panic_on_oom()))
209             }
210         })
211     }
212 
213     /// Resolves `GlobalData` of a global at the given index.
resolve_global(&mut self, index: GlobalIndex) -> GlobalData214     pub fn resolve_global(&mut self, index: GlobalIndex) -> GlobalData {
215         let ty = self.translation.module.globals[index].wasm_ty;
216         let val = || match self.translation.module.defined_global_index(index) {
217             Some(defined_index) => GlobalData {
218                 offset: self.vmoffsets.vmctx_vmglobal_definition(defined_index),
219                 imported: false,
220                 ty,
221             },
222             None => GlobalData {
223                 offset: self.vmoffsets.vmctx_vmglobal_import_from(index),
224                 imported: true,
225                 ty,
226             },
227         };
228 
229         *self.resolved_globals.entry(index).or_insert_with(val)
230     }
231 
232     /// Returns the table information for the given table index.
resolve_table_data(&mut self, index: TableIndex) -> TableData233     pub fn resolve_table_data(&mut self, index: TableIndex) -> TableData {
234         match self.resolved_tables.entry(index) {
235             Occupied(entry) => *entry.get(),
236             Vacant(entry) => {
237                 let (from_offset, base_offset, current_elems_offset) =
238                     match self.translation.module.defined_table_index(index) {
239                         Some(defined) => (
240                             None,
241                             self.vmoffsets.vmctx_vmtable_definition_base(defined),
242                             self.vmoffsets
243                                 .vmctx_vmtable_definition_current_elements(defined),
244                         ),
245                         None => (
246                             Some(self.vmoffsets.vmctx_vmtable_from(index)),
247                             self.vmoffsets.vmtable_definition_base().into(),
248                             self.vmoffsets.vmtable_definition_current_elements().into(),
249                         ),
250                     };
251 
252                 *entry.insert(TableData {
253                     import_from: from_offset,
254                     offset: base_offset,
255                     current_elems_offset,
256                     element_size: OperandSize::from_bytes(self.vmoffsets.ptr.size()),
257                     current_elements_size: OperandSize::from_bytes(
258                         self.vmoffsets.size_of_vmtable_definition_current_elements(),
259                     ),
260                     ty: self.translation.module.tables[index],
261                 })
262             }
263         }
264     }
265 
266     /// Resolve a `HeapData` from a [MemoryIndex].
resolve_heap(&mut self, index: MemoryIndex) -> HeapData267     pub fn resolve_heap(&mut self, index: MemoryIndex) -> HeapData {
268         let mem = self.translation.module.memories[index];
269         let is_shared = mem.shared;
270         match self.resolved_heaps.entry(index) {
271             Occupied(entry) => *entry.get(),
272             Vacant(entry) => {
273                 let (import_from, base_offset, current_length_offset) =
274                     match self.translation.module.defined_memory_index(index) {
275                         Some(defined) => {
276                             if is_shared {
277                                 (
278                                     Some(self.vmoffsets.vmctx_vmmemory_pointer(defined)),
279                                     self.vmoffsets.ptr.vmmemory_definition_base().into(),
280                                     self.vmoffsets
281                                         .ptr
282                                         .vmmemory_definition_current_length()
283                                         .into(),
284                                 )
285                             } else {
286                                 let owned = self.translation.module.owned_memory_index(defined);
287                                 (
288                                     None,
289                                     self.vmoffsets.vmctx_vmmemory_definition_base(owned),
290                                     self.vmoffsets
291                                         .vmctx_vmmemory_definition_current_length(owned),
292                                 )
293                             }
294                         }
295                         None => (
296                             Some(self.vmoffsets.vmctx_vmmemory_import_from(index)),
297                             self.vmoffsets.ptr.vmmemory_definition_base().into(),
298                             self.vmoffsets
299                                 .ptr
300                                 .vmmemory_definition_current_length()
301                                 .into(),
302                         ),
303                     };
304 
305                 let memory = &self.translation.module.memories[index];
306 
307                 *entry.insert(HeapData {
308                     offset: base_offset,
309                     import_from,
310                     current_length_offset,
311                     memory: *memory,
312                 })
313             }
314         }
315     }
316 
317     /// Get a [`Table`] from a [`TableIndex`].
table(&mut self, index: TableIndex) -> &Table318     pub fn table(&mut self, index: TableIndex) -> &Table {
319         &self.translation.module.tables[index]
320     }
321 
322     /// Returns true if Spectre mitigations are enabled for heap bounds check.
heap_access_spectre_mitigation(&self) -> bool323     pub fn heap_access_spectre_mitigation(&self) -> bool {
324         self.heap_access_spectre_mitigation
325     }
326 
327     /// Returns true if Spectre mitigations are enabled for table element
328     /// accesses.
table_access_spectre_mitigation(&self) -> bool329     pub fn table_access_spectre_mitigation(&self) -> bool {
330         self.table_access_spectre_mitigation
331     }
332 
callee_sig<'b, A>(&'b mut self, callee: &'b Callee) -> Result<&'b ABISig> where A: ABI,333     pub(crate) fn callee_sig<'b, A>(&'b mut self, callee: &'b Callee) -> Result<&'b ABISig>
334     where
335         A: ABI,
336     {
337         match callee {
338             Callee::Local(idx) | Callee::Import(idx) => {
339                 if self.resolved_callees.contains_key(idx) {
340                     Ok(self.resolved_callees.get(idx).unwrap())
341                 } else {
342                     let types = self.translation.get_types();
343                     let types = types.as_ref();
344                     let ty = types[types.core_function_at(idx.as_u32())].unwrap_func();
345                     let converter = TypeConverter::new(self.translation, self.types);
346                     let ty = converter.convert_func_type(&ty)?;
347                     let sig = wasm_sig::<A>(&ty)?;
348                     self.resolved_callees.insert(*idx, sig);
349                     Ok(self.resolved_callees.get(idx).unwrap())
350                 }
351             }
352             Callee::FuncRef(idx) => {
353                 if self.resolved_sigs.contains_key(idx) {
354                     Ok(self.resolved_sigs.get(idx).unwrap())
355                 } else {
356                     let sig_index = self.translation.module.types[*idx].unwrap_module_type_index();
357                     let ty = self.types[sig_index].unwrap_func();
358                     let sig = wasm_sig::<A>(ty)?;
359                     self.resolved_sigs.insert(*idx, sig);
360                     Ok(self.resolved_sigs.get(idx).unwrap())
361                 }
362             }
363             Callee::Builtin(b) | Callee::BuiltinWithDifferentVmctx(b, _) => Ok(b.sig()),
364         }
365     }
366 
367     /// Creates a name to reference the `builtin` provided.
name_builtin(&mut self, builtin: BuiltinFunctionIndex) -> UserExternalNameRef368     pub fn name_builtin(&mut self, builtin: BuiltinFunctionIndex) -> UserExternalNameRef {
369         let key = FuncKey::WasmToBuiltinTrampoline(builtin);
370         let (namespace, index) = key.into_raw_parts();
371         self.intern_name(UserExternalName { namespace, index })
372     }
373 
374     /// Creates a name to reference the wasm function `index` provided.
name_wasm(&mut self, def_func: DefinedFuncIndex) -> UserExternalNameRef375     pub fn name_wasm(&mut self, def_func: DefinedFuncIndex) -> UserExternalNameRef {
376         let key = FuncKey::DefinedWasmFunction(self.translation.module_index(), def_func);
377         let (namespace, index) = key.into_raw_parts();
378         self.intern_name(UserExternalName { namespace, index })
379     }
380 
381     /// Interns `name` into a `UserExternalNameRef` and ensures that duplicate
382     /// instances of `name` are given a unique name ref index.
intern_name(&mut self, name: UserExternalName) -> UserExternalNameRef383     fn intern_name(&mut self, name: UserExternalName) -> UserExternalNameRef {
384         *self
385             .name_intern
386             .entry(name.clone())
387             .or_insert_with(|| self.name_map.push(name))
388     }
389 
390     /// Extracts the name map that was created while translating this function.
take_name_map(&mut self) -> PrimaryMap<UserExternalNameRef, UserExternalName>391     pub fn take_name_map(&mut self) -> PrimaryMap<UserExternalNameRef, UserExternalName> {
392         self.name_intern.clear();
393         mem::take(&mut self.name_map)
394     }
395 }
396 
397 /// A wrapper struct over a reference to a [ModuleTranslation] and
398 /// [ModuleTypesBuilder].
399 pub(crate) struct TypeConverter<'a, 'data: 'a> {
400     translation: &'a ModuleTranslation<'data>,
401     types: &'a ModuleTypesBuilder,
402 }
403 
404 impl TypeConvert for TypeConverter<'_, '_> {
lookup_heap_type(&self, idx: wasmparser::UnpackedIndex) -> WasmHeapType405     fn lookup_heap_type(&self, idx: wasmparser::UnpackedIndex) -> WasmHeapType {
406         wasmtime_environ::WasmparserTypeConverter::new(self.types, |idx| {
407             self.translation.module.types[idx].unwrap_module_type_index()
408         })
409         .lookup_heap_type(idx)
410     }
411 
lookup_type_index( &self, index: wasmparser::UnpackedIndex, ) -> wasmtime_environ::EngineOrModuleTypeIndex412     fn lookup_type_index(
413         &self,
414         index: wasmparser::UnpackedIndex,
415     ) -> wasmtime_environ::EngineOrModuleTypeIndex {
416         wasmtime_environ::WasmparserTypeConverter::new(self.types, |idx| {
417             self.translation.module.types[idx].unwrap_module_type_index()
418         })
419         .lookup_type_index(index)
420     }
421 }
422 
423 impl<'a, 'data> TypeConverter<'a, 'data> {
new(translation: &'a ModuleTranslation<'data>, types: &'a ModuleTypesBuilder) -> Self424     pub fn new(translation: &'a ModuleTranslation<'data>, types: &'a ModuleTypesBuilder) -> Self {
425         Self { translation, types }
426     }
427 }
428