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