1use super::*;
8use crate::func_environ::FuncEnvironment;
9use cranelift_codegen::ir::{self, InstBuilder};
10use cranelift_frontend::FunctionBuilder;
11use wasmtime_environ::VMSharedTypeIndex;
12use wasmtime_environ::null::{EXCEPTION_TAG_DEFINED_OFFSET, EXCEPTION_TAG_INSTANCE_OFFSET};
13use wasmtime_environ::{
14 GcTypeLayouts, ModuleInternedTypeIndex, PtrSize, TypeIndex, VMGcKind, WasmRefType, WasmResult,
15 null::NullTypeLayouts,
16};
17
18#[derive(Default)]
19pub struct NullCompiler {
20 layouts: NullTypeLayouts,
21}
22
23impl NullCompiler {
24 fn emit_inline_alloc(
40 &mut self,
41 func_env: &mut FuncEnvironment<'_>,
42 builder: &mut FunctionBuilder,
43 kind: VMGcKind,
44 ty: Option<ModuleInternedTypeIndex>,
45 size: ir::Value,
46 align: ir::Value,
47 ) -> (ir::Value, ir::Value) {
48 log::trace!("emit_inline_alloc(kind={kind:?}, ty={ty:?}, size={size}, align={align})");
49
50 assert_eq!(builder.func.dfg.value_type(size), ir::types::I32);
51 assert_eq!(builder.func.dfg.value_type(align), ir::types::I32);
52
53 let current_block = builder.current_block().unwrap();
54 let continue_block = builder.create_block();
55 let grow_block = builder.create_block();
56
57 builder.ensure_inserted_block();
58 builder.insert_block_after(continue_block, current_block);
59 builder.insert_block_after(grow_block, continue_block);
60
61 let mask = builder
64 .ins()
65 .iconst(ir::types::I32, i64::from(VMGcKind::MASK));
66 let masked = builder.ins().band(size, mask);
67 func_env.trapnz(builder, masked, crate::TRAP_ALLOCATION_TOO_LARGE);
68
69 let pointer_type = func_env.pointer_type();
72 let vmctx = func_env.vmctx_val(&mut builder.cursor());
73 let ptr_to_next = builder.ins().load(
74 pointer_type,
75 ir::MemFlags::trusted().with_readonly(),
76 vmctx,
77 i32::from(func_env.offsets.ptr.vmctx_gc_heap_data()),
78 );
79 let next = builder
80 .ins()
81 .load(ir::types::I32, ir::MemFlags::trusted(), ptr_to_next, 0);
82
83 let minus_one = builder.ins().iconst(ir::types::I32, -1);
91 let align_minus_one = builder.ins().iadd(align, minus_one);
92 let next_plus_align_minus_one = func_env.uadd_overflow_trap(
93 builder,
94 next,
95 align_minus_one,
96 crate::TRAP_ALLOCATION_TOO_LARGE,
97 );
98 let not_align_minus_one = builder.ins().bnot(align_minus_one);
99 let aligned = builder
100 .ins()
101 .band(next_plus_align_minus_one, not_align_minus_one);
102
103 let end_of_object =
105 func_env.uadd_overflow_trap(builder, aligned, size, crate::TRAP_ALLOCATION_TOO_LARGE);
106 let uext_end_of_object = uextend_i32_to_pointer_type(builder, pointer_type, end_of_object);
107 let bound = func_env.get_gc_heap_bound(builder);
108 let is_in_bounds = builder.ins().icmp(
109 ir::condcodes::IntCC::UnsignedLessThanOrEqual,
110 uext_end_of_object,
111 bound,
112 );
113 builder
114 .ins()
115 .brif(is_in_bounds, continue_block, &[], grow_block, &[]);
116
117 log::trace!("emit_inline_alloc: grow_block");
118 builder.switch_to_block(grow_block);
119 builder.seal_block(grow_block);
120 builder.set_cold_block(grow_block);
121 let grow_gc_heap_builtin = func_env.builtin_functions.grow_gc_heap(builder.func);
122 let vmctx = func_env.vmctx_val(&mut builder.cursor());
123 let bytes_needed = builder.ins().isub(uext_end_of_object, bound);
124 let bytes_needed = match func_env.pointer_type() {
125 ir::types::I32 => builder.ins().uextend(ir::types::I64, bytes_needed),
126 ir::types::I64 => bytes_needed,
127 _ => unreachable!(),
128 };
129 builder
130 .ins()
131 .call(grow_gc_heap_builtin, &[vmctx, bytes_needed]);
132 builder.ins().jump(continue_block, &[]);
133
134 log::trace!("emit_inline_alloc: continue_block");
137 builder.switch_to_block(continue_block);
138 builder.seal_block(continue_block);
139
140 let base = func_env.get_gc_heap_base(builder);
145 let uext_aligned = uextend_i32_to_pointer_type(builder, pointer_type, aligned);
146 let ptr_to_object = builder.ins().iadd(base, uext_aligned);
147 let kind = builder
148 .ins()
149 .iconst(ir::types::I32, i64::from(kind.as_u32()));
150 let kind_and_size = builder.ins().bor(kind, size);
151 let ty = match ty {
152 Some(ty) => func_env.module_interned_to_shared_ty(&mut builder.cursor(), ty),
153 None => builder.ins().iconst(
154 func_env.vmshared_type_index_ty(),
155 i64::from(VMSharedTypeIndex::reserved_value().as_bits()),
156 ),
157 };
158 builder.ins().store(
159 ir::MemFlags::trusted(),
160 kind_and_size,
161 ptr_to_object,
162 i32::try_from(wasmtime_environ::VM_GC_HEADER_KIND_OFFSET).unwrap(),
163 );
164 builder.ins().store(
165 ir::MemFlags::trusted(),
166 ty,
167 ptr_to_object,
168 i32::try_from(wasmtime_environ::VM_GC_HEADER_TYPE_INDEX_OFFSET).unwrap(),
169 );
170 builder
171 .ins()
172 .store(ir::MemFlags::trusted(), end_of_object, ptr_to_next, 0);
173
174 log::trace!("emit_inline_alloc(..) -> ({aligned}, {ptr_to_object})");
175 (aligned, ptr_to_object)
176 }
177}
178
179impl GcCompiler for NullCompiler {
180 fn layouts(&self) -> &dyn GcTypeLayouts {
181 &self.layouts
182 }
183
184 fn alloc_array(
185 &mut self,
186 func_env: &mut FuncEnvironment<'_>,
187 builder: &mut FunctionBuilder<'_>,
188 array_type_index: TypeIndex,
189 init: super::ArrayInit<'_>,
190 ) -> WasmResult<ir::Value> {
191 let interned_type_index =
192 func_env.module.types[array_type_index].unwrap_module_type_index();
193 let ptr_ty = func_env.pointer_type();
194
195 let len_offset = gc_compiler(func_env)?.layouts().array_length_field_offset();
196 let array_layout = func_env.array_layout(interned_type_index).clone();
197 let base_size = array_layout.base_size;
198 let align = array_layout.align;
199 let len_to_elems_delta = base_size.checked_sub(len_offset).unwrap();
200
201 let len = init.len(&mut builder.cursor());
204 let size = emit_array_size(func_env, builder, &array_layout, len);
205
206 assert!(align.is_power_of_two());
208 let align = builder.ins().iconst(ir::types::I32, i64::from(align));
209 let (gc_ref, ptr_to_object) = self.emit_inline_alloc(
210 func_env,
211 builder,
212 VMGcKind::ArrayRef,
213 Some(interned_type_index),
214 size,
215 align,
216 );
217
218 let len_addr = builder.ins().iadd_imm(ptr_to_object, i64::from(len_offset));
224 let len = init.len(&mut builder.cursor());
225 builder
226 .ins()
227 .store(ir::MemFlags::trusted(), len, len_addr, 0);
228
229 let len_to_elems_delta = builder.ins().iconst(ptr_ty, i64::from(len_to_elems_delta));
231 let elems_addr = builder.ins().iadd(len_addr, len_to_elems_delta);
232 init.initialize(
233 func_env,
234 builder,
235 interned_type_index,
236 base_size,
237 size,
238 elems_addr,
239 |func_env, builder, elem_ty, elem_addr, val| {
240 write_field_at_addr(func_env, builder, elem_ty, elem_addr, val)
241 },
242 )?;
243
244 Ok(gc_ref)
245 }
246
247 fn alloc_struct(
248 &mut self,
249 func_env: &mut FuncEnvironment<'_>,
250 builder: &mut FunctionBuilder<'_>,
251 struct_type_index: TypeIndex,
252 field_vals: &[ir::Value],
253 ) -> WasmResult<ir::Value> {
254 let interned_type_index =
255 func_env.module.types[struct_type_index].unwrap_module_type_index();
256 let struct_layout = func_env.struct_or_exn_layout(interned_type_index);
257
258 let struct_size = struct_layout.size;
260 let struct_align = struct_layout.align;
261
262 assert_eq!(VMGcKind::MASK & struct_size, 0);
263 assert_eq!(VMGcKind::UNUSED_MASK & struct_size, struct_size);
264 let struct_size_val = builder.ins().iconst(ir::types::I32, i64::from(struct_size));
265
266 let align = builder
267 .ins()
268 .iconst(ir::types::I32, i64::from(struct_align));
269
270 let (struct_ref, raw_struct_pointer) = self.emit_inline_alloc(
271 func_env,
272 builder,
273 VMGcKind::StructRef,
274 Some(interned_type_index),
275 struct_size_val,
276 align,
277 );
278
279 initialize_struct_fields(
285 func_env,
286 builder,
287 interned_type_index,
288 raw_struct_pointer,
289 field_vals,
290 |func_env, builder, ty, field_addr, val| {
291 write_field_at_addr(func_env, builder, ty, field_addr, val)
292 },
293 )?;
294
295 Ok(struct_ref)
296 }
297
298 fn alloc_exn(
299 &mut self,
300 func_env: &mut FuncEnvironment<'_>,
301 builder: &mut FunctionBuilder<'_>,
302 tag_index: TagIndex,
303 field_vals: &[ir::Value],
304 instance_id: ir::Value,
305 tag: ir::Value,
306 ) -> WasmResult<ir::Value> {
307 let interned_type_index = func_env.module.tags[tag_index]
308 .exception
309 .unwrap_module_type_index();
310 let exn_layout = func_env.struct_or_exn_layout(interned_type_index);
311
312 let exn_size = exn_layout.size;
314 let exn_align = exn_layout.align;
315
316 assert_eq!(VMGcKind::MASK & exn_size, 0);
317 assert_eq!(VMGcKind::UNUSED_MASK & exn_size, exn_size);
318 let exn_size_val = builder.ins().iconst(ir::types::I32, i64::from(exn_size));
319
320 let align = builder.ins().iconst(ir::types::I32, i64::from(exn_align));
321
322 let (exn_ref, raw_exn_pointer) = self.emit_inline_alloc(
323 func_env,
324 builder,
325 VMGcKind::ExnRef,
326 Some(interned_type_index),
327 exn_size_val,
328 align,
329 );
330
331 initialize_struct_fields(
337 func_env,
338 builder,
339 interned_type_index,
340 raw_exn_pointer,
341 field_vals,
342 |func_env, builder, ty, field_addr, val| {
343 write_field_at_addr(func_env, builder, ty, field_addr, val)
344 },
345 )?;
346
347 let instance_id_addr = builder
349 .ins()
350 .iadd_imm(raw_exn_pointer, i64::from(EXCEPTION_TAG_INSTANCE_OFFSET));
351 write_field_at_addr(
352 func_env,
353 builder,
354 WasmStorageType::Val(WasmValType::I32),
355 instance_id_addr,
356 instance_id,
357 )?;
358 let tag_addr = builder
359 .ins()
360 .iadd_imm(raw_exn_pointer, i64::from(EXCEPTION_TAG_DEFINED_OFFSET));
361 write_field_at_addr(
362 func_env,
363 builder,
364 WasmStorageType::Val(WasmValType::I32),
365 tag_addr,
366 tag,
367 )?;
368
369 Ok(exn_ref)
370 }
371
372 fn translate_read_gc_reference(
373 &mut self,
374 _func_env: &mut FuncEnvironment<'_>,
375 builder: &mut FunctionBuilder,
376 _ty: WasmRefType,
377 src: ir::Value,
378 flags: ir::MemFlags,
379 ) -> WasmResult<ir::Value> {
380 Ok(builder.ins().load(ir::types::I32, flags, src, 0))
383 }
384
385 fn translate_write_gc_reference(
386 &mut self,
387 _func_env: &mut FuncEnvironment<'_>,
388 builder: &mut FunctionBuilder,
389 ty: WasmRefType,
390 dst: ir::Value,
391 new_val: ir::Value,
392 flags: ir::MemFlags,
393 ) -> WasmResult<()> {
394 unbarriered_store_gc_ref(builder, ty.heap_type, dst, new_val, flags)
395 }
396}