Path: blob/main/crates/cranelift/src/func_environ/stack_switching/instructions.rs
1693 views
use cranelift_codegen::ir::BlockArg;1use itertools::{Either, Itertools};23use cranelift_codegen::ir::condcodes::*;4use cranelift_codegen::ir::types::*;5use cranelift_codegen::ir::{self, MemFlags};6use cranelift_codegen::ir::{Block, BlockCall, InstBuilder, JumpTableData};7use cranelift_frontend::FunctionBuilder;8use wasmtime_environ::{PtrSize, TagIndex, TypeIndex, WasmResult, WasmValType, wasm_unsupported};910fn control_context_size(triple: &target_lexicon::Triple) -> WasmResult<u8> {11match (triple.architecture, triple.operating_system) {12(target_lexicon::Architecture::X86_64, target_lexicon::OperatingSystem::Linux) => Ok(24),13_ => Err(wasm_unsupported!(14"stack switching not supported on {triple}"15)),16}17}1819use super::control_effect::ControlEffect;20use super::fatpointer;2122/// This module contains compile-time counterparts to types defined elsewhere.23pub(crate) mod stack_switching_helpers {24use core::marker::PhantomData;25use cranelift_codegen::ir;26use cranelift_codegen::ir::InstBuilder;27use cranelift_codegen::ir::condcodes::IntCC;28use cranelift_codegen::ir::types::*;29use cranelift_codegen::ir::{StackSlot, StackSlotKind::*};30use cranelift_frontend::FunctionBuilder;31use wasmtime_environ::PtrSize;3233/// Provides information about the layout of a type when it is used as an34/// element in a host array. This is used for `VMHostArrayRef`.35pub(crate) trait VMHostArrayEntry {36/// Returns `(align, size)` in bytes.37fn vmhostarray_entry_layout<P: wasmtime_environ::PtrSize>(p: &P) -> (u8, u32);38}3940impl VMHostArrayEntry for u128 {41fn vmhostarray_entry_layout<P: wasmtime_environ::PtrSize>(_p: &P) -> (u8, u32) {42(16, 16)43}44}4546impl<T> VMHostArrayEntry for *mut T {47fn vmhostarray_entry_layout<P: wasmtime_environ::PtrSize>(p: &P) -> (u8, u32) {48(p.size(), p.size().into())49}50}5152#[derive(Copy, Clone)]53pub struct VMContRef {54pub address: ir::Value,55}5657#[derive(Copy, Clone)]58pub struct VMHostArrayRef<T> {59/// Address of the VMHostArray we are referencing60address: ir::Value,6162/// The type parameter T is never used in the fields above. We still63/// want to have it for consistency with64/// `wasmtime_environ::Vector` and to use it in the associated65/// functions.66phantom: PhantomData<T>,67}6869pub type VMPayloads = VMHostArrayRef<u128>;7071// Actually a vector of *mut VMTagDefinition72pub type VMHandlerList = VMHostArrayRef<*mut u8>;7374/// Compile-time representation of wasmtime_environ::VMStackChain,75/// consisting of two `ir::Value`s.76pub struct VMStackChain {77discriminant: ir::Value,78payload: ir::Value,79}8081pub struct VMCommonStackInformation {82pub address: ir::Value,83}8485/// Compile-time representation of `crate::runtime::vm::stack::VMContinuationStack`.86pub struct VMContinuationStack {87/// This is NOT the "top of stack" address of the stack itself. In line88/// with how the (runtime) `FiberStack` type works, this is a pointer to89/// the TOS address.90tos_ptr: ir::Value,91}9293impl VMContRef {94pub fn new(address: ir::Value) -> VMContRef {95VMContRef { address }96}9798pub fn args<'a>(99&self,100env: &mut crate::func_environ::FuncEnvironment<'a>,101builder: &mut FunctionBuilder,102) -> VMPayloads {103let offset: i64 = env.offsets.ptr.vmcontref_args().into();104let address = builder.ins().iadd_imm(self.address, offset);105VMPayloads::new(address)106}107108pub fn values<'a>(109&self,110env: &mut crate::func_environ::FuncEnvironment<'a>,111builder: &mut FunctionBuilder,112) -> VMPayloads {113let offset: i64 = env.offsets.ptr.vmcontref_values().into();114let address = builder.ins().iadd_imm(self.address, offset);115VMPayloads::new(address)116}117118pub fn common_stack_information<'a>(119&self,120env: &mut crate::func_environ::FuncEnvironment<'a>,121builder: &mut FunctionBuilder,122) -> VMCommonStackInformation {123let offset: i64 = env.offsets.ptr.vmcontref_common_stack_information().into();124let address = builder.ins().iadd_imm(self.address, offset);125VMCommonStackInformation { address }126}127128/// Stores the parent of this continuation, which may either be another129/// continuation or the initial stack. It is therefore represented as a130/// `VMStackChain` element.131pub fn set_parent_stack_chain<'a>(132&mut self,133env: &mut crate::func_environ::FuncEnvironment<'a>,134builder: &mut FunctionBuilder,135new_stack_chain: &VMStackChain,136) {137let offset = env.offsets.ptr.vmcontref_parent_chain().into();138new_stack_chain.store(env, builder, self.address, offset)139}140141/// Loads the parent of this continuation, which may either be another142/// continuation or the initial stack. It is therefore represented as a143/// `VMStackChain` element.144pub fn get_parent_stack_chain<'a>(145&self,146env: &mut crate::func_environ::FuncEnvironment<'a>,147builder: &mut FunctionBuilder,148) -> VMStackChain {149let offset = env.offsets.ptr.vmcontref_parent_chain().into();150VMStackChain::load(env, builder, self.address, offset, env.pointer_type())151}152153pub fn set_last_ancestor<'a>(154&self,155env: &mut crate::func_environ::FuncEnvironment<'a>,156builder: &mut FunctionBuilder,157last_ancestor: ir::Value,158) {159let offset: i32 = env.offsets.ptr.vmcontref_last_ancestor().into();160let mem_flags = ir::MemFlags::trusted();161builder162.ins()163.store(mem_flags, last_ancestor, self.address, offset);164}165166pub fn get_last_ancestor<'a>(167&self,168env: &mut crate::func_environ::FuncEnvironment<'a>,169builder: &mut FunctionBuilder,170) -> ir::Value {171let offset: i32 = env.offsets.ptr.vmcontref_last_ancestor().into();172let mem_flags = ir::MemFlags::trusted();173builder174.ins()175.load(env.pointer_type(), mem_flags, self.address, offset)176}177178/// Gets the revision counter the a given continuation179/// reference.180pub fn get_revision<'a>(181&mut self,182env: &mut crate::func_environ::FuncEnvironment<'a>,183builder: &mut FunctionBuilder,184) -> ir::Value {185let mem_flags = ir::MemFlags::trusted();186let offset: i32 = env.offsets.ptr.vmcontref_revision().into();187let revision = builder.ins().load(I64, mem_flags, self.address, offset);188revision189}190191/// Sets the revision counter on the given continuation192/// reference to `revision + 1`.193194pub fn incr_revision<'a>(195&mut self,196env: &mut crate::func_environ::FuncEnvironment<'a>,197builder: &mut FunctionBuilder,198revision: ir::Value,199) -> ir::Value {200let mem_flags = ir::MemFlags::trusted();201let offset: i32 = env.offsets.ptr.vmcontref_revision().into();202let revision_plus1 = builder.ins().iadd_imm(revision, 1);203builder204.ins()205.store(mem_flags, revision_plus1, self.address, offset);206revision_plus1207}208209pub fn get_fiber_stack<'a>(210&self,211env: &mut crate::func_environ::FuncEnvironment<'a>,212builder: &mut FunctionBuilder,213) -> VMContinuationStack {214// The top of stack field is stored at offset 0 of the `FiberStack`.215let offset: i64 = env.offsets.ptr.vmcontref_stack().into();216let fiber_stack_top_of_stack_ptr = builder.ins().iadd_imm(self.address, offset);217VMContinuationStack::new(fiber_stack_top_of_stack_ptr)218}219}220221impl<T: VMHostArrayEntry> VMHostArrayRef<T> {222pub(crate) fn new(address: ir::Value) -> Self {223Self {224address,225phantom: PhantomData::default(),226}227}228229fn get(&self, builder: &mut FunctionBuilder, ty: ir::Type, offset: i32) -> ir::Value {230let mem_flags = ir::MemFlags::trusted();231builder.ins().load(ty, mem_flags, self.address, offset)232}233234fn set<U>(&self, builder: &mut FunctionBuilder, offset: i32, value: ir::Value) {235debug_assert_eq!(236builder.func.dfg.value_type(value),237Type::int_with_byte_size(u16::try_from(core::mem::size_of::<U>()).unwrap())238.unwrap()239);240let mem_flags = ir::MemFlags::trusted();241builder.ins().store(mem_flags, value, self.address, offset);242}243244pub fn get_data<'a>(245&self,246env: &mut crate::func_environ::FuncEnvironment<'a>,247builder: &mut FunctionBuilder,248) -> ir::Value {249let offset = env.offsets.ptr.vmhostarray_data().into();250self.get(builder, env.pointer_type(), offset)251}252253pub fn get_length<'a>(254&self,255env: &mut crate::func_environ::FuncEnvironment<'a>,256builder: &mut FunctionBuilder,257) -> ir::Value {258// Array length is stored as u32.259let offset = env.offsets.ptr.vmhostarray_length().into();260self.get(builder, I32, offset)261}262263fn set_length<'a>(264&self,265env: &mut crate::func_environ::FuncEnvironment<'a>,266builder: &mut FunctionBuilder,267length: ir::Value,268) {269// Array length is stored as u32.270let offset = env.offsets.ptr.vmhostarray_length().into();271self.set::<u32>(builder, offset, length);272}273274fn set_capacity<'a>(275&self,276env: &mut crate::func_environ::FuncEnvironment<'a>,277builder: &mut FunctionBuilder,278capacity: ir::Value,279) {280// Array capacity is stored as u32.281let offset = env.offsets.ptr.vmhostarray_capacity().into();282self.set::<u32>(builder, offset, capacity);283}284285fn set_data<'a>(286&self,287env: &mut crate::func_environ::FuncEnvironment<'a>,288builder: &mut FunctionBuilder,289data: ir::Value,290) {291debug_assert_eq!(builder.func.dfg.value_type(data), env.pointer_type());292let offset: i32 = env.offsets.ptr.vmhostarray_data().into();293let mem_flags = ir::MemFlags::trusted();294builder.ins().store(mem_flags, data, self.address, offset);295}296297/// Returns pointer to next empty slot in data buffer and marks the298/// subsequent `arg_count` slots as occupied.299pub fn occupy_next_slots<'a>(300&self,301env: &mut crate::func_environ::FuncEnvironment<'a>,302builder: &mut FunctionBuilder,303arg_count: i32,304) -> ir::Value {305let data = self.get_data(env, builder);306let original_length = self.get_length(env, builder);307let new_length = builder308.ins()309.iadd_imm(original_length, i64::from(arg_count));310self.set_length(env, builder, new_length);311312let (_align, entry_size) = T::vmhostarray_entry_layout(&env.offsets.ptr);313let original_length = builder.ins().uextend(I64, original_length);314let byte_offset = builder315.ins()316.imul_imm(original_length, i64::from(entry_size));317builder.ins().iadd(data, byte_offset)318}319320pub fn allocate_or_reuse_stack_slot<'a>(321&self,322env: &mut crate::func_environ::FuncEnvironment<'a>,323builder: &mut FunctionBuilder,324required_capacity: u32,325existing_slot: Option<StackSlot>,326) -> StackSlot {327let (align, entry_size) = T::vmhostarray_entry_layout(&env.offsets.ptr);328let required_size = required_capacity * entry_size;329330match existing_slot {331Some(slot) if builder.func.sized_stack_slots[slot].size >= required_size => {332let slot_data = &builder.func.sized_stack_slots[slot];333debug_assert!(align <= slot_data.align_shift);334debug_assert_eq!(slot_data.kind, ExplicitSlot);335let existing_capacity = slot_data.size / entry_size;336337let capacity_value = builder.ins().iconst(I32, i64::from(existing_capacity));338let existing_data = builder.ins().stack_addr(env.pointer_type(), slot, 0);339340self.set_capacity(env, builder, capacity_value);341self.set_data(env, builder, existing_data);342343slot344}345_ => {346let capacity_value = builder.ins().iconst(I32, i64::from(required_capacity));347let slot_size = ir::StackSlotData::new(348ir::StackSlotKind::ExplicitSlot,349required_size,350align,351);352let slot = builder.create_sized_stack_slot(slot_size);353let new_data = builder.ins().stack_addr(env.pointer_type(), slot, 0);354355self.set_capacity(env, builder, capacity_value);356self.set_data(env, builder, new_data);357358slot359}360}361}362363/// Loads n entries from this Vector object, where n is the length of364/// `load_types`, which also gives the types of the values to load.365/// Loading starts at index 0 of the Vector object.366pub fn load_data_entries<'a>(367&self,368env: &mut crate::func_environ::FuncEnvironment<'a>,369builder: &mut FunctionBuilder,370load_types: &[ir::Type],371) -> Vec<ir::Value> {372let memflags = ir::MemFlags::trusted();373374let data_start_pointer = self.get_data(env, builder);375let mut values = vec![];376let mut offset = 0;377let (_align, entry_size) = T::vmhostarray_entry_layout(&env.offsets.ptr);378for valtype in load_types {379let val = builder380.ins()381.load(*valtype, memflags, data_start_pointer, offset);382values.push(val);383offset += i32::try_from(entry_size).unwrap();384}385values386}387388/// Stores the given `values` in this Vector object, beginning at389/// index 0. This expects the Vector object to be empty (i.e., current390/// length is 0), and to be of sufficient capacity to store |`values`|391/// entries.392pub fn store_data_entries<'a>(393&self,394env: &mut crate::func_environ::FuncEnvironment<'a>,395builder: &mut FunctionBuilder,396values: &[ir::Value],397) {398let store_count = builder399.ins()400.iconst(I32, i64::try_from(values.len()).unwrap());401402let (_align, entry_size) = T::vmhostarray_entry_layout(&env.offsets.ptr);403404debug_assert!(values.iter().all(|val| {405let ty = builder.func.dfg.value_type(*val);406let size = ty.bytes();407size <= entry_size408}));409410let memflags = ir::MemFlags::trusted();411412let data_start_pointer = self.get_data(env, builder);413414let mut offset = 0;415for value in values {416builder417.ins()418.store(memflags, *value, data_start_pointer, offset);419offset += i32::try_from(entry_size).unwrap();420}421422self.set_length(env, builder, store_count);423}424425pub fn clear<'a>(426&self,427env: &mut crate::func_environ::FuncEnvironment<'a>,428builder: &mut FunctionBuilder,429discard_buffer: bool,430) {431let zero32 = builder.ins().iconst(I32, 0);432self.set_length(env, builder, zero32);433434if discard_buffer {435let zero32 = builder.ins().iconst(I32, 0);436self.set_capacity(env, builder, zero32);437438let zero_ptr = builder.ins().iconst(env.pointer_type(), 0);439self.set_data(env, builder, zero_ptr);440}441}442}443444impl VMStackChain {445/// Creates a `Self` corressponding to `VMStackChain::Continuation(contref)`.446pub fn from_continuation<'a>(447env: &mut crate::func_environ::FuncEnvironment<'a>,448builder: &mut FunctionBuilder,449contref: ir::Value,450) -> VMStackChain {451debug_assert_eq!(452env.offsets.ptr.size_of_vmstack_chain(),4532 * env.offsets.ptr.size()454);455let discriminant = wasmtime_environ::STACK_CHAIN_CONTINUATION_DISCRIMINANT;456let discriminant = builder457.ins()458.iconst(env.pointer_type(), i64::try_from(discriminant).unwrap());459VMStackChain {460discriminant,461payload: contref,462}463}464465/// Creates a `Self` corressponding to `VMStackChain::Absent`.466pub fn absent<'a>(467env: &mut crate::func_environ::FuncEnvironment<'a>,468builder: &mut FunctionBuilder,469) -> VMStackChain {470debug_assert_eq!(471env.offsets.ptr.size_of_vmstack_chain(),4722 * env.offsets.ptr.size()473);474let discriminant = wasmtime_environ::STACK_CHAIN_ABSENT_DISCRIMINANT;475let discriminant = builder476.ins()477.iconst(env.pointer_type(), i64::try_from(discriminant).unwrap());478let zero_filler = builder.ins().iconst(env.pointer_type(), 0i64);479VMStackChain {480discriminant,481payload: zero_filler,482}483}484485pub fn is_initial_stack<'a>(486&self,487_env: &mut crate::func_environ::FuncEnvironment<'a>,488builder: &mut FunctionBuilder,489) -> ir::Value {490builder.ins().icmp_imm(491IntCC::Equal,492self.discriminant,493i64::try_from(wasmtime_environ::STACK_CHAIN_INITIAL_STACK_DISCRIMINANT).unwrap(),494)495}496497/// Return the two raw `ir::Value`s that represent this VMStackChain.498pub fn to_raw_parts(&self) -> [ir::Value; 2] {499[self.discriminant, self.payload]500}501502/// Construct a `Self` from two raw `ir::Value`s.503pub fn from_raw_parts(raw_data: [ir::Value; 2]) -> VMStackChain {504VMStackChain {505discriminant: raw_data[0],506payload: raw_data[1],507}508}509510/// Load a `VMStackChain` object from the given address.511pub fn load<'a>(512_env: &mut crate::func_environ::FuncEnvironment<'a>,513builder: &mut FunctionBuilder,514pointer: ir::Value,515initial_offset: i32,516pointer_type: ir::Type,517) -> VMStackChain {518let memflags = ir::MemFlags::trusted();519let mut offset = initial_offset;520let mut data = vec![];521for _ in 0..2 {522data.push(builder.ins().load(pointer_type, memflags, pointer, offset));523offset += i32::try_from(pointer_type.bytes()).unwrap();524}525let data = <[ir::Value; 2]>::try_from(data).unwrap();526Self::from_raw_parts(data)527}528529/// Store this `VMStackChain` object at the given address.530pub fn store<'a>(531&self,532env: &mut crate::func_environ::FuncEnvironment<'a>,533builder: &mut FunctionBuilder,534target_pointer: ir::Value,535initial_offset: i32,536) {537let memflags = ir::MemFlags::trusted();538let mut offset = initial_offset;539let data = self.to_raw_parts();540541for value in data {542debug_assert_eq!(builder.func.dfg.value_type(value), env.pointer_type());543builder.ins().store(memflags, value, target_pointer, offset);544offset += i32::try_from(env.pointer_type().bytes()).unwrap();545}546}547548/// Use this only if you've already checked that `self` corresponds to a `VMStackChain::Continuation`.549pub fn unchecked_get_continuation(&self) -> ir::Value {550self.payload551}552553/// Must only be called if `self` represents a `InitialStack` or554/// `Continuation` variant. Returns a pointer to the associated555/// `CommonStackInformation` object.556pub fn get_common_stack_information<'a>(557&self,558env: &mut crate::func_environ::FuncEnvironment<'a>,559_builder: &mut FunctionBuilder,560) -> VMCommonStackInformation {561// `self` corresponds to a VMStackChain::InitialStack or562// VMStackChain::Continuation.563// In both cases, the payload is a pointer.564let address = self.payload;565566// `obj` is now a pointer to the beginning of either567// 1. A `VMContRef` struct (in the case of a568// VMStackChain::Continuation)569// 2. A CommonStackInformation struct (in the case of570// VMStackChain::InitialStack)571//572// Since a `VMContRef` starts with an (inlined) CommonStackInformation573// object at offset 0, we actually have in both cases that `ptr` is574// now the address of the beginning of a VMStackLimits object.575debug_assert_eq!(env.offsets.ptr.vmcontref_common_stack_information(), 0);576VMCommonStackInformation { address }577}578}579580impl VMCommonStackInformation {581fn get_state_ptr<'a>(582&self,583env: &mut crate::func_environ::FuncEnvironment<'a>,584builder: &mut FunctionBuilder,585) -> ir::Value {586let offset: i64 = env.offsets.ptr.vmcommon_stack_information_state().into();587588builder.ins().iadd_imm(self.address, offset)589}590591fn get_stack_limits_ptr<'a>(592&self,593env: &mut crate::func_environ::FuncEnvironment<'a>,594builder: &mut FunctionBuilder,595) -> ir::Value {596let offset: i64 = env.offsets.ptr.vmcommon_stack_information_limits().into();597598builder.ins().iadd_imm(self.address, offset)599}600601fn load_state<'a>(602&self,603env: &mut crate::func_environ::FuncEnvironment<'a>,604builder: &mut FunctionBuilder,605) -> ir::Value {606let mem_flags = ir::MemFlags::trusted();607let state_ptr = self.get_state_ptr(env, builder);608609builder.ins().load(I32, mem_flags, state_ptr, 0)610}611612fn set_state_no_payload<'a>(613&self,614env: &mut crate::func_environ::FuncEnvironment<'a>,615builder: &mut FunctionBuilder,616discriminant: u32,617) {618let discriminant = builder.ins().iconst(I32, i64::from(discriminant));619let mem_flags = ir::MemFlags::trusted();620let state_ptr = self.get_state_ptr(env, builder);621622builder.ins().store(mem_flags, discriminant, state_ptr, 0);623}624625pub fn set_state_running<'a>(626&self,627env: &mut crate::func_environ::FuncEnvironment<'a>,628builder: &mut FunctionBuilder,629) {630let discriminant = wasmtime_environ::STACK_STATE_RUNNING_DISCRIMINANT;631self.set_state_no_payload(env, builder, discriminant);632}633634pub fn set_state_parent<'a>(635&self,636env: &mut crate::func_environ::FuncEnvironment<'a>,637builder: &mut FunctionBuilder,638) {639let discriminant = wasmtime_environ::STACK_STATE_PARENT_DISCRIMINANT;640self.set_state_no_payload(env, builder, discriminant);641}642643pub fn set_state_returned<'a>(644&self,645env: &mut crate::func_environ::FuncEnvironment<'a>,646builder: &mut FunctionBuilder,647) {648let discriminant = wasmtime_environ::STACK_STATE_RETURNED_DISCRIMINANT;649self.set_state_no_payload(env, builder, discriminant);650}651652pub fn set_state_suspended<'a>(653&self,654env: &mut crate::func_environ::FuncEnvironment<'a>,655builder: &mut FunctionBuilder,656) {657let discriminant = wasmtime_environ::STACK_STATE_SUSPENDED_DISCRIMINANT;658self.set_state_no_payload(env, builder, discriminant);659}660661/// Checks whether the `VMStackState` reflects that the stack has ever been662/// active (instead of just having been allocated, but never resumed).663pub fn was_invoked<'a>(664&self,665env: &mut crate::func_environ::FuncEnvironment<'a>,666builder: &mut FunctionBuilder,667) -> ir::Value {668let actual_state = self.load_state(env, builder);669let allocated = wasmtime_environ::STACK_STATE_FRESH_DISCRIMINANT;670builder671.ins()672.icmp_imm(IntCC::NotEqual, actual_state, i64::from(allocated))673}674675pub fn get_handler_list<'a>(676&self,677env: &mut crate::func_environ::FuncEnvironment<'a>,678builder: &mut FunctionBuilder,679) -> VMHandlerList {680let offset: i64 = env.offsets.ptr.vmcommon_stack_information_handlers().into();681let address = builder.ins().iadd_imm(self.address, offset);682VMHandlerList::new(address)683}684685pub fn get_first_switch_handler_index<'a>(686&self,687env: &mut crate::func_environ::FuncEnvironment<'a>,688builder: &mut FunctionBuilder,689) -> ir::Value {690// Field first_switch_handler_index has type u32691let memflags = ir::MemFlags::trusted();692let offset: i32 = env693.offsets694.ptr695.vmcommon_stack_information_first_switch_handler_index()696.into();697builder.ins().load(I32, memflags, self.address, offset)698}699700pub fn set_first_switch_handler_index<'a>(701&self,702env: &mut crate::func_environ::FuncEnvironment<'a>,703builder: &mut FunctionBuilder,704value: ir::Value,705) {706// Field first_switch_handler_index has type u32707let memflags = ir::MemFlags::trusted();708let offset: i32 = env709.offsets710.ptr711.vmcommon_stack_information_first_switch_handler_index()712.into();713builder.ins().store(memflags, value, self.address, offset);714}715716/// Sets `last_wasm_entry_sp` and `stack_limit` fields in717/// `VMRuntimelimits` using the values from the `VMStackLimits` of this718/// object.719pub fn write_limits_to_vmcontext<'a>(720&self,721env: &mut crate::func_environ::FuncEnvironment<'a>,722builder: &mut FunctionBuilder,723vmruntime_limits_ptr: ir::Value,724) {725let stack_limits_ptr = self.get_stack_limits_ptr(env, builder);726727let memflags = ir::MemFlags::trusted();728729let mut copy_to_vm_runtime_limits = |our_offset, their_offset| {730let our_value = builder.ins().load(731env.pointer_type(),732memflags,733stack_limits_ptr,734i32::from(our_offset),735);736builder.ins().store(737memflags,738our_value,739vmruntime_limits_ptr,740i32::from(their_offset),741);742};743744let pointer_size = u8::try_from(env.pointer_type().bytes()).unwrap();745let stack_limit_offset = env.offsets.ptr.vmstack_limits_stack_limit();746let last_wasm_entry_fp_offset = env.offsets.ptr.vmstack_limits_last_wasm_entry_fp();747copy_to_vm_runtime_limits(748stack_limit_offset,749pointer_size.vmstore_context_stack_limit(),750);751copy_to_vm_runtime_limits(752last_wasm_entry_fp_offset,753pointer_size.vmstore_context_last_wasm_entry_fp(),754);755}756757/// Overwrites the `last_wasm_entry_fp` field of the `VMStackLimits`758/// object in the `VMStackLimits` of this object by loading the corresponding759/// field from the `VMRuntimeLimits`.760/// If `load_stack_limit` is true, we do the same for the `stack_limit`761/// field.762pub fn load_limits_from_vmcontext<'a>(763&self,764env: &mut crate::func_environ::FuncEnvironment<'a>,765builder: &mut FunctionBuilder,766vmruntime_limits_ptr: ir::Value,767load_stack_limit: bool,768) {769let stack_limits_ptr = self.get_stack_limits_ptr(env, builder);770771let memflags = ir::MemFlags::trusted();772let pointer_size = u8::try_from(env.pointer_type().bytes()).unwrap();773774let mut copy = |runtime_limits_offset, stack_limits_offset| {775let from_vm_runtime_limits = builder.ins().load(776env.pointer_type(),777memflags,778vmruntime_limits_ptr,779runtime_limits_offset,780);781builder.ins().store(782memflags,783from_vm_runtime_limits,784stack_limits_ptr,785stack_limits_offset,786);787};788789let last_wasm_entry_fp_offset = env.offsets.ptr.vmstack_limits_last_wasm_entry_fp();790copy(791pointer_size.vmstore_context_last_wasm_entry_fp(),792last_wasm_entry_fp_offset,793);794795if load_stack_limit {796let stack_limit_offset = env.offsets.ptr.vmstack_limits_stack_limit();797copy(798pointer_size.vmstore_context_stack_limit(),799stack_limit_offset,800);801}802}803}804805impl VMContinuationStack {806/// The parameter is NOT the "top of stack" address of the stack itself. In line807/// with how the (runtime) `FiberStack` type works, this is a pointer to808/// the TOS address.809pub fn new(tos_ptr: ir::Value) -> Self {810Self { tos_ptr }811}812813fn load_top_of_stack<'a>(814&self,815env: &mut crate::func_environ::FuncEnvironment<'a>,816builder: &mut FunctionBuilder,817) -> ir::Value {818let mem_flags = ir::MemFlags::trusted();819builder820.ins()821.load(env.pointer_type(), mem_flags, self.tos_ptr, 0)822}823824/// Returns address of the control context stored in the stack memory,825/// as used by stack_switch instructions.826pub fn load_control_context<'a>(827&self,828env: &mut crate::func_environ::FuncEnvironment<'a>,829builder: &mut FunctionBuilder,830) -> ir::Value {831let tos = self.load_top_of_stack(env, builder);832// Control context begins 24 bytes below top of stack (see unix.rs)833builder.ins().iadd_imm(tos, -0x18)834}835}836}837838use helpers::VMStackChain;839use stack_switching_helpers as helpers;840841/// Stores the given arguments in the appropriate `VMPayloads` object in the842/// continuation. If the continuation was never invoked, use the `args` object.843/// Otherwise, use the `values` object.844pub(crate) fn vmcontref_store_payloads<'a>(845env: &mut crate::func_environ::FuncEnvironment<'a>,846builder: &mut FunctionBuilder,847values: &[ir::Value],848contref: ir::Value,849) {850let count =851i32::try_from(values.len()).expect("Number of stack switching payloads should fit in i32");852if values.len() > 0 {853let use_args_block = builder.create_block();854let use_payloads_block = builder.create_block();855let store_data_block = builder.create_block();856builder.append_block_param(store_data_block, env.pointer_type());857858let co = helpers::VMContRef::new(contref);859let csi = co.common_stack_information(env, builder);860let was_invoked = csi.was_invoked(env, builder);861builder862.ins()863.brif(was_invoked, use_payloads_block, &[], use_args_block, &[]);864865{866builder.switch_to_block(use_args_block);867builder.seal_block(use_args_block);868869let args = co.args(env, builder);870let ptr = args.occupy_next_slots(env, builder, count);871872builder873.ins()874.jump(store_data_block, &[BlockArg::Value(ptr)]);875}876877{878builder.switch_to_block(use_payloads_block);879builder.seal_block(use_payloads_block);880881let payloads = co.values(env, builder);882883// This also checks that the buffer is large enough to hold884// `values.len()` more elements.885let ptr = payloads.occupy_next_slots(env, builder, count);886builder887.ins()888.jump(store_data_block, &[BlockArg::Value(ptr)]);889}890891{892builder.switch_to_block(store_data_block);893builder.seal_block(store_data_block);894895let ptr = builder.block_params(store_data_block)[0];896897// Store the values.898let memflags = ir::MemFlags::trusted();899let mut offset = 0;900for value in values {901builder.ins().store(memflags, *value, ptr, offset);902offset += i32::from(env.offsets.ptr.maximum_value_size());903}904}905}906}907908pub(crate) fn tag_address<'a>(909env: &mut crate::func_environ::FuncEnvironment<'a>,910builder: &mut FunctionBuilder,911index: u32,912) -> ir::Value {913let vmctx = env.vmctx_val(&mut builder.cursor());914let tag_index = wasmtime_environ::TagIndex::from_u32(index);915let pointer_type = env.pointer_type();916if let Some(def_index) = env.module.defined_tag_index(tag_index) {917let offset = i32::try_from(env.offsets.vmctx_vmtag_definition(def_index)).unwrap();918builder.ins().iadd_imm(vmctx, i64::from(offset))919} else {920let offset = i32::try_from(env.offsets.vmctx_vmtag_import_from(tag_index)).unwrap();921builder.ins().load(922pointer_type,923ir::MemFlags::trusted().with_readonly(),924vmctx,925ir::immediates::Offset32::new(offset),926)927}928}929930/// Returns the stack chain saved in the given `VMContext`. Note that the931/// head of the list is the actively running stack (initial stack or932/// continuation).933pub fn vmctx_load_stack_chain<'a>(934env: &mut crate::func_environ::FuncEnvironment<'a>,935builder: &mut FunctionBuilder,936vmctx: ir::Value,937) -> VMStackChain {938let stack_chain_offset = env.offsets.ptr.vmstore_context_stack_chain().into();939940// First we need to get the `VMStoreContext`.941let vm_store_context_offset = env.offsets.ptr.vmctx_store_context();942let vm_store_context = builder.ins().load(943env.pointer_type(),944MemFlags::trusted(),945vmctx,946vm_store_context_offset,947);948949VMStackChain::load(950env,951builder,952vm_store_context,953stack_chain_offset,954env.pointer_type(),955)956}957958/// Stores the given stack chain saved in the `VMContext`, overwriting the959/// exsiting one.960pub fn vmctx_store_stack_chain<'a>(961env: &mut crate::func_environ::FuncEnvironment<'a>,962builder: &mut FunctionBuilder,963vmctx: ir::Value,964stack_chain: &VMStackChain,965) {966let stack_chain_offset = env.offsets.ptr.vmstore_context_stack_chain().into();967968// First we need to get the `VMStoreContext`.969let vm_store_context_offset = env.offsets.ptr.vmctx_store_context();970let vm_store_context = builder.ins().load(971env.pointer_type(),972MemFlags::trusted(),973vmctx,974vm_store_context_offset,975);976977stack_chain.store(env, builder, vm_store_context, stack_chain_offset)978}979980/// Similar to `vmctx_store_stack_chain`, but instead of storing an arbitrary981/// `VMStackChain`, stores VMStackChain::Continuation(contref)`.982pub fn vmctx_set_active_continuation<'a>(983env: &mut crate::func_environ::FuncEnvironment<'a>,984builder: &mut FunctionBuilder,985vmctx: ir::Value,986contref: ir::Value,987) {988let chain = VMStackChain::from_continuation(env, builder, contref);989vmctx_store_stack_chain(env, builder, vmctx, &chain)990}991992pub fn vmctx_load_vm_runtime_limits_ptr<'a>(993env: &mut crate::func_environ::FuncEnvironment<'a>,994builder: &mut FunctionBuilder,995vmctx: ir::Value,996) -> ir::Value {997let pointer_type = env.pointer_type();998let offset = i32::from(env.offsets.ptr.vmctx_store_context());9991000// The *pointer* to the VMRuntimeLimits does not change within the1001// same function, allowing us to set the `read_only` flag.1002let flags = ir::MemFlags::trusted().with_readonly();10031004builder.ins().load(pointer_type, flags, vmctx, offset)1005}10061007/// This function generates code that searches for a handler for `tag_address`,1008/// which must be a `*mut VMTagDefinition`. The search walks up the chain of1009/// continuations beginning at `start`.1010///1011/// The flag `search_suspend_handlers` determines whether we search for a1012/// suspend or switch handler. Concretely, this influences which part of each1013/// handler list we will search.1014///1015/// We trap if no handler was found.1016///1017/// The returned values are:1018/// 1. The stack (continuation or initial stack, represented as a VMStackChain) in1019/// whose handler list we found the tag (i.e., the stack that performed the1020/// resume instruction that installed handler for the tag).1021/// 2. The continuation whose parent is the stack mentioned in 1.1022/// 3. The index of the handler in the handler list.1023///1024/// In pseudo-code, the generated code's behavior can be expressed as1025/// follows:1026///1027/// chain_link = start1028/// while !chain_link.is_initial_stack() {1029/// contref = chain_link.get_contref()1030/// parent_link = contref.parent1031/// parent_csi = parent_link.get_common_stack_information();1032/// handlers = parent_csi.handlers;1033/// (begin_range, end_range) = if search_suspend_handlers {1034/// (0, parent_csi.first_switch_handler_index)1035/// } else {1036/// (parent_csi.first_switch_handler_index, handlers.length)1037/// };1038/// for index in begin_range..end_range {1039/// if handlers[index] == tag_address {1040/// goto on_match(contref, index)1041/// }1042/// }1043/// chain_link = parent_link1044/// }1045/// trap(unhandled_tag)1046///1047/// on_match(conref : VMContRef, handler_index : u32)1048/// ... execution continues here here ...1049///1050fn search_handler<'a>(1051env: &mut crate::func_environ::FuncEnvironment<'a>,1052builder: &mut FunctionBuilder,1053start: &helpers::VMStackChain,1054tag_address: ir::Value,1055search_suspend_handlers: bool,1056) -> (VMStackChain, ir::Value, ir::Value) {1057let handle_link = builder.create_block();1058let begin_search_handler_list = builder.create_block();1059let try_index = builder.create_block();1060let compare_tags = builder.create_block();1061let on_match = builder.create_block();1062let on_no_match = builder.create_block();1063let block_args = start.to_raw_parts().map(|v| BlockArg::Value(v));10641065// Terminate previous block:1066builder.ins().jump(handle_link, &block_args);10671068// Block handle_link1069let chain_link = {1070builder.append_block_param(handle_link, env.pointer_type());1071builder.append_block_param(handle_link, env.pointer_type());1072builder.switch_to_block(handle_link);10731074let raw_parts = builder.block_params(handle_link);1075let chain_link = helpers::VMStackChain::from_raw_parts([raw_parts[0], raw_parts[1]]);1076let is_initial_stack = chain_link.is_initial_stack(env, builder);1077builder.ins().brif(1078is_initial_stack,1079on_no_match,1080&[],1081begin_search_handler_list,1082&[],1083);1084chain_link1085};10861087// Block begin_search_handler_list1088let (contref, parent_link, handler_list_data_ptr, end_range) = {1089builder.switch_to_block(begin_search_handler_list);1090let contref = chain_link.unchecked_get_continuation();1091let contref = helpers::VMContRef::new(contref);10921093let parent_link = contref.get_parent_stack_chain(env, builder);1094let parent_csi = parent_link.get_common_stack_information(env, builder);10951096let handlers = parent_csi.get_handler_list(env, builder);1097let handler_list_data_ptr = handlers.get_data(env, builder);10981099let first_switch_handler_index = parent_csi.get_first_switch_handler_index(env, builder);11001101// Note that these indices are inclusive-exclusive, i.e. [begin_range, end_range).1102let (begin_range, end_range) = if search_suspend_handlers {1103let zero = builder.ins().iconst(I32, 0);1104(zero, first_switch_handler_index)1105} else {1106let length = handlers.get_length(env, builder);1107(first_switch_handler_index, length)1108};11091110builder1111.ins()1112.jump(try_index, &[BlockArg::Value(begin_range)]);11131114(contref, parent_link, handler_list_data_ptr, end_range)1115};11161117// Block try_index1118let index = {1119builder.append_block_param(try_index, I32);1120builder.switch_to_block(try_index);1121let index = builder.block_params(try_index)[0];11221123let in_bounds = builder1124.ins()1125.icmp(IntCC::UnsignedLessThan, index, end_range);1126let block_args = parent_link.to_raw_parts().map(|v| BlockArg::Value(v));1127builder1128.ins()1129.brif(in_bounds, compare_tags, &[], handle_link, &block_args);1130index1131};11321133// Block compare_tags1134{1135builder.switch_to_block(compare_tags);11361137let base = handler_list_data_ptr;1138let entry_size = env.pointer_type().bytes();1139let offset = builder.ins().imul_imm(index, i64::from(entry_size));1140let offset = builder.ins().uextend(I64, offset);1141let entry_address = builder.ins().iadd(base, offset);11421143let memflags = ir::MemFlags::trusted();11441145let handled_tag = builder1146.ins()1147.load(env.pointer_type(), memflags, entry_address, 0);11481149let tags_match = builder.ins().icmp(IntCC::Equal, handled_tag, tag_address);1150let incremented_index = builder.ins().iadd_imm(index, 1);1151builder.ins().brif(1152tags_match,1153on_match,1154&[],1155try_index,1156&[BlockArg::Value(incremented_index)],1157);1158}11591160// Block on_no_match1161{1162builder.switch_to_block(on_no_match);1163builder.set_cold_block(on_no_match);1164builder.ins().trap(crate::TRAP_UNHANDLED_TAG);1165}11661167builder.seal_block(handle_link);1168builder.seal_block(begin_search_handler_list);1169builder.seal_block(try_index);1170builder.seal_block(compare_tags);1171builder.seal_block(on_match);1172builder.seal_block(on_no_match);11731174// final block: on_match1175builder.switch_to_block(on_match);11761177(parent_link, contref.address, index)1178}11791180pub(crate) fn translate_cont_bind<'a>(1181env: &mut crate::func_environ::FuncEnvironment<'a>,1182builder: &mut FunctionBuilder,1183contobj: ir::Value,1184args: &[ir::Value],1185) -> ir::Value {1186let (witness, contref) = fatpointer::deconstruct(env, &mut builder.cursor(), contobj);11871188// The typing rules for cont.bind allow a null reference to be passed to it.1189builder.ins().trapz(contref, crate::TRAP_NULL_REFERENCE);11901191let mut vmcontref = helpers::VMContRef::new(contref);1192let revision = vmcontref.get_revision(env, builder);1193let evidence = builder.ins().icmp(IntCC::Equal, witness, revision);1194builder1195.ins()1196.trapz(evidence, crate::TRAP_CONTINUATION_ALREADY_CONSUMED);11971198vmcontref_store_payloads(env, builder, args, contref);11991200let revision = vmcontref.incr_revision(env, builder, revision);1201let contobj = fatpointer::construct(env, &mut builder.cursor(), revision, contref);1202contobj1203}12041205pub(crate) fn translate_cont_new<'a>(1206env: &mut crate::func_environ::FuncEnvironment<'a>,1207builder: &mut FunctionBuilder,1208func: ir::Value,1209arg_types: &[WasmValType],1210return_types: &[WasmValType],1211) -> WasmResult<ir::Value> {1212// The typing rules for cont.new allow a null reference to be passed to it.1213builder.ins().trapz(func, crate::TRAP_NULL_REFERENCE);12141215let nargs = builder1216.ins()1217.iconst(I32, i64::try_from(arg_types.len()).unwrap());1218let nreturns = builder1219.ins()1220.iconst(I32, i64::try_from(return_types.len()).unwrap());12211222let cont_new_func = super::builtins::cont_new(env, &mut builder.func)?;1223let vmctx = env.vmctx_val(&mut builder.cursor());1224let call_inst = builder1225.ins()1226.call(cont_new_func, &[vmctx, func, nargs, nreturns]);1227let contref = *builder.func.dfg.inst_results(call_inst).first().unwrap();12281229let tag = helpers::VMContRef::new(contref).get_revision(env, builder);1230let contobj = fatpointer::construct(env, &mut builder.cursor(), tag, contref);1231Ok(contobj)1232}12331234pub(crate) fn translate_resume<'a>(1235env: &mut crate::func_environ::FuncEnvironment<'a>,1236builder: &mut FunctionBuilder,1237type_index: u32,1238resume_contobj: ir::Value,1239resume_args: &[ir::Value],1240resumetable: &[(u32, Option<ir::Block>)],1241) -> WasmResult<Vec<ir::Value>> {1242// The resume instruction is the most involved instruction to1243// compile as it is responsible for both continuation application1244// and control tag dispatch.1245//1246// Here we translate a resume instruction into several basic1247// blocks as follows:1248//1249// previous block1250// |1251// |1252// resume_block1253// / \1254// / \1255// | |1256// return_block |1257// suspend block1258// |1259// dispatch block1260//1261// * resume_block handles continuation arguments and performs1262// actual stack switch. On ordinary return from resume, it jumps1263// to the `return_block`, whereas on suspension it jumps to the1264// `suspend_block`.1265// * suspend_block is used on suspension, jumps onward to1266// `dispatch_block`.1267// * dispatch_block uses a jump table to dispatch to actual1268// user-defined handler blocks, based on the handler index1269// provided on suspension. Note that we do not jump to the1270// handler blocks directly. Instead, each handler block has a1271// corresponding premable block, which we jump to in order to1272// reach a particular handler block. The preamble block prepares1273// the arguments and continuation object to be passed to the1274// actual handler block.1275//1276let resume_block = builder.create_block();1277let return_block = builder.create_block();1278let suspend_block = builder.create_block();1279let dispatch_block = builder.create_block();12801281let vmctx = env.vmctx_val(&mut builder.cursor());12821283// Split the resumetable into suspend handlers (each represented by the tag1284// index and handler block) and the switch handlers (represented just by the1285// tag index). Note that we currently don't remove duplicate tags.1286let (suspend_handlers, switch_tags): (Vec<(u32, Block)>, Vec<u32>) = resumetable1287.iter()1288.partition_map(|(tag_index, block_opt)| match block_opt {1289Some(block) => Either::Left((*tag_index, *block)),1290None => Either::Right(*tag_index),1291});12921293// Technically, there is no need to have a dedicated resume block, we could1294// just put all of its contents into the current block.1295builder.ins().jump(resume_block, &[]);12961297// Resume block: actually resume the continuation chain ending at `resume_contref`.1298let (resume_result, vm_runtime_limits_ptr, original_stack_chain, new_stack_chain) = {1299builder.switch_to_block(resume_block);1300builder.seal_block(resume_block);13011302let (witness, resume_contref) =1303fatpointer::deconstruct(env, &mut builder.cursor(), resume_contobj);13041305// The typing rules for resume allow a null reference to be passed to it.1306builder1307.ins()1308.trapz(resume_contref, crate::TRAP_NULL_REFERENCE);13091310let mut vmcontref = helpers::VMContRef::new(resume_contref);13111312let revision = vmcontref.get_revision(env, builder);1313let evidence = builder.ins().icmp(IntCC::Equal, revision, witness);1314builder1315.ins()1316.trapz(evidence, crate::TRAP_CONTINUATION_ALREADY_CONSUMED);1317let _next_revision = vmcontref.incr_revision(env, builder, revision);13181319if resume_args.len() > 0 {1320// We store the arguments in the `VMContRef` to be resumed.1321vmcontref_store_payloads(env, builder, resume_args, resume_contref);1322}13231324// Splice together stack chains:1325// Connect the end of the chain starting at `resume_contref` to the currently active chain.1326let mut last_ancestor = helpers::VMContRef::new(vmcontref.get_last_ancestor(env, builder));13271328// Make the currently running continuation (if any) the parent of the one we are about to resume.1329let original_stack_chain = vmctx_load_stack_chain(env, builder, vmctx);1330last_ancestor.set_parent_stack_chain(env, builder, &original_stack_chain);13311332// Just for consistency: `vmcontref` is about to get state Running, so let's zero out its last_ancestor field.1333let zero = builder.ins().iconst(env.pointer_type(), 0);1334vmcontref.set_last_ancestor(env, builder, zero);13351336// We mark `resume_contref` as the currently running one1337vmctx_set_active_continuation(env, builder, vmctx, resume_contref);13381339// Note that the resume_contref libcall a few lines further below1340// manipulates the stack limits as follows:1341// 1. Copy stack_limit, last_wasm_entry_sp and last_wasm_exit* values from1342// VMRuntimeLimits into the currently active continuation (i.e., the1343// one that will become the parent of the to-be-resumed one)1344//1345// 2. Copy `stack_limit` and `last_wasm_entry_sp` in the1346// `VMStackLimits` of `resume_contref` into the `VMRuntimeLimits`.1347//1348// See the comment on `wasmtime_environ::VMStackChain` for a1349// description of the invariants that we maintain for the various stack1350// limits.13511352// `resume_contref` is now active, and its parent is suspended.1353let resume_contref = helpers::VMContRef::new(resume_contref);1354let resume_csi = resume_contref.common_stack_information(env, builder);1355let parent_csi = original_stack_chain.get_common_stack_information(env, builder);1356resume_csi.set_state_running(env, builder);1357parent_csi.set_state_parent(env, builder);13581359// We update the `VMStackLimits` of the parent of the continuation to be resumed1360// as well as the `VMRuntimeLimits`.1361// See the comment on `wasmtime_environ::VMStackChain` for a description1362// of the invariants that we maintain for the various stack limits.1363let vm_runtime_limits_ptr = vmctx_load_vm_runtime_limits_ptr(env, builder, vmctx);1364parent_csi.load_limits_from_vmcontext(env, builder, vm_runtime_limits_ptr, true);1365resume_csi.write_limits_to_vmcontext(env, builder, vm_runtime_limits_ptr);13661367// Install handlers in (soon to be) parent's VMHandlerList:1368// Let the i-th handler clause be (on $tag $block).1369// Then the i-th entry of the VMHandlerList will be the address of $tag.1370let handler_list = parent_csi.get_handler_list(env, builder);13711372if resumetable.len() > 0 {1373// Total number of handlers (suspend and switch).1374let handler_count = u32::try_from(resumetable.len()).unwrap();1375// Populate the Array's data ptr with a pointer to a sufficiently1376// large area on this stack.1377env.stack_switching_handler_list_buffer =1378Some(handler_list.allocate_or_reuse_stack_slot(1379env,1380builder,1381handler_count,1382env.stack_switching_handler_list_buffer,1383));13841385let suspend_handler_count = suspend_handlers.len();13861387// All handlers, represented by the indices of the tags they handle.1388// All the suspend handlers come first, followed by all the switch handlers.1389let all_handlers = suspend_handlers1390.iter()1391.map(|(tag_index, _block)| *tag_index)1392.chain(switch_tags);13931394// Translate all tag indices to tag addresses (i.e., the corresponding *mut VMTagDefinition).1395let all_tag_addresses: Vec<ir::Value> = all_handlers1396.map(|tag_index| tag_address(env, builder, tag_index))1397.collect();13981399// Store all tag addresess in the handler list.1400handler_list.store_data_entries(env, builder, &all_tag_addresses);14011402// To enable distinguishing switch and suspend handlers when searching the handler list:1403// Store at which index the switch handlers start.1404let first_switch_handler_index = builder1405.ins()1406.iconst(I32, i64::try_from(suspend_handler_count).unwrap());1407parent_csi.set_first_switch_handler_index(env, builder, first_switch_handler_index);1408}14091410let resume_payload = ControlEffect::encode_resume(builder).to_u64();14111412// Note that the control context we use for switching is not the one in1413// (the stack of) resume_contref, but in (the stack of) last_ancestor!1414let fiber_stack = last_ancestor.get_fiber_stack(env, builder);1415let control_context_ptr = fiber_stack.load_control_context(env, builder);14161417let result =1418builder1419.ins()1420.stack_switch(control_context_ptr, control_context_ptr, resume_payload);14211422// At this point we know nothing about the continuation that just1423// suspended or returned. In particular, it does not have to be what we1424// called `resume_contref` earlier on. We must reload the information1425// about the now active continuation from the VMContext.1426let new_stack_chain = vmctx_load_stack_chain(env, builder, vmctx);14271428// Now the parent contref (or initial stack) is active again1429vmctx_store_stack_chain(env, builder, vmctx, &original_stack_chain);1430parent_csi.set_state_running(env, builder);14311432// Just for consistency: Clear the handler list.1433handler_list.clear(env, builder, true);1434parent_csi.set_first_switch_handler_index(env, builder, zero);14351436// Extract the result and signal bit.1437let result = ControlEffect::from_u64(result);1438let signal = result.signal(builder);14391440// Jump to the return block if the result signal is 0, otherwise jump to1441// the suspend block.1442builder1443.ins()1444.brif(signal, suspend_block, &[], return_block, &[]);14451446(1447result,1448vm_runtime_limits_ptr,1449original_stack_chain,1450new_stack_chain,1451)1452};14531454// The suspend block: Only used when we suspended, not for returns.1455// Here we extract the index of the handler to use.1456let (handler_index, suspended_contref, suspended_contobj) = {1457builder.switch_to_block(suspend_block);1458builder.seal_block(suspend_block);14591460let suspended_continuation = new_stack_chain.unchecked_get_continuation();1461let mut suspended_continuation = helpers::VMContRef::new(suspended_continuation);1462let suspended_csi = suspended_continuation.common_stack_information(env, builder);14631464// Note that at the suspend site, we already1465// 1. Set the state of suspended_continuation to Suspended1466// 2. Set suspended_continuation.last_ancestor1467// 3. Broke the continuation chain at suspended_continuation.last_ancestor14681469// We store parts of the VMRuntimeLimits into the continuation that just suspended.1470suspended_csi.load_limits_from_vmcontext(env, builder, vm_runtime_limits_ptr, false);14711472// Afterwards (!), restore parts of the VMRuntimeLimits from the1473// parent of the suspended continuation (which is now active).1474let parent_csi = original_stack_chain.get_common_stack_information(env, builder);1475parent_csi.write_limits_to_vmcontext(env, builder, vm_runtime_limits_ptr);14761477// Extract the handler index1478let handler_index = resume_result.handler_index(builder);14791480let revision = suspended_continuation.get_revision(env, builder);1481let suspended_contobj = fatpointer::construct(1482env,1483&mut builder.cursor(),1484revision,1485suspended_continuation.address,1486);14871488// We need to terminate this block before being allowed to switch to1489// another one.1490builder.ins().jump(dispatch_block, &[]);14911492(handler_index, suspended_continuation, suspended_contobj)1493};14941495// For technical reasons, the jump table needs to have a default1496// block. In our case, it should be unreachable, since the handler1497// index we dispatch on should correspond to a an actual handler1498// block in the jump table.1499let jt_default_block = builder.create_block();1500{1501builder.switch_to_block(jt_default_block);1502builder.set_cold_block(jt_default_block);15031504builder.ins().trap(crate::TRAP_UNREACHABLE);1505}15061507// We create a preamble block for each of the actual handler blocks: It1508// reads the necessary arguments and passes them to the actual handler1509// block, together with the continuation object.1510let target_preamble_blocks = {1511let mut preamble_blocks = vec![];15121513for &(handle_tag, target_block) in &suspend_handlers {1514let preamble_block = builder.create_block();1515preamble_blocks.push(preamble_block);1516builder.switch_to_block(preamble_block);15171518let param_types = env.tag_params(TagIndex::from_u32(handle_tag));1519let param_types: Vec<ir::Type> = param_types1520.iter()1521.map(|wty| crate::value_type(env.isa(), *wty))1522.collect();15231524let values = suspended_contref.values(env, builder);1525let mut suspend_args: Vec<BlockArg> = values1526.load_data_entries(env, builder, ¶m_types)1527.into_iter()1528.map(|v| BlockArg::Value(v))1529.collect();15301531// At the suspend site, we store the suspend args in the the1532// `values` buffer of the VMContRef that was active at the time that1533// the suspend instruction was performed.1534suspend_args.push(BlockArg::Value(suspended_contobj));15351536// We clear the suspend args. This is mostly for consistency. Note1537// that we don't zero out the data buffer, we still need it for the15381539values.clear(env, builder, false);15401541builder.ins().jump(target_block, &suspend_args);1542}15431544preamble_blocks1545};15461547// Dispatch block. All it does is jump to the right premable block based on1548// the handler index.1549{1550builder.switch_to_block(dispatch_block);1551builder.seal_block(dispatch_block);15521553let default_bc = builder.func.dfg.block_call(jt_default_block, &[]);15541555let adapter_bcs: Vec<BlockCall> = target_preamble_blocks1556.iter()1557.map(|b| builder.func.dfg.block_call(*b, &[]))1558.collect();15591560let jt_data = JumpTableData::new(default_bc, &adapter_bcs);1561let jt = builder.create_jump_table(jt_data);15621563builder.ins().br_table(handler_index, jt);15641565for preamble_block in target_preamble_blocks {1566builder.seal_block(preamble_block);1567}1568builder.seal_block(jt_default_block);1569}15701571// Return block: Jumped to by resume block if continuation1572// returned normally.1573{1574builder.switch_to_block(return_block);1575builder.seal_block(return_block);15761577// If we got a return signal, a continuation must have been running.1578let returned_contref = new_stack_chain.unchecked_get_continuation();1579let returned_contref = helpers::VMContRef::new(returned_contref);15801581// Restore parts of the VMRuntimeLimits from the parent of the1582// returned continuation (which is now active).1583let parent_csi = original_stack_chain.get_common_stack_information(env, builder);1584parent_csi.write_limits_to_vmcontext(env, builder, vm_runtime_limits_ptr);15851586let returned_csi = returned_contref.common_stack_information(env, builder);1587returned_csi.set_state_returned(env, builder);15881589// Load the values returned by the continuation.1590let return_types: Vec<_> = env1591.continuation_returns(TypeIndex::from_u32(type_index))1592.iter()1593.map(|ty| crate::value_type(env.isa(), *ty))1594.collect();1595let payloads = returned_contref.args(env, builder);1596let return_values = payloads.load_data_entries(env, builder, &return_types);1597payloads.clear(env, builder, true);15981599Ok(return_values)1600}1601}16021603pub(crate) fn translate_suspend<'a>(1604env: &mut crate::func_environ::FuncEnvironment<'a>,1605builder: &mut FunctionBuilder,1606tag_index: u32,1607suspend_args: &[ir::Value],1608tag_return_types: &[ir::Type],1609) -> Vec<ir::Value> {1610let tag_addr = tag_address(env, builder, tag_index);16111612let vmctx = env.vmctx_val(&mut builder.cursor());1613let active_stack_chain = vmctx_load_stack_chain(env, builder, vmctx);16141615let (_, end_of_chain_contref, handler_index) =1616search_handler(env, builder, &active_stack_chain, tag_addr, true);16171618// If we get here, the search_handler logic succeeded (i.e., did not trap).1619// Thus, there is at least one parent, so we are not on the initial stack.1620// Can therefore extract continuation directly.1621let active_contref = active_stack_chain.unchecked_get_continuation();1622let active_contref = helpers::VMContRef::new(active_contref);1623let mut end_of_chain_contref = helpers::VMContRef::new(end_of_chain_contref);16241625active_contref.set_last_ancestor(env, builder, end_of_chain_contref.address);16261627// In the active_contref's `values` buffer, stack-allocate enough room so that we can1628// later store the following:1629// 1. The suspend arguments1630// 2. Afterwards, the tag return values1631let values = active_contref.values(env, builder);1632let required_capacity =1633u32::try_from(std::cmp::max(suspend_args.len(), tag_return_types.len()))1634.expect("Number of stack switching payloads should fit in u32");16351636if required_capacity > 0 {1637env.stack_switching_values_buffer = Some(values.allocate_or_reuse_stack_slot(1638env,1639builder,1640required_capacity,1641env.stack_switching_values_buffer,1642));1643}16441645if suspend_args.len() > 0 {1646values.store_data_entries(env, builder, suspend_args);1647}16481649// Set current continuation to suspended and break up handler chain.1650let active_contref_csi = active_contref.common_stack_information(env, builder);1651active_contref_csi.set_state_suspended(env, builder);1652let absent_chain_link = VMStackChain::absent(env, builder);1653end_of_chain_contref.set_parent_stack_chain(env, builder, &absent_chain_link);16541655let suspend_payload = ControlEffect::encode_suspend(builder, handler_index).to_u64();16561657// Note that the control context we use for switching is the one1658// at the end of the chain, not the one in active_contref!1659// This also means that stack_switch saves the information about1660// the current stack in the control context located in the stack1661// of end_of_chain_contref.1662let fiber_stack = end_of_chain_contref.get_fiber_stack(env, builder);1663let control_context_ptr = fiber_stack.load_control_context(env, builder);16641665builder1666.ins()1667.stack_switch(control_context_ptr, control_context_ptr, suspend_payload);16681669// The return values of the suspend instruction are the tag return values, saved in the `args` buffer.1670let values = active_contref.values(env, builder);1671let return_values = values.load_data_entries(env, builder, tag_return_types);1672// We effectively consume the values and discard the stack allocated buffer.1673values.clear(env, builder, true);16741675return_values1676}16771678pub(crate) fn translate_switch<'a>(1679env: &mut crate::func_environ::FuncEnvironment<'a>,1680builder: &mut FunctionBuilder,1681tag_index: u32,1682switchee_contobj: ir::Value,1683switch_args: &[ir::Value],1684return_types: &[ir::Type],1685) -> WasmResult<Vec<ir::Value>> {1686let vmctx = env.vmctx_val(&mut builder.cursor());16871688// Check and increment revision on switchee continuation object (i.e., the1689// one being switched to). Logically, the switchee continuation extends from1690// `switchee_contref` to `switchee_contref.last_ancestor` (i.e., the end of1691// the parent chain starting at `switchee_contref`).1692let switchee_contref = {1693let (witness, target_contref) =1694fatpointer::deconstruct(env, &mut builder.cursor(), switchee_contobj);16951696// The typing rules for switch allow a null reference to be passed to it.1697builder1698.ins()1699.trapz(target_contref, crate::TRAP_NULL_REFERENCE);17001701let mut target_contref = helpers::VMContRef::new(target_contref);17021703let revision = target_contref.get_revision(env, builder);1704let evidence = builder.ins().icmp(IntCC::Equal, revision, witness);1705builder1706.ins()1707.trapz(evidence, crate::TRAP_CONTINUATION_ALREADY_CONSUMED);1708let _next_revision = target_contref.incr_revision(env, builder, revision);1709target_contref1710};17111712// We create the "switcher continuation" (i.e., the one executing switch)1713// from the current execution context: Logically, it extends from the1714// continuation reference executing `switch` (subsequently called1715// `switcher_contref`) to the immediate child (called1716// `switcher_contref_last_ancestor`) of the stack with the corresponding1717// handler (saved in `handler_stack_chain`).1718let (1719switcher_contref,1720switcher_contobj,1721switcher_contref_last_ancestor,1722handler_stack_chain,1723vm_runtime_limits_ptr,1724) = {1725let tag_addr = tag_address(env, builder, tag_index);1726let active_stack_chain = vmctx_load_stack_chain(env, builder, vmctx);1727let (handler_stack_chain, last_ancestor, _handler_index) =1728search_handler(env, builder, &active_stack_chain, tag_addr, false);1729let mut last_ancestor = helpers::VMContRef::new(last_ancestor);17301731// If we get here, the search_handler logic succeeded (i.e., did not trap).1732// Thus, there is at least one parent, so we are not on the initial stack.1733// Can therefore extract continuation directly.1734let switcher_contref = active_stack_chain.unchecked_get_continuation();1735let mut switcher_contref = helpers::VMContRef::new(switcher_contref);17361737switcher_contref.set_last_ancestor(env, builder, last_ancestor.address);17381739// In the switcher_contref's `values` buffer, stack-allocate enough room so that we can1740// later store `tag_return_types.len()` when resuming the continuation.1741let values = switcher_contref.values(env, builder);1742let required_capacity = u32::try_from(return_types.len()).unwrap();1743if required_capacity > 0 {1744env.stack_switching_values_buffer = Some(values.allocate_or_reuse_stack_slot(1745env,1746builder,1747required_capacity,1748env.stack_switching_values_buffer,1749));1750}17511752let switcher_contref_csi = switcher_contref.common_stack_information(env, builder);1753switcher_contref_csi.set_state_suspended(env, builder);1754// We break off `switcher_contref` from the chain of active1755// continuations, by separating the link between `last_ancestor` and its1756// parent stack.1757let absent = VMStackChain::absent(env, builder);1758last_ancestor.set_parent_stack_chain(env, builder, &absent);17591760// Load current runtime limits from `VMContext` and store in the1761// switcher continuation.1762let vm_runtime_limits_ptr = vmctx_load_vm_runtime_limits_ptr(env, builder, vmctx);1763switcher_contref_csi.load_limits_from_vmcontext(env, builder, vm_runtime_limits_ptr, false);17641765let revision = switcher_contref.get_revision(env, builder);1766let new_contobj = fatpointer::construct(1767env,1768&mut builder.cursor(),1769revision,1770switcher_contref.address,1771);17721773(1774switcher_contref,1775new_contobj,1776last_ancestor,1777handler_stack_chain,1778vm_runtime_limits_ptr,1779)1780};17811782// Prepare switchee continuation:1783// - Store "ordinary" switch arguments as well as the contobj just1784// synthesized from the current context (i.e., `switcher_contobj`) in the1785// switchee continuation's payload buffer.1786// - Splice switchee's continuation chain with handler stack to form new1787// overall chain of active continuations.1788let (switchee_contref_csi, switchee_contref_last_ancestor) = {1789let mut combined_payloads = switch_args.to_vec();1790combined_payloads.push(switcher_contobj);1791vmcontref_store_payloads(env, builder, &combined_payloads, switchee_contref.address);17921793let switchee_contref_csi = switchee_contref.common_stack_information(env, builder);1794switchee_contref_csi.set_state_running(env, builder);17951796let switchee_contref_last_ancestor = switchee_contref.get_last_ancestor(env, builder);1797let mut switchee_contref_last_ancestor =1798helpers::VMContRef::new(switchee_contref_last_ancestor);17991800switchee_contref_last_ancestor.set_parent_stack_chain(env, builder, &handler_stack_chain);18011802(switchee_contref_csi, switchee_contref_last_ancestor)1803};18041805// Update VMContext/Store: Update active continuation and `VMRuntimeLimits`.1806{1807vmctx_set_active_continuation(env, builder, vmctx, switchee_contref.address);18081809switchee_contref_csi.write_limits_to_vmcontext(env, builder, vm_runtime_limits_ptr);1810}18111812// Perform actual stack switch1813{1814let switcher_last_ancestor_fs =1815switcher_contref_last_ancestor.get_fiber_stack(env, builder);1816let switcher_last_ancestor_cc =1817switcher_last_ancestor_fs.load_control_context(env, builder);18181819let switchee_last_ancestor_fs =1820switchee_contref_last_ancestor.get_fiber_stack(env, builder);1821let switchee_last_ancestor_cc =1822switchee_last_ancestor_fs.load_control_context(env, builder);18231824// The stack switch involves the following control contexts (e.g., IP,1825// SP, FP, ...):1826// - `switchee_last_ancestor_cc` contains the information to continue1827// execution in the switchee/target continuation.1828// - `switcher_last_ancestor_cc` contains the information about how to1829// continue execution once we suspend/return to the stack with the1830// switch handler.1831//1832// In total, the following needs to happen:1833// 1. Load control context at `switchee_last_ancestor_cc` to perform1834// stack switch.1835// 2. Move control context at `switcher_last_ancestor_cc` over to1836// `switchee_last_ancestor_cc`.1837// 3. Upon actual switch, save current control context at1838// `switcher_last_ancestor_cc`.1839//1840// We implement this as follows:1841// 1. We copy `switchee_last_ancestor_cc` to a temporary area on the1842// stack (`tmp_control_context`).1843// 2. We copy `switcher_last_ancestor_cc` over to1844// `switchee_last_ancestor_cc`.1845// 3. We invoke the stack switch instruction such that it reads from the1846// temporary area, and writes to `switcher_last_ancestor_cc`.1847//1848// Note that the temporary area is only accessed once by the1849// `stack_switch` instruction emitted later in this block, meaning that we1850// don't have to worry about its lifetime.1851//1852// NOTE(frank-emrich) The implementation below results in one stack slot1853// being created per switch instruction, even though multiple switch1854// instructions in the same function could safely re-use the same stack1855// slot. Thus, we could implement logic for sharing the stack slot by1856// adding an appropriate field to `FuncEnvironment`.1857//1858// NOTE(frank-emrich) We could avoid the copying to a temporary area by1859// making `stack_switch` do all of the necessary moving itself. However,1860// that would be a rather ad-hoc change to how the instruction uses the1861// two pointers given to it.18621863let cctx_size = control_context_size(env.isa().triple())?;1864let slot_size = ir::StackSlotData::new(1865ir::StackSlotKind::ExplicitSlot,1866u32::from(cctx_size),1867u8::try_from(env.pointer_type().bytes()).unwrap(),1868);1869let slot = builder.create_sized_stack_slot(slot_size);1870let tmp_control_context = builder.ins().stack_addr(env.pointer_type(), slot, 0);18711872let flags = MemFlags::trusted();1873let mut offset: i32 = 0;1874while offset < i32::from(cctx_size) {1875// switchee_last_ancestor_cc -> tmp control context1876let tmp1 =1877builder1878.ins()1879.load(env.pointer_type(), flags, switchee_last_ancestor_cc, offset);1880builder1881.ins()1882.store(flags, tmp1, tmp_control_context, offset);18831884// switcher_last_ancestor_cc -> switchee_last_ancestor_cc1885let tmp2 =1886builder1887.ins()1888.load(env.pointer_type(), flags, switcher_last_ancestor_cc, offset);1889builder1890.ins()1891.store(flags, tmp2, switchee_last_ancestor_cc, offset);18921893offset += i32::try_from(env.pointer_type().bytes()).unwrap();1894}18951896let switch_payload = ControlEffect::encode_switch(builder).to_u64();18971898let _result = builder.ins().stack_switch(1899switcher_last_ancestor_cc,1900tmp_control_context,1901switch_payload,1902);1903}19041905// After switching back to the original stack: Load return values, they are1906// stored on the switcher continuation.1907let return_values = {1908let payloads = switcher_contref.values(env, builder);1909let return_values = payloads.load_data_entries(env, builder, return_types);1910// We consume the values and discard the buffer (allocated on this stack)1911payloads.clear(env, builder, true);1912return_values1913};19141915Ok(return_values)1916}191719181919