diff --git a/lib/cretonne/meta/gen_encoding.py b/lib/cretonne/meta/gen_encoding.py index a9bef26dba..0228c33839 100644 --- a/lib/cretonne/meta/gen_encoding.py +++ b/lib/cretonne/meta/gen_encoding.py @@ -790,22 +790,22 @@ def emit_operand_constraints( fmt.format('kind: ConstraintKind::Tied({}),', tied[n]) else: fmt.line('kind: ConstraintKind::Reg,') - fmt.format('regclass: {},', cons) + fmt.format('regclass: &{}_DATA,', cons) elif isinstance(cons, Register): assert n not in tied, "Can't tie fixed register operand" fmt.format( 'kind: ConstraintKind::FixedReg({}),', cons.unit) - fmt.format('regclass: {},', cons.regclass) + fmt.format('regclass: &{}_DATA,', cons.regclass) elif isinstance(cons, int): # This is a tied output constraint. It should never happen # for input constraints. assert cons == tied[n], "Invalid tied constraint" fmt.format('kind: ConstraintKind::Tied({}),', cons) - fmt.format('regclass: {},', recipe.ins[cons]) + fmt.format('regclass: &{}_DATA,', recipe.ins[cons]) elif isinstance(cons, Stack): assert n not in tied, "Can't tie stack operand" fmt.line('kind: ConstraintKind::Stack,') - fmt.format('regclass: {},', cons.regclass) + fmt.format('regclass: &{}_DATA,', cons.regclass) else: raise AssertionError( 'Unsupported constraint {}'.format(cons)) diff --git a/lib/cretonne/meta/gen_registers.py b/lib/cretonne/meta/gen_registers.py index 6d41a40330..9056441cb1 100644 --- a/lib/cretonne/meta/gen_registers.py +++ b/lib/cretonne/meta/gen_registers.py @@ -48,7 +48,9 @@ def gen_regclass(rc, fmt): """ Emit a static data definition for a register class. """ - with fmt.indented('RegClassData {', '},'): + with fmt.indented( + 'pub static {}_DATA: RegClassData = RegClassData {{' + .format(rc.name), '};'): fmt.format('name: "{}",', rc.name) fmt.format('index: {},', rc.index) fmt.format('width: {},', rc.width) @@ -58,6 +60,10 @@ def gen_regclass(rc, fmt): fmt.format('subclasses: 0x{:x},', rc.subclass_mask()) mask = ', '.join('0x{:08x}'.format(x) for x in rc.mask()) fmt.format('mask: [{}],', mask) + fmt.line('info: &INFO,') + # Also emit a convenient reference for use by hand-written code. + fmt.line('#[allow(dead_code)]') + fmt.format('pub static {0}: RegClass = &{0}_DATA;', rc.name) def gen_isa(isa, fmt): @@ -73,22 +79,13 @@ def gen_isa(isa, fmt): with fmt.indented('banks: &[', '],'): for regbank in isa.regbanks: gen_regbank(regbank, fmt) - fmt.line('classes: &CLASSES,') + with fmt.indented('classes: &[', '],'): + for rc in isa.regclasses: + fmt.format('&{}_DATA,', rc.name) # Register class descriptors. - with fmt.indented( - 'const CLASSES: [RegClassData; {}] = [' - .format(len(isa.regclasses)), '];'): - for idx, rc in enumerate(isa.regclasses): - assert idx == rc.index - gen_regclass(rc, fmt) - - # Emit constants referencing the register classes. for rc in isa.regclasses: - fmt.line('#[allow(dead_code)]') - fmt.line( - 'pub const {}: RegClass = &CLASSES[{}];' - .format(rc.name, rc.index)) + gen_regclass(rc, fmt) # Emit constants for all the register units. fmt.line('#[allow(dead_code, non_camel_case_types)]') diff --git a/lib/cretonne/src/isa/intel/registers.rs b/lib/cretonne/src/isa/intel/registers.rs index 14b4050d94..fcb1cf4445 100644 --- a/lib/cretonne/src/isa/intel/registers.rs +++ b/lib/cretonne/src/isa/intel/registers.rs @@ -49,14 +49,14 @@ mod tests { #[test] fn regclasses() { - assert_eq!(GPR.intersect(GPR), Some(GPR.into())); - assert_eq!(GPR.intersect(ABCD), Some(ABCD.into())); - assert_eq!(GPR.intersect(FPR), None); - assert_eq!(ABCD.intersect(GPR), Some(ABCD.into())); - assert_eq!(ABCD.intersect(ABCD), Some(ABCD.into())); - assert_eq!(ABCD.intersect(FPR), None); - assert_eq!(FPR.intersect(FPR), Some(FPR.into())); - assert_eq!(FPR.intersect(GPR), None); - assert_eq!(FPR.intersect(ABCD), None); + assert_eq!(GPR.intersect_index(GPR), Some(GPR.into())); + assert_eq!(GPR.intersect_index(ABCD), Some(ABCD.into())); + assert_eq!(GPR.intersect_index(FPR), None); + assert_eq!(ABCD.intersect_index(GPR), Some(ABCD.into())); + assert_eq!(ABCD.intersect_index(ABCD), Some(ABCD.into())); + assert_eq!(ABCD.intersect_index(FPR), None); + assert_eq!(FPR.intersect_index(FPR), Some(FPR.into())); + assert_eq!(FPR.intersect_index(GPR), None); + assert_eq!(FPR.intersect_index(ABCD), None); } } diff --git a/lib/cretonne/src/isa/registers.rs b/lib/cretonne/src/isa/registers.rs index a1de76d97e..961aae9c83 100644 --- a/lib/cretonne/src/isa/registers.rs +++ b/lib/cretonne/src/isa/registers.rs @@ -145,14 +145,17 @@ pub struct RegClassData { /// 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 that this register class. + pub info: &'static RegInfo, } impl RegClassData { - /// Get the register class corresponding to the intersection of `self` and `other`. + /// 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(&self, other: RegClass) -> Option { + pub fn intersect_index(&self, other: RegClass) -> Option { // Compute the set of common subclasses. let mask = self.subclasses & other.subclasses; @@ -166,12 +169,22 @@ impl RegClassData { } } + /// Get the intersection of `self` and `other`. + pub fn intersect(&self, other: RegClass) -> Option { + 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>(&self, other: RCI) -> bool { self.subclasses & (1 << other.into().0) != 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); @@ -246,7 +259,7 @@ pub struct RegInfo { pub banks: &'static [RegBank], /// All register classes ordered topologically so a sub-class always follows its parent. - pub classes: &'static [RegClassData], + pub classes: &'static [RegClass], } impl RegInfo { @@ -274,12 +287,7 @@ impl RegInfo { /// 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 `rc`. - pub fn toprc(&self, rc: RegClass) -> RegClass { - &self.classes[rc.toprc as usize] + self.classes[idx.index()] } } diff --git a/lib/cretonne/src/regalloc/affinity.rs b/lib/cretonne/src/regalloc/affinity.rs index d7dede67d2..5cfd3ada97 100644 --- a/lib/cretonne/src/regalloc/affinity.rs +++ b/lib/cretonne/src/regalloc/affinity.rs @@ -95,7 +95,7 @@ impl Affinity { { // If the register classes don't overlap, `intersect` returns `None`, and we // just keep our previous affinity. - if let Some(subclass) = constraint.regclass.intersect(reg_info.rc(rc)) { + if let Some(subclass) = constraint.regclass.intersect_index(reg_info.rc(rc)) { // This constraint shrinks our preferred register class. *self = Affinity::Reg(subclass); } diff --git a/lib/cretonne/src/regalloc/allocatable_set.rs b/lib/cretonne/src/regalloc/allocatable_set.rs index c765d73b1a..c6f8c86a07 100644 --- a/lib/cretonne/src/regalloc/allocatable_set.rs +++ b/lib/cretonne/src/regalloc/allocatable_set.rs @@ -198,7 +198,9 @@ mod tests { first: 28, subclasses: 0, mask: [0xf0000000, 0x0000000f, 0], + info: &INFO, }; + const DPR: RegClass = &RegClassData { name: "DPR", index: 0, @@ -208,6 +210,12 @@ mod tests { first: 28, subclasses: 0, mask: [0x50000000, 0x0000000a, 0], + info: &INFO, + }; + + const INFO: RegInfo = RegInfo { + banks: &[], + classes: &[], }; #[test] diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index 9aa5f48b2b..115fc2513b 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -445,12 +445,7 @@ impl<'a> Context<'a> { ConstraintKind::Reg | ConstraintKind::Tied(_) => { if !op.regclass.contains(cur_reg) { - self.solver.add_var( - value, - op.regclass, - cur_reg, - &self.reginfo, - ); + self.solver.add_var(value, op.regclass, cur_reg); } } ConstraintKind::Stack => unreachable!(), @@ -572,7 +567,7 @@ impl<'a> Context<'a> { let rc = self.reginfo.rc(rci); let reg = self.divert.reg(lv.value, &self.cur.func.locations); if self.solver.is_fixed_input_conflict(rc, reg) { - self.solver.add_var(lv.value, rc, reg, &self.reginfo); + self.solver.add_var(lv.value, rc, reg); } } } @@ -638,7 +633,7 @@ impl<'a> Context<'a> { // TODO: Use a looser constraint than the affinity hint. Any allocatable // register in the top-level register class would be OK. Maybe `add_var` // should take both a preferred class and a required constraint class. - self.solver.add_var(lv.value, rc2, reg2, &self.reginfo); + self.solver.add_var(lv.value, rc2, reg2); } } } @@ -712,8 +707,7 @@ impl<'a> Context<'a> { // The new variable gets to roam the whole top-level register class because // it is not actually constrained by the instruction. We just want it out // of the way. - let toprc = self.reginfo.toprc(rc2); - self.solver.add_var(lv.value, toprc, reg2, &self.reginfo); + self.solver.add_var(lv.value, rc2.toprc(), reg2); return true; } } @@ -730,7 +724,7 @@ impl<'a> Context<'a> { /// /// The solver needs to be reminded of the available registers before any moves are inserted. fn shuffle_inputs(&mut self, regs: &mut AllocatableSet) { - self.solver.schedule_moves(regs, &self.reginfo); + self.solver.schedule_moves(regs); for m in self.solver.moves() { self.divert.regmove(m.value, m.from, m.to); diff --git a/lib/cretonne/src/regalloc/solver.rs b/lib/cretonne/src/regalloc/solver.rs index e3491ba8cc..0256b0d0d1 100644 --- a/lib/cretonne/src/regalloc/solver.rs +++ b/lib/cretonne/src/regalloc/solver.rs @@ -101,7 +101,7 @@ use dbg::DisplayList; use entity::{SparseMap, SparseMapValue}; use ir::Value; -use isa::{RegInfo, RegClass, RegUnit}; +use isa::{RegClass, RegUnit}; use regalloc::allocatable_set::RegSetIter; use std::fmt; use super::AllocatableSet; @@ -391,20 +391,14 @@ impl Solver { /// /// It is assumed initially that the value is also live on the output side of the instruction. /// This can be changed by calling to `add_kill()`. - pub fn add_var( - &mut self, - value: Value, - constraint: RegClass, - from: RegUnit, - reginfo: &RegInfo, - ) { + pub fn add_var(&mut self, value: Value, constraint: RegClass, from: RegUnit) { // Check for existing entries for this value. if self.regs_in.is_avail(constraint, from) { dbg!( "add_var({}:{}, from={}/%{}) for existing entry", value, constraint, - reginfo.display_regunit(from), + constraint.info.display_regunit(from), from ); @@ -413,8 +407,8 @@ impl Solver { dbg!("-> combining constraint with {}", v); // We have an existing variable entry for `value`. Combine the constraints. - if let Some(rci) = v.constraint.intersect(constraint) { - v.constraint = reginfo.rc(rci); + if let Some(rc) = v.constraint.intersect(constraint) { + v.constraint = rc; return; } else { // The spiller should have made sure the same value is not used with disjoint @@ -443,7 +437,7 @@ impl Solver { "add_var({}:{}, from={}/%{}) new entry: {}", value, constraint, - reginfo.display_regunit(from), + constraint.info.display_regunit(from), from, new_var ); @@ -679,7 +673,7 @@ impl Solver { /// a register. /// /// Returns the number of spills that had to be emitted. - pub fn schedule_moves(&mut self, regs: &AllocatableSet, reginfo: &RegInfo) -> usize { + pub fn schedule_moves(&mut self, regs: &AllocatableSet) -> usize { self.collect_moves(); let mut avail = regs.clone(); @@ -727,13 +721,13 @@ impl Solver { // Check the top-level register class for an available register. It is an axiom of the // register allocator that we can move between all registers in the top-level RC. let m = self.moves[i].clone(); - let toprc = reginfo.toprc(m.rc); + let toprc = m.rc.toprc(); if let Some(reg) = avail.iter(toprc).next() { dbg!( "breaking cycle at {} with available {} register {}", m, toprc, - reginfo.display_regunit(reg) + toprc.info.display_regunit(reg) ); // Alter the move so it is guaranteed to be picked up when we loop. It is important @@ -838,7 +832,7 @@ mod tests { solver.reassign_in(v10, gpr, r1, r0); solver.inputs_done(); assert!(solver.quick_solve().is_ok()); - assert_eq!(solver.schedule_moves(®s, ®info), 0); + assert_eq!(solver.schedule_moves(®s), 0); assert_eq!(solver.moves(), &[mov(v10, gpr, r1, r0)]); // A bit harder: r0, r1 need to go in r1, r2. @@ -848,7 +842,7 @@ mod tests { solver.reassign_in(v11, gpr, r1, r2); solver.inputs_done(); assert!(solver.quick_solve().is_ok()); - assert_eq!(solver.schedule_moves(®s, ®info), 0); + assert_eq!(solver.schedule_moves(®s), 0); assert_eq!( solver.moves(), &[mov(v11, gpr, r1, r2), mov(v10, gpr, r0, r1)] @@ -860,7 +854,7 @@ mod tests { solver.reassign_in(v11, gpr, r1, r0); solver.inputs_done(); assert!(solver.quick_solve().is_ok()); - assert_eq!(solver.schedule_moves(®s, ®info), 0); + assert_eq!(solver.schedule_moves(®s), 0); assert_eq!( solver.moves(), &[ @@ -899,7 +893,7 @@ mod tests { solver.reassign_in(v12, s, s3, s1); solver.inputs_done(); assert!(solver.quick_solve().is_ok()); - assert_eq!(solver.schedule_moves(®s, ®info), 0); + assert_eq!(solver.schedule_moves(®s), 0); assert_eq!( solver.moves(), &[ @@ -920,7 +914,7 @@ mod tests { solver.reassign_in(v10, d, d1, d0); solver.inputs_done(); assert!(solver.quick_solve().is_ok()); - assert_eq!(solver.schedule_moves(®s, ®info), 0); + assert_eq!(solver.schedule_moves(®s), 0); assert_eq!( solver.moves(), &[