Remove more old backend ISA concepts (#3402)
This also paves the way for unifying TargetIsa and MachBackend, since now they map one to one. In theory the two traits could be merged, which would be nice to limit the number of total concepts. Also they have quite different responsibilities, so it might be fine to keep them separate. Interestingly, this PR started as removing RegInfo from the TargetIsa trait since the adapter returned a dummy value there. From the fallout, noticed that all Display implementations didn't needed an ISA anymore (since these were only used to render ISA specific registers). Also the whole family of RegInfo / ValueLoc / RegUnit was exclusively used for the old backend, and these could be removed. Notably, some IR instructions needed to be removed, because they were using RegUnit too: this was the oddball of regfill / regmove / regspill / copy_special, which were IR instructions inserted by the old regalloc. Fare thee well!
This commit is contained in:
@@ -1407,7 +1407,7 @@ pub(crate) fn lower_i64x2_mul<C: LowerCtx<I = Inst>>(c: &mut C, insn: IRInst) {
|
||||
// rd = |dg+ch|be+af||dg+ch|be+af|
|
||||
c.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::Addp,
|
||||
rd: rd,
|
||||
rd,
|
||||
rn: rd.to_reg(),
|
||||
rm: rd.to_reg(),
|
||||
size: VectorSize::Size32x4,
|
||||
|
||||
@@ -482,9 +482,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
|
||||
ctx.emit(Inst::AluRRRR {
|
||||
alu_op: ALUOp3::MSub64,
|
||||
rd: rd,
|
||||
rd,
|
||||
rn: rd.to_reg(),
|
||||
rm: rm,
|
||||
rm,
|
||||
ra: rn,
|
||||
});
|
||||
} else {
|
||||
@@ -2165,16 +2165,11 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
Opcode::Spill
|
||||
| Opcode::Fill
|
||||
| Opcode::FillNop
|
||||
| Opcode::Regmove
|
||||
| Opcode::CopySpecial
|
||||
| Opcode::CopyToSsa
|
||||
| Opcode::CopyNop
|
||||
| Opcode::AdjustSpDown
|
||||
| Opcode::AdjustSpUpImm
|
||||
| Opcode::AdjustSpDownImm
|
||||
| Opcode::IfcmpSp
|
||||
| Opcode::Regspill
|
||||
| Opcode::Regfill => {
|
||||
| Opcode::IfcmpSp => {
|
||||
panic!("Unused opcode should not be encountered.");
|
||||
}
|
||||
|
||||
@@ -2412,7 +2407,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
alu_op: VecALUOp::Umaxp,
|
||||
rd: tmp,
|
||||
rn: rm,
|
||||
rm: rm,
|
||||
rm,
|
||||
size,
|
||||
});
|
||||
} else {
|
||||
@@ -2835,9 +2830,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::Addp,
|
||||
rd: rd,
|
||||
rn: rn,
|
||||
rm: rm,
|
||||
rd,
|
||||
rn,
|
||||
rm,
|
||||
size: VectorSize::from_ty(ty),
|
||||
});
|
||||
}
|
||||
@@ -3446,7 +3441,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
ctx.emit(Inst::FpuRRR {
|
||||
fpu_op: choose_32_64(in_ty, FPUOp2::Min32, FPUOp2::Min64),
|
||||
rd: rtmp2,
|
||||
rn: rn,
|
||||
rn,
|
||||
rm: rtmp1.to_reg(),
|
||||
});
|
||||
if in_bits == 32 {
|
||||
@@ -3468,7 +3463,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
}
|
||||
}
|
||||
if in_bits == 32 {
|
||||
ctx.emit(Inst::FpuCmp32 { rn: rn, rm: rn });
|
||||
ctx.emit(Inst::FpuCmp32 { rn, rm: rn });
|
||||
ctx.emit(Inst::FpuCSel32 {
|
||||
rd: rtmp2,
|
||||
rn: rtmp1.to_reg(),
|
||||
@@ -3476,7 +3471,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
cond: Cond::Ne,
|
||||
});
|
||||
} else {
|
||||
ctx.emit(Inst::FpuCmp64 { rn: rn, rm: rn });
|
||||
ctx.emit(Inst::FpuCmp64 { rn, rm: rn });
|
||||
ctx.emit(Inst::FpuCSel64 {
|
||||
rd: rtmp2,
|
||||
rn: rtmp1.to_reg(),
|
||||
|
||||
@@ -11,7 +11,6 @@ use crate::machinst::{
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings as shared_settings;
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use regalloc::{PrettyPrint, RealRegUniverse};
|
||||
use target_lexicon::{Aarch64Architecture, Architecture, Triple};
|
||||
|
||||
@@ -111,11 +110,6 @@ impl MachBackend for AArch64Backend {
|
||||
self.isa_flags.iter().collect()
|
||||
}
|
||||
|
||||
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
|
||||
self.flags.hash(&mut hasher);
|
||||
self.isa_flags.hash(&mut hasher);
|
||||
}
|
||||
|
||||
fn reg_universe(&self) -> &RealRegUniverse {
|
||||
&self.reg_universe
|
||||
}
|
||||
@@ -126,13 +120,6 @@ impl MachBackend for AArch64Backend {
|
||||
IntCC::UnsignedGreaterThanOrEqual
|
||||
}
|
||||
|
||||
fn unsigned_sub_overflow_condition(&self) -> IntCC {
|
||||
// unsigned `<`; this corresponds to the carry flag cleared on aarch64, which happens on
|
||||
// underflow of a subtract (aarch64 follows a carry-cleared-on-borrow convention, the
|
||||
// opposite of x86).
|
||||
IntCC::UnsignedLessThan
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn emit_unwind_info(
|
||||
&self,
|
||||
|
||||
@@ -11,7 +11,6 @@ use crate::result::CodegenResult;
|
||||
use crate::settings;
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use regalloc::{PrettyPrint, RealRegUniverse};
|
||||
use target_lexicon::{Architecture, ArmArchitecture, Triple};
|
||||
|
||||
@@ -101,10 +100,6 @@ impl MachBackend for Arm32Backend {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
|
||||
self.flags.hash(&mut hasher);
|
||||
}
|
||||
|
||||
fn reg_universe(&self) -> &RealRegUniverse {
|
||||
&self.reg_universe
|
||||
}
|
||||
@@ -114,11 +109,6 @@ impl MachBackend for Arm32Backend {
|
||||
IntCC::UnsignedGreaterThanOrEqual
|
||||
}
|
||||
|
||||
fn unsigned_sub_overflow_condition(&self) -> IntCC {
|
||||
// Carry flag clear.
|
||||
IntCC::UnsignedLessThan
|
||||
}
|
||||
|
||||
fn text_section_builder(&self, num_funcs: u32) -> Box<dyn TextSectionBuilder> {
|
||||
Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs))
|
||||
}
|
||||
|
||||
@@ -8,83 +8,6 @@
|
||||
//! are satisfied.
|
||||
|
||||
use crate::binemit::CodeOffset;
|
||||
use crate::ir::ValueLoc;
|
||||
use crate::isa::{RegClass, RegUnit};
|
||||
|
||||
/// Register constraint for a single value operand or instruction result.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct OperandConstraint {
|
||||
/// The kind of constraint.
|
||||
pub kind: ConstraintKind,
|
||||
|
||||
/// The register class of the operand.
|
||||
///
|
||||
/// This applies to all kinds of constraints, but with slightly different meaning.
|
||||
pub regclass: RegClass,
|
||||
}
|
||||
|
||||
impl OperandConstraint {
|
||||
/// Check if this operand constraint is satisfied by the given value location.
|
||||
/// For tied constraints, this only checks the register class, not that the
|
||||
/// counterpart operand has the same value location.
|
||||
pub fn satisfied(&self, loc: ValueLoc) -> bool {
|
||||
match self.kind {
|
||||
ConstraintKind::Reg | ConstraintKind::Tied(_) => {
|
||||
if let ValueLoc::Reg(reg) = loc {
|
||||
self.regclass.contains(reg)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
ConstraintKind::FixedReg(reg) | ConstraintKind::FixedTied(reg) => {
|
||||
loc == ValueLoc::Reg(reg) && self.regclass.contains(reg)
|
||||
}
|
||||
ConstraintKind::Stack => {
|
||||
if let ValueLoc::Stack(_) = loc {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The different kinds of operand constraints.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum ConstraintKind {
|
||||
/// This operand or result must be a register from the given register class.
|
||||
Reg,
|
||||
|
||||
/// This operand or result must be a fixed register.
|
||||
///
|
||||
/// The constraint's `regclass` field is the top-level register class containing the fixed
|
||||
/// register.
|
||||
FixedReg(RegUnit),
|
||||
|
||||
/// This result value must use the same register as an input value operand.
|
||||
///
|
||||
/// The associated number is the index of the input value operand this result is tied to. The
|
||||
/// constraint's `regclass` field is the same as the tied operand's register class.
|
||||
///
|
||||
/// When an (in, out) operand pair is tied, this constraint kind appears in both the `ins` and
|
||||
/// the `outs` arrays. The constraint for the in operand is `Tied(out)`, and the constraint for
|
||||
/// the out operand is `Tied(in)`.
|
||||
Tied(u8),
|
||||
|
||||
/// This operand must be a fixed register, and it has a tied counterpart.
|
||||
///
|
||||
/// This works just like `FixedReg`, but additionally indicates that there are identical
|
||||
/// input/output operands for this fixed register. For an input operand, this means that the
|
||||
/// value will be clobbered by the instruction
|
||||
FixedTied(RegUnit),
|
||||
|
||||
/// This operand must be a value in a stack slot.
|
||||
///
|
||||
/// The constraint's `regclass` field is the register class that would normally be used to load
|
||||
/// and store values of this type.
|
||||
Stack,
|
||||
}
|
||||
|
||||
/// Constraints on the range of a branch instruction.
|
||||
///
|
||||
|
||||
@@ -44,8 +44,7 @@
|
||||
//! concurrent function compilations.
|
||||
|
||||
pub use crate::isa::call_conv::CallConv;
|
||||
pub use crate::isa::constraints::{BranchRange, ConstraintKind, OperandConstraint};
|
||||
pub use crate::isa::registers::{regs_overlap, RegClass, RegClassIndex, RegInfo, RegUnit};
|
||||
pub use crate::isa::constraints::BranchRange;
|
||||
pub use crate::isa::stack::{StackBase, StackBaseMask, StackRef};
|
||||
|
||||
use crate::flowgraph;
|
||||
@@ -57,10 +56,8 @@ use crate::result::CodegenResult;
|
||||
use crate::settings;
|
||||
use crate::settings::SetResult;
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::any::Any;
|
||||
use core::fmt;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::hash::Hasher;
|
||||
use target_lexicon::{triple, Architecture, OperatingSystem, PointerWidth, Triple};
|
||||
|
||||
// This module is made public here for benchmarking purposes. No guarantees are
|
||||
@@ -81,7 +78,6 @@ pub mod unwind;
|
||||
|
||||
mod call_conv;
|
||||
mod constraints;
|
||||
pub mod registers;
|
||||
mod stack;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -220,8 +216,9 @@ impl TargetFrontendConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods that are specialized to a target ISA. Implies a Display trait that shows the
|
||||
/// shared flags, as well as any isa-specific flags.
|
||||
/// Methods that are specialized to a target ISA.
|
||||
///
|
||||
/// Implies a Display trait that shows the shared flags, as well as any ISA-specific flags.
|
||||
pub trait TargetIsa: fmt::Display + Send + Sync {
|
||||
/// Get the name of this ISA.
|
||||
fn name(&self) -> &'static str;
|
||||
@@ -235,85 +232,6 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
||||
/// Get the ISA-dependent flag values that were used to make this trait object.
|
||||
fn isa_flags(&self) -> Vec<settings::Value>;
|
||||
|
||||
/// Hashes all flags, both ISA-independent and ISA-specific, into the
|
||||
/// specified hasher.
|
||||
fn hash_all_flags(&self, hasher: &mut dyn Hasher);
|
||||
|
||||
/// Get the default calling convention of this target.
|
||||
fn default_call_conv(&self) -> CallConv {
|
||||
CallConv::triple_default(self.triple())
|
||||
}
|
||||
|
||||
/// Get the endianness of this ISA.
|
||||
fn endianness(&self) -> ir::Endianness {
|
||||
match self.triple().endianness().unwrap() {
|
||||
target_lexicon::Endianness::Little => ir::Endianness::Little,
|
||||
target_lexicon::Endianness::Big => ir::Endianness::Big,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the code (text) section alignment for this ISA.
|
||||
fn code_section_alignment(&self) -> u64 {
|
||||
use target_lexicon::*;
|
||||
match (self.triple().operating_system, self.triple().architecture) {
|
||||
(
|
||||
OperatingSystem::MacOSX { .. }
|
||||
| OperatingSystem::Darwin
|
||||
| OperatingSystem::Ios
|
||||
| OperatingSystem::Tvos,
|
||||
Architecture::Aarch64(..),
|
||||
) => 0x4000,
|
||||
_ => 0x1000,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the pointer type of this ISA.
|
||||
fn pointer_type(&self) -> ir::Type {
|
||||
ir::Type::int(u16::from(self.pointer_bits())).unwrap()
|
||||
}
|
||||
|
||||
/// Get the width of pointers on this ISA.
|
||||
fn pointer_width(&self) -> PointerWidth {
|
||||
self.triple().pointer_width().unwrap()
|
||||
}
|
||||
|
||||
/// Get the width of pointers on this ISA, in units of bits.
|
||||
fn pointer_bits(&self) -> u8 {
|
||||
self.pointer_width().bits()
|
||||
}
|
||||
|
||||
/// Get the width of pointers on this ISA, in units of bytes.
|
||||
fn pointer_bytes(&self) -> u8 {
|
||||
self.pointer_width().bytes()
|
||||
}
|
||||
|
||||
/// Get the information needed by frontends producing Cranelift IR.
|
||||
fn frontend_config(&self) -> TargetFrontendConfig {
|
||||
TargetFrontendConfig {
|
||||
default_call_conv: self.default_call_conv(),
|
||||
pointer_width: self.pointer_width(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Does the CPU implement scalar comparisons using a CPU flags register?
|
||||
fn uses_cpu_flags(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Does the CPU implement multi-register addressing?
|
||||
fn uses_complex_addresses(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Get a data structure describing the registers in this ISA.
|
||||
fn register_info(&self) -> RegInfo;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
/// Map a Cranelift register to its corresponding DWARF register.
|
||||
fn map_dwarf_register(&self, _: RegUnit) -> Result<u16, RegisterMappingError> {
|
||||
Err(RegisterMappingError::UnsupportedArchitecture)
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
/// Map a regalloc::Reg to its corresponding DWARF register.
|
||||
fn map_regalloc_reg_to_dwarf(&self, _: ::regalloc::Reg) -> Result<u16, RegisterMappingError> {
|
||||
@@ -323,21 +241,6 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
||||
/// IntCC condition for Unsigned Addition Overflow (Carry).
|
||||
fn unsigned_add_overflow_condition(&self) -> ir::condcodes::IntCC;
|
||||
|
||||
/// IntCC condition for Unsigned Subtraction Overflow (Borrow/Carry).
|
||||
fn unsigned_sub_overflow_condition(&self) -> ir::condcodes::IntCC;
|
||||
|
||||
/// Returns the flavor of unwind information emitted for this target.
|
||||
fn unwind_info_kind(&self) -> UnwindInfoKind {
|
||||
match self.triple().operating_system {
|
||||
#[cfg(feature = "unwind")]
|
||||
OperatingSystem::Windows => UnwindInfoKind::Windows,
|
||||
#[cfg(feature = "unwind")]
|
||||
_ => UnwindInfoKind::SystemV,
|
||||
#[cfg(not(feature = "unwind"))]
|
||||
_ => UnwindInfoKind::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates unwind information for the function.
|
||||
///
|
||||
/// Returns `None` if there is no unwind information for the function.
|
||||
@@ -363,10 +266,77 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
||||
fn get_mach_backend(&self) -> Option<&dyn MachBackend> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an [Any] reference for downcasting to the ISA-specific implementation of this trait
|
||||
/// with `isa.as_any().downcast_ref::<isa::foo::Isa>()`.
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
/// Methods implemented for free for target ISA!
|
||||
impl<'a> dyn TargetIsa + 'a {
|
||||
/// Get the default calling convention of this target.
|
||||
pub fn default_call_conv(&self) -> CallConv {
|
||||
CallConv::triple_default(self.triple())
|
||||
}
|
||||
|
||||
/// Get the endianness of this ISA.
|
||||
pub fn endianness(&self) -> ir::Endianness {
|
||||
match self.triple().endianness().unwrap() {
|
||||
target_lexicon::Endianness::Little => ir::Endianness::Little,
|
||||
target_lexicon::Endianness::Big => ir::Endianness::Big,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the code (text) section alignment for this ISA.
|
||||
pub fn code_section_alignment(&self) -> u64 {
|
||||
use target_lexicon::*;
|
||||
match (self.triple().operating_system, self.triple().architecture) {
|
||||
(
|
||||
OperatingSystem::MacOSX { .. }
|
||||
| OperatingSystem::Darwin
|
||||
| OperatingSystem::Ios
|
||||
| OperatingSystem::Tvos,
|
||||
Architecture::Aarch64(..),
|
||||
) => 0x4000,
|
||||
_ => 0x1000,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the pointer type of this ISA.
|
||||
pub fn pointer_type(&self) -> ir::Type {
|
||||
ir::Type::int(u16::from(self.pointer_bits())).unwrap()
|
||||
}
|
||||
|
||||
/// Get the width of pointers on this ISA.
|
||||
pub(crate) fn pointer_width(&self) -> PointerWidth {
|
||||
self.triple().pointer_width().unwrap()
|
||||
}
|
||||
|
||||
/// Get the width of pointers on this ISA, in units of bits.
|
||||
pub fn pointer_bits(&self) -> u8 {
|
||||
self.pointer_width().bits()
|
||||
}
|
||||
|
||||
/// Get the width of pointers on this ISA, in units of bytes.
|
||||
pub fn pointer_bytes(&self) -> u8 {
|
||||
self.pointer_width().bytes()
|
||||
}
|
||||
|
||||
/// Get the information needed by frontends producing Cranelift IR.
|
||||
pub fn frontend_config(&self) -> TargetFrontendConfig {
|
||||
TargetFrontendConfig {
|
||||
default_call_conv: self.default_call_conv(),
|
||||
pointer_width: self.pointer_width(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the flavor of unwind information emitted for this target.
|
||||
pub(crate) fn unwind_info_kind(&self) -> UnwindInfoKind {
|
||||
match self.triple().operating_system {
|
||||
#[cfg(feature = "unwind")]
|
||||
OperatingSystem::Windows => UnwindInfoKind::Windows,
|
||||
#[cfg(feature = "unwind")]
|
||||
_ => UnwindInfoKind::SystemV,
|
||||
#[cfg(not(feature = "unwind"))]
|
||||
_ => UnwindInfoKind::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for &dyn TargetIsa {
|
||||
|
||||
@@ -1,360 +0,0 @@
|
||||
//! Data structures describing the registers in an ISA.
|
||||
|
||||
use crate::entity::EntityRef;
|
||||
use core::fmt;
|
||||
|
||||
/// Register units are the smallest units of register allocation.
|
||||
///
|
||||
/// Normally there is a 1-1 correspondence between registers and register units, but when an ISA
|
||||
/// has aliasing registers, the aliasing can be modeled with registers that cover multiple
|
||||
/// register units.
|
||||
///
|
||||
/// The register allocator will enforce that each register unit only gets used for one thing.
|
||||
pub type RegUnit = u16;
|
||||
|
||||
/// A bit mask indexed by register classes.
|
||||
///
|
||||
/// The size of this type is determined by the ISA with the most register classes.
|
||||
pub type RegClassMask = u32;
|
||||
|
||||
/// A bit mask indexed by register units.
|
||||
///
|
||||
/// The size of this type is determined by the target ISA that has the most register units defined.
|
||||
/// Currently that is arm32 which has 64+16 units.
|
||||
pub type RegUnitMask = [RegClassMask; 3];
|
||||
|
||||
/// The register units in a target ISA are divided into disjoint register banks. Each bank covers a
|
||||
/// contiguous range of register units.
|
||||
///
|
||||
/// The `RegBank` struct provides a static description of a register bank.
|
||||
pub struct RegBank {
|
||||
/// The name of this register bank as defined in the ISA's DSL definition.
|
||||
pub name: &'static str,
|
||||
|
||||
/// The first register unit in this bank.
|
||||
pub first_unit: RegUnit,
|
||||
|
||||
/// The total number of register units in this bank.
|
||||
pub units: RegUnit,
|
||||
|
||||
/// Array of specially named register units. This array can be shorter than the number of units
|
||||
/// in the bank.
|
||||
pub names: &'static [&'static str],
|
||||
|
||||
/// Name prefix to use for those register units in the bank not covered by the `names` array.
|
||||
/// The remaining register units will be named this prefix followed by their decimal offset in
|
||||
/// the bank. So with a prefix `r`, registers will be named `r8`, `r9`, ...
|
||||
pub prefix: &'static str,
|
||||
|
||||
/// Index of the first top-level register class in this bank.
|
||||
pub first_toprc: usize,
|
||||
|
||||
/// Number of top-level register classes in this bank.
|
||||
///
|
||||
/// The top-level register classes in a bank are guaranteed to be numbered sequentially from
|
||||
/// `first_toprc`, and all top-level register classes across banks come before any sub-classes.
|
||||
pub num_toprcs: usize,
|
||||
|
||||
/// Is register pressure tracking enabled for this bank?
|
||||
pub pressure_tracking: bool,
|
||||
}
|
||||
|
||||
impl RegBank {
|
||||
/// Does this bank contain `regunit`?
|
||||
fn contains(&self, regunit: RegUnit) -> bool {
|
||||
regunit >= self.first_unit && regunit - self.first_unit < self.units
|
||||
}
|
||||
|
||||
/// Try to parse a regunit name. The name is not expected to begin with `%`.
|
||||
fn parse_regunit(&self, name: &str) -> Option<RegUnit> {
|
||||
match self.names.iter().position(|&x| x == name) {
|
||||
Some(offset) => {
|
||||
// This is one of the special-cased names.
|
||||
Some(offset as RegUnit)
|
||||
}
|
||||
None => {
|
||||
// Try a regular prefixed name.
|
||||
if name.starts_with(self.prefix) {
|
||||
name[self.prefix.len()..].parse().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
.and_then(|offset| {
|
||||
if offset < self.units {
|
||||
Some(offset + self.first_unit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Write `regunit` to `w`, assuming that it belongs to this bank.
|
||||
/// All regunits are written with a `%` prefix.
|
||||
fn write_regunit(&self, f: &mut fmt::Formatter, regunit: RegUnit) -> fmt::Result {
|
||||
let offset = regunit - self.first_unit;
|
||||
assert!(offset < self.units);
|
||||
if (offset as usize) < self.names.len() {
|
||||
write!(f, "%{}", self.names[offset as usize])
|
||||
} else {
|
||||
write!(f, "%{}{}", self.prefix, offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A register class reference.
|
||||
///
|
||||
/// All register classes are statically defined in tables generated from the meta descriptions.
|
||||
pub type RegClass = &'static RegClassData;
|
||||
|
||||
/// Data about a register class.
|
||||
///
|
||||
/// A register class represents a subset of the registers in a bank. It describes the set of
|
||||
/// permitted registers for a register operand in a given encoding of an instruction.
|
||||
///
|
||||
/// A register class can be a subset of another register class. The top-level register classes are
|
||||
/// disjoint.
|
||||
pub struct RegClassData {
|
||||
/// The name of the register class.
|
||||
pub name: &'static str,
|
||||
|
||||
/// The index of this class in the ISA's RegInfo description.
|
||||
pub index: u8,
|
||||
|
||||
/// How many register units to allocate per register.
|
||||
pub width: u8,
|
||||
|
||||
/// Index of the register bank this class belongs to.
|
||||
pub bank: u8,
|
||||
|
||||
/// Index of the top-level register class contains this one.
|
||||
pub toprc: u8,
|
||||
|
||||
/// The first register unit in this class.
|
||||
pub first: RegUnit,
|
||||
|
||||
/// Bit-mask of sub-classes of this register class, including itself.
|
||||
///
|
||||
/// Bits correspond to RC indexes.
|
||||
pub subclasses: RegClassMask,
|
||||
|
||||
/// Mask of register units in the class. If `width > 1`, the mask only has a bit set for the
|
||||
/// first register unit in each allocatable register.
|
||||
pub mask: RegUnitMask,
|
||||
|
||||
/// The global `RegInfo` instance containing this register class.
|
||||
pub info: &'static RegInfo,
|
||||
|
||||
/// The "pinned" register of the associated register bank.
|
||||
///
|
||||
/// This register must be non-volatile (callee-preserved) and must not be the fixed
|
||||
/// output register of any instruction.
|
||||
pub pinned_reg: Option<RegUnit>,
|
||||
}
|
||||
|
||||
impl RegClassData {
|
||||
/// Get the register class index corresponding to the intersection of `self` and `other`.
|
||||
///
|
||||
/// This register class is guaranteed to exist if the register classes overlap. If the register
|
||||
/// classes don't overlap, returns `None`.
|
||||
pub fn intersect_index(&self, other: RegClass) -> Option<RegClassIndex> {
|
||||
// Compute the set of common subclasses.
|
||||
let mask = self.subclasses & other.subclasses;
|
||||
|
||||
if mask == 0 {
|
||||
// No overlap.
|
||||
None
|
||||
} else {
|
||||
// Register class indexes are topologically ordered, so the largest common subclass has
|
||||
// the smallest index.
|
||||
Some(RegClassIndex(mask.trailing_zeros() as u8))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the intersection of `self` and `other`.
|
||||
pub fn intersect(&self, other: RegClass) -> Option<RegClass> {
|
||||
self.intersect_index(other).map(|rci| self.info.rc(rci))
|
||||
}
|
||||
|
||||
/// Returns true if `other` is a subclass of this register class.
|
||||
/// A register class is considered to be a subclass of itself.
|
||||
pub fn has_subclass<RCI: Into<RegClassIndex>>(&self, other: RCI) -> bool {
|
||||
self.subclasses & (1 << other.into().0) as u32 != 0
|
||||
}
|
||||
|
||||
/// Get the top-level register class containing this class.
|
||||
pub fn toprc(&self) -> RegClass {
|
||||
self.info.rc(RegClassIndex(self.toprc))
|
||||
}
|
||||
|
||||
/// Get a specific register unit in this class.
|
||||
pub fn unit(&self, offset: usize) -> RegUnit {
|
||||
let uoffset = offset * usize::from(self.width);
|
||||
self.first + uoffset as RegUnit
|
||||
}
|
||||
|
||||
/// Does this register class contain `regunit`?
|
||||
pub fn contains(&self, regunit: RegUnit) -> bool {
|
||||
self.mask[(regunit / 32) as usize] & (1u32 << (regunit % 32) as u32) != 0
|
||||
}
|
||||
|
||||
/// If the pinned register is used, is the given regunit the pinned register of this class?
|
||||
#[inline]
|
||||
pub fn is_pinned_reg(&self, enabled: bool, regunit: RegUnit) -> bool {
|
||||
enabled
|
||||
&& self
|
||||
.pinned_reg
|
||||
.map_or(false, |pinned_reg| pinned_reg == regunit)
|
||||
}
|
||||
|
||||
/// Calculate the index of the register inside the class.
|
||||
pub fn index_of(&self, regunit: RegUnit) -> u16 {
|
||||
assert!(
|
||||
self.contains(regunit),
|
||||
"the {} register class does not contain {}",
|
||||
self.name,
|
||||
regunit
|
||||
);
|
||||
regunit - self.first
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RegClassData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for RegClassData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Within an ISA, register classes are uniquely identified by their index.
|
||||
impl PartialEq for RegClassData {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.index == other.index
|
||||
}
|
||||
}
|
||||
|
||||
/// A small reference to a register class.
|
||||
///
|
||||
/// Use this when storing register classes in compact data structures. The `RegInfo::rc()` method
|
||||
/// can be used to get the real register class reference back.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct RegClassIndex(u8);
|
||||
|
||||
impl EntityRef for RegClassIndex {
|
||||
fn new(idx: usize) -> Self {
|
||||
Self(idx as u8)
|
||||
}
|
||||
|
||||
fn index(self) -> usize {
|
||||
usize::from(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegClass> for RegClassIndex {
|
||||
fn from(rc: RegClass) -> Self {
|
||||
Self(rc.index)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RegClassIndex {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "rci{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Test of two registers overlap.
|
||||
///
|
||||
/// A register is identified as a `(RegClass, RegUnit)` pair. The register class is needed to
|
||||
/// determine the width (in regunits) of the register.
|
||||
pub fn regs_overlap(rc1: RegClass, reg1: RegUnit, rc2: RegClass, reg2: RegUnit) -> bool {
|
||||
let end1 = reg1 + RegUnit::from(rc1.width);
|
||||
let end2 = reg2 + RegUnit::from(rc2.width);
|
||||
!(end1 <= reg2 || end2 <= reg1)
|
||||
}
|
||||
|
||||
/// Information about the registers in an ISA.
|
||||
///
|
||||
/// The `RegUnit` data structure collects all relevant static information about the registers in an
|
||||
/// ISA.
|
||||
#[derive(Clone)]
|
||||
pub struct RegInfo {
|
||||
/// All register banks, ordered by their `first_unit`. The register banks are disjoint, but
|
||||
/// there may be holes of unused register unit numbers between banks due to alignment.
|
||||
pub banks: &'static [RegBank],
|
||||
|
||||
/// All register classes ordered topologically so a sub-class always follows its parent.
|
||||
pub classes: &'static [RegClass],
|
||||
}
|
||||
|
||||
impl RegInfo {
|
||||
/// Get the register bank holding `regunit`.
|
||||
pub fn bank_containing_regunit(&self, regunit: RegUnit) -> Option<&RegBank> {
|
||||
// We could do a binary search, but most ISAs have only two register banks...
|
||||
self.banks.iter().find(|b| b.contains(regunit))
|
||||
}
|
||||
|
||||
/// Try to parse a regunit name. The name is not expected to begin with `%`.
|
||||
pub fn parse_regunit(&self, name: &str) -> Option<RegUnit> {
|
||||
self.banks
|
||||
.iter()
|
||||
.filter_map(|b| b.parse_regunit(name))
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Make a temporary object that can display a register unit.
|
||||
pub fn display_regunit(&self, regunit: RegUnit) -> DisplayRegUnit {
|
||||
DisplayRegUnit {
|
||||
regunit,
|
||||
reginfo: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the register class corresponding to `idx`.
|
||||
pub fn rc(&self, idx: RegClassIndex) -> RegClass {
|
||||
self.classes[idx.index()]
|
||||
}
|
||||
|
||||
/// Get the top-level register class containing the `idx` class.
|
||||
pub fn toprc(&self, idx: RegClassIndex) -> RegClass {
|
||||
self.classes[self.rc(idx).toprc as usize]
|
||||
}
|
||||
}
|
||||
|
||||
/// Temporary object that holds enough information to print a register unit.
|
||||
pub struct DisplayRegUnit<'a> {
|
||||
regunit: RegUnit,
|
||||
reginfo: &'a RegInfo,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for DisplayRegUnit<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.reginfo.bank_containing_regunit(self.regunit) {
|
||||
Some(b) => b.write_regunit(f, self.regunit),
|
||||
None => write!(f, "%INVALID{}", self.regunit),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_sizes() {
|
||||
use cranelift_codegen_shared::constants;
|
||||
use std::mem::size_of;
|
||||
|
||||
// In these tests, size_of returns number of bytes: we actually want the number of bits, so
|
||||
// multiply these by 8.
|
||||
assert!(
|
||||
(size_of::<RegClassMask>() * 8) <= constants::MAX_NUM_REG_CLASSES,
|
||||
"need to bump MAX_NUM_REG_CLASSES or change RegClassMask type"
|
||||
);
|
||||
|
||||
assert!(
|
||||
constants::MAX_NUM_REG_CLASSES < (1 << (size_of::<RegClassIndex>() * 8)),
|
||||
"need to change RegClassIndex's type to a wider type"
|
||||
);
|
||||
}
|
||||
@@ -661,16 +661,7 @@ fn lower_icmp_to_flags<C: LowerCtx<I = Inst>>(
|
||||
(false, true) => NarrowValueMode::SignExtend64,
|
||||
(false, false) => NarrowValueMode::ZeroExtend64,
|
||||
};
|
||||
let inputs = [
|
||||
InsnInput {
|
||||
insn: insn,
|
||||
input: 0,
|
||||
},
|
||||
InsnInput {
|
||||
insn: insn,
|
||||
input: 1,
|
||||
},
|
||||
];
|
||||
let inputs = [InsnInput { insn, input: 0 }, InsnInput { insn, input: 1 }];
|
||||
let ty = ctx.input_ty(insn, 0);
|
||||
let rn = put_input_in_reg(ctx, inputs[0], narrow_mode);
|
||||
if is_signed {
|
||||
@@ -759,16 +750,7 @@ fn lower_icmp_to_flags<C: LowerCtx<I = Inst>>(
|
||||
fn lower_fcmp_to_flags<C: LowerCtx<I = Inst>>(ctx: &mut C, insn: IRInst) {
|
||||
let ty = ctx.input_ty(insn, 0);
|
||||
let bits = ty_bits(ty);
|
||||
let inputs = [
|
||||
InsnInput {
|
||||
insn: insn,
|
||||
input: 0,
|
||||
},
|
||||
InsnInput {
|
||||
insn: insn,
|
||||
input: 1,
|
||||
},
|
||||
];
|
||||
let inputs = [InsnInput { insn, input: 0 }, InsnInput { insn, input: 1 }];
|
||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
|
||||
match bits {
|
||||
@@ -2909,17 +2891,12 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
Opcode::Spill
|
||||
| Opcode::Fill
|
||||
| Opcode::FillNop
|
||||
| Opcode::Regmove
|
||||
| Opcode::CopySpecial
|
||||
| Opcode::CopyToSsa
|
||||
| Opcode::CopyNop
|
||||
| Opcode::AdjustSpDown
|
||||
| Opcode::AdjustSpUpImm
|
||||
| Opcode::AdjustSpDownImm
|
||||
| Opcode::DummySargT
|
||||
| Opcode::IfcmpSp
|
||||
| Opcode::Regspill
|
||||
| Opcode::Regfill => {
|
||||
| Opcode::IfcmpSp => {
|
||||
panic!("Unused opcode should not be encountered.");
|
||||
}
|
||||
|
||||
@@ -3117,7 +3094,7 @@ fn lower_branch<C: LowerCtx<I = Inst>>(
|
||||
info: Box::new(JTSequenceInfo {
|
||||
default_target,
|
||||
targets: jt_targets,
|
||||
targets_for_term: targets_for_term,
|
||||
targets_for_term,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ use crate::result::CodegenResult;
|
||||
use crate::settings as shared_settings;
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
use regalloc::{PrettyPrint, RealRegUniverse, Reg};
|
||||
use target_lexicon::{Architecture, Triple};
|
||||
@@ -114,11 +113,6 @@ impl MachBackend for S390xBackend {
|
||||
self.isa_flags.iter().collect()
|
||||
}
|
||||
|
||||
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
|
||||
self.flags.hash(&mut hasher);
|
||||
self.isa_flags.hash(&mut hasher);
|
||||
}
|
||||
|
||||
fn reg_universe(&self) -> &RealRegUniverse {
|
||||
&self.reg_universe
|
||||
}
|
||||
@@ -132,10 +126,6 @@ impl MachBackend for S390xBackend {
|
||||
IntCC::UnsignedGreaterThan
|
||||
}
|
||||
|
||||
fn unsigned_sub_overflow_condition(&self) -> IntCC {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn emit_unwind_info(
|
||||
&self,
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc};
|
||||
use crate::ir::Value;
|
||||
use crate::ir::{ConstantOffset, ExternalName, Function, JumpTable, Opcode, SourceLoc, TrapCode};
|
||||
use crate::isa::TargetIsa;
|
||||
use crate::ir::{ConstantOffset, ExternalName, JumpTable, Opcode, SourceLoc, TrapCode};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use std::string::String;
|
||||
@@ -80,7 +78,5 @@ impl CodeSink for TestCodeSink {
|
||||
|
||||
fn end_codegen(&mut self) {}
|
||||
|
||||
fn add_stack_map(&mut self, _val_list: &[Value], _func: &Function, _isa: &dyn TargetIsa) {}
|
||||
|
||||
fn add_call_site(&mut self, _opcode: Opcode, _srcloc: SourceLoc) {}
|
||||
}
|
||||
|
||||
@@ -6865,16 +6865,11 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
Opcode::Spill
|
||||
| Opcode::Fill
|
||||
| Opcode::FillNop
|
||||
| Opcode::Regmove
|
||||
| Opcode::CopySpecial
|
||||
| Opcode::CopyToSsa
|
||||
| Opcode::CopyNop
|
||||
| Opcode::AdjustSpDown
|
||||
| Opcode::AdjustSpUpImm
|
||||
| Opcode::AdjustSpDownImm
|
||||
| Opcode::IfcmpSp
|
||||
| Opcode::Regspill
|
||||
| Opcode::Regfill
|
||||
| Opcode::Copy
|
||||
| Opcode::DummySargT => {
|
||||
panic!("Unused opcode should not be encountered.");
|
||||
|
||||
@@ -15,7 +15,6 @@ use crate::machinst::{
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings::{self as shared_settings, Flags};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
use regalloc::{PrettyPrint, RealRegUniverse, Reg};
|
||||
use target_lexicon::Triple;
|
||||
@@ -95,11 +94,6 @@ impl MachBackend for X64Backend {
|
||||
self.x64_flags.iter().collect()
|
||||
}
|
||||
|
||||
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
|
||||
self.flags.hash(&mut hasher);
|
||||
self.x64_flags.hash(&mut hasher);
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"x64"
|
||||
}
|
||||
@@ -118,12 +112,6 @@ impl MachBackend for X64Backend {
|
||||
IntCC::UnsignedLessThan
|
||||
}
|
||||
|
||||
fn unsigned_sub_overflow_condition(&self) -> IntCC {
|
||||
// unsigned `<`; this corresponds to the carry flag set on x86, which
|
||||
// indicates a sub has underflowed (carry is borrow for subtract).
|
||||
IntCC::UnsignedLessThan
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn emit_unwind_info(
|
||||
&self,
|
||||
|
||||
Reference in New Issue
Block a user