1 use cranelift_codegen::ir::BlockArg;
2 use itertools::{Either, Itertools};
3 
4 use cranelift_codegen::ir::condcodes::*;
5 use cranelift_codegen::ir::types::*;
6 use cranelift_codegen::ir::{self, MemFlags};
7 use cranelift_codegen::ir::{Block, BlockCall, InstBuilder, JumpTableData};
8 use cranelift_frontend::FunctionBuilder;
9 use wasmtime_environ::{PtrSize, TagIndex, TypeIndex, WasmResult, WasmValType, wasm_unsupported};
10 
11 fn control_context_size(triple: &target_lexicon::Triple) -> WasmResult<u8> {
12     match (triple.architecture, triple.operating_system) {
13         (target_lexicon::Architecture::X86_64, target_lexicon::OperatingSystem::Linux) => Ok(24),
14         _ => Err(wasm_unsupported!(
15             "stack switching not supported on {triple}"
16         )),
17     }
18 }
19 
20 use super::control_effect::ControlEffect;
21 use super::fatpointer;
22 
23 /// This module contains compile-time counterparts to types defined elsewhere.
24 pub(crate) mod stack_switching_helpers {
25     use core::marker::PhantomData;
26     use cranelift_codegen::ir;
27     use cranelift_codegen::ir::InstBuilder;
28     use cranelift_codegen::ir::condcodes::IntCC;
29     use cranelift_codegen::ir::types::*;
30     use cranelift_codegen::ir::{StackSlot, StackSlotKind::*};
31     use cranelift_frontend::FunctionBuilder;
32     use wasmtime_environ::PtrSize;
33 
34     /// Provides information about the layout of a type when it is used as an
35     /// element in a host array. This is used for `VMHostArrayRef`.
36     pub(crate) trait VMHostArrayEntry {
37         /// Returns `(align, size)` in bytes.
38         fn vmhostarray_entry_layout<P: wasmtime_environ::PtrSize>(p: &P) -> (u8, u32);
39     }
40 
41     impl VMHostArrayEntry for u128 {
42         fn vmhostarray_entry_layout<P: wasmtime_environ::PtrSize>(_p: &P) -> (u8, u32) {
43             (16, 16)
44         }
45     }
46 
47     impl<T> VMHostArrayEntry for *mut T {
48         fn vmhostarray_entry_layout<P: wasmtime_environ::PtrSize>(p: &P) -> (u8, u32) {
49             (p.size(), p.size().into())
50         }
51     }
52 
53     #[derive(Copy, Clone)]
54     pub struct VMContRef {
55         pub address: ir::Value,
56     }
57 
58     #[derive(Copy, Clone)]
59     pub struct VMHostArrayRef<T> {
60         /// Address of the VMHostArray we are referencing
61         address: ir::Value,
62 
63         /// The type parameter T is never used in the fields above. We still
64         /// want to have it for consistency with
65         /// `wasmtime_environ::Vector` and to use it in the associated
66         /// functions.
67         phantom: PhantomData<T>,
68     }
69 
70     pub type VMPayloads = VMHostArrayRef<u128>;
71 
72     // Actually a vector of *mut VMTagDefinition
73     pub type VMHandlerList = VMHostArrayRef<*mut u8>;
74 
75     /// Compile-time representation of wasmtime_environ::VMStackChain,
76     /// consisting of two `ir::Value`s.
77     pub struct VMStackChain {
78         discriminant: ir::Value,
79         payload: ir::Value,
80     }
81 
82     pub struct VMCommonStackInformation {
83         pub address: ir::Value,
84     }
85 
86     /// Compile-time representation of `crate::runtime::vm::stack::VMContinuationStack`.
87     pub struct VMContinuationStack {
88         /// This is NOT the "top of stack" address of the stack itself. In line
89         /// with how the (runtime) `FiberStack` type works, this is a pointer to
90         /// the TOS address.
91         tos_ptr: ir::Value,
92     }
93 
94     impl VMContRef {
95         pub fn new(address: ir::Value) -> VMContRef {
96             VMContRef { address }
97         }
98 
99         pub fn args<'a>(
100             &self,
101             env: &mut crate::func_environ::FuncEnvironment<'a>,
102             builder: &mut FunctionBuilder,
103         ) -> VMPayloads {
104             let offset: i64 = env.offsets.ptr.vmcontref_args().into();
105             let address = builder.ins().iadd_imm(self.address, offset);
106             VMPayloads::new(address)
107         }
108 
109         pub fn values<'a>(
110             &self,
111             env: &mut crate::func_environ::FuncEnvironment<'a>,
112             builder: &mut FunctionBuilder,
113         ) -> VMPayloads {
114             let offset: i64 = env.offsets.ptr.vmcontref_values().into();
115             let address = builder.ins().iadd_imm(self.address, offset);
116             VMPayloads::new(address)
117         }
118 
119         pub fn common_stack_information<'a>(
120             &self,
121             env: &mut crate::func_environ::FuncEnvironment<'a>,
122             builder: &mut FunctionBuilder,
123         ) -> VMCommonStackInformation {
124             let offset: i64 = env.offsets.ptr.vmcontref_common_stack_information().into();
125             let address = builder.ins().iadd_imm(self.address, offset);
126             VMCommonStackInformation { address }
127         }
128 
129         /// Stores the parent of this continuation, which may either be another
130         /// continuation or the initial stack. It is therefore represented as a
131         /// `VMStackChain` element.
132         pub fn set_parent_stack_chain<'a>(
133             &mut self,
134             env: &mut crate::func_environ::FuncEnvironment<'a>,
135             builder: &mut FunctionBuilder,
136             new_stack_chain: &VMStackChain,
137         ) {
138             let offset = env.offsets.ptr.vmcontref_parent_chain().into();
139             new_stack_chain.store(env, builder, self.address, offset)
140         }
141 
142         /// Loads the parent of this continuation, which may either be another
143         /// continuation or the initial stack. It is therefore represented as a
144         /// `VMStackChain` element.
145         pub fn get_parent_stack_chain<'a>(
146             &self,
147             env: &mut crate::func_environ::FuncEnvironment<'a>,
148             builder: &mut FunctionBuilder,
149         ) -> VMStackChain {
150             let offset = env.offsets.ptr.vmcontref_parent_chain().into();
151             VMStackChain::load(env, builder, self.address, offset, env.pointer_type())
152         }
153 
154         pub fn set_last_ancestor<'a>(
155             &self,
156             env: &mut crate::func_environ::FuncEnvironment<'a>,
157             builder: &mut FunctionBuilder,
158             last_ancestor: ir::Value,
159         ) {
160             let offset: i32 = env.offsets.ptr.vmcontref_last_ancestor().into();
161             let mem_flags = ir::MemFlags::trusted();
162             builder
163                 .ins()
164                 .store(mem_flags, last_ancestor, self.address, offset);
165         }
166 
167         pub fn get_last_ancestor<'a>(
168             &self,
169             env: &mut crate::func_environ::FuncEnvironment<'a>,
170             builder: &mut FunctionBuilder,
171         ) -> ir::Value {
172             let offset: i32 = env.offsets.ptr.vmcontref_last_ancestor().into();
173             let mem_flags = ir::MemFlags::trusted();
174             builder
175                 .ins()
176                 .load(env.pointer_type(), mem_flags, self.address, offset)
177         }
178 
179         /// Gets the revision counter the a given continuation
180         /// reference.
181         pub fn get_revision<'a>(
182             &mut self,
183             env: &mut crate::func_environ::FuncEnvironment<'a>,
184             builder: &mut FunctionBuilder,
185         ) -> ir::Value {
186             let mem_flags = ir::MemFlags::trusted();
187             let offset: i32 = env.offsets.ptr.vmcontref_revision().into();
188             let revision = builder.ins().load(I64, mem_flags, self.address, offset);
189             revision
190         }
191 
192         /// Sets the revision counter on the given continuation
193         /// reference to `revision + 1`.
194 
195         pub fn incr_revision<'a>(
196             &mut self,
197             env: &mut crate::func_environ::FuncEnvironment<'a>,
198             builder: &mut FunctionBuilder,
199             revision: ir::Value,
200         ) -> ir::Value {
201             let mem_flags = ir::MemFlags::trusted();
202             let offset: i32 = env.offsets.ptr.vmcontref_revision().into();
203             let revision_plus1 = builder.ins().iadd_imm(revision, 1);
204             builder
205                 .ins()
206                 .store(mem_flags, revision_plus1, self.address, offset);
207             revision_plus1
208         }
209 
210         pub fn get_fiber_stack<'a>(
211             &self,
212             env: &mut crate::func_environ::FuncEnvironment<'a>,
213             builder: &mut FunctionBuilder,
214         ) -> VMContinuationStack {
215             // The top of stack field is stored at offset 0 of the `FiberStack`.
216             let offset: i64 = env.offsets.ptr.vmcontref_stack().into();
217             let fiber_stack_top_of_stack_ptr = builder.ins().iadd_imm(self.address, offset);
218             VMContinuationStack::new(fiber_stack_top_of_stack_ptr)
219         }
220     }
221 
222     impl<T: VMHostArrayEntry> VMHostArrayRef<T> {
223         pub(crate) fn new(address: ir::Value) -> Self {
224             Self {
225                 address,
226                 phantom: PhantomData::default(),
227             }
228         }
229 
230         fn get(&self, builder: &mut FunctionBuilder, ty: ir::Type, offset: i32) -> ir::Value {
231             let mem_flags = ir::MemFlags::trusted();
232             builder.ins().load(ty, mem_flags, self.address, offset)
233         }
234 
235         fn set<U>(&self, builder: &mut FunctionBuilder, offset: i32, value: ir::Value) {
236             debug_assert_eq!(
237                 builder.func.dfg.value_type(value),
238                 Type::int_with_byte_size(u16::try_from(core::mem::size_of::<U>()).unwrap())
239                     .unwrap()
240             );
241             let mem_flags = ir::MemFlags::trusted();
242             builder.ins().store(mem_flags, value, self.address, offset);
243         }
244 
245         pub fn get_data<'a>(
246             &self,
247             env: &mut crate::func_environ::FuncEnvironment<'a>,
248             builder: &mut FunctionBuilder,
249         ) -> ir::Value {
250             let offset = env.offsets.ptr.vmhostarray_data().into();
251             self.get(builder, env.pointer_type(), offset)
252         }
253 
254         pub fn get_length<'a>(
255             &self,
256             env: &mut crate::func_environ::FuncEnvironment<'a>,
257             builder: &mut FunctionBuilder,
258         ) -> ir::Value {
259             // Array length is stored as u32.
260             let offset = env.offsets.ptr.vmhostarray_length().into();
261             self.get(builder, I32, offset)
262         }
263 
264         fn set_length<'a>(
265             &self,
266             env: &mut crate::func_environ::FuncEnvironment<'a>,
267             builder: &mut FunctionBuilder,
268             length: ir::Value,
269         ) {
270             // Array length is stored as u32.
271             let offset = env.offsets.ptr.vmhostarray_length().into();
272             self.set::<u32>(builder, offset, length);
273         }
274 
275         fn set_capacity<'a>(
276             &self,
277             env: &mut crate::func_environ::FuncEnvironment<'a>,
278             builder: &mut FunctionBuilder,
279             capacity: ir::Value,
280         ) {
281             // Array capacity is stored as u32.
282             let offset = env.offsets.ptr.vmhostarray_capacity().into();
283             self.set::<u32>(builder, offset, capacity);
284         }
285 
286         fn set_data<'a>(
287             &self,
288             env: &mut crate::func_environ::FuncEnvironment<'a>,
289             builder: &mut FunctionBuilder,
290             data: ir::Value,
291         ) {
292             debug_assert_eq!(builder.func.dfg.value_type(data), env.pointer_type());
293             let offset: i32 = env.offsets.ptr.vmhostarray_data().into();
294             let mem_flags = ir::MemFlags::trusted();
295             builder.ins().store(mem_flags, data, self.address, offset);
296         }
297 
298         /// Returns pointer to next empty slot in data buffer and marks the
299         /// subsequent `arg_count` slots as occupied.
300         pub fn occupy_next_slots<'a>(
301             &self,
302             env: &mut crate::func_environ::FuncEnvironment<'a>,
303             builder: &mut FunctionBuilder,
304             arg_count: i32,
305         ) -> ir::Value {
306             let data = self.get_data(env, builder);
307             let original_length = self.get_length(env, builder);
308             let new_length = builder
309                 .ins()
310                 .iadd_imm(original_length, i64::from(arg_count));
311             self.set_length(env, builder, new_length);
312 
313             let (_align, entry_size) = T::vmhostarray_entry_layout(&env.offsets.ptr);
314             let original_length = builder.ins().uextend(I64, original_length);
315             let byte_offset = builder
316                 .ins()
317                 .imul_imm(original_length, i64::from(entry_size));
318             builder.ins().iadd(data, byte_offset)
319         }
320 
321         pub fn allocate_or_reuse_stack_slot<'a>(
322             &self,
323             env: &mut crate::func_environ::FuncEnvironment<'a>,
324             builder: &mut FunctionBuilder,
325             required_capacity: u32,
326             existing_slot: Option<StackSlot>,
327         ) -> StackSlot {
328             let (align, entry_size) = T::vmhostarray_entry_layout(&env.offsets.ptr);
329             let required_size = required_capacity * entry_size;
330 
331             match existing_slot {
332                 Some(slot) if builder.func.sized_stack_slots[slot].size >= required_size => {
333                     let slot_data = &builder.func.sized_stack_slots[slot];
334                     debug_assert!(align <= slot_data.align_shift);
335                     debug_assert_eq!(slot_data.kind, ExplicitSlot);
336                     let existing_capacity = slot_data.size / entry_size;
337 
338                     let capacity_value = builder.ins().iconst(I32, i64::from(existing_capacity));
339                     let existing_data = builder.ins().stack_addr(env.pointer_type(), slot, 0);
340 
341                     self.set_capacity(env, builder, capacity_value);
342                     self.set_data(env, builder, existing_data);
343 
344                     slot
345                 }
346                 _ => {
347                     let capacity_value = builder.ins().iconst(I32, i64::from(required_capacity));
348                     let slot_size = ir::StackSlotData::new(
349                         ir::StackSlotKind::ExplicitSlot,
350                         required_size,
351                         align,
352                     );
353                     let slot = builder.create_sized_stack_slot(slot_size);
354                     let new_data = builder.ins().stack_addr(env.pointer_type(), slot, 0);
355 
356                     self.set_capacity(env, builder, capacity_value);
357                     self.set_data(env, builder, new_data);
358 
359                     slot
360                 }
361             }
362         }
363 
364         /// Loads n entries from this Vector object, where n is the length of
365         /// `load_types`, which also gives the types of the values to load.
366         /// Loading starts at index 0 of the Vector object.
367         pub fn load_data_entries<'a>(
368             &self,
369             env: &mut crate::func_environ::FuncEnvironment<'a>,
370             builder: &mut FunctionBuilder,
371             load_types: &[ir::Type],
372         ) -> Vec<ir::Value> {
373             let memflags = ir::MemFlags::trusted();
374 
375             let data_start_pointer = self.get_data(env, builder);
376             let mut values = vec![];
377             let mut offset = 0;
378             let (_align, entry_size) = T::vmhostarray_entry_layout(&env.offsets.ptr);
379             for valtype in load_types {
380                 let val = builder
381                     .ins()
382                     .load(*valtype, memflags, data_start_pointer, offset);
383                 values.push(val);
384                 offset += i32::try_from(entry_size).unwrap();
385             }
386             values
387         }
388 
389         /// Stores the given `values` in this Vector object, beginning at
390         /// index 0. This expects the Vector object to be empty (i.e., current
391         /// length is 0), and to be of sufficient capacity to store |`values`|
392         /// entries.
393         pub fn store_data_entries<'a>(
394             &self,
395             env: &mut crate::func_environ::FuncEnvironment<'a>,
396             builder: &mut FunctionBuilder,
397             values: &[ir::Value],
398         ) {
399             let store_count = builder
400                 .ins()
401                 .iconst(I32, i64::try_from(values.len()).unwrap());
402 
403             let (_align, entry_size) = T::vmhostarray_entry_layout(&env.offsets.ptr);
404 
405             debug_assert!(values.iter().all(|val| {
406                 let ty = builder.func.dfg.value_type(*val);
407                 let size = ty.bytes();
408                 size <= entry_size
409             }));
410 
411             let memflags = ir::MemFlags::trusted();
412 
413             let data_start_pointer = self.get_data(env, builder);
414 
415             let mut offset = 0;
416             for value in values {
417                 builder
418                     .ins()
419                     .store(memflags, *value, data_start_pointer, offset);
420                 offset += i32::try_from(entry_size).unwrap();
421             }
422 
423             self.set_length(env, builder, store_count);
424         }
425 
426         pub fn clear<'a>(
427             &self,
428             env: &mut crate::func_environ::FuncEnvironment<'a>,
429             builder: &mut FunctionBuilder,
430             discard_buffer: bool,
431         ) {
432             let zero32 = builder.ins().iconst(I32, 0);
433             self.set_length(env, builder, zero32);
434 
435             if discard_buffer {
436                 let zero32 = builder.ins().iconst(I32, 0);
437                 self.set_capacity(env, builder, zero32);
438 
439                 let zero_ptr = builder.ins().iconst(env.pointer_type(), 0);
440                 self.set_data(env, builder, zero_ptr);
441             }
442         }
443     }
444 
445     impl VMStackChain {
446         /// Creates a `Self` corresponding to `VMStackChain::Continuation(contref)`.
447         pub fn from_continuation<'a>(
448             env: &mut crate::func_environ::FuncEnvironment<'a>,
449             builder: &mut FunctionBuilder,
450             contref: ir::Value,
451         ) -> VMStackChain {
452             debug_assert_eq!(
453                 env.offsets.ptr.size_of_vmstack_chain(),
454                 2 * env.offsets.ptr.size()
455             );
456             let discriminant = wasmtime_environ::STACK_CHAIN_CONTINUATION_DISCRIMINANT;
457             let discriminant = builder
458                 .ins()
459                 .iconst(env.pointer_type(), i64::try_from(discriminant).unwrap());
460             VMStackChain {
461                 discriminant,
462                 payload: contref,
463             }
464         }
465 
466         /// Creates a `Self` corresponding to `VMStackChain::Absent`.
467         pub fn absent<'a>(
468             env: &mut crate::func_environ::FuncEnvironment<'a>,
469             builder: &mut FunctionBuilder,
470         ) -> VMStackChain {
471             debug_assert_eq!(
472                 env.offsets.ptr.size_of_vmstack_chain(),
473                 2 * env.offsets.ptr.size()
474             );
475             let discriminant = wasmtime_environ::STACK_CHAIN_ABSENT_DISCRIMINANT;
476             let discriminant = builder
477                 .ins()
478                 .iconst(env.pointer_type(), i64::try_from(discriminant).unwrap());
479             let zero_filler = builder.ins().iconst(env.pointer_type(), 0i64);
480             VMStackChain {
481                 discriminant,
482                 payload: zero_filler,
483             }
484         }
485 
486         pub fn is_initial_stack<'a>(
487             &self,
488             _env: &mut crate::func_environ::FuncEnvironment<'a>,
489             builder: &mut FunctionBuilder,
490         ) -> ir::Value {
491             builder.ins().icmp_imm(
492                 IntCC::Equal,
493                 self.discriminant,
494                 i64::try_from(wasmtime_environ::STACK_CHAIN_INITIAL_STACK_DISCRIMINANT).unwrap(),
495             )
496         }
497 
498         /// Return the two raw `ir::Value`s that represent this VMStackChain.
499         pub fn to_raw_parts(&self) -> [ir::Value; 2] {
500             [self.discriminant, self.payload]
501         }
502 
503         /// Construct a `Self` from two raw `ir::Value`s.
504         pub fn from_raw_parts(raw_data: [ir::Value; 2]) -> VMStackChain {
505             VMStackChain {
506                 discriminant: raw_data[0],
507                 payload: raw_data[1],
508             }
509         }
510 
511         /// Load a `VMStackChain` object from the given address.
512         pub fn load<'a>(
513             _env: &mut crate::func_environ::FuncEnvironment<'a>,
514             builder: &mut FunctionBuilder,
515             pointer: ir::Value,
516             initial_offset: i32,
517             pointer_type: ir::Type,
518         ) -> VMStackChain {
519             let memflags = ir::MemFlags::trusted();
520             let mut offset = initial_offset;
521             let mut data = vec![];
522             for _ in 0..2 {
523                 data.push(builder.ins().load(pointer_type, memflags, pointer, offset));
524                 offset += i32::try_from(pointer_type.bytes()).unwrap();
525             }
526             let data = <[ir::Value; 2]>::try_from(data).unwrap();
527             Self::from_raw_parts(data)
528         }
529 
530         /// Store this `VMStackChain` object at the given address.
531         pub fn store<'a>(
532             &self,
533             env: &mut crate::func_environ::FuncEnvironment<'a>,
534             builder: &mut FunctionBuilder,
535             target_pointer: ir::Value,
536             initial_offset: i32,
537         ) {
538             let memflags = ir::MemFlags::trusted();
539             let mut offset = initial_offset;
540             let data = self.to_raw_parts();
541 
542             for value in data {
543                 debug_assert_eq!(builder.func.dfg.value_type(value), env.pointer_type());
544                 builder.ins().store(memflags, value, target_pointer, offset);
545                 offset += i32::try_from(env.pointer_type().bytes()).unwrap();
546             }
547         }
548 
549         /// Use this only if you've already checked that `self` corresponds to a `VMStackChain::Continuation`.
550         pub fn unchecked_get_continuation(&self) -> ir::Value {
551             self.payload
552         }
553 
554         /// Must only be called if `self` represents a `InitialStack` or
555         /// `Continuation` variant. Returns a pointer to the associated
556         /// `CommonStackInformation` object.
557         pub fn get_common_stack_information<'a>(
558             &self,
559             env: &mut crate::func_environ::FuncEnvironment<'a>,
560             _builder: &mut FunctionBuilder,
561         ) -> VMCommonStackInformation {
562             // `self` corresponds to a VMStackChain::InitialStack or
563             // VMStackChain::Continuation.
564             // In both cases, the payload is a pointer.
565             let address = self.payload;
566 
567             // `obj` is now a pointer to the beginning of either
568             // 1. A `VMContRef` struct (in the case of a
569             // VMStackChain::Continuation)
570             // 2. A CommonStackInformation struct (in the case of
571             // VMStackChain::InitialStack)
572             //
573             // Since a `VMContRef` starts with an (inlined) CommonStackInformation
574             // object at offset 0, we actually have in both cases that `ptr` is
575             // now the address of the beginning of a VMStackLimits object.
576             debug_assert_eq!(env.offsets.ptr.vmcontref_common_stack_information(), 0);
577             VMCommonStackInformation { address }
578         }
579     }
580 
581     impl VMCommonStackInformation {
582         fn get_state_ptr<'a>(
583             &self,
584             env: &mut crate::func_environ::FuncEnvironment<'a>,
585             builder: &mut FunctionBuilder,
586         ) -> ir::Value {
587             let offset: i64 = env.offsets.ptr.vmcommon_stack_information_state().into();
588 
589             builder.ins().iadd_imm(self.address, offset)
590         }
591 
592         fn get_stack_limits_ptr<'a>(
593             &self,
594             env: &mut crate::func_environ::FuncEnvironment<'a>,
595             builder: &mut FunctionBuilder,
596         ) -> ir::Value {
597             let offset: i64 = env.offsets.ptr.vmcommon_stack_information_limits().into();
598 
599             builder.ins().iadd_imm(self.address, offset)
600         }
601 
602         fn load_state<'a>(
603             &self,
604             env: &mut crate::func_environ::FuncEnvironment<'a>,
605             builder: &mut FunctionBuilder,
606         ) -> ir::Value {
607             let mem_flags = ir::MemFlags::trusted();
608             let state_ptr = self.get_state_ptr(env, builder);
609 
610             builder.ins().load(I32, mem_flags, state_ptr, 0)
611         }
612 
613         fn set_state_no_payload<'a>(
614             &self,
615             env: &mut crate::func_environ::FuncEnvironment<'a>,
616             builder: &mut FunctionBuilder,
617             discriminant: u32,
618         ) {
619             let discriminant = builder.ins().iconst(I32, i64::from(discriminant));
620             let mem_flags = ir::MemFlags::trusted();
621             let state_ptr = self.get_state_ptr(env, builder);
622 
623             builder.ins().store(mem_flags, discriminant, state_ptr, 0);
624         }
625 
626         pub fn set_state_running<'a>(
627             &self,
628             env: &mut crate::func_environ::FuncEnvironment<'a>,
629             builder: &mut FunctionBuilder,
630         ) {
631             let discriminant = wasmtime_environ::STACK_STATE_RUNNING_DISCRIMINANT;
632             self.set_state_no_payload(env, builder, discriminant);
633         }
634 
635         pub fn set_state_parent<'a>(
636             &self,
637             env: &mut crate::func_environ::FuncEnvironment<'a>,
638             builder: &mut FunctionBuilder,
639         ) {
640             let discriminant = wasmtime_environ::STACK_STATE_PARENT_DISCRIMINANT;
641             self.set_state_no_payload(env, builder, discriminant);
642         }
643 
644         pub fn set_state_returned<'a>(
645             &self,
646             env: &mut crate::func_environ::FuncEnvironment<'a>,
647             builder: &mut FunctionBuilder,
648         ) {
649             let discriminant = wasmtime_environ::STACK_STATE_RETURNED_DISCRIMINANT;
650             self.set_state_no_payload(env, builder, discriminant);
651         }
652 
653         pub fn set_state_suspended<'a>(
654             &self,
655             env: &mut crate::func_environ::FuncEnvironment<'a>,
656             builder: &mut FunctionBuilder,
657         ) {
658             let discriminant = wasmtime_environ::STACK_STATE_SUSPENDED_DISCRIMINANT;
659             self.set_state_no_payload(env, builder, discriminant);
660         }
661 
662         /// Checks whether the `VMStackState` reflects that the stack has ever been
663         /// active (instead of just having been allocated, but never resumed).
664         pub fn was_invoked<'a>(
665             &self,
666             env: &mut crate::func_environ::FuncEnvironment<'a>,
667             builder: &mut FunctionBuilder,
668         ) -> ir::Value {
669             let actual_state = self.load_state(env, builder);
670             let allocated = wasmtime_environ::STACK_STATE_FRESH_DISCRIMINANT;
671             builder
672                 .ins()
673                 .icmp_imm(IntCC::NotEqual, actual_state, i64::from(allocated))
674         }
675 
676         pub fn get_handler_list<'a>(
677             &self,
678             env: &mut crate::func_environ::FuncEnvironment<'a>,
679             builder: &mut FunctionBuilder,
680         ) -> VMHandlerList {
681             let offset: i64 = env.offsets.ptr.vmcommon_stack_information_handlers().into();
682             let address = builder.ins().iadd_imm(self.address, offset);
683             VMHandlerList::new(address)
684         }
685 
686         pub fn get_first_switch_handler_index<'a>(
687             &self,
688             env: &mut crate::func_environ::FuncEnvironment<'a>,
689             builder: &mut FunctionBuilder,
690         ) -> ir::Value {
691             // Field first_switch_handler_index has type u32
692             let memflags = ir::MemFlags::trusted();
693             let offset: i32 = env
694                 .offsets
695                 .ptr
696                 .vmcommon_stack_information_first_switch_handler_index()
697                 .into();
698             builder.ins().load(I32, memflags, self.address, offset)
699         }
700 
701         pub fn set_first_switch_handler_index<'a>(
702             &self,
703             env: &mut crate::func_environ::FuncEnvironment<'a>,
704             builder: &mut FunctionBuilder,
705             value: ir::Value,
706         ) {
707             // Field first_switch_handler_index has type u32
708             let memflags = ir::MemFlags::trusted();
709             let offset: i32 = env
710                 .offsets
711                 .ptr
712                 .vmcommon_stack_information_first_switch_handler_index()
713                 .into();
714             builder.ins().store(memflags, value, self.address, offset);
715         }
716 
717         /// Sets `last_wasm_entry_sp` and `stack_limit` fields in
718         /// `VMRuntimelimits` using the values from the `VMStackLimits` of this
719         /// object.
720         pub fn write_limits_to_vmcontext<'a>(
721             &self,
722             env: &mut crate::func_environ::FuncEnvironment<'a>,
723             builder: &mut FunctionBuilder,
724             vmruntime_limits_ptr: ir::Value,
725         ) {
726             let stack_limits_ptr = self.get_stack_limits_ptr(env, builder);
727 
728             let memflags = ir::MemFlags::trusted();
729 
730             let mut copy_to_vm_runtime_limits = |our_offset, their_offset| {
731                 let our_value = builder.ins().load(
732                     env.pointer_type(),
733                     memflags,
734                     stack_limits_ptr,
735                     i32::from(our_offset),
736                 );
737                 builder.ins().store(
738                     memflags,
739                     our_value,
740                     vmruntime_limits_ptr,
741                     i32::from(their_offset),
742                 );
743             };
744 
745             let pointer_size = u8::try_from(env.pointer_type().bytes()).unwrap();
746             let stack_limit_offset = env.offsets.ptr.vmstack_limits_stack_limit();
747             let last_wasm_entry_fp_offset = env.offsets.ptr.vmstack_limits_last_wasm_entry_fp();
748             copy_to_vm_runtime_limits(
749                 stack_limit_offset,
750                 pointer_size.vmstore_context_stack_limit(),
751             );
752             copy_to_vm_runtime_limits(
753                 last_wasm_entry_fp_offset,
754                 pointer_size.vmstore_context_last_wasm_entry_fp(),
755             );
756         }
757 
758         /// Overwrites the `last_wasm_entry_fp` field of the `VMStackLimits`
759         /// object in the `VMStackLimits` of this object by loading the corresponding
760         /// field from the `VMRuntimeLimits`.
761         /// If `load_stack_limit` is true, we do the same for the `stack_limit`
762         /// field.
763         pub fn load_limits_from_vmcontext<'a>(
764             &self,
765             env: &mut crate::func_environ::FuncEnvironment<'a>,
766             builder: &mut FunctionBuilder,
767             vmruntime_limits_ptr: ir::Value,
768             load_stack_limit: bool,
769         ) {
770             let stack_limits_ptr = self.get_stack_limits_ptr(env, builder);
771 
772             let memflags = ir::MemFlags::trusted();
773             let pointer_size = u8::try_from(env.pointer_type().bytes()).unwrap();
774 
775             let mut copy = |runtime_limits_offset, stack_limits_offset| {
776                 let from_vm_runtime_limits = builder.ins().load(
777                     env.pointer_type(),
778                     memflags,
779                     vmruntime_limits_ptr,
780                     runtime_limits_offset,
781                 );
782                 builder.ins().store(
783                     memflags,
784                     from_vm_runtime_limits,
785                     stack_limits_ptr,
786                     stack_limits_offset,
787                 );
788             };
789 
790             let last_wasm_entry_fp_offset = env.offsets.ptr.vmstack_limits_last_wasm_entry_fp();
791             copy(
792                 pointer_size.vmstore_context_last_wasm_entry_fp(),
793                 last_wasm_entry_fp_offset,
794             );
795 
796             if load_stack_limit {
797                 let stack_limit_offset = env.offsets.ptr.vmstack_limits_stack_limit();
798                 copy(
799                     pointer_size.vmstore_context_stack_limit(),
800                     stack_limit_offset,
801                 );
802             }
803         }
804     }
805 
806     impl VMContinuationStack {
807         /// The parameter is NOT the "top of stack" address of the stack itself. In line
808         /// with how the (runtime) `FiberStack` type works, this is a pointer to
809         /// the TOS address.
810         pub fn new(tos_ptr: ir::Value) -> Self {
811             Self { tos_ptr }
812         }
813 
814         fn load_top_of_stack<'a>(
815             &self,
816             env: &mut crate::func_environ::FuncEnvironment<'a>,
817             builder: &mut FunctionBuilder,
818         ) -> ir::Value {
819             let mem_flags = ir::MemFlags::trusted();
820             builder
821                 .ins()
822                 .load(env.pointer_type(), mem_flags, self.tos_ptr, 0)
823         }
824 
825         /// Returns address of the control context stored in the stack memory,
826         /// as used by stack_switch instructions.
827         pub fn load_control_context<'a>(
828             &self,
829             env: &mut crate::func_environ::FuncEnvironment<'a>,
830             builder: &mut FunctionBuilder,
831         ) -> ir::Value {
832             let tos = self.load_top_of_stack(env, builder);
833             // Control context begins 24 bytes below top of stack (see unix.rs)
834             builder.ins().iadd_imm(tos, -0x18)
835         }
836     }
837 }
838 
839 use helpers::VMStackChain;
840 use stack_switching_helpers as helpers;
841 
842 /// Stores the given arguments in the appropriate `VMPayloads` object in the
843 /// continuation. If the continuation was never invoked, use the `args` object.
844 /// Otherwise, use the `values` object.
845 pub(crate) fn vmcontref_store_payloads<'a>(
846     env: &mut crate::func_environ::FuncEnvironment<'a>,
847     builder: &mut FunctionBuilder,
848     values: &[ir::Value],
849     contref: ir::Value,
850 ) {
851     let count =
852         i32::try_from(values.len()).expect("Number of stack switching payloads should fit in i32");
853     if values.len() > 0 {
854         let use_args_block = builder.create_block();
855         let use_payloads_block = builder.create_block();
856         let store_data_block = builder.create_block();
857         builder.append_block_param(store_data_block, env.pointer_type());
858 
859         let co = helpers::VMContRef::new(contref);
860         let csi = co.common_stack_information(env, builder);
861         let was_invoked = csi.was_invoked(env, builder);
862         builder
863             .ins()
864             .brif(was_invoked, use_payloads_block, &[], use_args_block, &[]);
865 
866         {
867             builder.switch_to_block(use_args_block);
868             builder.seal_block(use_args_block);
869 
870             let args = co.args(env, builder);
871             let ptr = args.occupy_next_slots(env, builder, count);
872 
873             builder
874                 .ins()
875                 .jump(store_data_block, &[BlockArg::Value(ptr)]);
876         }
877 
878         {
879             builder.switch_to_block(use_payloads_block);
880             builder.seal_block(use_payloads_block);
881 
882             let payloads = co.values(env, builder);
883 
884             // This also checks that the buffer is large enough to hold
885             // `values.len()` more elements.
886             let ptr = payloads.occupy_next_slots(env, builder, count);
887             builder
888                 .ins()
889                 .jump(store_data_block, &[BlockArg::Value(ptr)]);
890         }
891 
892         {
893             builder.switch_to_block(store_data_block);
894             builder.seal_block(store_data_block);
895 
896             let ptr = builder.block_params(store_data_block)[0];
897 
898             // Store the values.
899             let memflags = ir::MemFlags::trusted();
900             let mut offset = 0;
901             for value in values {
902                 builder.ins().store(memflags, *value, ptr, offset);
903                 offset += i32::from(env.offsets.ptr.maximum_value_size());
904             }
905         }
906     }
907 }
908 
909 pub(crate) fn tag_address<'a>(
910     env: &mut crate::func_environ::FuncEnvironment<'a>,
911     builder: &mut FunctionBuilder,
912     index: u32,
913 ) -> ir::Value {
914     let vmctx = env.vmctx_val(&mut builder.cursor());
915     let tag_index = wasmtime_environ::TagIndex::from_u32(index);
916     let pointer_type = env.pointer_type();
917     if let Some(def_index) = env.module.defined_tag_index(tag_index) {
918         let offset = i32::try_from(env.offsets.vmctx_vmtag_definition(def_index)).unwrap();
919         builder.ins().iadd_imm(vmctx, i64::from(offset))
920     } else {
921         let offset = i32::try_from(env.offsets.vmctx_vmtag_import_from(tag_index)).unwrap();
922         builder.ins().load(
923             pointer_type,
924             ir::MemFlags::trusted().with_readonly(),
925             vmctx,
926             ir::immediates::Offset32::new(offset),
927         )
928     }
929 }
930 
931 /// Returns the stack chain saved in the given `VMContext`. Note that the
932 /// head of the list is the actively running stack (initial stack or
933 /// continuation).
934 pub fn vmctx_load_stack_chain<'a>(
935     env: &mut crate::func_environ::FuncEnvironment<'a>,
936     builder: &mut FunctionBuilder,
937     vmctx: ir::Value,
938 ) -> VMStackChain {
939     let stack_chain_offset = env.offsets.ptr.vmstore_context_stack_chain().into();
940 
941     // First we need to get the `VMStoreContext`.
942     let vm_store_context_offset = env.offsets.ptr.vmctx_store_context();
943     let vm_store_context = builder.ins().load(
944         env.pointer_type(),
945         MemFlags::trusted(),
946         vmctx,
947         vm_store_context_offset,
948     );
949 
950     VMStackChain::load(
951         env,
952         builder,
953         vm_store_context,
954         stack_chain_offset,
955         env.pointer_type(),
956     )
957 }
958 
959 /// Stores the given stack chain saved in the `VMContext`, overwriting the
960 /// existing one.
961 pub fn vmctx_store_stack_chain<'a>(
962     env: &mut crate::func_environ::FuncEnvironment<'a>,
963     builder: &mut FunctionBuilder,
964     vmctx: ir::Value,
965     stack_chain: &VMStackChain,
966 ) {
967     let stack_chain_offset = env.offsets.ptr.vmstore_context_stack_chain().into();
968 
969     // First we need to get the `VMStoreContext`.
970     let vm_store_context_offset = env.offsets.ptr.vmctx_store_context();
971     let vm_store_context = builder.ins().load(
972         env.pointer_type(),
973         MemFlags::trusted(),
974         vmctx,
975         vm_store_context_offset,
976     );
977 
978     stack_chain.store(env, builder, vm_store_context, stack_chain_offset)
979 }
980 
981 /// Similar to `vmctx_store_stack_chain`, but instead of storing an arbitrary
982 /// `VMStackChain`, stores VMStackChain::Continuation(contref)`.
983 pub fn vmctx_set_active_continuation<'a>(
984     env: &mut crate::func_environ::FuncEnvironment<'a>,
985     builder: &mut FunctionBuilder,
986     vmctx: ir::Value,
987     contref: ir::Value,
988 ) {
989     let chain = VMStackChain::from_continuation(env, builder, contref);
990     vmctx_store_stack_chain(env, builder, vmctx, &chain)
991 }
992 
993 pub fn vmctx_load_vm_runtime_limits_ptr<'a>(
994     env: &mut crate::func_environ::FuncEnvironment<'a>,
995     builder: &mut FunctionBuilder,
996     vmctx: ir::Value,
997 ) -> ir::Value {
998     let pointer_type = env.pointer_type();
999     let offset = i32::from(env.offsets.ptr.vmctx_store_context());
1000 
1001     // The *pointer* to the VMRuntimeLimits does not change within the
1002     // same function, allowing us to set the `read_only` flag.
1003     let flags = ir::MemFlags::trusted().with_readonly();
1004 
1005     builder.ins().load(pointer_type, flags, vmctx, offset)
1006 }
1007 
1008 /// This function generates code that searches for a handler for `tag_address`,
1009 /// which must be a `*mut VMTagDefinition`. The search walks up the chain of
1010 /// continuations beginning at `start`.
1011 ///
1012 /// The flag `search_suspend_handlers` determines whether we search for a
1013 /// suspend or switch handler. Concretely, this influences which part of each
1014 /// handler list we will search.
1015 ///
1016 /// We trap if no handler was found.
1017 ///
1018 /// The returned values are:
1019 /// 1. The stack (continuation or initial stack, represented as a VMStackChain) in
1020 ///    whose handler list we found the tag (i.e., the stack that performed the
1021 ///    resume instruction that installed handler for the tag).
1022 /// 2. The continuation whose parent is the stack mentioned in 1.
1023 /// 3. The index of the handler in the handler list.
1024 ///
1025 /// In pseudo-code, the generated code's behavior can be expressed as
1026 /// follows:
1027 ///
1028 /// chain_link = start
1029 /// while !chain_link.is_initial_stack() {
1030 ///   contref = chain_link.get_contref()
1031 ///   parent_link = contref.parent
1032 ///   parent_csi = parent_link.get_common_stack_information();
1033 ///   handlers = parent_csi.handlers;
1034 ///   (begin_range, end_range) = if search_suspend_handlers {
1035 ///     (0, parent_csi.first_switch_handler_index)
1036 ///   } else {
1037 ///     (parent_csi.first_switch_handler_index, handlers.length)
1038 ///   };
1039 ///   for index in begin_range..end_range {
1040 ///     if handlers[index] == tag_address {
1041 ///       goto on_match(contref, index)
1042 ///     }
1043 ///   }
1044 ///   chain_link = parent_link
1045 /// }
1046 /// trap(unhandled_tag)
1047 ///
1048 /// on_match(conref : VMContRef, handler_index : u32)
1049 /// ... execution continues here here ...
1050 ///
1051 fn search_handler<'a>(
1052     env: &mut crate::func_environ::FuncEnvironment<'a>,
1053     builder: &mut FunctionBuilder,
1054     start: &helpers::VMStackChain,
1055     tag_address: ir::Value,
1056     search_suspend_handlers: bool,
1057 ) -> (VMStackChain, ir::Value, ir::Value) {
1058     let handle_link = builder.create_block();
1059     let begin_search_handler_list = builder.create_block();
1060     let try_index = builder.create_block();
1061     let compare_tags = builder.create_block();
1062     let on_match = builder.create_block();
1063     let on_no_match = builder.create_block();
1064     let block_args = start.to_raw_parts().map(|v| BlockArg::Value(v));
1065 
1066     // Terminate previous block:
1067     builder.ins().jump(handle_link, &block_args);
1068 
1069     // Block handle_link
1070     let chain_link = {
1071         builder.append_block_param(handle_link, env.pointer_type());
1072         builder.append_block_param(handle_link, env.pointer_type());
1073         builder.switch_to_block(handle_link);
1074 
1075         let raw_parts = builder.block_params(handle_link);
1076         let chain_link = helpers::VMStackChain::from_raw_parts([raw_parts[0], raw_parts[1]]);
1077         let is_initial_stack = chain_link.is_initial_stack(env, builder);
1078         builder.ins().brif(
1079             is_initial_stack,
1080             on_no_match,
1081             &[],
1082             begin_search_handler_list,
1083             &[],
1084         );
1085         chain_link
1086     };
1087 
1088     // Block begin_search_handler_list
1089     let (contref, parent_link, handler_list_data_ptr, end_range) = {
1090         builder.switch_to_block(begin_search_handler_list);
1091         let contref = chain_link.unchecked_get_continuation();
1092         let contref = helpers::VMContRef::new(contref);
1093 
1094         let parent_link = contref.get_parent_stack_chain(env, builder);
1095         let parent_csi = parent_link.get_common_stack_information(env, builder);
1096 
1097         let handlers = parent_csi.get_handler_list(env, builder);
1098         let handler_list_data_ptr = handlers.get_data(env, builder);
1099 
1100         let first_switch_handler_index = parent_csi.get_first_switch_handler_index(env, builder);
1101 
1102         // Note that these indices are inclusive-exclusive, i.e. [begin_range, end_range).
1103         let (begin_range, end_range) = if search_suspend_handlers {
1104             let zero = builder.ins().iconst(I32, 0);
1105             (zero, first_switch_handler_index)
1106         } else {
1107             let length = handlers.get_length(env, builder);
1108             (first_switch_handler_index, length)
1109         };
1110 
1111         builder
1112             .ins()
1113             .jump(try_index, &[BlockArg::Value(begin_range)]);
1114 
1115         (contref, parent_link, handler_list_data_ptr, end_range)
1116     };
1117 
1118     // Block try_index
1119     let index = {
1120         builder.append_block_param(try_index, I32);
1121         builder.switch_to_block(try_index);
1122         let index = builder.block_params(try_index)[0];
1123 
1124         let in_bounds = builder
1125             .ins()
1126             .icmp(IntCC::UnsignedLessThan, index, end_range);
1127         let block_args = parent_link.to_raw_parts().map(|v| BlockArg::Value(v));
1128         builder
1129             .ins()
1130             .brif(in_bounds, compare_tags, &[], handle_link, &block_args);
1131         index
1132     };
1133 
1134     // Block compare_tags
1135     {
1136         builder.switch_to_block(compare_tags);
1137 
1138         let base = handler_list_data_ptr;
1139         let entry_size = env.pointer_type().bytes();
1140         let offset = builder.ins().imul_imm(index, i64::from(entry_size));
1141         let offset = builder.ins().uextend(I64, offset);
1142         let entry_address = builder.ins().iadd(base, offset);
1143 
1144         let memflags = ir::MemFlags::trusted();
1145 
1146         let handled_tag = builder
1147             .ins()
1148             .load(env.pointer_type(), memflags, entry_address, 0);
1149 
1150         let tags_match = builder.ins().icmp(IntCC::Equal, handled_tag, tag_address);
1151         let incremented_index = builder.ins().iadd_imm(index, 1);
1152         builder.ins().brif(
1153             tags_match,
1154             on_match,
1155             &[],
1156             try_index,
1157             &[BlockArg::Value(incremented_index)],
1158         );
1159     }
1160 
1161     // Block on_no_match
1162     {
1163         builder.switch_to_block(on_no_match);
1164         builder.set_cold_block(on_no_match);
1165         builder.ins().trap(crate::TRAP_UNHANDLED_TAG);
1166     }
1167 
1168     builder.seal_block(handle_link);
1169     builder.seal_block(begin_search_handler_list);
1170     builder.seal_block(try_index);
1171     builder.seal_block(compare_tags);
1172     builder.seal_block(on_match);
1173     builder.seal_block(on_no_match);
1174 
1175     // final block: on_match
1176     builder.switch_to_block(on_match);
1177 
1178     (parent_link, contref.address, index)
1179 }
1180 
1181 pub(crate) fn translate_cont_bind<'a>(
1182     env: &mut crate::func_environ::FuncEnvironment<'a>,
1183     builder: &mut FunctionBuilder,
1184     contobj: ir::Value,
1185     args: &[ir::Value],
1186 ) -> ir::Value {
1187     let (witness, contref) = fatpointer::deconstruct(env, &mut builder.cursor(), contobj);
1188 
1189     // The typing rules for cont.bind allow a null reference to be passed to it.
1190     builder.ins().trapz(contref, crate::TRAP_NULL_REFERENCE);
1191 
1192     let mut vmcontref = helpers::VMContRef::new(contref);
1193     let revision = vmcontref.get_revision(env, builder);
1194     let evidence = builder.ins().icmp(IntCC::Equal, witness, revision);
1195     builder
1196         .ins()
1197         .trapz(evidence, crate::TRAP_CONTINUATION_ALREADY_CONSUMED);
1198 
1199     vmcontref_store_payloads(env, builder, args, contref);
1200 
1201     let revision = vmcontref.incr_revision(env, builder, revision);
1202     let contobj = fatpointer::construct(env, &mut builder.cursor(), revision, contref);
1203     contobj
1204 }
1205 
1206 pub(crate) fn translate_cont_new<'a>(
1207     env: &mut crate::func_environ::FuncEnvironment<'a>,
1208     builder: &mut FunctionBuilder,
1209     func: ir::Value,
1210     arg_types: &[WasmValType],
1211     return_types: &[WasmValType],
1212 ) -> WasmResult<ir::Value> {
1213     // The typing rules for cont.new allow a null reference to be passed to it.
1214     builder.ins().trapz(func, crate::TRAP_NULL_REFERENCE);
1215 
1216     let nargs = builder
1217         .ins()
1218         .iconst(I32, i64::try_from(arg_types.len()).unwrap());
1219     let nreturns = builder
1220         .ins()
1221         .iconst(I32, i64::try_from(return_types.len()).unwrap());
1222 
1223     let cont_new_func = super::builtins::cont_new(env, &mut builder.func)?;
1224     let vmctx = env.vmctx_val(&mut builder.cursor());
1225     let call_inst = builder
1226         .ins()
1227         .call(cont_new_func, &[vmctx, func, nargs, nreturns]);
1228     let contref = *builder.func.dfg.inst_results(call_inst).first().unwrap();
1229 
1230     let tag = helpers::VMContRef::new(contref).get_revision(env, builder);
1231     let contobj = fatpointer::construct(env, &mut builder.cursor(), tag, contref);
1232     Ok(contobj)
1233 }
1234 
1235 pub(crate) fn translate_resume<'a>(
1236     env: &mut crate::func_environ::FuncEnvironment<'a>,
1237     builder: &mut FunctionBuilder,
1238     type_index: u32,
1239     resume_contobj: ir::Value,
1240     resume_args: &[ir::Value],
1241     resumetable: &[(u32, Option<ir::Block>)],
1242 ) -> WasmResult<Vec<ir::Value>> {
1243     // The resume instruction is the most involved instruction to
1244     // compile as it is responsible for both continuation application
1245     // and control tag dispatch.
1246     //
1247     // Here we translate a resume instruction into several basic
1248     // blocks as follows:
1249     //
1250     //        previous block
1251     //              |
1252     //              |
1253     //        resume_block
1254     //         /           \
1255     //        /             \
1256     //        |             |
1257     //  return_block        |
1258     //                suspend block
1259     //                      |
1260     //                dispatch block
1261     //
1262     // * resume_block handles continuation arguments and performs
1263     //   actual stack switch. On ordinary return from resume, it jumps
1264     //   to the `return_block`, whereas on suspension it jumps to the
1265     //   `suspend_block`.
1266     // * suspend_block is used on suspension, jumps onward to
1267     //   `dispatch_block`.
1268     // * dispatch_block uses a jump table to dispatch to actual
1269     //   user-defined handler blocks, based on the handler index
1270     //   provided on suspension. Note that we do not jump to the
1271     //   handler blocks directly. Instead, each handler block has a
1272     //   corresponding premable block, which we jump to in order to
1273     //   reach a particular handler block. The preamble block prepares
1274     //   the arguments and continuation object to be passed to the
1275     //   actual handler block.
1276     //
1277     let resume_block = builder.create_block();
1278     let return_block = builder.create_block();
1279     let suspend_block = builder.create_block();
1280     let dispatch_block = builder.create_block();
1281 
1282     let vmctx = env.vmctx_val(&mut builder.cursor());
1283 
1284     // Split the resumetable into suspend handlers (each represented by the tag
1285     // index and handler block) and the switch handlers (represented just by the
1286     // tag index). Note that we currently don't remove duplicate tags.
1287     let (suspend_handlers, switch_tags): (Vec<(u32, Block)>, Vec<u32>) = resumetable
1288         .iter()
1289         .partition_map(|(tag_index, block_opt)| match block_opt {
1290             Some(block) => Either::Left((*tag_index, *block)),
1291             None => Either::Right(*tag_index),
1292         });
1293 
1294     // Technically, there is no need to have a dedicated resume block, we could
1295     // just put all of its contents into the current block.
1296     builder.ins().jump(resume_block, &[]);
1297 
1298     // Resume block: actually resume the continuation chain ending at `resume_contref`.
1299     let (resume_result, vm_runtime_limits_ptr, original_stack_chain, new_stack_chain) = {
1300         builder.switch_to_block(resume_block);
1301         builder.seal_block(resume_block);
1302 
1303         let (witness, resume_contref) =
1304             fatpointer::deconstruct(env, &mut builder.cursor(), resume_contobj);
1305 
1306         // The typing rules for resume allow a null reference to be passed to it.
1307         builder
1308             .ins()
1309             .trapz(resume_contref, crate::TRAP_NULL_REFERENCE);
1310 
1311         let mut vmcontref = helpers::VMContRef::new(resume_contref);
1312 
1313         let revision = vmcontref.get_revision(env, builder);
1314         let evidence = builder.ins().icmp(IntCC::Equal, revision, witness);
1315         builder
1316             .ins()
1317             .trapz(evidence, crate::TRAP_CONTINUATION_ALREADY_CONSUMED);
1318         let _next_revision = vmcontref.incr_revision(env, builder, revision);
1319 
1320         if resume_args.len() > 0 {
1321             // We store the arguments in the `VMContRef` to be resumed.
1322             vmcontref_store_payloads(env, builder, resume_args, resume_contref);
1323         }
1324 
1325         // Splice together stack chains:
1326         // Connect the end of the chain starting at `resume_contref` to the currently active chain.
1327         let mut last_ancestor = helpers::VMContRef::new(vmcontref.get_last_ancestor(env, builder));
1328 
1329         // Make the currently running continuation (if any) the parent of the one we are about to resume.
1330         let original_stack_chain = vmctx_load_stack_chain(env, builder, vmctx);
1331         last_ancestor.set_parent_stack_chain(env, builder, &original_stack_chain);
1332 
1333         // Just for consistency: `vmcontref` is about to get state Running, so let's zero out its last_ancestor field.
1334         let zero = builder.ins().iconst(env.pointer_type(), 0);
1335         vmcontref.set_last_ancestor(env, builder, zero);
1336 
1337         // We mark `resume_contref` as the currently running one
1338         vmctx_set_active_continuation(env, builder, vmctx, resume_contref);
1339 
1340         // Note that the resume_contref libcall a few lines further below
1341         // manipulates the stack limits as follows:
1342         // 1. Copy stack_limit, last_wasm_entry_sp and last_wasm_exit* values from
1343         // VMRuntimeLimits into the currently active continuation (i.e., the
1344         // one that will become the parent of the to-be-resumed one)
1345         //
1346         // 2. Copy `stack_limit` and `last_wasm_entry_sp` in the
1347         // `VMStackLimits` of `resume_contref` into the `VMRuntimeLimits`.
1348         //
1349         // See the comment on `wasmtime_environ::VMStackChain` for a
1350         // description of the invariants that we maintain for the various stack
1351         // limits.
1352 
1353         // `resume_contref` is now active, and its parent is suspended.
1354         let resume_contref = helpers::VMContRef::new(resume_contref);
1355         let resume_csi = resume_contref.common_stack_information(env, builder);
1356         let parent_csi = original_stack_chain.get_common_stack_information(env, builder);
1357         resume_csi.set_state_running(env, builder);
1358         parent_csi.set_state_parent(env, builder);
1359 
1360         // We update the `VMStackLimits` of the parent of the continuation to be resumed
1361         // as well as the `VMRuntimeLimits`.
1362         // See the comment on `wasmtime_environ::VMStackChain` for a description
1363         // of the invariants that we maintain for the various stack limits.
1364         let vm_runtime_limits_ptr = vmctx_load_vm_runtime_limits_ptr(env, builder, vmctx);
1365         parent_csi.load_limits_from_vmcontext(env, builder, vm_runtime_limits_ptr, true);
1366         resume_csi.write_limits_to_vmcontext(env, builder, vm_runtime_limits_ptr);
1367 
1368         // Install handlers in (soon to be) parent's VMHandlerList:
1369         // Let the i-th handler clause be (on $tag $block).
1370         // Then the i-th entry of the VMHandlerList will be the address of $tag.
1371         let handler_list = parent_csi.get_handler_list(env, builder);
1372 
1373         if resumetable.len() > 0 {
1374             // Total number of handlers (suspend and switch).
1375             let handler_count = u32::try_from(resumetable.len()).unwrap();
1376             // Populate the Array's data ptr with a pointer to a sufficiently
1377             // large area on this stack.
1378             env.stack_switching_handler_list_buffer =
1379                 Some(handler_list.allocate_or_reuse_stack_slot(
1380                     env,
1381                     builder,
1382                     handler_count,
1383                     env.stack_switching_handler_list_buffer,
1384                 ));
1385 
1386             let suspend_handler_count = suspend_handlers.len();
1387 
1388             // All handlers, represented by the indices of the tags they handle.
1389             // All the suspend handlers come first, followed by all the switch handlers.
1390             let all_handlers = suspend_handlers
1391                 .iter()
1392                 .map(|(tag_index, _block)| *tag_index)
1393                 .chain(switch_tags);
1394 
1395             // Translate all tag indices to tag addresses (i.e., the corresponding *mut VMTagDefinition).
1396             let all_tag_addresses: Vec<ir::Value> = all_handlers
1397                 .map(|tag_index| tag_address(env, builder, tag_index))
1398                 .collect();
1399 
1400             // Store all tag addresses in the handler list.
1401             handler_list.store_data_entries(env, builder, &all_tag_addresses);
1402 
1403             // To enable distinguishing switch and suspend handlers when searching the handler list:
1404             // Store at which index the switch handlers start.
1405             let first_switch_handler_index = builder
1406                 .ins()
1407                 .iconst(I32, i64::try_from(suspend_handler_count).unwrap());
1408             parent_csi.set_first_switch_handler_index(env, builder, first_switch_handler_index);
1409         }
1410 
1411         let resume_payload = ControlEffect::encode_resume(builder).to_u64();
1412 
1413         // Note that the control context we use for switching is not the one in
1414         // (the stack of) resume_contref, but in (the stack of) last_ancestor!
1415         let fiber_stack = last_ancestor.get_fiber_stack(env, builder);
1416         let control_context_ptr = fiber_stack.load_control_context(env, builder);
1417 
1418         let result =
1419             builder
1420                 .ins()
1421                 .stack_switch(control_context_ptr, control_context_ptr, resume_payload);
1422 
1423         // At this point we know nothing about the continuation that just
1424         // suspended or returned. In particular, it does not have to be what we
1425         // called `resume_contref` earlier on. We must reload the information
1426         // about the now active continuation from the VMContext.
1427         let new_stack_chain = vmctx_load_stack_chain(env, builder, vmctx);
1428 
1429         // Now the parent contref (or initial stack) is active again
1430         vmctx_store_stack_chain(env, builder, vmctx, &original_stack_chain);
1431         parent_csi.set_state_running(env, builder);
1432 
1433         // Just for consistency: Clear the handler list.
1434         handler_list.clear(env, builder, true);
1435         parent_csi.set_first_switch_handler_index(env, builder, zero);
1436 
1437         // Extract the result and signal bit.
1438         let result = ControlEffect::from_u64(result);
1439         let signal = result.signal(builder);
1440 
1441         // Jump to the return block if the result signal is 0, otherwise jump to
1442         // the suspend block.
1443         builder
1444             .ins()
1445             .brif(signal, suspend_block, &[], return_block, &[]);
1446 
1447         (
1448             result,
1449             vm_runtime_limits_ptr,
1450             original_stack_chain,
1451             new_stack_chain,
1452         )
1453     };
1454 
1455     // The suspend block: Only used when we suspended, not for returns.
1456     // Here we extract the index of the handler to use.
1457     let (handler_index, suspended_contref, suspended_contobj) = {
1458         builder.switch_to_block(suspend_block);
1459         builder.seal_block(suspend_block);
1460 
1461         let suspended_continuation = new_stack_chain.unchecked_get_continuation();
1462         let mut suspended_continuation = helpers::VMContRef::new(suspended_continuation);
1463         let suspended_csi = suspended_continuation.common_stack_information(env, builder);
1464 
1465         // Note that at the suspend site, we already
1466         // 1. Set the state of suspended_continuation to Suspended
1467         // 2. Set suspended_continuation.last_ancestor
1468         // 3. Broke the continuation chain at suspended_continuation.last_ancestor
1469 
1470         // We store parts of the VMRuntimeLimits into the continuation that just suspended.
1471         suspended_csi.load_limits_from_vmcontext(env, builder, vm_runtime_limits_ptr, false);
1472 
1473         // Afterwards (!), restore parts of the VMRuntimeLimits from the
1474         // parent of the suspended continuation (which is now active).
1475         let parent_csi = original_stack_chain.get_common_stack_information(env, builder);
1476         parent_csi.write_limits_to_vmcontext(env, builder, vm_runtime_limits_ptr);
1477 
1478         // Extract the handler index
1479         let handler_index = resume_result.handler_index(builder);
1480 
1481         let revision = suspended_continuation.get_revision(env, builder);
1482         let suspended_contobj = fatpointer::construct(
1483             env,
1484             &mut builder.cursor(),
1485             revision,
1486             suspended_continuation.address,
1487         );
1488 
1489         // We need to terminate this block before being allowed to switch to
1490         // another one.
1491         builder.ins().jump(dispatch_block, &[]);
1492 
1493         (handler_index, suspended_continuation, suspended_contobj)
1494     };
1495 
1496     // For technical reasons, the jump table needs to have a default
1497     // block. In our case, it should be unreachable, since the handler
1498     // index we dispatch on should correspond to a an actual handler
1499     // block in the jump table.
1500     let jt_default_block = builder.create_block();
1501     {
1502         builder.switch_to_block(jt_default_block);
1503         builder.set_cold_block(jt_default_block);
1504 
1505         builder.ins().trap(crate::TRAP_UNREACHABLE);
1506     }
1507 
1508     // We create a preamble block for each of the actual handler blocks: It
1509     // reads the necessary arguments and passes them to the actual handler
1510     // block, together with the continuation object.
1511     let target_preamble_blocks = {
1512         let mut preamble_blocks = vec![];
1513 
1514         for &(handle_tag, target_block) in &suspend_handlers {
1515             let preamble_block = builder.create_block();
1516             preamble_blocks.push(preamble_block);
1517             builder.switch_to_block(preamble_block);
1518 
1519             let param_types = env.tag_params(TagIndex::from_u32(handle_tag));
1520             let param_types: Vec<ir::Type> = param_types
1521                 .iter()
1522                 .map(|wty| crate::value_type(env.isa(), *wty))
1523                 .collect();
1524 
1525             let values = suspended_contref.values(env, builder);
1526             let mut suspend_args: Vec<BlockArg> = values
1527                 .load_data_entries(env, builder, &param_types)
1528                 .into_iter()
1529                 .map(|v| BlockArg::Value(v))
1530                 .collect();
1531 
1532             // At the suspend site, we store the suspend args in the the
1533             // `values` buffer of the VMContRef that was active at the time that
1534             // the suspend instruction was performed.
1535             suspend_args.push(BlockArg::Value(suspended_contobj));
1536 
1537             // We clear the suspend args. This is mostly for consistency. Note
1538             // that we don't zero out the data buffer, we still need it for the
1539 
1540             values.clear(env, builder, false);
1541 
1542             builder.ins().jump(target_block, &suspend_args);
1543         }
1544 
1545         preamble_blocks
1546     };
1547 
1548     // Dispatch block. All it does is jump to the right premable block based on
1549     // the handler index.
1550     {
1551         builder.switch_to_block(dispatch_block);
1552         builder.seal_block(dispatch_block);
1553 
1554         let default_bc = builder.func.dfg.block_call(jt_default_block, &[]);
1555 
1556         let adapter_bcs: Vec<BlockCall> = target_preamble_blocks
1557             .iter()
1558             .map(|b| builder.func.dfg.block_call(*b, &[]))
1559             .collect();
1560 
1561         let jt_data = JumpTableData::new(default_bc, &adapter_bcs);
1562         let jt = builder.create_jump_table(jt_data);
1563 
1564         builder.ins().br_table(handler_index, jt);
1565 
1566         for preamble_block in target_preamble_blocks {
1567             builder.seal_block(preamble_block);
1568         }
1569         builder.seal_block(jt_default_block);
1570     }
1571 
1572     // Return block: Jumped to by resume block if continuation
1573     // returned normally.
1574     {
1575         builder.switch_to_block(return_block);
1576         builder.seal_block(return_block);
1577 
1578         // If we got a return signal, a continuation must have been running.
1579         let returned_contref = new_stack_chain.unchecked_get_continuation();
1580         let returned_contref = helpers::VMContRef::new(returned_contref);
1581 
1582         // Restore parts of the VMRuntimeLimits from the parent of the
1583         // returned continuation (which is now active).
1584         let parent_csi = original_stack_chain.get_common_stack_information(env, builder);
1585         parent_csi.write_limits_to_vmcontext(env, builder, vm_runtime_limits_ptr);
1586 
1587         let returned_csi = returned_contref.common_stack_information(env, builder);
1588         returned_csi.set_state_returned(env, builder);
1589 
1590         // Load the values returned by the continuation.
1591         let return_types: Vec<_> = env
1592             .continuation_returns(TypeIndex::from_u32(type_index))
1593             .iter()
1594             .map(|ty| crate::value_type(env.isa(), *ty))
1595             .collect();
1596         let payloads = returned_contref.args(env, builder);
1597         let return_values = payloads.load_data_entries(env, builder, &return_types);
1598         payloads.clear(env, builder, true);
1599 
1600         Ok(return_values)
1601     }
1602 }
1603 
1604 pub(crate) fn translate_suspend<'a>(
1605     env: &mut crate::func_environ::FuncEnvironment<'a>,
1606     builder: &mut FunctionBuilder,
1607     tag_index: u32,
1608     suspend_args: &[ir::Value],
1609     tag_return_types: &[ir::Type],
1610 ) -> Vec<ir::Value> {
1611     let tag_addr = tag_address(env, builder, tag_index);
1612 
1613     let vmctx = env.vmctx_val(&mut builder.cursor());
1614     let active_stack_chain = vmctx_load_stack_chain(env, builder, vmctx);
1615 
1616     let (_, end_of_chain_contref, handler_index) =
1617         search_handler(env, builder, &active_stack_chain, tag_addr, true);
1618 
1619     // If we get here, the search_handler logic succeeded (i.e., did not trap).
1620     // Thus, there is at least one parent, so we are not on the initial stack.
1621     // Can therefore extract continuation directly.
1622     let active_contref = active_stack_chain.unchecked_get_continuation();
1623     let active_contref = helpers::VMContRef::new(active_contref);
1624     let mut end_of_chain_contref = helpers::VMContRef::new(end_of_chain_contref);
1625 
1626     active_contref.set_last_ancestor(env, builder, end_of_chain_contref.address);
1627 
1628     // In the active_contref's `values` buffer, stack-allocate enough room so that we can
1629     // later store the following:
1630     // 1. The suspend arguments
1631     // 2. Afterwards, the tag return values
1632     let values = active_contref.values(env, builder);
1633     let required_capacity =
1634         u32::try_from(std::cmp::max(suspend_args.len(), tag_return_types.len()))
1635             .expect("Number of stack switching payloads should fit in u32");
1636 
1637     if required_capacity > 0 {
1638         env.stack_switching_values_buffer = Some(values.allocate_or_reuse_stack_slot(
1639             env,
1640             builder,
1641             required_capacity,
1642             env.stack_switching_values_buffer,
1643         ));
1644     }
1645 
1646     if suspend_args.len() > 0 {
1647         values.store_data_entries(env, builder, suspend_args);
1648     }
1649 
1650     // Set current continuation to suspended and break up handler chain.
1651     let active_contref_csi = active_contref.common_stack_information(env, builder);
1652     active_contref_csi.set_state_suspended(env, builder);
1653     let absent_chain_link = VMStackChain::absent(env, builder);
1654     end_of_chain_contref.set_parent_stack_chain(env, builder, &absent_chain_link);
1655 
1656     let suspend_payload = ControlEffect::encode_suspend(builder, handler_index).to_u64();
1657 
1658     // Note that the control context we use for switching is the one
1659     // at the end of the chain, not the one in active_contref!
1660     // This also means that stack_switch saves the information about
1661     // the current stack in the control context located in the stack
1662     // of end_of_chain_contref.
1663     let fiber_stack = end_of_chain_contref.get_fiber_stack(env, builder);
1664     let control_context_ptr = fiber_stack.load_control_context(env, builder);
1665 
1666     builder
1667         .ins()
1668         .stack_switch(control_context_ptr, control_context_ptr, suspend_payload);
1669 
1670     // The return values of the suspend instruction are the tag return values, saved in the `args` buffer.
1671     let values = active_contref.values(env, builder);
1672     let return_values = values.load_data_entries(env, builder, tag_return_types);
1673     // We effectively consume the values and discard the stack allocated buffer.
1674     values.clear(env, builder, true);
1675 
1676     return_values
1677 }
1678 
1679 pub(crate) fn translate_switch<'a>(
1680     env: &mut crate::func_environ::FuncEnvironment<'a>,
1681     builder: &mut FunctionBuilder,
1682     tag_index: u32,
1683     switchee_contobj: ir::Value,
1684     switch_args: &[ir::Value],
1685     return_types: &[ir::Type],
1686 ) -> WasmResult<Vec<ir::Value>> {
1687     let vmctx = env.vmctx_val(&mut builder.cursor());
1688 
1689     // Check and increment revision on switchee continuation object (i.e., the
1690     // one being switched to). Logically, the switchee continuation extends from
1691     // `switchee_contref` to `switchee_contref.last_ancestor` (i.e., the end of
1692     // the parent chain starting at `switchee_contref`).
1693     let switchee_contref = {
1694         let (witness, target_contref) =
1695             fatpointer::deconstruct(env, &mut builder.cursor(), switchee_contobj);
1696 
1697         // The typing rules for switch allow a null reference to be passed to it.
1698         builder
1699             .ins()
1700             .trapz(target_contref, crate::TRAP_NULL_REFERENCE);
1701 
1702         let mut target_contref = helpers::VMContRef::new(target_contref);
1703 
1704         let revision = target_contref.get_revision(env, builder);
1705         let evidence = builder.ins().icmp(IntCC::Equal, revision, witness);
1706         builder
1707             .ins()
1708             .trapz(evidence, crate::TRAP_CONTINUATION_ALREADY_CONSUMED);
1709         let _next_revision = target_contref.incr_revision(env, builder, revision);
1710         target_contref
1711     };
1712 
1713     // We create the "switcher continuation" (i.e., the one executing switch)
1714     // from the current execution context: Logically, it extends from the
1715     // continuation reference executing `switch` (subsequently called
1716     // `switcher_contref`) to the immediate child (called
1717     // `switcher_contref_last_ancestor`) of the stack with the corresponding
1718     // handler (saved in `handler_stack_chain`).
1719     let (
1720         switcher_contref,
1721         switcher_contobj,
1722         switcher_contref_last_ancestor,
1723         handler_stack_chain,
1724         vm_runtime_limits_ptr,
1725     ) = {
1726         let tag_addr = tag_address(env, builder, tag_index);
1727         let active_stack_chain = vmctx_load_stack_chain(env, builder, vmctx);
1728         let (handler_stack_chain, last_ancestor, _handler_index) =
1729             search_handler(env, builder, &active_stack_chain, tag_addr, false);
1730         let mut last_ancestor = helpers::VMContRef::new(last_ancestor);
1731 
1732         // If we get here, the search_handler logic succeeded (i.e., did not trap).
1733         // Thus, there is at least one parent, so we are not on the initial stack.
1734         // Can therefore extract continuation directly.
1735         let switcher_contref = active_stack_chain.unchecked_get_continuation();
1736         let mut switcher_contref = helpers::VMContRef::new(switcher_contref);
1737 
1738         switcher_contref.set_last_ancestor(env, builder, last_ancestor.address);
1739 
1740         // In the switcher_contref's `values` buffer, stack-allocate enough room so that we can
1741         // later store `tag_return_types.len()` when resuming the continuation.
1742         let values = switcher_contref.values(env, builder);
1743         let required_capacity = u32::try_from(return_types.len()).unwrap();
1744         if required_capacity > 0 {
1745             env.stack_switching_values_buffer = Some(values.allocate_or_reuse_stack_slot(
1746                 env,
1747                 builder,
1748                 required_capacity,
1749                 env.stack_switching_values_buffer,
1750             ));
1751         }
1752 
1753         let switcher_contref_csi = switcher_contref.common_stack_information(env, builder);
1754         switcher_contref_csi.set_state_suspended(env, builder);
1755         // We break off `switcher_contref` from the chain of active
1756         // continuations, by separating the link between `last_ancestor` and its
1757         // parent stack.
1758         let absent = VMStackChain::absent(env, builder);
1759         last_ancestor.set_parent_stack_chain(env, builder, &absent);
1760 
1761         // Load current runtime limits from `VMContext` and store in the
1762         // switcher continuation.
1763         let vm_runtime_limits_ptr = vmctx_load_vm_runtime_limits_ptr(env, builder, vmctx);
1764         switcher_contref_csi.load_limits_from_vmcontext(env, builder, vm_runtime_limits_ptr, false);
1765 
1766         let revision = switcher_contref.get_revision(env, builder);
1767         let new_contobj = fatpointer::construct(
1768             env,
1769             &mut builder.cursor(),
1770             revision,
1771             switcher_contref.address,
1772         );
1773 
1774         (
1775             switcher_contref,
1776             new_contobj,
1777             last_ancestor,
1778             handler_stack_chain,
1779             vm_runtime_limits_ptr,
1780         )
1781     };
1782 
1783     // Prepare switchee continuation:
1784     // - Store "ordinary" switch arguments as well as the contobj just
1785     //   synthesized from the current context (i.e., `switcher_contobj`) in the
1786     //   switchee continuation's payload buffer.
1787     // - Splice switchee's continuation chain with handler stack to form new
1788     //   overall chain of active continuations.
1789     let (switchee_contref_csi, switchee_contref_last_ancestor) = {
1790         let mut combined_payloads = switch_args.to_vec();
1791         combined_payloads.push(switcher_contobj);
1792         vmcontref_store_payloads(env, builder, &combined_payloads, switchee_contref.address);
1793 
1794         let switchee_contref_csi = switchee_contref.common_stack_information(env, builder);
1795         switchee_contref_csi.set_state_running(env, builder);
1796 
1797         let switchee_contref_last_ancestor = switchee_contref.get_last_ancestor(env, builder);
1798         let mut switchee_contref_last_ancestor =
1799             helpers::VMContRef::new(switchee_contref_last_ancestor);
1800 
1801         switchee_contref_last_ancestor.set_parent_stack_chain(env, builder, &handler_stack_chain);
1802 
1803         (switchee_contref_csi, switchee_contref_last_ancestor)
1804     };
1805 
1806     // Update VMContext/Store: Update active continuation and `VMRuntimeLimits`.
1807     {
1808         vmctx_set_active_continuation(env, builder, vmctx, switchee_contref.address);
1809 
1810         switchee_contref_csi.write_limits_to_vmcontext(env, builder, vm_runtime_limits_ptr);
1811     }
1812 
1813     // Perform actual stack switch
1814     {
1815         let switcher_last_ancestor_fs =
1816             switcher_contref_last_ancestor.get_fiber_stack(env, builder);
1817         let switcher_last_ancestor_cc =
1818             switcher_last_ancestor_fs.load_control_context(env, builder);
1819 
1820         let switchee_last_ancestor_fs =
1821             switchee_contref_last_ancestor.get_fiber_stack(env, builder);
1822         let switchee_last_ancestor_cc =
1823             switchee_last_ancestor_fs.load_control_context(env, builder);
1824 
1825         // The stack switch involves the following control contexts (e.g., IP,
1826         // SP, FP, ...):
1827         // - `switchee_last_ancestor_cc` contains the information to continue
1828         //    execution in the switchee/target continuation.
1829         // - `switcher_last_ancestor_cc` contains the information about how to
1830         //    continue execution once we suspend/return to the stack with the
1831         //    switch handler.
1832         //
1833         // In total, the following needs to happen:
1834         // 1. Load control context at `switchee_last_ancestor_cc` to perform
1835         //    stack switch.
1836         // 2. Move control context at `switcher_last_ancestor_cc` over to
1837         //    `switchee_last_ancestor_cc`.
1838         // 3. Upon actual switch, save current control context at
1839         //    `switcher_last_ancestor_cc`.
1840         //
1841         // We implement this as follows:
1842         // 1. We copy `switchee_last_ancestor_cc` to a temporary area on the
1843         //    stack (`tmp_control_context`).
1844         // 2. We copy `switcher_last_ancestor_cc` over to
1845         //    `switchee_last_ancestor_cc`.
1846         // 3. We invoke the stack switch instruction such that it reads from the
1847         //    temporary area, and writes to `switcher_last_ancestor_cc`.
1848         //
1849         // Note that the temporary area is only accessed once by the
1850         // `stack_switch` instruction emitted later in this block, meaning that we
1851         // don't have to worry about its lifetime.
1852         //
1853         // NOTE(frank-emrich) The implementation below results in one stack slot
1854         // being created per switch instruction, even though multiple switch
1855         // instructions in the same function could safely re-use the same stack
1856         // slot. Thus, we could implement logic for sharing the stack slot by
1857         // adding an appropriate field to `FuncEnvironment`.
1858         //
1859         // NOTE(frank-emrich) We could avoid the copying to a temporary area by
1860         // making `stack_switch` do all of the necessary moving itself. However,
1861         // that would be a rather ad-hoc change to how the instruction uses the
1862         // two pointers given to it.
1863 
1864         let cctx_size = control_context_size(env.isa().triple())?;
1865         let slot_size = ir::StackSlotData::new(
1866             ir::StackSlotKind::ExplicitSlot,
1867             u32::from(cctx_size),
1868             u8::try_from(env.pointer_type().bytes()).unwrap(),
1869         );
1870         let slot = builder.create_sized_stack_slot(slot_size);
1871         let tmp_control_context = builder.ins().stack_addr(env.pointer_type(), slot, 0);
1872 
1873         let flags = MemFlags::trusted();
1874         let mut offset: i32 = 0;
1875         while offset < i32::from(cctx_size) {
1876             // switchee_last_ancestor_cc -> tmp control context
1877             let tmp1 =
1878                 builder
1879                     .ins()
1880                     .load(env.pointer_type(), flags, switchee_last_ancestor_cc, offset);
1881             builder
1882                 .ins()
1883                 .store(flags, tmp1, tmp_control_context, offset);
1884 
1885             // switcher_last_ancestor_cc -> switchee_last_ancestor_cc
1886             let tmp2 =
1887                 builder
1888                     .ins()
1889                     .load(env.pointer_type(), flags, switcher_last_ancestor_cc, offset);
1890             builder
1891                 .ins()
1892                 .store(flags, tmp2, switchee_last_ancestor_cc, offset);
1893 
1894             offset += i32::try_from(env.pointer_type().bytes()).unwrap();
1895         }
1896 
1897         let switch_payload = ControlEffect::encode_switch(builder).to_u64();
1898 
1899         let _result = builder.ins().stack_switch(
1900             switcher_last_ancestor_cc,
1901             tmp_control_context,
1902             switch_payload,
1903         );
1904     }
1905 
1906     // After switching back to the original stack: Load return values, they are
1907     // stored on the switcher continuation.
1908     let return_values = {
1909         let payloads = switcher_contref.values(env, builder);
1910         let return_values = payloads.load_data_entries(env, builder, return_types);
1911         // We consume the values and discard the buffer (allocated on this stack)
1912         payloads.clear(env, builder, true);
1913         return_values
1914     };
1915 
1916     Ok(return_values)
1917 }
1918