1use crate::{
2 abi::{ABI, ABISig, wasm_sig},
3 codegen::{BlockSig, BuiltinFunction, BuiltinFunctions, OperandSize, control},
4 isa::TargetIsa,
5};
6use anyhow::Result;
7use cranelift_codegen::ir::{UserExternalName, UserExternalNameRef};
8use std::collections::{
9 HashMap,
10 hash_map::Entry::{Occupied, Vacant},
11};
12use std::mem;
13use wasmparser::BlockType;
14use wasmtime_environ::{
15 BuiltinFunctionIndex, FuncIndex, GlobalIndex, IndexType, Memory, MemoryIndex,
16 ModuleTranslation, ModuleTypesBuilder, PrimaryMap, PtrSize, Table, TableIndex, TypeConvert,
17 TypeIndex, VMOffsets, WasmHeapType, WasmValType,
18};
19
20#[derive(Debug, Clone, Copy)]
21pub struct GlobalData {
22 pub offset: u32,
24 pub imported: bool,
26 pub ty: WasmValType,
28}
29
30#[derive(Debug, Copy, Clone)]
32pub struct TableData {
33 pub offset: u32,
35 pub current_elems_offset: u32,
37 pub import_from: Option<u32>,
40 pub(crate) element_size: OperandSize,
42 pub(crate) current_elements_size: OperandSize,
44}
45
46#[derive(Debug, Copy, Clone)]
50pub struct HeapData {
51 pub offset: u32,
56 pub current_length_offset: u32,
58 pub import_from: Option<u32>,
61 pub memory: Memory,
63}
64
65impl HeapData {
66 pub fn index_type(&self) -> WasmValType {
67 match self.memory.idx_type {
68 IndexType::I32 => WasmValType::I32,
69 IndexType::I64 => WasmValType::I64,
70 }
71 }
72}
73
74#[derive(Clone)]
78pub(crate) enum Callee {
79 Local(FuncIndex),
81 Import(FuncIndex),
83 FuncRef(TypeIndex),
85 Builtin(BuiltinFunction),
87}
88
89pub struct FuncEnv<'a, 'translation: 'a, 'data: 'translation, P: PtrSize> {
94 pub vmoffsets: &'a VMOffsets<P>,
96 pub translation: &'translation ModuleTranslation<'data>,
98 pub types: &'translation ModuleTypesBuilder,
100 pub builtins: &'translation mut BuiltinFunctions,
102 resolved_tables: HashMap<TableIndex, TableData>,
104 resolved_heaps: HashMap<MemoryIndex, HeapData>,
106 resolved_callees: HashMap<FuncIndex, ABISig>,
109 resolved_sigs: HashMap<TypeIndex, ABISig>,
112 resolved_globals: HashMap<GlobalIndex, GlobalData>,
114 ptr_type: WasmValType,
116 heap_access_spectre_mitigation: bool,
118 table_access_spectre_mitigation: bool,
120 pub page_size_log2: u8,
122 name_map: PrimaryMap<UserExternalNameRef, UserExternalName>,
123 name_intern: HashMap<UserExternalName, UserExternalNameRef>,
124}
125
126pub fn ptr_type_from_ptr_size(size: u8) -> WasmValType {
127 (size == 8)
128 .then(|| WasmValType::I64)
129 .unwrap_or_else(|| unimplemented!("Support for non-64-bit architectures"))
130}
131
132impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
133 pub fn new(
135 vmoffsets: &'a VMOffsets<P>,
136 translation: &'translation ModuleTranslation<'data>,
137 types: &'translation ModuleTypesBuilder,
138 builtins: &'translation mut BuiltinFunctions,
139 isa: &dyn TargetIsa,
140 ptr_type: WasmValType,
141 ) -> Self {
142 Self {
143 vmoffsets,
144 translation,
145 types,
146 resolved_tables: HashMap::new(),
147 resolved_heaps: HashMap::new(),
148 resolved_callees: HashMap::new(),
149 resolved_sigs: HashMap::new(),
150 resolved_globals: HashMap::new(),
151 ptr_type,
152 heap_access_spectre_mitigation: isa.flags().enable_heap_access_spectre_mitigation(),
153 table_access_spectre_mitigation: isa.flags().enable_table_access_spectre_mitigation(),
154 page_size_log2: isa.page_size_align_log2(),
155 builtins,
156 name_map: Default::default(),
157 name_intern: Default::default(),
158 }
159 }
160
161 pub(crate) fn ptr_type(&self) -> WasmValType {
163 self.ptr_type
164 }
165
166 pub(crate) fn funcref(&mut self, idx: TypeIndex) -> Callee {
168 Callee::FuncRef(idx)
169 }
170
171 pub(crate) fn callee_from_index(&mut self, idx: FuncIndex) -> Callee {
173 let import = self.translation.module.is_imported_function(idx);
174 if import {
175 Callee::Import(idx)
176 } else {
177 Callee::Local(idx)
178 }
179 }
180
181 pub(crate) fn resolve_block_sig(&self, ty: BlockType) -> Result<BlockSig> {
183 use BlockType::*;
184 Ok(match ty {
185 Empty => BlockSig::new(control::BlockType::void()),
186 Type(ty) => {
187 let ty = TypeConverter::new(self.translation, self.types).convert_valtype(ty)?;
188 BlockSig::new(control::BlockType::single(ty))
189 }
190 FuncType(idx) => {
191 let sig_index = self.translation.module.types[TypeIndex::from_u32(idx)]
192 .unwrap_module_type_index();
193 let sig = self.types[sig_index].unwrap_func();
194 BlockSig::new(control::BlockType::func(sig.clone()))
195 }
196 })
197 }
198
199 pub fn resolve_global(&mut self, index: GlobalIndex) -> GlobalData {
201 let ty = self.translation.module.globals[index].wasm_ty;
202 let val = || match self.translation.module.defined_global_index(index) {
203 Some(defined_index) => GlobalData {
204 offset: self.vmoffsets.vmctx_vmglobal_definition(defined_index),
205 imported: false,
206 ty,
207 },
208 None => GlobalData {
209 offset: self.vmoffsets.vmctx_vmglobal_import_from(index),
210 imported: true,
211 ty,
212 },
213 };
214
215 *self.resolved_globals.entry(index).or_insert_with(val)
216 }
217
218 pub fn resolve_table_data(&mut self, index: TableIndex) -> TableData {
220 match self.resolved_tables.entry(index) {
221 Occupied(entry) => *entry.get(),
222 Vacant(entry) => {
223 let (from_offset, base_offset, current_elems_offset) =
224 match self.translation.module.defined_table_index(index) {
225 Some(defined) => (
226 None,
227 self.vmoffsets.vmctx_vmtable_definition_base(defined),
228 self.vmoffsets
229 .vmctx_vmtable_definition_current_elements(defined),
230 ),
231 None => (
232 Some(self.vmoffsets.vmctx_vmtable_from(index)),
233 self.vmoffsets.vmtable_definition_base().into(),
234 self.vmoffsets.vmtable_definition_current_elements().into(),
235 ),
236 };
237
238 *entry.insert(TableData {
239 import_from: from_offset,
240 offset: base_offset,
241 current_elems_offset,
242 element_size: OperandSize::from_bytes(self.vmoffsets.ptr.size()),
243 current_elements_size: OperandSize::from_bytes(
244 self.vmoffsets.size_of_vmtable_definition_current_elements(),
245 ),
246 })
247 }
248 }
249 }
250
251 pub fn resolve_heap(&mut self, index: MemoryIndex) -> HeapData {
253 let mem = self.translation.module.memories[index];
254 let is_shared = mem.shared;
255 match self.resolved_heaps.entry(index) {
256 Occupied(entry) => *entry.get(),
257 Vacant(entry) => {
258 let (import_from, base_offset, current_length_offset) =
259 match self.translation.module.defined_memory_index(index) {
260 Some(defined) => {
261 if is_shared {
262 (
263 Some(self.vmoffsets.vmctx_vmmemory_pointer(defined)),
264 self.vmoffsets.ptr.vmmemory_definition_base().into(),
265 self.vmoffsets
266 .ptr
267 .vmmemory_definition_current_length()
268 .into(),
269 )
270 } else {
271 let owned = self.translation.module.owned_memory_index(defined);
272 (
273 None,
274 self.vmoffsets.vmctx_vmmemory_definition_base(owned),
275 self.vmoffsets
276 .vmctx_vmmemory_definition_current_length(owned),
277 )
278 }
279 }
280 None => (
281 Some(self.vmoffsets.vmctx_vmmemory_import_from(index)),
282 self.vmoffsets.ptr.vmmemory_definition_base().into(),
283 self.vmoffsets
284 .ptr
285 .vmmemory_definition_current_length()
286 .into(),
287 ),
288 };
289
290 let memory = &self.translation.module.memories[index];
291
292 *entry.insert(HeapData {
293 offset: base_offset,
294 import_from,
295 current_length_offset,
296 memory: *memory,
297 })
298 }
299 }
300 }
301
302 pub fn table(&mut self, index: TableIndex) -> &Table {
304 &self.translation.module.tables[index]
305 }
306
307 pub fn heap_access_spectre_mitigation(&self) -> bool {
309 self.heap_access_spectre_mitigation
310 }
311
312 pub fn table_access_spectre_mitigation(&self) -> bool {
315 self.table_access_spectre_mitigation
316 }
317
318 pub(crate) fn callee_sig<'b, A>(&'b mut self, callee: &'b Callee) -> Result<&'b ABISig>
319 where
320 A: ABI,
321 {
322 match callee {
323 Callee::Local(idx) | Callee::Import(idx) => {
324 if self.resolved_callees.contains_key(idx) {
325 Ok(self.resolved_callees.get(idx).unwrap())
326 } else {
327 let types = self.translation.get_types();
328 let types = types.as_ref();
329 let ty = types[types.core_function_at(idx.as_u32())].unwrap_func();
330 let converter = TypeConverter::new(self.translation, self.types);
331 let ty = converter.convert_func_type(&ty)?;
332 let sig = wasm_sig::<A>(&ty)?;
333 self.resolved_callees.insert(*idx, sig);
334 Ok(self.resolved_callees.get(idx).unwrap())
335 }
336 }
337 Callee::FuncRef(idx) => {
338 if self.resolved_sigs.contains_key(idx) {
339 Ok(self.resolved_sigs.get(idx).unwrap())
340 } else {
341 let sig_index = self.translation.module.types[*idx].unwrap_module_type_index();
342 let ty = self.types[sig_index].unwrap_func();
343 let sig = wasm_sig::<A>(ty)?;
344 self.resolved_sigs.insert(*idx, sig);
345 Ok(self.resolved_sigs.get(idx).unwrap())
346 }
347 }
348 Callee::Builtin(b) => Ok(b.sig()),
349 }
350 }
351
352 pub fn name_builtin(&mut self, builtin: BuiltinFunctionIndex) -> UserExternalNameRef {
354 self.intern_name(UserExternalName {
355 namespace: wasmtime_cranelift::NS_WASMTIME_BUILTIN,
356 index: builtin.index(),
357 })
358 }
359
360 pub fn name_wasm(&mut self, index: FuncIndex) -> UserExternalNameRef {
362 self.intern_name(UserExternalName {
363 namespace: wasmtime_cranelift::NS_WASM_FUNC,
364 index: index.as_u32(),
365 })
366 }
367
368 fn intern_name(&mut self, name: UserExternalName) -> UserExternalNameRef {
371 *self
372 .name_intern
373 .entry(name.clone())
374 .or_insert_with(|| self.name_map.push(name))
375 }
376
377 pub fn take_name_map(&mut self) -> PrimaryMap<UserExternalNameRef, UserExternalName> {
379 self.name_intern.clear();
380 mem::take(&mut self.name_map)
381 }
382}
383
384pub(crate) struct TypeConverter<'a, 'data: 'a> {
387 translation: &'a ModuleTranslation<'data>,
388 types: &'a ModuleTypesBuilder,
389}
390
391impl TypeConvert for TypeConverter<'_, '_> {
392 fn lookup_heap_type(&self, idx: wasmparser::UnpackedIndex) -> WasmHeapType {
393 wasmtime_environ::WasmparserTypeConverter::new(self.types, |idx| {
394 self.translation.module.types[idx].unwrap_module_type_index()
395 })
396 .lookup_heap_type(idx)
397 }
398
399 fn lookup_type_index(
400 &self,
401 index: wasmparser::UnpackedIndex,
402 ) -> wasmtime_environ::EngineOrModuleTypeIndex {
403 wasmtime_environ::WasmparserTypeConverter::new(self.types, |idx| {
404 self.translation.module.types[idx].unwrap_module_type_index()
405 })
406 .lookup_type_index(index)
407 }
408}
409
410impl<'a, 'data> TypeConverter<'a, 'data> {
411 pub fn new(translation: &'a ModuleTranslation<'data>, types: &'a ModuleTypesBuilder) -> Self {
412 Self { translation, types }
413 }
414}