cranelift-codegen: Prepare aarch64 for usage from Winch (#5570)

This commit exposes the necessary aarch64 pieces to be used by Winch for binary emission.
This commit is contained in:
Saúl Cabrera
2023-01-13 14:46:25 -05:00
committed by GitHub
parent 7682a40d62
commit f0979af157
7 changed files with 98 additions and 18 deletions

View File

@@ -14,12 +14,13 @@ use std::string::String;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[repr(u8)] #[repr(u8)]
pub enum ShiftOp { pub enum ShiftOp {
/// Logical shift left.
LSL = 0b00, LSL = 0b00,
#[allow(dead_code)] /// Logical shift right.
LSR = 0b01, LSR = 0b01,
#[allow(dead_code)] /// Arithmentic shift right.
ASR = 0b10, ASR = 0b10,
#[allow(dead_code)] /// Rotate right.
ROR = 0b11, ROR = 0b11,
} }
@@ -61,11 +62,14 @@ impl ShiftOpShiftImm {
/// A shift operator with an amount, guaranteed to be within range. /// A shift operator with an amount, guaranteed to be within range.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct ShiftOpAndAmt { pub struct ShiftOpAndAmt {
/// The shift operator.
op: ShiftOp, op: ShiftOp,
/// The shift operator amount.
shift: ShiftOpShiftImm, shift: ShiftOpShiftImm,
} }
impl ShiftOpAndAmt { impl ShiftOpAndAmt {
/// Create a new shift operator with an amount.
pub fn new(op: ShiftOp, shift: ShiftOpShiftImm) -> ShiftOpAndAmt { pub fn new(op: ShiftOp, shift: ShiftOpShiftImm) -> ShiftOpAndAmt {
ShiftOpAndAmt { op, shift } ShiftOpAndAmt { op, shift }
} }
@@ -85,14 +89,21 @@ impl ShiftOpAndAmt {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[repr(u8)] #[repr(u8)]
pub enum ExtendOp { pub enum ExtendOp {
/// Unsigned extend byte.
UXTB = 0b000, UXTB = 0b000,
/// Unsigned extend halfword.
UXTH = 0b001, UXTH = 0b001,
/// Unsigned extend word.
UXTW = 0b010, UXTW = 0b010,
/// Unsigned extend doubleword.
UXTX = 0b011, UXTX = 0b011,
/// Signed extend byte.
SXTB = 0b100, SXTB = 0b100,
/// Signed extend halfword.
SXTH = 0b101, SXTH = 0b101,
/// Signed extend word.
SXTW = 0b110, SXTW = 0b110,
#[allow(dead_code)] /// Signed extend doubleword.
SXTX = 0b111, SXTX = 0b111,
} }
@@ -137,7 +148,7 @@ impl AMode {
} }
} }
pub fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self { pub(crate) fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self {
// This should match `memarg_operands()`. // This should match `memarg_operands()`.
match self { match self {
&AMode::Unscaled { rn, simm9 } => AMode::Unscaled { &AMode::Unscaled { rn, simm9 } => AMode::Unscaled {
@@ -191,13 +202,16 @@ impl AMode {
/// A memory argument to a load/store-pair. /// A memory argument to a load/store-pair.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum PairAMode { pub enum PairAMode {
/// Signed, scaled 7-bit offset from a register.
SignedOffset(Reg, SImm7Scaled), SignedOffset(Reg, SImm7Scaled),
/// Pre-increment register before address computation.
SPPreIndexed(SImm7Scaled), SPPreIndexed(SImm7Scaled),
/// Post-increment register after address computation.
SPPostIndexed(SImm7Scaled), SPPostIndexed(SImm7Scaled),
} }
impl PairAMode { impl PairAMode {
pub fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self { pub(crate) fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self {
// Should match `pairmemarg_operands()`. // Should match `pairmemarg_operands()`.
match self { match self {
&PairAMode::SignedOffset(reg, simm7scaled) => { &PairAMode::SignedOffset(reg, simm7scaled) => {
@@ -216,21 +230,37 @@ impl PairAMode {
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)] #[repr(u8)]
pub enum Cond { pub enum Cond {
/// Equal.
Eq = 0, Eq = 0,
/// Not equal.
Ne = 1, Ne = 1,
/// Unsigned greater than or equal to.
Hs = 2, Hs = 2,
/// Unsigned less than.
Lo = 3, Lo = 3,
/// Minus, negative.
Mi = 4, Mi = 4,
/// Positive or zero.
Pl = 5, Pl = 5,
/// Signed overflow.
Vs = 6, Vs = 6,
/// No signed overflow.
Vc = 7, Vc = 7,
/// Unsigned greater than.
Hi = 8, Hi = 8,
/// Unsigned less than or equal to.
Ls = 9, Ls = 9,
/// Signed greater or equal to.
Ge = 10, Ge = 10,
/// Signed less than.
Lt = 11, Lt = 11,
/// Signed greater than.
Gt = 12, Gt = 12,
/// Signed less than or equal.
Le = 13, Le = 13,
/// Always executed.
Al = 14, Al = 14,
/// Always executed.
Nv = 15, Nv = 15,
} }
@@ -491,7 +521,9 @@ impl PrettyPrint for BranchTarget {
/// 64-bit variants of many instructions (and integer registers). /// 64-bit variants of many instructions (and integer registers).
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OperandSize { pub enum OperandSize {
/// 32-bit.
Size32, Size32,
/// 64-bit.
Size64, Size64,
} }
@@ -517,6 +549,7 @@ impl OperandSize {
} }
} }
/// Return the operand size in bits.
pub fn bits(&self) -> u8 { pub fn bits(&self) -> u8 {
match self { match self {
OperandSize::Size32 => 32, OperandSize::Size32 => 32,
@@ -539,6 +572,9 @@ impl OperandSize {
} }
} }
/// Register interpretation bit.
/// When 0, the register is interpreted as the 32-bit version.
/// When 1, the register is interpreted as the 64-bit version.
pub fn sf_bit(&self) -> u32 { pub fn sf_bit(&self) -> u32 {
match self { match self {
OperandSize::Size32 => 0, OperandSize::Size32 => 0,
@@ -550,10 +586,15 @@ impl OperandSize {
/// Type used to communicate the size of a scalar SIMD & FP operand. /// Type used to communicate the size of a scalar SIMD & FP operand.
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ScalarSize { pub enum ScalarSize {
/// 8-bit.
Size8, Size8,
/// 16-bit.
Size16, Size16,
/// 32-bit.
Size32, Size32,
/// 64-bit.
Size64, Size64,
/// 128-bit.
Size128, Size128,
} }
@@ -578,6 +619,7 @@ impl ScalarSize {
} }
} }
/// Return the widened version of the scalar size.
pub fn widen(&self) -> ScalarSize { pub fn widen(&self) -> ScalarSize {
match self { match self {
ScalarSize::Size8 => ScalarSize::Size16, ScalarSize::Size8 => ScalarSize::Size16,
@@ -588,6 +630,7 @@ impl ScalarSize {
} }
} }
/// Return the narrowed version of the scalar size.
pub fn narrow(&self) -> ScalarSize { pub fn narrow(&self) -> ScalarSize {
match self { match self {
ScalarSize::Size8 => panic!("can't narrow 8-bits"), ScalarSize::Size8 => panic!("can't narrow 8-bits"),
@@ -602,12 +645,19 @@ impl ScalarSize {
/// Type used to communicate the size of a vector operand. /// Type used to communicate the size of a vector operand.
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VectorSize { pub enum VectorSize {
/// 8-bit, 8 lanes.
Size8x8, Size8x8,
/// 8 bit, 16 lanes.
Size8x16, Size8x16,
/// 16-bit, 4 lanes.
Size16x4, Size16x4,
/// 16-bit, 8 lanes.
Size16x8, Size16x8,
/// 32-bit, 2 lanes.
Size32x2, Size32x2,
/// 32-bit, 4 lanes.
Size32x4, Size32x4,
/// 64-bit, 2 lanes.
Size64x2, Size64x2,
} }
@@ -644,6 +694,7 @@ impl VectorSize {
} }
} }
/// Returns true if the VectorSize is 128-bits.
pub fn is_128bits(&self) -> bool { pub fn is_128bits(&self) -> bool {
match self { match self {
VectorSize::Size8x8 => false, VectorSize::Size8x8 => false,

View File

@@ -675,7 +675,8 @@ impl EmitState {
pub struct EmitInfo(settings::Flags); pub struct EmitInfo(settings::Flags);
impl EmitInfo { impl EmitInfo {
pub(crate) fn new(flags: settings::Flags) -> Self { /// Create a constant state for emission of instructions.
pub fn new(flags: settings::Flags) -> Self {
Self(flags) Self(flags)
} }
} }

View File

@@ -1,7 +1,5 @@
//! AArch64 ISA definitions: immediate constants. //! AArch64 ISA definitions: immediate constants.
// Some variants are never constructed, but we still want them as options in the future.
#[allow(dead_code)]
use crate::ir::types::*; use crate::ir::types::*;
use crate::ir::Type; use crate::ir::Type;
use crate::isa::aarch64::inst::{OperandSize, ScalarSize}; use crate::isa::aarch64::inst::{OperandSize, ScalarSize};
@@ -24,6 +22,7 @@ pub struct NZCV {
} }
impl NZCV { impl NZCV {
/// Create a new NZCV flags representation.
pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV { pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV {
NZCV { n, z, c, v } NZCV { n, z, c, v }
} }
@@ -45,6 +44,7 @@ pub struct UImm5 {
} }
impl UImm5 { impl UImm5 {
/// Create an unsigned 5-bit immediate from u8.
pub fn maybe_from_u8(value: u8) -> Option<UImm5> { pub fn maybe_from_u8(value: u8) -> Option<UImm5> {
if value < 32 { if value < 32 {
Some(UImm5 { value }) Some(UImm5 { value })
@@ -99,13 +99,17 @@ impl SImm7Scaled {
} }
} }
/// Floating-point unit immediate left shift.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct FPULeftShiftImm { pub struct FPULeftShiftImm {
/// Shift amount.
pub amount: u8, pub amount: u8,
/// Lane size in bits.
pub lane_size_in_bits: u8, pub lane_size_in_bits: u8,
} }
impl FPULeftShiftImm { impl FPULeftShiftImm {
/// Create a floating-point unit immediate left shift from u8.
pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> { pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64); debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
if amount < lane_size_in_bits { if amount < lane_size_in_bits {
@@ -118,6 +122,7 @@ impl FPULeftShiftImm {
} }
} }
/// Returns the encoding of the immediate.
pub fn enc(&self) -> u32 { pub fn enc(&self) -> u32 {
debug_assert!(self.lane_size_in_bits.is_power_of_two()); debug_assert!(self.lane_size_in_bits.is_power_of_two());
debug_assert!(self.lane_size_in_bits > self.amount); debug_assert!(self.lane_size_in_bits > self.amount);
@@ -139,13 +144,17 @@ impl FPULeftShiftImm {
} }
} }
/// Floating-point unit immediate right shift.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct FPURightShiftImm { pub struct FPURightShiftImm {
/// Shift amount.
pub amount: u8, pub amount: u8,
/// Lane size in bits.
pub lane_size_in_bits: u8, pub lane_size_in_bits: u8,
} }
impl FPURightShiftImm { impl FPURightShiftImm {
/// Create a floating-point unit immediate right shift from u8.
pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> { pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64); debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
if amount > 0 && amount <= lane_size_in_bits { if amount > 0 && amount <= lane_size_in_bits {
@@ -158,6 +167,7 @@ impl FPURightShiftImm {
} }
} }
/// Returns encoding of the immediate.
pub fn enc(&self) -> u32 { pub fn enc(&self) -> u32 {
debug_assert_ne!(0, self.amount); debug_assert_ne!(0, self.amount);
// The encoding of the immediate follows the table below, // The encoding of the immediate follows the table below,
@@ -596,6 +606,7 @@ impl MoveWideConst {
None None
} }
/// Create a `MoveWideCosnt` from a given shift, if possible.
pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst> { pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<MoveWideConst> {
let shift_enc = shift / 16; let shift_enc = shift / 16;
if shift_enc > 3 { if shift_enc > 3 {
@@ -608,6 +619,7 @@ impl MoveWideConst {
} }
} }
/// Create a zero immediate of this format.
pub fn zero() -> MoveWideConst { pub fn zero() -> MoveWideConst {
MoveWideConst { bits: 0, shift: 0 } MoveWideConst { bits: 0, shift: 0 }
} }

View File

@@ -15,17 +15,17 @@ use regalloc2::{PRegSet, VReg};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::string::{String, ToString}; use std::string::{String, ToString};
pub mod regs; pub(crate) mod regs;
pub use self::regs::*; pub(crate) use self::regs::*;
pub mod imms; pub mod imms;
pub use self::imms::*; pub use self::imms::*;
pub mod args; pub mod args;
pub use self::args::*; pub use self::args::*;
pub mod emit; pub(crate) mod emit;
pub use self::emit::*; pub(crate) use self::emit::*;
use crate::isa::aarch64::abi::AArch64MachineDeps; use crate::isa::aarch64::abi::AArch64MachineDeps;
pub mod unwind; pub(crate) mod unwind;
#[cfg(test)] #[cfg(test)]
mod emit_tests; mod emit_tests;
@@ -78,12 +78,19 @@ impl BitOp {
/// the Inst enum. /// the Inst enum.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CallInfo { pub struct CallInfo {
/// Call destination.
pub dest: ExternalName, pub dest: ExternalName,
/// Arguments to the call instruction.
pub uses: CallArgList, pub uses: CallArgList,
/// Return values from the call instruction.
pub defs: CallRetList, pub defs: CallRetList,
/// Clobbers register set.
pub clobbers: PRegSet, pub clobbers: PRegSet,
/// Instruction opcode.
pub opcode: Opcode, pub opcode: Opcode,
/// Caller calling convention.
pub caller_callconv: CallConv, pub caller_callconv: CallConv,
/// Callee calling convention.
pub callee_callconv: CallConv, pub callee_callconv: CallConv,
} }
@@ -91,12 +98,19 @@ pub struct CallInfo {
/// enum. /// enum.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CallIndInfo { pub struct CallIndInfo {
/// Function pointer for indirect call.
pub rn: Reg, pub rn: Reg,
/// Arguments to the call instruction.
pub uses: SmallVec<[CallArgPair; 8]>, pub uses: SmallVec<[CallArgPair; 8]>,
/// Return values from the call instruction.
pub defs: SmallVec<[CallRetPair; 8]>, pub defs: SmallVec<[CallRetPair; 8]>,
/// Clobbers register set.
pub clobbers: PRegSet, pub clobbers: PRegSet,
/// Instruction opcode.
pub opcode: Opcode, pub opcode: Opcode,
/// Caller calling convention.
pub caller_callconv: CallConv, pub caller_callconv: CallConv,
/// Callee calling convention.
pub callee_callconv: CallConv, pub callee_callconv: CallConv,
} }
@@ -104,7 +118,9 @@ pub struct CallIndInfo {
/// enum. /// enum.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct JTSequenceInfo { pub struct JTSequenceInfo {
/// Possible branch targets.
pub targets: Vec<BranchTarget>, pub targets: Vec<BranchTarget>,
/// Default branch target.
pub default_target: BranchTarget, pub default_target: BranchTarget,
} }

View File

@@ -2,7 +2,7 @@
// the generated ISLE source below because we include!() it. We must include!() it because its path // the generated ISLE source below because we include!() it. We must include!() it because its path
// depends on an environment variable; and also because of this, we can't do the `#[path = "..."] // depends on an environment variable; and also because of this, we can't do the `#[path = "..."]
// mod generated_code;` trick either. // mod generated_code;` trick either.
#![allow(dead_code, unreachable_code, unreachable_patterns)] #![allow(missing_docs, dead_code, unreachable_code, unreachable_patterns)]
#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)] #![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]
#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)] #![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]

View File

@@ -19,9 +19,9 @@ use target_lexicon::{Aarch64Architecture, Architecture, OperatingSystem, Triple}
// New backend: // New backend:
mod abi; mod abi;
pub(crate) mod inst; pub mod inst;
mod lower; mod lower;
mod settings; pub mod settings;
use inst::create_reg_env; use inst::create_reg_env;

View File

@@ -64,7 +64,7 @@ use target_lexicon::{triple, Architecture, PointerWidth, Triple};
pub mod x64; pub mod x64;
#[cfg(feature = "arm64")] #[cfg(feature = "arm64")]
pub(crate) mod aarch64; pub mod aarch64;
#[cfg(feature = "riscv64")] #[cfg(feature = "riscv64")]
pub mod riscv64; pub mod riscv64;