[build] Introduce a TargetIsaBuilder to better encapsulate the TargetIsa;

It was the caller's responsibility to call TargetIsa::check() before;
now one can't manipulate a TargetIsa without calling the
TargetIsaBuilder::finish() method, which is less error-prone and more in
line with what's coming for other things we're going to generate in the
meta crate.

Also splits the construction of a RegClass in two parts: a prototype is
made first when declaring the RegClass, and missing bits are filled in
when adding it to the TargetIsa(Builder). This avoids an awkward passing
of the isa to the RegClass ctor.
This commit is contained in:
Benjamin Bouvier
2018-11-08 18:41:15 +01:00
committed by Dan Gohman
parent f896bfb946
commit add4043bb5
7 changed files with 108 additions and 104 deletions

View File

@@ -1,7 +1,7 @@
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use super::regs::{ use super::regs::{
RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegClassProto,
}; };
pub struct TargetIsa { pub struct TargetIsa {
@@ -18,12 +18,24 @@ impl TargetIsa {
reg_classes: PrimaryMap::new(), reg_classes: PrimaryMap::new(),
} }
} }
}
pub struct TargetIsaBuilder {
isa: TargetIsa,
}
impl TargetIsaBuilder {
pub fn new(name: &'static str) -> Self {
Self {
isa: TargetIsa::new(name),
}
}
pub fn add_reg_bank(&mut self, builder: RegBankBuilder) -> RegBankIndex { pub fn add_reg_bank(&mut self, builder: RegBankBuilder) -> RegBankIndex {
let first_unit = if self.reg_banks.len() == 0 { let first_unit = if self.isa.reg_banks.len() == 0 {
0 0
} else { } else {
let last = &self.reg_banks.last().unwrap(); let last = &self.isa.reg_banks.last().unwrap();
let first_available_unit = (last.first_unit + last.units) as i8; let first_available_unit = (last.first_unit + last.units) as i8;
let units = builder.units; let units = builder.units;
let align = if units.is_power_of_two() { let align = if units.is_power_of_two() {
@@ -34,7 +46,7 @@ impl TargetIsa {
(first_available_unit + align - 1) & -align (first_available_unit + align - 1) & -align
} as u8; } as u8;
self.reg_banks.push(RegBank::new( self.isa.reg_banks.push(RegBank::new(
builder.name, builder.name,
first_unit, first_unit,
builder.units, builder.units,
@@ -47,38 +59,51 @@ impl TargetIsa {
} }
pub fn add_reg_class(&mut self, builder: RegClassBuilder) -> RegClassIndex { pub fn add_reg_class(&mut self, builder: RegClassBuilder) -> RegClassIndex {
let reg_bank_units = self.reg_banks.get(builder.bank).unwrap().units; let class_index = self.isa.reg_classes.next_key();
let start = builder.start; // Finish delayed construction of RegClass.
let (bank, toprc, start, width) = match builder.proto {
RegClassProto::TopLevel(bank_index) => {
self.isa
.reg_banks
.get_mut(bank_index)
.unwrap()
.toprcs
.push(class_index);
(bank_index, class_index, builder.start, builder.width)
}
RegClassProto::SubClass(parent_class_index) => {
assert!(builder.width == 0);
let (bank, toprc, start, width) = {
let parent = self.isa.reg_classes.get(parent_class_index).unwrap();
(parent.bank, parent.toprc, parent.start, parent.width)
};
for reg_class in self.isa.reg_classes.values_mut() {
if reg_class.toprc == toprc {
reg_class.subclasses.push(class_index);
}
}
let subclass_start = start + builder.start * width;
(bank, toprc, subclass_start, width)
}
};
let reg_bank_units = self.isa.reg_banks.get(bank).unwrap().units;
assert!(start < reg_bank_units); assert!(start < reg_bank_units);
let count = if builder.count != 0 { let count = if builder.count != 0 {
builder.count builder.count
} else { } else {
reg_bank_units / builder.width reg_bank_units / width
}; };
let reg_class_index = builder.index; let reg_class = RegClass::new(builder.name, class_index, width, bank, toprc, count, start);
assert!( self.isa.reg_classes.push(reg_class);
self.reg_classes.next_key() == reg_class_index,
"should have inserted RegClass where expected"
);
let reg_class = RegClass::new( let reg_bank = self.isa.reg_banks.get_mut(bank).unwrap();
builder.name, reg_bank.classes.push(class_index);
reg_class_index,
builder.width,
builder.bank,
builder.toprc,
count,
start,
);
self.reg_classes.push(reg_class);
let reg_bank = self.reg_banks.get_mut(builder.bank).unwrap(); class_index
reg_bank.classes.push(reg_class_index);
reg_class_index
} }
/// Checks that the set of register classes satisfies: /// Checks that the set of register classes satisfies:
@@ -89,16 +114,16 @@ impl TargetIsa {
/// 2. There are no identical classes under different names. /// 2. There are no identical classes under different names.
/// 3. Classes are sorted topologically such that all subclasses have a /// 3. Classes are sorted topologically such that all subclasses have a
/// higher index that the superclass. /// higher index that the superclass.
pub fn check(&self) { pub fn finish(self) -> TargetIsa {
for reg_bank in self.reg_banks.values() { for reg_bank in self.isa.reg_banks.values() {
for i1 in reg_bank.classes.iter() { for i1 in reg_bank.classes.iter() {
for i2 in reg_bank.classes.iter() { for i2 in reg_bank.classes.iter() {
if i1 >= i2 { if i1 >= i2 {
continue; continue;
} }
let rc1 = self.reg_classes.get(*i1).unwrap(); let rc1 = self.isa.reg_classes.get(*i1).unwrap();
let rc2 = self.reg_classes.get(*i2).unwrap(); let rc2 = self.isa.reg_classes.get(*i2).unwrap();
let rc1_mask = rc1.mask(0); let rc1_mask = rc1.mask(0);
let rc2_mask = rc2.mask(0); let rc2_mask = rc2.mask(0);
@@ -126,7 +151,8 @@ impl TargetIsa {
// If the intersection is the second one, then it must be a subclass. // If the intersection is the second one, then it must be a subclass.
if intersect == rc2_mask { if intersect == rc2_mask {
assert!( assert!(
self.reg_classes self.isa
.reg_classes
.get(*i1) .get(*i1)
.unwrap() .unwrap()
.subclasses .subclasses
@@ -138,5 +164,7 @@ impl TargetIsa {
} }
} }
} }
self.isa
} }
} }

View File

@@ -1,7 +1,5 @@
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use super::isa::TargetIsa;
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RegBankIndex(u32); pub struct RegBankIndex(u32);
entity_impl!(RegBankIndex); entity_impl!(RegBankIndex);
@@ -97,71 +95,53 @@ impl RegClass {
} }
} }
pub enum RegClassProto {
TopLevel(RegBankIndex),
SubClass(RegClassIndex),
}
pub struct RegClassBuilder { pub struct RegClassBuilder {
pub name: &'static str, pub name: &'static str,
pub index: RegClassIndex,
pub width: u8, pub width: u8,
pub bank: RegBankIndex,
pub toprc: RegClassIndex,
pub count: u8, pub count: u8,
pub start: u8, pub start: u8,
pub proto: RegClassProto,
} }
impl RegClassBuilder { impl RegClassBuilder {
pub fn new_toplevel(isa: &mut TargetIsa, name: &'static str, bank: RegBankIndex) -> Self { pub fn new_toplevel(name: &'static str, bank: RegBankIndex) -> Self {
let index = isa.reg_classes.next_key();
// Add it to the top-level register classes of the register bank.
isa.reg_banks.get_mut(bank).unwrap().toprcs.push(index);
Self { Self {
name, name,
index,
width: 1, width: 1,
bank,
toprc: index,
count: 0, count: 0,
start: 0, start: 0,
proto: RegClassProto::TopLevel(bank),
} }
} }
pub fn subclass_of( pub fn subclass_of(
isa: &mut TargetIsa,
name: &'static str, name: &'static str,
parent_index: RegClassIndex, parent_index: RegClassIndex,
start: u8, start: u8,
stop: u8, stop: u8,
) -> Self { ) -> Self {
assert!(stop >= start); assert!(stop >= start);
let index = isa.reg_classes.next_key();
let toprc = isa.reg_classes.get(parent_index).unwrap().toprc;
for reg_class in isa.reg_classes.values_mut() {
if reg_class.toprc == toprc {
reg_class.subclasses.push(index);
}
}
let parent = &isa.reg_classes.get(parent_index).unwrap();
Self { Self {
name, name,
width: 0,
count: stop - start, count: stop - start,
width: parent.width, start: start,
start: parent.start + start * parent.width, proto: RegClassProto::SubClass(parent_index),
bank: parent.bank,
toprc: parent.toprc,
index,
} }
} }
pub fn count(mut self, count: u8) -> Self { pub fn count(mut self, count: u8) -> Self {
self.count = count; self.count = count;
self self
} }
pub fn width(mut self, width: u8) -> Self { pub fn width(mut self, width: u8) -> Self {
self.width = width; match self.proto {
RegClassProto::TopLevel(_) => self.width = width,
RegClassProto::SubClass(_) => panic!("Subclasses inherit their parent's width."),
}
self self
} }
} }

View File

@@ -1,8 +1,8 @@
use cdsl::isa::{TargetIsa, TargetIsaBuilder};
use cdsl::regs::{RegBankBuilder, RegClassBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder};
use isa;
pub fn define() -> isa::TargetIsa { pub fn define() -> TargetIsa {
let mut isa = isa::TargetIsa::new("arm32"); let mut isa = TargetIsaBuilder::new("arm32");
let builder = RegBankBuilder::new("FloatRegs", "s") let builder = RegBankBuilder::new("FloatRegs", "s")
.units(64) .units(64)
@@ -20,20 +20,20 @@ pub fn define() -> isa::TargetIsa {
.track_pressure(false); .track_pressure(false);
let flag_reg = isa.add_reg_bank(builder); let flag_reg = isa.add_reg_bank(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "S", float_regs).count(32); let builder = RegClassBuilder::new_toplevel("S", float_regs).count(32);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "D", float_regs).width(2); let builder = RegClassBuilder::new_toplevel("D", float_regs).width(2);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "Q", float_regs).width(4); let builder = RegClassBuilder::new_toplevel("Q", float_regs).width(4);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "GPR", int_regs); let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "FLAG", flag_reg); let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
isa.add_reg_class(builder); isa.add_reg_class(builder);
isa isa.finish()
} }

View File

@@ -1,8 +1,8 @@
use cdsl::isa::{TargetIsa, TargetIsaBuilder};
use cdsl::regs::{RegBankBuilder, RegClassBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder};
use isa;
pub fn define() -> isa::TargetIsa { pub fn define() -> TargetIsa {
let mut isa = isa::TargetIsa::new("arm64"); let mut isa = TargetIsaBuilder::new("arm64");
// The `x31` regunit serves as the stack pointer / zero register depending on context. We // The `x31` regunit serves as the stack pointer / zero register depending on context. We
// reserve it and don't model the difference. // reserve it and don't model the difference.
@@ -22,14 +22,14 @@ pub fn define() -> isa::TargetIsa {
.track_pressure(false); .track_pressure(false);
let flag_reg = isa.add_reg_bank(builder); let flag_reg = isa.add_reg_bank(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "GPR", int_regs); let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "FPR", float_regs); let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "FLAG", flag_reg); let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
isa.add_reg_class(builder); isa.add_reg_class(builder);
isa isa.finish()
} }

View File

@@ -62,14 +62,10 @@ impl fmt::Display for Isa {
} }
pub fn define_all() -> Vec<TargetIsa> { pub fn define_all() -> Vec<TargetIsa> {
let isas = vec![ vec![
riscv::define(), riscv::define(),
arm32::define(), arm32::define(),
arm64::define(), arm64::define(),
x86::define(), x86::define(),
]; ]
for isa in isas.iter() {
isa.check();
}
isas
} }

View File

@@ -1,8 +1,8 @@
use cdsl::isa::{TargetIsa, TargetIsaBuilder};
use cdsl::regs::{RegBankBuilder, RegClassBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder};
use isa;
pub fn define() -> isa::TargetIsa { pub fn define() -> TargetIsa {
let mut isa = isa::TargetIsa::new("riscv"); let mut isa = TargetIsaBuilder::new("riscv");
let builder = RegBankBuilder::new("IntRegs", "x") let builder = RegBankBuilder::new("IntRegs", "x")
.units(32) .units(32)
@@ -14,11 +14,11 @@ pub fn define() -> isa::TargetIsa {
.track_pressure(true); .track_pressure(true);
let float_regs = isa.add_reg_bank(builder); let float_regs = isa.add_reg_bank(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "GPR", int_regs); let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "FPR", float_regs); let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
isa.add_reg_class(builder); isa.add_reg_class(builder);
isa isa.finish()
} }

View File

@@ -1,8 +1,8 @@
use cdsl::isa::{TargetIsa, TargetIsaBuilder};
use cdsl::regs::{RegBankBuilder, RegClassBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder};
use isa;
pub fn define() -> isa::TargetIsa { pub fn define() -> TargetIsa {
let mut isa = isa::TargetIsa::new("x86"); let mut isa = TargetIsaBuilder::new("x86");
let builder = RegBankBuilder::new("IntRegs", "r") let builder = RegBankBuilder::new("IntRegs", "r")
.units(16) .units(16)
@@ -21,23 +21,23 @@ pub fn define() -> isa::TargetIsa {
.track_pressure(false); .track_pressure(false);
let flag_reg = isa.add_reg_bank(builder); let flag_reg = isa.add_reg_bank(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "GPR", int_regs); let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
let gpr = isa.add_reg_class(builder); let gpr = isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "FPR", float_regs); let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
let fpr = isa.add_reg_class(builder); let fpr = isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel(&mut isa, "FLAG", flag_reg); let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::subclass_of(&mut isa, "GPR8", gpr, 0, 8); let builder = RegClassBuilder::subclass_of("GPR8", gpr, 0, 8);
let gpr8 = isa.add_reg_class(builder); let gpr8 = isa.add_reg_class(builder);
let builder = RegClassBuilder::subclass_of(&mut isa, "ABCD", gpr8, 0, 4); let builder = RegClassBuilder::subclass_of("ABCD", gpr8, 0, 4);
isa.add_reg_class(builder); isa.add_reg_class(builder);
let builder = RegClassBuilder::subclass_of(&mut isa, "FPR8", fpr, 0, 8); let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8);
isa.add_reg_class(builder); isa.add_reg_class(builder);
isa isa.finish()
} }