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