1 //! This module provides all the necessary building blocks for 2 //! implementing ISA specific ABIs. 3 //! 4 //! # Default ABI 5 //! 6 //! Winch uses a default internal ABI, for all internal functions. 7 //! This allows us to push the complexity of system ABI compliance to 8 //! the trampolines (not yet implemented). The default ABI treats all 9 //! allocatable registers as caller saved, which means that (i) all 10 //! register values in the Wasm value stack (which are normally 11 //! referred to as "live"), must be saved onto the machine stack (ii) 12 //! function prologues and epilogues don't store/restore other 13 //! registers more than the non-allocatable ones (e.g. rsp/rbp in 14 //! x86_64). 15 //! 16 //! The calling convention in the default ABI, uses registers to a 17 //! certain fixed count for arguments and return values, and then the 18 //! stack is used for all additional arguments. 19 //! 20 //! Generally the stack layout looks like: 21 //! +-------------------------------+ 22 //! | | 23 //! | | 24 //! | Stack Args | 25 //! | | 26 //! | | 27 //! +-------------------------------+----> SP @ function entry 28 //! | Ret addr | 29 //! +-------------------------------+ 30 //! | SP | 31 //! +-------------------------------+----> SP @ Function prologue 32 //! | | 33 //! | | 34 //! | | 35 //! | Stack slots | 36 //! | + `VMContext` slot | 37 //! | + dynamic space | 38 //! | | 39 //! | | 40 //! | | 41 //! +-------------------------------+----> SP @ callsite (after) 42 //! | alignment | 43 //! | + arguments | 44 //! | | ----> Space allocated for calls 45 //! | | 46 use crate::codegen::ptr_type_from_ptr_size; 47 use crate::isa::{reg::Reg, CallingConvention}; 48 use crate::masm::{OperandSize, SPOffset}; 49 use smallvec::SmallVec; 50 use std::collections::HashSet; 51 use std::ops::{Add, BitAnd, Not, Sub}; 52 use wasmtime_environ::{WasmFuncType, WasmHeapType, WasmRefType, WasmType}; 53 54 pub(crate) mod local; 55 pub(crate) use local::*; 56 57 /// Internal classification for params or returns, 58 /// mainly used for params and return register assignment. 59 #[derive(Clone, Copy, Eq, PartialEq, Debug)] 60 pub(super) enum ParamsOrReturns { 61 Params, 62 Returns, 63 } 64 65 /// Trait implemented by a specific ISA and used to provide 66 /// information about alignment, parameter passing, usage of 67 /// specific registers, etc. 68 pub(crate) trait ABI { 69 /// The required stack alignment. 70 fn stack_align() -> u8; 71 72 /// The required stack alignment for calls. 73 fn call_stack_align() -> u8; 74 75 /// The offset to the argument base, relative to the frame pointer. 76 fn arg_base_offset() -> u8; 77 78 /// The offset to the return address, relative to the frame pointer. 79 fn ret_addr_offset() -> u8; 80 81 /// Construct the ABI-specific signature from a WebAssembly 82 /// function type. 83 fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> ABISig; 84 85 /// Construct an ABI signature from WasmType params and returns. 86 fn sig_from(params: &[WasmType], returns: &[WasmType], call_conv: &CallingConvention) 87 -> ABISig; 88 89 /// Construct [`ABIResults`] from a slice of [`WasmType`]. 90 fn abi_results(returns: &[WasmType], call_conv: &CallingConvention) -> ABIResults; 91 92 /// Returns the number of bits in a word. 93 fn word_bits() -> u32; 94 95 /// Returns the number of bytes in a word. 96 fn word_bytes() -> u32 { 97 Self::word_bits() / 8 98 } 99 100 /// Returns the designated general purpose scratch register. 101 fn scratch_reg() -> Reg; 102 103 /// Returns the designated floating point scratch register. 104 fn float_scratch_reg() -> Reg; 105 106 /// Returns the designated scratch register for the given [WasmType]. 107 fn scratch_for(ty: &WasmType) -> Reg { 108 match ty { 109 WasmType::I32 110 | WasmType::I64 111 | WasmType::Ref(WasmRefType { 112 heap_type: WasmHeapType::Func, 113 .. 114 }) => Self::scratch_reg(), 115 WasmType::F32 | WasmType::F64 => Self::float_scratch_reg(), 116 _ => unimplemented!(), 117 } 118 } 119 120 /// Returns the frame pointer register. 121 fn fp_reg() -> Reg; 122 123 /// Returns the stack pointer register. 124 fn sp_reg() -> Reg; 125 126 /// Returns the pinned register used to hold 127 /// the `VMContext`. 128 fn vmctx_reg() -> Reg; 129 130 /// Returns the callee-saved registers for the given 131 /// calling convention. 132 fn callee_saved_regs(call_conv: &CallingConvention) -> SmallVec<[(Reg, OperandSize); 18]>; 133 134 /// The size, in bytes, of each stack slot used for stack parameter passing. 135 fn stack_slot_size() -> u32; 136 137 /// Returns the size in bytes of the given [`WasmType`]. 138 fn sizeof(ty: &WasmType) -> u32; 139 } 140 141 /// ABI-specific representation of function argument or result. 142 #[derive(Clone, Debug)] 143 pub enum ABIOperand { 144 /// A register [`ABIOperand`]. 145 Reg { 146 /// The type of the [`ABIOperand`]. 147 ty: WasmType, 148 /// Register holding the [`ABIOperand`]. 149 reg: Reg, 150 /// The size of the [`ABIOperand`], in bytes. 151 size: u32, 152 }, 153 /// A stack [`ABIOperand`]. 154 Stack { 155 /// The type of the [`ABIOperand`]. 156 ty: WasmType, 157 /// Offset of the operand referenced through FP by the callee and 158 /// through SP by the caller. 159 offset: u32, 160 /// The size of the [`ABIOperand`], in bytes. 161 size: u32, 162 }, 163 } 164 165 impl ABIOperand { 166 /// Allocate a new register [`ABIOperand`]. 167 pub fn reg(reg: Reg, ty: WasmType, size: u32) -> Self { 168 Self::Reg { reg, ty, size } 169 } 170 171 /// Allocate a new stack [`ABIOperand`]. 172 pub fn stack_offset(offset: u32, ty: WasmType, size: u32) -> Self { 173 Self::Stack { ty, offset, size } 174 } 175 176 /// Is this [`ABIOperand`] in a register. 177 pub fn is_reg(&self) -> bool { 178 match *self { 179 ABIOperand::Reg { .. } => true, 180 _ => false, 181 } 182 } 183 184 /// Unwraps the underlying register if it is one. 185 /// 186 /// # Panics 187 /// This function panics if the [`ABIOperand`] is not a register. 188 pub fn unwrap_reg(&self) -> Reg { 189 match self { 190 ABIOperand::Reg { reg, .. } => *reg, 191 _ => unreachable!(), 192 } 193 } 194 195 /// Get the register associated to this [`ABIOperand`]. 196 pub fn get_reg(&self) -> Option<Reg> { 197 match *self { 198 ABIOperand::Reg { reg, .. } => Some(reg), 199 _ => None, 200 } 201 } 202 203 /// Get the type associated to this [`ABIOperand`]. 204 pub fn ty(&self) -> WasmType { 205 match *self { 206 ABIOperand::Reg { ty, .. } | ABIOperand::Stack { ty, .. } => ty, 207 } 208 } 209 } 210 211 /// Information about the [`ABIOperand`] information used in [`ABISig`]. 212 #[derive(Clone, Debug)] 213 pub(crate) struct ABIOperands { 214 /// All the operands. 215 pub inner: SmallVec<[ABIOperand; 6]>, 216 /// All the registers used as operands. 217 pub regs: HashSet<Reg>, 218 /// Stack bytes used by the operands. 219 pub bytes: u32, 220 } 221 222 impl Default for ABIOperands { 223 fn default() -> Self { 224 Self { 225 inner: Default::default(), 226 regs: HashSet::with_capacity(0), 227 bytes: 0, 228 } 229 } 230 } 231 232 /// Machine stack location of the stack results. 233 #[derive(Debug, Copy, Clone)] 234 pub(crate) enum RetArea { 235 /// Addressed from SP at the given offset. 236 SP(SPOffset), 237 /// The address of the results base is stored at a particular, 238 /// well known [LocalSlot]. 239 Slot(LocalSlot), 240 } 241 242 impl RetArea { 243 /// Create a [RetArea] addressed from SP at the given offset. 244 pub fn sp(offs: SPOffset) -> Self { 245 Self::SP(offs) 246 } 247 248 /// Create a [RetArea] addressed stored at the given [LocalSlot]. 249 pub fn slot(local: LocalSlot) -> Self { 250 Self::Slot(local) 251 } 252 253 /// Returns the [SPOffset] used as the base of the return area. 254 /// 255 /// # Panics 256 /// This function panics if the return area doesn't hold a [SPOffset]. 257 pub fn unwrap_sp(&self) -> SPOffset { 258 match self { 259 Self::SP(offs) => *offs, 260 _ => unreachable!(), 261 } 262 } 263 } 264 265 /// ABI-specific representation of an [`ABISig`]. 266 #[derive(Clone, Debug, Default)] 267 pub(crate) struct ABIResults { 268 /// The result operands. 269 operands: ABIOperands, 270 } 271 272 /// Data about the [`ABIResults`]. 273 /// This struct is meant to be used once the [`ABIResults`] can be 274 /// materialized to a particular location in the machine stack, 275 /// if any. 276 #[derive(Debug, Clone)] 277 pub(crate) struct ABIResultsData { 278 /// The results. 279 pub results: ABIResults, 280 /// The return pointer, if any. 281 pub ret_area: Option<RetArea>, 282 } 283 284 impl ABIResultsData { 285 /// Create a [`ABIResultsData`] without a stack results base. 286 pub fn wrap(results: ABIResults) -> Self { 287 Self { 288 results, 289 ret_area: None, 290 } 291 } 292 293 /// Unwraps the stack results base. 294 pub fn unwrap_ret_area(&self) -> &RetArea { 295 self.ret_area.as_ref().unwrap() 296 } 297 } 298 299 impl ABIResults { 300 /// Creates [`ABIResults`] from a slice of `WasmType`. 301 /// This function maps the given return types to their ABI specific 302 /// representation. It does so, by iterating over them and applying the 303 /// given `map` closure. The map closure takes a [WasmType], maps its ABI 304 /// representation, according to the calling convention. In the case of 305 /// results, one result is stored in registers and the rest at particular 306 /// offsets in the stack. 307 pub fn from<F>(returns: &[WasmType], call_conv: &CallingConvention, mut map: F) -> Self 308 where 309 F: FnMut(&WasmType, u32) -> (ABIOperand, u32), 310 { 311 if returns.len() == 0 { 312 return Self::default(); 313 } 314 315 type FoldTuple = (SmallVec<[ABIOperand; 6]>, HashSet<Reg>, u32); 316 317 let fold_impl = |(mut operands, mut regs, stack_bytes): FoldTuple, arg| { 318 let (operand, bytes) = map(arg, stack_bytes); 319 if operand.is_reg() { 320 regs.insert(operand.unwrap_reg()); 321 } 322 operands.push(operand); 323 (operands, regs, bytes) 324 }; 325 326 // When dealing with multiple results, Winch's calling convention stores the 327 // last return value in a register rather than the first one. In that 328 // sense, Winch's return values in the ABI signature are "reversed" in 329 // terms of storage. This technique is particularly helpful to ensure that 330 // the following invariants are maintained: 331 // * Spilled memory values always precede register values 332 // * Spilled values are stored from oldest to newest, matching their 333 // respective locations on the machine stack. 334 let (mut operands, regs, bytes): FoldTuple = if call_conv.is_default() { 335 returns 336 .iter() 337 .rev() 338 .fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl) 339 } else { 340 returns 341 .iter() 342 .fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl) 343 }; 344 345 // Similar to above, we reverse the result of the operands calculation 346 // to ensure that they match the declared order. 347 if call_conv.is_default() { 348 operands.reverse(); 349 } 350 351 Self::new(ABIOperands { 352 inner: operands, 353 regs, 354 bytes, 355 }) 356 } 357 358 /// Create a new [`ABIResults`] from [`ABIOperands`]. 359 pub fn new(operands: ABIOperands) -> Self { 360 Self { operands } 361 } 362 363 /// Returns a reference to a [HashSet<Reg>], which includes 364 /// all the registers used to hold function results. 365 pub fn regs(&self) -> &HashSet<Reg> { 366 &self.operands.regs 367 } 368 369 /// Get a slice over all the result [`ABIOperand`]s. 370 pub fn operands(&self) -> &[ABIOperand] { 371 &self.operands.inner 372 } 373 374 /// Returns the length of the result. 375 pub fn len(&self) -> usize { 376 self.operands.inner.len() 377 } 378 379 /// Get the [`ABIOperand`] result in the nth position. 380 #[cfg(test)] 381 pub fn get(&self, n: usize) -> Option<&ABIOperand> { 382 self.operands.inner.get(n) 383 } 384 385 /// Returns the first [`ABIOperand`]. 386 /// Useful in situations where the function signature is known to 387 /// have a single return. 388 /// 389 /// # Panics 390 /// This function panics if the function signature contains more 391 pub fn unwrap_singleton(&self) -> &ABIOperand { 392 debug_assert_eq!(self.len(), 1); 393 &self.operands.inner[0] 394 } 395 396 /// Returns the size, in bytes of all the [`ABIOperand`]s in the stack. 397 pub fn size(&self) -> u32 { 398 self.operands.bytes 399 } 400 401 /// Returns true if the [`ABIResults`] require space on the machine stack 402 /// for results. 403 pub fn has_stack_results(&self) -> bool { 404 self.operands.bytes > 0 405 } 406 } 407 408 /// ABI-specific representation of an [`ABISig`]. 409 #[derive(Debug, Clone, Default)] 410 pub(crate) struct ABIParams { 411 /// The param operands. 412 operands: ABIOperands, 413 /// Whether [`ABIParams`] contains an extra paramter for the stack 414 /// result area. 415 has_retptr: bool, 416 } 417 418 impl ABIParams { 419 /// Creates [`ABIParams`] from a slice of `WasmType`. 420 /// This function maps the given param types to their ABI specific 421 /// representation. It does so, by iterating over them and applying the 422 /// given `map` closure. The map closure takes a [WasmType], maps its ABI 423 /// representation, according to the calling convention. In the case of 424 /// params, multiple params may be passed in registers and the rest on the 425 /// stack depending on the calling convention. 426 pub fn from<F, A: ABI>( 427 params: &[WasmType], 428 initial_bytes: u32, 429 needs_stack_results: bool, 430 mut map: F, 431 ) -> Self 432 where 433 F: FnMut(&WasmType, u32) -> (ABIOperand, u32), 434 { 435 if params.len() == 0 && !needs_stack_results { 436 return Self::with_bytes(initial_bytes); 437 } 438 439 let regiser_capacity = params.len().min(6); 440 let (mut operands, mut regs, mut stack_bytes): ( 441 SmallVec<[ABIOperand; 6]>, 442 HashSet<Reg>, 443 u32, 444 ) = params.iter().fold( 445 ( 446 SmallVec::new(), 447 HashSet::with_capacity(regiser_capacity), 448 initial_bytes, 449 ), 450 |(mut operands, mut regs, stack_bytes), arg| { 451 let (operand, bytes) = map(arg, stack_bytes); 452 if operand.is_reg() { 453 regs.insert(operand.unwrap_reg()); 454 } 455 operands.push(operand); 456 (operands, regs, bytes) 457 }, 458 ); 459 460 let ptr_type = ptr_type_from_ptr_size(<A as ABI>::word_bytes() as u8); 461 // Handle stack results by specifying an extra, implicit last argument. 462 if needs_stack_results { 463 let (operand, bytes) = map(&ptr_type, stack_bytes); 464 if operand.is_reg() { 465 regs.insert(operand.unwrap_reg()); 466 } 467 operands.push(operand); 468 stack_bytes = bytes; 469 } 470 471 Self { 472 operands: ABIOperands { 473 inner: operands, 474 regs, 475 bytes: stack_bytes, 476 }, 477 has_retptr: needs_stack_results, 478 } 479 } 480 481 /// Creates new [`ABIParams`], with the specified amount of stack bytes. 482 pub fn with_bytes(bytes: u32) -> Self { 483 let mut params = Self::default(); 484 params.operands.bytes = bytes; 485 params 486 } 487 488 /// Get the [`ABIOperand`] param in the nth position. 489 pub fn get(&self, n: usize) -> Option<&ABIOperand> { 490 self.operands.inner.get(n) 491 } 492 493 /// Get a slice over all the parameter [`ABIOperand`]s. 494 pub fn operands(&self) -> &[ABIOperand] { 495 &self.operands.inner 496 } 497 498 /// Returns the length of the params, including the return pointer, 499 /// if any. 500 pub fn len(&self) -> usize { 501 self.operands.inner.len() 502 } 503 504 /// Returns the length of the params, excluding the return pointer, 505 /// if any. 506 pub fn len_without_retptr(&self) -> usize { 507 if self.has_retptr { 508 self.len() - 1 509 } else { 510 self.len() 511 } 512 } 513 514 /// Returns true if the [ABISig] has an extra parameter for stack results. 515 pub fn has_retptr(&self) -> bool { 516 self.has_retptr 517 } 518 519 /// Returns the last [ABIOperand] used as the pointer to the 520 /// stack results area. 521 /// 522 /// # Panics 523 /// This function panics if the [ABIParams] doesn't have a stack results 524 /// parameter. 525 pub fn unwrap_results_area_operand(&self) -> &ABIOperand { 526 debug_assert!(self.has_retptr); 527 self.operands.inner.last().unwrap() 528 } 529 } 530 531 /// An ABI-specific representation of a function signature. 532 #[derive(Debug, Clone, Default)] 533 pub(crate) struct ABISig { 534 /// Function parameters. 535 pub params: ABIParams, 536 /// Function result. 537 pub results: ABIResults, 538 /// A unique set of registers used in the entire [`ABISig`]. 539 pub regs: HashSet<Reg>, 540 } 541 542 impl ABISig { 543 /// Create a new ABI signature. 544 pub fn new(params: ABIParams, results: ABIResults) -> Self { 545 let regs = params 546 .operands 547 .regs 548 .union(&results.operands.regs) 549 .copied() 550 .collect(); 551 Self { 552 params, 553 results, 554 regs, 555 } 556 } 557 558 /// Returns an iterator over all the parameter operands. 559 pub fn params(&self) -> &[ABIOperand] { 560 self.params.operands() 561 } 562 563 /// Returns an iterator over all the result operands. 564 pub fn results(&self) -> &[ABIOperand] { 565 self.results.operands() 566 } 567 568 /// Returns a slice over the signature params, excluding the results 569 /// base paramter, if any. 570 pub fn params_without_retptr(&self) -> &[ABIOperand] { 571 if self.params.has_retptr() { 572 &self.params()[0..(self.params.len() - 1)] 573 } else { 574 self.params() 575 } 576 } 577 578 /// Returns the stack size, in bytes, needed for arguments on the stack. 579 pub fn params_stack_size(&self) -> u32 { 580 self.params.operands.bytes 581 } 582 583 /// Returns the stack size, in bytes, needed for results on the stack. 584 pub fn results_stack_size(&self) -> u32 { 585 self.results.operands.bytes 586 } 587 588 /// Returns true if the signature has results on the stack. 589 pub fn has_stack_results(&self) -> bool { 590 self.results.has_stack_results() 591 } 592 } 593 594 /// Align a value up to the given power-of-two-alignment. 595 // See https://sites.google.com/site/theoryofoperatingsystems/labs/malloc/align8 596 pub(crate) fn align_to<N>(value: N, alignment: N) -> N 597 where 598 N: Not<Output = N> 599 + BitAnd<N, Output = N> 600 + Add<N, Output = N> 601 + Sub<N, Output = N> 602 + From<u8> 603 + Copy, 604 { 605 let alignment_mask = alignment - 1.into(); 606 (value + alignment_mask) & !alignment_mask 607 } 608 609 /// Calculates the delta needed to adjust a function's frame plus some 610 /// addend to a given alignment. 611 pub(crate) fn calculate_frame_adjustment(frame_size: u32, addend: u32, alignment: u32) -> u32 { 612 let total = frame_size + addend; 613 (alignment - (total % alignment)) % alignment 614 } 615