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:
Benjamin Bouvier
2021-10-04 10:36:12 +02:00
committed by GitHub
parent 76afcab0c2
commit 43a86f14d5
71 changed files with 302 additions and 2059 deletions

View File

@@ -7,7 +7,6 @@ use crate::ir;
use crate::ir::types;
use crate::ir::{DataFlowGraph, InstructionData};
use crate::ir::{Inst, Opcode, Type, Value};
use crate::isa;
/// Base trait for instruction builders.
///

View File

@@ -10,7 +10,6 @@ use crate::ir::{
Block, FuncRef, Inst, SigRef, Signature, SourceLoc, Type, Value, ValueLabelAssignments,
ValueList, ValueListPool,
};
use crate::isa::TargetIsa;
use crate::packed_option::ReservedValue;
use crate::write::write_operands;
use crate::HashMap;
@@ -466,12 +465,8 @@ impl DataFlowGraph {
}
/// Returns an object that displays `inst`.
pub fn display_inst<'a, I: Into<Option<&'a dyn TargetIsa>>>(
&'a self,
inst: Inst,
isa: I,
) -> DisplayInst<'a> {
DisplayInst(self, isa.into(), inst)
pub fn display_inst<'a>(&'a self, inst: Inst) -> DisplayInst<'a> {
DisplayInst(self, inst)
}
/// Get all value arguments on `inst` as a slice.
@@ -657,7 +652,7 @@ impl DataFlowGraph {
old_value,
"{} wasn't detached from {}",
old_value,
self.display_inst(inst, None)
self.display_inst(inst)
);
new_value
}
@@ -963,13 +958,12 @@ impl BlockData {
}
/// Object that can display an instruction.
pub struct DisplayInst<'a>(&'a DataFlowGraph, Option<&'a dyn TargetIsa>, Inst);
pub struct DisplayInst<'a>(&'a DataFlowGraph, Inst);
impl<'a> fmt::Display for DisplayInst<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let dfg = self.0;
let isa = self.1;
let inst = self.2;
let inst = self.1;
if let Some((first, rest)) = dfg.inst_results(inst).split_first() {
write!(f, "{}", first)?;
@@ -985,7 +979,7 @@ impl<'a> fmt::Display for DisplayInst<'a> {
} else {
write!(f, "{}.{}", dfg[inst].opcode(), typevar)?;
}
write_operands(f, dfg, isa, inst)
write_operands(f, dfg, inst)
}
}
@@ -1150,10 +1144,7 @@ mod tests {
dfg.make_inst_results(inst, types::I32);
assert_eq!(inst.to_string(), "inst0");
assert_eq!(
dfg.display_inst(inst, None).to_string(),
"v0 = iconst.i32 0"
);
assert_eq!(dfg.display_inst(inst).to_string(), "v0 = iconst.i32 0");
// Immutable reference resolution.
{
@@ -1188,7 +1179,7 @@ mod tests {
code: TrapCode::User(0),
};
let inst = dfg.make_inst(idata);
assert_eq!(dfg.display_inst(inst, None).to_string(), "trap user0");
assert_eq!(dfg.display_inst(inst).to_string(), "trap user0");
// Result slice should be empty.
assert_eq!(dfg.inst_results(inst), &[]);

View File

@@ -5,8 +5,8 @@
//!
//! This module declares the data types used to represent external functions and call signatures.
use crate::ir::{ArgumentLoc, ExternalName, SigRef, Type};
use crate::isa::{CallConv, RegInfo, RegUnit};
use crate::ir::{ExternalName, SigRef, Type};
use crate::isa::CallConv;
use crate::machinst::RelocDistance;
use alloc::vec::Vec;
use core::fmt;
@@ -50,11 +50,6 @@ impl Signature {
self.call_conv = call_conv;
}
/// Return an object that can display `self` with correct register names.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplaySignature<'a> {
DisplaySignature(self, regs.into())
}
/// Find the index of a presumed unique special-purpose parameter.
pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
self.params.iter().rposition(|arg| arg.purpose == purpose)
@@ -108,38 +103,29 @@ impl Signature {
}
}
/// Wrapper type capable of displaying a `Signature` with correct register names.
pub struct DisplaySignature<'a>(&'a Signature, Option<&'a RegInfo>);
fn write_list(f: &mut fmt::Formatter, args: &[AbiParam], regs: Option<&RegInfo>) -> fmt::Result {
fn write_list(f: &mut fmt::Formatter, args: &[AbiParam]) -> fmt::Result {
match args.split_first() {
None => {}
Some((first, rest)) => {
write!(f, "{}", first.display(regs))?;
write!(f, "{}", first)?;
for arg in rest {
write!(f, ", {}", arg.display(regs))?;
write!(f, ", {}", arg)?;
}
}
}
Ok(())
}
impl<'a> fmt::Display for DisplaySignature<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(")?;
write_list(f, &self.0.params, self.1)?;
write!(f, ")")?;
if !self.0.returns.is_empty() {
write!(f, " -> ")?;
write_list(f, &self.0.returns, self.1)?;
}
write!(f, " {}", self.0.call_conv)
}
}
impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display(None).fmt(f)
write!(f, "(")?;
write_list(f, &self.params)?;
write!(f, ")")?;
if !self.returns.is_empty() {
write!(f, " -> ")?;
write_list(f, &self.returns)?;
}
write!(f, " {}", self.call_conv)
}
}
@@ -157,9 +143,6 @@ pub struct AbiParam {
/// Method for extending argument to a full register.
pub extension: ArgumentExtension,
/// ABI-specific location of this argument, or `Unassigned` for arguments that have not yet
/// been legalized.
pub location: ArgumentLoc,
/// Was the argument converted to pointer during legalization?
pub legalized_to_pointer: bool,
}
@@ -171,7 +154,6 @@ impl AbiParam {
value_type: vt,
extension: ArgumentExtension::None,
purpose: ArgumentPurpose::Normal,
location: Default::default(),
legalized_to_pointer: false,
}
}
@@ -182,18 +164,6 @@ impl AbiParam {
value_type: vt,
extension: ArgumentExtension::None,
purpose,
location: Default::default(),
legalized_to_pointer: false,
}
}
/// Create a parameter for a special-purpose register.
pub fn special_reg(vt: Type, purpose: ArgumentPurpose, regunit: RegUnit) -> Self {
Self {
value_type: vt,
extension: ArgumentExtension::None,
purpose,
location: ArgumentLoc::Reg(regunit),
legalized_to_pointer: false,
}
}
@@ -215,42 +185,23 @@ impl AbiParam {
..self
}
}
/// Return an object that can display `self` with correct register names.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplayAbiParam<'a> {
DisplayAbiParam(self, regs.into())
}
}
/// Wrapper type capable of displaying a `AbiParam` with correct register names.
pub struct DisplayAbiParam<'a>(&'a AbiParam, Option<&'a RegInfo>);
impl<'a> fmt::Display for DisplayAbiParam<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.value_type)?;
if self.0.legalized_to_pointer {
write!(f, " ptr")?;
}
match self.0.extension {
ArgumentExtension::None => {}
ArgumentExtension::Uext => write!(f, " uext")?,
ArgumentExtension::Sext => write!(f, " sext")?,
}
if self.0.purpose != ArgumentPurpose::Normal {
write!(f, " {}", self.0.purpose)?;
}
if self.0.location.is_assigned() {
write!(f, " [{}]", self.0.location.display(self.1))?;
}
Ok(())
}
}
impl fmt::Display for AbiParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display(None).fmt(f)
write!(f, "{}", self.value_type)?;
if self.legalized_to_pointer {
write!(f, " ptr")?;
}
match self.extension {
ArgumentExtension::None => {}
ArgumentExtension::Uext => write!(f, " uext")?,
ArgumentExtension::Sext => write!(f, " sext")?,
}
if self.purpose != ArgumentPurpose::Normal {
write!(f, " {}", self.purpose)?;
}
Ok(())
}
}
@@ -519,15 +470,5 @@ mod tests {
sig.to_string(),
"(i32, i32x4) -> f32, b8 baldrdash_system_v"
);
// Order does not matter.
sig.params[0].location = ArgumentLoc::Stack(24);
sig.params[1].location = ArgumentLoc::Stack(8);
// Writing ABI-annotated signatures.
assert_eq!(
sig.to_string(),
"(i32 [24], i32x4 [8]) -> f32, b8 baldrdash_system_v"
);
}
}

