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