winch(x64): Initial implementation for function calls (#6067)

* winch(x64): Initial implementation for function calls

This change adds the main building blocks for calling locally defined
functions. Support for function imports will be added iteratively after this
change lands and once trampolines are supported.

To support function calls, this change introduces the following functionality to
the MacroAssembler:

* `pop` to pop the machine stack into a given register, which in the case of
this change, translates to the x64 pop instruction.

* `call` to a emit a call to locally defined functions.

* `address_from_sp` to construct memory addresses with the SP as a base.

* `free_stack` to emit the necessary instrunctions to claim stack space.

The heavy lifting of setting up and emitting the function call is done through
the implementation of `FnCall`.

* Fix spill behaviour in function calls and add more documentation

This commits adds a more detailed documentation to the `call.rs` module.

It also fixes a couple of bugs, mainly:

* The previous commit didn't account for memory addresses used as arguments for
the function call, any memory entry in the value stack used as a function
argument should be tracked and then used to claim that memory when the function
call ends. We could `pop` and do this implicitly, but we can also track this
down and emit a single instruction to decrement the stack pointer, which will
result in better code.

* Introduce a differentiator between addresses relative or absolute to the stack
pointer. When passing arguments in the stack -- assuming that SP at that point
is aligned for the function call -- we should store the arguments relative to
the absolute position of the stack pointer and when addressing a memory entry in
the Wasm value stack, we should use an address relative to the offset and the
position of the stack pointer.

* Simplify tracking of the stack space needed for emitting a function call
This commit is contained in:
Saúl Cabrera
2023-03-28 14:30:31 -04:00
committed by GitHub
parent d54c00ba4d
commit af4d94c85a
22 changed files with 737 additions and 68 deletions

View File

@@ -1,7 +1,7 @@
use wasmparser::ValType;
/// Base register used to address the local slot.
///
/// Slots for stack arguments are addressed from the frame pointer
/// Slots for stack arguments are addressed from the frame pointer.
/// Slots for function-defined locals and for registers are addressed
/// from the stack pointer.
#[derive(Eq, PartialEq)]

View File

@@ -1,3 +1,47 @@
//! This module provides all the necessary building blocks for
//! implementing ISA specific ABIs.
//!
//! # Default ABI
//!
//! Winch uses a default internal ABI, for all internal functions.
//! This allows us to push the complexity of system ABI compliance to
//! the trampolines (not yet implemented). The default ABI treats all
//! allocatable registers as caller saved, which means that (i) all
//! register values in the Wasm value stack (which are normally
//! referred to as "live"), must be saved onto the machine stack (ii)
//! function prologues and epilogues don't store/restore other
//! registers more than the non-allocatable ones (e.g. rsp/rbp in
//! x86_64).
//!
//! The calling convention in the default ABI, uses registers to a
//! certain fixed count for arguments and return values, and then the
//! stack is used for all additional arguments.
//!
//! Generally the stack layout looks like:
//! +-------------------------------+
//! | |
//! | |
//! | Stack Args |
//! | |
//! | |
//! +-------------------------------+----> SP @ function entry
//! | Ret addr |
//! +-------------------------------+
//! | SP |
//! +-------------------------------+----> SP @ Function prologue
//! | |
//! | |
//! | |
//! | Stack slots |
//! | + dynamic space |
//! | |
//! | |
//! | |
//! +-------------------------------+----> SP @ callsite (after)
//! | alignment |
//! | + arguments |
//! | | ----> Space allocated for calls
//! | |
use crate::isa::reg::Reg;
use smallvec::SmallVec;
use std::ops::{Add, BitAnd, Not, Sub};
@@ -13,6 +57,9 @@ pub(crate) trait ABI {
/// The required stack alignment.
fn stack_align(&self) -> u8;
/// The required stack alignment for calls.
fn call_stack_align(&self) -> u8;
/// The offset to the argument base, relative to the frame pointer.
fn arg_base_offset(&self) -> u8;
@@ -117,11 +164,27 @@ impl ABIResult {
}
}
pub(crate) type ABIParams = SmallVec<[ABIArg; 6]>;
/// An ABI-specific representation of a function signature.
pub(crate) struct ABISig {
/// Function parameters.
pub params: SmallVec<[ABIArg; 6]>,
pub params: ABIParams,
/// Function result.
pub result: ABIResult,
/// Stack space needed for stack arguments.
pub stack_bytes: u32,
}
impl ABISig {
/// Create a new ABI signature.
pub fn new(params: ABIParams, result: ABIResult, stack_bytes: u32) -> Self {
Self {
params,
result,
stack_bytes,
}
}
}
/// Returns the size in bytes of a given WebAssembly type.