diff --git a/cranelift/codegen/meta/src/cdsl/isa.rs b/cranelift/codegen/meta/src/cdsl/isa.rs index 65edbbbb8b..86b40a12c3 100644 --- a/cranelift/codegen/meta/src/cdsl/isa.rs +++ b/cranelift/codegen/meta/src/cdsl/isa.rs @@ -1,192 +1,18 @@ -use cranelift_entity::PrimaryMap; - -use super::regs::{ - RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegClassProto, -}; +use super::regs::IsaRegs; use super::settings::SettingGroup; pub struct TargetIsa { pub name: &'static str, - pub reg_banks: PrimaryMap, - pub reg_classes: PrimaryMap, pub settings: SettingGroup, + pub regs: IsaRegs, } impl TargetIsa { - pub fn new(name: &'static str, settings: SettingGroup) -> Self { + pub fn new(name: &'static str, settings: SettingGroup, regs: IsaRegs) -> Self { Self { name, - reg_banks: PrimaryMap::new(), - reg_classes: PrimaryMap::new(), settings, + regs, } } } - -pub struct TargetIsaBuilder { - isa: TargetIsa, -} - -impl TargetIsaBuilder { - pub fn new(name: &'static str, settings: SettingGroup) -> Self { - Self { - isa: TargetIsa::new(name, settings), - } - } - - pub fn add_reg_bank(&mut self, builder: RegBankBuilder) -> RegBankIndex { - let first_unit = if self.isa.reg_banks.len() == 0 { - 0 - } else { - let last = &self.isa.reg_banks.last().unwrap(); - let first_available_unit = (last.first_unit + last.units) as i8; - let units = builder.units; - let align = if units.is_power_of_two() { - units - } else { - units.next_power_of_two() - } as i8; - (first_available_unit + align - 1) & -align - } as u8; - - self.isa.reg_banks.push(RegBank::new( - builder.name, - first_unit, - builder.units, - builder.names, - builder.prefix, - builder - .pressure_tracking - .expect("Pressure tracking must be explicitly set"), - )) - } - - pub fn add_reg_class(&mut self, builder: RegClassBuilder) -> RegClassIndex { - let class_index = self.isa.reg_classes.next_key(); - - // 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); - - let count = if builder.count != 0 { - builder.count - } else { - reg_bank_units / width - }; - - let reg_class = RegClass::new(builder.name, class_index, width, bank, toprc, count, start); - self.isa.reg_classes.push(reg_class); - - let reg_bank = self.isa.reg_banks.get_mut(bank).unwrap(); - reg_bank.classes.push(class_index); - - class_index - } - - /// Checks that the set of register classes satisfies: - /// - /// 1. Closed under intersection: The intersection of any two register - /// classes in the set is either empty or identical to a member of the - /// set. - /// 2. There are no identical classes under different names. - /// 3. Classes are sorted topologically such that all subclasses have a - /// higher index that the superclass. - pub fn finish(self) -> TargetIsa { - for reg_bank in self.isa.reg_banks.values() { - for i1 in reg_bank.classes.iter() { - for i2 in reg_bank.classes.iter() { - if i1 >= i2 { - continue; - } - - let rc1 = self.isa.reg_classes.get(*i1).unwrap(); - let rc2 = self.isa.reg_classes.get(*i2).unwrap(); - - let rc1_mask = rc1.mask(0); - let rc2_mask = rc2.mask(0); - - assert!( - rc1.width != rc2.width || rc1_mask != rc2_mask, - "no duplicates" - ); - if rc1.width != rc2.width { - continue; - } - - let mut intersect = Vec::new(); - for (a, b) in rc1_mask.iter().zip(rc2_mask.iter()) { - intersect.push(a & b); - } - if intersect == vec![0; intersect.len()] { - continue; - } - - // Classes must be topologically ordered, so the intersection can't be the - // superclass. - assert!(intersect != rc1_mask); - - // If the intersection is the second one, then it must be a subclass. - if intersect == rc2_mask { - assert!(self - .isa - .reg_classes - .get(*i1) - .unwrap() - .subclasses - .iter() - .find(|x| **x == *i2) - .is_some()); - } - } - } - } - - // This limit should be coordinated with the `RegClassMask` and `RegClassIndex` types in - // isa/registers.rs of the non-meta code. - assert!( - self.isa.reg_classes.len() <= 32, - "Too many register classes" - ); - - // The maximum number of top-level register classes which have pressure tracking should be - // kept in sync with the MAX_TRACKED_TOPRCS constant in isa/registers.rs of the non-meta - // code. - let num_toplevel = self - .isa - .reg_classes - .values() - .filter(|x| { - x.toprc == x.index && self.isa.reg_banks.get(x.bank).unwrap().pressure_tracking - }) - .count(); - assert!(num_toplevel <= 4, "Too many top-level register classes"); - - self.isa - } -} diff --git a/cranelift/codegen/meta/src/cdsl/regs.rs b/cranelift/codegen/meta/src/cdsl/regs.rs index 11605de563..22d7d3a6c1 100644 --- a/cranelift/codegen/meta/src/cdsl/regs.rs +++ b/cranelift/codegen/meta/src/cdsl/regs.rs @@ -1,5 +1,4 @@ -use cranelift_entity::entity_impl; -use cranelift_entity::EntityRef; +use cranelift_entity::{entity_impl, EntityRef, PrimaryMap}; #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct RegBankIndex(u32); @@ -178,3 +177,179 @@ impl RegBankBuilder { self } } + +pub struct IsaRegsBuilder { + pub banks: PrimaryMap, + pub classes: PrimaryMap, +} + +impl IsaRegsBuilder { + pub fn new() -> Self { + Self { + banks: PrimaryMap::new(), + classes: PrimaryMap::new(), + } + } + + pub fn add_bank(&mut self, builder: RegBankBuilder) -> RegBankIndex { + let first_unit = if self.banks.len() == 0 { + 0 + } else { + let last = &self.banks.last().unwrap(); + let first_available_unit = (last.first_unit + last.units) as i8; + let units = builder.units; + let align = if units.is_power_of_two() { + units + } else { + units.next_power_of_two() + } as i8; + (first_available_unit + align - 1) & -align + } as u8; + + self.banks.push(RegBank::new( + builder.name, + first_unit, + builder.units, + builder.names, + builder.prefix, + builder + .pressure_tracking + .expect("Pressure tracking must be explicitly set"), + )) + } + + pub fn add_class(&mut self, builder: RegClassBuilder) -> RegClassIndex { + let class_index = self.classes.next_key(); + + // Finish delayed construction of RegClass. + let (bank, toprc, start, width) = match builder.proto { + RegClassProto::TopLevel(bank_index) => { + self.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.classes.get(parent_class_index).unwrap(); + (parent.bank, parent.toprc, parent.start, parent.width) + }; + for reg_class in self.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.banks.get(bank).unwrap().units; + assert!(start < reg_bank_units); + + let count = if builder.count != 0 { + builder.count + } else { + reg_bank_units / width + }; + + let reg_class = RegClass::new(builder.name, class_index, width, bank, toprc, count, start); + self.classes.push(reg_class); + + let reg_bank = self.banks.get_mut(bank).unwrap(); + reg_bank.classes.push(class_index); + + class_index + } + + /// Checks that the set of register classes satisfies: + /// + /// 1. Closed under intersection: The intersection of any two register + /// classes in the set is either empty or identical to a member of the + /// set. + /// 2. There are no identical classes under different names. + /// 3. Classes are sorted topologically such that all subclasses have a + /// higher index that the superclass. + pub fn finish(self) -> IsaRegs { + for reg_bank in self.banks.values() { + for i1 in reg_bank.classes.iter() { + for i2 in reg_bank.classes.iter() { + if i1 >= i2 { + continue; + } + + let rc1 = self.classes.get(*i1).unwrap(); + let rc2 = self.classes.get(*i2).unwrap(); + + let rc1_mask = rc1.mask(0); + let rc2_mask = rc2.mask(0); + + assert!( + rc1.width != rc2.width || rc1_mask != rc2_mask, + "no duplicates" + ); + if rc1.width != rc2.width { + continue; + } + + let mut intersect = Vec::new(); + for (a, b) in rc1_mask.iter().zip(rc2_mask.iter()) { + intersect.push(a & b); + } + if intersect == vec![0; intersect.len()] { + continue; + } + + // Classes must be topologically ordered, so the intersection can't be the + // superclass. + assert!(intersect != rc1_mask); + + // If the intersection is the second one, then it must be a subclass. + if intersect == rc2_mask { + assert!(self + .classes + .get(*i1) + .unwrap() + .subclasses + .iter() + .find(|x| **x == *i2) + .is_some()); + } + } + } + } + + // This limit should be coordinated with the `RegClassMask` and `RegClassIndex` types in + // isa/registers.rs of the non-meta code. + assert!(self.classes.len() <= 32, "Too many register classes"); + + // The maximum number of top-level register classes which have pressure tracking should be + // kept in sync with the MAX_TRACKED_TOPRCS constant in isa/registers.rs of the non-meta + // code. + let num_toplevel = self + .classes + .values() + .filter(|x| x.toprc == x.index && self.banks.get(x.bank).unwrap().pressure_tracking) + .count(); + assert!(num_toplevel <= 4, "Too many top-level register classes"); + + IsaRegs::new(self.banks, self.classes) + } +} + +pub struct IsaRegs { + pub banks: PrimaryMap, + pub classes: PrimaryMap, +} + +impl IsaRegs { + fn new( + banks: PrimaryMap, + classes: PrimaryMap, + ) -> Self { + Self { banks, classes } + } +} diff --git a/cranelift/codegen/meta/src/gen_registers.rs b/cranelift/codegen/meta/src/gen_registers.rs index 5fb326e84e..8488dab228 100644 --- a/cranelift/codegen/meta/src/gen_registers.rs +++ b/cranelift/codegen/meta/src/gen_registers.rs @@ -32,7 +32,7 @@ fn gen_regbank(fmt: &mut Formatter, reg_bank: &RegBank) { } fn gen_regclass(isa: &TargetIsa, reg_class: &RegClass, fmt: &mut Formatter) { - let reg_bank = isa.reg_banks.get(reg_class.bank).unwrap(); + let reg_bank = isa.regs.banks.get(reg_class.bank).unwrap(); let mask: Vec = reg_class .mask(reg_bank.first_unit) @@ -86,7 +86,7 @@ fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) { fmt.line("banks: &["); // Bank descriptors. fmt.indent(|fmt| { - for reg_bank in isa.reg_banks.values() { + for reg_bank in isa.regs.banks.values() { gen_regbank(fmt, ®_bank); } }); @@ -94,7 +94,7 @@ fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) { // References to register classes. fmt.line("classes: &["); fmt.indent(|fmt| { - for reg_class in isa.reg_classes.values() { + for reg_class in isa.regs.classes.values() { fmt.line(&format!("&{}_DATA,", reg_class.name)); } }); @@ -103,7 +103,7 @@ fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) { fmt.line("};"); // Register class descriptors. - for rc in isa.reg_classes.values() { + for rc in isa.regs.classes.values() { gen_regclass(&isa, rc, fmt); } @@ -112,7 +112,7 @@ fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) { fmt.line("#[derive(Clone, Copy)]"); fmt.line("pub enum RU {"); fmt.indent(|fmt| { - for reg_bank in isa.reg_banks.values() { + for reg_bank in isa.regs.banks.values() { gen_regbank_units(reg_bank, fmt); } }); diff --git a/cranelift/codegen/meta/src/isa/arm32/mod.rs b/cranelift/codegen/meta/src/isa/arm32/mod.rs index ad615dfad2..e3decd4da0 100644 --- a/cranelift/codegen/meta/src/isa/arm32/mod.rs +++ b/cranelift/codegen/meta/src/isa/arm32/mod.rs @@ -1,5 +1,5 @@ -use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder}; -use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder}; +use crate::cdsl::isa::TargetIsa; +use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder}; fn define_settings(_shared: &SettingGroup) -> SettingGroup { @@ -7,39 +7,45 @@ fn define_settings(_shared: &SettingGroup) -> SettingGroup { setting.finish() } -pub fn define(shared_settings: &SettingGroup) -> TargetIsa { - let mut isa = TargetIsaBuilder::new("arm32", define_settings(shared_settings)); +fn define_regs() -> IsaRegs { + let mut regs = IsaRegsBuilder::new(); let builder = RegBankBuilder::new("FloatRegs", "s") .units(64) .track_pressure(true); - let float_regs = isa.add_reg_bank(builder); + let float_regs = regs.add_bank(builder); let builder = RegBankBuilder::new("IntRegs", "r") .units(16) .track_pressure(true); - let int_regs = isa.add_reg_bank(builder); + let int_regs = regs.add_bank(builder); let builder = RegBankBuilder::new("FlagRegs", "") .units(1) .names(vec!["nzcv"]) .track_pressure(false); - let flag_reg = isa.add_reg_bank(builder); + let flag_reg = regs.add_bank(builder); let builder = RegClassBuilder::new_toplevel("S", float_regs).count(32); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("D", float_regs).width(2); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("Q", float_regs).width(4); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("GPR", int_regs); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg); - isa.add_reg_class(builder); + regs.add_class(builder); - isa.finish() + regs.finish() +} + +pub fn define(shared_settings: &SettingGroup) -> TargetIsa { + let settings = define_settings(shared_settings); + let regs = define_regs(); + TargetIsa::new("arm32", settings, regs) } diff --git a/cranelift/codegen/meta/src/isa/arm64/mod.rs b/cranelift/codegen/meta/src/isa/arm64/mod.rs index 7abdd51ca3..baca5226ab 100644 --- a/cranelift/codegen/meta/src/isa/arm64/mod.rs +++ b/cranelift/codegen/meta/src/isa/arm64/mod.rs @@ -1,5 +1,5 @@ -use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder}; -use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder}; +use crate::cdsl::isa::TargetIsa; +use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder}; fn define_settings(_shared: &SettingGroup) -> SettingGroup { @@ -7,35 +7,41 @@ fn define_settings(_shared: &SettingGroup) -> SettingGroup { setting.finish() } -pub fn define(shared_settings: &SettingGroup) -> TargetIsa { - let mut isa = TargetIsaBuilder::new("arm64", define_settings(shared_settings)); +fn define_registers() -> IsaRegs { + let mut regs = IsaRegsBuilder::new(); // The `x31` regunit serves as the stack pointer / zero register depending on context. We // reserve it and don't model the difference. let builder = RegBankBuilder::new("IntRegs", "x") .units(32) .track_pressure(true); - let int_regs = isa.add_reg_bank(builder); + let int_regs = regs.add_bank(builder); let builder = RegBankBuilder::new("FloatRegs", "v") .units(32) .track_pressure(true); - let float_regs = isa.add_reg_bank(builder); + let float_regs = regs.add_bank(builder); let builder = RegBankBuilder::new("FlagRegs", "") .units(1) .names(vec!["nzcv"]) .track_pressure(false); - let flag_reg = isa.add_reg_bank(builder); + let flag_reg = regs.add_bank(builder); let builder = RegClassBuilder::new_toplevel("GPR", int_regs); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("FPR", float_regs); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg); - isa.add_reg_class(builder); + regs.add_class(builder); - isa.finish() + regs.finish() +} + +pub fn define(shared_settings: &SettingGroup) -> TargetIsa { + let settings = define_settings(shared_settings); + let regs = define_registers(); + TargetIsa::new("arm64", settings, regs) } diff --git a/cranelift/codegen/meta/src/isa/riscv/mod.rs b/cranelift/codegen/meta/src/isa/riscv/mod.rs index dbf0e16e8a..040d088386 100644 --- a/cranelift/codegen/meta/src/isa/riscv/mod.rs +++ b/cranelift/codegen/meta/src/isa/riscv/mod.rs @@ -1,5 +1,5 @@ -use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder}; -use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder}; +use crate::cdsl::isa::TargetIsa; +use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; fn define_settings(shared: &SettingGroup) -> SettingGroup { @@ -54,24 +54,30 @@ fn define_settings(shared: &SettingGroup) -> SettingGroup { setting.finish() } -pub fn define(shared_settings: &SettingGroup) -> TargetIsa { - let mut isa = TargetIsaBuilder::new("riscv", define_settings(shared_settings)); +fn define_registers() -> IsaRegs { + let mut regs = IsaRegsBuilder::new(); let builder = RegBankBuilder::new("IntRegs", "x") .units(32) .track_pressure(true); - let int_regs = isa.add_reg_bank(builder); + let int_regs = regs.add_bank(builder); let builder = RegBankBuilder::new("FloatRegs", "f") .units(32) .track_pressure(true); - let float_regs = isa.add_reg_bank(builder); + let float_regs = regs.add_bank(builder); let builder = RegClassBuilder::new_toplevel("GPR", int_regs); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("FPR", float_regs); - isa.add_reg_class(builder); + regs.add_class(builder); - isa.finish() + regs.finish() +} + +pub fn define(shared_settings: &SettingGroup) -> TargetIsa { + let settings = define_settings(shared_settings); + let regs = define_registers(); + TargetIsa::new("riscv", settings, regs) } diff --git a/cranelift/codegen/meta/src/isa/x86/mod.rs b/cranelift/codegen/meta/src/isa/x86/mod.rs index 63087a2697..da64f3cc50 100644 --- a/cranelift/codegen/meta/src/isa/x86/mod.rs +++ b/cranelift/codegen/meta/src/isa/x86/mod.rs @@ -1,8 +1,8 @@ -use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder}; -use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder}; +use crate::cdsl::isa::TargetIsa; +use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; -pub fn define_settings(_shared: &SettingGroup) -> SettingGroup { +fn define_settings(_shared: &SettingGroup) -> SettingGroup { let mut settings = SettingGroupBuilder::new("x86"); // CPUID.01H:ECX @@ -68,49 +68,49 @@ pub fn define_settings(_shared: &SettingGroup) -> SettingGroup { settings.finish() } -fn define_registers(isa: &mut TargetIsaBuilder) { +fn define_registers() -> IsaRegs { + let mut regs = IsaRegsBuilder::new(); + let builder = RegBankBuilder::new("IntRegs", "r") .units(16) .names(vec!["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"]) .track_pressure(true); - let int_regs = isa.add_reg_bank(builder); + let int_regs = regs.add_bank(builder); let builder = RegBankBuilder::new("FloatRegs", "xmm") .units(16) .track_pressure(true); - let float_regs = isa.add_reg_bank(builder); + let float_regs = regs.add_bank(builder); let builder = RegBankBuilder::new("FlagRegs", "") .units(1) .names(vec!["rflags"]) .track_pressure(false); - let flag_reg = isa.add_reg_bank(builder); + let flag_reg = regs.add_bank(builder); let builder = RegClassBuilder::new_toplevel("GPR", int_regs); - let gpr = isa.add_reg_class(builder); + let gpr = regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("FPR", float_regs); - let fpr = isa.add_reg_class(builder); + let fpr = regs.add_class(builder); let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::subclass_of("GPR8", gpr, 0, 8); - let gpr8 = isa.add_reg_class(builder); + let gpr8 = regs.add_class(builder); let builder = RegClassBuilder::subclass_of("ABCD", gpr8, 0, 4); - isa.add_reg_class(builder); + regs.add_class(builder); let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8); - isa.add_reg_class(builder); + regs.add_class(builder); + + regs.finish() } pub fn define(shared_settings: &SettingGroup) -> TargetIsa { let settings = define_settings(shared_settings); - - let mut isa = TargetIsaBuilder::new("x86", settings); - - define_registers(&mut isa); - - isa.finish() + let regs = define_registers(); + TargetIsa::new("x86", settings, regs) }