View File

@@ -10,10 +10,10 @@ use crate::ir::{
HeapData, Inst, InstructionData, JumpTable, JumpTableData, Opcode, SigRef, StackSlot,
StackSlotData, Table, TableData,
};
use crate::ir::{BlockOffsets, SourceLocs, StackSlots, ValueLocations};
use crate::ir::{BlockOffsets, SourceLocs, StackSlots};
use crate::ir::{DataFlowGraph, ExternalName, Layout, Signature};
use crate::ir::{JumpTableOffsets, JumpTables};
use crate::isa::{CallConv, TargetIsa};
use crate::isa::CallConv;
use crate::value_label::ValueLabelsRanges;
use crate::write::write_function;
#[cfg(feature = "enable-serde")]
@@ -104,9 +104,6 @@ pub struct Function {
/// Layout of blocks and instructions in the function body.
pub layout: Layout,
/// Location assigned to every value.
pub locations: ValueLocations,
/// Code offsets of the block headers.
///
/// This information is only transiently available after the `binemit::relax_branches` function
@@ -156,7 +153,6 @@ impl Function {
jump_tables: PrimaryMap::new(),
dfg: DataFlowGraph::new(),
layout: Layout::new(),
locations: SecondaryMap::new(),
offsets: SecondaryMap::new(),
jt_offsets: SecondaryMap::new(),
srclocs: SecondaryMap::new(),
@@ -176,7 +172,6 @@ impl Function {
self.jump_tables.clear();
self.dfg.clear();
self.layout.clear();
self.locations.clear();
self.offsets.clear();
self.jt_offsets.clear();
self.srclocs.clear();
@@ -227,11 +222,8 @@ impl Function {
}
/// Return an object that can display this function with correct ISA-specific annotations.
pub fn display<'a, I: Into<Option<&'a dyn TargetIsa>>>(
&'a self,
isa: I,
) -> DisplayFunction<'a> {
DisplayFunction(self, isa.into().into())
pub fn display(&self) -> DisplayFunction<'_> {
DisplayFunction(self, Default::default())
}
/// Return an object that can display this function with correct ISA-specific annotations.
@@ -295,7 +287,7 @@ impl Function {
}
_ => panic!(
"Unexpected instruction {} having default destination",
self.dfg.display_inst(inst, None)
self.dfg.display_inst(inst)
),
}
}
@@ -372,39 +364,27 @@ impl Function {
/// Additional annotations for function display.
#[derive(Default)]
pub struct DisplayFunctionAnnotations<'a> {
/// Enable ISA annotations.
pub isa: Option<&'a dyn TargetIsa>,
/// Enable value labels annotations.
pub value_ranges: Option<&'a ValueLabelsRanges>,
}
impl<'a> From<Option<&'a dyn TargetIsa>> for DisplayFunctionAnnotations<'a> {
fn from(isa: Option<&'a dyn TargetIsa>) -> DisplayFunctionAnnotations {
DisplayFunctionAnnotations {
isa,
value_ranges: None,
}
}
}
/// Wrapper type capable of displaying a `Function` with correct ISA annotations.
pub struct DisplayFunction<'a>(&'a Function, DisplayFunctionAnnotations<'a>);
impl<'a> fmt::Display for DisplayFunction<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self.0, &self.1)
write_function(fmt, self.0)
}
}
impl fmt::Display for Function {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self, &DisplayFunctionAnnotations::default())
write_function(fmt, self)
}
}
impl fmt::Debug for Function {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self, &DisplayFunctionAnnotations::default())
write_function(fmt, self)
}
}

