Skip to content

[Wasm RyuJIT] Calling Convention (Stub generator) #121215

@kg

Description

@kg

Calling Convention

Wasm RyuJit needs a robust calling convention implementation in order for managed-managed, managed-native, and native-managed calls to work correctly.

A few different problems have to be solved. Some of them will be solved later, during the Interop/Pinvoke design and development process, but we have to take them into account early.

Stub generation

Certain types of transitions will require the assistance of generated stubs in some cases, for example when a delegate or virtual call ends up transitioning into the interpreter. We need a system for generating these stubs and a mechanism to locate the generated stubs and use them.

Parameters for 'hidden arguments' and special registers

Our calling convention on other targets relies on the ability to smuggle special values through extra registers alongside formal parameters in the registers normally used for parameter values. We have to make all these special values formal arguments or store them in globals, which requires identifying which methods use which special values, and making sure that we pass them at the right call sites. This complicates stub generation as well.

Smuggling hidden arguments across the managed/native boundary

While managed callers will be passing the right values at the right places, native callers have no way to know about these special values. On other targets we use generated stubs to load the special values into the appropriate registers before calling the managed target. The same cannot be done here, so we will rely on a mixture of stub logic and careful managed codegen to make these scenarios work.

Parameter for emulated stack

Because wasm does not expose a way to walk the stack, we need to maintain an emulated stack by pushing and popping frames as we enter/exit functions. Storing this emulated stack pointer in a global is possible, but research by @SingleAccretion indicates that it causes significant regressions to code size compared to passing the stack pointer around as a parameter. So we likely need to pass this emulated stack pointer from function to function when performing calls (and smuggle it across managed/native boundaries like the other hidden arguments). The implementation of this emulated stack is a separate task.

General description of Wasm calling convention

Functions in wasm return 0-1 values and accept 0-N formal parameters, where the return value and parameters are of known types like i32 or f64.

At every call site you either specify a known call target for call (which has a known signature), or specify a signature + a function pointer (for call_indirect). As a result due to the known signature you have to push a specific set of types onto the evaluation stack before performing a call. The callee receives the arguments as local variables, numbered starting from 0 (preceding the actual declared locals in the function).

(incomplete - work in progress)

Metadata

Metadata

Assignees

No one assigned

    Labels

    arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions