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