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
```
123 lines
3.4 KiB
Rust
123 lines
3.4 KiB
Rust
use anyhow::{anyhow, Result};
|
|
use core::fmt::Formatter;
|
|
use cranelift_codegen::{isa::CallConv, settings, Final, MachBufferFinalized};
|
|
use std::{
|
|
error,
|
|
fmt::{self, Debug, Display},
|
|
};
|
|
use target_lexicon::{Architecture, Triple};
|
|
use wasmparser::{FuncType, FuncValidator, FunctionBody, ValidatorResources};
|
|
|
|
#[cfg(feature = "x64")]
|
|
pub(crate) mod x64;
|
|
|
|
#[cfg(feature = "arm64")]
|
|
pub(crate) mod aarch64;
|
|
|
|
pub(crate) mod reg;
|
|
|
|
macro_rules! isa_builder {
|
|
($name: ident, $cfg_terms: tt, $triple: ident) => {{
|
|
#[cfg $cfg_terms]
|
|
{
|
|
Ok($name::isa_builder($triple))
|
|
}
|
|
#[cfg(not $cfg_terms)]
|
|
{
|
|
Err(anyhow!(LookupError::SupportDisabled))
|
|
}
|
|
}};
|
|
}
|
|
|
|
/// 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_builder!(x64, (feature = "x64"), triple)
|
|
}
|
|
Architecture::Aarch64 { .. } => {
|
|
isa_builder!(aarch64, (feature = "arm64"), triple)
|
|
}
|
|
|
|
_ => Err(anyhow!(LookupError::Unsupported)),
|
|
}
|
|
}
|
|
|
|
impl error::Error for LookupError {}
|
|
impl Display for LookupError {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
LookupError::Unsupported => write!(f, "This target is not supported yet"),
|
|
LookupError::SupportDisabled => write!(f, "Support for this target was disabled"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) enum LookupError {
|
|
Unsupported,
|
|
// This directive covers the case in which the consumer
|
|
// enables the `all-arch` feature; in such case, this variant
|
|
// will never be used. This is most likely going to change
|
|
// in the future; this is one of the simplest options for now.
|
|
#[allow(dead_code)]
|
|
SupportDisabled,
|
|
}
|
|
|
|
/// A trait representing commonalities between the supported
|
|
/// instruction set architectures.
|
|
pub trait TargetIsa: Send + Sync {
|
|
/// Get the name of the ISA.
|
|
fn name(&self) -> &'static str;
|
|
|
|
/// Get the target triple of the ISA.
|
|
fn triple(&self) -> &Triple;
|
|
|
|
fn compile_function(
|
|
&self,
|
|
sig: &FuncType,
|
|
body: &FunctionBody,
|
|
validator: FuncValidator<ValidatorResources>,
|
|
) -> Result<MachBufferFinalized<Final>>;
|
|
|
|
/// Get the default calling convention of the underlying target triple.
|
|
fn call_conv(&self) -> CallConv {
|
|
CallConv::triple_default(&self.triple())
|
|
}
|
|
|
|
/// Get the endianess of the underlying target triple.
|
|
fn endianness(&self) -> target_lexicon::Endianness {
|
|
self.triple().endianness().unwrap()
|
|
}
|
|
}
|
|
|
|
impl Debug for &dyn TargetIsa {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
write!(
|
|
f,
|
|
"Target ISA {{ triple: {:?}, calling convention: {:?} }}",
|
|
self.triple(),
|
|
self.call_conv()
|
|
)
|
|
}
|
|
}
|