1 mod gc;
2 pub(crate) mod stack_switching;
3
4 use crate::BuiltinFunctionSignatures;
5 use crate::compiler::Compiler;
6 use crate::translate::{
7 FuncTranslationStacks, GlobalVariable, Heap, HeapData, StructFieldsVec, TableData, TableSize,
8 TargetEnvironment,
9 };
10 use crate::trap::TranslateTrap;
11 use cranelift_codegen::cursor::FuncCursor;
12 use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
13 use cranelift_codegen::ir::immediates::{Imm64, Offset32, V128Imm};
14 use cranelift_codegen::ir::{
15 self, BlockArg, Endianness, ExceptionTableData, ExceptionTableItem, types,
16 };
17 use cranelift_codegen::ir::{ArgumentPurpose, ConstantData, Function, InstBuilder, MemFlags};
18 use cranelift_codegen::ir::{Block, types::*};
19 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
20 use cranelift_entity::packed_option::{PackedOption, ReservedValue};
21 use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap};
22 use cranelift_frontend::Variable;
23 use cranelift_frontend::{FuncInstBuilder, FunctionBuilder};
24 use smallvec::{SmallVec, smallvec};
25 use std::mem;
26 use wasmparser::{FuncValidator, Operator, WasmFeatures, WasmModuleResources};
27 use wasmtime_core::math::f64_cvt_to_int_bounds;
28 use wasmtime_environ::{
29 BuiltinFunctionIndex, ComponentPC, DataIndex, DefinedFuncIndex, ElemIndex,
30 EngineOrModuleTypeIndex, FrameStateSlotBuilder, FrameValType, FuncIndex, FuncKey,
31 GlobalConstValue, GlobalIndex, IndexType, Memory, MemoryIndex, Module, ModuleInternedTypeIndex,
32 ModuleTranslation, ModuleTypesBuilder, PtrSize, Table, TableIndex, TagIndex, Tunables,
33 TypeConvert, TypeIndex, VMOffsets, WasmCompositeInnerType, WasmFuncType, WasmHeapTopType,
34 WasmHeapType, WasmRefType, WasmResult, WasmValType,
35 };
36 use wasmtime_environ::{FUNCREF_INIT_BIT, FUNCREF_MASK};
37
38 #[derive(Debug)]
39 pub(crate) enum Extension {
40 Sign,
41 Zero,
42 }
43
44 /// A struct with an `Option<ir::FuncRef>` member for every builtin
45 /// function, to de-duplicate constructing/getting its function.
46 pub(crate) struct BuiltinFunctions {
47 types: BuiltinFunctionSignatures,
48
49 builtins: [Option<ir::FuncRef>; BuiltinFunctionIndex::len() as usize],
50 breakpoint_trampoline: Option<ir::FuncRef>,
51 }
52
53 impl BuiltinFunctions {
new(compiler: &Compiler) -> Self54 pub(crate) fn new(compiler: &Compiler) -> Self {
55 Self {
56 types: BuiltinFunctionSignatures::new(compiler),
57 builtins: [None; BuiltinFunctionIndex::len() as usize],
58 breakpoint_trampoline: None,
59 }
60 }
61
load_builtin( &mut self, func: &mut Function, builtin: BuiltinFunctionIndex, ) -> ir::FuncRef62 pub(crate) fn load_builtin(
63 &mut self,
64 func: &mut Function,
65 builtin: BuiltinFunctionIndex,
66 ) -> ir::FuncRef {
67 let cache = &mut self.builtins[builtin.index() as usize];
68 if let Some(f) = cache {
69 return *f;
70 }
71 let signature = func.import_signature(self.types.wasm_signature(builtin));
72 let key = FuncKey::WasmToBuiltinTrampoline(builtin);
73 let (namespace, index) = key.into_raw_parts();
74 let name = ir::ExternalName::User(
75 func.declare_imported_user_function(ir::UserExternalName { namespace, index }),
76 );
77 let f = func.import_function(ir::ExtFuncData {
78 name,
79 signature,
80 colocated: true,
81 patchable: false,
82 });
83 *cache = Some(f);
84 f
85 }
86
patchable_breakpoint(&mut self, func: &mut Function) -> ir::FuncRef87 pub(crate) fn patchable_breakpoint(&mut self, func: &mut Function) -> ir::FuncRef {
88 *self.breakpoint_trampoline.get_or_insert_with(|| {
89 let mut signature = ir::Signature::new(CallConv::PreserveAll);
90 signature
91 .params
92 .push(ir::AbiParam::new(self.types.pointer_type));
93 let signature = func.import_signature(signature);
94 let key = FuncKey::PatchableToBuiltinTrampoline(BuiltinFunctionIndex::breakpoint());
95 let (namespace, index) = key.into_raw_parts();
96 let name = ir::ExternalName::User(
97 func.declare_imported_user_function(ir::UserExternalName { namespace, index }),
98 );
99 func.import_function(ir::ExtFuncData {
100 name,
101 signature,
102 colocated: true,
103 patchable: true,
104 })
105 })
106 }
107 }
108
109 // Generate helper methods on `BuiltinFunctions` above for each named builtin
110 // as well.
111 macro_rules! declare_function_signatures {
112 ($(
113 $( #[$attr:meta] )*
114 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
115 )*) => {
116 $(impl BuiltinFunctions {
117 $( #[$attr] )*
118 #[allow(dead_code, reason = "debug breakpoint libcall not used in host ABI, only patchable ABI")]
119 pub(crate) fn $name(&mut self, func: &mut Function) -> ir::FuncRef {
120 self.load_builtin(func, BuiltinFunctionIndex::$name())
121 }
122 })*
123 };
124 }
125 wasmtime_environ::foreach_builtin_function!(declare_function_signatures);
126
127 /// The `FuncEnvironment` implementation for use by the `ModuleEnvironment`.
128 pub struct FuncEnvironment<'module_environment> {
129 compiler: &'module_environment Compiler,
130 isa: &'module_environment (dyn TargetIsa + 'module_environment),
131 key: FuncKey,
132 pub(crate) module: &'module_environment Module,
133 types: &'module_environment ModuleTypesBuilder,
134 wasm_func_ty: &'module_environment WasmFuncType,
135 sig_ref_to_ty: SecondaryMap<ir::SigRef, Option<&'module_environment WasmFuncType>>,
136 needs_gc_heap: bool,
137 entities: WasmEntities,
138
139 /// The byte offset of the module's wasm binary within the outer
140 /// binary (e.g. a component). Used to make source locations in
141 /// guest-debug frame tables module-relative.
142 pub(crate) wasm_module_offset: u64,
143
144 /// Translation state at the given point.
145 pub(crate) stacks: FuncTranslationStacks,
146
147 #[cfg(feature = "gc")]
148 ty_to_gc_layout: std::collections::HashMap<
149 wasmtime_environ::ModuleInternedTypeIndex,
150 wasmtime_environ::GcLayout,
151 >,
152
153 #[cfg(feature = "gc")]
154 gc_heap: Option<Heap>,
155
156 /// The Cranelift global holding the GC heap's base address.
157 #[cfg(feature = "gc")]
158 gc_heap_base: Option<ir::GlobalValue>,
159
160 /// The Cranelift global holding the GC heap's base address.
161 #[cfg(feature = "gc")]
162 gc_heap_bound: Option<ir::GlobalValue>,
163
164 translation: &'module_environment ModuleTranslation<'module_environment>,
165
166 /// Heaps implementing WebAssembly linear memories.
167 heaps: PrimaryMap<Heap, HeapData>,
168
169 /// The Cranelift global holding the vmctx address.
170 vmctx: Option<ir::GlobalValue>,
171
172 /// The Cranelift global for our vmctx's `*mut VMStoreContext`.
173 vm_store_context: Option<ir::GlobalValue>,
174
175 /// Caches of signatures for builtin functions.
176 builtin_functions: BuiltinFunctions,
177
178 /// Offsets to struct fields accessed by JIT code.
179 pub(crate) offsets: VMOffsets<u8>,
180
181 tunables: &'module_environment Tunables,
182
183 /// A function-local variable which stores the cached value of the amount of
184 /// fuel remaining to execute. If used this is modified frequently so it's
185 /// stored locally as a variable instead of always referenced from the field
186 /// in `*const VMStoreContext`
187 fuel_var: cranelift_frontend::Variable,
188
189 /// A cached epoch deadline value, when performing epoch-based
190 /// interruption. Loaded from `VMStoreContext` and reloaded after
191 /// any yield.
192 epoch_deadline_var: cranelift_frontend::Variable,
193
194 /// A cached pointer to the per-Engine epoch counter, when
195 /// performing epoch-based interruption. Initialized in the
196 /// function prologue. We prefer to use a variable here rather
197 /// than reload on each check because it's better to let the
198 /// regalloc keep it in a register if able; if not, it can always
199 /// spill, and this isn't any worse than reloading each time.
200 epoch_ptr_var: cranelift_frontend::Variable,
201
202 fuel_consumed: i64,
203
204 /// A `GlobalValue` in CLIF which represents the stack limit.
205 ///
206 /// Typically this resides in the `stack_limit` value of `ir::Function` but
207 /// that requires signal handlers on the host and when that's disabled this
208 /// is here with an explicit check instead. Note that the explicit check is
209 /// always present even if this is a "leaf" function, as we have to call
210 /// into the host to trap when signal handlers are disabled.
211 pub(crate) stack_limit_at_function_entry: Option<ir::GlobalValue>,
212
213 /// Used by the stack switching feature. If set, we have a allocated a
214 /// slot on this function's stack to be used for the
215 /// current stack's `handler_list` field.
216 stack_switching_handler_list_buffer: Option<ir::StackSlot>,
217
218 /// Used by the stack switching feature. If set, we have a allocated a
219 /// slot on this function's stack to be used for the
220 /// current continuation's `values` field.
221 stack_switching_values_buffer: Option<ir::StackSlot>,
222
223 /// The stack-slot used for exposing Wasm state via debug
224 /// instrumentation, if any, and the builder containing its metadata.
225 pub(crate) state_slot: Option<(ir::StackSlot, FrameStateSlotBuilder)>,
226
227 /// The next-srcloc: the location of the operator *after* this one
228 /// (in original bytecode order, i.e., not accounting for
229 /// nonlinear control flow). This is useful in cases where we need
230 /// to e.g. record the return-address of a callsite for debuginfo.
231 pub(crate) next_srcloc: ir::SourceLoc,
232 }
233
234 impl<'module_environment> FuncEnvironment<'module_environment> {
new( compiler: &'module_environment Compiler, translation: &'module_environment ModuleTranslation<'module_environment>, types: &'module_environment ModuleTypesBuilder, wasm_func_ty: &'module_environment WasmFuncType, key: FuncKey, ) -> Self235 pub fn new(
236 compiler: &'module_environment Compiler,
237 translation: &'module_environment ModuleTranslation<'module_environment>,
238 types: &'module_environment ModuleTypesBuilder,
239 wasm_func_ty: &'module_environment WasmFuncType,
240 key: FuncKey,
241 ) -> Self {
242 let tunables = compiler.tunables();
243 let builtin_functions = BuiltinFunctions::new(compiler);
244
245 // This isn't used during translation, so squash the warning about this
246 // being unused from the compiler.
247 let _ = BuiltinFunctions::raise;
248
249 Self {
250 key,
251 isa: compiler.isa(),
252 module: &translation.module,
253 compiler,
254 types,
255 wasm_func_ty,
256 sig_ref_to_ty: SecondaryMap::default(),
257 needs_gc_heap: false,
258 entities: WasmEntities::default(),
259 stacks: FuncTranslationStacks::new(),
260
261 #[cfg(feature = "gc")]
262 ty_to_gc_layout: std::collections::HashMap::new(),
263 #[cfg(feature = "gc")]
264 gc_heap: None,
265 #[cfg(feature = "gc")]
266 gc_heap_base: None,
267 #[cfg(feature = "gc")]
268 gc_heap_bound: None,
269
270 heaps: PrimaryMap::default(),
271 vmctx: None,
272 vm_store_context: None,
273 builtin_functions,
274 offsets: VMOffsets::new(compiler.isa().pointer_bytes(), &translation.module),
275 tunables,
276 fuel_var: Variable::reserved_value(),
277 epoch_deadline_var: Variable::reserved_value(),
278 epoch_ptr_var: Variable::reserved_value(),
279
280 // Start with at least one fuel being consumed because even empty
281 // functions should consume at least some fuel.
282 fuel_consumed: 1,
283
284 translation,
285
286 stack_limit_at_function_entry: None,
287
288 stack_switching_handler_list_buffer: None,
289 stack_switching_values_buffer: None,
290
291 state_slot: None,
292 next_srcloc: ir::SourceLoc::default(),
293 wasm_module_offset: translation.wasm_module_offset,
294 }
295 }
296
pointer_type(&self) -> ir::Type297 pub(crate) fn pointer_type(&self) -> ir::Type {
298 self.isa.pointer_type()
299 }
300
vmctx(&mut self, func: &mut Function) -> ir::GlobalValue301 pub(crate) fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue {
302 self.vmctx.unwrap_or_else(|| {
303 let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
304 self.vmctx = Some(vmctx);
305 vmctx
306 })
307 }
308
get_table_copy_func( &mut self, func: &mut Function, dst_table_index: TableIndex, src_table_index: TableIndex, ) -> (ir::FuncRef, usize, usize)309 fn get_table_copy_func(
310 &mut self,
311 func: &mut Function,
312 dst_table_index: TableIndex,
313 src_table_index: TableIndex,
314 ) -> (ir::FuncRef, usize, usize) {
315 let sig = self.builtin_functions.table_copy(func);
316 (
317 sig,
318 dst_table_index.as_u32() as usize,
319 src_table_index.as_u32() as usize,
320 )
321 }
322
323 #[cfg(feature = "threads")]
get_memory_atomic_wait(&mut self, func: &mut Function, ty: ir::Type) -> ir::FuncRef324 fn get_memory_atomic_wait(&mut self, func: &mut Function, ty: ir::Type) -> ir::FuncRef {
325 match ty {
326 I32 => self.builtin_functions.memory_atomic_wait32(func),
327 I64 => self.builtin_functions.memory_atomic_wait64(func),
328 x => panic!("get_memory_atomic_wait unsupported type: {x:?}"),
329 }
330 }
331
get_global_location( &mut self, func: &mut ir::Function, index: GlobalIndex, ) -> (ir::GlobalValue, i32)332 fn get_global_location(
333 &mut self,
334 func: &mut ir::Function,
335 index: GlobalIndex,
336 ) -> (ir::GlobalValue, i32) {
337 let pointer_type = self.pointer_type();
338 let vmctx = self.vmctx(func);
339 if let Some(def_index) = self.module.defined_global_index(index) {
340 let offset = i32::try_from(self.offsets.vmctx_vmglobal_definition(def_index)).unwrap();
341 (vmctx, offset)
342 } else {
343 let from_offset = self.offsets.vmctx_vmglobal_import_from(index);
344 let global = func.create_global_value(ir::GlobalValueData::Load {
345 base: vmctx,
346 offset: Offset32::new(i32::try_from(from_offset).unwrap()),
347 global_type: pointer_type,
348 flags: MemFlags::trusted().with_readonly().with_can_move(),
349 });
350 (global, 0)
351 }
352 }
353
354 /// Get or create the `ir::Global` for the `*mut VMStoreContext` in our
355 /// `VMContext`.
get_vmstore_context_ptr_global(&mut self, func: &mut ir::Function) -> ir::GlobalValue356 fn get_vmstore_context_ptr_global(&mut self, func: &mut ir::Function) -> ir::GlobalValue {
357 if let Some(ptr) = self.vm_store_context {
358 return ptr;
359 }
360
361 let offset = self.offsets.ptr.vmctx_store_context();
362 let base = self.vmctx(func);
363 let ptr = func.create_global_value(ir::GlobalValueData::Load {
364 base,
365 offset: Offset32::new(offset.into()),
366 global_type: self.pointer_type(),
367 flags: ir::MemFlags::trusted().with_readonly().with_can_move(),
368 });
369 self.vm_store_context = Some(ptr);
370 ptr
371 }
372
373 /// Get the `*mut VMStoreContext` value for our `VMContext`.
get_vmstore_context_ptr(&mut self, builder: &mut FunctionBuilder) -> ir::Value374 fn get_vmstore_context_ptr(&mut self, builder: &mut FunctionBuilder) -> ir::Value {
375 let global = self.get_vmstore_context_ptr_global(&mut builder.func);
376 builder.ins().global_value(self.pointer_type(), global)
377 }
378
fuel_function_entry(&mut self, builder: &mut FunctionBuilder<'_>)379 fn fuel_function_entry(&mut self, builder: &mut FunctionBuilder<'_>) {
380 // On function entry we load the amount of fuel into a function-local
381 // `self.fuel_var` to make fuel modifications fast locally. This cache
382 // is then periodically flushed to the Store-defined location in
383 // `VMStoreContext` later.
384 debug_assert!(self.fuel_var.is_reserved_value());
385 self.fuel_var = builder.declare_var(ir::types::I64);
386 self.fuel_load_into_var(builder);
387 self.fuel_check(builder);
388 }
389
fuel_function_exit(&mut self, builder: &mut FunctionBuilder<'_>)390 fn fuel_function_exit(&mut self, builder: &mut FunctionBuilder<'_>) {
391 // On exiting the function we need to be sure to save the fuel we have
392 // cached locally in `self.fuel_var` back into the Store-defined
393 // location.
394 self.fuel_save_from_var(builder);
395 }
396
fuel_before_op( &mut self, op: &Operator<'_>, builder: &mut FunctionBuilder<'_>, reachable: bool, )397 fn fuel_before_op(
398 &mut self,
399 op: &Operator<'_>,
400 builder: &mut FunctionBuilder<'_>,
401 reachable: bool,
402 ) {
403 if !reachable {
404 // In unreachable code we shouldn't have any leftover fuel we
405 // haven't accounted for since the reason for us to become
406 // unreachable should have already added it to `self.fuel_var`.
407 debug_assert_eq!(self.fuel_consumed, 0);
408 return;
409 }
410
411 self.fuel_consumed += self.tunables.operator_cost.cost(op);
412
413 match op {
414 // Exiting a function (via a return or unreachable) or otherwise
415 // entering a different function (via a call) means that we need to
416 // update the fuel consumption in `VMStoreContext` because we're
417 // about to move control out of this function itself and the fuel
418 // may need to be read.
419 //
420 // Before this we need to update the fuel counter from our own cost
421 // leading up to this function call, and then we can store
422 // `self.fuel_var` into `VMStoreContext`.
423 Operator::Unreachable
424 | Operator::Return
425 | Operator::CallIndirect { .. }
426 | Operator::Call { .. }
427 | Operator::ReturnCall { .. }
428 | Operator::ReturnCallRef { .. }
429 | Operator::ReturnCallIndirect { .. }
430 | Operator::Throw { .. } | Operator::ThrowRef => {
431 self.fuel_increment_var(builder);
432 self.fuel_save_from_var(builder);
433 }
434
435 // To ensure all code preceding a loop is only counted once we
436 // update the fuel variable on entry.
437 Operator::Loop { .. }
438
439 // Entering into an `if` block means that the edge we take isn't
440 // known until runtime, so we need to update our fuel consumption
441 // before we take the branch.
442 | Operator::If { .. }
443
444 // Control-flow instructions mean that we're moving to the end/exit
445 // of a block somewhere else. That means we need to update the fuel
446 // counter since we're effectively terminating our basic block.
447 | Operator::Br { .. }
448 | Operator::BrIf { .. }
449 | Operator::BrTable { .. }
450 | Operator::BrOnNull { .. }
451 | Operator::BrOnNonNull { .. }
452 | Operator::BrOnCast { .. }
453 | Operator::BrOnCastFail { .. }
454
455 // Exiting a scope means that we need to update the fuel
456 // consumption because there are multiple ways to exit a scope and
457 // this is the only time we have to account for instructions
458 // executed so far.
459 | Operator::End
460
461 // This is similar to `end`, except that it's only the terminator
462 // for an `if` block. The same reasoning applies though in that we
463 // are terminating a basic block and need to update the fuel
464 // variable.
465 | Operator::Else => self.fuel_increment_var(builder),
466
467 // This is a normal instruction where the fuel is buffered to later
468 // get added to `self.fuel_var`.
469 //
470 // Note that we generally ignore instructions which may trap and
471 // therefore result in exiting a block early. Current usage of fuel
472 // means that it's not too important to account for a precise amount
473 // of fuel consumed but rather "close to the actual amount" is good
474 // enough. For 100% precise counting, however, we'd probably need to
475 // not only increment but also save the fuel amount more often
476 // around trapping instructions. (see the `unreachable` instruction
477 // case above)
478 //
479 // Note that `Block` is specifically omitted from incrementing the
480 // fuel variable. Control flow entering a `block` is unconditional
481 // which means it's effectively executing straight-line code. We'll
482 // update the counter when exiting a block, but we shouldn't need to
483 // do so upon entering a block.
484 _ => {}
485 }
486 }
487
fuel_after_op(&mut self, op: &Operator<'_>, builder: &mut FunctionBuilder<'_>)488 fn fuel_after_op(&mut self, op: &Operator<'_>, builder: &mut FunctionBuilder<'_>) {
489 // After a function call we need to reload our fuel value since the
490 // function may have changed it.
491 match op {
492 Operator::Call { .. } | Operator::CallIndirect { .. } => {
493 self.fuel_load_into_var(builder);
494 }
495 _ => {}
496 }
497 }
498
499 /// Adds `self.fuel_consumed` to the `fuel_var`, zero-ing out the amount of
500 /// fuel consumed at that point.
fuel_increment_var(&mut self, builder: &mut FunctionBuilder<'_>)501 fn fuel_increment_var(&mut self, builder: &mut FunctionBuilder<'_>) {
502 let consumption = mem::replace(&mut self.fuel_consumed, 0);
503 if consumption == 0 {
504 return;
505 }
506
507 let fuel = builder.use_var(self.fuel_var);
508 let fuel = builder.ins().iadd_imm(fuel, consumption);
509 builder.def_var(self.fuel_var, fuel);
510 }
511
512 /// Loads the fuel consumption value from `VMStoreContext` into `self.fuel_var`
fuel_load_into_var(&mut self, builder: &mut FunctionBuilder<'_>)513 fn fuel_load_into_var(&mut self, builder: &mut FunctionBuilder<'_>) {
514 let (addr, offset) = self.fuel_addr_offset(builder);
515 let fuel = builder
516 .ins()
517 .load(ir::types::I64, ir::MemFlags::trusted(), addr, offset);
518 builder.def_var(self.fuel_var, fuel);
519 }
520
521 /// Stores the fuel consumption value from `self.fuel_var` into
522 /// `VMStoreContext`.
fuel_save_from_var(&mut self, builder: &mut FunctionBuilder<'_>)523 fn fuel_save_from_var(&mut self, builder: &mut FunctionBuilder<'_>) {
524 let (addr, offset) = self.fuel_addr_offset(builder);
525 let fuel_consumed = builder.use_var(self.fuel_var);
526 builder
527 .ins()
528 .store(ir::MemFlags::trusted(), fuel_consumed, addr, offset);
529 }
530
531 /// Returns the `(address, offset)` of the fuel consumption within
532 /// `VMStoreContext`, used to perform loads/stores later.
fuel_addr_offset( &mut self, builder: &mut FunctionBuilder<'_>, ) -> (ir::Value, ir::immediates::Offset32)533 fn fuel_addr_offset(
534 &mut self,
535 builder: &mut FunctionBuilder<'_>,
536 ) -> (ir::Value, ir::immediates::Offset32) {
537 let vmstore_ctx = self.get_vmstore_context_ptr(builder);
538 (
539 vmstore_ctx,
540 i32::from(self.offsets.ptr.vmstore_context_fuel_consumed()).into(),
541 )
542 }
543
544 /// Checks the amount of remaining, and if we've run out of fuel we call
545 /// the out-of-fuel function.
fuel_check(&mut self, builder: &mut FunctionBuilder)546 fn fuel_check(&mut self, builder: &mut FunctionBuilder) {
547 self.fuel_increment_var(builder);
548 let out_of_gas_block = builder.create_block();
549 let continuation_block = builder.create_block();
550
551 // Note that our fuel is encoded as adding positive values to a
552 // negative number. Whenever the negative number goes positive that
553 // means we ran out of fuel.
554 //
555 // Compare to see if our fuel is positive, and if so we ran out of gas.
556 // Otherwise we can continue on like usual.
557 let zero = builder.ins().iconst(ir::types::I64, 0);
558 let fuel = builder.use_var(self.fuel_var);
559 let cmp = builder
560 .ins()
561 .icmp(IntCC::SignedGreaterThanOrEqual, fuel, zero);
562 builder
563 .ins()
564 .brif(cmp, out_of_gas_block, &[], continuation_block, &[]);
565 builder.seal_block(out_of_gas_block);
566
567 // If we ran out of gas then we call our out-of-gas intrinsic and it
568 // figures out what to do. Note that this may raise a trap, or do
569 // something like yield to an async runtime. In either case we don't
570 // assume what happens and handle the case the intrinsic returns.
571 //
572 // Note that we save/reload fuel around this since the out-of-gas
573 // intrinsic may alter how much fuel is in the system.
574 builder.switch_to_block(out_of_gas_block);
575 self.fuel_save_from_var(builder);
576 let out_of_gas = self.builtin_functions.out_of_gas(builder.func);
577 let vmctx = self.vmctx_val(&mut builder.cursor());
578 builder.ins().call(out_of_gas, &[vmctx]);
579 self.fuel_load_into_var(builder);
580 builder.ins().jump(continuation_block, &[]);
581 builder.seal_block(continuation_block);
582
583 builder.switch_to_block(continuation_block);
584 }
585
epoch_function_entry(&mut self, builder: &mut FunctionBuilder<'_>)586 fn epoch_function_entry(&mut self, builder: &mut FunctionBuilder<'_>) {
587 debug_assert!(self.epoch_deadline_var.is_reserved_value());
588 self.epoch_deadline_var = builder.declare_var(ir::types::I64);
589 // Let epoch_check_full load the current deadline and call def_var
590
591 debug_assert!(self.epoch_ptr_var.is_reserved_value());
592 self.epoch_ptr_var = builder.declare_var(self.pointer_type());
593 let epoch_ptr = self.epoch_ptr(builder);
594 builder.def_var(self.epoch_ptr_var, epoch_ptr);
595
596 // We must check for an epoch change when entering a
597 // function. Why? Why aren't checks at loops sufficient to
598 // bound runtime to O(|static program size|)?
599 //
600 // The reason is that one can construct a "zip-bomb-like"
601 // program with exponential-in-program-size runtime, with no
602 // backedges (loops), by building a tree of function calls: f0
603 // calls f1 ten times, f1 calls f2 ten times, etc. E.g., nine
604 // levels of this yields a billion function calls with no
605 // backedges. So we can't do checks only at backedges.
606 //
607 // In this "call-tree" scenario, and in fact in any program
608 // that uses calls as a sort of control flow to try to evade
609 // backedge checks, a check at every function entry is
610 // sufficient. Then, combined with checks at every backedge
611 // (loop) the longest runtime between checks is bounded by the
612 // straightline length of any function body.
613 let continuation_block = builder.create_block();
614 let cur_epoch_value = self.epoch_load_current(builder);
615 self.epoch_check_full(builder, cur_epoch_value, continuation_block);
616 }
617
618 #[cfg(feature = "wmemcheck")]
hook_malloc_exit(&mut self, builder: &mut FunctionBuilder, retvals: &[ir::Value])619 fn hook_malloc_exit(&mut self, builder: &mut FunctionBuilder, retvals: &[ir::Value]) {
620 let check_malloc = self.builtin_functions.check_malloc(builder.func);
621 let vmctx = self.vmctx_val(&mut builder.cursor());
622 let func_args = builder
623 .func
624 .dfg
625 .block_params(builder.func.layout.entry_block().unwrap());
626 let len = if func_args.len() < 3 {
627 return;
628 } else {
629 // If a function named `malloc` has at least one argument, we assume the
630 // first argument is the requested allocation size.
631 func_args[2]
632 };
633 let retval = if retvals.len() < 1 {
634 return;
635 } else {
636 retvals[0]
637 };
638 builder.ins().call(check_malloc, &[vmctx, retval, len]);
639 }
640
641 #[cfg(feature = "wmemcheck")]
hook_free_exit(&mut self, builder: &mut FunctionBuilder)642 fn hook_free_exit(&mut self, builder: &mut FunctionBuilder) {
643 let check_free = self.builtin_functions.check_free(builder.func);
644 let vmctx = self.vmctx_val(&mut builder.cursor());
645 let func_args = builder
646 .func
647 .dfg
648 .block_params(builder.func.layout.entry_block().unwrap());
649 let ptr = if func_args.len() < 3 {
650 return;
651 } else {
652 // If a function named `free` has at least one argument, we assume the
653 // first argument is a pointer to memory.
654 func_args[2]
655 };
656 builder.ins().call(check_free, &[vmctx, ptr]);
657 }
658
epoch_ptr(&mut self, builder: &mut FunctionBuilder<'_>) -> ir::Value659 fn epoch_ptr(&mut self, builder: &mut FunctionBuilder<'_>) -> ir::Value {
660 let vmctx = self.vmctx(builder.func);
661 let pointer_type = self.pointer_type();
662 let base = builder.ins().global_value(pointer_type, vmctx);
663 let offset = i32::from(self.offsets.ptr.vmctx_epoch_ptr());
664 let epoch_ptr = builder
665 .ins()
666 .load(pointer_type, ir::MemFlags::trusted(), base, offset);
667 epoch_ptr
668 }
669
epoch_load_current(&mut self, builder: &mut FunctionBuilder<'_>) -> ir::Value670 fn epoch_load_current(&mut self, builder: &mut FunctionBuilder<'_>) -> ir::Value {
671 let addr = builder.use_var(self.epoch_ptr_var);
672 builder.ins().load(
673 ir::types::I64,
674 ir::MemFlags::trusted(),
675 addr,
676 ir::immediates::Offset32::new(0),
677 )
678 }
679
epoch_check(&mut self, builder: &mut FunctionBuilder<'_>)680 fn epoch_check(&mut self, builder: &mut FunctionBuilder<'_>) {
681 let continuation_block = builder.create_block();
682
683 // Load new epoch and check against the cached deadline.
684 let cur_epoch_value = self.epoch_load_current(builder);
685 self.epoch_check_cached(builder, cur_epoch_value, continuation_block);
686
687 // At this point we've noticed that the epoch has exceeded our
688 // cached deadline. However the real deadline may have been
689 // updated (within another yield) during some function that we
690 // called in the meantime, so reload the cache and check again.
691 self.epoch_check_full(builder, cur_epoch_value, continuation_block);
692 }
693
epoch_check_cached( &mut self, builder: &mut FunctionBuilder, cur_epoch_value: ir::Value, continuation_block: ir::Block, )694 fn epoch_check_cached(
695 &mut self,
696 builder: &mut FunctionBuilder,
697 cur_epoch_value: ir::Value,
698 continuation_block: ir::Block,
699 ) {
700 let new_epoch_block = builder.create_block();
701 builder.set_cold_block(new_epoch_block);
702
703 let epoch_deadline = builder.use_var(self.epoch_deadline_var);
704 let cmp = builder.ins().icmp(
705 IntCC::UnsignedGreaterThanOrEqual,
706 cur_epoch_value,
707 epoch_deadline,
708 );
709 builder
710 .ins()
711 .brif(cmp, new_epoch_block, &[], continuation_block, &[]);
712 builder.seal_block(new_epoch_block);
713
714 builder.switch_to_block(new_epoch_block);
715 }
716
epoch_check_full( &mut self, builder: &mut FunctionBuilder, cur_epoch_value: ir::Value, continuation_block: ir::Block, )717 fn epoch_check_full(
718 &mut self,
719 builder: &mut FunctionBuilder,
720 cur_epoch_value: ir::Value,
721 continuation_block: ir::Block,
722 ) {
723 // We keep the deadline cached in a register to speed the checks
724 // in the common case (between epoch ticks) but we want to do a
725 // precise check here by reloading the cache first.
726 let vmstore_ctx = self.get_vmstore_context_ptr(builder);
727 let deadline = builder.ins().load(
728 ir::types::I64,
729 ir::MemFlags::trusted(),
730 vmstore_ctx,
731 ir::immediates::Offset32::new(self.offsets.ptr.vmstore_context_epoch_deadline() as i32),
732 );
733 builder.def_var(self.epoch_deadline_var, deadline);
734 self.epoch_check_cached(builder, cur_epoch_value, continuation_block);
735
736 let new_epoch = self.builtin_functions.new_epoch(builder.func);
737 let vmctx = self.vmctx_val(&mut builder.cursor());
738 // new_epoch() returns the new deadline, so we don't have to
739 // reload it.
740 let call = builder.ins().call(new_epoch, &[vmctx]);
741 let new_deadline = *builder.func.dfg.inst_results(call).first().unwrap();
742 builder.def_var(self.epoch_deadline_var, new_deadline);
743 builder.ins().jump(continuation_block, &[]);
744 builder.seal_block(continuation_block);
745
746 builder.switch_to_block(continuation_block);
747 }
748
749 /// Get the Memory for the given index.
memory(&self, index: MemoryIndex) -> Memory750 fn memory(&self, index: MemoryIndex) -> Memory {
751 self.module.memories[index]
752 }
753
754 /// Get the Table for the given index.
table(&self, index: TableIndex) -> Table755 fn table(&self, index: TableIndex) -> Table {
756 self.module.tables[index]
757 }
758
759 /// Cast the value to I64 and sign extend if necessary.
760 ///
761 /// Returns the value casted to I64.
cast_index_to_i64( &self, pos: &mut FuncCursor<'_>, val: ir::Value, index_type: IndexType, ) -> ir::Value762 fn cast_index_to_i64(
763 &self,
764 pos: &mut FuncCursor<'_>,
765 val: ir::Value,
766 index_type: IndexType,
767 ) -> ir::Value {
768 match index_type {
769 IndexType::I32 => pos.ins().uextend(I64, val),
770 IndexType::I64 => val,
771 }
772 }
773
774 /// Convert the target pointer-sized integer `val` into the memory/table's index type.
775 ///
776 /// For memory, `val` is holding a memory length (or the `-1` `memory.grow`-failed sentinel).
777 /// For table, `val` is holding a table length.
778 ///
779 /// This might involve extending or truncating it depending on the memory/table's
780 /// index type and the target's pointer type.
convert_pointer_to_index_type( &self, mut pos: FuncCursor<'_>, val: ir::Value, index_type: IndexType, single_byte_pages: bool, ) -> ir::Value781 fn convert_pointer_to_index_type(
782 &self,
783 mut pos: FuncCursor<'_>,
784 val: ir::Value,
785 index_type: IndexType,
786 // When it is a memory and the memory is using single-byte pages,
787 // we need to handle the truncation differently. See comments below.
788 //
789 // When it is a table, this should be set to false.
790 single_byte_pages: bool,
791 ) -> ir::Value {
792 let desired_type = index_type_to_ir_type(index_type);
793 let pointer_type = self.pointer_type();
794 assert_eq!(pos.func.dfg.value_type(val), pointer_type);
795
796 // The current length is of type `pointer_type` but we need to fit it
797 // into `desired_type`. We are guaranteed that the result will always
798 // fit, so we just need to do the right ireduce/sextend here.
799 if pointer_type == desired_type {
800 val
801 } else if pointer_type.bits() > desired_type.bits() {
802 pos.ins().ireduce(desired_type, val)
803 } else {
804 // We have a 64-bit memory/table on a 32-bit host -- this combo doesn't
805 // really make a whole lot of sense to do from a user perspective
806 // but that is neither here nor there. We want to logically do an
807 // unsigned extend *except* when we are given the `-1` sentinel,
808 // which we must preserve as `-1` in the wider type.
809 match single_byte_pages {
810 false => {
811 // In the case that we have default page sizes, we can
812 // always sign extend, since valid memory lengths (in pages)
813 // never have their sign bit set, and so if the sign bit is
814 // set then this must be the `-1` sentinel, which we want to
815 // preserve through the extension.
816 //
817 // When it comes to table, `single_byte_pages` should have always been set to false.
818 // Then we simply do a signed extension.
819 pos.ins().sextend(desired_type, val)
820 }
821 true => {
822 // For single-byte pages, we have to explicitly check for
823 // `-1` and choose whether to do an unsigned extension or
824 // return a larger `-1` because there are valid memory
825 // lengths (in pages) that have the sign bit set.
826 let extended = pos.ins().uextend(desired_type, val);
827 let neg_one = pos.ins().iconst(desired_type, -1);
828 let is_failure = pos.ins().icmp_imm(IntCC::Equal, val, -1);
829 pos.ins().select(is_failure, neg_one, extended)
830 }
831 }
832 }
833 }
834
get_or_init_func_ref_table_elem( &mut self, builder: &mut FunctionBuilder, table_index: TableIndex, index: ir::Value, cold_blocks: bool, ) -> ir::Value835 fn get_or_init_func_ref_table_elem(
836 &mut self,
837 builder: &mut FunctionBuilder,
838 table_index: TableIndex,
839 index: ir::Value,
840 cold_blocks: bool,
841 ) -> ir::Value {
842 let pointer_type = self.pointer_type();
843 let table_data = self.get_or_create_table(builder.func, table_index);
844
845 // To support lazy initialization of table
846 // contents, we check for a null entry here, and
847 // if null, we take a slow-path that invokes a
848 // libcall.
849 let (table_entry_addr, flags) = table_data.prepare_table_addr(self, builder, index);
850 let value = builder.ins().load(pointer_type, flags, table_entry_addr, 0);
851
852 if !self.tunables.table_lazy_init {
853 return value;
854 }
855
856 // Mask off the "initialized bit". See documentation on
857 // FUNCREF_INIT_BIT in crates/environ/src/ref_bits.rs for more
858 // details. Note that `FUNCREF_MASK` has type `usize` which may not be
859 // appropriate for the target architecture. Right now its value is
860 // always -2 so assert that part doesn't change and then thread through
861 // -2 as the immediate.
862 assert_eq!(FUNCREF_MASK as isize, -2);
863 let value_masked = builder.ins().band_imm(value, Imm64::from(-2));
864
865 let null_block = builder.create_block();
866 let continuation_block = builder.create_block();
867 if cold_blocks {
868 builder.set_cold_block(null_block);
869 builder.set_cold_block(continuation_block);
870 }
871 let result_param = builder.append_block_param(continuation_block, pointer_type);
872 builder.set_cold_block(null_block);
873
874 builder.ins().brif(
875 value,
876 continuation_block,
877 &[value_masked.into()],
878 null_block,
879 &[],
880 );
881 builder.seal_block(null_block);
882
883 builder.switch_to_block(null_block);
884 let index_type = self.table(table_index).idx_type;
885 let table_index = builder.ins().iconst(I32, table_index.index() as i64);
886 let lazy_init = self
887 .builtin_functions
888 .table_get_lazy_init_func_ref(builder.func);
889 let vmctx = self.vmctx_val(&mut builder.cursor());
890 let index = self.cast_index_to_i64(&mut builder.cursor(), index, index_type);
891 let call_inst = builder.ins().call(lazy_init, &[vmctx, table_index, index]);
892 let returned_entry = builder.func.dfg.inst_results(call_inst)[0];
893 builder
894 .ins()
895 .jump(continuation_block, &[returned_entry.into()]);
896 builder.seal_block(continuation_block);
897
898 builder.switch_to_block(continuation_block);
899 result_param
900 }
901
902 #[cfg(feature = "wmemcheck")]
check_malloc_start(&mut self, builder: &mut FunctionBuilder)903 fn check_malloc_start(&mut self, builder: &mut FunctionBuilder) {
904 let malloc_start = self.builtin_functions.malloc_start(builder.func);
905 let vmctx = self.vmctx_val(&mut builder.cursor());
906 builder.ins().call(malloc_start, &[vmctx]);
907 }
908
909 #[cfg(feature = "wmemcheck")]
check_free_start(&mut self, builder: &mut FunctionBuilder)910 fn check_free_start(&mut self, builder: &mut FunctionBuilder) {
911 let free_start = self.builtin_functions.free_start(builder.func);
912 let vmctx = self.vmctx_val(&mut builder.cursor());
913 builder.ins().call(free_start, &[vmctx]);
914 }
915
916 #[cfg(feature = "wmemcheck")]
current_func_name(&self, builder: &mut FunctionBuilder) -> Option<&str>917 fn current_func_name(&self, builder: &mut FunctionBuilder) -> Option<&str> {
918 let func_index = match &builder.func.name {
919 ir::UserFuncName::User(user) => FuncIndex::from_u32(user.index),
920 _ => {
921 panic!("function name not a UserFuncName::User as expected")
922 }
923 };
924 self.translation
925 .debuginfo
926 .name_section
927 .func_names
928 .get(&func_index)
929 .copied()
930 }
931
932 /// Create an `ir::Global` that does `load(ptr + offset)`.
global_load( &mut self, func: &mut ir::Function, ptr: ir::GlobalValue, offset: u32, flags: ir::MemFlags, ) -> ir::GlobalValue933 pub(crate) fn global_load(
934 &mut self,
935 func: &mut ir::Function,
936 ptr: ir::GlobalValue,
937 offset: u32,
938 flags: ir::MemFlags,
939 ) -> ir::GlobalValue {
940 func.create_global_value(ir::GlobalValueData::Load {
941 base: ptr,
942 offset: Offset32::new(i32::try_from(offset).unwrap()),
943 global_type: self.pointer_type(),
944 flags,
945 })
946 }
947
948 /// Like `global_load` but specialized for loads out of the
949 /// `vmctx`.
global_load_from_vmctx( &mut self, func: &mut ir::Function, offset: u32, flags: ir::MemFlags, ) -> ir::GlobalValue950 pub(crate) fn global_load_from_vmctx(
951 &mut self,
952 func: &mut ir::Function,
953 offset: u32,
954 flags: ir::MemFlags,
955 ) -> ir::GlobalValue {
956 let vmctx = self.vmctx(func);
957 self.global_load(func, vmctx, offset, flags)
958 }
959
960 /// Helper used when `!self.clif_instruction_traps_enabled()` is enabled to
961 /// test whether the divisor is zero.
guard_zero_divisor(&mut self, builder: &mut FunctionBuilder, rhs: ir::Value)962 fn guard_zero_divisor(&mut self, builder: &mut FunctionBuilder, rhs: ir::Value) {
963 if self.clif_instruction_traps_enabled() {
964 return;
965 }
966 self.trapz(builder, rhs, ir::TrapCode::INTEGER_DIVISION_BY_ZERO);
967 }
968
969 /// Helper used when `!self.clif_instruction_traps_enabled()` is enabled to
970 /// test whether a signed division operation will raise a trap.
guard_signed_divide( &mut self, builder: &mut FunctionBuilder, lhs: ir::Value, rhs: ir::Value, )971 fn guard_signed_divide(
972 &mut self,
973 builder: &mut FunctionBuilder,
974 lhs: ir::Value,
975 rhs: ir::Value,
976 ) {
977 if self.clif_instruction_traps_enabled() {
978 return;
979 }
980 self.trapz(builder, rhs, ir::TrapCode::INTEGER_DIVISION_BY_ZERO);
981
982 let ty = builder.func.dfg.value_type(rhs);
983 let minus_one = builder.ins().iconst(ty, -1);
984 let rhs_is_minus_one = builder.ins().icmp(IntCC::Equal, rhs, minus_one);
985 let int_min = builder.ins().iconst(
986 ty,
987 match ty {
988 I32 => i64::from(i32::MIN),
989 I64 => i64::MIN,
990 _ => unreachable!(),
991 },
992 );
993 let lhs_is_int_min = builder.ins().icmp(IntCC::Equal, lhs, int_min);
994 let is_integer_overflow = builder.ins().band(rhs_is_minus_one, lhs_is_int_min);
995 self.conditionally_trap(builder, is_integer_overflow, ir::TrapCode::INTEGER_OVERFLOW);
996 }
997
998 /// Helper used when `!self.clif_instruction_traps_enabled()` is enabled to
999 /// guard the traps from float-to-int conversions.
guard_fcvt_to_int( &mut self, builder: &mut FunctionBuilder, ty: ir::Type, val: ir::Value, signed: bool, )1000 fn guard_fcvt_to_int(
1001 &mut self,
1002 builder: &mut FunctionBuilder,
1003 ty: ir::Type,
1004 val: ir::Value,
1005 signed: bool,
1006 ) {
1007 assert!(!self.clif_instruction_traps_enabled());
1008 let val_ty = builder.func.dfg.value_type(val);
1009 let val = if val_ty == F64 {
1010 val
1011 } else {
1012 builder.ins().fpromote(F64, val)
1013 };
1014 let isnan = builder.ins().fcmp(FloatCC::NotEqual, val, val);
1015 self.trapnz(builder, isnan, ir::TrapCode::BAD_CONVERSION_TO_INTEGER);
1016 let val = self.trunc_f64(builder, val);
1017 let (lower_bound, upper_bound) = f64_cvt_to_int_bounds(signed, ty.bits());
1018 let lower_bound = builder.ins().f64const(lower_bound);
1019 let too_small = builder
1020 .ins()
1021 .fcmp(FloatCC::LessThanOrEqual, val, lower_bound);
1022 self.trapnz(builder, too_small, ir::TrapCode::INTEGER_OVERFLOW);
1023 let upper_bound = builder.ins().f64const(upper_bound);
1024 let too_large = builder
1025 .ins()
1026 .fcmp(FloatCC::GreaterThanOrEqual, val, upper_bound);
1027 self.trapnz(builder, too_large, ir::TrapCode::INTEGER_OVERFLOW);
1028 }
1029
1030 /// Get the `ir::Type` for a `VMSharedTypeIndex`.
vmshared_type_index_ty(&self) -> Type1031 pub(crate) fn vmshared_type_index_ty(&self) -> Type {
1032 Type::int_with_byte_size(self.offsets.size_of_vmshared_type_index().into()).unwrap()
1033 }
1034
1035 /// Given a `ModuleInternedTypeIndex`, emit code to get the corresponding
1036 /// `VMSharedTypeIndex` at runtime.
module_interned_to_shared_ty( &mut self, pos: &mut FuncCursor, interned_ty: ModuleInternedTypeIndex, ) -> ir::Value1037 pub(crate) fn module_interned_to_shared_ty(
1038 &mut self,
1039 pos: &mut FuncCursor,
1040 interned_ty: ModuleInternedTypeIndex,
1041 ) -> ir::Value {
1042 let vmctx = self.vmctx_val(pos);
1043 let pointer_type = self.pointer_type();
1044 let mem_flags = ir::MemFlags::trusted().with_readonly().with_can_move();
1045
1046 // Load the base pointer of the array of `VMSharedTypeIndex`es.
1047 let shared_indices = pos.ins().load(
1048 pointer_type,
1049 mem_flags,
1050 vmctx,
1051 i32::from(self.offsets.ptr.vmctx_type_ids_array()),
1052 );
1053
1054 // Calculate the offset in that array for this type's entry.
1055 let ty = self.vmshared_type_index_ty();
1056 let offset = i32::try_from(interned_ty.as_u32().checked_mul(ty.bytes()).unwrap()).unwrap();
1057
1058 // Load the`VMSharedTypeIndex` that this `ModuleInternedTypeIndex` is
1059 // associated with at runtime from the array.
1060 pos.ins().load(ty, mem_flags, shared_indices, offset)
1061 }
1062
1063 /// Load the associated `VMSharedTypeIndex` from inside a `*const VMFuncRef`.
1064 ///
1065 /// Does not check for null; just assumes that the `funcref` is a valid
1066 /// pointer.
load_funcref_type_index( &mut self, pos: &mut FuncCursor, mem_flags: ir::MemFlags, funcref: ir::Value, ) -> ir::Value1067 pub(crate) fn load_funcref_type_index(
1068 &mut self,
1069 pos: &mut FuncCursor,
1070 mem_flags: ir::MemFlags,
1071 funcref: ir::Value,
1072 ) -> ir::Value {
1073 let ty = self.vmshared_type_index_ty();
1074 pos.ins().load(
1075 ty,
1076 mem_flags,
1077 funcref,
1078 i32::from(self.offsets.ptr.vm_func_ref_type_index()),
1079 )
1080 }
1081
1082 /// Does this function need a GC heap?
needs_gc_heap(&self) -> bool1083 pub fn needs_gc_heap(&self) -> bool {
1084 self.needs_gc_heap
1085 }
1086
1087 /// Get the number of Wasm parameters for the given function.
num_params_for_func(&self, function_index: FuncIndex) -> usize1088 pub(crate) fn num_params_for_func(&self, function_index: FuncIndex) -> usize {
1089 let ty = self.module.functions[function_index]
1090 .signature
1091 .unwrap_module_type_index();
1092 self.types[ty].unwrap_func().params().len()
1093 }
1094
1095 /// Get the number of Wasm parameters for the given function type.
1096 ///
1097 /// Panics on non-function types.
num_params_for_function_type(&self, type_index: TypeIndex) -> usize1098 pub(crate) fn num_params_for_function_type(&self, type_index: TypeIndex) -> usize {
1099 let ty = self.module.types[type_index].unwrap_module_type_index();
1100 self.types[ty].unwrap_func().params().len()
1101 }
1102
1103 /// Initialize the state slot with an empty layout.
create_state_slot(&mut self, builder: &mut FunctionBuilder)1104 pub(crate) fn create_state_slot(&mut self, builder: &mut FunctionBuilder) {
1105 if self.tunables.debug_guest {
1106 let frame_builder = FrameStateSlotBuilder::new(self.key, self.pointer_type().bytes());
1107
1108 // Initially zero-size and with no descriptor; we will fill in
1109 // this info once we're done with the function body.
1110 let slot = builder
1111 .func
1112 .create_sized_stack_slot(ir::StackSlotData::new_with_key(
1113 ir::StackSlotKind::ExplicitSlot,
1114 0,
1115 0,
1116 ir::StackSlotKey::new(self.key.into_raw_u64()),
1117 ));
1118
1119 self.state_slot = Some((slot, frame_builder));
1120 }
1121 }
1122
memflags_for_debug_slot_value_wasm_ty(&self, ty: WasmValType) -> MemFlags1123 fn memflags_for_debug_slot_value_wasm_ty(&self, ty: WasmValType) -> MemFlags {
1124 // Store vectors in little-endian format: this is
1125 // universally supported, while native or
1126 // big-endian formats may not be in all cases
1127 // (e.g. Pulley on s390x).
1128 let mut flags = MemFlags::trusted();
1129 if ty == WasmValType::V128 {
1130 flags.set_endianness(Endianness::Little);
1131 }
1132 flags
1133 }
1134
memflags_for_debug_slot_value_clif_ty(&self, ty: ir::Type) -> MemFlags1135 fn memflags_for_debug_slot_value_clif_ty(&self, ty: ir::Type) -> MemFlags {
1136 let mut flags = MemFlags::trusted();
1137 if ty.is_vector() {
1138 flags.set_endianness(Endianness::Little);
1139 }
1140 flags
1141 }
1142
1143 /// Update the state slot layout with a new layout given a local.
add_state_slot_local( &mut self, builder: &mut FunctionBuilder, ty: WasmValType, init: Option<ir::Value>, )1144 pub(crate) fn add_state_slot_local(
1145 &mut self,
1146 builder: &mut FunctionBuilder,
1147 ty: WasmValType,
1148 init: Option<ir::Value>,
1149 ) {
1150 if let Some((slot, b)) = &mut self.state_slot {
1151 let offset = b.add_local(FrameValType::from(ty));
1152 if let Some(init) = init {
1153 let slot = *slot;
1154 let address = builder
1155 .ins()
1156 .stack_addr(self.pointer_type(), slot, offset.offset());
1157 builder.ins().store(
1158 self.memflags_for_debug_slot_value_wasm_ty(ty),
1159 init,
1160 address,
1161 0,
1162 );
1163 }
1164 }
1165 }
1166
update_state_slot_stack( &mut self, validator: &FuncValidator<impl WasmModuleResources>, builder: &mut FunctionBuilder, ) -> WasmResult<()>1167 fn update_state_slot_stack(
1168 &mut self,
1169 validator: &FuncValidator<impl WasmModuleResources>,
1170 builder: &mut FunctionBuilder,
1171 ) -> WasmResult<()> {
1172 // Take ownership of the state-slot builder temporarily rather
1173 // than mutably borrowing so we can invoke a method below.
1174 if let Some((slot, mut b)) = self.state_slot.take() {
1175 // If the stack-shape stack is shorter than the value
1176 // stack, that means that values were popped and then new
1177 // values were pushed; hence, these operand-stack values
1178 // are "dirty" and need to be flushed to the stackslot.
1179 //
1180 // N.B.: note that we don't re-sync GC-rooted values, and
1181 // we don't root the instrumentation slots
1182 // explicitly. This is safe as long as we don't have a
1183 // moving GC, because the value that we're observing in
1184 // the main program dataflow is already rooted in the main
1185 // program (we are only storing an extra copy of it). But
1186 // if/when we do build a moving GC, we will need to handle
1187 // this, probably by invalidating the "freshness" of all
1188 // ref-typed values after a safepoint and re-writing them
1189 // to the instrumentation slot; or alternately, extending
1190 // the debug instrumentation mechanism to be able to
1191 // directly refer to the user stack-slot.
1192 for i in self.stacks.stack_shape.len()..self.stacks.stack.len() {
1193 let parent_shape = i
1194 .checked_sub(1)
1195 .map(|parent_idx| self.stacks.stack_shape[parent_idx]);
1196 if let Some(this_ty) = validator
1197 .get_operand_type(self.stacks.stack.len() - i - 1)
1198 .expect("Index should not be out of range")
1199 {
1200 let wasm_ty = self.convert_valtype(this_ty)?;
1201 let (this_shape, offset) =
1202 b.push_stack(parent_shape, FrameValType::from(wasm_ty));
1203 self.stacks.stack_shape.push(this_shape);
1204
1205 let value = self.stacks.stack[i];
1206 let address =
1207 builder
1208 .ins()
1209 .stack_addr(self.pointer_type(), slot, offset.offset());
1210 builder.ins().store(
1211 self.memflags_for_debug_slot_value_wasm_ty(wasm_ty),
1212 value,
1213 address,
1214 0,
1215 );
1216 } else {
1217 // Unreachable code with unknown type -- no
1218 // flushes for this or later-pushed values.
1219 break;
1220 }
1221 }
1222
1223 self.state_slot = Some((slot, b));
1224 }
1225
1226 Ok(())
1227 }
1228
debug_tags(&self, srcloc: ir::SourceLoc) -> Vec<ir::DebugTag>1229 pub(crate) fn debug_tags(&self, srcloc: ir::SourceLoc) -> Vec<ir::DebugTag> {
1230 if let Some((slot, _b)) = &self.state_slot {
1231 self.stacks.assert_debug_stack_is_synced();
1232 let stack_shape = self
1233 .stacks
1234 .stack_shape
1235 .last()
1236 .map(|s| s.raw())
1237 .unwrap_or(u32::MAX);
1238 // Convert component-relative srcloc to module-relative
1239 // Wasm PC for the frame table. The srcloc on the builder
1240 // remains component-relative for native DWARF and other
1241 // purposes, but the frame table must be module-relative
1242 // because the guest-debug API presents a purely core-Wasm
1243 // view of the world where components are deconstructed
1244 // into core Wasm modules.
1245 let component_pc = ComponentPC::new(srcloc.bits());
1246 let module_pc = component_pc.to_module_pc(self.wasm_module_offset);
1247 vec![
1248 ir::DebugTag::StackSlot(*slot),
1249 ir::DebugTag::User(module_pc.raw()),
1250 ir::DebugTag::User(stack_shape),
1251 ]
1252 } else {
1253 vec![]
1254 }
1255 }
1256
finish_debug_metadata(&self, builder: &mut FunctionBuilder)1257 fn finish_debug_metadata(&self, builder: &mut FunctionBuilder) {
1258 if let Some((slot, b)) = &self.state_slot {
1259 builder.func.sized_stack_slots[*slot].size = b.size();
1260 }
1261 }
1262
1263 /// Store a new value for a local in the state slot, if present.
state_slot_local_set( &self, builder: &mut FunctionBuilder, local: u32, value: ir::Value, )1264 pub(crate) fn state_slot_local_set(
1265 &self,
1266 builder: &mut FunctionBuilder,
1267 local: u32,
1268 value: ir::Value,
1269 ) {
1270 if let Some((slot, b)) = &self.state_slot {
1271 let offset = b.local_offset(local);
1272 let address = builder
1273 .ins()
1274 .stack_addr(self.pointer_type(), *slot, offset.offset());
1275 let ty = builder.func.dfg.value_type(value);
1276 builder.ins().store(
1277 self.memflags_for_debug_slot_value_clif_ty(ty),
1278 value,
1279 address,
1280 0,
1281 );
1282 }
1283 }
1284
update_state_slot_vmctx(&mut self, builder: &mut FunctionBuilder)1285 fn update_state_slot_vmctx(&mut self, builder: &mut FunctionBuilder) {
1286 if let &Some((slot, _)) = &self.state_slot {
1287 let vmctx = self.vmctx_val(&mut builder.cursor());
1288 // N.B.: we always store vmctx at offset 0 in the
1289 // slot. This is relied upon in
1290 // crates/wasmtime/src/runtime/debug.rs in
1291 // `raw_instance()`. See also the slot layout computation in crates/environ/src/
1292 //
1293 // This is a native-endian store (the only mode for
1294 // `stack_store`) because it is read by host code directly
1295 // as a pointer.
1296 builder.ins().stack_store(vmctx, slot, 0);
1297 }
1298 }
1299
val_ty_needs_stack_map(&self, ty: WasmValType) -> bool1300 pub(crate) fn val_ty_needs_stack_map(&self, ty: WasmValType) -> bool {
1301 match ty {
1302 WasmValType::Ref(r) => self.heap_ty_needs_stack_map(r.heap_type),
1303 _ => false,
1304 }
1305 }
1306
heap_ty_needs_stack_map(&self, ty: WasmHeapType) -> bool1307 pub(crate) fn heap_ty_needs_stack_map(&self, ty: WasmHeapType) -> bool {
1308 ty.is_vmgcref_type_and_not_i31() && !ty.is_bottom()
1309 }
1310 }
1311
1312 impl TranslateTrap for FuncEnvironment<'_> {
compiler(&self) -> &Compiler1313 fn compiler(&self) -> &Compiler {
1314 &self.compiler
1315 }
1316
vmctx_val(&mut self, pos: &mut FuncCursor<'_>) -> ir::Value1317 fn vmctx_val(&mut self, pos: &mut FuncCursor<'_>) -> ir::Value {
1318 let pointer_type = self.pointer_type();
1319 let vmctx = self.vmctx(&mut pos.func);
1320 pos.ins().global_value(pointer_type, vmctx)
1321 }
1322
builtin_funcref( &mut self, builder: &mut FunctionBuilder<'_>, index: BuiltinFunctionIndex, ) -> ir::FuncRef1323 fn builtin_funcref(
1324 &mut self,
1325 builder: &mut FunctionBuilder<'_>,
1326 index: BuiltinFunctionIndex,
1327 ) -> ir::FuncRef {
1328 self.builtin_functions.load_builtin(builder.func, index)
1329 }
1330
debug_tags(&self, srcloc: ir::SourceLoc) -> Vec<ir::DebugTag>1331 fn debug_tags(&self, srcloc: ir::SourceLoc) -> Vec<ir::DebugTag> {
1332 FuncEnvironment::debug_tags(self, srcloc)
1333 }
1334 }
1335
1336 #[derive(Default)]
1337 pub(crate) struct WasmEntities {
1338 /// Map from a Wasm global index from this module to its implementation in
1339 /// the Cranelift function we are building.
1340 pub(crate) globals: SecondaryMap<GlobalIndex, Option<GlobalVariable>>,
1341
1342 /// Map from a Wasm memory index to its `Heap` implementation in the
1343 /// Cranelift function we are building.
1344 pub(crate) memories: SecondaryMap<MemoryIndex, PackedOption<Heap>>,
1345
1346 /// Map from an (interned) Wasm type index from this module to its
1347 /// `ir::SigRef` in the Cranelift function we are building.
1348 pub(crate) sig_refs: SecondaryMap<ModuleInternedTypeIndex, PackedOption<ir::SigRef>>,
1349
1350 /// Map from a defined Wasm function index to its associated function
1351 /// reference in the Cranelift function we are building.
1352 pub(crate) defined_func_refs: SecondaryMap<DefinedFuncIndex, PackedOption<ir::FuncRef>>,
1353
1354 /// Map from an imported Wasm function index for which we statically know
1355 /// which function will always be used to satisfy that import to its
1356 /// associated function reference in the Cranelift function we are building.
1357 pub(crate) imported_func_refs: SecondaryMap<FuncIndex, PackedOption<ir::FuncRef>>,
1358
1359 /// Map from a Wasm table index to its associated implementation in the
1360 /// Cranelift function we are building.
1361 pub(crate) tables: SecondaryMap<TableIndex, Option<TableData>>,
1362 }
1363
1364 macro_rules! define_get_or_create_methods {
1365 ( $( $name:ident ( $map:ident ) : $create:ident : $key:ty => $val:ty ; )* ) => {
1366 $(
1367 pub(crate) fn $name(&mut self, func: &mut ir::Function, key: $key) -> $val {
1368 match self.entities.$map[key].clone().into() {
1369 Some(val) => val,
1370 None => {
1371 let val = self.$create(func, key);
1372 self.entities.$map[key] = Some(val.clone()).into();
1373 val
1374 }
1375 }
1376 }
1377 )*
1378 };
1379 }
1380
1381 impl FuncEnvironment<'_> {
1382 define_get_or_create_methods! {
1383 get_or_create_global(globals) : make_global : GlobalIndex => GlobalVariable;
1384 get_or_create_heap(memories) : make_heap : MemoryIndex => Heap;
1385 get_or_create_interned_sig_ref(sig_refs) : make_sig_ref : ModuleInternedTypeIndex => ir::SigRef;
1386 get_or_create_defined_func_ref(defined_func_refs) : make_defined_func_ref : DefinedFuncIndex => ir::FuncRef;
1387 get_or_create_imported_func_ref(imported_func_refs) : make_imported_func_ref : FuncIndex => ir::FuncRef;
1388 get_or_create_table(tables) : make_table : TableIndex => TableData;
1389 }
1390
make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable1391 fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
1392 let ty = self.module.globals[index].wasm_ty;
1393
1394 if ty.is_vmgcref_type() {
1395 // Although reference-typed globals live at the same memory location as
1396 // any other type of global at the same index would, getting or
1397 // setting them requires ref counting barriers. Therefore, we need
1398 // to use `GlobalVariable::Custom`, as that is the only kind of
1399 // `GlobalVariable` for which translation supports custom
1400 // access translation.
1401 return GlobalVariable::Custom;
1402 }
1403
1404 if !self.module.globals[index].mutability {
1405 if let Some(index) = self.module.defined_global_index(index) {
1406 let init = &self.module.global_initializers[index];
1407 if let Some(value) = init.const_eval() {
1408 return GlobalVariable::Constant { value };
1409 }
1410 }
1411 }
1412
1413 let (gv, offset) = self.get_global_location(func, index);
1414 GlobalVariable::Memory {
1415 gv,
1416 offset: offset.into(),
1417 ty: super::value_type(self.isa, ty),
1418 }
1419 }
1420
get_or_create_sig_ref( &mut self, func: &mut ir::Function, ty: TypeIndex, ) -> ir::SigRef1421 pub(crate) fn get_or_create_sig_ref(
1422 &mut self,
1423 func: &mut ir::Function,
1424 ty: TypeIndex,
1425 ) -> ir::SigRef {
1426 let ty = self.module.types[ty].unwrap_module_type_index();
1427 self.get_or_create_interned_sig_ref(func, ty)
1428 }
1429
make_sig_ref( &mut self, func: &mut ir::Function, index: ModuleInternedTypeIndex, ) -> ir::SigRef1430 fn make_sig_ref(
1431 &mut self,
1432 func: &mut ir::Function,
1433 index: ModuleInternedTypeIndex,
1434 ) -> ir::SigRef {
1435 let wasm_func_ty = self.types[index].unwrap_func();
1436 let sig = crate::wasm_call_signature(self.isa, wasm_func_ty, &self.tunables);
1437 let sig_ref = func.import_signature(sig);
1438 self.sig_ref_to_ty[sig_ref] = Some(wasm_func_ty);
1439 sig_ref
1440 }
1441
make_defined_func_ref( &mut self, func: &mut ir::Function, def_func_index: DefinedFuncIndex, ) -> ir::FuncRef1442 fn make_defined_func_ref(
1443 &mut self,
1444 func: &mut ir::Function,
1445 def_func_index: DefinedFuncIndex,
1446 ) -> ir::FuncRef {
1447 let func_index = self.module.func_index(def_func_index);
1448
1449 let ty = self.module.functions[func_index]
1450 .signature
1451 .unwrap_module_type_index();
1452 let signature = self.get_or_create_interned_sig_ref(func, ty);
1453
1454 let key = FuncKey::DefinedWasmFunction(self.translation.module_index(), def_func_index);
1455 let (namespace, index) = key.into_raw_parts();
1456 let name = ir::ExternalName::User(
1457 func.declare_imported_user_function(ir::UserExternalName { namespace, index }),
1458 );
1459
1460 func.import_function(ir::ExtFuncData {
1461 name,
1462 signature,
1463 colocated: true,
1464 patchable: false,
1465 })
1466 }
1467
make_imported_func_ref( &mut self, func: &mut ir::Function, func_index: FuncIndex, ) -> ir::FuncRef1468 fn make_imported_func_ref(
1469 &mut self,
1470 func: &mut ir::Function,
1471 func_index: FuncIndex,
1472 ) -> ir::FuncRef {
1473 assert!(self.module.is_imported_function(func_index));
1474 assert!(self.translation.known_imported_functions[func_index].is_some());
1475
1476 let ty = self.module.functions[func_index]
1477 .signature
1478 .unwrap_module_type_index();
1479 let signature = self.get_or_create_interned_sig_ref(func, ty);
1480
1481 let key = match self.translation.known_imported_functions[func_index] {
1482 Some(key @ FuncKey::DefinedWasmFunction(..)) => key,
1483
1484 #[cfg(feature = "component-model")]
1485 Some(key @ FuncKey::UnsafeIntrinsic(..)) => key,
1486
1487 Some(key) => {
1488 panic!("unexpected kind of known-import function: {key:?}")
1489 }
1490
1491 None => panic!(
1492 "cannot make an `ir::FuncRef` for a function import that is not statically known"
1493 ),
1494 };
1495
1496 let (namespace, index) = key.into_raw_parts();
1497 let name = ir::ExternalName::User(
1498 func.declare_imported_user_function(ir::UserExternalName { namespace, index }),
1499 );
1500
1501 func.import_function(ir::ExtFuncData {
1502 name,
1503 signature,
1504 colocated: true,
1505 patchable: false,
1506 })
1507 }
1508
make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> Heap1509 fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> Heap {
1510 let pointer_type = self.pointer_type();
1511 let memory = self.module.memories[index];
1512 let is_shared = memory.shared;
1513
1514 let (base_ptr, base_offset, current_length_offset) = {
1515 let vmctx = self.vmctx(func);
1516 if let Some(def_index) = self.module.defined_memory_index(index) {
1517 if is_shared {
1518 // As with imported memory, the `VMMemoryDefinition` for a
1519 // shared memory is stored elsewhere. We store a `*mut
1520 // VMMemoryDefinition` to it and dereference that when
1521 // atomically growing it.
1522 let from_offset = self.offsets.vmctx_vmmemory_pointer(def_index);
1523 let memory = self.global_load_from_vmctx(
1524 func,
1525 from_offset,
1526 ir::MemFlags::trusted().with_readonly().with_can_move(),
1527 );
1528 let base_offset = i32::from(self.offsets.ptr.vmmemory_definition_base());
1529 let current_length_offset =
1530 i32::from(self.offsets.ptr.vmmemory_definition_current_length());
1531 (memory, base_offset, current_length_offset)
1532 } else {
1533 let owned_index = self.module.owned_memory_index(def_index);
1534 let owned_base_offset =
1535 self.offsets.vmctx_vmmemory_definition_base(owned_index);
1536 let owned_length_offset = self
1537 .offsets
1538 .vmctx_vmmemory_definition_current_length(owned_index);
1539 let current_base_offset = i32::try_from(owned_base_offset).unwrap();
1540 let current_length_offset = i32::try_from(owned_length_offset).unwrap();
1541 (vmctx, current_base_offset, current_length_offset)
1542 }
1543 } else {
1544 let from_offset = self.offsets.vmctx_vmmemory_import_from(index);
1545 let memory = self.global_load_from_vmctx(
1546 func,
1547 from_offset,
1548 ir::MemFlags::trusted().with_readonly().with_can_move(),
1549 );
1550 let base_offset = i32::from(self.offsets.ptr.vmmemory_definition_base());
1551 let current_length_offset =
1552 i32::from(self.offsets.ptr.vmmemory_definition_current_length());
1553 (memory, base_offset, current_length_offset)
1554 }
1555 };
1556
1557 let bound = func.create_global_value(ir::GlobalValueData::Load {
1558 base: base_ptr,
1559 offset: Offset32::new(current_length_offset),
1560 global_type: pointer_type,
1561 flags: MemFlags::trusted(),
1562 });
1563
1564 let base = self.make_heap_base(func, memory, base_ptr, base_offset);
1565
1566 self.heaps.push(HeapData {
1567 base,
1568 bound,
1569 memory,
1570 })
1571 }
1572
make_heap_base( &self, func: &mut Function, memory: Memory, ptr: ir::GlobalValue, offset: i32, ) -> ir::GlobalValue1573 pub(crate) fn make_heap_base(
1574 &self,
1575 func: &mut Function,
1576 memory: Memory,
1577 ptr: ir::GlobalValue,
1578 offset: i32,
1579 ) -> ir::GlobalValue {
1580 let pointer_type = self.pointer_type();
1581
1582 let mut flags = ir::MemFlags::trusted().with_can_move();
1583 if !memory.memory_may_move(self.tunables) {
1584 flags.set_readonly();
1585 }
1586
1587 let heap_base = func.create_global_value(ir::GlobalValueData::Load {
1588 base: ptr,
1589 offset: Offset32::new(offset),
1590 global_type: pointer_type,
1591 flags,
1592 });
1593 heap_base
1594 }
1595
make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> TableData1596 fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> TableData {
1597 let pointer_type = self.pointer_type();
1598
1599 let (ptr, base_offset, current_elements_offset) = {
1600 let vmctx = self.vmctx(func);
1601 if let Some(def_index) = self.module.defined_table_index(index) {
1602 let base_offset =
1603 i32::try_from(self.offsets.vmctx_vmtable_definition_base(def_index)).unwrap();
1604 let current_elements_offset = i32::try_from(
1605 self.offsets
1606 .vmctx_vmtable_definition_current_elements(def_index),
1607 )
1608 .unwrap();
1609 (vmctx, base_offset, current_elements_offset)
1610 } else {
1611 let from_offset = self.offsets.vmctx_vmtable_from(index);
1612 let table = func.create_global_value(ir::GlobalValueData::Load {
1613 base: vmctx,
1614 offset: Offset32::new(i32::try_from(from_offset).unwrap()),
1615 global_type: pointer_type,
1616 flags: MemFlags::trusted().with_readonly().with_can_move(),
1617 });
1618 let base_offset = i32::from(self.offsets.vmtable_definition_base());
1619 let current_elements_offset =
1620 i32::from(self.offsets.vmtable_definition_current_elements());
1621 (table, base_offset, current_elements_offset)
1622 }
1623 };
1624
1625 let table = &self.module.tables[index];
1626 let element_size = if table.ref_type.is_vmgcref_type() {
1627 // For GC-managed references, tables store `Option<VMGcRef>`s.
1628 ir::types::I32.bytes()
1629 } else {
1630 self.reference_type(table.ref_type.heap_type).0.bytes()
1631 };
1632
1633 let base_gv = func.create_global_value(ir::GlobalValueData::Load {
1634 base: ptr,
1635 offset: Offset32::new(base_offset),
1636 global_type: pointer_type,
1637 flags: if Some(table.limits.min) == table.limits.max {
1638 // A fixed-size table can't be resized so its base address won't
1639 // change.
1640 MemFlags::trusted().with_readonly().with_can_move()
1641 } else {
1642 MemFlags::trusted()
1643 },
1644 });
1645
1646 let bound = if Some(table.limits.min) == table.limits.max {
1647 TableSize::Static {
1648 bound: table.limits.min,
1649 }
1650 } else {
1651 TableSize::Dynamic {
1652 bound_gv: func.create_global_value(ir::GlobalValueData::Load {
1653 base: ptr,
1654 offset: Offset32::new(current_elements_offset),
1655 global_type: ir::Type::int(
1656 u16::from(self.offsets.size_of_vmtable_definition_current_elements()) * 8,
1657 )
1658 .unwrap(),
1659 flags: MemFlags::trusted(),
1660 }),
1661 }
1662 };
1663
1664 TableData {
1665 base_gv,
1666 bound,
1667 element_size,
1668 }
1669 }
1670
1671 /// Get the type index associated with an exception object.
1672 #[cfg(feature = "gc")]
exception_type_from_tag(&self, tag: TagIndex) -> EngineOrModuleTypeIndex1673 pub(crate) fn exception_type_from_tag(&self, tag: TagIndex) -> EngineOrModuleTypeIndex {
1674 self.module.tags[tag].exception
1675 }
1676
1677 /// Get the parameter arity of the associated function type for the given tag.
tag_param_arity(&self, tag: TagIndex) -> usize1678 pub(crate) fn tag_param_arity(&self, tag: TagIndex) -> usize {
1679 let func_ty = self.module.tags[tag].signature.unwrap_module_type_index();
1680 let func_ty = self
1681 .types
1682 .unwrap_func(func_ty)
1683 .expect("already validated to refer to a function type");
1684 func_ty.params().len()
1685 }
1686
1687 /// Get the runtime instance ID and defined-tag ID in that
1688 /// instance for a particular static tag ID.
1689 #[cfg(feature = "gc")]
get_instance_and_tag( &mut self, builder: &mut FunctionBuilder<'_>, tag_index: TagIndex, ) -> (ir::Value, ir::Value)1690 pub(crate) fn get_instance_and_tag(
1691 &mut self,
1692 builder: &mut FunctionBuilder<'_>,
1693 tag_index: TagIndex,
1694 ) -> (ir::Value, ir::Value) {
1695 if let Some(defined_tag_index) = self.module.defined_tag_index(tag_index) {
1696 // Our own tag -- we only need to get our instance ID.
1697 let builtin = self.builtin_functions.get_instance_id(builder.func);
1698 let vmctx = self.vmctx_val(&mut builder.cursor());
1699 let call = builder.ins().call(builtin, &[vmctx]);
1700 let instance_id = builder.func.dfg.inst_results(call)[0];
1701 let tag_id = builder
1702 .ins()
1703 .iconst(I32, i64::from(defined_tag_index.as_u32()));
1704 (instance_id, tag_id)
1705 } else {
1706 // An imported tag -- we need to load the VMTagImport struct.
1707 let vmctx_tag_vmctx_offset = self.offsets.vmctx_vmtag_import_vmctx(tag_index);
1708 let vmctx_tag_index_offset = self.offsets.vmctx_vmtag_import_index(tag_index);
1709 let vmctx = self.vmctx_val(&mut builder.cursor());
1710 let pointer_type = self.pointer_type();
1711 let from_vmctx = builder.ins().load(
1712 pointer_type,
1713 MemFlags::trusted().with_readonly(),
1714 vmctx,
1715 i32::try_from(vmctx_tag_vmctx_offset).unwrap(),
1716 );
1717 let index = builder.ins().load(
1718 I32,
1719 MemFlags::trusted().with_readonly(),
1720 vmctx,
1721 i32::try_from(vmctx_tag_index_offset).unwrap(),
1722 );
1723 let builtin = self.builtin_functions.get_instance_id(builder.func);
1724 let call = builder.ins().call(builtin, &[from_vmctx]);
1725 let from_instance_id = builder.func.dfg.inst_results(call)[0];
1726 (from_instance_id, index)
1727 }
1728 }
1729 }
1730
1731 struct Call<'a, 'func, 'module_env> {
1732 builder: &'a mut FunctionBuilder<'func>,
1733 env: &'a mut FuncEnvironment<'module_env>,
1734 srcloc: ir::SourceLoc,
1735 tail: bool,
1736 }
1737
1738 enum CheckIndirectCallTypeSignature {
1739 Runtime,
1740 StaticMatch {
1741 /// Whether or not the funcref may be null or if it's statically known
1742 /// to not be null.
1743 may_be_null: bool,
1744 },
1745 StaticTrap,
1746 }
1747
1748 type CallRets = SmallVec<[ir::Value; 4]>;
1749
1750 impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
1751 /// Create a new `Call` site that will do regular, non-tail calls.
new( builder: &'a mut FunctionBuilder<'func>, env: &'a mut FuncEnvironment<'module_env>, srcloc: ir::SourceLoc, ) -> Self1752 pub fn new(
1753 builder: &'a mut FunctionBuilder<'func>,
1754 env: &'a mut FuncEnvironment<'module_env>,
1755 srcloc: ir::SourceLoc,
1756 ) -> Self {
1757 Call {
1758 builder,
1759 env,
1760 srcloc,
1761 tail: false,
1762 }
1763 }
1764
1765 /// Create a new `Call` site that will perform tail calls.
new_tail( builder: &'a mut FunctionBuilder<'func>, env: &'a mut FuncEnvironment<'module_env>, srcloc: ir::SourceLoc, ) -> Self1766 pub fn new_tail(
1767 builder: &'a mut FunctionBuilder<'func>,
1768 env: &'a mut FuncEnvironment<'module_env>,
1769 srcloc: ir::SourceLoc,
1770 ) -> Self {
1771 Call {
1772 builder,
1773 env,
1774 srcloc,
1775 tail: true,
1776 }
1777 }
1778
1779 /// Do a Wasm-level direct call to the given callee function.
direct_call( mut self, callee_index: FuncIndex, sig_ref: ir::SigRef, wasm_call_args: &[ir::Value], ) -> WasmResult<CallRets>1780 pub fn direct_call(
1781 mut self,
1782 callee_index: FuncIndex,
1783 sig_ref: ir::SigRef,
1784 wasm_call_args: &[ir::Value],
1785 ) -> WasmResult<CallRets> {
1786 let mut real_call_args = Vec::with_capacity(wasm_call_args.len() + 2);
1787 let caller_vmctx = self
1788 .builder
1789 .func
1790 .special_param(ArgumentPurpose::VMContext)
1791 .unwrap();
1792
1793 // Handle direct calls to locally-defined functions.
1794 if let Some(def_func_index) = self.env.module.defined_func_index(callee_index) {
1795 // First append the callee vmctx address, which is the same as the caller vmctx in
1796 // this case.
1797 real_call_args.push(caller_vmctx);
1798
1799 // Then append the caller vmctx address.
1800 real_call_args.push(caller_vmctx);
1801
1802 // Then append the regular call arguments.
1803 real_call_args.extend_from_slice(wasm_call_args);
1804
1805 // Finally, make the direct call!
1806 let callee = self
1807 .env
1808 .get_or_create_defined_func_ref(self.builder.func, def_func_index);
1809 return Ok(self.direct_call_inst(callee, &real_call_args));
1810 }
1811
1812 // Handle direct calls to imported functions. We use an indirect call
1813 // so that we don't have to patch the code at runtime.
1814 let pointer_type = self.env.pointer_type();
1815 let vmctx = self.env.vmctx(self.builder.func);
1816 let base = self.builder.ins().global_value(pointer_type, vmctx);
1817
1818 let mem_flags = ir::MemFlags::trusted().with_readonly().with_can_move();
1819
1820 // Load the callee address.
1821 let body_offset = i32::try_from(
1822 self.env
1823 .offsets
1824 .vmctx_vmfunction_import_wasm_call(callee_index),
1825 )
1826 .unwrap();
1827
1828 // First append the callee vmctx address.
1829 let vmctx_offset =
1830 i32::try_from(self.env.offsets.vmctx_vmfunction_import_vmctx(callee_index)).unwrap();
1831 let callee_vmctx = self
1832 .builder
1833 .ins()
1834 .load(pointer_type, mem_flags, base, vmctx_offset);
1835 real_call_args.push(callee_vmctx);
1836 real_call_args.push(caller_vmctx);
1837
1838 // Then append the Wasm call arguments.
1839 real_call_args.extend_from_slice(wasm_call_args);
1840
1841 // If we statically know the imported function (e.g. this is a
1842 // component-to-component call where we statically know both components)
1843 // then we can avoid doing an indirect call.
1844 match self.env.translation.known_imported_functions[callee_index].as_ref() {
1845 // The import is always a compile-time builtin intrinsic. Make a
1846 // direct call to that function (presumably it will eventually be
1847 // inlined).
1848 #[cfg(feature = "component-model")]
1849 Some(FuncKey::UnsafeIntrinsic(..)) => {
1850 let callee = self
1851 .env
1852 .get_or_create_imported_func_ref(self.builder.func, callee_index);
1853 Ok(self.direct_call_inst(callee, &real_call_args))
1854 }
1855
1856 // The import is always satisfied with the given defined Wasm
1857 // function, so do a direct call to that function! (Although we take
1858 // care to still pass its `funcref`'s `vmctx` as the callee `vmctx`
1859 // in `real_call_args` and not the caller's.)
1860 Some(FuncKey::DefinedWasmFunction(..)) => {
1861 let callee = self
1862 .env
1863 .get_or_create_imported_func_ref(self.builder.func, callee_index);
1864 Ok(self.direct_call_inst(callee, &real_call_args))
1865 }
1866
1867 Some(key) => panic!("unexpected kind of known-import function: {key:?}"),
1868
1869 // Unknown import function or this module is instantiated many times
1870 // and with different functions. Either way, we have to do the
1871 // indirect call.
1872 None => {
1873 let func_addr = self
1874 .builder
1875 .ins()
1876 .load(pointer_type, mem_flags, base, body_offset);
1877 Ok(self.indirect_call_inst(sig_ref, func_addr, &real_call_args))
1878 }
1879 }
1880 }
1881
1882 /// Do a Wasm-level indirect call through the given funcref table.
indirect_call( mut self, features: &WasmFeatures, table_index: TableIndex, ty_index: TypeIndex, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], ) -> WasmResult<Option<CallRets>>1883 pub fn indirect_call(
1884 mut self,
1885 features: &WasmFeatures,
1886 table_index: TableIndex,
1887 ty_index: TypeIndex,
1888 sig_ref: ir::SigRef,
1889 callee: ir::Value,
1890 call_args: &[ir::Value],
1891 ) -> WasmResult<Option<CallRets>> {
1892 let (code_ptr, callee_vmctx) = match self.check_and_load_code_and_callee_vmctx(
1893 features,
1894 table_index,
1895 ty_index,
1896 callee,
1897 false,
1898 )? {
1899 Some(pair) => pair,
1900 None => return Ok(None),
1901 };
1902
1903 self.unchecked_call_impl(sig_ref, code_ptr, callee_vmctx, call_args)
1904 .map(Some)
1905 }
1906
check_and_load_code_and_callee_vmctx( &mut self, features: &WasmFeatures, table_index: TableIndex, ty_index: TypeIndex, callee: ir::Value, cold_blocks: bool, ) -> WasmResult<Option<(ir::Value, ir::Value)>>1907 fn check_and_load_code_and_callee_vmctx(
1908 &mut self,
1909 features: &WasmFeatures,
1910 table_index: TableIndex,
1911 ty_index: TypeIndex,
1912 callee: ir::Value,
1913 cold_blocks: bool,
1914 ) -> WasmResult<Option<(ir::Value, ir::Value)>> {
1915 // Get the funcref pointer from the table.
1916 let funcref_ptr = self.env.get_or_init_func_ref_table_elem(
1917 self.builder,
1918 table_index,
1919 callee,
1920 cold_blocks,
1921 );
1922
1923 // If necessary, check the signature.
1924 let check =
1925 self.check_indirect_call_type_signature(features, table_index, ty_index, funcref_ptr);
1926
1927 let trap_code = match check {
1928 // `funcref_ptr` is checked at runtime that its type matches,
1929 // meaning that if code gets this far it's guaranteed to not be
1930 // null. That means nothing in `unchecked_call` can fail.
1931 CheckIndirectCallTypeSignature::Runtime => None,
1932
1933 // No type check was performed on `funcref_ptr` because it's
1934 // statically known to have the right type. Note that whether or
1935 // not the function is null is not necessarily tested so far since
1936 // no type information was inspected.
1937 //
1938 // If the table may hold null functions, then further loads in
1939 // `unchecked_call` may fail. If the table only holds non-null
1940 // functions, though, then there's no possibility of a trap.
1941 CheckIndirectCallTypeSignature::StaticMatch { may_be_null } => {
1942 if may_be_null {
1943 Some(crate::TRAP_INDIRECT_CALL_TO_NULL)
1944 } else {
1945 None
1946 }
1947 }
1948
1949 // Code has already trapped, so return nothing indicating that this
1950 // is now unreachable code.
1951 CheckIndirectCallTypeSignature::StaticTrap => return Ok(None),
1952 };
1953
1954 Ok(Some(self.load_code_and_vmctx(funcref_ptr, trap_code)))
1955 }
1956
check_indirect_call_type_signature( &mut self, features: &WasmFeatures, table_index: TableIndex, ty_index: TypeIndex, funcref_ptr: ir::Value, ) -> CheckIndirectCallTypeSignature1957 fn check_indirect_call_type_signature(
1958 &mut self,
1959 features: &WasmFeatures,
1960 table_index: TableIndex,
1961 ty_index: TypeIndex,
1962 funcref_ptr: ir::Value,
1963 ) -> CheckIndirectCallTypeSignature {
1964 let table = &self.env.module.tables[table_index];
1965 let sig_id_size = self.env.offsets.size_of_vmshared_type_index();
1966 let sig_id_type = Type::int(u16::from(sig_id_size) * 8).unwrap();
1967
1968 // Test if a type check is necessary for this table. If this table is a
1969 // table of typed functions and that type matches `ty_index`, then
1970 // there's no need to perform a typecheck.
1971 match table.ref_type.heap_type {
1972 // Functions do not have a statically known type in the table, a
1973 // typecheck is required. Fall through to below to perform the
1974 // actual typecheck.
1975 WasmHeapType::Func => {}
1976
1977 // Functions that have a statically known type are either going to
1978 // always succeed or always fail. Figure out by inspecting the types
1979 // further.
1980 WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(table_ty)) => {
1981 // If `ty_index` matches `table_ty`, then this call is
1982 // statically known to have the right type, so no checks are
1983 // necessary.
1984 let specified_ty = self.env.module.types[ty_index].unwrap_module_type_index();
1985 if specified_ty == table_ty {
1986 return CheckIndirectCallTypeSignature::StaticMatch {
1987 may_be_null: table.ref_type.nullable,
1988 };
1989 }
1990
1991 if features.gc() {
1992 // If we are in the Wasm GC world, then we need to perform
1993 // an actual subtype check at runtime. Fall through to below
1994 // to do that.
1995 } else {
1996 // Otherwise if the types don't match then either (a) this
1997 // is a null pointer or (b) it's a pointer with the wrong
1998 // type. Figure out which and trap here.
1999 //
2000 // If it's possible to have a null here then try to load the
2001 // type information. If that fails due to the function being
2002 // a null pointer, then this was a call to null. Otherwise
2003 // if it succeeds then we know it won't match, so trap
2004 // anyway.
2005 if table.ref_type.nullable {
2006 if self.env.clif_memory_traps_enabled() {
2007 self.builder.ins().load(
2008 sig_id_type,
2009 ir::MemFlags::trusted()
2010 .with_readonly()
2011 .with_trap_code(Some(crate::TRAP_INDIRECT_CALL_TO_NULL)),
2012 funcref_ptr,
2013 i32::from(self.env.offsets.ptr.vm_func_ref_type_index()),
2014 );
2015 } else {
2016 self.env.trapz(
2017 self.builder,
2018 funcref_ptr,
2019 crate::TRAP_INDIRECT_CALL_TO_NULL,
2020 );
2021 }
2022 }
2023 self.env.trap(self.builder, crate::TRAP_BAD_SIGNATURE);
2024 return CheckIndirectCallTypeSignature::StaticTrap;
2025 }
2026 }
2027
2028 // Tables of `nofunc` can only be inhabited by null, so go ahead and
2029 // trap with that.
2030 WasmHeapType::NoFunc => {
2031 assert!(table.ref_type.nullable);
2032 self.env
2033 .trap(self.builder, crate::TRAP_INDIRECT_CALL_TO_NULL);
2034 return CheckIndirectCallTypeSignature::StaticTrap;
2035 }
2036
2037 // Engine-indexed types don't show up until runtime and it's a Wasm
2038 // validation error to perform a call through a non-function table,
2039 // so these cases are dynamically not reachable.
2040 WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(_))
2041 | WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::RecGroup(_))
2042 | WasmHeapType::Extern
2043 | WasmHeapType::NoExtern
2044 | WasmHeapType::Any
2045 | WasmHeapType::Eq
2046 | WasmHeapType::I31
2047 | WasmHeapType::Array
2048 | WasmHeapType::ConcreteArray(_)
2049 | WasmHeapType::Struct
2050 | WasmHeapType::ConcreteStruct(_)
2051 | WasmHeapType::Exn
2052 | WasmHeapType::ConcreteExn(_)
2053 | WasmHeapType::NoExn
2054 | WasmHeapType::Cont
2055 | WasmHeapType::ConcreteCont(_)
2056 | WasmHeapType::NoCont
2057 | WasmHeapType::None => {
2058 unreachable!()
2059 }
2060 }
2061
2062 // Load the caller's `VMSharedTypeIndex.
2063 let interned_ty = self.env.module.types[ty_index].unwrap_module_type_index();
2064 let caller_sig_id = self
2065 .env
2066 .module_interned_to_shared_ty(&mut self.builder.cursor(), interned_ty);
2067
2068 // Load the callee's `VMSharedTypeIndex`.
2069 //
2070 // Note that the callee may be null in which case this load may
2071 // trap. If so use the `TRAP_INDIRECT_CALL_TO_NULL` trap code.
2072 let mut mem_flags = ir::MemFlags::trusted().with_readonly();
2073 if self.env.clif_memory_traps_enabled() {
2074 mem_flags = mem_flags.with_trap_code(Some(crate::TRAP_INDIRECT_CALL_TO_NULL));
2075 } else {
2076 self.env
2077 .trapz(self.builder, funcref_ptr, crate::TRAP_INDIRECT_CALL_TO_NULL);
2078 }
2079 let callee_sig_id =
2080 self.env
2081 .load_funcref_type_index(&mut self.builder.cursor(), mem_flags, funcref_ptr);
2082
2083 // Check that they match: in the case of Wasm GC, this means doing a
2084 // full subtype check. Otherwise, we do a simple equality check.
2085 let matches = if features.gc() {
2086 #[cfg(feature = "gc")]
2087 {
2088 self.env
2089 .is_subtype(self.builder, callee_sig_id, caller_sig_id)
2090 }
2091 #[cfg(not(feature = "gc"))]
2092 {
2093 unreachable!()
2094 }
2095 } else {
2096 self.builder
2097 .ins()
2098 .icmp(IntCC::Equal, callee_sig_id, caller_sig_id)
2099 };
2100 self.env
2101 .trapz(self.builder, matches, crate::TRAP_BAD_SIGNATURE);
2102 CheckIndirectCallTypeSignature::Runtime
2103 }
2104
2105 /// Call a typed function reference.
call_ref( self, sig_ref: ir::SigRef, callee: ir::Value, args: &[ir::Value], ) -> WasmResult<CallRets>2106 pub fn call_ref(
2107 self,
2108 sig_ref: ir::SigRef,
2109 callee: ir::Value,
2110 args: &[ir::Value],
2111 ) -> WasmResult<CallRets> {
2112 // FIXME: the wasm type system tracks enough information to know whether
2113 // `callee` is a null reference or not. In some situations it can be
2114 // statically known here that `callee` cannot be null in which case this
2115 // can be `None` instead. This requires feeding type information from
2116 // wasmparser's validator into this function, however, which is not
2117 // easily done at this time.
2118 let callee_load_trap_code = Some(crate::TRAP_NULL_REFERENCE);
2119
2120 self.unchecked_call(sig_ref, callee, callee_load_trap_code, args)
2121 }
2122
2123 /// This calls a function by reference without checking the signature.
2124 ///
2125 /// It gets the function address, sets relevant flags, and passes the
2126 /// special callee/caller vmctxs. It is used by both call_indirect (which
2127 /// checks the signature) and call_ref (which doesn't).
unchecked_call( mut self, sig_ref: ir::SigRef, callee: ir::Value, callee_load_trap_code: Option<ir::TrapCode>, call_args: &[ir::Value], ) -> WasmResult<CallRets>2128 fn unchecked_call(
2129 mut self,
2130 sig_ref: ir::SigRef,
2131 callee: ir::Value,
2132 callee_load_trap_code: Option<ir::TrapCode>,
2133 call_args: &[ir::Value],
2134 ) -> WasmResult<CallRets> {
2135 let (func_addr, callee_vmctx) = self.load_code_and_vmctx(callee, callee_load_trap_code);
2136 self.unchecked_call_impl(sig_ref, func_addr, callee_vmctx, call_args)
2137 }
2138
load_code_and_vmctx( &mut self, callee: ir::Value, callee_load_trap_code: Option<ir::TrapCode>, ) -> (ir::Value, ir::Value)2139 fn load_code_and_vmctx(
2140 &mut self,
2141 callee: ir::Value,
2142 callee_load_trap_code: Option<ir::TrapCode>,
2143 ) -> (ir::Value, ir::Value) {
2144 let pointer_type = self.env.pointer_type();
2145
2146 // Dereference callee pointer to get the function address.
2147 //
2148 // Note that this may trap if `callee` hasn't previously been verified
2149 // to be non-null. This means that this load is annotated with an
2150 // optional trap code provided by the caller of `unchecked_call` which
2151 // will handle the case where this is either already known to be
2152 // non-null or may trap.
2153 let mem_flags = ir::MemFlags::trusted().with_readonly();
2154 let mut callee_flags = mem_flags;
2155 if self.env.clif_memory_traps_enabled() {
2156 callee_flags = callee_flags.with_trap_code(callee_load_trap_code);
2157 } else {
2158 if let Some(trap) = callee_load_trap_code {
2159 self.env.trapz(self.builder, callee, trap);
2160 }
2161 }
2162 let func_addr = self.builder.ins().load(
2163 pointer_type,
2164 callee_flags,
2165 callee,
2166 i32::from(self.env.offsets.ptr.vm_func_ref_wasm_call()),
2167 );
2168 let callee_vmctx = self.builder.ins().load(
2169 pointer_type,
2170 mem_flags,
2171 callee,
2172 i32::from(self.env.offsets.ptr.vm_func_ref_vmctx()),
2173 );
2174
2175 (func_addr, callee_vmctx)
2176 }
2177
caller_vmctx(&self) -> ir::Value2178 fn caller_vmctx(&self) -> ir::Value {
2179 self.builder
2180 .func
2181 .special_param(ArgumentPurpose::VMContext)
2182 .unwrap()
2183 }
2184
2185 /// This calls a function by reference without checking the
2186 /// signature, given the raw code pointer to the
2187 /// Wasm-calling-convention entry point and the callee vmctx.
unchecked_call_impl( mut self, sig_ref: ir::SigRef, func_addr: ir::Value, callee_vmctx: ir::Value, call_args: &[ir::Value], ) -> WasmResult<CallRets>2188 fn unchecked_call_impl(
2189 mut self,
2190 sig_ref: ir::SigRef,
2191 func_addr: ir::Value,
2192 callee_vmctx: ir::Value,
2193 call_args: &[ir::Value],
2194 ) -> WasmResult<CallRets> {
2195 let mut real_call_args = Vec::with_capacity(call_args.len() + 2);
2196 let caller_vmctx = self.caller_vmctx();
2197
2198 // First append the callee and caller vmctx addresses.
2199 real_call_args.push(callee_vmctx);
2200 real_call_args.push(caller_vmctx);
2201
2202 // Then append the regular call arguments.
2203 real_call_args.extend_from_slice(call_args);
2204
2205 Ok(self.indirect_call_inst(sig_ref, func_addr, &real_call_args))
2206 }
2207
exception_table( &mut self, sig: ir::SigRef, ) -> Option<(ir::ExceptionTable, Block, CallRets)>2208 fn exception_table(
2209 &mut self,
2210 sig: ir::SigRef,
2211 ) -> Option<(ir::ExceptionTable, Block, CallRets)> {
2212 if !self.tail && !self.env.stacks.handlers.is_empty() {
2213 let continuation_block = self.builder.create_block();
2214 let mut args = vec![];
2215 let mut results = smallvec![];
2216 for i in 0..self.builder.func.dfg.signatures[sig].returns.len() {
2217 let ty = self.builder.func.dfg.signatures[sig].returns[i].value_type;
2218 results.push(
2219 self.builder
2220 .func
2221 .dfg
2222 .append_block_param(continuation_block, ty),
2223 );
2224 args.push(BlockArg::TryCallRet(u32::try_from(i).unwrap()));
2225 }
2226
2227 let continuation = self
2228 .builder
2229 .func
2230 .dfg
2231 .block_call(continuation_block, args.iter());
2232 let mut handlers = vec![ExceptionTableItem::Context(self.caller_vmctx())];
2233 for (tag, block) in self.env.stacks.handlers.handlers() {
2234 let block_call = self
2235 .builder
2236 .func
2237 .dfg
2238 .block_call(block, &[BlockArg::TryCallExn(0)]);
2239 handlers.push(match tag {
2240 Some(tag) => ExceptionTableItem::Tag(tag, block_call),
2241 None => ExceptionTableItem::Default(block_call),
2242 });
2243 }
2244 let etd = ExceptionTableData::new(sig, continuation, handlers);
2245 let et = self.builder.func.dfg.exception_tables.push(etd);
2246 Some((et, continuation_block, results))
2247 } else {
2248 None
2249 }
2250 }
2251
results_from_call_inst(&self, inst: ir::Inst) -> CallRets2252 fn results_from_call_inst(&self, inst: ir::Inst) -> CallRets {
2253 self.builder
2254 .func
2255 .dfg
2256 .inst_results(inst)
2257 .iter()
2258 .copied()
2259 .collect()
2260 }
2261
handle_call_result_stackmap(&mut self, results: &[ir::Value], sig_ref: ir::SigRef)2262 fn handle_call_result_stackmap(&mut self, results: &[ir::Value], sig_ref: ir::SigRef) {
2263 for (i, &val) in results.iter().enumerate() {
2264 if self.env.sig_ref_result_needs_stack_map(sig_ref, i) {
2265 self.builder.declare_value_needs_stack_map(val);
2266 }
2267 }
2268 }
2269
direct_call_inst(&mut self, callee: ir::FuncRef, args: &[ir::Value]) -> CallRets2270 fn direct_call_inst(&mut self, callee: ir::FuncRef, args: &[ir::Value]) -> CallRets {
2271 let sig_ref = self.builder.func.dfg.ext_funcs[callee].signature;
2272 if self.tail {
2273 self.builder.ins().return_call(callee, args);
2274 smallvec![]
2275 } else if let Some((exception_table, continuation_block, results)) =
2276 self.exception_table(sig_ref)
2277 {
2278 let inst = self.builder.ins().try_call(callee, args, exception_table);
2279 self.handle_call_result_stackmap(&results, sig_ref);
2280 self.builder.switch_to_block(continuation_block);
2281 self.builder.seal_block(continuation_block);
2282 self.attach_tags(inst);
2283 results
2284 } else {
2285 let inst = self.builder.ins().call(callee, args);
2286 let results = self.results_from_call_inst(inst);
2287 self.handle_call_result_stackmap(&results, sig_ref);
2288 self.attach_tags(inst);
2289 results
2290 }
2291 }
2292
indirect_call_inst( &mut self, sig_ref: ir::SigRef, func_addr: ir::Value, args: &[ir::Value], ) -> CallRets2293 fn indirect_call_inst(
2294 &mut self,
2295 sig_ref: ir::SigRef,
2296 func_addr: ir::Value,
2297 args: &[ir::Value],
2298 ) -> CallRets {
2299 if self.tail {
2300 self.builder
2301 .ins()
2302 .return_call_indirect(sig_ref, func_addr, args);
2303 smallvec![]
2304 } else if let Some((exception_table, continuation_block, results)) =
2305 self.exception_table(sig_ref)
2306 {
2307 let inst = self
2308 .builder
2309 .ins()
2310 .try_call_indirect(func_addr, args, exception_table);
2311 self.handle_call_result_stackmap(&results, sig_ref);
2312 self.builder.switch_to_block(continuation_block);
2313 self.builder.seal_block(continuation_block);
2314 self.attach_tags(inst);
2315 results
2316 } else {
2317 let inst = self.builder.ins().call_indirect(sig_ref, func_addr, args);
2318 let results = self.results_from_call_inst(inst);
2319 self.handle_call_result_stackmap(&results, sig_ref);
2320 self.attach_tags(inst);
2321 results
2322 }
2323 }
2324
attach_tags(&mut self, inst: ir::Inst)2325 fn attach_tags(&mut self, inst: ir::Inst) {
2326 let tags = self.env.debug_tags(self.srcloc);
2327 if !tags.is_empty() {
2328 self.builder.func.debug_tags.set(inst, tags);
2329 }
2330 }
2331 }
2332
2333 impl TypeConvert for FuncEnvironment<'_> {
lookup_heap_type(&self, ty: wasmparser::UnpackedIndex) -> WasmHeapType2334 fn lookup_heap_type(&self, ty: wasmparser::UnpackedIndex) -> WasmHeapType {
2335 wasmtime_environ::WasmparserTypeConverter::new(self.types, |idx| {
2336 self.module.types[idx].unwrap_module_type_index()
2337 })
2338 .lookup_heap_type(ty)
2339 }
2340
lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex2341 fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
2342 wasmtime_environ::WasmparserTypeConverter::new(self.types, |idx| {
2343 self.module.types[idx].unwrap_module_type_index()
2344 })
2345 .lookup_type_index(index)
2346 }
2347 }
2348
2349 impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environment> {
target_config(&self) -> TargetFrontendConfig2350 fn target_config(&self) -> TargetFrontendConfig {
2351 self.isa.frontend_config()
2352 }
2353
reference_type(&self, wasm_ty: WasmHeapType) -> (ir::Type, bool)2354 fn reference_type(&self, wasm_ty: WasmHeapType) -> (ir::Type, bool) {
2355 let ty = crate::reference_type(wasm_ty, self.pointer_type());
2356 let needs_stack_map = self.heap_ty_needs_stack_map(wasm_ty);
2357 (ty, needs_stack_map)
2358 }
2359
heap_access_spectre_mitigation(&self) -> bool2360 fn heap_access_spectre_mitigation(&self) -> bool {
2361 self.isa.flags().enable_heap_access_spectre_mitigation()
2362 }
2363
tunables(&self) -> &Tunables2364 fn tunables(&self) -> &Tunables {
2365 self.compiler.tunables()
2366 }
2367 }
2368
2369 impl FuncEnvironment<'_> {
heaps(&self) -> &PrimaryMap<Heap, HeapData>2370 pub fn heaps(&self) -> &PrimaryMap<Heap, HeapData> {
2371 &self.heaps
2372 }
2373
is_wasm_parameter(&self, index: usize) -> bool2374 pub fn is_wasm_parameter(&self, index: usize) -> bool {
2375 // The first two parameters are the vmctx and caller vmctx. The rest are
2376 // the wasm parameters.
2377 index >= 2
2378 }
2379
clif_param_as_wasm_param(&self, index: usize) -> Option<WasmValType>2380 pub fn clif_param_as_wasm_param(&self, index: usize) -> Option<WasmValType> {
2381 if index >= 2 {
2382 Some(self.wasm_func_ty.params()[index - 2])
2383 } else {
2384 None
2385 }
2386 }
2387
param_needs_stack_map(&self, _signature: &ir::Signature, index: usize) -> bool2388 pub fn param_needs_stack_map(&self, _signature: &ir::Signature, index: usize) -> bool {
2389 // Skip the caller and callee vmctx.
2390 if index < 2 {
2391 return false;
2392 }
2393
2394 self.wasm_func_ty.params()[index - 2].is_vmgcref_type_and_not_i31()
2395 }
2396
sig_ref_result_needs_stack_map(&self, sig_ref: ir::SigRef, index: usize) -> bool2397 pub fn sig_ref_result_needs_stack_map(&self, sig_ref: ir::SigRef, index: usize) -> bool {
2398 let wasm_func_ty = self.sig_ref_to_ty[sig_ref].as_ref().unwrap();
2399 wasm_func_ty.results()[index].is_vmgcref_type_and_not_i31()
2400 }
2401
translate_table_grow( &mut self, builder: &mut FunctionBuilder<'_>, table_index: TableIndex, delta: ir::Value, init_value: ir::Value, ) -> WasmResult<ir::Value>2402 pub fn translate_table_grow(
2403 &mut self,
2404 builder: &mut FunctionBuilder<'_>,
2405 table_index: TableIndex,
2406 delta: ir::Value,
2407 init_value: ir::Value,
2408 ) -> WasmResult<ir::Value> {
2409 let mut pos = builder.cursor();
2410 let table = self.table(table_index);
2411 let ty = table.ref_type.heap_type;
2412 let (table_vmctx, defined_table_index) =
2413 self.table_vmctx_and_defined_index(&mut pos, table_index);
2414 let index_type = table.idx_type;
2415 let delta = self.cast_index_to_i64(&mut pos, delta, index_type);
2416
2417 let mut args: SmallVec<[_; 6]> = smallvec![table_vmctx, defined_table_index, delta];
2418 let grow = match ty.top() {
2419 WasmHeapTopType::Extern | WasmHeapTopType::Any | WasmHeapTopType::Exn => {
2420 args.push(init_value);
2421 gc::builtins::table_grow_gc_ref(self, pos.func)?
2422 }
2423 WasmHeapTopType::Func => {
2424 args.push(init_value);
2425 self.builtin_functions.table_grow_func_ref(pos.func)
2426 }
2427 WasmHeapTopType::Cont => {
2428 let (revision, contref) =
2429 stack_switching::fatpointer::deconstruct(self, &mut pos, init_value);
2430 args.extend_from_slice(&[contref, revision]);
2431 stack_switching::builtins::table_grow_cont_obj(self, pos.func)?
2432 }
2433 };
2434
2435 let call_inst = pos.ins().call(grow, &args);
2436 let result = builder.func.dfg.first_result(call_inst);
2437
2438 Ok(self.convert_pointer_to_index_type(builder.cursor(), result, index_type, false))
2439 }
2440
translate_table_get( &mut self, builder: &mut FunctionBuilder, table_index: TableIndex, index: ir::Value, ) -> WasmResult<ir::Value>2441 pub fn translate_table_get(
2442 &mut self,
2443 builder: &mut FunctionBuilder,
2444 table_index: TableIndex,
2445 index: ir::Value,
2446 ) -> WasmResult<ir::Value> {
2447 let table = self.module.tables[table_index];
2448 let table_data = self.get_or_create_table(builder.func, table_index);
2449 let heap_ty = table.ref_type.heap_type;
2450 match heap_ty.top() {
2451 // GC-managed types.
2452 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => {
2453 let (src, flags) = table_data.prepare_table_addr(self, builder, index);
2454 gc::gc_compiler(self)?.translate_read_gc_reference(
2455 self,
2456 builder,
2457 table.ref_type,
2458 src,
2459 flags,
2460 )
2461 }
2462
2463 // Function types.
2464 WasmHeapTopType::Func => {
2465 Ok(self.get_or_init_func_ref_table_elem(builder, table_index, index, false))
2466 }
2467
2468 // Continuation types.
2469 WasmHeapTopType::Cont => {
2470 let (elem_addr, flags) = table_data.prepare_table_addr(self, builder, index);
2471 Ok(builder.ins().load(
2472 stack_switching::fatpointer::fatpointer_type(self),
2473 flags,
2474 elem_addr,
2475 0,
2476 ))
2477 }
2478 }
2479 }
2480
translate_table_set( &mut self, builder: &mut FunctionBuilder, table_index: TableIndex, value: ir::Value, index: ir::Value, ) -> WasmResult<()>2481 pub fn translate_table_set(
2482 &mut self,
2483 builder: &mut FunctionBuilder,
2484 table_index: TableIndex,
2485 value: ir::Value,
2486 index: ir::Value,
2487 ) -> WasmResult<()> {
2488 let table = self.module.tables[table_index];
2489 let table_data = self.get_or_create_table(builder.func, table_index);
2490 let heap_ty = table.ref_type.heap_type;
2491 match heap_ty.top() {
2492 // GC-managed types.
2493 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => {
2494 let (dst, flags) = table_data.prepare_table_addr(self, builder, index);
2495 gc::gc_compiler(self)?.translate_write_gc_reference(
2496 self,
2497 builder,
2498 table.ref_type,
2499 dst,
2500 value,
2501 flags,
2502 )
2503 }
2504
2505 // Function types.
2506 WasmHeapTopType::Func => {
2507 let (elem_addr, flags) = table_data.prepare_table_addr(self, builder, index);
2508 // Set the "initialized bit". See doc-comment on
2509 // `FUNCREF_INIT_BIT` in
2510 // crates/environ/src/ref_bits.rs for details.
2511 let value_with_init_bit = if self.tunables.table_lazy_init {
2512 builder
2513 .ins()
2514 .bor_imm(value, Imm64::from(FUNCREF_INIT_BIT as i64))
2515 } else {
2516 value
2517 };
2518 builder
2519 .ins()
2520 .store(flags, value_with_init_bit, elem_addr, 0);
2521 Ok(())
2522 }
2523
2524 // Continuation types.
2525 WasmHeapTopType::Cont => {
2526 let (elem_addr, flags) = table_data.prepare_table_addr(self, builder, index);
2527 builder.ins().store(flags, value, elem_addr, 0);
2528 Ok(())
2529 }
2530 }
2531 }
2532
translate_table_fill( &mut self, builder: &mut FunctionBuilder<'_>, table_index: TableIndex, dst: ir::Value, val: ir::Value, len: ir::Value, ) -> WasmResult<()>2533 pub fn translate_table_fill(
2534 &mut self,
2535 builder: &mut FunctionBuilder<'_>,
2536 table_index: TableIndex,
2537 dst: ir::Value,
2538 val: ir::Value,
2539 len: ir::Value,
2540 ) -> WasmResult<()> {
2541 let mut pos = builder.cursor();
2542 let table = self.table(table_index);
2543 let ty = table.ref_type.heap_type;
2544 let dst = self.cast_index_to_i64(&mut pos, dst, table.idx_type);
2545 let len = self.cast_index_to_i64(&mut pos, len, table.idx_type);
2546 let (table_vmctx, table_index) = self.table_vmctx_and_defined_index(&mut pos, table_index);
2547
2548 let mut args: SmallVec<[_; 6]> = smallvec![table_vmctx, table_index, dst];
2549 let libcall = match ty.top() {
2550 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => {
2551 args.push(val);
2552 gc::builtins::table_fill_gc_ref(self, &mut pos.func)?
2553 }
2554 WasmHeapTopType::Func => {
2555 args.push(val);
2556 self.builtin_functions.table_fill_func_ref(&mut pos.func)
2557 }
2558 WasmHeapTopType::Cont => {
2559 let (revision, contref) =
2560 stack_switching::fatpointer::deconstruct(self, &mut pos, val);
2561 args.extend_from_slice(&[contref, revision]);
2562 stack_switching::builtins::table_fill_cont_obj(self, &mut pos.func)?
2563 }
2564 };
2565
2566 args.push(len);
2567 builder.ins().call(libcall, &args);
2568
2569 Ok(())
2570 }
2571
translate_ref_i31( &mut self, mut pos: FuncCursor, val: ir::Value, ) -> WasmResult<ir::Value>2572 pub fn translate_ref_i31(
2573 &mut self,
2574 mut pos: FuncCursor,
2575 val: ir::Value,
2576 ) -> WasmResult<ir::Value> {
2577 debug_assert_eq!(pos.func.dfg.value_type(val), ir::types::I32);
2578 let shifted = pos.ins().ishl_imm(val, 1);
2579 let tagged = pos
2580 .ins()
2581 .bor_imm(shifted, i64::from(crate::I31_REF_DISCRIMINANT));
2582 let (ref_ty, _needs_stack_map) = self.reference_type(WasmHeapType::I31);
2583 debug_assert_eq!(ref_ty, ir::types::I32);
2584 Ok(tagged)
2585 }
2586
translate_i31_get_s( &mut self, builder: &mut FunctionBuilder, i31ref: ir::Value, ) -> WasmResult<ir::Value>2587 pub fn translate_i31_get_s(
2588 &mut self,
2589 builder: &mut FunctionBuilder,
2590 i31ref: ir::Value,
2591 ) -> WasmResult<ir::Value> {
2592 // TODO: If we knew we have a `(ref i31)` here, instead of maybe a `(ref
2593 // null i31)`, we could omit the `trapz`. But plumbing that type info
2594 // from `wasmparser` and through to here is a bit funky.
2595 self.trapz(builder, i31ref, crate::TRAP_NULL_REFERENCE);
2596 Ok(builder.ins().sshr_imm(i31ref, 1))
2597 }
2598
translate_i31_get_u( &mut self, builder: &mut FunctionBuilder, i31ref: ir::Value, ) -> WasmResult<ir::Value>2599 pub fn translate_i31_get_u(
2600 &mut self,
2601 builder: &mut FunctionBuilder,
2602 i31ref: ir::Value,
2603 ) -> WasmResult<ir::Value> {
2604 // TODO: If we knew we have a `(ref i31)` here, instead of maybe a `(ref
2605 // null i31)`, we could omit the `trapz`. But plumbing that type info
2606 // from `wasmparser` and through to here is a bit funky.
2607 self.trapz(builder, i31ref, crate::TRAP_NULL_REFERENCE);
2608 Ok(builder.ins().ushr_imm(i31ref, 1))
2609 }
2610
struct_fields_len(&mut self, struct_type_index: TypeIndex) -> WasmResult<usize>2611 pub fn struct_fields_len(&mut self, struct_type_index: TypeIndex) -> WasmResult<usize> {
2612 let ty = self.module.types[struct_type_index].unwrap_module_type_index();
2613 match &self.types[ty].composite_type.inner {
2614 WasmCompositeInnerType::Struct(s) => Ok(s.fields.len()),
2615 _ => unreachable!(),
2616 }
2617 }
2618
translate_struct_new( &mut self, builder: &mut FunctionBuilder, struct_type_index: TypeIndex, fields: StructFieldsVec, ) -> WasmResult<ir::Value>2619 pub fn translate_struct_new(
2620 &mut self,
2621 builder: &mut FunctionBuilder,
2622 struct_type_index: TypeIndex,
2623 fields: StructFieldsVec,
2624 ) -> WasmResult<ir::Value> {
2625 gc::translate_struct_new(self, builder, struct_type_index, &fields)
2626 }
2627
translate_struct_new_default( &mut self, builder: &mut FunctionBuilder, struct_type_index: TypeIndex, ) -> WasmResult<ir::Value>2628 pub fn translate_struct_new_default(
2629 &mut self,
2630 builder: &mut FunctionBuilder,
2631 struct_type_index: TypeIndex,
2632 ) -> WasmResult<ir::Value> {
2633 gc::translate_struct_new_default(self, builder, struct_type_index)
2634 }
2635
translate_struct_get( &mut self, builder: &mut FunctionBuilder, struct_type_index: TypeIndex, field_index: u32, struct_ref: ir::Value, extension: Option<Extension>, ) -> WasmResult<ir::Value>2636 pub fn translate_struct_get(
2637 &mut self,
2638 builder: &mut FunctionBuilder,
2639 struct_type_index: TypeIndex,
2640 field_index: u32,
2641 struct_ref: ir::Value,
2642 extension: Option<Extension>,
2643 ) -> WasmResult<ir::Value> {
2644 gc::translate_struct_get(
2645 self,
2646 builder,
2647 struct_type_index,
2648 field_index,
2649 struct_ref,
2650 extension,
2651 )
2652 }
2653
translate_struct_set( &mut self, builder: &mut FunctionBuilder, struct_type_index: TypeIndex, field_index: u32, struct_ref: ir::Value, value: ir::Value, ) -> WasmResult<()>2654 pub fn translate_struct_set(
2655 &mut self,
2656 builder: &mut FunctionBuilder,
2657 struct_type_index: TypeIndex,
2658 field_index: u32,
2659 struct_ref: ir::Value,
2660 value: ir::Value,
2661 ) -> WasmResult<()> {
2662 gc::translate_struct_set(
2663 self,
2664 builder,
2665 struct_type_index,
2666 field_index,
2667 struct_ref,
2668 value,
2669 )
2670 }
2671
translate_exn_unbox( &mut self, builder: &mut FunctionBuilder<'_>, tag_index: TagIndex, exn_ref: ir::Value, ) -> WasmResult<SmallVec<[ir::Value; 4]>>2672 pub fn translate_exn_unbox(
2673 &mut self,
2674 builder: &mut FunctionBuilder<'_>,
2675 tag_index: TagIndex,
2676 exn_ref: ir::Value,
2677 ) -> WasmResult<SmallVec<[ir::Value; 4]>> {
2678 gc::translate_exn_unbox(self, builder, tag_index, exn_ref)
2679 }
2680
translate_exn_throw( &mut self, builder: &mut FunctionBuilder<'_>, tag_index: TagIndex, args: &[ir::Value], ) -> WasmResult<()>2681 pub fn translate_exn_throw(
2682 &mut self,
2683 builder: &mut FunctionBuilder<'_>,
2684 tag_index: TagIndex,
2685 args: &[ir::Value],
2686 ) -> WasmResult<()> {
2687 gc::translate_exn_throw(self, builder, tag_index, args)
2688 }
2689
translate_exn_throw_ref( &mut self, builder: &mut FunctionBuilder<'_>, exnref: ir::Value, ) -> WasmResult<()>2690 pub fn translate_exn_throw_ref(
2691 &mut self,
2692 builder: &mut FunctionBuilder<'_>,
2693 exnref: ir::Value,
2694 ) -> WasmResult<()> {
2695 gc::translate_exn_throw_ref(self, builder, exnref)
2696 }
2697
translate_array_new( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, elem: ir::Value, len: ir::Value, ) -> WasmResult<ir::Value>2698 pub fn translate_array_new(
2699 &mut self,
2700 builder: &mut FunctionBuilder,
2701 array_type_index: TypeIndex,
2702 elem: ir::Value,
2703 len: ir::Value,
2704 ) -> WasmResult<ir::Value> {
2705 gc::translate_array_new(self, builder, array_type_index, elem, len)
2706 }
2707
translate_array_new_default( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, len: ir::Value, ) -> WasmResult<ir::Value>2708 pub fn translate_array_new_default(
2709 &mut self,
2710 builder: &mut FunctionBuilder,
2711 array_type_index: TypeIndex,
2712 len: ir::Value,
2713 ) -> WasmResult<ir::Value> {
2714 gc::translate_array_new_default(self, builder, array_type_index, len)
2715 }
2716
translate_array_new_fixed( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, elems: &[ir::Value], ) -> WasmResult<ir::Value>2717 pub fn translate_array_new_fixed(
2718 &mut self,
2719 builder: &mut FunctionBuilder,
2720 array_type_index: TypeIndex,
2721 elems: &[ir::Value],
2722 ) -> WasmResult<ir::Value> {
2723 gc::translate_array_new_fixed(self, builder, array_type_index, elems)
2724 }
2725
translate_array_new_data( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, data_index: DataIndex, data_offset: ir::Value, len: ir::Value, ) -> WasmResult<ir::Value>2726 pub fn translate_array_new_data(
2727 &mut self,
2728 builder: &mut FunctionBuilder,
2729 array_type_index: TypeIndex,
2730 data_index: DataIndex,
2731 data_offset: ir::Value,
2732 len: ir::Value,
2733 ) -> WasmResult<ir::Value> {
2734 let libcall = gc::builtins::array_new_data(self, builder.func)?;
2735 let vmctx = self.vmctx_val(&mut builder.cursor());
2736 let interned_type_index = self.module.types[array_type_index].unwrap_module_type_index();
2737 let interned_type_index = builder
2738 .ins()
2739 .iconst(I32, i64::from(interned_type_index.as_u32()));
2740 let data_index = builder.ins().iconst(I32, i64::from(data_index.as_u32()));
2741 let call_inst = builder.ins().call(
2742 libcall,
2743 &[vmctx, interned_type_index, data_index, data_offset, len],
2744 );
2745 let array_ref = builder.func.dfg.first_result(call_inst);
2746 builder.declare_value_needs_stack_map(array_ref);
2747 Ok(array_ref)
2748 }
2749
translate_array_new_elem( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, elem_index: ElemIndex, elem_offset: ir::Value, len: ir::Value, ) -> WasmResult<ir::Value>2750 pub fn translate_array_new_elem(
2751 &mut self,
2752 builder: &mut FunctionBuilder,
2753 array_type_index: TypeIndex,
2754 elem_index: ElemIndex,
2755 elem_offset: ir::Value,
2756 len: ir::Value,
2757 ) -> WasmResult<ir::Value> {
2758 let libcall = gc::builtins::array_new_elem(self, builder.func)?;
2759 let vmctx = self.vmctx_val(&mut builder.cursor());
2760 let interned_type_index = self.module.types[array_type_index].unwrap_module_type_index();
2761 let interned_type_index = builder
2762 .ins()
2763 .iconst(I32, i64::from(interned_type_index.as_u32()));
2764 let elem_index = builder.ins().iconst(I32, i64::from(elem_index.as_u32()));
2765 let call_inst = builder.ins().call(
2766 libcall,
2767 &[vmctx, interned_type_index, elem_index, elem_offset, len],
2768 );
2769 let array_ref = builder.func.dfg.first_result(call_inst);
2770 builder.declare_value_needs_stack_map(array_ref);
2771 Ok(array_ref)
2772 }
2773
translate_array_copy( &mut self, builder: &mut FunctionBuilder, _dst_array_type_index: TypeIndex, dst_array: ir::Value, dst_index: ir::Value, _src_array_type_index: TypeIndex, src_array: ir::Value, src_index: ir::Value, len: ir::Value, ) -> WasmResult<()>2774 pub fn translate_array_copy(
2775 &mut self,
2776 builder: &mut FunctionBuilder,
2777 _dst_array_type_index: TypeIndex,
2778 dst_array: ir::Value,
2779 dst_index: ir::Value,
2780 _src_array_type_index: TypeIndex,
2781 src_array: ir::Value,
2782 src_index: ir::Value,
2783 len: ir::Value,
2784 ) -> WasmResult<()> {
2785 let libcall = gc::builtins::array_copy(self, builder.func)?;
2786 let vmctx = self.vmctx_val(&mut builder.cursor());
2787 builder.ins().call(
2788 libcall,
2789 &[vmctx, dst_array, dst_index, src_array, src_index, len],
2790 );
2791 Ok(())
2792 }
2793
translate_array_fill( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, array: ir::Value, index: ir::Value, value: ir::Value, len: ir::Value, ) -> WasmResult<()>2794 pub fn translate_array_fill(
2795 &mut self,
2796 builder: &mut FunctionBuilder,
2797 array_type_index: TypeIndex,
2798 array: ir::Value,
2799 index: ir::Value,
2800 value: ir::Value,
2801 len: ir::Value,
2802 ) -> WasmResult<()> {
2803 gc::translate_array_fill(self, builder, array_type_index, array, index, value, len)
2804 }
2805
translate_array_init_data( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, array: ir::Value, dst_index: ir::Value, data_index: DataIndex, data_offset: ir::Value, len: ir::Value, ) -> WasmResult<()>2806 pub fn translate_array_init_data(
2807 &mut self,
2808 builder: &mut FunctionBuilder,
2809 array_type_index: TypeIndex,
2810 array: ir::Value,
2811 dst_index: ir::Value,
2812 data_index: DataIndex,
2813 data_offset: ir::Value,
2814 len: ir::Value,
2815 ) -> WasmResult<()> {
2816 let libcall = gc::builtins::array_init_data(self, builder.func)?;
2817 let vmctx = self.vmctx_val(&mut builder.cursor());
2818 let interned_type_index = self.module.types[array_type_index].unwrap_module_type_index();
2819 let interned_type_index = builder
2820 .ins()
2821 .iconst(I32, i64::from(interned_type_index.as_u32()));
2822 let data_index = builder.ins().iconst(I32, i64::from(data_index.as_u32()));
2823 builder.ins().call(
2824 libcall,
2825 &[
2826 vmctx,
2827 interned_type_index,
2828 array,
2829 dst_index,
2830 data_index,
2831 data_offset,
2832 len,
2833 ],
2834 );
2835 Ok(())
2836 }
2837
translate_array_init_elem( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, array: ir::Value, dst_index: ir::Value, elem_index: ElemIndex, elem_offset: ir::Value, len: ir::Value, ) -> WasmResult<()>2838 pub fn translate_array_init_elem(
2839 &mut self,
2840 builder: &mut FunctionBuilder,
2841 array_type_index: TypeIndex,
2842 array: ir::Value,
2843 dst_index: ir::Value,
2844 elem_index: ElemIndex,
2845 elem_offset: ir::Value,
2846 len: ir::Value,
2847 ) -> WasmResult<()> {
2848 let libcall = gc::builtins::array_init_elem(self, builder.func)?;
2849 let vmctx = self.vmctx_val(&mut builder.cursor());
2850 let interned_type_index = self.module.types[array_type_index].unwrap_module_type_index();
2851 let interned_type_index = builder
2852 .ins()
2853 .iconst(I32, i64::from(interned_type_index.as_u32()));
2854 let elem_index = builder.ins().iconst(I32, i64::from(elem_index.as_u32()));
2855 builder.ins().call(
2856 libcall,
2857 &[
2858 vmctx,
2859 interned_type_index,
2860 array,
2861 dst_index,
2862 elem_index,
2863 elem_offset,
2864 len,
2865 ],
2866 );
2867 Ok(())
2868 }
2869
translate_array_len( &mut self, builder: &mut FunctionBuilder, array: ir::Value, ) -> WasmResult<ir::Value>2870 pub fn translate_array_len(
2871 &mut self,
2872 builder: &mut FunctionBuilder,
2873 array: ir::Value,
2874 ) -> WasmResult<ir::Value> {
2875 gc::translate_array_len(self, builder, array)
2876 }
2877
translate_array_get( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, array: ir::Value, index: ir::Value, extension: Option<Extension>, ) -> WasmResult<ir::Value>2878 pub fn translate_array_get(
2879 &mut self,
2880 builder: &mut FunctionBuilder,
2881 array_type_index: TypeIndex,
2882 array: ir::Value,
2883 index: ir::Value,
2884 extension: Option<Extension>,
2885 ) -> WasmResult<ir::Value> {
2886 gc::translate_array_get(self, builder, array_type_index, array, index, extension)
2887 }
2888
translate_array_set( &mut self, builder: &mut FunctionBuilder, array_type_index: TypeIndex, array: ir::Value, index: ir::Value, value: ir::Value, ) -> WasmResult<()>2889 pub fn translate_array_set(
2890 &mut self,
2891 builder: &mut FunctionBuilder,
2892 array_type_index: TypeIndex,
2893 array: ir::Value,
2894 index: ir::Value,
2895 value: ir::Value,
2896 ) -> WasmResult<()> {
2897 gc::translate_array_set(self, builder, array_type_index, array, index, value)
2898 }
2899
translate_ref_test( &mut self, builder: &mut FunctionBuilder<'_>, test_ty: WasmRefType, gc_ref: ir::Value, gc_ref_ty: WasmRefType, ) -> WasmResult<ir::Value>2900 pub fn translate_ref_test(
2901 &mut self,
2902 builder: &mut FunctionBuilder<'_>,
2903 test_ty: WasmRefType,
2904 gc_ref: ir::Value,
2905 gc_ref_ty: WasmRefType,
2906 ) -> WasmResult<ir::Value> {
2907 gc::translate_ref_test(self, builder, test_ty, gc_ref, gc_ref_ty)
2908 }
2909
translate_ref_null( &mut self, mut pos: cranelift_codegen::cursor::FuncCursor, ht: WasmHeapType, ) -> WasmResult<ir::Value>2910 pub fn translate_ref_null(
2911 &mut self,
2912 mut pos: cranelift_codegen::cursor::FuncCursor,
2913 ht: WasmHeapType,
2914 ) -> WasmResult<ir::Value> {
2915 Ok(match ht.top() {
2916 WasmHeapTopType::Func => pos.ins().iconst(self.pointer_type(), 0),
2917 // NB: null GC references don't need to be in stack maps.
2918 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => {
2919 pos.ins().iconst(types::I32, 0)
2920 }
2921 WasmHeapTopType::Cont => {
2922 let zero = pos.ins().iconst(self.pointer_type(), 0);
2923 stack_switching::fatpointer::construct(self, &mut pos, zero, zero)
2924 }
2925 })
2926 }
2927
translate_ref_is_null( &mut self, mut pos: cranelift_codegen::cursor::FuncCursor, value: ir::Value, ty: WasmRefType, ) -> WasmResult<ir::Value>2928 pub fn translate_ref_is_null(
2929 &mut self,
2930 mut pos: cranelift_codegen::cursor::FuncCursor,
2931 value: ir::Value,
2932 ty: WasmRefType,
2933 ) -> WasmResult<ir::Value> {
2934 // If we know the type is not nullable, then we don't actually need to
2935 // check for null.
2936 if !ty.nullable {
2937 return Ok(pos.ins().iconst(ir::types::I32, 0));
2938 }
2939
2940 let byte_is_null = match ty.heap_type.top() {
2941 WasmHeapTopType::Cont => {
2942 let (_revision, contref) =
2943 stack_switching::fatpointer::deconstruct(self, &mut pos, value);
2944 pos.ins()
2945 .icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, contref, 0)
2946 }
2947 _ => pos
2948 .ins()
2949 .icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, value, 0),
2950 };
2951
2952 Ok(pos.ins().uextend(ir::types::I32, byte_is_null))
2953 }
2954
translate_ref_func( &mut self, mut pos: cranelift_codegen::cursor::FuncCursor<'_>, func_index: FuncIndex, ) -> WasmResult<ir::Value>2955 pub fn translate_ref_func(
2956 &mut self,
2957 mut pos: cranelift_codegen::cursor::FuncCursor<'_>,
2958 func_index: FuncIndex,
2959 ) -> WasmResult<ir::Value> {
2960 let func_index = pos.ins().iconst(I32, func_index.as_u32() as i64);
2961 let ref_func = self.builtin_functions.ref_func(&mut pos.func);
2962 let vmctx = self.vmctx_val(&mut pos);
2963
2964 let call_inst = pos.ins().call(ref_func, &[vmctx, func_index]);
2965 Ok(pos.func.dfg.first_result(call_inst))
2966 }
2967
translate_global_get( &mut self, builder: &mut FunctionBuilder<'_>, global_index: GlobalIndex, ) -> WasmResult<ir::Value>2968 pub(crate) fn translate_global_get(
2969 &mut self,
2970 builder: &mut FunctionBuilder<'_>,
2971 global_index: GlobalIndex,
2972 ) -> WasmResult<ir::Value> {
2973 match self.get_or_create_global(builder.func, global_index) {
2974 GlobalVariable::Constant { value } => match value {
2975 GlobalConstValue::I32(x) => Ok(builder.ins().iconst(ir::types::I32, i64::from(x))),
2976 GlobalConstValue::I64(x) => Ok(builder.ins().iconst(ir::types::I64, x)),
2977 GlobalConstValue::F32(x) => {
2978 Ok(builder.ins().f32const(ir::immediates::Ieee32::with_bits(x)))
2979 }
2980 GlobalConstValue::F64(x) => {
2981 Ok(builder.ins().f64const(ir::immediates::Ieee64::with_bits(x)))
2982 }
2983 GlobalConstValue::V128(x) => {
2984 let data = x.to_le_bytes().to_vec().into();
2985 let handle = builder.func.dfg.constants.insert(data);
2986 Ok(builder.ins().vconst(ir::types::I8X16, handle))
2987 }
2988 },
2989 GlobalVariable::Memory { gv, offset, ty } => {
2990 let addr = builder.ins().global_value(self.pointer_type(), gv);
2991 let mut flags = ir::MemFlags::trusted();
2992 // Store vector globals in little-endian format to avoid
2993 // byte swaps on big-endian platforms since at-rest vectors
2994 // should already be in little-endian format anyway.
2995 if ty.is_vector() {
2996 flags.set_endianness(ir::Endianness::Little);
2997 }
2998 // Put globals in the "table" abstract heap category as well.
2999 flags.set_alias_region(Some(ir::AliasRegion::Table));
3000 Ok(builder.ins().load(ty, flags, addr, offset))
3001 }
3002 GlobalVariable::Custom => {
3003 let global_ty = self.module.globals[global_index];
3004 let wasm_ty = global_ty.wasm_ty;
3005 debug_assert!(
3006 wasm_ty.is_vmgcref_type(),
3007 "We only use GlobalVariable::Custom for VMGcRef types"
3008 );
3009 let WasmValType::Ref(ref_ty) = wasm_ty else {
3010 unreachable!()
3011 };
3012
3013 let (gv, offset) = self.get_global_location(builder.func, global_index);
3014 let gv = builder.ins().global_value(self.pointer_type(), gv);
3015 let src = builder.ins().iadd_imm(gv, i64::from(offset));
3016
3017 gc::gc_compiler(self)?.translate_read_gc_reference(
3018 self,
3019 builder,
3020 ref_ty,
3021 src,
3022 if global_ty.mutability {
3023 ir::MemFlags::trusted()
3024 } else {
3025 ir::MemFlags::trusted().with_readonly().with_can_move()
3026 },
3027 )
3028 }
3029 }
3030 }
3031
translate_global_set( &mut self, builder: &mut FunctionBuilder<'_>, global_index: GlobalIndex, val: ir::Value, ) -> WasmResult<()>3032 pub(crate) fn translate_global_set(
3033 &mut self,
3034 builder: &mut FunctionBuilder<'_>,
3035 global_index: GlobalIndex,
3036 val: ir::Value,
3037 ) -> WasmResult<()> {
3038 match self.get_or_create_global(builder.func, global_index) {
3039 GlobalVariable::Constant { .. } => {
3040 unreachable!("validation checks that Wasm cannot `global.set` constant globals")
3041 }
3042 GlobalVariable::Memory { gv, offset, ty } => {
3043 let addr = builder.ins().global_value(self.pointer_type(), gv);
3044 let mut flags = ir::MemFlags::trusted();
3045 // Like `global.get`, store globals in little-endian format.
3046 if ty.is_vector() {
3047 flags.set_endianness(ir::Endianness::Little);
3048 }
3049 // Put globals in the "table" abstract heap category as well.
3050 flags.set_alias_region(Some(ir::AliasRegion::Table));
3051 debug_assert_eq!(ty, builder.func.dfg.value_type(val));
3052 builder.ins().store(flags, val, addr, offset);
3053 self.update_global(builder, global_index, val);
3054 }
3055 GlobalVariable::Custom => {
3056 let ty = self.module.globals[global_index].wasm_ty;
3057 debug_assert!(
3058 ty.is_vmgcref_type(),
3059 "We only use GlobalVariable::Custom for VMGcRef types"
3060 );
3061 let WasmValType::Ref(ty) = ty else {
3062 unreachable!()
3063 };
3064
3065 let (gv, offset) = self.get_global_location(builder.func, global_index);
3066 let gv = builder.ins().global_value(self.pointer_type(), gv);
3067 let src = builder.ins().iadd_imm(gv, i64::from(offset));
3068
3069 gc::gc_compiler(self)?.translate_write_gc_reference(
3070 self,
3071 builder,
3072 ty,
3073 src,
3074 val,
3075 ir::MemFlags::trusted(),
3076 )?
3077 }
3078 }
3079 Ok(())
3080 }
3081
translate_call_indirect<'a>( &mut self, builder: &'a mut FunctionBuilder, srcloc: ir::SourceLoc, features: &WasmFeatures, table_index: TableIndex, ty_index: TypeIndex, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], ) -> WasmResult<Option<CallRets>>3082 pub fn translate_call_indirect<'a>(
3083 &mut self,
3084 builder: &'a mut FunctionBuilder,
3085 srcloc: ir::SourceLoc,
3086 features: &WasmFeatures,
3087 table_index: TableIndex,
3088 ty_index: TypeIndex,
3089 sig_ref: ir::SigRef,
3090 callee: ir::Value,
3091 call_args: &[ir::Value],
3092 ) -> WasmResult<Option<CallRets>> {
3093 Call::new(builder, self, srcloc).indirect_call(
3094 features,
3095 table_index,
3096 ty_index,
3097 sig_ref,
3098 callee,
3099 call_args,
3100 )
3101 }
3102
translate_call<'a>( &mut self, builder: &'a mut FunctionBuilder, srcloc: ir::SourceLoc, callee_index: FuncIndex, sig_ref: ir::SigRef, call_args: &[ir::Value], ) -> WasmResult<CallRets>3103 pub fn translate_call<'a>(
3104 &mut self,
3105 builder: &'a mut FunctionBuilder,
3106 srcloc: ir::SourceLoc,
3107 callee_index: FuncIndex,
3108 sig_ref: ir::SigRef,
3109 call_args: &[ir::Value],
3110 ) -> WasmResult<CallRets> {
3111 Call::new(builder, self, srcloc).direct_call(callee_index, sig_ref, call_args)
3112 }
3113
translate_call_ref<'a>( &mut self, builder: &'a mut FunctionBuilder, srcloc: ir::SourceLoc, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], ) -> WasmResult<CallRets>3114 pub fn translate_call_ref<'a>(
3115 &mut self,
3116 builder: &'a mut FunctionBuilder,
3117 srcloc: ir::SourceLoc,
3118 sig_ref: ir::SigRef,
3119 callee: ir::Value,
3120 call_args: &[ir::Value],
3121 ) -> WasmResult<CallRets> {
3122 Call::new(builder, self, srcloc).call_ref(sig_ref, callee, call_args)
3123 }
3124
translate_return_call( &mut self, builder: &mut FunctionBuilder, srcloc: ir::SourceLoc, callee_index: FuncIndex, sig_ref: ir::SigRef, call_args: &[ir::Value], ) -> WasmResult<()>3125 pub fn translate_return_call(
3126 &mut self,
3127 builder: &mut FunctionBuilder,
3128 srcloc: ir::SourceLoc,
3129 callee_index: FuncIndex,
3130 sig_ref: ir::SigRef,
3131 call_args: &[ir::Value],
3132 ) -> WasmResult<()> {
3133 Call::new_tail(builder, self, srcloc).direct_call(callee_index, sig_ref, call_args)?;
3134 Ok(())
3135 }
3136
translate_return_call_indirect( &mut self, builder: &mut FunctionBuilder, srcloc: ir::SourceLoc, features: &WasmFeatures, table_index: TableIndex, ty_index: TypeIndex, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], ) -> WasmResult<()>3137 pub fn translate_return_call_indirect(
3138 &mut self,
3139 builder: &mut FunctionBuilder,
3140 srcloc: ir::SourceLoc,
3141 features: &WasmFeatures,
3142 table_index: TableIndex,
3143 ty_index: TypeIndex,
3144 sig_ref: ir::SigRef,
3145 callee: ir::Value,
3146 call_args: &[ir::Value],
3147 ) -> WasmResult<()> {
3148 Call::new_tail(builder, self, srcloc).indirect_call(
3149 features,
3150 table_index,
3151 ty_index,
3152 sig_ref,
3153 callee,
3154 call_args,
3155 )?;
3156 Ok(())
3157 }
3158
translate_return_call_ref( &mut self, builder: &mut FunctionBuilder, srcloc: ir::SourceLoc, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], ) -> WasmResult<()>3159 pub fn translate_return_call_ref(
3160 &mut self,
3161 builder: &mut FunctionBuilder,
3162 srcloc: ir::SourceLoc,
3163 sig_ref: ir::SigRef,
3164 callee: ir::Value,
3165 call_args: &[ir::Value],
3166 ) -> WasmResult<()> {
3167 Call::new_tail(builder, self, srcloc).call_ref(sig_ref, callee, call_args)?;
3168 Ok(())
3169 }
3170
3171 /// Returns two `ir::Value`s, the first of which is the vmctx for the memory
3172 /// `index` and the second of which is the `DefinedMemoryIndex` for `index`.
3173 ///
3174 /// Handles internally whether `index` is an imported memory or not.
memory_vmctx_and_defined_index( &mut self, pos: &mut FuncCursor, index: MemoryIndex, ) -> (ir::Value, ir::Value)3175 fn memory_vmctx_and_defined_index(
3176 &mut self,
3177 pos: &mut FuncCursor,
3178 index: MemoryIndex,
3179 ) -> (ir::Value, ir::Value) {
3180 let cur_vmctx = self.vmctx_val(pos);
3181 match self.module.defined_memory_index(index) {
3182 // This is a defined memory, so the vmctx is our own and the defined
3183 // index is `index` here.
3184 Some(index) => (cur_vmctx, pos.ins().iconst(I32, i64::from(index.as_u32()))),
3185
3186 // This is an imported memory, so load the vmctx/defined index from
3187 // the import definition itself.
3188 None => {
3189 let vmimport = self.offsets.vmctx_vmmemory_import(index);
3190
3191 let vmctx = pos.ins().load(
3192 self.isa.pointer_type(),
3193 ir::MemFlags::trusted(),
3194 cur_vmctx,
3195 i32::try_from(vmimport + u32::from(self.offsets.vmmemory_import_vmctx()))
3196 .unwrap(),
3197 );
3198 let index = pos.ins().load(
3199 ir::types::I32,
3200 ir::MemFlags::trusted(),
3201 cur_vmctx,
3202 i32::try_from(vmimport + u32::from(self.offsets.vmmemory_import_index()))
3203 .unwrap(),
3204 );
3205 (vmctx, index)
3206 }
3207 }
3208 }
3209
3210 /// Returns two `ir::Value`s, the first of which is the vmctx for the table
3211 /// `index` and the second of which is the `DefinedTableIndex` for `index`.
3212 ///
3213 /// Handles internally whether `index` is an imported table or not.
table_vmctx_and_defined_index( &mut self, pos: &mut FuncCursor, index: TableIndex, ) -> (ir::Value, ir::Value)3214 fn table_vmctx_and_defined_index(
3215 &mut self,
3216 pos: &mut FuncCursor,
3217 index: TableIndex,
3218 ) -> (ir::Value, ir::Value) {
3219 // NB: the body of this method is similar to
3220 // `memory_vmctx_and_defined_index` above.
3221 let cur_vmctx = self.vmctx_val(pos);
3222 match self.module.defined_table_index(index) {
3223 Some(index) => (cur_vmctx, pos.ins().iconst(I32, i64::from(index.as_u32()))),
3224 None => {
3225 let vmimport = self.offsets.vmctx_vmtable_import(index);
3226
3227 let vmctx = pos.ins().load(
3228 self.isa.pointer_type(),
3229 ir::MemFlags::trusted(),
3230 cur_vmctx,
3231 i32::try_from(vmimport + u32::from(self.offsets.vmtable_import_vmctx()))
3232 .unwrap(),
3233 );
3234 let index = pos.ins().load(
3235 ir::types::I32,
3236 ir::MemFlags::trusted(),
3237 cur_vmctx,
3238 i32::try_from(vmimport + u32::from(self.offsets.vmtable_import_index()))
3239 .unwrap(),
3240 );
3241 (vmctx, index)
3242 }
3243 }
3244 }
3245
translate_memory_grow( &mut self, builder: &mut FunctionBuilder<'_>, index: MemoryIndex, val: ir::Value, ) -> WasmResult<ir::Value>3246 pub fn translate_memory_grow(
3247 &mut self,
3248 builder: &mut FunctionBuilder<'_>,
3249 index: MemoryIndex,
3250 val: ir::Value,
3251 ) -> WasmResult<ir::Value> {
3252 let mut pos = builder.cursor();
3253 let memory_grow = self.builtin_functions.memory_grow(&mut pos.func);
3254
3255 let (memory_vmctx, defined_memory_index) =
3256 self.memory_vmctx_and_defined_index(&mut pos, index);
3257
3258 let index_type = self.memory(index).idx_type;
3259 let val = self.cast_index_to_i64(&mut pos, val, index_type);
3260 let call_inst = pos
3261 .ins()
3262 .call(memory_grow, &[memory_vmctx, val, defined_memory_index]);
3263 let result = *pos.func.dfg.inst_results(call_inst).first().unwrap();
3264 let single_byte_pages = match self.memory(index).page_size_log2 {
3265 16 => false,
3266 0 => true,
3267 _ => unreachable!("only page sizes 2**0 and 2**16 are currently valid"),
3268 };
3269 Ok(self.convert_pointer_to_index_type(
3270 builder.cursor(),
3271 result,
3272 index_type,
3273 single_byte_pages,
3274 ))
3275 }
3276
translate_memory_size( &mut self, mut pos: FuncCursor<'_>, index: MemoryIndex, ) -> WasmResult<ir::Value>3277 pub fn translate_memory_size(
3278 &mut self,
3279 mut pos: FuncCursor<'_>,
3280 index: MemoryIndex,
3281 ) -> WasmResult<ir::Value> {
3282 let pointer_type = self.pointer_type();
3283 let vmctx = self.vmctx(&mut pos.func);
3284 let is_shared = self.module.memories[index].shared;
3285 let base = pos.ins().global_value(pointer_type, vmctx);
3286 let current_length_in_bytes = match self.module.defined_memory_index(index) {
3287 Some(def_index) => {
3288 if is_shared {
3289 let offset =
3290 i32::try_from(self.offsets.vmctx_vmmemory_pointer(def_index)).unwrap();
3291 let vmmemory_ptr =
3292 pos.ins()
3293 .load(pointer_type, ir::MemFlags::trusted(), base, offset);
3294 let vmmemory_definition_offset =
3295 i64::from(self.offsets.ptr.vmmemory_definition_current_length());
3296 let vmmemory_definition_ptr =
3297 pos.ins().iadd_imm(vmmemory_ptr, vmmemory_definition_offset);
3298 // This atomic access of the
3299 // `VMMemoryDefinition::current_length` is direct; no bounds
3300 // check is needed. This is possible because shared memory
3301 // has a static size (the maximum is always known). Shared
3302 // memory is thus built with a static memory plan and no
3303 // bounds-checked version of this is implemented.
3304 pos.ins().atomic_load(
3305 pointer_type,
3306 ir::MemFlags::trusted(),
3307 vmmemory_definition_ptr,
3308 )
3309 } else {
3310 let owned_index = self.module.owned_memory_index(def_index);
3311 let offset = i32::try_from(
3312 self.offsets
3313 .vmctx_vmmemory_definition_current_length(owned_index),
3314 )
3315 .unwrap();
3316 pos.ins()
3317 .load(pointer_type, ir::MemFlags::trusted(), base, offset)
3318 }
3319 }
3320 None => {
3321 let offset = i32::try_from(self.offsets.vmctx_vmmemory_import_from(index)).unwrap();
3322 let vmmemory_ptr =
3323 pos.ins()
3324 .load(pointer_type, ir::MemFlags::trusted(), base, offset);
3325 if is_shared {
3326 let vmmemory_definition_offset =
3327 i64::from(self.offsets.ptr.vmmemory_definition_current_length());
3328 let vmmemory_definition_ptr =
3329 pos.ins().iadd_imm(vmmemory_ptr, vmmemory_definition_offset);
3330 pos.ins().atomic_load(
3331 pointer_type,
3332 ir::MemFlags::trusted(),
3333 vmmemory_definition_ptr,
3334 )
3335 } else {
3336 pos.ins().load(
3337 pointer_type,
3338 ir::MemFlags::trusted(),
3339 vmmemory_ptr,
3340 i32::from(self.offsets.ptr.vmmemory_definition_current_length()),
3341 )
3342 }
3343 }
3344 };
3345
3346 let page_size_log2 = i64::from(self.module.memories[index].page_size_log2);
3347 let current_length_in_pages = pos.ins().ushr_imm(current_length_in_bytes, page_size_log2);
3348 let single_byte_pages = match page_size_log2 {
3349 16 => false,
3350 0 => true,
3351 _ => unreachable!("only page sizes 2**0 and 2**16 are currently valid"),
3352 };
3353 Ok(self.convert_pointer_to_index_type(
3354 pos,
3355 current_length_in_pages,
3356 self.memory(index).idx_type,
3357 single_byte_pages,
3358 ))
3359 }
3360
translate_memory_copy( &mut self, builder: &mut FunctionBuilder<'_>, src_index: MemoryIndex, dst_index: MemoryIndex, dst: ir::Value, src: ir::Value, len: ir::Value, ) -> WasmResult<()>3361 pub fn translate_memory_copy(
3362 &mut self,
3363 builder: &mut FunctionBuilder<'_>,
3364 src_index: MemoryIndex,
3365 dst_index: MemoryIndex,
3366 dst: ir::Value,
3367 src: ir::Value,
3368 len: ir::Value,
3369 ) -> WasmResult<()> {
3370 let mut pos = builder.cursor();
3371 let vmctx = self.vmctx_val(&mut pos);
3372
3373 let memory_copy = self.builtin_functions.memory_copy(&mut pos.func);
3374 let dst = self.cast_index_to_i64(&mut pos, dst, self.memory(dst_index).idx_type);
3375 let src = self.cast_index_to_i64(&mut pos, src, self.memory(src_index).idx_type);
3376 // The length is 32-bit if either memory is 32-bit, but if they're both
3377 // 64-bit then it's 64-bit. Our intrinsic takes a 64-bit length for
3378 // compatibility across all memories, so make sure that it's cast
3379 // correctly here (this is a bit special so no generic helper unlike for
3380 // `dst`/`src` above)
3381 let len = if index_type_to_ir_type(self.memory(dst_index).idx_type) == I64
3382 && index_type_to_ir_type(self.memory(src_index).idx_type) == I64
3383 {
3384 len
3385 } else {
3386 pos.ins().uextend(I64, len)
3387 };
3388 let src_index = pos.ins().iconst(I32, i64::from(src_index.as_u32()));
3389 let dst_index = pos.ins().iconst(I32, i64::from(dst_index.as_u32()));
3390 pos.ins()
3391 .call(memory_copy, &[vmctx, dst_index, dst, src_index, src, len]);
3392
3393 Ok(())
3394 }
3395
translate_memory_fill( &mut self, builder: &mut FunctionBuilder<'_>, memory_index: MemoryIndex, dst: ir::Value, val: ir::Value, len: ir::Value, ) -> WasmResult<()>3396 pub fn translate_memory_fill(
3397 &mut self,
3398 builder: &mut FunctionBuilder<'_>,
3399 memory_index: MemoryIndex,
3400 dst: ir::Value,
3401 val: ir::Value,
3402 len: ir::Value,
3403 ) -> WasmResult<()> {
3404 let mut pos = builder.cursor();
3405 let memory_fill = self.builtin_functions.memory_fill(&mut pos.func);
3406 let dst = self.cast_index_to_i64(&mut pos, dst, self.memory(memory_index).idx_type);
3407 let len = self.cast_index_to_i64(&mut pos, len, self.memory(memory_index).idx_type);
3408 let (memory_vmctx, defined_memory_index) =
3409 self.memory_vmctx_and_defined_index(&mut pos, memory_index);
3410
3411 pos.ins().call(
3412 memory_fill,
3413 &[memory_vmctx, defined_memory_index, dst, val, len],
3414 );
3415
3416 Ok(())
3417 }
3418
translate_memory_init( &mut self, builder: &mut FunctionBuilder<'_>, memory_index: MemoryIndex, seg_index: u32, dst: ir::Value, src: ir::Value, len: ir::Value, ) -> WasmResult<()>3419 pub fn translate_memory_init(
3420 &mut self,
3421 builder: &mut FunctionBuilder<'_>,
3422 memory_index: MemoryIndex,
3423 seg_index: u32,
3424 dst: ir::Value,
3425 src: ir::Value,
3426 len: ir::Value,
3427 ) -> WasmResult<()> {
3428 let mut pos = builder.cursor();
3429 let memory_init = self.builtin_functions.memory_init(&mut pos.func);
3430
3431 let memory_index_arg = pos.ins().iconst(I32, memory_index.index() as i64);
3432 let seg_index_arg = pos.ins().iconst(I32, seg_index as i64);
3433
3434 let vmctx = self.vmctx_val(&mut pos);
3435
3436 let dst = self.cast_index_to_i64(&mut pos, dst, self.memory(memory_index).idx_type);
3437
3438 pos.ins().call(
3439 memory_init,
3440 &[vmctx, memory_index_arg, seg_index_arg, dst, src, len],
3441 );
3442
3443 Ok(())
3444 }
3445
translate_data_drop(&mut self, mut pos: FuncCursor, seg_index: u32) -> WasmResult<()>3446 pub fn translate_data_drop(&mut self, mut pos: FuncCursor, seg_index: u32) -> WasmResult<()> {
3447 let data_drop = self.builtin_functions.data_drop(&mut pos.func);
3448 let seg_index_arg = pos.ins().iconst(I32, seg_index as i64);
3449 let vmctx = self.vmctx_val(&mut pos);
3450 pos.ins().call(data_drop, &[vmctx, seg_index_arg]);
3451 Ok(())
3452 }
3453
translate_table_size( &mut self, pos: FuncCursor, table_index: TableIndex, ) -> WasmResult<ir::Value>3454 pub fn translate_table_size(
3455 &mut self,
3456 pos: FuncCursor,
3457 table_index: TableIndex,
3458 ) -> WasmResult<ir::Value> {
3459 let table_data = self.get_or_create_table(pos.func, table_index);
3460 let index_type = index_type_to_ir_type(self.table(table_index).idx_type);
3461 Ok(table_data.bound.bound(&*self.isa, pos, index_type))
3462 }
3463
translate_table_copy( &mut self, builder: &mut FunctionBuilder<'_>, dst_table_index: TableIndex, src_table_index: TableIndex, dst: ir::Value, src: ir::Value, len: ir::Value, ) -> WasmResult<()>3464 pub fn translate_table_copy(
3465 &mut self,
3466 builder: &mut FunctionBuilder<'_>,
3467 dst_table_index: TableIndex,
3468 src_table_index: TableIndex,
3469 dst: ir::Value,
3470 src: ir::Value,
3471 len: ir::Value,
3472 ) -> WasmResult<()> {
3473 let (table_copy, dst_table_index_arg, src_table_index_arg) =
3474 self.get_table_copy_func(&mut builder.func, dst_table_index, src_table_index);
3475
3476 let mut pos = builder.cursor();
3477 let dst = self.cast_index_to_i64(&mut pos, dst, self.table(dst_table_index).idx_type);
3478 let src = self.cast_index_to_i64(&mut pos, src, self.table(src_table_index).idx_type);
3479 let len = if index_type_to_ir_type(self.table(dst_table_index).idx_type) == I64
3480 && index_type_to_ir_type(self.table(src_table_index).idx_type) == I64
3481 {
3482 len
3483 } else {
3484 pos.ins().uextend(I64, len)
3485 };
3486 let dst_table_index_arg = pos.ins().iconst(I32, dst_table_index_arg as i64);
3487 let src_table_index_arg = pos.ins().iconst(I32, src_table_index_arg as i64);
3488 let vmctx = self.vmctx_val(&mut pos);
3489 pos.ins().call(
3490 table_copy,
3491 &[
3492 vmctx,
3493 dst_table_index_arg,
3494 src_table_index_arg,
3495 dst,
3496 src,
3497 len,
3498 ],
3499 );
3500
3501 Ok(())
3502 }
3503
translate_table_init( &mut self, builder: &mut FunctionBuilder<'_>, seg_index: u32, table_index: TableIndex, dst: ir::Value, src: ir::Value, len: ir::Value, ) -> WasmResult<()>3504 pub fn translate_table_init(
3505 &mut self,
3506 builder: &mut FunctionBuilder<'_>,
3507 seg_index: u32,
3508 table_index: TableIndex,
3509 dst: ir::Value,
3510 src: ir::Value,
3511 len: ir::Value,
3512 ) -> WasmResult<()> {
3513 let mut pos = builder.cursor();
3514 let table_init = self.builtin_functions.table_init(&mut pos.func);
3515 let table_index_arg = pos.ins().iconst(I32, i64::from(table_index.as_u32()));
3516 let seg_index_arg = pos.ins().iconst(I32, i64::from(seg_index));
3517 let vmctx = self.vmctx_val(&mut pos);
3518 let index_type = self.table(table_index).idx_type;
3519 let dst = self.cast_index_to_i64(&mut pos, dst, index_type);
3520 let src = pos.ins().uextend(I64, src);
3521 let len = pos.ins().uextend(I64, len);
3522
3523 pos.ins().call(
3524 table_init,
3525 &[vmctx, table_index_arg, seg_index_arg, dst, src, len],
3526 );
3527
3528 Ok(())
3529 }
3530
translate_elem_drop(&mut self, mut pos: FuncCursor, elem_index: u32) -> WasmResult<()>3531 pub fn translate_elem_drop(&mut self, mut pos: FuncCursor, elem_index: u32) -> WasmResult<()> {
3532 let elem_drop = self.builtin_functions.elem_drop(&mut pos.func);
3533 let elem_index_arg = pos.ins().iconst(I32, elem_index as i64);
3534 let vmctx = self.vmctx_val(&mut pos);
3535 pos.ins().call(elem_drop, &[vmctx, elem_index_arg]);
3536 Ok(())
3537 }
3538
translate_atomic_wait( &mut self, builder: &mut FunctionBuilder<'_>, memory_index: MemoryIndex, _heap: Heap, addr: ir::Value, expected: ir::Value, timeout: ir::Value, ) -> WasmResult<ir::Value>3539 pub fn translate_atomic_wait(
3540 &mut self,
3541 builder: &mut FunctionBuilder<'_>,
3542 memory_index: MemoryIndex,
3543 _heap: Heap,
3544 addr: ir::Value,
3545 expected: ir::Value,
3546 timeout: ir::Value,
3547 ) -> WasmResult<ir::Value> {
3548 #[cfg(feature = "threads")]
3549 {
3550 let mut pos = builder.cursor();
3551 let addr = self.cast_index_to_i64(&mut pos, addr, self.memory(memory_index).idx_type);
3552 let implied_ty = pos.func.dfg.value_type(expected);
3553 let wait_func = self.get_memory_atomic_wait(&mut pos.func, implied_ty);
3554
3555 let (memory_vmctx, defined_memory_index) =
3556 self.memory_vmctx_and_defined_index(&mut pos, memory_index);
3557
3558 let call_inst = pos.ins().call(
3559 wait_func,
3560 &[memory_vmctx, defined_memory_index, addr, expected, timeout],
3561 );
3562 let ret = pos.func.dfg.inst_results(call_inst)[0];
3563 Ok(builder.ins().ireduce(ir::types::I32, ret))
3564 }
3565 #[cfg(not(feature = "threads"))]
3566 {
3567 let _ = (builder, memory_index, addr, expected, timeout);
3568 Err(wasmtime_environ::WasmError::Unsupported(
3569 "threads support disabled at compile time".to_string(),
3570 ))
3571 }
3572 }
3573
translate_atomic_notify( &mut self, builder: &mut FunctionBuilder<'_>, memory_index: MemoryIndex, _heap: Heap, addr: ir::Value, count: ir::Value, ) -> WasmResult<ir::Value>3574 pub fn translate_atomic_notify(
3575 &mut self,
3576 builder: &mut FunctionBuilder<'_>,
3577 memory_index: MemoryIndex,
3578 _heap: Heap,
3579 addr: ir::Value,
3580 count: ir::Value,
3581 ) -> WasmResult<ir::Value> {
3582 #[cfg(feature = "threads")]
3583 {
3584 let mut pos = builder.cursor();
3585 let addr = self.cast_index_to_i64(&mut pos, addr, self.memory(memory_index).idx_type);
3586 let atomic_notify = self.builtin_functions.memory_atomic_notify(&mut pos.func);
3587
3588 let (memory_vmctx, defined_memory_index) =
3589 self.memory_vmctx_and_defined_index(&mut pos, memory_index);
3590 let call_inst = pos.ins().call(
3591 atomic_notify,
3592 &[memory_vmctx, defined_memory_index, addr, count],
3593 );
3594 let ret = pos.func.dfg.inst_results(call_inst)[0];
3595 Ok(builder.ins().ireduce(ir::types::I32, ret))
3596 }
3597 #[cfg(not(feature = "threads"))]
3598 {
3599 let _ = (builder, memory_index, addr, count);
3600 Err(wasmtime_environ::WasmError::Unsupported(
3601 "threads support disabled at compile time".to_string(),
3602 ))
3603 }
3604 }
3605
translate_loop_header(&mut self, builder: &mut FunctionBuilder) -> WasmResult<()>3606 pub fn translate_loop_header(&mut self, builder: &mut FunctionBuilder) -> WasmResult<()> {
3607 // Additionally if enabled check how much fuel we have remaining to see
3608 // if we've run out by this point.
3609 if self.tunables.consume_fuel {
3610 self.fuel_check(builder);
3611 }
3612
3613 // If we are performing epoch-based interruption, check to see
3614 // if the epoch counter has changed.
3615 if self.tunables.epoch_interruption {
3616 self.epoch_check(builder);
3617 }
3618
3619 Ok(())
3620 }
3621
before_translate_operator( &mut self, op: &Operator, _operand_types: Option<&[WasmValType]>, builder: &mut FunctionBuilder, ) -> WasmResult<()>3622 pub fn before_translate_operator(
3623 &mut self,
3624 op: &Operator,
3625 _operand_types: Option<&[WasmValType]>,
3626 builder: &mut FunctionBuilder,
3627 ) -> WasmResult<()> {
3628 if self.tunables.consume_fuel {
3629 self.fuel_before_op(op, builder, self.is_reachable());
3630 }
3631 if self.is_reachable() && self.state_slot.is_some() {
3632 let builtin = self.builtin_functions.patchable_breakpoint(builder.func);
3633 let vmctx = self.vmctx_val(&mut builder.cursor());
3634 let inst = builder.ins().call(builtin, &[vmctx]);
3635 let tags = self.debug_tags(builder.srcloc());
3636 builder.func.debug_tags.set(inst, tags);
3637 }
3638
3639 Ok(())
3640 }
3641
after_translate_operator( &mut self, op: &Operator, validator: &FuncValidator<impl WasmModuleResources>, builder: &mut FunctionBuilder, ) -> WasmResult<()>3642 pub fn after_translate_operator(
3643 &mut self,
3644 op: &Operator,
3645 validator: &FuncValidator<impl WasmModuleResources>,
3646 builder: &mut FunctionBuilder,
3647 ) -> WasmResult<()> {
3648 if self.tunables.consume_fuel && self.is_reachable() {
3649 self.fuel_after_op(op, builder);
3650 }
3651 if self.is_reachable() {
3652 self.update_state_slot_stack(validator, builder)?;
3653 }
3654 Ok(())
3655 }
3656
before_unconditionally_trapping_memory_access(&mut self, builder: &mut FunctionBuilder)3657 pub fn before_unconditionally_trapping_memory_access(&mut self, builder: &mut FunctionBuilder) {
3658 if self.tunables.consume_fuel {
3659 self.fuel_increment_var(builder);
3660 self.fuel_save_from_var(builder);
3661 }
3662 }
3663
before_translate_function(&mut self, builder: &mut FunctionBuilder) -> WasmResult<()>3664 pub fn before_translate_function(&mut self, builder: &mut FunctionBuilder) -> WasmResult<()> {
3665 // If an explicit stack limit is requested, emit one here at the start
3666 // of the function.
3667 if let Some(gv) = self.stack_limit_at_function_entry {
3668 let limit = builder.ins().global_value(self.pointer_type(), gv);
3669 let sp = builder.ins().get_stack_pointer(self.pointer_type());
3670 let overflow = builder.ins().icmp(IntCC::UnsignedLessThan, sp, limit);
3671 self.conditionally_trap(builder, overflow, ir::TrapCode::STACK_OVERFLOW);
3672 }
3673
3674 self.update_state_slot_vmctx(builder);
3675
3676 // Additionally we initialize `fuel_var` if it will get used.
3677 if self.tunables.consume_fuel {
3678 self.fuel_function_entry(builder);
3679 }
3680
3681 // Initialize `epoch_var` with the current epoch.
3682 if self.tunables.epoch_interruption {
3683 self.epoch_function_entry(builder);
3684 }
3685
3686 #[cfg(feature = "wmemcheck")]
3687 if self.compiler.wmemcheck {
3688 let func_name = self.current_func_name(builder);
3689 if func_name == Some("malloc") {
3690 self.check_malloc_start(builder);
3691 } else if func_name == Some("free") {
3692 self.check_free_start(builder);
3693 }
3694 }
3695
3696 Ok(())
3697 }
3698
after_translate_function(&mut self, builder: &mut FunctionBuilder) -> WasmResult<()>3699 pub fn after_translate_function(&mut self, builder: &mut FunctionBuilder) -> WasmResult<()> {
3700 if self.tunables.consume_fuel && self.is_reachable() {
3701 self.fuel_function_exit(builder);
3702 }
3703 self.finish_debug_metadata(builder);
3704 Ok(())
3705 }
3706
relaxed_simd_deterministic(&self) -> bool3707 pub fn relaxed_simd_deterministic(&self) -> bool {
3708 self.tunables.relaxed_simd_deterministic
3709 }
3710
has_native_fma(&self) -> bool3711 pub fn has_native_fma(&self) -> bool {
3712 self.isa.has_native_fma()
3713 }
3714
is_x86(&self) -> bool3715 pub fn is_x86(&self) -> bool {
3716 self.isa.triple().architecture == target_lexicon::Architecture::X86_64
3717 }
3718
translate_cont_bind( &mut self, builder: &mut FunctionBuilder<'_>, contobj: ir::Value, args: &[ir::Value], ) -> ir::Value3719 pub fn translate_cont_bind(
3720 &mut self,
3721 builder: &mut FunctionBuilder<'_>,
3722 contobj: ir::Value,
3723 args: &[ir::Value],
3724 ) -> ir::Value {
3725 stack_switching::instructions::translate_cont_bind(self, builder, contobj, args)
3726 }
3727
translate_cont_new( &mut self, builder: &mut FunctionBuilder<'_>, func: ir::Value, arg_types: &[WasmValType], return_types: &[WasmValType], ) -> WasmResult<ir::Value>3728 pub fn translate_cont_new(
3729 &mut self,
3730 builder: &mut FunctionBuilder<'_>,
3731 func: ir::Value,
3732 arg_types: &[WasmValType],
3733 return_types: &[WasmValType],
3734 ) -> WasmResult<ir::Value> {
3735 stack_switching::instructions::translate_cont_new(
3736 self,
3737 builder,
3738 func,
3739 arg_types,
3740 return_types,
3741 )
3742 }
3743
translate_resume( &mut self, builder: &mut FunctionBuilder<'_>, type_index: u32, contobj: ir::Value, resume_args: &[ir::Value], resumetable: &[(u32, Option<ir::Block>)], ) -> WasmResult<Vec<ir::Value>>3744 pub fn translate_resume(
3745 &mut self,
3746 builder: &mut FunctionBuilder<'_>,
3747 type_index: u32,
3748 contobj: ir::Value,
3749 resume_args: &[ir::Value],
3750 resumetable: &[(u32, Option<ir::Block>)],
3751 ) -> WasmResult<Vec<ir::Value>> {
3752 stack_switching::instructions::translate_resume(
3753 self,
3754 builder,
3755 type_index,
3756 contobj,
3757 resume_args,
3758 resumetable,
3759 )
3760 }
3761
translate_suspend( &mut self, builder: &mut FunctionBuilder<'_>, tag_index: u32, suspend_args: &[ir::Value], tag_return_types: &[ir::Type], ) -> Vec<ir::Value>3762 pub fn translate_suspend(
3763 &mut self,
3764 builder: &mut FunctionBuilder<'_>,
3765 tag_index: u32,
3766 suspend_args: &[ir::Value],
3767 tag_return_types: &[ir::Type],
3768 ) -> Vec<ir::Value> {
3769 stack_switching::instructions::translate_suspend(
3770 self,
3771 builder,
3772 tag_index,
3773 suspend_args,
3774 tag_return_types,
3775 )
3776 }
3777
3778 /// Translates switch instructions.
translate_switch( &mut self, builder: &mut FunctionBuilder, tag_index: u32, contobj: ir::Value, switch_args: &[ir::Value], return_types: &[ir::Type], ) -> WasmResult<Vec<ir::Value>>3779 pub fn translate_switch(
3780 &mut self,
3781 builder: &mut FunctionBuilder,
3782 tag_index: u32,
3783 contobj: ir::Value,
3784 switch_args: &[ir::Value],
3785 return_types: &[ir::Type],
3786 ) -> WasmResult<Vec<ir::Value>> {
3787 stack_switching::instructions::translate_switch(
3788 self,
3789 builder,
3790 tag_index,
3791 contobj,
3792 switch_args,
3793 return_types,
3794 )
3795 }
3796
continuation_arguments(&self, index: TypeIndex) -> &[WasmValType]3797 pub fn continuation_arguments(&self, index: TypeIndex) -> &[WasmValType] {
3798 let idx = self.module.types[index].unwrap_module_type_index();
3799 self.types[self.types[idx].unwrap_cont().unwrap_module_type_index()]
3800 .unwrap_func()
3801 .params()
3802 }
3803
continuation_returns(&self, index: TypeIndex) -> &[WasmValType]3804 pub fn continuation_returns(&self, index: TypeIndex) -> &[WasmValType] {
3805 let idx = self.module.types[index].unwrap_module_type_index();
3806 self.types[self.types[idx].unwrap_cont().unwrap_module_type_index()]
3807 .unwrap_func()
3808 .results()
3809 }
3810
tag_params(&self, tag_index: TagIndex) -> &[WasmValType]3811 pub fn tag_params(&self, tag_index: TagIndex) -> &[WasmValType] {
3812 let idx = self.module.tags[tag_index].signature;
3813 self.types[idx.unwrap_module_type_index()]
3814 .unwrap_func()
3815 .params()
3816 }
3817
tag_returns(&self, tag_index: TagIndex) -> &[WasmValType]3818 pub fn tag_returns(&self, tag_index: TagIndex) -> &[WasmValType] {
3819 let idx = self.module.tags[tag_index].signature;
3820 self.types[idx.unwrap_module_type_index()]
3821 .unwrap_func()
3822 .results()
3823 }
3824
use_blendv_for_relaxed_laneselect(&self, ty: Type) -> bool3825 pub fn use_blendv_for_relaxed_laneselect(&self, ty: Type) -> bool {
3826 self.isa.has_blendv_lowering(ty)
3827 }
3828
use_x86_pmulhrsw_for_relaxed_q15mul(&self) -> bool3829 pub fn use_x86_pmulhrsw_for_relaxed_q15mul(&self) -> bool {
3830 self.isa.has_x86_pmulhrsw_lowering()
3831 }
3832
use_x86_pmaddubsw_for_dot(&self) -> bool3833 pub fn use_x86_pmaddubsw_for_dot(&self) -> bool {
3834 self.isa.has_x86_pmaddubsw_lowering()
3835 }
3836
handle_before_return(&mut self, retvals: &[ir::Value], builder: &mut FunctionBuilder)3837 pub fn handle_before_return(&mut self, retvals: &[ir::Value], builder: &mut FunctionBuilder) {
3838 #[cfg(feature = "wmemcheck")]
3839 if self.compiler.wmemcheck {
3840 let func_name = self.current_func_name(builder);
3841 if func_name == Some("malloc") {
3842 self.hook_malloc_exit(builder, retvals);
3843 } else if func_name == Some("free") {
3844 self.hook_free_exit(builder);
3845 }
3846 }
3847 #[cfg(not(feature = "wmemcheck"))]
3848 let _ = (retvals, builder);
3849 }
3850
before_load( &mut self, builder: &mut FunctionBuilder, val_size: u8, addr: ir::Value, offset: u64, )3851 pub fn before_load(
3852 &mut self,
3853 builder: &mut FunctionBuilder,
3854 val_size: u8,
3855 addr: ir::Value,
3856 offset: u64,
3857 ) {
3858 #[cfg(feature = "wmemcheck")]
3859 if self.compiler.wmemcheck {
3860 let check_load = self.builtin_functions.check_load(builder.func);
3861 let vmctx = self.vmctx_val(&mut builder.cursor());
3862 let num_bytes = builder.ins().iconst(I32, val_size as i64);
3863 let offset_val = builder.ins().iconst(I64, offset as i64);
3864 builder
3865 .ins()
3866 .call(check_load, &[vmctx, num_bytes, addr, offset_val]);
3867 }
3868 #[cfg(not(feature = "wmemcheck"))]
3869 let _ = (builder, val_size, addr, offset);
3870 }
3871
before_store( &mut self, builder: &mut FunctionBuilder, val_size: u8, addr: ir::Value, offset: u64, )3872 pub fn before_store(
3873 &mut self,
3874 builder: &mut FunctionBuilder,
3875 val_size: u8,
3876 addr: ir::Value,
3877 offset: u64,
3878 ) {
3879 #[cfg(feature = "wmemcheck")]
3880 if self.compiler.wmemcheck {
3881 let check_store = self.builtin_functions.check_store(builder.func);
3882 let vmctx = self.vmctx_val(&mut builder.cursor());
3883 let num_bytes = builder.ins().iconst(I32, val_size as i64);
3884 let offset_val = builder.ins().iconst(I64, offset as i64);
3885 builder
3886 .ins()
3887 .call(check_store, &[vmctx, num_bytes, addr, offset_val]);
3888 }
3889 #[cfg(not(feature = "wmemcheck"))]
3890 let _ = (builder, val_size, addr, offset);
3891 }
3892
update_global( &mut self, builder: &mut FunctionBuilder, global_index: GlobalIndex, value: ir::Value, )3893 pub fn update_global(
3894 &mut self,
3895 builder: &mut FunctionBuilder,
3896 global_index: GlobalIndex,
3897 value: ir::Value,
3898 ) {
3899 #[cfg(feature = "wmemcheck")]
3900 if self.compiler.wmemcheck {
3901 if global_index.index() == 0 {
3902 // We are making the assumption that global 0 is the auxiliary stack pointer.
3903 let update_stack_pointer =
3904 self.builtin_functions.update_stack_pointer(builder.func);
3905 let vmctx = self.vmctx_val(&mut builder.cursor());
3906 builder.ins().call(update_stack_pointer, &[vmctx, value]);
3907 }
3908 }
3909 #[cfg(not(feature = "wmemcheck"))]
3910 let _ = (builder, global_index, value);
3911 }
3912
before_memory_grow( &mut self, builder: &mut FunctionBuilder, num_pages: ir::Value, mem_index: MemoryIndex, )3913 pub fn before_memory_grow(
3914 &mut self,
3915 builder: &mut FunctionBuilder,
3916 num_pages: ir::Value,
3917 mem_index: MemoryIndex,
3918 ) {
3919 #[cfg(feature = "wmemcheck")]
3920 if self.compiler.wmemcheck && mem_index.as_u32() == 0 {
3921 let update_mem_size = self.builtin_functions.update_mem_size(builder.func);
3922 let vmctx = self.vmctx_val(&mut builder.cursor());
3923 builder.ins().call(update_mem_size, &[vmctx, num_pages]);
3924 }
3925 #[cfg(not(feature = "wmemcheck"))]
3926 let _ = (builder, num_pages, mem_index);
3927 }
3928
3929 /// If the ISA has rounding instructions, let Cranelift use them. But if
3930 /// not, lower to a libcall here, rather than having Cranelift do it. We
3931 /// can pass our libcall the vmctx pointer, which we use for stack
3932 /// overflow checking.
3933 ///
3934 /// This helper is generic for all rounding instructions below, both for
3935 /// scalar and simd types. The `clif_round` argument is the CLIF-level
3936 /// rounding instruction to use if the ISA has the instruction, and the
3937 /// `round_builtin` helper is used to determine which element-level
3938 /// rounding operation builtin is used. Note that this handles the case
3939 /// when `value` is a vector by doing an element-wise libcall invocation.
isa_round( &mut self, builder: &mut FunctionBuilder, value: ir::Value, clif_round: fn(FuncInstBuilder<'_, '_>, ir::Value) -> ir::Value, round_builtin: fn(&mut BuiltinFunctions, &mut Function) -> ir::FuncRef, ) -> ir::Value3940 fn isa_round(
3941 &mut self,
3942 builder: &mut FunctionBuilder,
3943 value: ir::Value,
3944 clif_round: fn(FuncInstBuilder<'_, '_>, ir::Value) -> ir::Value,
3945 round_builtin: fn(&mut BuiltinFunctions, &mut Function) -> ir::FuncRef,
3946 ) -> ir::Value {
3947 if self.isa.has_round() {
3948 return clif_round(builder.ins(), value);
3949 }
3950
3951 let vmctx = self.vmctx_val(&mut builder.cursor());
3952 let round = round_builtin(&mut self.builtin_functions, builder.func);
3953 let round_one = |builder: &mut FunctionBuilder, value: ir::Value| {
3954 let call = builder.ins().call(round, &[vmctx, value]);
3955 *builder.func.dfg.inst_results(call).first().unwrap()
3956 };
3957
3958 let ty = builder.func.dfg.value_type(value);
3959 if !ty.is_vector() {
3960 return round_one(builder, value);
3961 }
3962
3963 assert_eq!(ty.bits(), 128);
3964 let zero = builder.func.dfg.constants.insert(V128Imm([0; 16]).into());
3965 let mut result = builder.ins().vconst(ty, zero);
3966 for i in 0..u8::try_from(ty.lane_count()).unwrap() {
3967 let element = builder.ins().extractlane(value, i);
3968 let element_rounded = round_one(builder, element);
3969 result = builder.ins().insertlane(result, element_rounded, i);
3970 }
3971 result
3972 }
3973
ceil_f32(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value3974 pub fn ceil_f32(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
3975 self.isa_round(
3976 builder,
3977 value,
3978 |ins, val| ins.ceil(val),
3979 BuiltinFunctions::ceil_f32,
3980 )
3981 }
3982
ceil_f64(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value3983 pub fn ceil_f64(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
3984 self.isa_round(
3985 builder,
3986 value,
3987 |ins, val| ins.ceil(val),
3988 BuiltinFunctions::ceil_f64,
3989 )
3990 }
3991
ceil_f32x4(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value3992 pub fn ceil_f32x4(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
3993 self.isa_round(
3994 builder,
3995 value,
3996 |ins, val| ins.ceil(val),
3997 BuiltinFunctions::ceil_f32,
3998 )
3999 }
4000
ceil_f64x2(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4001 pub fn ceil_f64x2(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4002 self.isa_round(
4003 builder,
4004 value,
4005 |ins, val| ins.ceil(val),
4006 BuiltinFunctions::ceil_f64,
4007 )
4008 }
4009
floor_f32(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4010 pub fn floor_f32(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4011 self.isa_round(
4012 builder,
4013 value,
4014 |ins, val| ins.floor(val),
4015 BuiltinFunctions::floor_f32,
4016 )
4017 }
4018
floor_f64(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4019 pub fn floor_f64(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4020 self.isa_round(
4021 builder,
4022 value,
4023 |ins, val| ins.floor(val),
4024 BuiltinFunctions::floor_f64,
4025 )
4026 }
4027
floor_f32x4(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4028 pub fn floor_f32x4(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4029 self.isa_round(
4030 builder,
4031 value,
4032 |ins, val| ins.floor(val),
4033 BuiltinFunctions::floor_f32,
4034 )
4035 }
4036
floor_f64x2(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4037 pub fn floor_f64x2(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4038 self.isa_round(
4039 builder,
4040 value,
4041 |ins, val| ins.floor(val),
4042 BuiltinFunctions::floor_f64,
4043 )
4044 }
4045
trunc_f32(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4046 pub fn trunc_f32(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4047 self.isa_round(
4048 builder,
4049 value,
4050 |ins, val| ins.trunc(val),
4051 BuiltinFunctions::trunc_f32,
4052 )
4053 }
4054
trunc_f64(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4055 pub fn trunc_f64(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4056 self.isa_round(
4057 builder,
4058 value,
4059 |ins, val| ins.trunc(val),
4060 BuiltinFunctions::trunc_f64,
4061 )
4062 }
4063
trunc_f32x4(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4064 pub fn trunc_f32x4(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4065 self.isa_round(
4066 builder,
4067 value,
4068 |ins, val| ins.trunc(val),
4069 BuiltinFunctions::trunc_f32,
4070 )
4071 }
4072
trunc_f64x2(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4073 pub fn trunc_f64x2(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4074 self.isa_round(
4075 builder,
4076 value,
4077 |ins, val| ins.trunc(val),
4078 BuiltinFunctions::trunc_f64,
4079 )
4080 }
4081
nearest_f32(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4082 pub fn nearest_f32(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4083 self.isa_round(
4084 builder,
4085 value,
4086 |ins, val| ins.nearest(val),
4087 BuiltinFunctions::nearest_f32,
4088 )
4089 }
4090
nearest_f64(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4091 pub fn nearest_f64(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4092 self.isa_round(
4093 builder,
4094 value,
4095 |ins, val| ins.nearest(val),
4096 BuiltinFunctions::nearest_f64,
4097 )
4098 }
4099
nearest_f32x4(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4100 pub fn nearest_f32x4(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4101 self.isa_round(
4102 builder,
4103 value,
4104 |ins, val| ins.nearest(val),
4105 BuiltinFunctions::nearest_f32,
4106 )
4107 }
4108
nearest_f64x2(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value4109 pub fn nearest_f64x2(&mut self, builder: &mut FunctionBuilder, value: ir::Value) -> ir::Value {
4110 self.isa_round(
4111 builder,
4112 value,
4113 |ins, val| ins.nearest(val),
4114 BuiltinFunctions::nearest_f64,
4115 )
4116 }
4117
swizzle( &mut self, builder: &mut FunctionBuilder, a: ir::Value, b: ir::Value, ) -> ir::Value4118 pub fn swizzle(
4119 &mut self,
4120 builder: &mut FunctionBuilder,
4121 a: ir::Value,
4122 b: ir::Value,
4123 ) -> ir::Value {
4124 // On x86, swizzle would typically be compiled to `pshufb`, except
4125 // that that's not available on CPUs that lack SSSE3. In that case,
4126 // fall back to a builtin function.
4127 if !self.is_x86() || self.isa.has_x86_pshufb_lowering() {
4128 builder.ins().swizzle(a, b)
4129 } else {
4130 let swizzle = self.builtin_functions.i8x16_swizzle(builder.func);
4131 let vmctx = self.vmctx_val(&mut builder.cursor());
4132 let call = builder.ins().call(swizzle, &[vmctx, a, b]);
4133 *builder.func.dfg.inst_results(call).first().unwrap()
4134 }
4135 }
4136
relaxed_swizzle( &mut self, builder: &mut FunctionBuilder, a: ir::Value, b: ir::Value, ) -> ir::Value4137 pub fn relaxed_swizzle(
4138 &mut self,
4139 builder: &mut FunctionBuilder,
4140 a: ir::Value,
4141 b: ir::Value,
4142 ) -> ir::Value {
4143 // As above, fall back to a builtin if we lack SSSE3.
4144 if !self.is_x86() || self.isa.has_x86_pshufb_lowering() {
4145 if !self.is_x86() || self.relaxed_simd_deterministic() {
4146 builder.ins().swizzle(a, b)
4147 } else {
4148 builder.ins().x86_pshufb(a, b)
4149 }
4150 } else {
4151 let swizzle = self.builtin_functions.i8x16_swizzle(builder.func);
4152 let vmctx = self.vmctx_val(&mut builder.cursor());
4153 let call = builder.ins().call(swizzle, &[vmctx, a, b]);
4154 *builder.func.dfg.inst_results(call).first().unwrap()
4155 }
4156 }
4157
i8x16_shuffle( &mut self, builder: &mut FunctionBuilder, a: ir::Value, b: ir::Value, lanes: &[u8; 16], ) -> ir::Value4158 pub fn i8x16_shuffle(
4159 &mut self,
4160 builder: &mut FunctionBuilder,
4161 a: ir::Value,
4162 b: ir::Value,
4163 lanes: &[u8; 16],
4164 ) -> ir::Value {
4165 // As with swizzle, i8x16.shuffle would also commonly be implemented
4166 // with pshufb, so if we lack SSSE3, fall back to a builtin.
4167 if !self.is_x86() || self.isa.has_x86_pshufb_lowering() {
4168 let lanes = ConstantData::from(&lanes[..]);
4169 let mask = builder.func.dfg.immediates.push(lanes);
4170 builder.ins().shuffle(a, b, mask)
4171 } else {
4172 let lanes = builder
4173 .func
4174 .dfg
4175 .constants
4176 .insert(ConstantData::from(&lanes[..]));
4177 let lanes = builder.ins().vconst(I8X16, lanes);
4178 let i8x16_shuffle = self.builtin_functions.i8x16_shuffle(builder.func);
4179 let vmctx = self.vmctx_val(&mut builder.cursor());
4180 let call = builder.ins().call(i8x16_shuffle, &[vmctx, a, b, lanes]);
4181 *builder.func.dfg.inst_results(call).first().unwrap()
4182 }
4183 }
4184
fma_f32x4( &mut self, builder: &mut FunctionBuilder, a: ir::Value, b: ir::Value, c: ir::Value, ) -> ir::Value4185 pub fn fma_f32x4(
4186 &mut self,
4187 builder: &mut FunctionBuilder,
4188 a: ir::Value,
4189 b: ir::Value,
4190 c: ir::Value,
4191 ) -> ir::Value {
4192 if self.has_native_fma() {
4193 builder.ins().fma(a, b, c)
4194 } else if self.relaxed_simd_deterministic() {
4195 // Deterministic semantics are "fused multiply and add".
4196 let fma = self.builtin_functions.fma_f32x4(builder.func);
4197 let vmctx = self.vmctx_val(&mut builder.cursor());
4198 let call = builder.ins().call(fma, &[vmctx, a, b, c]);
4199 *builder.func.dfg.inst_results(call).first().unwrap()
4200 } else {
4201 let mul = builder.ins().fmul(a, b);
4202 builder.ins().fadd(mul, c)
4203 }
4204 }
4205
fma_f64x2( &mut self, builder: &mut FunctionBuilder, a: ir::Value, b: ir::Value, c: ir::Value, ) -> ir::Value4206 pub fn fma_f64x2(
4207 &mut self,
4208 builder: &mut FunctionBuilder,
4209 a: ir::Value,
4210 b: ir::Value,
4211 c: ir::Value,
4212 ) -> ir::Value {
4213 if self.has_native_fma() {
4214 builder.ins().fma(a, b, c)
4215 } else if self.relaxed_simd_deterministic() {
4216 // Deterministic semantics are "fused multiply and add".
4217 let fma = self.builtin_functions.fma_f64x2(builder.func);
4218 let vmctx = self.vmctx_val(&mut builder.cursor());
4219 let call = builder.ins().call(fma, &[vmctx, a, b, c]);
4220 *builder.func.dfg.inst_results(call).first().unwrap()
4221 } else {
4222 let mul = builder.ins().fmul(a, b);
4223 builder.ins().fadd(mul, c)
4224 }
4225 }
4226
isa(&self) -> &dyn TargetIsa4227 pub fn isa(&self) -> &dyn TargetIsa {
4228 &*self.isa
4229 }
4230
translate_sdiv( &mut self, builder: &mut FunctionBuilder, lhs: ir::Value, rhs: ir::Value, ) -> ir::Value4231 pub fn translate_sdiv(
4232 &mut self,
4233 builder: &mut FunctionBuilder,
4234 lhs: ir::Value,
4235 rhs: ir::Value,
4236 ) -> ir::Value {
4237 self.guard_signed_divide(builder, lhs, rhs);
4238 builder.ins().sdiv(lhs, rhs)
4239 }
4240
translate_udiv( &mut self, builder: &mut FunctionBuilder, lhs: ir::Value, rhs: ir::Value, ) -> ir::Value4241 pub fn translate_udiv(
4242 &mut self,
4243 builder: &mut FunctionBuilder,
4244 lhs: ir::Value,
4245 rhs: ir::Value,
4246 ) -> ir::Value {
4247 self.guard_zero_divisor(builder, rhs);
4248 builder.ins().udiv(lhs, rhs)
4249 }
4250
translate_srem( &mut self, builder: &mut FunctionBuilder, lhs: ir::Value, rhs: ir::Value, ) -> ir::Value4251 pub fn translate_srem(
4252 &mut self,
4253 builder: &mut FunctionBuilder,
4254 lhs: ir::Value,
4255 rhs: ir::Value,
4256 ) -> ir::Value {
4257 self.guard_zero_divisor(builder, rhs);
4258 builder.ins().srem(lhs, rhs)
4259 }
4260
translate_urem( &mut self, builder: &mut FunctionBuilder, lhs: ir::Value, rhs: ir::Value, ) -> ir::Value4261 pub fn translate_urem(
4262 &mut self,
4263 builder: &mut FunctionBuilder,
4264 lhs: ir::Value,
4265 rhs: ir::Value,
4266 ) -> ir::Value {
4267 self.guard_zero_divisor(builder, rhs);
4268 builder.ins().urem(lhs, rhs)
4269 }
4270
translate_fcvt_to_sint( &mut self, builder: &mut FunctionBuilder, ty: ir::Type, val: ir::Value, ) -> ir::Value4271 pub fn translate_fcvt_to_sint(
4272 &mut self,
4273 builder: &mut FunctionBuilder,
4274 ty: ir::Type,
4275 val: ir::Value,
4276 ) -> ir::Value {
4277 // NB: for now avoid translating this entire instruction to CLIF and
4278 // just do it in a libcall.
4279 if !self.clif_instruction_traps_enabled() {
4280 self.guard_fcvt_to_int(builder, ty, val, true);
4281 }
4282 builder.ins().fcvt_to_sint(ty, val)
4283 }
4284
translate_fcvt_to_uint( &mut self, builder: &mut FunctionBuilder, ty: ir::Type, val: ir::Value, ) -> ir::Value4285 pub fn translate_fcvt_to_uint(
4286 &mut self,
4287 builder: &mut FunctionBuilder,
4288 ty: ir::Type,
4289 val: ir::Value,
4290 ) -> ir::Value {
4291 if !self.clif_instruction_traps_enabled() {
4292 self.guard_fcvt_to_int(builder, ty, val, false);
4293 }
4294 builder.ins().fcvt_to_uint(ty, val)
4295 }
4296
4297 /// Returns whether it's acceptable to rely on traps in CLIF memory-related
4298 /// instructions (e.g. loads and stores).
4299 ///
4300 /// This is enabled if `signals_based_traps` is `true` since signal handlers
4301 /// are available, but this is additionally forcibly disabled if Pulley is
4302 /// being targeted since the Pulley runtime doesn't catch segfaults for
4303 /// itself.
clif_memory_traps_enabled(&self) -> bool4304 pub fn clif_memory_traps_enabled(&self) -> bool {
4305 self.tunables.signals_based_traps && !self.is_pulley()
4306 }
4307
4308 /// Returns whether loads from the null address are allowed as signals of
4309 /// whether to trap or not.
load_from_zero_allowed(&self) -> bool4310 pub fn load_from_zero_allowed(&self) -> bool {
4311 // Pulley allows loads-from-zero and otherwise this is only allowed with
4312 // traps + spectre mitigations.
4313 self.is_pulley()
4314 || (self.clif_memory_traps_enabled() && self.heap_access_spectre_mitigation())
4315 }
4316
4317 /// Returns whether the current location is reachable.
is_reachable(&self) -> bool4318 pub fn is_reachable(&self) -> bool {
4319 self.stacks.reachable()
4320 }
4321 }
4322
4323 // Helper function to convert an `IndexType` to an `ir::Type`.
4324 //
4325 // Implementing From/Into trait for `IndexType` or `ir::Type` would
4326 // introduce an extra dependency between `wasmtime_types` and `cranelift_codegen`.
index_type_to_ir_type(index_type: IndexType) -> ir::Type4327 fn index_type_to_ir_type(index_type: IndexType) -> ir::Type {
4328 match index_type {
4329 IndexType::I32 => I32,
4330 IndexType::I64 => I64,
4331 }
4332 }
4333