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
```
97 lines
2.8 KiB
Rust
97 lines
2.8 KiB
Rust
use crate::abi::ABI;
|
|
use crate::codegen::{CodeGen, CodeGenContext};
|
|
use crate::frame::Frame;
|
|
use crate::isa::x64::masm::MacroAssembler as X64Masm;
|
|
use crate::masm::MacroAssembler;
|
|
use crate::regalloc::RegAlloc;
|
|
use crate::stack::Stack;
|
|
use crate::{
|
|
isa::{Builder, TargetIsa},
|
|
regset::RegSet,
|
|
};
|
|
use anyhow::Result;
|
|
use cranelift_codegen::{
|
|
isa::x64::settings as x64_settings, settings::Flags, Final, MachBufferFinalized,
|
|
};
|
|
use target_lexicon::Triple;
|
|
use wasmparser::{FuncType, FuncValidator, FunctionBody, ValidatorResources};
|
|
|
|
use self::regs::ALL_GPR;
|
|
|
|
mod abi;
|
|
mod asm;
|
|
mod masm;
|
|
// Not all the fpr and gpr constructors are used at the moment;
|
|
// in that sense, this directive is a temporary measure to avoid
|
|
// dead code warnings.
|
|
#[allow(dead_code)]
|
|
mod regs;
|
|
|
|
/// Create an ISA builder.
|
|
pub(crate) fn isa_builder(triple: Triple) -> Builder {
|
|
Builder {
|
|
triple,
|
|
settings: x64_settings::builder(),
|
|
constructor: |triple, shared_flags, settings| {
|
|
// TODO: Once enabling/disabling flags is allowed, and once features like SIMD are supported
|
|
// ensure compatibility between shared flags and ISA flags.
|
|
let isa_flags = x64_settings::Flags::new(&shared_flags, settings);
|
|
let isa = X64::new(triple, shared_flags, isa_flags);
|
|
Ok(Box::new(isa))
|
|
},
|
|
}
|
|
}
|
|
|
|
/// x64 ISA.
|
|
pub(crate) struct X64 {
|
|
/// The target triple.
|
|
triple: Triple,
|
|
/// ISA specific flags.
|
|
isa_flags: x64_settings::Flags,
|
|
/// Shared flags.
|
|
shared_flags: Flags,
|
|
}
|
|
|
|
impl X64 {
|
|
/// Create a x64 ISA.
|
|
pub fn new(triple: Triple, shared_flags: Flags, isa_flags: x64_settings::Flags) -> Self {
|
|
Self {
|
|
isa_flags,
|
|
shared_flags,
|
|
triple,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TargetIsa for X64 {
|
|
fn name(&self) -> &'static str {
|
|
"x64"
|
|
}
|
|
|
|
fn triple(&self) -> &Triple {
|
|
&self.triple
|
|
}
|
|
|
|
fn compile_function(
|
|
&self,
|
|
sig: &FuncType,
|
|
body: &FunctionBody,
|
|
mut validator: 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)?;
|
|
// TODO Add in floating point bitmask
|
|
let regalloc = RegAlloc::new(RegSet::new(ALL_GPR, 0), regs::scratch());
|
|
let codegen_context = CodeGenContext::new(&mut masm, stack, &frame);
|
|
let mut codegen = CodeGen::new::<abi::X64ABI>(codegen_context, abi_sig, regalloc);
|
|
|
|
codegen.emit(&mut body, validator)?;
|
|
|
|
Ok(masm.finalize())
|
|
}
|
|
}
|