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