1 //! 2 //! The Default ABI 3 //! 4 //! Winch uses a default ABI, for all internal functions. This allows 5 //! us to push the complexity of system ABI compliance to the trampolines. The 6 //! default ABI treats all allocatable registers as caller saved, which means 7 //! that (i) all register values in the Wasm value stack (which are normally 8 //! referred to as "live"), must be saved onto the machine stack (ii) function 9 //! prologues and epilogues don't store/restore other registers more than the 10 //! non-allocatable ones (e.g. rsp/rbp in x86_64). 11 //! 12 //! The calling convention in the default ABI, uses registers to a certain fixed 13 //! count for arguments and return values, and then the stack is used for all 14 //! additional arguments and return values. Aside from the parameters declared 15 //! in each WebAssembly function, Winch's ABI declares two extra parameters, to 16 //! hold the callee and caller `VMContext` pointers. A well-known `LocalSlot` is 17 //! reserved for the callee VMContext pointer and also a particular pinned 18 //! register is used to hold the value of the callee `VMContext`, which is 19 //! available throughout the lifetime of the function. 20 //! 21 //! 22 //! Generally the stack layout looks like: 23 //! +-------------------------------+ 24 //! | | 25 //! | | 26 //! | Stack Args | 27 //! | | 28 //! | | 29 //! +-------------------------------+----> SP @ function entry 30 //! | Ret addr | 31 //! +-------------------------------+ 32 //! | SP | 33 //! +-------------------------------+----> SP @ Function prologue 34 //! | | 35 //! +-------------------------------+----> VMContext slot 36 //! | | 37 //! | | 38 //! | Stack slots | 39 //! | + dynamic space | 40 //! | | 41 //! | | 42 //! | | 43 //! +-------------------------------+----> SP @ callsite (after) 44 //! | alignment | 45 //! | + arguments | 46 //! | | ----> Space allocated for calls 47 //! | | 48 use crate::codegen::ptr_type_from_ptr_size; 49 use crate::isa::{reg::Reg, CallingConvention}; 50 use crate::masm::SPOffset; 51 use anyhow::Result; 52 use smallvec::SmallVec; 53 use std::collections::HashSet; 54 use std::ops::{Add, BitAnd, Not, Sub}; 55 use wasmtime_environ::{WasmFuncType, WasmValType}; 56 57 pub(crate) mod local; 58 pub(crate) use local::*; 59 60 /// Internal classification for params or returns, 61 /// mainly used for params and return register assignment. 62 #[derive(Clone, Copy, Eq, PartialEq, Debug)] 63 pub(super) enum ParamsOrReturns { 64 Params, 65 Returns, 66 } 67 68 /// Macro to get the pinned register holding the [VMContext]. 69 macro_rules! vmctx { 70 ($m:ident) => { 71 <$m::ABI as $crate::abi::ABI>::vmctx_reg() 72 }; 73 } 74 75 /// Macro to get the designated general purpose scratch register or the 76 /// designated scratch register for the given type. 77 macro_rules! scratch { 78 ($m:ident) => { 79 <$m::ABI as $crate::abi::ABI>::scratch_for(&wasmtime_environ::WasmValType::I64) 80 }; 81 ($m:ident, $wasm_type:expr) => { 82 <$m::ABI as $crate::abi::ABI>::scratch_for($wasm_type) 83 }; 84 } 85 86 pub(crate) use scratch; 87 pub(crate) use vmctx; 88 89 /// Constructs an [ABISig] using Winch's ABI. 90 pub(crate) fn wasm_sig<A: ABI>(ty: &WasmFuncType) -> Result<ABISig> { 91 // 6 is used semi-arbitrarily here, we can modify as we see fit. 92 let mut params: SmallVec<[WasmValType; 6]> = SmallVec::new(); 93 params.extend_from_slice(&vmctx_types::<A>()); 94 params.extend_from_slice(ty.params()); 95 96 A::sig_from(¶ms, ty.returns(), &CallingConvention::Default) 97 } 98 99 /// Returns the callee and caller [VMContext] types. 100 pub(crate) fn vmctx_types<A: ABI>() -> [WasmValType; 2] { 101 [A::ptr_type(), A::ptr_type()] 102 } 103 104 /// Trait implemented by a specific ISA and used to provide 105 /// information about alignment, parameter passing, usage of 106 /// specific registers, etc. 107 pub(crate) trait ABI { 108 /// The required stack alignment. 109 fn stack_align() -> u8; 110 111 /// The required stack alignment for calls. 112 fn call_stack_align() -> u8; 113 114 /// The offset to the argument base, relative to the frame pointer. 115 fn arg_base_offset() -> u8; 116 117 /// The initial size in bytes of the function's frame. 118 /// 119 /// This amount is constant and accounts for all the stack space allocated 120 /// at the frame setup. 121 fn initial_frame_size() -> u8; 122 123 /// Construct the ABI-specific signature from a WebAssembly 124 /// function type. 125 #[cfg(test)] 126 fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> Result<ABISig> { 127 Self::sig_from(wasm_sig.params(), wasm_sig.returns(), call_conv) 128 } 129 130 /// Construct an ABI signature from WasmType params and returns. 131 fn sig_from( 132 params: &[WasmValType], 133 returns: &[WasmValType], 134 call_conv: &CallingConvention, 135 ) -> Result<ABISig>; 136 137 /// Construct [`ABIResults`] from a slice of [`WasmType`]. 138 fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> Result<ABIResults>; 139 140 /// Returns the number of bits in a word. 141 fn word_bits() -> u8; 142 143 /// Returns the number of bytes in a word. 144 fn word_bytes() -> u8 { 145 Self::word_bits() / 8 146 } 147 148 /// Returns the designated scratch register for the given [WasmType]. 149 fn scratch_for(ty: &WasmValType) -> Reg; 150 151 /// Returns the pinned register used to hold 152 /// the `VMContext`. 153 fn vmctx_reg() -> Reg; 154 155 /// The size, in bytes, of each stack slot used for stack parameter passing. 156 fn stack_slot_size() -> u8; 157 158 /// Returns the size in bytes of the given [`WasmType`]. 159 fn sizeof(ty: &WasmValType) -> u8; 160 161 /// The target pointer size represented as [WasmValType]. 162 fn ptr_type() -> WasmValType { 163 // Defaulting to 64, since we currently only support 64-bit 164 // architectures. 165 WasmValType::I64 166 } 167 } 168 169 /// ABI-specific representation of function argument or result. 170 #[derive(Clone, Debug)] 171 pub enum ABIOperand { 172 /// A register [`ABIOperand`]. 173 Reg { 174 /// The type of the [`ABIOperand`]. 175 ty: WasmValType, 176 /// Register holding the [`ABIOperand`]. 177 reg: Reg, 178 /// The size of the [`ABIOperand`], in bytes. 179 size: u32, 180 }, 181 /// A stack [`ABIOperand`]. 182 Stack { 183 /// The type of the [`ABIOperand`]. 184 ty: WasmValType, 185 /// Offset of the operand referenced through FP by the callee and 186 /// through SP by the caller. 187 offset: u32, 188 /// The size of the [`ABIOperand`], in bytes. 189 size: u32, 190 }, 191 } 192 193 impl ABIOperand { 194 /// Allocate a new register [`ABIOperand`]. 195 pub fn reg(reg: Reg, ty: WasmValType, size: u32) -> Self { 196 Self::Reg { reg, ty, size } 197 } 198 199 /// Allocate a new stack [`ABIOperand`]. 200 pub fn stack_offset(offset: u32, ty: WasmValType, size: u32) -> Self { 201 Self::Stack { ty, offset, size } 202 } 203 204 /// Is this [`ABIOperand`] in a register. 205 pub fn is_reg(&self) -> bool { 206 match *self { 207 ABIOperand::Reg { .. } => true, 208 _ => false, 209 } 210 } 211 212 /// Unwraps the underlying register if it is one. 213 /// 214 /// # Panics 215 /// This function panics if the [`ABIOperand`] is not a register. 216 pub fn unwrap_reg(&self) -> Reg { 217 match self { 218 ABIOperand::Reg { reg, .. } => *reg, 219 _ => unreachable!(), 220 } 221 } 222 } 223 224 /// Information about the [`ABIOperand`] information used in [`ABISig`]. 225 #[derive(Clone, Debug)] 226 pub(crate) struct ABIOperands { 227 /// All the operands. 228 pub inner: SmallVec<[ABIOperand; 6]>, 229 /// All the registers used as operands. 230 pub regs: HashSet<Reg>, 231 /// Stack bytes used by the operands. 232 pub bytes: u32, 233 } 234 235 impl Default for ABIOperands { 236 fn default() -> Self { 237 Self { 238 inner: Default::default(), 239 regs: HashSet::with_capacity(0), 240 bytes: 0, 241 } 242 } 243 } 244 245 /// Machine stack location of the stack results. 246 #[derive(Debug, Copy, Clone)] 247 pub(crate) enum RetArea { 248 /// Addressed from the stack pointer at the given offset. 249 SP(SPOffset), 250 /// The address of the results base is stored at a particular, 251 /// well known [LocalSlot]. 252 Slot(LocalSlot), 253 /// The return area cannot be fully resolved ahead-of-time. 254 /// If there are results on the stack, this is the default state to which 255 /// all return areas get initialized to until they can be fully resolved to 256 /// either a [RetArea::SP] or [RetArea::Slot]. 257 /// 258 /// This allows a more explicit differentiation between the existence of 259 /// a return area versus no return area at all. 260 Uninit, 261 } 262 263 impl Default for RetArea { 264 fn default() -> Self { 265 Self::Uninit 266 } 267 } 268 269 impl RetArea { 270 /// Create a [RetArea] addressed from SP at the given offset. 271 pub fn sp(offs: SPOffset) -> Self { 272 Self::SP(offs) 273 } 274 275 /// Create a [RetArea] addressed stored at the given [LocalSlot]. 276 pub fn slot(local: LocalSlot) -> Self { 277 Self::Slot(local) 278 } 279 280 /// Returns the [SPOffset] used as the base of the return area. 281 /// 282 /// # Panics 283 /// This function panics if the return area doesn't hold a [SPOffset]. 284 pub fn unwrap_sp(&self) -> SPOffset { 285 match self { 286 Self::SP(offs) => *offs, 287 _ => unreachable!(), 288 } 289 } 290 291 /// Returns true if the return area is addressed via the stack pointer. 292 pub fn is_sp(&self) -> bool { 293 match self { 294 Self::SP(_) => true, 295 _ => false, 296 } 297 } 298 299 /// Returns true if the return area is uninitialized. 300 pub fn is_uninit(&self) -> bool { 301 match self { 302 Self::Uninit => true, 303 _ => false, 304 } 305 } 306 } 307 308 /// ABI-specific representation of an [`ABISig`]. 309 #[derive(Clone, Debug, Default)] 310 pub(crate) struct ABIResults { 311 /// The result operands. 312 operands: ABIOperands, 313 /// The return area, if there are results on the stack. 314 ret_area: Option<RetArea>, 315 } 316 317 impl ABIResults { 318 /// Creates [`ABIResults`] from a slice of `WasmType`. 319 /// This function maps the given return types to their ABI specific 320 /// representation. It does so, by iterating over them and applying the 321 /// given `map` closure. The map closure takes a [WasmValType], maps its ABI 322 /// representation, according to the calling convention. In the case of 323 /// results, one result is stored in registers and the rest at particular 324 /// offsets in the stack. 325 pub fn from<F>( 326 returns: &[WasmValType], 327 call_conv: &CallingConvention, 328 mut map: F, 329 ) -> Result<Self> 330 where 331 F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>, 332 { 333 if returns.len() == 0 { 334 return Ok(Self::default()); 335 } 336 337 type FoldTuple = (SmallVec<[ABIOperand; 6]>, HashSet<Reg>, u32); 338 type FoldTupleResult = Result<FoldTuple>; 339 340 let fold_impl = 341 |(mut operands, mut regs, stack_bytes): FoldTuple, arg| -> FoldTupleResult { 342 let (operand, bytes) = map(arg, stack_bytes)?; 343 if operand.is_reg() { 344 regs.insert(operand.unwrap_reg()); 345 } 346 operands.push(operand); 347 Ok((operands, regs, bytes)) 348 }; 349 350 // When dealing with multiple results, Winch's calling convention stores the 351 // last return value in a register rather than the first one. In that 352 // sense, Winch's return values in the ABI signature are "reversed" in 353 // terms of storage. This technique is particularly helpful to ensure that 354 // the following invariants are maintained: 355 // * Spilled memory values always precede register values 356 // * Spilled values are stored from oldest to newest, matching their 357 // respective locations on the machine stack. 358 let (mut operands, regs, bytes) = if call_conv.is_default() { 359 returns 360 .iter() 361 .rev() 362 .try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)? 363 } else { 364 returns 365 .iter() 366 .try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)? 367 }; 368 369 // Similar to above, we reverse the result of the operands calculation 370 // to ensure that they match the declared order. 371 if call_conv.is_default() { 372 operands.reverse(); 373 } 374 375 Ok(Self::new(ABIOperands { 376 inner: operands, 377 regs, 378 bytes, 379 })) 380 } 381 382 /// Create a new [`ABIResults`] from [`ABIOperands`]. 383 pub fn new(operands: ABIOperands) -> Self { 384 let ret_area = (operands.bytes > 0).then(|| RetArea::default()); 385 Self { operands, ret_area } 386 } 387 388 /// Returns a reference to a [HashSet<Reg>], which includes 389 /// all the registers used to hold function results. 390 pub fn regs(&self) -> &HashSet<Reg> { 391 &self.operands.regs 392 } 393 394 /// Get a slice over all the result [`ABIOperand`]s. 395 pub fn operands(&self) -> &[ABIOperand] { 396 &self.operands.inner 397 } 398 399 /// Returns the length of the result. 400 pub fn len(&self) -> usize { 401 self.operands.inner.len() 402 } 403 404 /// Returns the length of results on the stack. 405 pub fn stack_operands_len(&self) -> usize { 406 self.operands().len() - self.regs().len() 407 } 408 409 /// Get the [`ABIOperand`] result in the nth position. 410 #[cfg(test)] 411 pub fn get(&self, n: usize) -> Option<&ABIOperand> { 412 self.operands.inner.get(n) 413 } 414 415 /// Returns the first [`ABIOperand`]. 416 /// Useful in situations where the function signature is known to 417 /// have a single return. 418 /// 419 /// # Panics 420 /// This function panics if the function signature contains more 421 pub fn unwrap_singleton(&self) -> &ABIOperand { 422 debug_assert_eq!(self.len(), 1); 423 &self.operands.inner[0] 424 } 425 426 /// Returns the size, in bytes of all the [`ABIOperand`]s in the stack. 427 pub fn size(&self) -> u32 { 428 self.operands.bytes 429 } 430 431 /// Returns true if the [`ABIResults`] require space on the machine stack 432 /// for results. 433 pub fn on_stack(&self) -> bool { 434 self.operands.bytes > 0 435 } 436 437 /// Set the return area of the signature. 438 /// 439 /// # Panics 440 /// 441 /// This function will panic if trying to set a return area if there are 442 /// no results on the stack or if trying to set an uninitialize return area. 443 /// This method must only be used when the return area can be fully 444 /// materialized. 445 pub fn set_ret_area(&mut self, area: RetArea) { 446 debug_assert!(self.on_stack()); 447 debug_assert!(!area.is_uninit()); 448 self.ret_area = Some(area); 449 } 450 451 /// Returns a reference to the return area, if any. 452 pub fn ret_area(&self) -> Option<&RetArea> { 453 self.ret_area.as_ref() 454 } 455 } 456 457 /// ABI-specific representation of an [`ABISig`]. 458 #[derive(Debug, Clone, Default)] 459 pub(crate) struct ABIParams { 460 /// The param operands. 461 operands: ABIOperands, 462 /// Whether [`ABIParams`] contains an extra parameter for the stack 463 /// result area. 464 has_retptr: bool, 465 } 466 467 impl ABIParams { 468 /// Creates [`ABIParams`] from a slice of `WasmType`. 469 /// This function maps the given param types to their ABI specific 470 /// representation. It does so, by iterating over them and applying the 471 /// given `map` closure. The map closure takes a [WasmType], maps its ABI 472 /// representation, according to the calling convention. In the case of 473 /// params, multiple params may be passed in registers and the rest on the 474 /// stack depending on the calling convention. 475 pub fn from<F, A: ABI>( 476 params: &[WasmValType], 477 initial_bytes: u32, 478 needs_stack_results: bool, 479 mut map: F, 480 ) -> Result<Self> 481 where 482 F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>, 483 { 484 if params.len() == 0 && !needs_stack_results { 485 return Ok(Self::with_bytes(initial_bytes)); 486 } 487 488 let register_capacity = params.len().min(6); 489 let mut operands = SmallVec::new(); 490 let mut regs = HashSet::with_capacity(register_capacity); 491 let mut stack_bytes = initial_bytes; 492 493 let ptr_type = ptr_type_from_ptr_size(<A as ABI>::word_bytes()); 494 // Handle stack results by specifying an extra, implicit first argument. 495 let stack_results = if needs_stack_results { 496 let (operand, bytes) = map(&ptr_type, stack_bytes)?; 497 if operand.is_reg() { 498 regs.insert(operand.unwrap_reg()); 499 } 500 stack_bytes = bytes; 501 Some(operand) 502 } else { 503 None 504 }; 505 506 for arg in params.iter() { 507 let (operand, bytes) = map(arg, stack_bytes)?; 508 if operand.is_reg() { 509 regs.insert(operand.unwrap_reg()); 510 } 511 operands.push(operand); 512 stack_bytes = bytes; 513 } 514 515 if let Some(operand) = stack_results { 516 // But still push the operand for stack results last as that is what 517 // the rest of the code expects. 518 operands.push(operand); 519 } 520 521 Ok(Self { 522 operands: ABIOperands { 523 inner: operands, 524 regs, 525 bytes: stack_bytes, 526 }, 527 has_retptr: needs_stack_results, 528 }) 529 } 530 531 /// Creates new [`ABIParams`], with the specified amount of stack bytes. 532 pub fn with_bytes(bytes: u32) -> Self { 533 let mut params = Self::default(); 534 params.operands.bytes = bytes; 535 params 536 } 537 538 /// Get the [`ABIOperand`] param in the nth position. 539 #[allow(unused)] 540 pub fn get(&self, n: usize) -> Option<&ABIOperand> { 541 self.operands.inner.get(n) 542 } 543 544 /// Get a slice over all the parameter [`ABIOperand`]s. 545 pub fn operands(&self) -> &[ABIOperand] { 546 &self.operands.inner 547 } 548 549 /// Returns the length of the params, including the return pointer, 550 /// if any. 551 pub fn len(&self) -> usize { 552 self.operands.inner.len() 553 } 554 555 /// Returns the length of the params, excluding the return pointer, 556 /// if any. 557 pub fn len_without_retptr(&self) -> usize { 558 if self.has_retptr { 559 self.len() - 1 560 } else { 561 self.len() 562 } 563 } 564 565 /// Returns true if the [ABISig] has an extra parameter for stack results. 566 pub fn has_retptr(&self) -> bool { 567 self.has_retptr 568 } 569 570 /// Returns the last [ABIOperand] used as the pointer to the 571 /// stack results area. 572 /// 573 /// # Panics 574 /// This function panics if the [ABIParams] doesn't have a stack results 575 /// parameter. 576 pub fn unwrap_results_area_operand(&self) -> &ABIOperand { 577 debug_assert!(self.has_retptr); 578 self.operands.inner.last().unwrap() 579 } 580 } 581 582 /// An ABI-specific representation of a function signature. 583 #[derive(Debug, Clone)] 584 pub(crate) struct ABISig { 585 /// Function parameters. 586 pub params: ABIParams, 587 /// Function result. 588 pub results: ABIResults, 589 /// A unique set of registers used in the entire [`ABISig`]. 590 pub regs: HashSet<Reg>, 591 /// Calling convention used. 592 pub call_conv: CallingConvention, 593 } 594 595 impl Default for ABISig { 596 fn default() -> Self { 597 Self { 598 params: Default::default(), 599 results: Default::default(), 600 regs: Default::default(), 601 call_conv: CallingConvention::Default, 602 } 603 } 604 } 605 606 impl ABISig { 607 /// Create a new ABI signature. 608 pub fn new(cc: CallingConvention, params: ABIParams, results: ABIResults) -> Self { 609 let regs = params 610 .operands 611 .regs 612 .union(&results.operands.regs) 613 .copied() 614 .collect(); 615 Self { 616 params, 617 results, 618 regs, 619 call_conv: cc, 620 } 621 } 622 623 /// Returns an iterator over all the parameter operands. 624 pub fn params(&self) -> &[ABIOperand] { 625 self.params.operands() 626 } 627 628 /// Returns an iterator over all the result operands. 629 pub fn results(&self) -> &[ABIOperand] { 630 self.results.operands() 631 } 632 633 /// Returns a slice over the signature params, excluding the results 634 /// base parameter, if any. 635 pub fn params_without_retptr(&self) -> &[ABIOperand] { 636 if self.params.has_retptr() { 637 &self.params()[0..(self.params.len() - 1)] 638 } else { 639 self.params() 640 } 641 } 642 643 /// Returns the stack size, in bytes, needed for arguments on the stack. 644 pub fn params_stack_size(&self) -> u32 { 645 self.params.operands.bytes 646 } 647 648 /// Returns the stack size, in bytes, needed for results on the stack. 649 pub fn results_stack_size(&self) -> u32 { 650 self.results.operands.bytes 651 } 652 653 /// Returns true if the signature has results on the stack. 654 pub fn has_stack_results(&self) -> bool { 655 self.results.on_stack() 656 } 657 } 658 659 /// Align a value up to the given power-of-two-alignment. 660 // See https://sites.google.com/site/theoryofoperatingsystems/labs/malloc/align8 661 pub(crate) fn align_to<N>(value: N, alignment: N) -> N 662 where 663 N: Not<Output = N> 664 + BitAnd<N, Output = N> 665 + Add<N, Output = N> 666 + Sub<N, Output = N> 667 + From<u8> 668 + Copy, 669 { 670 let alignment_mask = alignment - 1.into(); 671 (value + alignment_mask) & !alignment_mask 672 } 673 674 /// Calculates the delta needed to adjust a function's frame plus some 675 /// addend to a given alignment. 676 pub(crate) fn calculate_frame_adjustment(frame_size: u32, addend: u32, alignment: u32) -> u32 { 677 let total = frame_size + addend; 678 (alignment - (total % alignment)) % alignment 679 } 680