winch: Initial integration with wasmtime (#6119)

* Adding in trampoline compiling method for ISA

* Adding support for indirect call to memory address

* Refactoring frame to externalize defined locals, so it removes WASM depedencies in trampoline case

* Adding initial version of trampoline for testing

* Refactoring trampoline to be re-used by other architectures

* Initial wiring for winch with wasmtime

* Add a Wasmtime CLI option to select `winch`

This is effectively an option to select the `Strategy` enumeration.

* Implement `Compiler::compile_function` for Winch

Hook this into the `TargetIsa::compile_function` hook as well. Currently
this doesn't take into account `Tunables`, but that's left as a TODO for
later.

* Filling out Winch append_code method

* Adding back in changes from previous branch

Most of these are a WIP. It's missing trampolines for x64, but a basic
one exists for aarch64. It's missing the handling of arguments that
exist on the stack.

It currently imports `cranelift_wasm::WasmFuncType` since it's what's
passed to the `Compiler` trait. It's a bit awkward to use in the
`winch_codegen` crate since it mostly operates on `wasmparser` types.
I've had to hack in a conversion to get things working. Long term, I'm
not sure it's wise to rely on this type but it seems like it's easier on
the Cranelift side when creating the stub IR.

* Small API changes to make integration easier

* Adding in new FuncEnv, only a stub for now

* Removing unneeded parts of the old PoC, and refactoring trampoline code

* Moving FuncEnv into a separate file

* More comments for trampolines

* Adding in winch integration tests for first pass

* Using new addressing method to fix stack pointer error

* Adding test for stack arguments

* Only run tests on x86 for now, it's more complete for winch

* Add in missing documentation after rebase

* Updating based on feedback in draft PR

* Fixing formatting on doc comment for argv register

* Running formatting

* Lock updates, and turning on winch feature flags during tests

* Updating configuration with comments to no longer gate Strategy enum

* Using the winch-environ FuncEnv, but it required changing the sig

* Proper comment formatting

* Removing wasmtime-winch from dev-dependencies, adding the winch feature makes this not necessary

* Update doc attr to include winch check

* Adding winch feature to doc generation, which seems to fix the feature error in CI

* Add the `component-model` feature to the cargo doc invocation in CI

To match the metadata used by the docs.rs invocation when building docs.

* Add a comment clarifying the usage of `component-model` for docs.rs

* Correctly order wasmtime-winch and winch-environ in the publish script

* Ensure x86 test dependencies are included in cfg(target_arch)

* Further constrain Winch tests to x86_64 _and_ unix

---------

Co-authored-by: Alex Crichton <alex@alexcrichton.com>
Co-authored-by: Saúl Cabrera <saulecabrera@gmail.com>
This commit is contained in:
Kevin Rizzo
2023-04-04 20:32:40 -04:00
committed by GitHub
parent 81545c3a86
commit 3a92aa3d0a
36 changed files with 663 additions and 143 deletions

View File

@@ -7,7 +7,7 @@ use crate::{
abi::local::LocalSlot,
codegen::CodeGenContext,
isa::reg::Reg,
masm::{DivKind, MacroAssembler as Masm, OperandSize, RegImm, RemKind},
masm::{CalleeKind, DivKind, MacroAssembler as Masm, OperandSize, RegImm, RemKind},
};
use cranelift_codegen::{settings, Final, MachBufferFinalized};
@@ -136,7 +136,7 @@ impl Masm for MacroAssembler {
self.asm.str(src, dst, size);
}
fn call(&mut self, _callee: u32) {
fn call(&mut self, _callee: CalleeKind) {
todo!()
}
@@ -148,7 +148,7 @@ impl Masm for MacroAssembler {
todo!()
}
fn sp_offset(&mut self) -> u32 {
fn sp_offset(&self) -> u32 {
self.sp_offset
}
@@ -194,6 +194,10 @@ impl Masm for MacroAssembler {
self.sp_offset
}
fn address_from_reg(&self, reg: Reg, offset: u32) -> Self::Address {
Address::offset(reg, offset as i64)
}
}
impl MacroAssembler {

View File

@@ -2,7 +2,7 @@ use self::regs::{scratch, ALL_GPR};
use crate::{
abi::ABI,
codegen::{CodeGen, CodeGenContext},
frame::Frame,
frame::{DefinedLocals, Frame},
isa::{Builder, TargetIsa},
masm::MacroAssembler,
regalloc::RegAlloc,
@@ -86,14 +86,16 @@ impl TargetIsa for Aarch64 {
sig: &FuncType,
body: &FunctionBody,
env: &dyn FuncEnv,
mut validator: FuncValidator<ValidatorResources>,
validator: &mut FuncValidator<ValidatorResources>,
) -> Result<MachBufferFinalized<Final>> {
let mut body = body.get_binary_reader();
let mut masm = Aarch64Masm::new(self.shared_flags.clone());
let stack = Stack::new();
let abi = abi::Aarch64ABI::default();
let abi_sig = abi.sig(sig);
let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?;
let defined_locals = DefinedLocals::new(&mut body, validator)?;
let frame = Frame::new(&abi_sig, &defined_locals, &abi)?;
// TODO: Add floating point bitmask
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), scratch());
let codegen_context = CodeGenContext::new(regalloc, stack, &frame);
@@ -113,4 +115,8 @@ impl TargetIsa for Aarch64 {
// See `cranelift_codegen::isa::TargetIsa::function_alignment`.
32
}
fn host_to_wasm_trampoline(&self, _ty: &FuncType) -> Result<MachBufferFinalized<Final>> {
todo!()
}
}

View File

@@ -90,12 +90,13 @@ pub trait TargetIsa: Send + Sync {
false
}
/// Compile a function.
fn compile_function(
&self,
sig: &FuncType,
body: &FunctionBody,
env: &dyn FuncEnv,
validator: FuncValidator<ValidatorResources>,
validator: &mut FuncValidator<ValidatorResources>,
) -> Result<MachBufferFinalized<Final>>;
/// Get the default calling convention of the underlying target triple.
@@ -119,6 +120,9 @@ pub trait TargetIsa: Send + Sync {
/// See `cranelift_codegen::isa::TargetIsa::function_alignment`.
fn function_alignment(&self) -> u32;
/// Generate a trampoline that can be used to call a wasm function from wasmtime.
fn host_to_wasm_trampoline(&self, ty: &FuncType) -> Result<MachBufferFinalized<Final>>;
}
impl Debug for &dyn TargetIsa {

View File

@@ -2,7 +2,7 @@
use crate::{
isa::reg::Reg,
masm::{DivKind, OperandSize, RemKind},
masm::{CalleeKind, DivKind, OperandSize, RemKind},
};
use cranelift_codegen::{
entity::EntityRef,
@@ -469,17 +469,31 @@ impl Assembler {
});
}
/// Direct function call to a user defined function.
pub fn call(&mut self, callee: u32) {
let dest = ExternalName::user(UserExternalNameRef::new(callee as usize));
self.emit(Inst::CallKnown {
dest,
info: Box::new(CallInfo {
uses: smallvec![],
defs: smallvec![],
clobbers: Default::default(),
opcode: Opcode::Call,
}),
});
pub fn call(&mut self, callee: CalleeKind) {
match callee {
CalleeKind::Indirect(reg) => {
self.emit(Inst::CallUnknown {
dest: RegMem::reg(reg.into()),
info: Box::new(CallInfo {
uses: smallvec![],
defs: smallvec![],
clobbers: Default::default(),
opcode: Opcode::Call,
}),
});
}
CalleeKind::Direct(index) => {
let dest = ExternalName::user(UserExternalNameRef::new(index as usize));
self.emit(Inst::CallKnown {
dest,
info: Box::new(CallInfo {
uses: smallvec![],
defs: smallvec![],
clobbers: Default::default(),
opcode: Opcode::Call,
}),
});
}
}
}
}

View File

@@ -3,9 +3,9 @@ use super::{
asm::{Assembler, Operand},
regs::{self, rbp, rsp},
};
use crate::isa::reg::Reg;
use crate::masm::{DivKind, MacroAssembler as Masm, OperandSize, RegImm, RemKind};
use crate::{abi::LocalSlot, codegen::CodeGenContext, stack::Val};
use crate::{isa::reg::Reg, masm::CalleeKind};
use cranelift_codegen::{isa::x64::settings as x64_settings, settings, Final, MachBufferFinalized};
/// x64 MacroAssembler.
@@ -114,7 +114,7 @@ impl Masm for MacroAssembler {
self.decrement_sp(8);
}
fn call(&mut self, callee: u32) {
fn call(&mut self, callee: CalleeKind) {
self.asm.call(callee);
}
@@ -124,7 +124,7 @@ impl Masm for MacroAssembler {
self.asm.mov(src, dst, size);
}
fn sp_offset(&mut self) -> u32 {
fn sp_offset(&self) -> u32 {
self.sp_offset
}
@@ -236,6 +236,10 @@ impl Masm for MacroAssembler {
fn finalize(self) -> MachBufferFinalized<Final> {
self.asm.finalize()
}
fn address_from_reg(&self, reg: Reg, offset: u32) -> Self::Address {
Address::offset(reg, offset)
}
}
impl MacroAssembler {

View File

@@ -1,10 +1,14 @@
use crate::abi::ABI;
use crate::codegen::{CodeGen, CodeGenContext};
use crate::frame::Frame;
use crate::{
abi::ABI,
codegen::{CodeGen, CodeGenContext},
};
use crate::frame::{DefinedLocals, Frame};
use crate::isa::x64::masm::MacroAssembler as X64Masm;
use crate::masm::MacroAssembler;
use crate::regalloc::RegAlloc;
use crate::stack::Stack;
use crate::trampoline::Trampoline;
use crate::FuncEnv;
use crate::{
isa::{Builder, TargetIsa},
@@ -87,14 +91,16 @@ impl TargetIsa for X64 {
sig: &FuncType,
body: &FunctionBody,
env: &dyn FuncEnv,
mut validator: FuncValidator<ValidatorResources>,
validator: &mut FuncValidator<ValidatorResources>,
) -> Result<MachBufferFinalized<Final>> {
let mut body = body.get_binary_reader();
let mut masm = X64Masm::new(self.shared_flags.clone(), self.isa_flags.clone());
let stack = Stack::new();
let abi = abi::X64ABI::default();
let abi_sig = abi.sig(sig);
let frame = Frame::new(&abi_sig, &mut body, &mut validator, &abi)?;
let defined_locals = DefinedLocals::new(&mut body, validator)?;
let frame = Frame::new(&abi_sig, &defined_locals, &abi)?;
// TODO Add in floating point bitmask
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), regs::scratch());
let codegen_context = CodeGenContext::new(regalloc, stack, &frame);
@@ -113,4 +119,15 @@ impl TargetIsa for X64 {
// See `cranelift_codegen`'s value of this for more information.
16
}
fn host_to_wasm_trampoline(&self, ty: &FuncType) -> Result<MachBufferFinalized<Final>> {
let abi = abi::X64ABI::default();
let mut masm = X64Masm::new(self.shared_flags.clone(), self.isa_flags.clone());
let mut trampoline = Trampoline::new(&mut masm, &abi, regs::scratch(), regs::argv());
trampoline.emit_host_to_wasm(ty);
Ok(masm.finalize())
}
}

View File

@@ -81,6 +81,18 @@ pub(crate) fn scratch() -> Reg {
r11()
}
/// This register is used as a scratch register, in the context of trampolines only,
/// where we assume that callee-saved registers are given the correct handling
/// according to the system ABI. r12 is chosen given that it's a callee-saved,
/// non-argument register.
///
/// In the context of all other internal functions, this register is not excluded
/// from register allocation, so no extra assumptions should be made regarding
/// its availability.
pub(crate) fn argv() -> Reg {
r12()
}
fn fpr(enc: u8) -> Reg {
Reg::new(PReg::new(enc as usize, RegClass::Float))
}