Give RegClassData a reference to its parent RegInfo.
This makes it possible to materialize new RegClass references without requiring a RegInfo reference to be passed around. - Move the RegInfo::toprc() method to RegClassData. - Rename RegClassData::intersect() to intersect_index() and provide a new intersect() which returns a register class. - Remove some &RegInfo parameters that are no longer needed.
This commit is contained in:
@@ -790,22 +790,22 @@ def emit_operand_constraints(
|
|||||||
fmt.format('kind: ConstraintKind::Tied({}),', tied[n])
|
fmt.format('kind: ConstraintKind::Tied({}),', tied[n])
|
||||||
else:
|
else:
|
||||||
fmt.line('kind: ConstraintKind::Reg,')
|
fmt.line('kind: ConstraintKind::Reg,')
|
||||||
fmt.format('regclass: {},', cons)
|
fmt.format('regclass: &{}_DATA,', cons)
|
||||||
elif isinstance(cons, Register):
|
elif isinstance(cons, Register):
|
||||||
assert n not in tied, "Can't tie fixed register operand"
|
assert n not in tied, "Can't tie fixed register operand"
|
||||||
fmt.format(
|
fmt.format(
|
||||||
'kind: ConstraintKind::FixedReg({}),', cons.unit)
|
'kind: ConstraintKind::FixedReg({}),', cons.unit)
|
||||||
fmt.format('regclass: {},', cons.regclass)
|
fmt.format('regclass: &{}_DATA,', cons.regclass)
|
||||||
elif isinstance(cons, int):
|
elif isinstance(cons, int):
|
||||||
# This is a tied output constraint. It should never happen
|
# This is a tied output constraint. It should never happen
|
||||||
# for input constraints.
|
# for input constraints.
|
||||||
assert cons == tied[n], "Invalid tied constraint"
|
assert cons == tied[n], "Invalid tied constraint"
|
||||||
fmt.format('kind: ConstraintKind::Tied({}),', cons)
|
fmt.format('kind: ConstraintKind::Tied({}),', cons)
|
||||||
fmt.format('regclass: {},', recipe.ins[cons])
|
fmt.format('regclass: &{}_DATA,', recipe.ins[cons])
|
||||||
elif isinstance(cons, Stack):
|
elif isinstance(cons, Stack):
|
||||||
assert n not in tied, "Can't tie stack operand"
|
assert n not in tied, "Can't tie stack operand"
|
||||||
fmt.line('kind: ConstraintKind::Stack,')
|
fmt.line('kind: ConstraintKind::Stack,')
|
||||||
fmt.format('regclass: {},', cons.regclass)
|
fmt.format('regclass: &{}_DATA,', cons.regclass)
|
||||||
else:
|
else:
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
'Unsupported constraint {}'.format(cons))
|
'Unsupported constraint {}'.format(cons))
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ def gen_regclass(rc, fmt):
|
|||||||
"""
|
"""
|
||||||
Emit a static data definition for a register class.
|
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('name: "{}",', rc.name)
|
||||||
fmt.format('index: {},', rc.index)
|
fmt.format('index: {},', rc.index)
|
||||||
fmt.format('width: {},', rc.width)
|
fmt.format('width: {},', rc.width)
|
||||||
@@ -58,6 +60,10 @@ def gen_regclass(rc, fmt):
|
|||||||
fmt.format('subclasses: 0x{:x},', rc.subclass_mask())
|
fmt.format('subclasses: 0x{:x},', rc.subclass_mask())
|
||||||
mask = ', '.join('0x{:08x}'.format(x) for x in rc.mask())
|
mask = ', '.join('0x{:08x}'.format(x) for x in rc.mask())
|
||||||
fmt.format('mask: [{}],', 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):
|
def gen_isa(isa, fmt):
|
||||||
@@ -73,22 +79,13 @@ def gen_isa(isa, fmt):
|
|||||||
with fmt.indented('banks: &[', '],'):
|
with fmt.indented('banks: &[', '],'):
|
||||||
for regbank in isa.regbanks:
|
for regbank in isa.regbanks:
|
||||||
gen_regbank(regbank, fmt)
|
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.
|
# 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:
|
for rc in isa.regclasses:
|
||||||
fmt.line('#[allow(dead_code)]')
|
gen_regclass(rc, fmt)
|
||||||
fmt.line(
|
|
||||||
'pub const {}: RegClass = &CLASSES[{}];'
|
|
||||||
.format(rc.name, rc.index))
|
|
||||||
|
|
||||||
# Emit constants for all the register units.
|
# Emit constants for all the register units.
|
||||||
fmt.line('#[allow(dead_code, non_camel_case_types)]')
|
fmt.line('#[allow(dead_code, non_camel_case_types)]')
|
||||||
|
|||||||
@@ -49,14 +49,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn regclasses() {
|
fn regclasses() {
|
||||||
assert_eq!(GPR.intersect(GPR), Some(GPR.into()));
|
assert_eq!(GPR.intersect_index(GPR), Some(GPR.into()));
|
||||||
assert_eq!(GPR.intersect(ABCD), Some(ABCD.into()));
|
assert_eq!(GPR.intersect_index(ABCD), Some(ABCD.into()));
|
||||||
assert_eq!(GPR.intersect(FPR), None);
|
assert_eq!(GPR.intersect_index(FPR), None);
|
||||||
assert_eq!(ABCD.intersect(GPR), Some(ABCD.into()));
|
assert_eq!(ABCD.intersect_index(GPR), Some(ABCD.into()));
|
||||||
assert_eq!(ABCD.intersect(ABCD), Some(ABCD.into()));
|
assert_eq!(ABCD.intersect_index(ABCD), Some(ABCD.into()));
|
||||||
assert_eq!(ABCD.intersect(FPR), None);
|
assert_eq!(ABCD.intersect_index(FPR), None);
|
||||||
assert_eq!(FPR.intersect(FPR), Some(FPR.into()));
|
assert_eq!(FPR.intersect_index(FPR), Some(FPR.into()));
|
||||||
assert_eq!(FPR.intersect(GPR), None);
|
assert_eq!(FPR.intersect_index(GPR), None);
|
||||||
assert_eq!(FPR.intersect(ABCD), None);
|
assert_eq!(FPR.intersect_index(ABCD), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
/// 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.
|
/// first register unit in each allocatable register.
|
||||||
pub mask: RegUnitMask,
|
pub mask: RegUnitMask,
|
||||||
|
|
||||||
|
/// The global `RegInfo` instance containing that this register class.
|
||||||
|
pub info: &'static RegInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegClassData {
|
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
|
/// This register class is guaranteed to exist if the register classes overlap. If the register
|
||||||
/// classes don't overlap, returns `None`.
|
/// classes don't overlap, returns `None`.
|
||||||
pub fn intersect(&self, other: RegClass) -> Option<RegClassIndex> {
|
pub fn intersect_index(&self, other: RegClass) -> Option<RegClassIndex> {
|
||||||
// Compute the set of common subclasses.
|
// Compute the set of common subclasses.
|
||||||
let mask = self.subclasses & other.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<RegClass> {
|
||||||
|
self.intersect_index(other).map(|rci| self.info.rc(rci))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if `other` is a subclass of this register class.
|
/// Returns true if `other` is a subclass of this register class.
|
||||||
/// A register class is considered to be a subclass of itself.
|
/// A register class is considered to be a subclass of itself.
|
||||||
pub fn has_subclass<RCI: Into<RegClassIndex>>(&self, other: RCI) -> bool {
|
pub fn has_subclass<RCI: Into<RegClassIndex>>(&self, other: RCI) -> bool {
|
||||||
self.subclasses & (1 << other.into().0) != 0
|
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.
|
/// Get a specific register unit in this class.
|
||||||
pub fn unit(&self, offset: usize) -> RegUnit {
|
pub fn unit(&self, offset: usize) -> RegUnit {
|
||||||
let uoffset = offset * usize::from(self.width);
|
let uoffset = offset * usize::from(self.width);
|
||||||
@@ -246,7 +259,7 @@ pub struct RegInfo {
|
|||||||
pub banks: &'static [RegBank],
|
pub banks: &'static [RegBank],
|
||||||
|
|
||||||
/// All register classes ordered topologically so a sub-class always follows its parent.
|
/// All register classes ordered topologically so a sub-class always follows its parent.
|
||||||
pub classes: &'static [RegClassData],
|
pub classes: &'static [RegClass],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegInfo {
|
impl RegInfo {
|
||||||
@@ -274,12 +287,7 @@ impl RegInfo {
|
|||||||
|
|
||||||
/// Get the register class corresponding to `idx`.
|
/// Get the register class corresponding to `idx`.
|
||||||
pub fn rc(&self, idx: RegClassIndex) -> RegClass {
|
pub fn rc(&self, idx: RegClassIndex) -> RegClass {
|
||||||
&self.classes[idx.index()]
|
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]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ impl Affinity {
|
|||||||
{
|
{
|
||||||
// If the register classes don't overlap, `intersect` returns `None`, and we
|
// If the register classes don't overlap, `intersect` returns `None`, and we
|
||||||
// just keep our previous affinity.
|
// 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.
|
// This constraint shrinks our preferred register class.
|
||||||
*self = Affinity::Reg(subclass);
|
*self = Affinity::Reg(subclass);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,9 @@ mod tests {
|
|||||||
first: 28,
|
first: 28,
|
||||||
subclasses: 0,
|
subclasses: 0,
|
||||||
mask: [0xf0000000, 0x0000000f, 0],
|
mask: [0xf0000000, 0x0000000f, 0],
|
||||||
|
info: &INFO,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DPR: RegClass = &RegClassData {
|
const DPR: RegClass = &RegClassData {
|
||||||
name: "DPR",
|
name: "DPR",
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -208,6 +210,12 @@ mod tests {
|
|||||||
first: 28,
|
first: 28,
|
||||||
subclasses: 0,
|
subclasses: 0,
|
||||||
mask: [0x50000000, 0x0000000a, 0],
|
mask: [0x50000000, 0x0000000a, 0],
|
||||||
|
info: &INFO,
|
||||||
|
};
|
||||||
|
|
||||||
|
const INFO: RegInfo = RegInfo {
|
||||||
|
banks: &[],
|
||||||
|
classes: &[],
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -445,12 +445,7 @@ impl<'a> Context<'a> {
|
|||||||
ConstraintKind::Reg |
|
ConstraintKind::Reg |
|
||||||
ConstraintKind::Tied(_) => {
|
ConstraintKind::Tied(_) => {
|
||||||
if !op.regclass.contains(cur_reg) {
|
if !op.regclass.contains(cur_reg) {
|
||||||
self.solver.add_var(
|
self.solver.add_var(value, op.regclass, cur_reg);
|
||||||
value,
|
|
||||||
op.regclass,
|
|
||||||
cur_reg,
|
|
||||||
&self.reginfo,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstraintKind::Stack => unreachable!(),
|
ConstraintKind::Stack => unreachable!(),
|
||||||
@@ -572,7 +567,7 @@ impl<'a> Context<'a> {
|
|||||||
let rc = self.reginfo.rc(rci);
|
let rc = self.reginfo.rc(rci);
|
||||||
let reg = self.divert.reg(lv.value, &self.cur.func.locations);
|
let reg = self.divert.reg(lv.value, &self.cur.func.locations);
|
||||||
if self.solver.is_fixed_input_conflict(rc, reg) {
|
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
|
// TODO: Use a looser constraint than the affinity hint. Any allocatable
|
||||||
// register in the top-level register class would be OK. Maybe `add_var`
|
// register in the top-level register class would be OK. Maybe `add_var`
|
||||||
// should take both a preferred class and a required constraint class.
|
// 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
|
// 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
|
// it is not actually constrained by the instruction. We just want it out
|
||||||
// of the way.
|
// of the way.
|
||||||
let toprc = self.reginfo.toprc(rc2);
|
self.solver.add_var(lv.value, rc2.toprc(), reg2);
|
||||||
self.solver.add_var(lv.value, toprc, reg2, &self.reginfo);
|
|
||||||
return true;
|
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.
|
/// The solver needs to be reminded of the available registers before any moves are inserted.
|
||||||
fn shuffle_inputs(&mut self, regs: &mut AllocatableSet) {
|
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() {
|
for m in self.solver.moves() {
|
||||||
self.divert.regmove(m.value, m.from, m.to);
|
self.divert.regmove(m.value, m.from, m.to);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@
|
|||||||
use dbg::DisplayList;
|
use dbg::DisplayList;
|
||||||
use entity::{SparseMap, SparseMapValue};
|
use entity::{SparseMap, SparseMapValue};
|
||||||
use ir::Value;
|
use ir::Value;
|
||||||
use isa::{RegInfo, RegClass, RegUnit};
|
use isa::{RegClass, RegUnit};
|
||||||
use regalloc::allocatable_set::RegSetIter;
|
use regalloc::allocatable_set::RegSetIter;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use super::AllocatableSet;
|
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.
|
/// 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()`.
|
/// This can be changed by calling to `add_kill()`.
|
||||||
pub fn add_var(
|
pub fn add_var(&mut self, value: Value, constraint: RegClass, from: RegUnit) {
|
||||||
&mut self,
|
|
||||||
value: Value,
|
|
||||||
constraint: RegClass,
|
|
||||||
from: RegUnit,
|
|
||||||
reginfo: &RegInfo,
|
|
||||||
) {
|
|
||||||
// Check for existing entries for this value.
|
// Check for existing entries for this value.
|
||||||
if self.regs_in.is_avail(constraint, from) {
|
if self.regs_in.is_avail(constraint, from) {
|
||||||
dbg!(
|
dbg!(
|
||||||
"add_var({}:{}, from={}/%{}) for existing entry",
|
"add_var({}:{}, from={}/%{}) for existing entry",
|
||||||
value,
|
value,
|
||||||
constraint,
|
constraint,
|
||||||
reginfo.display_regunit(from),
|
constraint.info.display_regunit(from),
|
||||||
from
|
from
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -413,8 +407,8 @@ impl Solver {
|
|||||||
dbg!("-> combining constraint with {}", v);
|
dbg!("-> combining constraint with {}", v);
|
||||||
|
|
||||||
// We have an existing variable entry for `value`. Combine the constraints.
|
// We have an existing variable entry for `value`. Combine the constraints.
|
||||||
if let Some(rci) = v.constraint.intersect(constraint) {
|
if let Some(rc) = v.constraint.intersect(constraint) {
|
||||||
v.constraint = reginfo.rc(rci);
|
v.constraint = rc;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// The spiller should have made sure the same value is not used with disjoint
|
// 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: {}",
|
"add_var({}:{}, from={}/%{}) new entry: {}",
|
||||||
value,
|
value,
|
||||||
constraint,
|
constraint,
|
||||||
reginfo.display_regunit(from),
|
constraint.info.display_regunit(from),
|
||||||
from,
|
from,
|
||||||
new_var
|
new_var
|
||||||
);
|
);
|
||||||
@@ -679,7 +673,7 @@ impl Solver {
|
|||||||
/// a register.
|
/// a register.
|
||||||
///
|
///
|
||||||
/// Returns the number of spills that had to be emitted.
|
/// 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();
|
self.collect_moves();
|
||||||
|
|
||||||
let mut avail = regs.clone();
|
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
|
// 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.
|
// register allocator that we can move between all registers in the top-level RC.
|
||||||
let m = self.moves[i].clone();
|
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() {
|
if let Some(reg) = avail.iter(toprc).next() {
|
||||||
dbg!(
|
dbg!(
|
||||||
"breaking cycle at {} with available {} register {}",
|
"breaking cycle at {} with available {} register {}",
|
||||||
m,
|
m,
|
||||||
toprc,
|
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
|
// 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.reassign_in(v10, gpr, r1, r0);
|
||||||
solver.inputs_done();
|
solver.inputs_done();
|
||||||
assert!(solver.quick_solve().is_ok());
|
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)]);
|
assert_eq!(solver.moves(), &[mov(v10, gpr, r1, r0)]);
|
||||||
|
|
||||||
// A bit harder: r0, r1 need to go in r1, r2.
|
// 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.reassign_in(v11, gpr, r1, r2);
|
||||||
solver.inputs_done();
|
solver.inputs_done();
|
||||||
assert!(solver.quick_solve().is_ok());
|
assert!(solver.quick_solve().is_ok());
|
||||||
assert_eq!(solver.schedule_moves(®s, ®info), 0);
|
assert_eq!(solver.schedule_moves(®s), 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
solver.moves(),
|
solver.moves(),
|
||||||
&[mov(v11, gpr, r1, r2), mov(v10, gpr, r0, r1)]
|
&[mov(v11, gpr, r1, r2), mov(v10, gpr, r0, r1)]
|
||||||
@@ -860,7 +854,7 @@ mod tests {
|
|||||||
solver.reassign_in(v11, gpr, r1, r0);
|
solver.reassign_in(v11, gpr, r1, r0);
|
||||||
solver.inputs_done();
|
solver.inputs_done();
|
||||||
assert!(solver.quick_solve().is_ok());
|
assert!(solver.quick_solve().is_ok());
|
||||||
assert_eq!(solver.schedule_moves(®s, ®info), 0);
|
assert_eq!(solver.schedule_moves(®s), 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
solver.moves(),
|
solver.moves(),
|
||||||
&[
|
&[
|
||||||
@@ -899,7 +893,7 @@ mod tests {
|
|||||||
solver.reassign_in(v12, s, s3, s1);
|
solver.reassign_in(v12, s, s3, s1);
|
||||||
solver.inputs_done();
|
solver.inputs_done();
|
||||||
assert!(solver.quick_solve().is_ok());
|
assert!(solver.quick_solve().is_ok());
|
||||||
assert_eq!(solver.schedule_moves(®s, ®info), 0);
|
assert_eq!(solver.schedule_moves(®s), 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
solver.moves(),
|
solver.moves(),
|
||||||
&[
|
&[
|
||||||
@@ -920,7 +914,7 @@ mod tests {
|
|||||||
solver.reassign_in(v10, d, d1, d0);
|
solver.reassign_in(v10, d, d1, d0);
|
||||||
solver.inputs_done();
|
solver.inputs_done();
|
||||||
assert!(solver.quick_solve().is_ok());
|
assert!(solver.quick_solve().is_ok());
|
||||||
assert_eq!(solver.schedule_moves(®s, ®info), 0);
|
assert_eq!(solver.schedule_moves(®s), 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
solver.moves(),
|
solver.moves(),
|
||||||
&[
|
&[
|
||||||
|
|||||||
Reference in New Issue
Block a user