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