1 use super::{ArrayInit, GcCompiler};
2 use crate::bounds_checks::BoundsCheck;
3 use crate::func_environ::{Extension, FuncEnvironment};
4 use crate::translate::{Heap, HeapData, StructFieldsVec, TargetEnvironment};
5 use crate::trap::TranslateTrap;
6 use crate::{Reachability, TRAP_INTERNAL_ASSERT};
7 use cranelift_codegen::ir::immediates::Offset32;
8 use cranelift_codegen::ir::{BlockArg, ExceptionTableData, ExceptionTableItem};
9 use cranelift_codegen::{
10     cursor::FuncCursor,
11     ir::{self, InstBuilder, condcodes::IntCC},
12 };
13 use cranelift_entity::packed_option::ReservedValue;
14 use cranelift_frontend::FunctionBuilder;
15 use smallvec::{SmallVec, smallvec};
16 use wasmtime_environ::{
17     Collector, GcArrayLayout, GcLayout, GcStructLayout, I31_DISCRIMINANT, ModuleInternedTypeIndex,
18     PtrSize, TagIndex, TypeIndex, VMGcKind, WasmCompositeInnerType, WasmHeapTopType, WasmHeapType,
19     WasmRefType, WasmResult, WasmStorageType, WasmValType, wasm_unsupported,
20 };
21 
22 #[cfg(feature = "gc-drc")]
23 mod drc;
24 #[cfg(feature = "gc-null")]
25 mod null;
26 
27 /// Get the default GC compiler.
gc_compiler(func_env: &mut FuncEnvironment<'_>) -> WasmResult<Box<dyn GcCompiler>>28 pub fn gc_compiler(func_env: &mut FuncEnvironment<'_>) -> WasmResult<Box<dyn GcCompiler>> {
29     // If this function requires a GC compiler, that is not too bad of an
30     // over-approximation for it requiring a GC heap.
31     func_env.needs_gc_heap = true;
32 
33     match func_env.tunables.collector {
34         #[cfg(feature = "gc-drc")]
35         Some(Collector::DeferredReferenceCounting) => Ok(Box::new(drc::DrcCompiler::default())),
36         #[cfg(not(feature = "gc-drc"))]
37         Some(Collector::DeferredReferenceCounting) => Err(wasm_unsupported!(
38             "the DRC collector is unavailable because the `gc-drc` feature \
39              was disabled at compile time",
40         )),
41 
42         #[cfg(feature = "gc-null")]
43         Some(Collector::Null) => Ok(Box::new(null::NullCompiler::default())),
44         #[cfg(not(feature = "gc-null"))]
45         Some(Collector::Null) => Err(wasm_unsupported!(
46             "the null collector is unavailable because the `gc-null` feature \
47              was disabled at compile time",
48         )),
49 
50         #[cfg(any(feature = "gc-drc", feature = "gc-null"))]
51         None => Err(wasm_unsupported!(
52             "support for GC types disabled at configuration time"
53         )),
54         #[cfg(not(any(feature = "gc-drc", feature = "gc-null")))]
55         None => Err(wasm_unsupported!(
56             "support for GC types disabled because no collector implementation \
57              was selected at compile time; enable one of the `gc-drc` or \
58              `gc-null` features",
59         )),
60     }
61 }
62 
63 #[cfg_attr(
64     not(feature = "gc-drc"),
65     expect(dead_code, reason = "easier to define")
66 )]
unbarriered_load_gc_ref( builder: &mut FunctionBuilder, ty: WasmHeapType, ptr_to_gc_ref: ir::Value, flags: ir::MemFlags, ) -> WasmResult<ir::Value>67 fn unbarriered_load_gc_ref(
68     builder: &mut FunctionBuilder,
69     ty: WasmHeapType,
70     ptr_to_gc_ref: ir::Value,
71     flags: ir::MemFlags,
72 ) -> WasmResult<ir::Value> {
73     debug_assert!(ty.is_vmgcref_type());
74     let gc_ref = builder.ins().load(ir::types::I32, flags, ptr_to_gc_ref, 0);
75     if ty != WasmHeapType::I31 {
76         builder.declare_value_needs_stack_map(gc_ref);
77     }
78     Ok(gc_ref)
79 }
80 
81 #[cfg_attr(
82     not(any(feature = "gc-drc", feature = "gc-null")),
83     expect(dead_code, reason = "easier to define")
84 )]
unbarriered_store_gc_ref( builder: &mut FunctionBuilder, ty: WasmHeapType, dst: ir::Value, gc_ref: ir::Value, flags: ir::MemFlags, ) -> WasmResult<()>85 fn unbarriered_store_gc_ref(
86     builder: &mut FunctionBuilder,
87     ty: WasmHeapType,
88     dst: ir::Value,
89     gc_ref: ir::Value,
90     flags: ir::MemFlags,
91 ) -> WasmResult<()> {
92     debug_assert!(ty.is_vmgcref_type());
93     builder.ins().store(flags, gc_ref, dst, 0);
94     Ok(())
95 }
96 
97 /// Emit inline CLIF code that asserts an object's `VMGcKind` matches the
98 /// expected kind. Only emits code when `cfg(gc_zeal)` is enabled.
99 ///
100 /// `gc_ref` must be a non-null, non-i31 GC reference (i32 heap index).
emit_gc_kind_assert( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, gc_ref: ir::Value, expected_kind: VMGcKind, )101 fn emit_gc_kind_assert(
102     func_env: &mut FuncEnvironment<'_>,
103     builder: &mut FunctionBuilder<'_>,
104     gc_ref: ir::Value,
105     expected_kind: VMGcKind,
106 ) {
107     if !cfg!(gc_zeal) {
108         return;
109     }
110 
111     func_env.trapz(builder, gc_ref, crate::TRAP_NULL_REFERENCE);
112 
113     let kind_addr = func_env.prepare_gc_ref_access(
114         builder,
115         gc_ref,
116         BoundsCheck::StaticObjectField {
117             offset: wasmtime_environ::VM_GC_HEADER_KIND_OFFSET,
118             access_size: wasmtime_environ::VM_GC_KIND_SIZE,
119             object_size: wasmtime_environ::VM_GC_HEADER_SIZE,
120         },
121     );
122     let kind_and_reserved_bits = builder.ins().load(
123         ir::types::I32,
124         ir::MemFlags::trusted().with_readonly(),
125         kind_addr,
126         0,
127     );
128     let kind_mask = builder
129         .ins()
130         .iconst(ir::types::I32, i64::from(VMGcKind::MASK));
131     let actual_kind = builder.ins().band(kind_and_reserved_bits, kind_mask);
132 
133     let expected_kind = builder
134         .ins()
135         .iconst(ir::types::I32, i64::from(expected_kind.as_u32()));
136 
137     // NB: Do a subtype check rather than a strict equality check. See
138     // `VMGcKind::matches` for details.
139     let and = builder.ins().band(actual_kind, expected_kind);
140     let matches = builder.ins().icmp(IntCC::Equal, and, expected_kind);
141 
142     builder.ins().trapz(matches, TRAP_INTERNAL_ASSERT);
143 }
144 
145 /// Read a struct field or array element from its raw address in the GC heap.
146 ///
147 /// The given address MUST have already been bounds-checked via
148 /// `prepare_gc_ref_access`.
read_field_at_addr( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, ty: WasmStorageType, addr: ir::Value, extension: Option<Extension>, ) -> WasmResult<ir::Value>149 fn read_field_at_addr(
150     func_env: &mut FuncEnvironment<'_>,
151     builder: &mut FunctionBuilder<'_>,
152     ty: WasmStorageType,
153     addr: ir::Value,
154     extension: Option<Extension>,
155 ) -> WasmResult<ir::Value> {
156     assert_eq!(extension.is_none(), matches!(ty, WasmStorageType::Val(_)));
157     assert_eq!(
158         extension.is_some(),
159         matches!(ty, WasmStorageType::I8 | WasmStorageType::I16)
160     );
161 
162     // Data inside GC objects is always little endian.
163     let flags = ir::MemFlags::trusted().with_endianness(ir::Endianness::Little);
164 
165     let value = match ty {
166         WasmStorageType::I8 => builder.ins().load(ir::types::I8, flags, addr, 0),
167         WasmStorageType::I16 => builder.ins().load(ir::types::I16, flags, addr, 0),
168         WasmStorageType::Val(v) => match v {
169             WasmValType::I32 => builder.ins().load(ir::types::I32, flags, addr, 0),
170             WasmValType::I64 => builder.ins().load(ir::types::I64, flags, addr, 0),
171             WasmValType::F32 => builder.ins().load(ir::types::F32, flags, addr, 0),
172             WasmValType::F64 => builder.ins().load(ir::types::F64, flags, addr, 0),
173             WasmValType::V128 => builder.ins().load(ir::types::I8X16, flags, addr, 0),
174             WasmValType::Ref(r) => match r.heap_type.top() {
175                 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => {
176                     gc_compiler(func_env)?
177                         .translate_read_gc_reference(func_env, builder, r, addr, flags)?
178                 }
179                 WasmHeapTopType::Func => {
180                     let expected_ty = match r.heap_type {
181                         WasmHeapType::Func => ModuleInternedTypeIndex::reserved_value(),
182                         WasmHeapType::ConcreteFunc(ty) => ty.unwrap_module_type_index(),
183                         WasmHeapType::NoFunc => {
184                             let null = builder.ins().iconst(func_env.pointer_type(), 0);
185                             if !r.nullable {
186                                 // Because `nofunc` is uninhabited, and this
187                                 // reference is non-null, this is unreachable
188                                 // code. Unconditionally trap via conditional
189                                 // trap instructions to avoid inserting block
190                                 // terminators in the middle of this block.
191                                 builder.ins().trapz(null, TRAP_INTERNAL_ASSERT);
192                             }
193                             return Ok(null);
194                         }
195                         _ => unreachable!("not a function heap type"),
196                     };
197                     let expected_ty = builder
198                         .ins()
199                         .iconst(ir::types::I32, i64::from(expected_ty.as_bits()));
200 
201                     let vmctx = func_env.vmctx_val(&mut builder.cursor());
202 
203                     let func_ref_id = builder.ins().load(ir::types::I32, flags, addr, 0);
204                     let get_interned_func_ref = func_env
205                         .builtin_functions
206                         .get_interned_func_ref(builder.func);
207 
208                     let call_inst = builder
209                         .ins()
210                         .call(get_interned_func_ref, &[vmctx, func_ref_id, expected_ty]);
211                     builder.func.dfg.first_result(call_inst)
212                 }
213                 WasmHeapTopType::Cont => {
214                     // TODO(#10248) GC integration for stack switching
215                     return Err(wasmtime_environ::WasmError::Unsupported(
216                         "Stack switching feature not compatible with GC, yet".to_string(),
217                     ));
218                 }
219             },
220         },
221     };
222 
223     let value = match extension {
224         Some(Extension::Sign) => builder.ins().sextend(ir::types::I32, value),
225         Some(Extension::Zero) => builder.ins().uextend(ir::types::I32, value),
226         None => value,
227     };
228 
229     Ok(value)
230 }
231 
write_func_ref_at_addr( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, ref_type: WasmRefType, flags: ir::MemFlags, field_addr: ir::Value, func_ref: ir::Value, ) -> WasmResult<()>232 fn write_func_ref_at_addr(
233     func_env: &mut FuncEnvironment<'_>,
234     builder: &mut FunctionBuilder<'_>,
235     ref_type: WasmRefType,
236     flags: ir::MemFlags,
237     field_addr: ir::Value,
238     func_ref: ir::Value,
239 ) -> WasmResult<()> {
240     assert_eq!(ref_type.heap_type.top(), WasmHeapTopType::Func);
241 
242     let vmctx = func_env.vmctx_val(&mut builder.cursor());
243 
244     let intern_func_ref_for_gc_heap = func_env
245         .builtin_functions
246         .intern_func_ref_for_gc_heap(builder.func);
247 
248     let func_ref = if ref_type.heap_type == WasmHeapType::NoFunc {
249         let null = builder.ins().iconst(func_env.pointer_type(), 0);
250         if !ref_type.nullable {
251             // Because `nofunc` is uninhabited, and this reference is
252             // non-null, this is unreachable code. Unconditionally trap
253             // via conditional trap instructions to avoid inserting
254             // block terminators in the middle of this block.
255             builder.ins().trapz(null, TRAP_INTERNAL_ASSERT);
256         }
257         null
258     } else {
259         func_ref
260     };
261 
262     // Convert the raw `funcref` into a `FuncRefTableId` for use in the
263     // GC heap.
264     let call_inst = builder
265         .ins()
266         .call(intern_func_ref_for_gc_heap, &[vmctx, func_ref]);
267     let func_ref_id = builder.func.dfg.first_result(call_inst);
268     let func_ref_id = builder.ins().ireduce(ir::types::I32, func_ref_id);
269 
270     // Store the id in the field.
271     builder.ins().store(flags, func_ref_id, field_addr, 0);
272 
273     Ok(())
274 }
275 
write_field_at_addr( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, field_ty: WasmStorageType, field_addr: ir::Value, new_val: ir::Value, ) -> WasmResult<()>276 fn write_field_at_addr(
277     func_env: &mut FuncEnvironment<'_>,
278     builder: &mut FunctionBuilder<'_>,
279     field_ty: WasmStorageType,
280     field_addr: ir::Value,
281     new_val: ir::Value,
282 ) -> WasmResult<()> {
283     // Data inside GC objects is always little endian.
284     let flags = ir::MemFlags::trusted().with_endianness(ir::Endianness::Little);
285 
286     match field_ty {
287         WasmStorageType::I8 => {
288             builder.ins().istore8(flags, new_val, field_addr, 0);
289         }
290         WasmStorageType::I16 => {
291             builder.ins().istore16(flags, new_val, field_addr, 0);
292         }
293         WasmStorageType::Val(WasmValType::Ref(r)) if r.heap_type.top() == WasmHeapTopType::Func => {
294             write_func_ref_at_addr(func_env, builder, r, flags, field_addr, new_val)?;
295         }
296         WasmStorageType::Val(WasmValType::Ref(r)) => {
297             gc_compiler(func_env)?
298                 .translate_write_gc_reference(func_env, builder, r, field_addr, new_val, flags)?;
299         }
300         WasmStorageType::Val(_) => {
301             assert_eq!(
302                 builder.func.dfg.value_type(new_val).bytes(),
303                 wasmtime_environ::byte_size_of_wasm_ty_in_gc_heap(&field_ty)
304             );
305             builder.ins().store(flags, new_val, field_addr, 0);
306         }
307     }
308     Ok(())
309 }
310 
translate_struct_new( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, struct_type_index: TypeIndex, fields: &[ir::Value], ) -> WasmResult<ir::Value>311 pub fn translate_struct_new(
312     func_env: &mut FuncEnvironment<'_>,
313     builder: &mut FunctionBuilder<'_>,
314     struct_type_index: TypeIndex,
315     fields: &[ir::Value],
316 ) -> WasmResult<ir::Value> {
317     gc_compiler(func_env)?.alloc_struct(func_env, builder, struct_type_index, &fields)
318 }
319 
default_value( cursor: &mut FuncCursor, func_env: &FuncEnvironment<'_>, ty: &WasmStorageType, ) -> ir::Value320 fn default_value(
321     cursor: &mut FuncCursor,
322     func_env: &FuncEnvironment<'_>,
323     ty: &WasmStorageType,
324 ) -> ir::Value {
325     match ty {
326         WasmStorageType::I8 | WasmStorageType::I16 => cursor.ins().iconst(ir::types::I32, 0),
327         WasmStorageType::Val(v) => match v {
328             WasmValType::I32 => cursor.ins().iconst(ir::types::I32, 0),
329             WasmValType::I64 => cursor.ins().iconst(ir::types::I64, 0),
330             WasmValType::F32 => cursor.ins().f32const(0.0),
331             WasmValType::F64 => cursor.ins().f64const(0.0),
332             WasmValType::V128 => {
333                 let c = cursor.func.dfg.constants.insert(vec![0; 16].into());
334                 cursor.ins().vconst(ir::types::I8X16, c)
335             }
336             WasmValType::Ref(r) => {
337                 assert!(r.nullable);
338                 let (ty, needs_stack_map) = func_env.reference_type(r.heap_type);
339 
340                 // NB: The collector doesn't need to know about null references.
341                 let _ = needs_stack_map;
342 
343                 cursor.ins().iconst(ty, 0)
344             }
345         },
346     }
347 }
348 
translate_struct_new_default( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, struct_type_index: TypeIndex, ) -> WasmResult<ir::Value>349 pub fn translate_struct_new_default(
350     func_env: &mut FuncEnvironment<'_>,
351     builder: &mut FunctionBuilder<'_>,
352     struct_type_index: TypeIndex,
353 ) -> WasmResult<ir::Value> {
354     let interned_ty = func_env.module.types[struct_type_index].unwrap_module_type_index();
355     let struct_ty = func_env.types.unwrap_struct(interned_ty)?;
356     let fields = struct_ty
357         .fields
358         .iter()
359         .map(|f| default_value(&mut builder.cursor(), func_env, &f.element_type))
360         .collect::<StructFieldsVec>();
361     gc_compiler(func_env)?.alloc_struct(func_env, builder, struct_type_index, &fields)
362 }
363 
translate_struct_get( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, struct_type_index: TypeIndex, field_index: u32, struct_ref: ir::Value, extension: Option<Extension>, ) -> WasmResult<ir::Value>364 pub fn translate_struct_get(
365     func_env: &mut FuncEnvironment<'_>,
366     builder: &mut FunctionBuilder<'_>,
367     struct_type_index: TypeIndex,
368     field_index: u32,
369     struct_ref: ir::Value,
370     extension: Option<Extension>,
371 ) -> WasmResult<ir::Value> {
372     log::trace!(
373         "translate_struct_get({struct_type_index:?}, {field_index:?}, {struct_ref:?}, {extension:?})"
374     );
375 
376     // TODO: If we know we have a `(ref $my_struct)` here, instead of maybe a
377     // `(ref null $my_struct)`, we could omit the `trapz`. But plumbing that
378     // type info from `wasmparser` and through to here is a bit funky.
379     func_env.trapz(builder, struct_ref, crate::TRAP_NULL_REFERENCE);
380 
381     emit_gc_kind_assert(func_env, builder, struct_ref, VMGcKind::StructRef);
382 
383     let field_index = usize::try_from(field_index).unwrap();
384     let interned_type_index = func_env.module.types[struct_type_index].unwrap_module_type_index();
385 
386     let struct_layout = func_env.struct_or_exn_layout(interned_type_index);
387     let struct_size = struct_layout.size;
388 
389     let field_offset = struct_layout.fields[field_index].offset;
390     let field_ty = &func_env.types.unwrap_struct(interned_type_index)?.fields[field_index];
391     let field_size = wasmtime_environ::byte_size_of_wasm_ty_in_gc_heap(&field_ty.element_type);
392     assert!(field_offset + field_size <= struct_size);
393 
394     let field_addr = func_env.prepare_gc_ref_access(
395         builder,
396         struct_ref,
397         BoundsCheck::StaticObjectField {
398             offset: field_offset,
399             access_size: u8::try_from(field_size).unwrap(),
400             object_size: struct_size,
401         },
402     );
403 
404     let result = read_field_at_addr(
405         func_env,
406         builder,
407         field_ty.element_type,
408         field_addr,
409         extension,
410     );
411     log::trace!("translate_struct_get(..) -> {result:?}");
412     result
413 }
414 
translate_struct_set( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, struct_type_index: TypeIndex, field_index: u32, struct_ref: ir::Value, new_val: ir::Value, ) -> WasmResult<()>415 pub fn translate_struct_set(
416     func_env: &mut FuncEnvironment<'_>,
417     builder: &mut FunctionBuilder<'_>,
418     struct_type_index: TypeIndex,
419     field_index: u32,
420     struct_ref: ir::Value,
421     new_val: ir::Value,
422 ) -> WasmResult<()> {
423     log::trace!(
424         "translate_struct_set({struct_type_index:?}, {field_index:?}, struct_ref: {struct_ref:?}, new_val: {new_val:?})"
425     );
426 
427     // TODO: See comment in `translate_struct_get` about the `trapz`.
428     func_env.trapz(builder, struct_ref, crate::TRAP_NULL_REFERENCE);
429 
430     emit_gc_kind_assert(func_env, builder, struct_ref, VMGcKind::StructRef);
431 
432     let field_index = usize::try_from(field_index).unwrap();
433     let interned_type_index = func_env.module.types[struct_type_index].unwrap_module_type_index();
434 
435     let struct_layout = func_env.struct_or_exn_layout(interned_type_index);
436     let struct_size = struct_layout.size;
437 
438     let field_offset = struct_layout.fields[field_index].offset;
439     let field_ty = &func_env.types.unwrap_struct(interned_type_index)?.fields[field_index];
440     let field_size = wasmtime_environ::byte_size_of_wasm_ty_in_gc_heap(&field_ty.element_type);
441     assert!(field_offset + field_size <= struct_size);
442 
443     let field_addr = func_env.prepare_gc_ref_access(
444         builder,
445         struct_ref,
446         BoundsCheck::StaticObjectField {
447             offset: field_offset,
448             access_size: u8::try_from(field_size).unwrap(),
449             object_size: struct_size,
450         },
451     );
452 
453     write_field_at_addr(
454         func_env,
455         builder,
456         field_ty.element_type,
457         field_addr,
458         new_val,
459     )?;
460 
461     log::trace!("translate_struct_set: finished");
462     Ok(())
463 }
464 
translate_exn_unbox( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, tag_index: TagIndex, exn_ref: ir::Value, ) -> WasmResult<SmallVec<[ir::Value; 4]>>465 pub fn translate_exn_unbox(
466     func_env: &mut FuncEnvironment<'_>,
467     builder: &mut FunctionBuilder<'_>,
468     tag_index: TagIndex,
469     exn_ref: ir::Value,
470 ) -> WasmResult<SmallVec<[ir::Value; 4]>> {
471     log::trace!("translate_exn_unbox({tag_index:?}, {exn_ref:?})");
472 
473     // We know that the `exn_ref` is not null because we reach this
474     // operation only in catch blocks, and throws are initiated from
475     // runtime code that checks for nulls first.
476 
477     // Get the GcExceptionLayout associated with this tag's
478     // function type, and generate loads for each field.
479     let exception_ty_idx = func_env
480         .exception_type_from_tag(tag_index)
481         .unwrap_module_type_index();
482     let exception_ty = func_env.types.unwrap_exn(exception_ty_idx)?;
483     let exn_layout = func_env.struct_or_exn_layout(exception_ty_idx);
484     let exn_size = exn_layout.size;
485 
486     // Gather accesses first because these require a borrow on
487     // `func_env`, which we later mutate below via
488     // `prepare_gc_ref_access()`.
489     let mut accesses: SmallVec<[_; 4]> = smallvec![];
490     for (field_ty, field_layout) in exception_ty.fields.iter().zip(exn_layout.fields.iter()) {
491         accesses.push((field_layout.offset, field_ty.element_type));
492     }
493 
494     let mut result = smallvec![];
495     for (field_offset, field_ty) in accesses {
496         let field_size = wasmtime_environ::byte_size_of_wasm_ty_in_gc_heap(&field_ty);
497         assert!(field_offset + field_size <= exn_size);
498         let field_addr = func_env.prepare_gc_ref_access(
499             builder,
500             exn_ref,
501             BoundsCheck::StaticObjectField {
502                 offset: field_offset,
503                 access_size: u8::try_from(field_size).unwrap(),
504                 object_size: exn_size,
505             },
506         );
507 
508         let value = read_field_at_addr(func_env, builder, field_ty, field_addr, None)?;
509         result.push(value);
510     }
511 
512     log::trace!("translate_exn_unbox(..) -> {result:?}");
513     Ok(result)
514 }
515 
translate_exn_throw( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, tag_index: TagIndex, args: &[ir::Value], ) -> WasmResult<()>516 pub fn translate_exn_throw(
517     func_env: &mut FuncEnvironment<'_>,
518     builder: &mut FunctionBuilder<'_>,
519     tag_index: TagIndex,
520     args: &[ir::Value],
521 ) -> WasmResult<()> {
522     let (instance_id, defined_tag_id) = func_env.get_instance_and_tag(builder, tag_index);
523     let exnref = gc_compiler(func_env)?.alloc_exn(
524         func_env,
525         builder,
526         tag_index,
527         args,
528         instance_id,
529         defined_tag_id,
530     )?;
531     translate_exn_throw_ref(func_env, builder, exnref)
532 }
533 
translate_exn_throw_ref( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, exnref: ir::Value, ) -> WasmResult<()>534 pub fn translate_exn_throw_ref(
535     func_env: &mut FuncEnvironment<'_>,
536     builder: &mut FunctionBuilder<'_>,
537     exnref: ir::Value,
538 ) -> WasmResult<()> {
539     let builtin = func_env.builtin_functions.throw_ref(builder.func);
540     let sig = builder.func.dfg.ext_funcs[builtin].signature;
541     let vmctx = func_env.vmctx_val(&mut builder.cursor());
542 
543     // Generate a `try_call` with handlers from the current
544     // stack. This libcall is unique among libcall implementations of
545     // opcodes: we know the others will not throw, but `throw_ref`'s
546     // entire purpose is to throw. So if there are any handlers in the
547     // local function body, we need to attach them to this callsite
548     // like any other.
549     let continuation = builder.create_block();
550     let current_block = builder.current_block().unwrap();
551     builder.insert_block_after(continuation, current_block);
552     let continuation_call = builder.func.dfg.block_call(continuation, &[]);
553     let mut table_items = vec![ExceptionTableItem::Context(vmctx)];
554     for (tag, block) in func_env.stacks.handlers.handlers() {
555         let block_call = builder
556             .func
557             .dfg
558             .block_call(block, &[BlockArg::TryCallExn(0)]);
559         table_items.push(match tag {
560             Some(tag) => ExceptionTableItem::Tag(tag, block_call),
561             None => ExceptionTableItem::Default(block_call),
562         });
563     }
564     let etd = ExceptionTableData::new(sig, continuation_call, table_items);
565     let et = builder.func.dfg.exception_tables.push(etd);
566 
567     builder.ins().try_call(builtin, &[vmctx, exnref], et);
568 
569     builder.switch_to_block(continuation);
570     builder.seal_block(continuation);
571     func_env.trap(builder, crate::TRAP_UNREACHABLE);
572 
573     Ok(())
574 }
575 
translate_array_new( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder, array_type_index: TypeIndex, elem: ir::Value, len: ir::Value, ) -> WasmResult<ir::Value>576 pub fn translate_array_new(
577     func_env: &mut FuncEnvironment<'_>,
578     builder: &mut FunctionBuilder,
579     array_type_index: TypeIndex,
580     elem: ir::Value,
581     len: ir::Value,
582 ) -> WasmResult<ir::Value> {
583     log::trace!("translate_array_new({array_type_index:?}, {elem:?}, {len:?})");
584     let result = gc_compiler(func_env)?.alloc_array(
585         func_env,
586         builder,
587         array_type_index,
588         ArrayInit::Fill { elem, len },
589     )?;
590     log::trace!("translate_array_new(..) -> {result:?}");
591     Ok(result)
592 }
593 
translate_array_new_default( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder, array_type_index: TypeIndex, len: ir::Value, ) -> WasmResult<ir::Value>594 pub fn translate_array_new_default(
595     func_env: &mut FuncEnvironment<'_>,
596     builder: &mut FunctionBuilder,
597     array_type_index: TypeIndex,
598     len: ir::Value,
599 ) -> WasmResult<ir::Value> {
600     log::trace!("translate_array_new_default({array_type_index:?}, {len:?})");
601 
602     let interned_ty = func_env.module.types[array_type_index].unwrap_module_type_index();
603     let array_ty = func_env.types.unwrap_array(interned_ty)?;
604     let elem = default_value(&mut builder.cursor(), func_env, &array_ty.0.element_type);
605     let result = gc_compiler(func_env)?.alloc_array(
606         func_env,
607         builder,
608         array_type_index,
609         ArrayInit::Fill { elem, len },
610     )?;
611     log::trace!("translate_array_new_default(..) -> {result:?}");
612     Ok(result)
613 }
614 
translate_array_new_fixed( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder, array_type_index: TypeIndex, elems: &[ir::Value], ) -> WasmResult<ir::Value>615 pub fn translate_array_new_fixed(
616     func_env: &mut FuncEnvironment<'_>,
617     builder: &mut FunctionBuilder,
618     array_type_index: TypeIndex,
619     elems: &[ir::Value],
620 ) -> WasmResult<ir::Value> {
621     log::trace!("translate_array_new_fixed({array_type_index:?}, {elems:?})");
622     let result = gc_compiler(func_env)?.alloc_array(
623         func_env,
624         builder,
625         array_type_index,
626         ArrayInit::Elems(elems),
627     )?;
628     log::trace!("translate_array_new_fixed(..) -> {result:?}");
629     Ok(result)
630 }
631 
632 impl ArrayInit<'_> {
633     /// Get the length (as an `i32`-typed `ir::Value`) of these array elements.
634     #[cfg_attr(
635         not(any(feature = "gc-drc", feature = "gc-null")),
636         expect(dead_code, reason = "easier to define")
637     )]
len(self, pos: &mut FuncCursor) -> ir::Value638     fn len(self, pos: &mut FuncCursor) -> ir::Value {
639         match self {
640             ArrayInit::Fill { len, .. } => len,
641             ArrayInit::Elems(e) => {
642                 let len = u32::try_from(e.len()).unwrap();
643                 pos.ins().iconst(ir::types::I32, i64::from(len))
644             }
645         }
646     }
647 
648     /// Initialize a newly-allocated array's elements.
649     #[cfg_attr(
650         not(any(feature = "gc-drc", feature = "gc-null")),
651         expect(dead_code, reason = "easier to define")
652     )]
initialize( self, func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, interned_type_index: ModuleInternedTypeIndex, base_size: u32, size: ir::Value, elems_addr: ir::Value, mut init_field: impl FnMut( &mut FuncEnvironment<'_>, &mut FunctionBuilder<'_>, WasmStorageType, ir::Value, ir::Value, ) -> WasmResult<()>, ) -> WasmResult<()>653     fn initialize(
654         self,
655         func_env: &mut FuncEnvironment<'_>,
656         builder: &mut FunctionBuilder<'_>,
657         interned_type_index: ModuleInternedTypeIndex,
658         base_size: u32,
659         size: ir::Value,
660         elems_addr: ir::Value,
661         mut init_field: impl FnMut(
662             &mut FuncEnvironment<'_>,
663             &mut FunctionBuilder<'_>,
664             WasmStorageType,
665             ir::Value,
666             ir::Value,
667         ) -> WasmResult<()>,
668     ) -> WasmResult<()> {
669         log::trace!(
670             "initialize_array({interned_type_index:?}, {base_size:?}, {size:?}, {elems_addr:?})"
671         );
672 
673         assert!(!func_env.types[interned_type_index].composite_type.shared);
674         let array_ty = func_env.types[interned_type_index]
675             .composite_type
676             .inner
677             .unwrap_array();
678         let elem_ty = array_ty.0.element_type;
679         let elem_size = wasmtime_environ::byte_size_of_wasm_ty_in_gc_heap(&elem_ty);
680         let pointer_type = func_env.pointer_type();
681         let elem_size = builder.ins().iconst(pointer_type, i64::from(elem_size));
682         match self {
683             ArrayInit::Elems(elems) => {
684                 let mut elem_addr = elems_addr;
685                 for val in elems {
686                     init_field(func_env, builder, elem_ty, elem_addr, *val)?;
687                     elem_addr = builder.ins().iadd(elem_addr, elem_size);
688                 }
689             }
690             ArrayInit::Fill { elem, len: _ } => {
691                 // Compute the end address of the elements.
692                 let base_size = builder.ins().iconst(pointer_type, i64::from(base_size));
693                 let array_addr = builder.ins().isub(elems_addr, base_size);
694                 let size = uextend_i32_to_pointer_type(builder, pointer_type, size);
695                 let elems_end = builder.ins().iadd(array_addr, size);
696 
697                 emit_array_fill_impl(
698                     func_env,
699                     builder,
700                     elems_addr,
701                     elem_size,
702                     elems_end,
703                     |func_env, builder, elem_addr| {
704                         init_field(func_env, builder, elem_ty, elem_addr, elem)
705                     },
706                 )?;
707             }
708         }
709         log::trace!("initialize_array: finished");
710         Ok(())
711     }
712 }
713 
emit_array_fill_impl( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, elem_addr: ir::Value, elem_size: ir::Value, fill_end: ir::Value, mut emit_elem_write: impl FnMut( &mut FuncEnvironment<'_>, &mut FunctionBuilder<'_>, ir::Value, ) -> WasmResult<()>, ) -> WasmResult<()>714 fn emit_array_fill_impl(
715     func_env: &mut FuncEnvironment<'_>,
716     builder: &mut FunctionBuilder<'_>,
717     elem_addr: ir::Value,
718     elem_size: ir::Value,
719     fill_end: ir::Value,
720     mut emit_elem_write: impl FnMut(
721         &mut FuncEnvironment<'_>,
722         &mut FunctionBuilder<'_>,
723         ir::Value,
724     ) -> WasmResult<()>,
725 ) -> WasmResult<()> {
726     log::trace!(
727         "emit_array_fill_impl(elem_addr: {elem_addr:?}, elem_size: {elem_size:?}, fill_end: {fill_end:?})"
728     );
729 
730     let pointer_ty = func_env.pointer_type();
731 
732     assert_eq!(builder.func.dfg.value_type(elem_addr), pointer_ty);
733     assert_eq!(builder.func.dfg.value_type(elem_size), pointer_ty);
734     assert_eq!(builder.func.dfg.value_type(fill_end), pointer_ty);
735 
736     // Loop to fill the elements, emitting the equivalent of the following
737     // pseudo-CLIF:
738     //
739     // current_block:
740     //     ...
741     //     jump loop_header_block(elem_addr)
742     //
743     // loop_header_block(elem_addr: i32):
744     //     done = icmp eq elem_addr, fill_end
745     //     brif done, continue_block, loop_body_block
746     //
747     // loop_body_block:
748     //     emit_elem_write()
749     //     next_elem_addr = iadd elem_addr, elem_size
750     //     jump loop_header_block(next_elem_addr)
751     //
752     // continue_block:
753     //     ...
754 
755     let current_block = builder.current_block().unwrap();
756     let loop_header_block = builder.create_block();
757     let loop_body_block = builder.create_block();
758     let continue_block = builder.create_block();
759 
760     builder.ensure_inserted_block();
761     builder.insert_block_after(loop_header_block, current_block);
762     builder.insert_block_after(loop_body_block, loop_header_block);
763     builder.insert_block_after(continue_block, loop_body_block);
764 
765     // Current block: jump to the loop header block with the first element's
766     // address.
767     builder.ins().jump(loop_header_block, &[elem_addr.into()]);
768 
769     // Loop header block: check if we're done, then jump to either the continue
770     // block or the loop body block.
771     builder.switch_to_block(loop_header_block);
772     builder.append_block_param(loop_header_block, pointer_ty);
773     log::trace!("emit_array_fill_impl: loop header");
774     func_env.translate_loop_header(builder)?;
775     let elem_addr = builder.block_params(loop_header_block)[0];
776     let done = builder.ins().icmp(IntCC::Equal, elem_addr, fill_end);
777     builder
778         .ins()
779         .brif(done, continue_block, &[], loop_body_block, &[]);
780 
781     // Loop body block: write the value to the current element, compute the next
782     // element's address, and then jump back to the loop header block.
783     builder.switch_to_block(loop_body_block);
784     log::trace!("emit_array_fill_impl: loop body");
785     emit_elem_write(func_env, builder, elem_addr)?;
786     let next_elem_addr = builder.ins().iadd(elem_addr, elem_size);
787     builder
788         .ins()
789         .jump(loop_header_block, &[next_elem_addr.into()]);
790 
791     // Continue...
792     builder.switch_to_block(continue_block);
793     log::trace!("emit_array_fill_impl: finished");
794     builder.seal_block(loop_header_block);
795     builder.seal_block(loop_body_block);
796     builder.seal_block(continue_block);
797     Ok(())
798 }
799 
translate_array_fill( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, array_type_index: TypeIndex, array_ref: ir::Value, index: ir::Value, value: ir::Value, n: ir::Value, ) -> WasmResult<()>800 pub fn translate_array_fill(
801     func_env: &mut FuncEnvironment<'_>,
802     builder: &mut FunctionBuilder<'_>,
803     array_type_index: TypeIndex,
804     array_ref: ir::Value,
805     index: ir::Value,
806     value: ir::Value,
807     n: ir::Value,
808 ) -> WasmResult<()> {
809     log::trace!(
810         "translate_array_fill({array_type_index:?}, {array_ref:?}, {index:?}, {value:?}, {n:?})"
811     );
812 
813     let len = translate_array_len(func_env, builder, array_ref)?;
814 
815     // Check that the full range of elements we want to fill is within bounds.
816     let end_index = func_env.uadd_overflow_trap(builder, index, n, crate::TRAP_ARRAY_OUT_OF_BOUNDS);
817     let out_of_bounds = builder
818         .ins()
819         .icmp(IntCC::UnsignedGreaterThan, end_index, len);
820     func_env.trapnz(builder, out_of_bounds, crate::TRAP_ARRAY_OUT_OF_BOUNDS);
821 
822     // Get the address of the first element we want to fill.
823     let interned_type_index = func_env.module.types[array_type_index].unwrap_module_type_index();
824     let ArraySizeInfo {
825         obj_size,
826         one_elem_size,
827         base_size,
828     } = emit_array_size_info(func_env, builder, interned_type_index, len);
829     let offset_in_elems = builder.ins().imul(index, one_elem_size);
830     let obj_offset = builder.ins().iadd(base_size, offset_in_elems);
831     let elem_addr = func_env.prepare_gc_ref_access(
832         builder,
833         array_ref,
834         BoundsCheck::DynamicObjectField {
835             offset: obj_offset,
836             object_size: obj_size,
837         },
838     );
839 
840     // Calculate the end address, just after the filled region.
841     let fill_size = builder.ins().imul(n, one_elem_size);
842     let fill_size = uextend_i32_to_pointer_type(builder, func_env.pointer_type(), fill_size);
843     let fill_end = builder.ins().iadd(elem_addr, fill_size);
844 
845     let one_elem_size =
846         uextend_i32_to_pointer_type(builder, func_env.pointer_type(), one_elem_size);
847 
848     let result = emit_array_fill_impl(
849         func_env,
850         builder,
851         elem_addr,
852         one_elem_size,
853         fill_end,
854         |func_env, builder, elem_addr| {
855             let elem_ty = func_env
856                 .types
857                 .unwrap_array(interned_type_index)?
858                 .0
859                 .element_type;
860             write_field_at_addr(func_env, builder, elem_ty, elem_addr, value)
861         },
862     );
863     log::trace!("translate_array_fill(..) -> {result:?}");
864     result
865 }
866 
translate_array_len( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder, array_ref: ir::Value, ) -> WasmResult<ir::Value>867 pub fn translate_array_len(
868     func_env: &mut FuncEnvironment<'_>,
869     builder: &mut FunctionBuilder,
870     array_ref: ir::Value,
871 ) -> WasmResult<ir::Value> {
872     log::trace!("translate_array_len({array_ref:?})");
873 
874     func_env.trapz(builder, array_ref, crate::TRAP_NULL_REFERENCE);
875 
876     let len_offset = gc_compiler(func_env)?.layouts().array_length_field_offset();
877     let len_field = func_env.prepare_gc_ref_access(
878         builder,
879         array_ref,
880         // Note: We can't bounds check the whole array object's size because we
881         // don't know its length yet. Chicken and egg problem.
882         BoundsCheck::StaticOffset {
883             offset: len_offset,
884             access_size: u8::try_from(ir::types::I32.bytes()).unwrap(),
885         },
886     );
887     let result = builder.ins().load(
888         ir::types::I32,
889         ir::MemFlags::trusted().with_readonly(),
890         len_field,
891         0,
892     );
893     log::trace!("translate_array_len(..) -> {result:?}");
894     Ok(result)
895 }
896 
897 struct ArraySizeInfo {
898     /// The `i32` size of the whole array object, in bytes.
899     obj_size: ir::Value,
900 
901     /// The `i32` size of each one of the array's elements, in bytes.
902     one_elem_size: ir::Value,
903 
904     /// The `i32` size of the array's base object, in bytes. This is also the
905     /// offset from the start of the array object to its elements.
906     base_size: ir::Value,
907 }
908 
909 /// Emit code to get the dynamic size (in bytes) of a whole array object, along
910 /// with some other related bits.
emit_array_size_info( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, array_type_index: ModuleInternedTypeIndex, array_len: ir::Value, ) -> ArraySizeInfo911 fn emit_array_size_info(
912     func_env: &mut FuncEnvironment<'_>,
913     builder: &mut FunctionBuilder<'_>,
914     array_type_index: ModuleInternedTypeIndex,
915     // `i32` value containing the array's length.
916     array_len: ir::Value,
917 ) -> ArraySizeInfo {
918     let array_layout = func_env.array_layout(array_type_index);
919 
920     // Note that we check for overflow below because we can't trust the array's
921     // length: it came from inside the GC heap.
922     //
923     // We check for 32-bit multiplication overflow by performing a 64-bit
924     // multiplication and testing the high bits.
925     let one_elem_size = builder
926         .ins()
927         .iconst(ir::types::I64, i64::from(array_layout.elem_size));
928     let array_len = builder.ins().uextend(ir::types::I64, array_len);
929     let all_elems_size = builder.ins().imul(one_elem_size, array_len);
930 
931     let high_bits = builder.ins().ushr_imm(all_elems_size, 32);
932     builder.ins().trapnz(high_bits, TRAP_INTERNAL_ASSERT);
933 
934     let all_elems_size = builder.ins().ireduce(ir::types::I32, all_elems_size);
935     let base_size = builder
936         .ins()
937         .iconst(ir::types::I32, i64::from(array_layout.base_size));
938     let obj_size =
939         builder
940             .ins()
941             .uadd_overflow_trap(all_elems_size, base_size, TRAP_INTERNAL_ASSERT);
942 
943     let one_elem_size = builder.ins().ireduce(ir::types::I32, one_elem_size);
944 
945     ArraySizeInfo {
946         obj_size,
947         one_elem_size,
948         base_size,
949     }
950 }
951 
952 /// Get the bounds-checked address of an element in an array.
953 ///
954 /// The emitted code will trap if `index >= array.length`.
955 ///
956 /// Returns the `ir::Value` containing the address of the `index`th element in
957 /// the array. You may read or write a value of the array's element type at this
958 /// address. You may not use it for any other kind of access, nor reuse this
959 /// value across GC safepoints.
array_elem_addr( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, array_type_index: ModuleInternedTypeIndex, array_ref: ir::Value, index: ir::Value, ) -> ir::Value960 fn array_elem_addr(
961     func_env: &mut FuncEnvironment<'_>,
962     builder: &mut FunctionBuilder<'_>,
963     array_type_index: ModuleInternedTypeIndex,
964     array_ref: ir::Value,
965     index: ir::Value,
966 ) -> ir::Value {
967     // First, assert that `index < array.length`.
968     //
969     // This check is visible at the Wasm-semantics level.
970     //
971     // TODO: We should emit spectre-safe bounds checks for array accesses (if
972     // configured) but we don't currently have a great way to do that here. The
973     // proper solution is to use linear memories to back GC heaps and reuse the
974     // code in `bounds_check.rs` to implement these bounds checks. That is all
975     // planned, but not yet implemented.
976 
977     let len = translate_array_len(func_env, builder, array_ref).unwrap();
978 
979     let in_bounds = builder.ins().icmp(IntCC::UnsignedLessThan, index, len);
980     func_env.trapz(builder, in_bounds, crate::TRAP_ARRAY_OUT_OF_BOUNDS);
981 
982     // Compute the size (in bytes) of the whole array object.
983     let ArraySizeInfo {
984         obj_size,
985         one_elem_size,
986         base_size,
987     } = emit_array_size_info(func_env, builder, array_type_index, len);
988 
989     // Compute the offset of the `index`th element within the array object.
990     //
991     // NB: no need to check for overflow here, since at this point we know that
992     // `len * elem_size + base_size` did not overflow and `i < len`.
993     let offset_in_elems = builder.ins().imul(index, one_elem_size);
994     let offset_in_array = builder.ins().iadd(offset_in_elems, base_size);
995 
996     // Finally, use the object size and element offset we just computed to
997     // perform our implementation-internal bounds checks.
998     //
999     // Checking the whole object's size, rather than the `index`th element's
1000     // size allows these bounds checks to be deduplicated across repeated
1001     // accesses to the same array at different indices.
1002     //
1003     // This check should not be visible to Wasm, and serve to protect us from
1004     // our own implementation bugs. The goal is to keep any potential widgets
1005     // confined within the GC heap, and turn what would otherwise be a security
1006     // vulnerability into a simple bug.
1007     //
1008     // TODO: Ideally we should fold the first Wasm-visible bounds check into
1009     // this internal bounds check, so that we aren't performing multiple,
1010     // redundant bounds checks. But we should figure out how to do this in a way
1011     // that doesn't defeat the object-size bounds checking's deduplication
1012     // mentioned above.
1013     func_env.prepare_gc_ref_access(
1014         builder,
1015         array_ref,
1016         BoundsCheck::DynamicObjectField {
1017             offset: offset_in_array,
1018             object_size: obj_size,
1019         },
1020     )
1021 }
1022 
translate_array_get( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder, array_type_index: TypeIndex, array_ref: ir::Value, index: ir::Value, extension: Option<Extension>, ) -> WasmResult<ir::Value>1023 pub fn translate_array_get(
1024     func_env: &mut FuncEnvironment<'_>,
1025     builder: &mut FunctionBuilder,
1026     array_type_index: TypeIndex,
1027     array_ref: ir::Value,
1028     index: ir::Value,
1029     extension: Option<Extension>,
1030 ) -> WasmResult<ir::Value> {
1031     log::trace!("translate_array_get({array_type_index:?}, {array_ref:?}, {index:?})");
1032 
1033     emit_gc_kind_assert(func_env, builder, array_ref, VMGcKind::ArrayRef);
1034 
1035     let array_type_index = func_env.module.types[array_type_index].unwrap_module_type_index();
1036     let elem_addr = array_elem_addr(func_env, builder, array_type_index, array_ref, index);
1037 
1038     let array_ty = func_env.types.unwrap_array(array_type_index)?;
1039     let elem_ty = array_ty.0.element_type;
1040 
1041     let result = read_field_at_addr(func_env, builder, elem_ty, elem_addr, extension)?;
1042     log::trace!("translate_array_get(..) -> {result:?}");
1043     Ok(result)
1044 }
1045 
translate_array_set( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder, array_type_index: TypeIndex, array_ref: ir::Value, index: ir::Value, value: ir::Value, ) -> WasmResult<()>1046 pub fn translate_array_set(
1047     func_env: &mut FuncEnvironment<'_>,
1048     builder: &mut FunctionBuilder,
1049     array_type_index: TypeIndex,
1050     array_ref: ir::Value,
1051     index: ir::Value,
1052     value: ir::Value,
1053 ) -> WasmResult<()> {
1054     log::trace!("translate_array_set({array_type_index:?}, {array_ref:?}, {index:?}, {value:?})");
1055 
1056     emit_gc_kind_assert(func_env, builder, array_ref, VMGcKind::ArrayRef);
1057 
1058     let array_type_index = func_env.module.types[array_type_index].unwrap_module_type_index();
1059     let elem_addr = array_elem_addr(func_env, builder, array_type_index, array_ref, index);
1060 
1061     let array_ty = func_env.types.unwrap_array(array_type_index)?;
1062     let elem_ty = array_ty.0.element_type;
1063 
1064     write_field_at_addr(func_env, builder, elem_ty, elem_addr, value)?;
1065 
1066     log::trace!("translate_array_set: finished");
1067     Ok(())
1068 }
1069 
translate_ref_test( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, test_ty: WasmRefType, val: ir::Value, val_ty: WasmRefType, ) -> WasmResult<ir::Value>1070 pub fn translate_ref_test(
1071     func_env: &mut FuncEnvironment<'_>,
1072     builder: &mut FunctionBuilder<'_>,
1073     test_ty: WasmRefType,
1074     val: ir::Value,
1075     val_ty: WasmRefType,
1076 ) -> WasmResult<ir::Value> {
1077     log::trace!("translate_ref_test({test_ty:?}, {val:?})");
1078 
1079     // First special case: testing for references to bottom types.
1080     if test_ty.heap_type.is_bottom() {
1081         let result = if test_ty.nullable {
1082             // All null references (within the same type hierarchy) match null
1083             // references to the bottom type.
1084             func_env.translate_ref_is_null(builder.cursor(), val, val_ty)?
1085         } else {
1086             // `ref.test` is always false for non-nullable bottom types, as the
1087             // bottom types are uninhabited.
1088             builder.ins().iconst(ir::types::I32, 0)
1089         };
1090         log::trace!("translate_ref_test(..) -> {result:?}");
1091         return Ok(result);
1092     }
1093 
1094     // And because `ref.test heap_ty` is only valid on operands whose type is in
1095     // the same type hierarchy as `heap_ty`, if `heap_ty` is its hierarchy's top
1096     // type, we only need to worry about whether we are testing for nullability
1097     // or not.
1098     if test_ty.heap_type.is_top() {
1099         let result = if test_ty.nullable {
1100             builder.ins().iconst(ir::types::I32, 1)
1101         } else {
1102             let is_null = func_env.translate_ref_is_null(builder.cursor(), val, val_ty)?;
1103             let zero = builder.ins().iconst(ir::types::I32, 0);
1104             let one = builder.ins().iconst(ir::types::I32, 1);
1105             builder.ins().select(is_null, zero, one)
1106         };
1107         log::trace!("translate_ref_test(..) -> {result:?}");
1108         return Ok(result);
1109     }
1110 
1111     // `i31ref`s are a little interesting because they don't point to GC
1112     // objects; we test the bit pattern of the reference itself.
1113     if test_ty.heap_type == WasmHeapType::I31 {
1114         let i31_mask = builder.ins().iconst(
1115             ir::types::I32,
1116             i64::from(wasmtime_environ::I31_DISCRIMINANT),
1117         );
1118         let is_i31 = builder.ins().band(val, i31_mask);
1119         let result = if test_ty.nullable {
1120             let is_null = func_env.translate_ref_is_null(builder.cursor(), val, val_ty)?;
1121             builder.ins().bor(is_null, is_i31)
1122         } else {
1123             is_i31
1124         };
1125         log::trace!("translate_ref_test(..) -> {result:?}");
1126         return Ok(result);
1127     }
1128 
1129     // Otherwise, in the general case, we need to inspect our given object's
1130     // actual type, which also requires null-checking and i31-checking it.
1131 
1132     let is_any_hierarchy = test_ty.heap_type.top() == WasmHeapTopType::Any;
1133 
1134     let non_null_block = builder.create_block();
1135     let non_null_non_i31_block = builder.create_block();
1136     let continue_block = builder.create_block();
1137 
1138     // Current block: check if the reference is null and branch appropriately.
1139     let is_null = func_env.translate_ref_is_null(builder.cursor(), val, val_ty)?;
1140     let result_when_is_null = builder
1141         .ins()
1142         .iconst(ir::types::I32, test_ty.nullable as i64);
1143     builder.ins().brif(
1144         is_null,
1145         continue_block,
1146         &[result_when_is_null.into()],
1147         non_null_block,
1148         &[],
1149     );
1150 
1151     // Non-null block: We know the GC ref is non-null, but we need to also check
1152     // for `i31` references that don't point to GC objects.
1153     builder.switch_to_block(non_null_block);
1154     log::trace!("translate_ref_test: non-null ref block");
1155     if is_any_hierarchy {
1156         let i31_mask = builder.ins().iconst(
1157             ir::types::I32,
1158             i64::from(wasmtime_environ::I31_DISCRIMINANT),
1159         );
1160         let is_i31 = builder.ins().band(val, i31_mask);
1161         // If it is an `i31`, then create the result value based on whether we
1162         // want `i31`s to pass the test or not.
1163         let result_when_is_i31 = builder.ins().iconst(
1164             ir::types::I32,
1165             matches!(
1166                 test_ty.heap_type,
1167                 WasmHeapType::Any | WasmHeapType::Eq | WasmHeapType::I31
1168             ) as i64,
1169         );
1170         builder.ins().brif(
1171             is_i31,
1172             continue_block,
1173             &[result_when_is_i31.into()],
1174             non_null_non_i31_block,
1175             &[],
1176         );
1177     } else {
1178         // If we aren't testing the `any` hierarchy, the reference cannot be an
1179         // `i31ref`. Jump directly to the non-null and non-i31 block; rely on
1180         // branch folding during lowering to clean this up.
1181         builder.ins().jump(non_null_non_i31_block, &[]);
1182     }
1183 
1184     // Non-null and non-i31 block: Read the actual `VMGcKind` or
1185     // `VMSharedTypeIndex` out of the object's header and check whether it
1186     // matches the expected type.
1187     builder.switch_to_block(non_null_non_i31_block);
1188     log::trace!("translate_ref_test: non-null and non-i31 ref block");
1189     let check_header_kind = |func_env: &mut FuncEnvironment<'_>,
1190                              builder: &mut FunctionBuilder,
1191                              val: ir::Value,
1192                              expected_kind: VMGcKind|
1193      -> ir::Value {
1194         let kind_addr = func_env.prepare_gc_ref_access(
1195             builder,
1196             val,
1197             BoundsCheck::StaticObjectField {
1198                 offset: wasmtime_environ::VM_GC_HEADER_KIND_OFFSET,
1199                 access_size: wasmtime_environ::VM_GC_KIND_SIZE,
1200                 object_size: wasmtime_environ::VM_GC_HEADER_SIZE,
1201             },
1202         );
1203         let actual_kind = builder.ins().load(
1204             ir::types::I32,
1205             ir::MemFlags::trusted().with_readonly(),
1206             kind_addr,
1207             0,
1208         );
1209         let expected_kind = builder
1210             .ins()
1211             .iconst(ir::types::I32, i64::from(expected_kind.as_u32()));
1212         // Inline version of `VMGcKind::matches`.
1213         let and = builder.ins().band(actual_kind, expected_kind);
1214         let kind_matches = builder
1215             .ins()
1216             .icmp(ir::condcodes::IntCC::Equal, and, expected_kind);
1217         builder.ins().uextend(ir::types::I32, kind_matches)
1218     };
1219     let result = match test_ty.heap_type {
1220         WasmHeapType::Any
1221         | WasmHeapType::None
1222         | WasmHeapType::Extern
1223         | WasmHeapType::NoExtern
1224         | WasmHeapType::Func
1225         | WasmHeapType::NoFunc
1226         | WasmHeapType::Cont
1227         | WasmHeapType::NoCont
1228         | WasmHeapType::Exn
1229         | WasmHeapType::NoExn
1230         | WasmHeapType::I31 => unreachable!("handled top, bottom, and i31 types above"),
1231 
1232         // For these abstract but non-top and non-bottom types, we check the
1233         // `VMGcKind` that is in the object's header.
1234         WasmHeapType::Eq => check_header_kind(func_env, builder, val, VMGcKind::EqRef),
1235         WasmHeapType::Struct => check_header_kind(func_env, builder, val, VMGcKind::StructRef),
1236         WasmHeapType::Array => check_header_kind(func_env, builder, val, VMGcKind::ArrayRef),
1237 
1238         // For concrete types, we need to do a full subtype check between the
1239         // `VMSharedTypeIndex` in the object's header and the
1240         // `ModuleInternedTypeIndex` we have here.
1241         //
1242         // TODO: This check should ideally be done inline, but we don't have a
1243         // good way to access the `TypeRegistry`'s supertypes arrays from Wasm
1244         // code at the moment.
1245         WasmHeapType::ConcreteArray(ty)
1246         | WasmHeapType::ConcreteStruct(ty)
1247         | WasmHeapType::ConcreteExn(ty) => {
1248             let expected_interned_ty = ty.unwrap_module_type_index();
1249             let expected_shared_ty =
1250                 func_env.module_interned_to_shared_ty(&mut builder.cursor(), expected_interned_ty);
1251 
1252             let ty_addr = func_env.prepare_gc_ref_access(
1253                 builder,
1254                 val,
1255                 BoundsCheck::StaticOffset {
1256                     offset: wasmtime_environ::VM_GC_HEADER_TYPE_INDEX_OFFSET,
1257                     access_size: func_env.offsets.size_of_vmshared_type_index(),
1258                 },
1259             );
1260             let actual_shared_ty = builder.ins().load(
1261                 ir::types::I32,
1262                 ir::MemFlags::trusted().with_readonly(),
1263                 ty_addr,
1264                 0,
1265             );
1266 
1267             func_env.is_subtype(builder, actual_shared_ty, expected_shared_ty)
1268         }
1269 
1270         // Same as for concrete arrays and structs except that a `VMFuncRef`
1271         // doesn't begin with a `VMGcHeader` and is a raw pointer rather than GC
1272         // heap index.
1273         WasmHeapType::ConcreteFunc(ty) => {
1274             let expected_interned_ty = ty.unwrap_module_type_index();
1275             let expected_shared_ty =
1276                 func_env.module_interned_to_shared_ty(&mut builder.cursor(), expected_interned_ty);
1277 
1278             let actual_shared_ty = func_env.load_funcref_type_index(
1279                 &mut builder.cursor(),
1280                 ir::MemFlags::trusted().with_readonly(),
1281                 val,
1282             );
1283 
1284             func_env.is_subtype(builder, actual_shared_ty, expected_shared_ty)
1285         }
1286         WasmHeapType::ConcreteCont(_) => {
1287             // TODO(#10248) GC integration for stack switching
1288             return Err(wasmtime_environ::WasmError::Unsupported(
1289                 "Stack switching feature not compatible with GC, yet".to_string(),
1290             ));
1291         }
1292     };
1293     builder.ins().jump(continue_block, &[result.into()]);
1294 
1295     // Control flow join point with the result.
1296     builder.switch_to_block(continue_block);
1297     let result = builder.append_block_param(continue_block, ir::types::I32);
1298     log::trace!("translate_ref_test(..) -> {result:?}");
1299 
1300     builder.seal_block(non_null_block);
1301     builder.seal_block(non_null_non_i31_block);
1302     builder.seal_block(continue_block);
1303 
1304     Ok(result)
1305 }
1306 
uextend_i32_to_pointer_type( builder: &mut FunctionBuilder, pointer_type: ir::Type, value: ir::Value, ) -> ir::Value1307 fn uextend_i32_to_pointer_type(
1308     builder: &mut FunctionBuilder,
1309     pointer_type: ir::Type,
1310     value: ir::Value,
1311 ) -> ir::Value {
1312     assert_eq!(builder.func.dfg.value_type(value), ir::types::I32);
1313     match pointer_type {
1314         ir::types::I32 => value,
1315         ir::types::I64 => builder.ins().uextend(ir::types::I64, value),
1316         _ => unreachable!(),
1317     }
1318 }
1319 
1320 /// Emit CLIF to compute an array object's total size, given the dynamic length
1321 /// in its initialization.
1322 ///
1323 /// Traps if the size overflows.
1324 #[cfg_attr(
1325     not(any(feature = "gc-drc", feature = "gc-null")),
1326     expect(dead_code, reason = "easier to define")
1327 )]
emit_array_size( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, array_layout: &GcArrayLayout, len: ir::Value, ) -> ir::Value1328 fn emit_array_size(
1329     func_env: &mut FuncEnvironment<'_>,
1330     builder: &mut FunctionBuilder<'_>,
1331     array_layout: &GcArrayLayout,
1332     len: ir::Value,
1333 ) -> ir::Value {
1334     let base_size = builder
1335         .ins()
1336         .iconst(ir::types::I32, i64::from(array_layout.base_size));
1337 
1338     // `elems_size = len * elem_size`
1339     //
1340     // Check for multiplication overflow and trap if it occurs, since that
1341     // means Wasm is attempting to allocate an array that is larger than our
1342     // implementation limits. (Note: there is no standard implementation
1343     // limit for array length beyond `u32::MAX`.)
1344     //
1345     // We implement this check by encoding our logically-32-bit operands as
1346     // i64 values, doing a 64-bit multiplication, and then checking the high
1347     // 32 bits of the multiplication's result. If the high 32 bits are not
1348     // all zeros, then the multiplication overflowed.
1349     debug_assert_eq!(builder.func.dfg.value_type(len), ir::types::I32);
1350     let len = builder.ins().uextend(ir::types::I64, len);
1351     let elems_size_64 = builder
1352         .ins()
1353         .imul_imm(len, i64::from(array_layout.elem_size));
1354     let high_bits = builder.ins().ushr_imm(elems_size_64, 32);
1355     func_env.trapnz(builder, high_bits, crate::TRAP_ALLOCATION_TOO_LARGE);
1356     let elems_size = builder.ins().ireduce(ir::types::I32, elems_size_64);
1357 
1358     // And if adding the base size and elements size overflows, then the
1359     // allocation is too large.
1360     let size = func_env.uadd_overflow_trap(
1361         builder,
1362         base_size,
1363         elems_size,
1364         crate::TRAP_ALLOCATION_TOO_LARGE,
1365     );
1366 
1367     size
1368 }
1369 
1370 /// Common helper for struct-field initialization that can be reused across
1371 /// collectors.
1372 #[cfg_attr(
1373     not(any(feature = "gc-drc", feature = "gc-null")),
1374     expect(dead_code, reason = "easier to define")
1375 )]
initialize_struct_fields( func_env: &mut FuncEnvironment<'_>, builder: &mut FunctionBuilder<'_>, struct_ty: ModuleInternedTypeIndex, raw_ptr_to_struct: ir::Value, field_values: &[ir::Value], mut init_field: impl FnMut( &mut FuncEnvironment<'_>, &mut FunctionBuilder<'_>, WasmStorageType, ir::Value, ir::Value, ) -> WasmResult<()>, ) -> WasmResult<()>1376 fn initialize_struct_fields(
1377     func_env: &mut FuncEnvironment<'_>,
1378     builder: &mut FunctionBuilder<'_>,
1379     struct_ty: ModuleInternedTypeIndex,
1380     raw_ptr_to_struct: ir::Value,
1381     field_values: &[ir::Value],
1382     mut init_field: impl FnMut(
1383         &mut FuncEnvironment<'_>,
1384         &mut FunctionBuilder<'_>,
1385         WasmStorageType,
1386         ir::Value,
1387         ir::Value,
1388     ) -> WasmResult<()>,
1389 ) -> WasmResult<()> {
1390     let struct_layout = func_env.struct_or_exn_layout(struct_ty);
1391     let struct_size = struct_layout.size;
1392     let field_offsets: SmallVec<[_; 8]> = struct_layout.fields.iter().map(|f| f.offset).collect();
1393     assert_eq!(field_offsets.len(), field_values.len());
1394 
1395     assert!(!func_env.types[struct_ty].composite_type.shared);
1396     let fields = match &func_env.types[struct_ty].composite_type.inner {
1397         WasmCompositeInnerType::Struct(s) => &s.fields,
1398         WasmCompositeInnerType::Exn(e) => &e.fields,
1399         _ => panic!("Not a struct or exception type"),
1400     };
1401 
1402     let field_types: SmallVec<[_; 8]> = fields.iter().cloned().collect();
1403     assert_eq!(field_types.len(), field_values.len());
1404 
1405     for ((ty, val), offset) in field_types.into_iter().zip(field_values).zip(field_offsets) {
1406         let size_of_access = wasmtime_environ::byte_size_of_wasm_ty_in_gc_heap(&ty.element_type);
1407         assert!(offset + size_of_access <= struct_size);
1408         let field_addr = builder.ins().iadd_imm(raw_ptr_to_struct, i64::from(offset));
1409         init_field(func_env, builder, ty.element_type, field_addr, *val)?;
1410     }
1411 
1412     Ok(())
1413 }
1414 
1415 impl FuncEnvironment<'_> {
gc_layout(&mut self, type_index: ModuleInternedTypeIndex) -> &GcLayout1416     fn gc_layout(&mut self, type_index: ModuleInternedTypeIndex) -> &GcLayout {
1417         // Lazily compute and cache the layout.
1418         if !self.ty_to_gc_layout.contains_key(&type_index) {
1419             let ty = &self.types[type_index].composite_type;
1420             let layout = gc_compiler(self)
1421                 .unwrap()
1422                 .layouts()
1423                 .gc_layout(ty)
1424                 .expect("should only call `FuncEnvironment::gc_layout` for GC types");
1425             self.ty_to_gc_layout.insert(type_index, layout);
1426         }
1427 
1428         self.ty_to_gc_layout.get(&type_index).unwrap()
1429     }
1430 
1431     /// Get the `GcArrayLayout` for the array type at the given `type_index`.
array_layout(&mut self, type_index: ModuleInternedTypeIndex) -> &GcArrayLayout1432     fn array_layout(&mut self, type_index: ModuleInternedTypeIndex) -> &GcArrayLayout {
1433         self.gc_layout(type_index).unwrap_array()
1434     }
1435 
1436     /// Get the `GcStructLayout` for the struct or exception type at the given `type_index`.
struct_or_exn_layout(&mut self, type_index: ModuleInternedTypeIndex) -> &GcStructLayout1437     fn struct_or_exn_layout(&mut self, type_index: ModuleInternedTypeIndex) -> &GcStructLayout {
1438         let result = self.gc_layout(type_index).unwrap_struct();
1439         result
1440     }
1441 
1442     /// Get or create the global for our GC heap's base pointer.
get_gc_heap_base_global(&mut self, func: &mut ir::Function) -> ir::GlobalValue1443     fn get_gc_heap_base_global(&mut self, func: &mut ir::Function) -> ir::GlobalValue {
1444         if let Some(base) = self.gc_heap_base {
1445             return base;
1446         }
1447 
1448         let store_context_ptr = self.get_vmstore_context_ptr_global(func);
1449         let offset = self.offsets.ptr.vmstore_context_gc_heap_base();
1450 
1451         let mut flags = ir::MemFlags::trusted();
1452         if !self
1453             .tunables
1454             .gc_heap_memory_type()
1455             .memory_may_move(self.tunables)
1456         {
1457             flags.set_readonly();
1458             flags.set_can_move();
1459         }
1460 
1461         let base = func.create_global_value(ir::GlobalValueData::Load {
1462             base: store_context_ptr,
1463             offset: Offset32::new(offset.into()),
1464             global_type: self.pointer_type(),
1465             flags,
1466         });
1467 
1468         self.gc_heap_base = Some(base);
1469         base
1470     }
1471 
1472     /// Get the GC heap's base.
1473     #[cfg(any(feature = "gc-null", feature = "gc-drc"))]
get_gc_heap_base(&mut self, builder: &mut FunctionBuilder) -> ir::Value1474     fn get_gc_heap_base(&mut self, builder: &mut FunctionBuilder) -> ir::Value {
1475         let global = self.get_gc_heap_base_global(&mut builder.func);
1476         builder.ins().global_value(self.pointer_type(), global)
1477     }
1478 
get_gc_heap_bound_global(&mut self, func: &mut ir::Function) -> ir::GlobalValue1479     fn get_gc_heap_bound_global(&mut self, func: &mut ir::Function) -> ir::GlobalValue {
1480         if let Some(bound) = self.gc_heap_bound {
1481             return bound;
1482         }
1483         let store_context_ptr = self.get_vmstore_context_ptr_global(func);
1484         let offset = self.offsets.ptr.vmstore_context_gc_heap_current_length();
1485         let bound = func.create_global_value(ir::GlobalValueData::Load {
1486             base: store_context_ptr,
1487             offset: Offset32::new(offset.into()),
1488             global_type: self.pointer_type(),
1489             flags: ir::MemFlags::trusted(),
1490         });
1491         self.gc_heap_bound = Some(bound);
1492         bound
1493     }
1494 
1495     /// Get the GC heap's bound.
1496     #[cfg(feature = "gc-null")]
get_gc_heap_bound(&mut self, builder: &mut FunctionBuilder) -> ir::Value1497     fn get_gc_heap_bound(&mut self, builder: &mut FunctionBuilder) -> ir::Value {
1498         let global = self.get_gc_heap_bound_global(&mut builder.func);
1499         builder.ins().global_value(self.pointer_type(), global)
1500     }
1501 
1502     /// Get or create the `Heap` for our GC heap.
get_gc_heap(&mut self, func: &mut ir::Function) -> Heap1503     fn get_gc_heap(&mut self, func: &mut ir::Function) -> Heap {
1504         if let Some(heap) = self.gc_heap {
1505             return heap;
1506         }
1507 
1508         let base = self.get_gc_heap_base_global(func);
1509         let bound = self.get_gc_heap_bound_global(func);
1510         let memory = self.tunables.gc_heap_memory_type();
1511         let heap = self.heaps.push(HeapData {
1512             base,
1513             bound,
1514             memory,
1515         });
1516         self.gc_heap = Some(heap);
1517         heap
1518     }
1519 
1520     /// Get the raw pointer of `gc_ref[offset]` bounds checked for an access of
1521     /// `size` bytes.
1522     ///
1523     /// The given `gc_ref` must be a non-null, non-i31 GC reference.
1524     ///
1525     /// If `check` is a `BoundsCheck::Object`, then it is the callers
1526     /// responsibility to ensure that `offset + access_size <= object_size`.
1527     ///
1528     /// Returns a raw pointer to `gc_ref[offset]` -- not a raw pointer to the GC
1529     /// object itself (unless `offset` happens to be `0`). This raw pointer may
1530     /// be used to read or write up to as many bytes as described by `bound`. Do
1531     /// NOT attempt accesses bytes outside of `bound`; that may lead to
1532     /// unchecked out-of-bounds accesses.
1533     ///
1534     /// This method is collector-agnostic.
prepare_gc_ref_access( &mut self, builder: &mut FunctionBuilder, gc_ref: ir::Value, bounds_check: BoundsCheck, ) -> ir::Value1535     fn prepare_gc_ref_access(
1536         &mut self,
1537         builder: &mut FunctionBuilder,
1538         gc_ref: ir::Value,
1539         bounds_check: BoundsCheck,
1540     ) -> ir::Value {
1541         log::trace!("prepare_gc_ref_access({gc_ref:?}, {bounds_check:?})");
1542         assert_eq!(builder.func.dfg.value_type(gc_ref), ir::types::I32);
1543 
1544         let gc_heap = self.get_gc_heap(&mut builder.func);
1545         let gc_heap = self.heaps[gc_heap].clone();
1546         let result = match crate::bounds_checks::bounds_check_and_compute_addr(
1547             builder,
1548             self,
1549             &gc_heap,
1550             gc_ref,
1551             bounds_check,
1552             crate::TRAP_INTERNAL_ASSERT,
1553         ) {
1554             Reachability::Reachable(v) => v,
1555             Reachability::Unreachable => {
1556                 // We are now in unreachable code, but we don't want to plumb
1557                 // through a bunch of `Reachability` through all of our callers,
1558                 // so just assert we won't reach here and return `null`
1559                 let null = builder.ins().iconst(self.pointer_type(), 0);
1560                 builder.ins().trapz(null, crate::TRAP_INTERNAL_ASSERT);
1561                 null
1562             }
1563         };
1564         log::trace!("prepare_gc_ref_access(..) -> {result:?}");
1565         result
1566     }
1567 
1568     /// Emit checks (if necessary) for whether the given `gc_ref` is null or is
1569     /// an `i31ref`.
1570     ///
1571     /// Takes advantage of static information based on `ty` as to whether the GC
1572     /// reference is nullable or can ever be an `i31`.
1573     ///
1574     /// Returns an `ir::Value` that is an `i32` will be non-zero if the GC
1575     /// reference is null or is an `i31ref`; otherwise, it will be zero.
1576     ///
1577     /// This method is collector-agnostic.
1578     #[cfg_attr(
1579         not(feature = "gc-drc"),
1580         expect(dead_code, reason = "easier to define")
1581     )]
gc_ref_is_null_or_i31( &mut self, builder: &mut FunctionBuilder, ty: WasmRefType, gc_ref: ir::Value, ) -> ir::Value1582     fn gc_ref_is_null_or_i31(
1583         &mut self,
1584         builder: &mut FunctionBuilder,
1585         ty: WasmRefType,
1586         gc_ref: ir::Value,
1587     ) -> ir::Value {
1588         assert_eq!(builder.func.dfg.value_type(gc_ref), ir::types::I32);
1589         assert!(ty.is_vmgcref_type_and_not_i31());
1590 
1591         let might_be_i31 = match ty.heap_type {
1592             // If we are definitely dealing with an i31, we shouldn't be
1593             // emitting dynamic checks for it, and the caller shouldn't call
1594             // this function. Should have been caught by the assertion at the
1595             // start of the function.
1596             WasmHeapType::I31 => unreachable!(),
1597 
1598             // Could potentially be an i31.
1599             WasmHeapType::Any | WasmHeapType::Eq => true,
1600 
1601             // If it is definitely a struct, array, or uninhabited type, then it
1602             // is definitely not an i31.
1603             WasmHeapType::Array
1604             | WasmHeapType::ConcreteArray(_)
1605             | WasmHeapType::Struct
1606             | WasmHeapType::ConcreteStruct(_)
1607             | WasmHeapType::None => false,
1608 
1609             // Despite being a different type hierarchy, this *could* be an
1610             // `i31` if it is the result of
1611             //
1612             //     (extern.convert_any (ref.i31 ...))
1613             WasmHeapType::Extern => true,
1614 
1615             // Can only ever be `null`.
1616             WasmHeapType::NoExtern => false,
1617 
1618             WasmHeapType::Exn | WasmHeapType::ConcreteExn(_) | WasmHeapType::NoExn => false,
1619 
1620             // Wrong type hierarchy, and also funcrefs are not GC-managed
1621             // types. Should have been caught by the assertion at the start of
1622             // the function.
1623             WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
1624                 unreachable!()
1625             }
1626             WasmHeapType::Cont | WasmHeapType::ConcreteCont(_) | WasmHeapType::NoCont => {
1627                 unreachable!()
1628             }
1629         };
1630 
1631         match (ty.nullable, might_be_i31) {
1632             // This GC reference statically cannot be null nor an i31. (Let
1633             // Cranelift's optimizer const-propagate this value and erase any
1634             // unnecessary control flow resulting from branching on this value.)
1635             (false, false) => builder.ins().iconst(ir::types::I32, 0),
1636 
1637             // This GC reference is always non-null, but might be an i31.
1638             (false, true) => builder.ins().band_imm(gc_ref, i64::from(I31_DISCRIMINANT)),
1639 
1640             // This GC reference might be null, but can never be an i31.
1641             (true, false) => builder.ins().icmp_imm(IntCC::Equal, gc_ref, 0),
1642 
1643             // Fully general case: this GC reference could be either null or an
1644             // i31.
1645             (true, true) => {
1646                 let is_i31 = builder.ins().band_imm(gc_ref, i64::from(I31_DISCRIMINANT));
1647                 let is_null = builder.ins().icmp_imm(IntCC::Equal, gc_ref, 0);
1648                 let is_null = builder.ins().uextend(ir::types::I32, is_null);
1649                 builder.ins().bor(is_i31, is_null)
1650             }
1651         }
1652     }
1653 
1654     // Emit code to check whether `a <: b` for two `VMSharedTypeIndex`es.
is_subtype( &mut self, builder: &mut FunctionBuilder<'_>, a: ir::Value, b: ir::Value, ) -> ir::Value1655     pub(crate) fn is_subtype(
1656         &mut self,
1657         builder: &mut FunctionBuilder<'_>,
1658         a: ir::Value,
1659         b: ir::Value,
1660     ) -> ir::Value {
1661         log::trace!("is_subtype({a:?}, {b:?})");
1662 
1663         let diff_tys_block = builder.create_block();
1664         let continue_block = builder.create_block();
1665 
1666         // Current block: fast path for when `a == b`.
1667         log::trace!("is_subtype: fast path check for exact same types");
1668         let same_ty = builder.ins().icmp(IntCC::Equal, a, b);
1669         let same_ty = builder.ins().uextend(ir::types::I32, same_ty);
1670         builder.ins().brif(
1671             same_ty,
1672             continue_block,
1673             &[same_ty.into()],
1674             diff_tys_block,
1675             &[],
1676         );
1677 
1678         // Different types block: fall back to the `is_subtype` libcall.
1679         builder.switch_to_block(diff_tys_block);
1680         log::trace!("is_subtype: slow path to do full `is_subtype` libcall");
1681         let is_subtype = self.builtin_functions.is_subtype(builder.func);
1682         let vmctx = self.vmctx_val(&mut builder.cursor());
1683         let call_inst = builder.ins().call(is_subtype, &[vmctx, a, b]);
1684         let result = builder.func.dfg.first_result(call_inst);
1685         builder.ins().jump(continue_block, &[result.into()]);
1686 
1687         // Continue block: join point for the result.
1688         builder.switch_to_block(continue_block);
1689         let result = builder.append_block_param(continue_block, ir::types::I32);
1690         log::trace!("is_subtype(..) -> {result:?}");
1691 
1692         builder.seal_block(diff_tys_block);
1693         builder.seal_block(continue_block);
1694 
1695         result
1696     }
1697 }
1698