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