View File

@@ -25,7 +25,6 @@ use crate::ir::{
trapcode::TrapCode,
types, Block, FuncRef, JumpTable, MemFlags, SigRef, StackSlot, Type, Value,
};
use crate::isa;
/// Some instructions use an external list of argument values because there is not enough space in
/// the 16-byte `InstructionData` struct. These value lists are stored in a memory pool in

View File

@@ -1,10 +1,6 @@
//! Naming well-known routines in the runtime library.
use crate::ir::{
types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Opcode,
Signature, Type,
};
use crate::isa::{CallConv, RegUnit, TargetIsa};
use crate::ir::{types, ExternalName, FuncRef, Function, Opcode, Type};
use core::fmt;
use core::str::FromStr;
#[cfg(feature = "enable-serde")]
@@ -169,14 +165,8 @@ impl LibCall {
/// Get a function reference for the probestack function in `func`.
///
/// If there is an existing reference, use it, otherwise make a new one.
pub fn get_probestack_funcref(
func: &mut Function,
reg_type: Type,
arg_reg: RegUnit,
isa: &dyn TargetIsa,
) -> FuncRef {
pub fn get_probestack_funcref(func: &mut Function) -> Option<FuncRef> {
find_funcref(LibCall::Probestack, func)
.unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa))
}
/// Get the existing function reference for `libcall` in `func` if it exists.
@@ -196,38 +186,6 @@ fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
None
}
/// Create a funcref for `LibCall::Probestack`.
fn make_funcref_for_probestack(
func: &mut Function,
reg_type: Type,
arg_reg: RegUnit,
isa: &dyn TargetIsa,
) -> FuncRef {
let mut sig = Signature::new(CallConv::Probestack);
let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg);
sig.params.push(rax);
if !isa.flags().probestack_func_adjusts_sp() {
sig.returns.push(rax);
}
make_funcref(LibCall::Probestack, func, sig, isa)
}
/// Create a funcref for `libcall`.
fn make_funcref(
libcall: LibCall,
func: &mut Function,
sig: Signature,
isa: &dyn TargetIsa,
) -> FuncRef {
let sigref = func.import_signature(sig);
func.import_function(ExtFuncData {
name: ExternalName::LibCall(libcall),
signature: sigref,
colocated: isa.flags().use_colocated_libcalls(),
})
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -22,7 +22,6 @@ pub mod stackslot;
mod table;
mod trapcode;
pub mod types;
mod valueloc;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
@@ -57,16 +56,12 @@ pub use crate::ir::stackslot::{StackLayoutInfo, StackSlotData, StackSlotKind, St
pub use crate::ir::table::TableData;
pub use crate::ir::trapcode::TrapCode;
pub use crate::ir::types::Type;
pub use crate::ir::valueloc::{ArgumentLoc, ValueLoc};
pub use crate::value_label::LabelValueLoc;
pub use cranelift_codegen_shared::condcodes;
use crate::binemit;
use crate::entity::{entity_impl, PrimaryMap, SecondaryMap};
/// Map of value locations.
pub type ValueLocations = SecondaryMap<Value, ValueLoc>;
/// Map of jump tables.
pub type JumpTables = PrimaryMap<JumpTable, JumpTableData>;

View File

@@ -1,166 +0,0 @@
//! Value locations.
//!
//! The register allocator assigns every SSA value to either a register or a stack slot. This
//! assignment is represented by a `ValueLoc` object.
use crate::ir::StackSlot;
use crate::isa::{RegInfo, RegUnit};
use core::fmt;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
/// Value location.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ValueLoc {
/// This value has not been assigned to a location yet.
Unassigned,
/// Value is assigned to a register.
Reg(RegUnit),
/// Value is assigned to a stack slot.
Stack(StackSlot),
}
impl Default for ValueLoc {
fn default() -> Self {
Self::Unassigned
}
}
impl ValueLoc {
/// Is this an assigned location? (That is, not `Unassigned`).
pub fn is_assigned(self) -> bool {
match self {
Self::Unassigned => false,
_ => true,
}
}
/// Get the register unit of this location, or panic.
pub fn unwrap_reg(self) -> RegUnit {
match self {
Self::Reg(ru) => ru,
_ => panic!("unwrap_reg expected register, found {:?}", self),
}
}
/// Get the stack slot of this location, or panic.
pub fn unwrap_stack(self) -> StackSlot {
match self {
Self::Stack(ss) => ss,
_ => panic!("unwrap_stack expected stack slot, found {:?}", self),
}
}
/// Return an object that can display this value location, using the register info from the
/// target ISA.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(self, regs: R) -> DisplayValueLoc<'a> {
DisplayValueLoc(self, regs.into())
}
}
/// Displaying a `ValueLoc` correctly requires the associated `RegInfo` from the target ISA.
/// Without the register info, register units are simply show as numbers.
///
/// The `DisplayValueLoc` type can display the contained `ValueLoc`.
pub struct DisplayValueLoc<'a>(ValueLoc, Option<&'a RegInfo>);
impl<'a> fmt::Display for DisplayValueLoc<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
ValueLoc::Unassigned => write!(f, "-"),
ValueLoc::Reg(ru) => match self.1 {
Some(regs) => write!(f, "{}", regs.display_regunit(ru)),
None => write!(f, "%{}", ru),
},
ValueLoc::Stack(ss) => write!(f, "{}", ss),
}
}
}
/// Function argument location.
///
/// The ABI specifies how arguments are passed to a function, and where return values appear after
/// the call. Just like a `ValueLoc`, function arguments can be passed in registers or on the
/// stack.
///
/// Function arguments on the stack are accessed differently for the incoming arguments to the
/// current function and the outgoing arguments to a called external function. For this reason,
/// the location of stack arguments is described as an offset into the array of function arguments
/// on the stack.
///
/// An `ArgumentLoc` can be translated to a `ValueLoc` only when we know if we're talking about an
/// incoming argument or an outgoing argument.
///
/// - For stack arguments, different `StackSlot` entities are used to represent incoming and
/// outgoing arguments.
/// - For register arguments, there is usually no difference, but if we ever add support for a
/// register-window ISA like SPARC, register arguments would also need to be translated.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ArgumentLoc {
/// This argument has not been assigned to a location yet.
Unassigned,
/// Argument is passed in a register.
Reg(RegUnit),
/// Argument is passed on the stack, at the given byte offset into the argument array.
Stack(i32),
}
impl Default for ArgumentLoc {
fn default() -> Self {
Self::Unassigned
}
}
impl ArgumentLoc {
/// Is this an assigned location? (That is, not `Unassigned`).
pub fn is_assigned(self) -> bool {
match self {
Self::Unassigned => false,
_ => true,
}
}
/// Is this a register location?
pub fn is_reg(self) -> bool {
match self {
Self::Reg(_) => true,
_ => false,
}
}
/// Is this a stack location?
pub fn is_stack(self) -> bool {
match self {
Self::Stack(_) => true,
_ => false,
}
}
/// Return an object that can display this argument location, using the register info from the
/// target ISA.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(self, regs: R) -> DisplayArgumentLoc<'a> {
DisplayArgumentLoc(self, regs.into())
}
}
/// Displaying a `ArgumentLoc` correctly requires the associated `RegInfo` from the target ISA.
/// Without the register info, register units are simply show as numbers.
///
/// The `DisplayArgumentLoc` type can display the contained `ArgumentLoc`.
pub struct DisplayArgumentLoc<'a>(ArgumentLoc, Option<&'a RegInfo>);
impl<'a> fmt::Display for DisplayArgumentLoc<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
ArgumentLoc::Unassigned => write!(f, "-"),
ArgumentLoc::Reg(ru) => match self.1 {
Some(regs) => write!(f, "{}", regs.display_regunit(ru)),
None => write!(f, "%{}", ru),
},
ArgumentLoc::Stack(offset) => write!(f, "{}", offset),
}
}
}