Skip to main content

winch_codegen/codegen/
control.rs

1//! Data structures for control flow emission.
2//!
3//! Winch currently doesn't apply any sort of optimizations to control flow, but
4//! as a future optimization, for starters, we could perform a look ahead to the
5//! next instruction when reaching any of the comparison instructions. If the
6//! next instruction is a control instruction, we could avoid emitting
7//! a [`crate::masm::MacroAssembler::cmp_with_set`] and instead emit
8//! a conditional jump inline when emitting the control flow instruction.
9use super::{
10    CodeGenContext, CodeGenError, Emission, OperandSize, Reg, TypedReg, exceptions::TryTableInfo,
11};
12use crate::{
13    CallingConvention, Result,
14    abi::{ABI, ABIOperand, ABIResults, ABISig, RetArea},
15    bail, ensure, format_err,
16    masm::{IntCmpKind, MacroAssembler, MemMoveDirection, RegImm, SPOffset},
17    reg::writable,
18    stack::Val,
19};
20use cranelift_codegen::MachLabel;
21use wasmtime_environ::{WasmFuncType, WasmValType, collections::TryClone as _};
22
23/// Categorization of the type of the block.
24#[derive(Debug)]
25pub(crate) enum BlockType {
26    /// Doesn't produce or consume any values.
27    Void,
28    /// Produces a single value.
29    Single(WasmValType),
30    /// Consumes multiple values and produces multiple values.
31    Func(WasmFuncType),
32    /// An already resolved ABI signature.
33    ABISig(ABISig),
34}
35
36impl Clone for BlockType {
37    fn clone(&self) -> Self {
38        match self {
39            Self::Void => Self::Void,
40            Self::Single(x) => Self::Single(*x),
41            Self::ABISig(x) => Self::ABISig(x.clone()),
42            Self::Func(f) => Self::Func(f.clone_panic_on_oom()),
43        }
44    }
45}
46
47/// Holds all the information about the signature of the block.
48#[derive(Debug, Clone)]
49pub(crate) struct BlockSig {
50    /// The type of the block.
51    pub ty: BlockType,
52    /// ABI representation of the results of the block.
53    results: Option<ABIResults>,
54    /// ABI representation of the params of the block interpreted as results.
55    params: Option<ABIResults>,
56}
57
58impl BlockSig {
59    /// Create a new [BlockSig].
60    pub fn new(ty: BlockType) -> Self {
61        Self {
62            ty,
63            results: None,
64            params: None,
65        }
66    }
67
68    /// Create a new [BlockSig] from an [ABISig].
69    pub fn from_sig(sig: ABISig) -> Self {
70        Self {
71            ty: BlockType::sig(sig),
72            results: None,
73            params: None,
74        }
75    }
76
77    /// Return the ABI representation of the results of the block.
78    /// This method will lazily initialize the results if not present.
79    pub fn results<M>(&mut self) -> Result<&mut ABIResults>
80    where
81        M: MacroAssembler,
82    {
83        if self.ty.is_sig() {
84            return match &mut self.ty {
85                BlockType::ABISig(sig) => Ok(&mut sig.results),
86                _ => unreachable!(),
87            };
88        }
89
90        if self.results.is_some() {
91            return Ok(self.results.as_mut().unwrap());
92        }
93
94        let results = match &self.ty {
95            BlockType::Void => <M::ABI as ABI>::abi_results(&[], &CallingConvention::Default),
96            BlockType::Single(ty) => {
97                <M::ABI as ABI>::abi_results(&[*ty], &CallingConvention::Default)
98            }
99            BlockType::Func(f) => {
100                <M::ABI as ABI>::abi_results(f.results(), &CallingConvention::Default)
101            }
102            BlockType::ABISig(_) => unreachable!(),
103        };
104
105        self.results = Some(results?);
106        Ok(self.results.as_mut().unwrap())
107    }
108
109    /// Construct an ABI result representation of the params of the block.
110    /// This is needed for loops and for handling cases in which params flow as
111    /// the block's results, i.e. in the presence of an empty then or else.
112    pub fn params<M>(&mut self) -> Result<&mut ABIResults>
113    where
114        M: MacroAssembler,
115    {
116        if self.params.is_some() {
117            return Ok(self.params.as_mut().unwrap());
118        }
119
120        let params_as_results = match &self.ty {
121            BlockType::Void | BlockType::Single(_) => {
122                <M::ABI as ABI>::abi_results(&[], &CallingConvention::Default)
123            }
124            BlockType::Func(f) => {
125                <M::ABI as ABI>::abi_results(f.params(), &CallingConvention::Default)
126            }
127            // Once we have created a block type from a known signature, we
128            // can't modify its meaning. This should only be used for the
129            // function body block, in which case there's no need for treating
130            // params as results.
131            BlockType::ABISig(_) => unreachable!(),
132        };
133
134        self.params = Some(params_as_results?);
135        Ok(self.params.as_mut().unwrap())
136    }
137
138    /// Returns the signature param count.
139    pub fn param_count(&self) -> usize {
140        match &self.ty {
141            BlockType::Void | BlockType::Single(_) => 0,
142            BlockType::Func(f) => f.params().len(),
143            BlockType::ABISig(sig) => sig.params_without_retptr().len(),
144        }
145    }
146
147    /// Returns the signature return count.
148    pub fn return_count(&self) -> usize {
149        match &self.ty {
150            BlockType::Void => 0,
151            BlockType::Single(_) => 1,
152            BlockType::Func(f) => f.results().len(),
153            BlockType::ABISig(sig) => sig.results().len(),
154        }
155    }
156}
157
158impl BlockType {
159    /// Create a [BlockType::Void].
160    pub fn void() -> Self {
161        Self::Void
162    }
163
164    /// Create a [BlockType::Single] from the given [WasmType].
165    pub fn single(ty: WasmValType) -> Self {
166        Self::Single(ty)
167    }
168
169    /// Create a [BlockType::Func] from the given [WasmFuncType].
170    pub fn func(ty: WasmFuncType) -> Self {
171        Self::Func(ty)
172    }
173
174    /// Create a [BlockType::ABISig].
175    pub fn sig(sig: ABISig) -> Self {
176        Self::ABISig(sig)
177    }
178
179    /// Returns true if the type of the block is [BlockType::ABISig].
180    pub fn is_sig(&self) -> bool {
181        match self {
182            Self::ABISig(_) => true,
183            _ => false,
184        }
185    }
186}
187
188/// The expected value and machine stack state when entering and exiting the block.
189#[derive(Debug, Default, Copy, Clone)]
190pub(crate) struct StackState {
191    /// The base stack pointer offset.
192    /// This offset is set when entering the block, after saving any live
193    /// registers and locals.
194    /// It is calculated by subtracting the size, in bytes, of any block params
195    /// to the current stack pointer offset.
196    pub base_offset: SPOffset,
197    /// The target stack pointer offset.
198    /// This offset is calculated by adding the size of the stack results
199    /// to the base stack pointer offset.
200    pub target_offset: SPOffset,
201    /// The base length of the value stack when entering the block.
202    /// Which is the current length of the value stack minus any block parameters.
203    pub base_len: usize,
204    /// The target length of the value stack when exiting the block.
205    /// Calculate by adding the number of results to the base value stack
206    /// length.
207    pub target_len: usize,
208}
209
210/// Holds the all the metadata to support the emission
211/// of control flow instructions.
212#[derive(Debug)]
213pub(crate) enum ControlStackFrame {
214    If {
215        /// The if continuation label.
216        cont: MachLabel,
217        /// The exit label of the block.
218        exit: MachLabel,
219        /// The signature of the block.
220        sig: BlockSig,
221        /// The stack state of the block.
222        stack_state: StackState,
223        /// Local reachability state when entering the block.
224        reachable: bool,
225    },
226    Else {
227        /// The exit label of the block.
228        exit: MachLabel,
229        /// The signature of the block.
230        sig: BlockSig,
231        /// The stack state of the block.
232        stack_state: StackState,
233        /// Local reachability state when entering the block.
234        reachable: bool,
235    },
236    Block {
237        /// The block exit label.
238        exit: MachLabel,
239        /// The signature of the block.
240        sig: BlockSig,
241        /// The stack state of the block.
242        stack_state: StackState,
243        /// Exit state of the block.
244        ///
245        /// This flag is used to determine if a block is a branch
246        /// target. By default, this is false, and it's updated when
247        /// emitting a `br` or `br_if`.
248        is_branch_target: bool,
249        /// Exception-handling information when this block is a `try_table`.
250        try_table_info: Option<TryTableInfo>,
251    },
252    Loop {
253        /// The start of the Loop.
254        head: MachLabel,
255        /// The stack state of the block.
256        stack_state: StackState,
257        /// The signature of the block.
258        sig: BlockSig,
259    },
260}
261
262impl ControlStackFrame {
263    /// Returns [`ControlStackFrame`] for an if.
264    pub fn r#if<M: MacroAssembler>(
265        sig: BlockSig,
266        masm: &mut M,
267        context: &mut CodeGenContext<Emission>,
268    ) -> Result<Self> {
269        let mut control = Self::If {
270            cont: masm.get_label()?,
271            exit: masm.get_label()?,
272            sig,
273            reachable: context.reachable,
274            stack_state: Default::default(),
275        };
276
277        control.emit(masm, context)?;
278        Ok(control)
279    }
280
281    /// Returns [`ControlStackFrame`] for a block.
282    pub fn block<M: MacroAssembler>(
283        sig: BlockSig,
284        masm: &mut M,
285        context: &mut CodeGenContext<Emission>,
286    ) -> Result<Self> {
287        Self::block_impl(sig, None, masm, context)
288    }
289
290    /// Returns a block control frame with exception-handler information.
291    pub fn try_table<M: MacroAssembler>(
292        sig: BlockSig,
293        info: TryTableInfo,
294        masm: &mut M,
295        context: &mut CodeGenContext<Emission>,
296    ) -> Result<Self> {
297        Self::block_impl(sig, Some(info), masm, context)
298    }
299
300    fn block_impl<M: MacroAssembler>(
301        sig: BlockSig,
302        try_table_info: Option<TryTableInfo>,
303        masm: &mut M,
304        context: &mut CodeGenContext<Emission>,
305    ) -> Result<Self> {
306        let mut control = Self::Block {
307            sig,
308            is_branch_target: false,
309            exit: masm.get_label()?,
310            stack_state: Default::default(),
311            try_table_info,
312        };
313
314        control.emit(masm, context)?;
315        Ok(control)
316    }
317
318    /// Returns this block's try-table information, if present.
319    pub fn try_table_info(&self) -> Option<&TryTableInfo> {
320        match self {
321            Self::Block { try_table_info, .. } => try_table_info.as_ref(),
322            _ => None,
323        }
324    }
325
326    /// Takes this block's try-table information, if present.
327    pub fn take_try_table_info(&mut self) -> Option<TryTableInfo> {
328        match self {
329            Self::Block { try_table_info, .. } => try_table_info.take(),
330            _ => None,
331        }
332    }
333
334    /// Returns [`ControlStackFrame`] for a loop.
335    pub fn r#loop<M: MacroAssembler>(
336        sig: BlockSig,
337        masm: &mut M,
338        context: &mut CodeGenContext<Emission>,
339    ) -> Result<Self> {
340        let mut control = Self::Loop {
341            stack_state: Default::default(),
342            sig,
343            head: masm.get_label()?,
344        };
345
346        control.emit(masm, context)?;
347        Ok(control)
348    }
349
350    fn init<M: MacroAssembler>(
351        &mut self,
352        masm: &mut M,
353        context: &mut CodeGenContext<Emission>,
354    ) -> Result<()> {
355        self.calculate_stack_state(context, masm)?;
356        // If the block has stack results, immediately resolve the return area
357        // base.
358        if self.results::<M>()?.on_stack() {
359            let results_base = self.stack_state().target_offset;
360            self.results::<M>()?.set_ret_area(RetArea::sp(results_base));
361        }
362
363        if self.is_if() || self.is_loop() {
364            // Preemptively handle block params as results so that the params
365            // are correctly placed in memory. This is especially
366            // important for control flow joins with empty blocks:
367            //
368            //(module
369            //  (func (export "params") (param i32) (result i32)
370            //       (i32.const 2)
371            //       (if (param i32) (result i32) (local.get 0)
372            //       (then))
373            //     (i32.const 3)
374            //     (i32.add)
375            //   )
376            //)
377            let base_offset = self.stack_state().base_offset;
378            if self.params::<M>()?.on_stack() {
379                let offset = base_offset.as_u32() + self.params::<M>()?.size();
380                self.params::<M>()?
381                    .set_ret_area(RetArea::sp(SPOffset::from_u32(offset)));
382            }
383            Self::top_abi_results_impl(
384                self.params::<M>()?,
385                context,
386                masm,
387                |params: &ABIResults, _, _| Ok(params.ret_area().copied()),
388            )?;
389        }
390        Ok(())
391    }
392
393    /// Calculates the [StackState] of the block.
394    fn calculate_stack_state<M: MacroAssembler>(
395        &mut self,
396        context: &mut CodeGenContext<Emission>,
397        masm: &mut M,
398    ) -> Result<()> {
399        use ControlStackFrame::*;
400        let sig = self.sig();
401        // If the block type contains a full [ABISig], do not take into account
402        // the params, since these are the params of the function that is
403        // currently being compiled and the value stack doesn't currently
404        // contain any values anyway.
405        let param_count = if sig.ty.is_sig() {
406            0
407        } else {
408            sig.param_count()
409        };
410        let return_count = sig.return_count();
411        ensure!(
412            context.stack.len() >= param_count,
413            CodeGenError::missing_values_in_stack()
414        );
415        let results_size = self.results::<M>()?.size();
416
417        // Save any live registers and locals.
418        context.spill(masm)?;
419
420        let base_len = context.stack.len() - param_count;
421        let stack_consumed = context.stack.sizeof(param_count);
422        let current_sp = masm.sp_offset()?;
423        let base_offset = SPOffset::from_u32(current_sp.as_u32() - stack_consumed);
424
425        match self {
426            If { stack_state, .. } | Block { stack_state, .. } | Loop { stack_state, .. } => {
427                stack_state.base_offset = base_offset;
428                stack_state.base_len = base_len;
429                stack_state.target_offset = SPOffset::from_u32(base_offset.as_u32() + results_size);
430                stack_state.target_len = base_len + return_count;
431            }
432            _ => {}
433        }
434        Ok(())
435    }
436
437    /// This function ensures that the state of the -- machine and value --
438    /// stack  is the right one when reaching a control frame branch in which
439    /// reachability is restored or when reaching the end of a function in an
440    /// unreachable state. This function is intended to be called when handling
441    /// an unreachable else or end.
442    //
443    /// This function will truncate the value stack to the base length of
444    /// the control frame and will also set the stack pointer offset to reflect
445    /// the offset expected by the target branch.
446    ///
447    // NB: This method is assumed to be called *before* pushing any block
448    // results to the value stack, so that any excess values are cleaned up.
449    pub fn ensure_stack_state<M: MacroAssembler>(
450        &mut self,
451        masm: &mut M,
452        context: &mut CodeGenContext<Emission>,
453    ) -> Result<()> {
454        let state = self.stack_state();
455        // This assumes that at jump sites, the machine stack pointer will be
456        // adjusted to match the expectations of the target branch (e.g.
457        // `target_offset`); after performing the jump, the MacroAssembler
458        // implementation will soft-reset the stack pointer offset to its
459        // original offset, ensure that other parts of the program have access
460        // to the right offset, this is especially important in conditional
461        // branches.
462        // When restoring reachability we ensure that the MacroAssembler offset
463        // is set to match the expectations of the target branch, similar to how
464        // the machine stack pointer was adjusted at jump sites.
465        masm.reset_stack_pointer(state.target_offset)?;
466        // We use the base length, because this function is assumed to be called
467        // *before* pushing any results to the value stack. This way, any excess
468        // values will be discarded.
469        context.truncate_stack_to(state.base_len)
470    }
471
472    /// Return the type information of the block.
473    pub fn sig(&self) -> &BlockSig {
474        use ControlStackFrame::*;
475        match self {
476            If { sig, .. } | Else { sig, .. } | Loop { sig, .. } | Block { sig, .. } => sig,
477        }
478    }
479
480    fn emit<M: MacroAssembler>(
481        &mut self,
482        masm: &mut M,
483        context: &mut CodeGenContext<Emission>,
484    ) -> Result<()> {
485        use ControlStackFrame::*;
486
487        // Do not perform any emissions if we are in an unreachable state.
488        if !context.reachable {
489            return Ok(());
490        }
491
492        match *self {
493            If { cont, .. } => {
494                // Pop the condition value.
495                // Because in the case of Self::If, Self::init, will top the
496                // branch params, we exclude any result registers from being
497                // used as the branch test.
498                let top = context.without::<Result<TypedReg>, _, _>(
499                    self.params::<M>()?.regs(),
500                    masm,
501                    |cx, masm| cx.pop_to_reg(masm, None),
502                )??;
503                self.init(masm, context)?;
504                masm.branch(
505                    IntCmpKind::Eq,
506                    top.reg,
507                    top.reg.into(),
508                    cont,
509                    OperandSize::S32,
510                )?;
511                context.free_reg(top);
512                Ok(())
513            }
514            Block { .. } => self.init(masm, context),
515            Loop { head, .. } => {
516                self.init(masm, context)?;
517                masm.bind(head)?;
518                Ok(())
519            }
520            _ => Err(format_err!(CodeGenError::if_control_frame_expected())),
521        }
522    }
523
524    /// Handles the else branch if the current control stack frame is
525    /// [`ControlStackFrame::If`].
526    pub fn emit_else<M: MacroAssembler>(
527        &mut self,
528        masm: &mut M,
529        context: &mut CodeGenContext<Emission>,
530    ) -> Result<()> {
531        ensure!(self.is_if(), CodeGenError::if_control_frame_expected());
532        let state = self.stack_state();
533
534        ensure!(
535            state.target_len == context.stack.len(),
536            CodeGenError::control_frame_state_mismatch()
537        );
538        self.pop_abi_results(context, masm, |results, _, _| {
539            Ok(results.ret_area().copied())
540        })?;
541        masm.jmp(*self.exit_label().unwrap())?;
542        self.bind_else(masm, context)?;
543        Ok(())
544    }
545
546    /// Binds the else branch label and converts `self` to
547    /// [`ControlStackFrame::Else`].
548    pub fn bind_else<M: MacroAssembler>(
549        &mut self,
550        masm: &mut M,
551        context: &mut CodeGenContext<Emission>,
552    ) -> Result<()> {
553        use ControlStackFrame::*;
554        match self {
555            If {
556                cont,
557                sig,
558                stack_state,
559                exit,
560                ..
561            } => {
562                // Bind the else branch.
563                masm.bind(*cont)?;
564
565                // Push the abi results to the value stack, so that they are
566                // used as params for the else branch. At the beginning of the
567                // if block, any params are preemptively resolved as results;
568                // when reaching the else all params are already materialized as
569                // stack results. As part of ensuring the right state when
570                // entering the else branch, the following snippet also soft
571                // resets the stack pointer so that it matches the expectations
572                // of the else branch: the stack pointer is expected to be at
573                // the base stack pointer, plus the params stack size in bytes.
574                let params_size = sig.params::<M>()?.size();
575                context.push_abi_results::<M, _>(sig.params::<M>()?, masm, |params, _, _| {
576                    params.ret_area().copied()
577                })?;
578                masm.reset_stack_pointer(SPOffset::from_u32(
579                    stack_state.base_offset.as_u32() + params_size,
580                ))?;
581
582                // Update the stack control frame with an else control frame.
583                *self = ControlStackFrame::Else {
584                    exit: *exit,
585                    stack_state: *stack_state,
586                    reachable: context.reachable,
587                    sig: sig.clone(),
588                };
589            }
590            _ => bail!(CodeGenError::if_control_frame_expected()),
591        }
592        Ok(())
593    }
594
595    /// Handles the end of a control stack frame.
596    pub fn emit_end<M: MacroAssembler>(
597        &mut self,
598        masm: &mut M,
599        context: &mut CodeGenContext<Emission>,
600    ) -> Result<()> {
601        use ControlStackFrame::*;
602        match self {
603            If { stack_state, .. } | Else { stack_state, .. } | Block { stack_state, .. } => {
604                ensure!(
605                    stack_state.target_len == context.stack.len(),
606                    CodeGenError::control_frame_state_mismatch()
607                );
608                // Before binding the exit label, we handle the block results.
609                self.pop_abi_results(context, masm, |results, _, _| {
610                    Ok(results.ret_area().copied())
611                })?;
612                self.bind_end(masm, context)?;
613            }
614            Loop { stack_state, .. } => {
615                ensure!(
616                    stack_state.target_len == context.stack.len(),
617                    CodeGenError::control_frame_state_mismatch()
618                );
619            }
620        };
621
622        Ok(())
623    }
624
625    /// Binds the exit label of the current control stack frame and pushes the
626    /// ABI results to the value stack.
627    pub fn bind_end<M: MacroAssembler>(
628        &mut self,
629        masm: &mut M,
630        context: &mut CodeGenContext<Emission>,
631    ) -> Result<()> {
632        self.push_abi_results(context, masm)?;
633        self.bind_exit_label(masm)
634    }
635
636    /// Binds the exit label of the control stack frame.
637    pub fn bind_exit_label<M: MacroAssembler>(&self, masm: &mut M) -> Result<()> {
638        use ControlStackFrame::*;
639        match self {
640            // We use an explicit label to track the exit of an if block. In case there's no
641            // else, we bind the if's continuation block to make sure that any jumps from the if
642            // condition are reachable and we bind the explicit exit label as well to ensure that any
643            // branching instructions are able to correctly reach the block's end.
644            If { cont, .. } => masm.bind(*cont)?,
645            _ => {}
646        }
647        if let Some(label) = self.exit_label() {
648            masm.bind(*label)?;
649        }
650        Ok(())
651    }
652
653    /// Returns the continuation label of the current control stack frame.
654    pub fn label(&self) -> &MachLabel {
655        use ControlStackFrame::*;
656
657        match self {
658            If { exit, .. } | Else { exit, .. } | Block { exit, .. } => exit,
659            Loop { head, .. } => head,
660        }
661    }
662
663    /// Returns the exit label of the current control stack frame. Note that
664    /// this is similar to [`ControlStackFrame::label`], with the only difference that it
665    /// returns `None` for `Loop` since its label doesn't represent an exit.
666    pub fn exit_label(&self) -> Option<&MachLabel> {
667        use ControlStackFrame::*;
668
669        match self {
670            If { exit, .. } | Else { exit, .. } | Block { exit, .. } => Some(exit),
671            Loop { .. } => None,
672        }
673    }
674
675    /// Set the current control stack frame as a branch target.
676    pub fn set_as_target(&mut self) {
677        match self {
678            ControlStackFrame::Block {
679                is_branch_target, ..
680            } => {
681                *is_branch_target = true;
682            }
683            _ => {}
684        }
685    }
686
687    /// Returns [`crate::abi::ABIResults`] of the control stack frame
688    /// block.
689    pub fn results<M>(&mut self) -> Result<&mut ABIResults>
690    where
691        M: MacroAssembler,
692    {
693        use ControlStackFrame::*;
694
695        match self {
696            If { sig, .. } | Else { sig, .. } | Block { sig, .. } => sig.results::<M>(),
697            Loop { sig, .. } => sig.params::<M>(),
698        }
699    }
700
701    /// Returns the block params interpreted as [crate::abi::ABIResults].
702    pub fn params<M>(&mut self) -> Result<&mut ABIResults>
703    where
704        M: MacroAssembler,
705    {
706        use ControlStackFrame::*;
707        match self {
708            If { sig, .. } | Else { sig, .. } | Block { sig, .. } | Loop { sig, .. } => {
709                sig.params::<M>()
710            }
711        }
712    }
713
714    /// Orchestrates how block results are handled.
715    /// Results are handled in reverse order, starting from register results
716    /// continuing to memory values. This guarantees that the stack ordering
717    /// invariant is maintained. See [ABIResults] for more details.
718    ///
719    /// This function will iterate through each result and invoke the provided
720    /// callback if there are results on the stack.
721    ///
722    /// Calculating the return area involves ensuring that there's enough stack
723    /// space to store the block's results. To make the process of handling
724    /// multiple results easier, this function will save all live registers and
725    /// locals right after handling any register results. This will ensure that
726    /// the top `n` values in the value stack are correctly placed in the memory
727    /// locations corresponding to multiple stack results. Once the iteration
728    /// over all the results is done, the stack result area of the block will be
729    /// updated.
730    pub fn pop_abi_results<M, F>(
731        &mut self,
732        context: &mut CodeGenContext<Emission>,
733        masm: &mut M,
734        calculate_ret_area: F,
735    ) -> Result<()>
736    where
737        M: MacroAssembler,
738        F: FnMut(&ABIResults, &mut CodeGenContext<Emission>, &mut M) -> Result<Option<RetArea>>,
739    {
740        Self::pop_abi_results_impl(self.results::<M>()?, context, masm, calculate_ret_area)
741    }
742
743    /// Shared implementation for popping the ABI results.
744    /// This is needed because, in some cases, params must be interpreted and
745    /// used as the results of the block. When emitting code at control flow
746    /// joins, the block params are interpreted as results, to ensure that they
747    /// can correctly "flow" as the results of the block. This is especially
748    /// important in the presence of empty then, else and loop blocks. This
749    /// interpretation is an internal detail of the control module, and having
750    /// a shared implementation allows the caller to decide how the
751    /// results should be interpreted.
752    pub fn pop_abi_results_impl<M, F>(
753        results: &mut ABIResults,
754        context: &mut CodeGenContext<Emission>,
755        masm: &mut M,
756        mut calculate_ret_area: F,
757    ) -> Result<()>
758    where
759        M: MacroAssembler,
760        F: FnMut(&ABIResults, &mut CodeGenContext<Emission>, &mut M) -> Result<Option<RetArea>>,
761    {
762        let mut iter = results.operands().iter().rev().peekable();
763
764        while let Some(ABIOperand::Reg { reg, .. }) = iter.peek() {
765            let TypedReg { reg, .. } = context.pop_to_reg(masm, Some(*reg))?;
766            context.free_reg(reg);
767            iter.next().unwrap();
768        }
769
770        let ret_area = calculate_ret_area(results, context, masm)?;
771
772        let retptr = Self::maybe_load_retptr(ret_area.as_ref(), &results, context, masm)?;
773        if let Some(area) = ret_area {
774            if area.is_sp() {
775                Self::ensure_ret_area(&area, context, masm)?;
776            }
777        }
778
779        if let Some(retptr) = retptr {
780            while let Some(ABIOperand::Stack { offset, .. }) = iter.peek() {
781                let addr = masm.address_at_reg(retptr, *offset)?;
782                context.pop_to_addr(masm, addr)?;
783                iter.next().unwrap();
784            }
785            context.free_reg(retptr);
786        }
787
788        if let Some(area) = ret_area {
789            if area.is_sp() {
790                Self::adjust_stack_results(area, results, context, masm)?;
791            }
792        }
793
794        Ok(())
795    }
796
797    /// Convenience wrapper around [CodeGenContext::push_abi_results] using the
798    /// results of the current frame.
799    fn push_abi_results<M>(
800        &mut self,
801        context: &mut CodeGenContext<Emission>,
802        masm: &mut M,
803    ) -> Result<()>
804    where
805        M: MacroAssembler,
806    {
807        context.push_abi_results(self.results::<M>()?, masm, |results, _, _| {
808            results.ret_area().copied()
809        })
810    }
811
812    /// Preemptively handles the ABI results of the current frame.
813    /// This function is meant to be used when emitting control flow with joins,
814    /// in which it's not possible to know at compile time which branch will be
815    /// taken.
816    pub fn top_abi_results<M, F>(
817        &mut self,
818        context: &mut CodeGenContext<Emission>,
819        masm: &mut M,
820        calculate_ret_area: F,
821    ) -> Result<()>
822    where
823        M: MacroAssembler,
824        F: FnMut(&ABIResults, &mut CodeGenContext<Emission>, &mut M) -> Result<Option<RetArea>>,
825    {
826        Self::top_abi_results_impl::<M, _>(self.results::<M>()?, context, masm, calculate_ret_area)
827    }
828
829    /// Internal implementation of [Self::top_abi_results].
830    /// See [Self::pop_abi_results_impl] on why an internal implementation is
831    /// needed.
832    fn top_abi_results_impl<M, F>(
833        results: &mut ABIResults,
834        context: &mut CodeGenContext<Emission>,
835        masm: &mut M,
836        mut calculate_ret_area: F,
837    ) -> Result<()>
838    where
839        M: MacroAssembler,
840        F: FnMut(&ABIResults, &mut CodeGenContext<Emission>, &mut M) -> Result<Option<RetArea>>,
841    {
842        let mut area = None;
843        Self::pop_abi_results_impl::<M, _>(results, context, masm, |r, context, masm| {
844            area = calculate_ret_area(r, context, masm)?;
845            Ok(area)
846        })?;
847        // Use the previously calculated area to ensure that the ret area is
848        // kept in sync between both operations.
849        context.push_abi_results::<M, _>(results, masm, |_, _, _| area)
850    }
851
852    // If the results on the stack are handled via the stack pointer, ensure
853    // that the stack results are correctly located. In general, since values in
854    // the value stack are spilled when exiting the block, the top `n` entries
855    // in the value stack, representing the `n` stack results of the block are
856    // almost correctly located. However, since constants are not
857    // spilled, their presence complicate block exits. For this reason, the
858    // last step for finalizing multiple block results involves:
859    // * Scanning the value stack from oldest to newest memory values and
860    // calculating the source and destination of each value, if the source
861    // is closer to the stack pointer (greater) than the destination,
862    // perform a memory move of the bytes to its destination, else stop,
863    // because the memory values are in place.
864    // * Scanning the value stack from newest to oldest and calculating the
865    // source and destination of each value, if the source is closer to the
866    // frame pointer (less) than the destination, perform a memory move of
867    // the bytes to its destination, else stop, because the memory values
868    // are in place.
869    // * Lastly, iterate over the top `n` elements of the value stack,
870    // and spill any constant values, placing them in their respective
871    // memory location.
872    //
873    // The implementation in Winch is inspired by how this is handled in
874    // SpiderMonkey's WebAssembly Baseline Compiler:
875    // https://wingolog.org/archives/2020/04/03/multi-value-webassembly-in-firefox-from-1-to-n
876    fn adjust_stack_results<M>(
877        ret_area: RetArea,
878        results: &ABIResults,
879        context: &mut CodeGenContext<Emission>,
880        masm: &mut M,
881    ) -> Result<()>
882    where
883        M: MacroAssembler,
884    {
885        ensure!(ret_area.is_sp(), CodeGenError::sp_addressing_expected());
886        let results_offset = ret_area.unwrap_sp();
887
888        // Start iterating from memory values that are closer to the
889        // frame pointer (oldest entries first).
890        for (i, operand) in results.operands().iter().enumerate() {
891            if operand.is_reg() {
892                break;
893            }
894
895            let value_index = (context.stack.len() - results.stack_operands_len()) + i;
896            let val = context.stack.inner()[value_index];
897
898            match (val, operand) {
899                (Val::Memory(mem), ABIOperand::Stack { offset, size, .. }) => {
900                    let dst = results_offset.as_u32() - *offset;
901                    let src = mem.slot.offset;
902
903                    // Values are moved from lower (SP) to higher (FP)
904                    // addresses.
905                    if src.as_u32() <= dst {
906                        break;
907                    }
908
909                    masm.memmove(
910                        src,
911                        SPOffset::from_u32(dst),
912                        *size,
913                        MemMoveDirection::LowToHigh,
914                    )?;
915                }
916                _ => {}
917            }
918        }
919
920        // Start iterating from memory values that are closer to the
921        // stack pointer (newest entries first).
922        for (i, operand) in results
923            .operands()
924            .iter()
925            .rev()
926            // Skip any register results.
927            .skip(results.regs().len())
928            .enumerate()
929        {
930            let value_index = context.stack.len() - i - 1;
931            let val = context.stack.inner()[value_index];
932            match (val, operand) {
933                (Val::Memory(mem), ABIOperand::Stack { offset, size, .. }) => {
934                    let dst = results_offset.as_u32() - *offset;
935                    let src = mem.slot.offset;
936
937                    // Values are moved from higher (FP) to lower (SP)
938                    // addresses.
939                    if src.as_u32() >= dst {
940                        break;
941                    }
942
943                    masm.memmove(
944                        src,
945                        SPOffset::from_u32(dst),
946                        *size,
947                        MemMoveDirection::HighToLow,
948                    )?;
949                }
950                _ => {}
951            }
952        }
953
954        // Finally store any constants in the value stack in their respective
955        // locations.
956        for operand in results
957            .operands()
958            .iter()
959            .take(results.stack_operands_len())
960            .rev()
961        {
962            // If we want to do this, we should start from newest, essentially from top to
963            // bottom in the iteration of the operands.
964            match (operand, context.stack.peek().unwrap()) {
965                (ABIOperand::Stack { ty, offset, .. }, Val::I32(v)) => {
966                    let addr = masm
967                        .address_from_sp(SPOffset::from_u32(results_offset.as_u32() - *offset))?;
968                    masm.store(RegImm::i32(*v), addr, (*ty).try_into()?)?;
969                }
970                (ABIOperand::Stack { ty, offset, .. }, Val::I64(v)) => {
971                    let addr = masm
972                        .address_from_sp(SPOffset::from_u32(results_offset.as_u32() - *offset))?;
973                    masm.store(RegImm::i64(*v), addr, (*ty).try_into()?)?;
974                }
975                (ABIOperand::Stack { ty, offset, .. }, Val::F32(v)) => {
976                    let addr = masm
977                        .address_from_sp(SPOffset::from_u32(results_offset.as_u32() - *offset))?;
978                    masm.store(RegImm::f32(v.bits()), addr, (*ty).try_into()?)?;
979                }
980                (ABIOperand::Stack { ty, offset, .. }, Val::F64(v)) => {
981                    let addr = masm
982                        .address_from_sp(SPOffset::from_u32(results_offset.as_u32() - *offset))?;
983                    masm.store(RegImm::f64(v.bits()), addr, (*ty).try_into()?)?;
984                }
985                (ABIOperand::Stack { ty, offset, .. }, Val::V128(v)) => {
986                    let addr = masm
987                        .address_from_sp(SPOffset::from_u32(results_offset.as_u32() - *offset))?;
988                    masm.store(RegImm::v128(*v), addr, (*ty).try_into()?)?;
989                }
990                (_, v) => debug_assert!(v.is_mem()),
991            }
992
993            let _ = context.stack.pop().unwrap();
994        }
995
996        // Adjust any excess stack space: the stack space after handling the
997        // block's results should be the exact amount needed by the return area.
998        ensure!(
999            masm.sp_offset()?.as_u32() >= results_offset.as_u32(),
1000            CodeGenError::invalid_sp_offset()
1001        );
1002        masm.free_stack(masm.sp_offset()?.as_u32() - results_offset.as_u32())?;
1003        Ok(())
1004    }
1005
1006    /// Ensures that there is enough space for return values on the stack.
1007    /// This function is called at the end of all blocks and when branching from
1008    /// within blocks.
1009    fn ensure_ret_area<M>(
1010        ret_area: &RetArea,
1011        context: &mut CodeGenContext<Emission>,
1012        masm: &mut M,
1013    ) -> Result<()>
1014    where
1015        M: MacroAssembler,
1016    {
1017        ensure!(ret_area.is_sp(), CodeGenError::sp_addressing_expected());
1018        // Save any live registers and locals when exiting the block to ensure
1019        // that the respective values are correctly located in memory.
1020        // See [Self::adjust_stack_results] for more details.
1021        context.spill(masm)?;
1022        if ret_area.unwrap_sp() > masm.sp_offset()? {
1023            masm.reserve_stack(ret_area.unwrap_sp().as_u32() - masm.sp_offset()?.as_u32())?
1024        }
1025
1026        Ok(())
1027    }
1028
1029    /// Loads the return pointer, if it exists, into the next available register.
1030    fn maybe_load_retptr<M>(
1031        ret_area: Option<&RetArea>,
1032        results: &ABIResults,
1033        context: &mut CodeGenContext<Emission>,
1034        masm: &mut M,
1035    ) -> Result<Option<Reg>>
1036    where
1037        M: MacroAssembler,
1038    {
1039        if let Some(area) = ret_area {
1040            match area {
1041                RetArea::Slot(slot) => {
1042                    let base = context.without::<Result<Reg>, M, _>(
1043                        results.regs(),
1044                        masm,
1045                        |cx, masm| cx.any_gpr(masm),
1046                    )??;
1047                    let local_addr = masm.local_address(&slot)?;
1048                    masm.load_ptr(local_addr, writable!(base))?;
1049                    Ok(Some(base))
1050                }
1051                _ => Ok(None),
1052            }
1053        } else {
1054            Ok(None)
1055        }
1056    }
1057
1058    /// This function is used at the end of unreachable code handling
1059    /// to determine if the reachability status should be updated.
1060    pub fn is_next_sequence_reachable(&self) -> bool {
1061        use ControlStackFrame::*;
1062
1063        match self {
1064            // For if/else, the reachability of the next sequence is determined
1065            // by the reachability state at the start of the block. An else
1066            // block will be reachable if the if block is also reachable at
1067            // entry.
1068            If { reachable, .. } | Else { reachable, .. } => *reachable,
1069            // For blocks, the reachability of the next sequence is determined
1070            // if they're a branch target.
1071            Block {
1072                is_branch_target, ..
1073            } => *is_branch_target,
1074            // Loops are not used for reachability analysis,
1075            // given that they don't have exit branches.
1076            Loop { .. } => false,
1077        }
1078    }
1079
1080    /// Returns a reference to the [StackState] of the block.
1081    pub fn stack_state(&self) -> &StackState {
1082        use ControlStackFrame::*;
1083        match self {
1084            If { stack_state, .. }
1085            | Else { stack_state, .. }
1086            | Block { stack_state, .. }
1087            | Loop { stack_state, .. } => stack_state,
1088        }
1089    }
1090
1091    /// Returns true if the current frame is [ControlStackFrame::If].
1092    pub fn is_if(&self) -> bool {
1093        match self {
1094            Self::If { .. } => true,
1095            _ => false,
1096        }
1097    }
1098
1099    /// Returns true if the current frame is [ControlStackFrame::Loop].
1100    pub fn is_loop(&self) -> bool {
1101        match self {
1102            Self::Loop { .. } => true,
1103            _ => false,
1104        }
1105    }
1106
1107    /// Returns true if the current stack pointer is unbalanced
1108    /// relative to the the expected control frame stack pointer
1109    /// offset. The stack pointer is considered unbalanced relative
1110    /// to the control frame if the stack pointer is greater than the
1111    /// the target stack pointer offset expected by the control frame.
1112    pub fn unbalanced<M: MacroAssembler>(&self, masm: &mut M) -> Result<bool> {
1113        Ok(masm.sp_offset()? > self.stack_state().target_offset)
1114    }
1115}