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