Creates [`ABIResults`] from a slice of `WasmType`. This function maps the given return types to their ABI specific representation. It does so, by iterating over them and applying the given `map` closure. The map closure takes a [WasmValType], maps its ABI representation, according to the calling convention. In the case of results, one result is stored in registers and the rest at particular offset
(
returns: &[WasmValType],
call_conv: &CallingConvention,
mut map: F,
)
| 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`]. |
nothing calls this directly
no test coverage detected