winch: Use cranelift-codegen x64 backend for emission. (#5581)
This change substitutes the string based emission mechanism with
cranelift-codegen's x64 backend.
This change _does not_:
* Introduce new functionality in terms of supported instructions.
* Change the semantics of the assembler/macroassembler in terms of the logic to
emit instructions.
The most notable differences between this change and the previous version are:
* Handling of shared flags and ISA-specific flags, which for now are left with
the default value.
* Simplification of instruction emission per operand size: previously the
assembler defined different methods depending on the operand size (e.g. `mov`
for 64 bits, and `movl` for 32 bits). This change updates such approach so that
each assembler method takes an operand size as a parameter, reducing duplication
and making the code more concise and easier to integrate with the x64's `Inst` enum.
* Introduction of a disassembler for testing purposes.
As of this change, Winch generates the following code for the following test
programs:
```wat
(module
(export "main" (func $main))
(func $main (result i32)
(i32.const 10)
(i32.const 20)
i32.add
))
```
```asm
0: 55 push rbp
1: 48 89 e5 mov rbp, rsp
4: b8 0a 00 00 00 mov eax, 0xa
9: 83 c0 14 add eax, 0x14
c: 5d pop rbp
d: c3 ret
```
```wat
(module
(export "main" (func $main))
(func $main (result i32)
(local $foo i32)
(local $bar i32)
(i32.const 10)
(local.set $foo)
(i32.const 20)
(local.set $bar)
(local.get $foo)
(local.get $bar)
i32.add
))
```
```asm
0: 55 push rbp
1: 48 89 e5 mov rbp, rsp
4: 48 83 ec 08 sub rsp, 8
8: 48 c7 04 24 00 00 00 00 mov qword ptr [rsp], 0
10: b8 0a 00 00 00 mov eax, 0xa
15: 89 44 24 04 mov dword ptr [rsp + 4], eax
19: b8 14 00 00 00 mov eax, 0x14
1e: 89 04 24 mov dword ptr [rsp], eax
21: 8b 04 24 mov eax, dword ptr [rsp]
24: 8b 4c 24 04 mov ecx, dword ptr [rsp + 4]
28: 01 c1 add ecx, eax
2a: 48 89 c8 mov rax, rcx
2d: 48 83 c4 08 add rsp, 8
31: 5d pop rbp
32: c3 ret
```
```wat
(module
(export "main" (func $main))
(func $main (param i32) (param i32) (result i32)
(local.get 0)
(local.get 1)
i32.add
))
```
```asm
0: 55 push rbp
1: 48 89 e5 mov rbp, rsp
4: 48 83 ec 08 sub rsp, 8
8: 89 7c 24 04 mov dword ptr [rsp + 4], edi
c: 89 34 24 mov dword ptr [rsp], esi
f: 8b 04 24 mov eax, dword ptr [rsp]
12: 8b 4c 24 04 mov ecx, dword ptr [rsp + 4]
16: 01 c1 add ecx, eax
18: 48 89 c8 mov rax, rcx
1b: 48 83 c4 08 add rsp, 8
1f: 5d pop rbp
20: c3 ret
```
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use core::fmt::Formatter;
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use cranelift_codegen::{isa::CallConv, settings, Final, MachBufferFinalized};
|
||||
use std::{
|
||||
error,
|
||||
fmt::{self, Debug, Display},
|
||||
@@ -16,11 +16,11 @@ pub(crate) mod aarch64;
|
||||
|
||||
pub(crate) mod reg;
|
||||
|
||||
macro_rules! isa {
|
||||
macro_rules! isa_builder {
|
||||
($name: ident, $cfg_terms: tt, $triple: ident) => {{
|
||||
#[cfg $cfg_terms]
|
||||
{
|
||||
Ok(Box::new($name::isa_from($triple)))
|
||||
Ok($name::isa_builder($triple))
|
||||
}
|
||||
#[cfg(not $cfg_terms)]
|
||||
{
|
||||
@@ -29,23 +29,33 @@ macro_rules! isa {
|
||||
}};
|
||||
}
|
||||
|
||||
/// Look for an ISA for the given target triple.
|
||||
//
|
||||
// The ISA, as it's currently implemented in Cranelift
|
||||
// needs a builder since it adds settings
|
||||
// depending on those available in the host architecture.
|
||||
// I'm intentionally skipping the builder for now.
|
||||
// The lookup method will return the ISA directly.
|
||||
//
|
||||
// Once features like SIMD are supported, returning a builder
|
||||
// will make more sense.
|
||||
pub fn lookup(triple: Triple) -> Result<Box<dyn TargetIsa>> {
|
||||
/// The target ISA builder.
|
||||
#[derive(Clone)]
|
||||
pub struct Builder {
|
||||
/// The target triple.
|
||||
triple: Triple,
|
||||
/// The ISA settings builder.
|
||||
settings: settings::Builder,
|
||||
/// The Target ISA constructor.
|
||||
constructor: fn(Triple, settings::Flags, settings::Builder) -> Result<Box<dyn TargetIsa>>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Create a TargetIsa by combining ISA-specific settings with the provided
|
||||
/// shared flags.
|
||||
pub fn build(self, shared_flags: settings::Flags) -> Result<Box<dyn TargetIsa>> {
|
||||
(self.constructor)(self.triple, shared_flags, self.settings)
|
||||
}
|
||||
}
|
||||
|
||||
/// Look for an ISA builder for the given target triple.
|
||||
pub fn lookup(triple: Triple) -> Result<Builder> {
|
||||
match triple.architecture {
|
||||
Architecture::X86_64 => {
|
||||
isa!(x64, (feature = "x64"), triple)
|
||||
isa_builder!(x64, (feature = "x64"), triple)
|
||||
}
|
||||
Architecture::Aarch64 { .. } => {
|
||||
isa!(aarch64, (feature = "arm64"), triple)
|
||||
isa_builder!(aarch64, (feature = "arm64"), triple)
|
||||
}
|
||||
|
||||
_ => Err(anyhow!(LookupError::Unsupported)),
|
||||
@@ -87,7 +97,7 @@ pub trait TargetIsa: Send + Sync {
|
||||
sig: &FuncType,
|
||||
body: &FunctionBody,
|
||||
validator: FuncValidator<ValidatorResources>,
|
||||
) -> Result<Vec<String>>;
|
||||
) -> Result<MachBufferFinalized<Final>>;
|
||||
|
||||
/// Get the default calling convention of the underlying target triple.
|
||||
fn call_conv(&self) -> CallConv {
|
||||
|
||||
Reference in New Issue
Block a user