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