1use crate::{
2 abi::{wasm_sig, ABISig, ABI},
3 codegen::{control, BlockSig, BuiltinFunction, BuiltinFunctions, OperandSize},
4 isa::TargetIsa,
5};
6use cranelift_codegen::ir::{UserExternalName, UserExternalNameRef};
7use std::collections::{
8 hash_map::Entry::{Occupied, Vacant},
9 HashMap,
10};
11use std::mem;
12use wasmparser::BlockType;
13use wasmtime_environ::{
14 BuiltinFunctionIndex, FuncIndex, GlobalIndex, IndexType, Memory, MemoryIndex,
15 ModuleTranslation, ModuleTypesBuilder, PrimaryMap, PtrSize, Table, TableIndex, TypeConvert,
16 TypeIndex, VMOffsets, WasmHeapType, WasmValType,
17};
18
19use anyhow::Result;
20
21#[derive(Debug, Clone, Copy)]
22pub struct GlobalData {
23 pub offset: u32,
25 pub imported: bool,
27 pub ty: WasmValType,
29}
30
31#[derive(Debug, Copy, Clone)]
33pub struct TableData {
34 pub offset: u32,
36 pub current_elems_offset: u32,
38 pub import_from: Option<u32>,
41 pub(crate) element_size: OperandSize,
43 pub(crate) current_elements_size: OperandSize,
45}
46
47#[derive(Debug, Copy, Clone)]
51pub struct HeapData {
52 pub offset: u32,
57 pub current_length_offset: u32,
59 pub import_from: Option<u32>,
62 pub memory: Memory,
64}
65
66impl HeapData {
67 pub fn index_type(&self) -> WasmValType {
68 match self.memory.idx_type {
69 IndexType::I32 => WasmValType::I32,
70 IndexType::I64 => WasmValType::I64,
71 }
72 }
73}
74
75#[derive(Clone)]
79pub(crate) enum Callee {
80 Local(FuncIndex),
82 Import(FuncIndex),
84 FuncRef(TypeIndex),
86 Builtin(BuiltinFunction),
88}
89
90pub struct FuncEnv<'a, 'translation: 'a, 'data: 'translation, P: PtrSize> {
95 pub vmoffsets: &'a VMOffsets<P>,
97 pub translation: &'translation ModuleTranslation<'data>,
99 pub types: &'translation ModuleTypesBuilder,
101 pub builtins: &'translation mut BuiltinFunctions,
103 resolved_tables: HashMap<TableIndex, TableData>,
105 resolved_heaps: HashMap<MemoryIndex, HeapData>,
107 resolved_callees: HashMap<FuncIndex, ABISig>,
110 resolved_sigs: HashMap<TypeIndex, ABISig>,
113 resolved_globals: HashMap<GlobalIndex, GlobalData>,
115 ptr_type: WasmValType,
117 heap_access_spectre_mitigation: bool,
119 table_access_spectre_mitigation: bool,
121 pub page_size_log2: u8,
123 name_map: PrimaryMap<UserExternalNameRef, UserExternalName>,
124 name_intern: HashMap<UserExternalName, UserExternalNameRef>,
125}
126
127pub fn ptr_type_from_ptr_size(size: u8) -> WasmValType {
128 (size == 8)
129 .then(|| WasmValType::I64)
130 .unwrap_or_else(|| unimplemented!("Support for non-64-bit architectures"))
131}
132
133impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
134 pub fn new(
136 vmoffsets: &'a VMOffsets<P>,
137 translation: &'translation ModuleTranslation<'data>,
138 types: &'translation ModuleTypesBuilder,
139 builtins: &'translation mut BuiltinFunctions,
140 isa: &dyn TargetIsa,
141 ptr_type: WasmValType,
142 ) -> Self {
143 Self {
144 vmoffsets,
145 translation,
146 types,
147 resolved_tables: HashMap::new(),
148 resolved_heaps: HashMap::new(),
149 resolved_callees: HashMap::new(),
150 resolved_sigs: HashMap::new(),
151 resolved_globals: HashMap::new(),
152 ptr_type,
153 heap_access_spectre_mitigation: isa.flags().enable_heap_access_spectre_mitigation(),
154 table_access_spectre_mitigation: isa.flags().enable_table_access_spectre_mitigation(),
155 page_size_log2: isa.page_size_align_log2(),
156 builtins,
157 name_map: Default::default(),
158 name_intern: Default::default(),
159 }
160 }
161
162 pub(crate) fn ptr_type(&self) -> WasmValType {
164 self.ptr_type
165 }
166
167 pub(crate) fn funcref(&mut self, idx: TypeIndex) -> Callee {
169 Callee::FuncRef(idx)
170 }
171
172 pub(crate) fn callee_from_index(&mut self, idx: FuncIndex) -> Callee {
174 let import = self.translation.module.is_imported_function(idx);
175 if import {
176 Callee::Import(idx)
177 } else {
178 Callee::Local(idx)
179 }
180 }
181
182 pub(crate) fn resolve_block_sig(&self, ty: BlockType) -> BlockSig {
184 use BlockType::*;
185 match ty {
186 Empty => BlockSig::new(control::BlockType::void()),
187 Type(ty) => {
188 let ty = TypeConverter::new(self.translation, self.types).convert_valtype(ty);
189 BlockSig::new(control::BlockType::single(ty))
190 }
191 FuncType(idx) => {
192 let sig_index = self.translation.module.types[TypeIndex::from_u32(idx)];
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_import_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];
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]
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]
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}