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:
Saúl Cabrera
2023-01-18 06:58:13 -05:00
committed by GitHub
parent 1e6c13d83e
commit 94b51cdb17
22 changed files with 533 additions and 383 deletions

View File

@@ -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 {