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, MemoryIndex, MemoryPlan, MemoryStyle,
15 ModuleTranslation, ModuleTypesBuilder, PrimaryMap, PtrSize, TableIndex, TablePlan, TypeConvert,
16 TypeIndex, VMOffsets, WasmHeapType, WasmValType,
17};
18
19#[derive(Debug, Clone, Copy)]
20pub struct GlobalData {
21 pub offset: u32,
23 pub imported: bool,
25 pub ty: WasmValType,
27}
28
29#[derive(Debug, Copy, Clone)]
31pub struct TableData {
32 pub offset: u32,
34 pub current_elems_offset: u32,
36 pub import_from: Option<u32>,
39 pub(crate) element_size: OperandSize,
41 pub(crate) current_elements_size: OperandSize,
43}
44
45#[derive(Debug, Copy, Clone)]
47pub enum HeapStyle {
48 Static {
50 bound: u64,
53 },
54 Dynamic,
57}
58
59#[derive(Debug, Copy, Clone)]
63pub struct HeapData {
64 pub offset: u32,
69 pub current_length_offset: u32,
71 pub import_from: Option<u32>,
74 pub ty: WasmValType,
76 pub style: HeapStyle,
78 pub min_size: u64,
80 pub max_size: Option<u64>,
82 pub page_size_log2: u8,
87 pub offset_guard_size: u64,
89}
90
91#[derive(Clone)]
95pub(crate) enum Callee {
96 Local(FuncIndex),
98 Import(FuncIndex),
100 FuncRef(TypeIndex),
102 Builtin(BuiltinFunction),
104}
105
106pub struct FuncEnv<'a, 'translation: 'a, 'data: 'translation, P: PtrSize> {
111 pub vmoffsets: &'a VMOffsets<P>,
113 pub translation: &'translation ModuleTranslation<'data>,
115 pub types: &'translation ModuleTypesBuilder,
117 pub builtins: &'translation mut BuiltinFunctions,
119 resolved_tables: HashMap<TableIndex, TableData>,
121 resolved_heaps: HashMap<MemoryIndex, HeapData>,
123 resolved_callees: HashMap<FuncIndex, ABISig>,
126 resolved_sigs: HashMap<TypeIndex, ABISig>,
129 resolved_globals: HashMap<GlobalIndex, GlobalData>,
131 ptr_type: WasmValType,
133 heap_access_spectre_mitigation: bool,
135 table_access_spectre_mitigation: bool,
137 name_map: PrimaryMap<UserExternalNameRef, UserExternalName>,
138 name_intern: HashMap<UserExternalName, UserExternalNameRef>,
139}
140
141pub fn ptr_type_from_ptr_size(size: u8) -> WasmValType {
142 (size == 8)
143 .then(|| WasmValType::I64)
144 .unwrap_or_else(|| unimplemented!("Support for non-64-bit architectures"))
145}
146
147impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
148 pub fn new(
150 vmoffsets: &'a VMOffsets<P>,
151 translation: &'translation ModuleTranslation<'data>,
152 types: &'translation ModuleTypesBuilder,
153 builtins: &'translation mut BuiltinFunctions,
154 isa: &dyn TargetIsa,
155 ptr_type: WasmValType,
156 ) -> Self {
157 Self {
158 vmoffsets,
159 translation,
160 types,
161 resolved_tables: HashMap::new(),
162 resolved_heaps: HashMap::new(),
163 resolved_callees: HashMap::new(),
164 resolved_sigs: HashMap::new(),
165 resolved_globals: HashMap::new(),
166 ptr_type,
167 heap_access_spectre_mitigation: isa.flags().enable_heap_access_spectre_mitigation(),
168 table_access_spectre_mitigation: isa.flags().enable_table_access_spectre_mitigation(),
169 builtins,
170 name_map: Default::default(),
171 name_intern: Default::default(),
172 }
173 }
174
175 pub(crate) fn ptr_type(&self) -> WasmValType {
177 self.ptr_type
178 }
179
180 pub(crate) fn funcref(&mut self, idx: TypeIndex) -> Callee {
182 Callee::FuncRef(idx)
183 }
184
185 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 pub(crate) fn resolve_block_sig(&self, ty: BlockType) -> BlockSig {
197 use BlockType::*;
198 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 let sig = self.types[sig_index].unwrap_func();
207 BlockSig::new(control::BlockType::func(sig.clone()))
208 }
209 }
210 }
211
212 pub fn resolve_global(&mut self, index: GlobalIndex) -> GlobalData {
214 let ty = self.translation.module.globals[index].wasm_ty;
215 let val = || match self.translation.module.defined_global_index(index) {
216 Some(defined_index) => GlobalData {
217 offset: self.vmoffsets.vmctx_vmglobal_definition(defined_index),
218 imported: false,
219 ty,
220 },
221 None => GlobalData {
222 offset: self.vmoffsets.vmctx_vmglobal_import_from(index),
223 imported: true,
224 ty,
225 },
226 };
227
228 *self.resolved_globals.entry(index).or_insert_with(val)
229 }
230
231 pub fn resolve_table_data(&mut self, index: TableIndex) -> TableData {
233 match self.resolved_tables.entry(index) {
234 Occupied(entry) => *entry.get(),
235 Vacant(entry) => {
236 let (from_offset, base_offset, current_elems_offset) =
237 match self.translation.module.defined_table_index(index) {
238 Some(defined) => (
239 None,
240 self.vmoffsets.vmctx_vmtable_definition_base(defined),
241 self.vmoffsets
242 .vmctx_vmtable_definition_current_elements(defined),
243 ),
244 None => (
245 Some(self.vmoffsets.vmctx_vmtable_import_from(index)),
246 self.vmoffsets.vmtable_definition_base().into(),
247 self.vmoffsets.vmtable_definition_current_elements().into(),
248 ),
249 };
250
251 *entry.insert(TableData {
252 import_from: from_offset,
253 offset: base_offset,
254 current_elems_offset,
255 element_size: OperandSize::from_bytes(self.vmoffsets.ptr.size()),
256 current_elements_size: OperandSize::from_bytes(
257 self.vmoffsets.size_of_vmtable_definition_current_elements(),
258 ),
259 })
260 }
261 }
262 }
263
264 pub fn resolve_heap(&mut self, index: MemoryIndex) -> HeapData {
268 match self.resolved_heaps.entry(index) {
269 Occupied(entry) => *entry.get(),
270 Vacant(entry) => {
271 let (import_from, base_offset, current_length_offset) =
272 match self.translation.module.defined_memory_index(index) {
273 Some(defined) => {
274 let owned = self.translation.module.owned_memory_index(defined);
275 (
276 None,
277 self.vmoffsets.vmctx_vmmemory_definition_base(owned),
278 self.vmoffsets
279 .vmctx_vmmemory_definition_current_length(owned),
280 )
281 }
282 None => (
283 Some(self.vmoffsets.vmctx_vmmemory_import_from(index)),
284 self.vmoffsets.ptr.vmmemory_definition_base().into(),
285 self.vmoffsets
286 .ptr
287 .vmmemory_definition_current_length()
288 .into(),
289 ),
290 };
291
292 let plan = &self.translation.module.memory_plans[index];
293 let (min_size, max_size) = heap_limits(&plan);
294 let (style, offset_guard_size) = heap_style_and_offset_guard_size(&plan);
295
296 *entry.insert(HeapData {
297 offset: base_offset,
298 import_from,
299 current_length_offset,
300 style,
301 ty: if plan.memory.memory64 {
302 WasmValType::I64
303 } else {
304 WasmValType::I32
305 },
306 min_size,
307 max_size,
308 page_size_log2: plan.memory.page_size_log2,
309 offset_guard_size,
310 })
311 }
312 }
313 }
314
315 pub fn table_plan(&mut self, index: TableIndex) -> &TablePlan {
317 &self.translation.module.table_plans[index]
318 }
319
320 pub fn heap_access_spectre_mitigation(&self) -> bool {
322 self.heap_access_spectre_mitigation
323 }
324
325 pub fn table_access_spectre_mitigation(&self) -> bool {
328 self.table_access_spectre_mitigation
329 }
330
331 pub(crate) fn callee_sig<'b, A>(&'b mut self, callee: &'b Callee) -> &'b ABISig
332 where
333 A: ABI,
334 {
335 match callee {
336 Callee::Local(idx) | Callee::Import(idx) => {
337 let types = self.translation.get_types();
338 let ty = types[types.core_function_at(idx.as_u32())].unwrap_func();
339 let val = || {
340 let converter = TypeConverter::new(self.translation, self.types);
341 let ty = converter.convert_func_type(&ty);
342 wasm_sig::<A>(&ty)
343 };
344 self.resolved_callees.entry(*idx).or_insert_with(val)
345 }
346 Callee::FuncRef(idx) => {
347 let val = || {
348 let sig_index = self.translation.module.types[*idx];
349 let ty = self.types[sig_index].unwrap_func();
350 let sig = wasm_sig::<A>(ty);
351 sig
352 };
353 self.resolved_sigs.entry(*idx).or_insert_with(val)
354 }
355 Callee::Builtin(b) => b.sig(),
356 }
357 }
358
359 pub fn name_builtin(&mut self, builtin: BuiltinFunctionIndex) -> UserExternalNameRef {
361 self.intern_name(UserExternalName {
362 namespace: wasmtime_cranelift::NS_WASMTIME_BUILTIN,
363 index: builtin.index(),
364 })
365 }
366
367 pub fn name_wasm(&mut self, index: FuncIndex) -> UserExternalNameRef {
369 self.intern_name(UserExternalName {
370 namespace: wasmtime_cranelift::NS_WASM_FUNC,
371 index: index.as_u32(),
372 })
373 }
374
375 fn intern_name(&mut self, name: UserExternalName) -> UserExternalNameRef {
378 *self
379 .name_intern
380 .entry(name.clone())
381 .or_insert_with(|| self.name_map.push(name))
382 }
383
384 pub fn take_name_map(&mut self) -> PrimaryMap<UserExternalNameRef, UserExternalName> {
386 self.name_intern.clear();
387 mem::take(&mut self.name_map)
388 }
389}
390
391pub(crate) struct TypeConverter<'a, 'data: 'a> {
394 translation: &'a ModuleTranslation<'data>,
395 types: &'a ModuleTypesBuilder,
396}
397
398impl TypeConvert for TypeConverter<'_, '_> {
399 fn lookup_heap_type(&self, idx: wasmparser::UnpackedIndex) -> WasmHeapType {
400 wasmtime_environ::WasmparserTypeConverter::new(self.types, &self.translation.module)
401 .lookup_heap_type(idx)
402 }
403
404 fn lookup_type_index(
405 &self,
406 index: wasmparser::UnpackedIndex,
407 ) -> wasmtime_environ::EngineOrModuleTypeIndex {
408 wasmtime_environ::WasmparserTypeConverter::new(self.types, &self.translation.module)
409 .lookup_type_index(index)
410 }
411}
412
413impl<'a, 'data> TypeConverter<'a, 'data> {
414 pub fn new(translation: &'a ModuleTranslation<'data>, types: &'a ModuleTypesBuilder) -> Self {
415 Self { translation, types }
416 }
417}
418
419fn heap_style_and_offset_guard_size(plan: &MemoryPlan) -> (HeapStyle, u64) {
420 match plan {
421 MemoryPlan {
422 style: MemoryStyle::Static { byte_reservation },
423 offset_guard_size,
424 ..
425 } => (
426 HeapStyle::Static {
427 bound: *byte_reservation,
428 },
429 *offset_guard_size,
430 ),
431
432 MemoryPlan {
433 style: MemoryStyle::Dynamic { .. },
434 offset_guard_size,
435 ..
436 } => (HeapStyle::Dynamic, *offset_guard_size),
437 }
438}
439
440fn heap_limits(plan: &MemoryPlan) -> (u64, Option<u64>) {
441 (
442 plan.memory.minimum_byte_size().unwrap_or_else(|_| {
443 u64::MAX
446 }),
447 plan.memory.maximum_byte_size().ok(),
448 )
449}