Address review comments: more doc comments and some minor refactorings.
This commit is contained in:
21
src/cfg.rs
21
src/cfg.rs
@@ -38,23 +38,24 @@ pub struct CFGInfo {
|
|||||||
|
|
||||||
impl CFGInfo {
|
impl CFGInfo {
|
||||||
pub fn new<F: Function>(f: &F) -> Result<CFGInfo, RegAllocError> {
|
pub fn new<F: Function>(f: &F) -> Result<CFGInfo, RegAllocError> {
|
||||||
let postorder =
|
let postorder = postorder::calculate(f.num_blocks(), f.entry_block(), |block| {
|
||||||
postorder::calculate(f.blocks(), f.entry_block(), |block| f.block_succs(block));
|
f.block_succs(block)
|
||||||
|
});
|
||||||
let domtree = domtree::calculate(
|
let domtree = domtree::calculate(
|
||||||
f.blocks(),
|
f.num_blocks(),
|
||||||
|block| f.block_preds(block),
|
|block| f.block_preds(block),
|
||||||
&postorder[..],
|
&postorder[..],
|
||||||
f.entry_block(),
|
f.entry_block(),
|
||||||
);
|
);
|
||||||
let mut insn_block = vec![Block::invalid(); f.insts()];
|
let mut insn_block = vec![Block::invalid(); f.num_insts()];
|
||||||
let mut vreg_def_inst = vec![Inst::invalid(); f.num_vregs()];
|
let mut vreg_def_inst = vec![Inst::invalid(); f.num_vregs()];
|
||||||
let mut vreg_def_blockparam = vec![(Block::invalid(), 0); f.num_vregs()];
|
let mut vreg_def_blockparam = vec![(Block::invalid(), 0); f.num_vregs()];
|
||||||
let mut block_entry = vec![ProgPoint::before(Inst::invalid()); f.blocks()];
|
let mut block_entry = vec![ProgPoint::before(Inst::invalid()); f.num_blocks()];
|
||||||
let mut block_exit = vec![ProgPoint::before(Inst::invalid()); f.blocks()];
|
let mut block_exit = vec![ProgPoint::before(Inst::invalid()); f.num_blocks()];
|
||||||
let mut backedge_in = vec![0; f.blocks()];
|
let mut backedge_in = vec![0; f.num_blocks()];
|
||||||
let mut backedge_out = vec![0; f.blocks()];
|
let mut backedge_out = vec![0; f.num_blocks()];
|
||||||
|
|
||||||
for block in 0..f.blocks() {
|
for block in 0..f.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
for (i, param) in f.block_params(block).iter().enumerate() {
|
for (i, param) in f.block_params(block).iter().enumerate() {
|
||||||
vreg_def_blockparam[param.vreg()] = (block, i as u32);
|
vreg_def_blockparam[param.vreg()] = (block, i as u32);
|
||||||
@@ -116,7 +117,7 @@ impl CFGInfo {
|
|||||||
let mut approx_loop_depth = vec![];
|
let mut approx_loop_depth = vec![];
|
||||||
let mut backedge_stack: SmallVec<[usize; 4]> = smallvec![];
|
let mut backedge_stack: SmallVec<[usize; 4]> = smallvec![];
|
||||||
let mut cur_depth = 0;
|
let mut cur_depth = 0;
|
||||||
for block in 0..f.blocks() {
|
for block in 0..f.num_blocks() {
|
||||||
if backedge_in[block] > 0 {
|
if backedge_in[block] > 0 {
|
||||||
cur_depth += 1;
|
cur_depth += 1;
|
||||||
backedge_stack.push(backedge_in[block]);
|
backedge_stack.push(backedge_in[block]);
|
||||||
|
|||||||
@@ -389,17 +389,6 @@ impl CheckerState {
|
|||||||
self.allocations.remove(&Allocation::reg(*clobber));
|
self.allocations.remove(&Allocation::reg(*clobber));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&CheckerInst::BlockParams {
|
|
||||||
ref vregs,
|
|
||||||
ref allocs,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
for (vreg, alloc) in vregs.iter().zip(allocs.iter()) {
|
|
||||||
let reftyped = checker.reftyped_vregs.contains(vreg);
|
|
||||||
self.allocations
|
|
||||||
.insert(*alloc, CheckerValue::Reg(*vreg, reftyped));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&CheckerInst::DefAlloc { alloc, vreg } => {
|
&CheckerInst::DefAlloc { alloc, vreg } => {
|
||||||
let reftyped = checker.reftyped_vregs.contains(&vreg);
|
let reftyped = checker.reftyped_vregs.contains(&vreg);
|
||||||
self.allocations
|
self.allocations
|
||||||
@@ -478,14 +467,6 @@ pub(crate) enum CheckerInst {
|
|||||||
clobbers: Vec<PReg>,
|
clobbers: Vec<PReg>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The top of a block with blockparams. We define the given vregs
|
|
||||||
/// into the given allocations.
|
|
||||||
BlockParams {
|
|
||||||
block: Block,
|
|
||||||
vregs: Vec<VReg>,
|
|
||||||
allocs: Vec<Allocation>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Define an allocation's contents. Like BlockParams but for one
|
/// Define an allocation's contents. Like BlockParams but for one
|
||||||
/// allocation. Used sometimes when moves are elided but ownership
|
/// allocation. Used sometimes when moves are elided but ownership
|
||||||
/// of a value is logically transferred to a new vreg.
|
/// of a value is logically transferred to a new vreg.
|
||||||
@@ -514,7 +495,7 @@ impl<'a, F: Function> Checker<'a, F> {
|
|||||||
let mut bb_insts = HashMap::new();
|
let mut bb_insts = HashMap::new();
|
||||||
let mut reftyped_vregs = HashSet::new();
|
let mut reftyped_vregs = HashSet::new();
|
||||||
|
|
||||||
for block in 0..f.blocks() {
|
for block in 0..f.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
bb_in.insert(block, Default::default());
|
bb_in.insert(block, Default::default());
|
||||||
bb_insts.insert(block, vec![]);
|
bb_insts.insert(block, vec![]);
|
||||||
@@ -548,7 +529,7 @@ impl<'a, F: Function> Checker<'a, F> {
|
|||||||
// For each original instruction, create an `Op`.
|
// For each original instruction, create an `Op`.
|
||||||
let mut last_inst = None;
|
let mut last_inst = None;
|
||||||
let mut insert_idx = 0;
|
let mut insert_idx = 0;
|
||||||
for block in 0..self.f.blocks() {
|
for block in 0..self.f.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
for inst in self.f.block_insns(block).iter() {
|
for inst in self.f.block_insns(block).iter() {
|
||||||
assert!(last_inst.is_none() || inst > last_inst.unwrap());
|
assert!(last_inst.is_none() || inst > last_inst.unwrap());
|
||||||
@@ -617,17 +598,6 @@ impl<'a, F: Function> Checker<'a, F> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.push(CheckerInst::DefAlloc { alloc, vreg });
|
.push(CheckerInst::DefAlloc { alloc, vreg });
|
||||||
}
|
}
|
||||||
&Edit::BlockParams {
|
|
||||||
ref vregs,
|
|
||||||
ref allocs,
|
|
||||||
} => {
|
|
||||||
let inst = CheckerInst::BlockParams {
|
|
||||||
block,
|
|
||||||
vregs: vregs.clone(),
|
|
||||||
allocs: allocs.clone(),
|
|
||||||
};
|
|
||||||
self.bb_insts.get_mut(&block).unwrap().push(inst);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -636,7 +606,7 @@ impl<'a, F: Function> Checker<'a, F> {
|
|||||||
fn analyze(&mut self) {
|
fn analyze(&mut self) {
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut queue_set = HashSet::new();
|
let mut queue_set = HashSet::new();
|
||||||
for block in 0..self.f.blocks() {
|
for block in 0..self.f.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
queue.push_back(block);
|
queue.push_back(block);
|
||||||
queue_set.insert(block);
|
queue_set.insert(block);
|
||||||
@@ -718,7 +688,7 @@ impl<'a, F: Function> Checker<'a, F> {
|
|||||||
for vreg in self.f.reftype_vregs() {
|
for vreg in self.f.reftype_vregs() {
|
||||||
log::trace!(" REF: {}", vreg);
|
log::trace!(" REF: {}", vreg);
|
||||||
}
|
}
|
||||||
for bb in 0..self.f.blocks() {
|
for bb in 0..self.f.num_blocks() {
|
||||||
let bb = Block::new(bb);
|
let bb = Block::new(bb);
|
||||||
log::trace!("block{}:", bb.index());
|
log::trace!("block{}:", bb.index());
|
||||||
let insts = self.bb_insts.get(&bb).unwrap();
|
let insts = self.bb_insts.get(&bb).unwrap();
|
||||||
@@ -743,17 +713,6 @@ impl<'a, F: Function> Checker<'a, F> {
|
|||||||
&CheckerInst::Move { from, into } => {
|
&CheckerInst::Move { from, into } => {
|
||||||
log::trace!(" {} -> {}", from, into);
|
log::trace!(" {} -> {}", from, into);
|
||||||
}
|
}
|
||||||
&CheckerInst::BlockParams {
|
|
||||||
ref vregs,
|
|
||||||
ref allocs,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let mut args = vec![];
|
|
||||||
for (vreg, alloc) in vregs.iter().zip(allocs.iter()) {
|
|
||||||
args.push(format!("{}:{}", vreg, alloc));
|
|
||||||
}
|
|
||||||
log::trace!(" blockparams: {}", args.join(", "));
|
|
||||||
}
|
|
||||||
&CheckerInst::DefAlloc { alloc, vreg } => {
|
&CheckerInst::DefAlloc { alloc, vreg } => {
|
||||||
log::trace!(" defalloc: {}:{}", vreg, alloc);
|
log::trace!(" defalloc: {}:{}", vreg, alloc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,11 +75,11 @@ pub struct Func {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Function for Func {
|
impl Function for Func {
|
||||||
fn insts(&self) -> usize {
|
fn num_insts(&self) -> usize {
|
||||||
self.insts.len()
|
self.insts.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blocks(&self) -> usize {
|
fn num_blocks(&self) -> usize {
|
||||||
self.blocks.len()
|
self.blocks.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
|
|
||||||
pub fn dump_results(&self) {
|
pub fn dump_results(&self) {
|
||||||
log::info!("=== REGALLOC RESULTS ===");
|
log::info!("=== REGALLOC RESULTS ===");
|
||||||
for block in 0..self.func.blocks() {
|
for block in 0..self.func.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
log::info!(
|
log::info!(
|
||||||
"block{}: [succs {:?} preds {:?}]",
|
"block{}: [succs {:?} preds {:?}]",
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
self.vregs[v.vreg()].is_pinned = true;
|
self.vregs[v.vreg()].is_pinned = true;
|
||||||
}
|
}
|
||||||
// Create allocations too.
|
// Create allocations too.
|
||||||
for inst in 0..self.func.insts() {
|
for inst in 0..self.func.num_insts() {
|
||||||
let start = self.allocs.len() as u32;
|
let start = self.allocs.len() as u32;
|
||||||
self.inst_alloc_offsets.push(start);
|
self.inst_alloc_offsets.push(start);
|
||||||
for _ in 0..self.func.inst_operands(Inst::new(inst)).len() {
|
for _ in 0..self.func.inst_operands(Inst::new(inst)).len() {
|
||||||
@@ -247,7 +247,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
|
|
||||||
pub fn compute_liveness(&mut self) -> Result<(), RegAllocError> {
|
pub fn compute_liveness(&mut self) -> Result<(), RegAllocError> {
|
||||||
// Create initial LiveIn and LiveOut bitsets.
|
// Create initial LiveIn and LiveOut bitsets.
|
||||||
for _ in 0..self.func.blocks() {
|
for _ in 0..self.func.num_blocks() {
|
||||||
self.liveins.push(BitVec::new());
|
self.liveins.push(BitVec::new());
|
||||||
self.liveouts.push(BitVec::new());
|
self.liveouts.push(BitVec::new());
|
||||||
}
|
}
|
||||||
@@ -347,7 +347,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
let mut vreg_ranges: Vec<LiveRangeIndex> =
|
let mut vreg_ranges: Vec<LiveRangeIndex> =
|
||||||
vec![LiveRangeIndex::invalid(); self.func.num_vregs()];
|
vec![LiveRangeIndex::invalid(); self.func.num_vregs()];
|
||||||
|
|
||||||
for i in (0..self.func.blocks()).rev() {
|
for i in (0..self.func.num_blocks()).rev() {
|
||||||
let block = Block::new(i);
|
let block = Block::new(i);
|
||||||
|
|
||||||
self.stats.livein_blocks += 1;
|
self.stats.livein_blocks += 1;
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
self.bundles[bundle.index()].spillset = ssidx;
|
self.bundles[bundle.index()].spillset = ssidx;
|
||||||
}
|
}
|
||||||
|
|
||||||
for inst in 0..self.func.insts() {
|
for inst in 0..self.func.num_insts() {
|
||||||
let inst = Inst::new(inst);
|
let inst = Inst::new(inst);
|
||||||
|
|
||||||
// Attempt to merge Reuse-constraint operand outputs with the
|
// Attempt to merge Reuse-constraint operand outputs with the
|
||||||
|
|||||||
@@ -44,14 +44,14 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
cfginfo: CFGInfo,
|
cfginfo: CFGInfo,
|
||||||
annotations_enabled: bool,
|
annotations_enabled: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let n = func.insts();
|
let n = func.num_insts();
|
||||||
Self {
|
Self {
|
||||||
func,
|
func,
|
||||||
env,
|
env,
|
||||||
cfginfo,
|
cfginfo,
|
||||||
|
|
||||||
liveins: Vec::with_capacity(func.blocks()),
|
liveins: Vec::with_capacity(func.num_blocks()),
|
||||||
liveouts: Vec::with_capacity(func.blocks()),
|
liveouts: Vec::with_capacity(func.num_blocks()),
|
||||||
blockparam_outs: vec![],
|
blockparam_outs: vec![],
|
||||||
blockparam_ins: vec![],
|
blockparam_ins: vec![],
|
||||||
blockparam_allocs: vec![],
|
blockparam_allocs: vec![],
|
||||||
|
|||||||
@@ -166,8 +166,8 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut half_moves: Vec<HalfMove> = Vec::with_capacity(6 * self.func.insts());
|
let mut half_moves: Vec<HalfMove> = Vec::with_capacity(6 * self.func.num_insts());
|
||||||
let mut reuse_input_insts = Vec::with_capacity(self.func.insts() / 2);
|
let mut reuse_input_insts = Vec::with_capacity(self.func.num_insts() / 2);
|
||||||
|
|
||||||
let mut blockparam_in_idx = 0;
|
let mut blockparam_in_idx = 0;
|
||||||
let mut blockparam_out_idx = 0;
|
let mut blockparam_out_idx = 0;
|
||||||
@@ -290,7 +290,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
// same allocation) and if the vreg is live, add a
|
// same allocation) and if the vreg is live, add a
|
||||||
// Source half-move.
|
// Source half-move.
|
||||||
let mut block = self.cfginfo.insn_block[range.from.inst().index()];
|
let mut block = self.cfginfo.insn_block[range.from.inst().index()];
|
||||||
while block.is_valid() && block.index() < self.func.blocks() {
|
while block.is_valid() && block.index() < self.func.num_blocks() {
|
||||||
if range.to < self.cfginfo.block_exit[block.index()].next() {
|
if range.to < self.cfginfo.block_exit[block.index()].next() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -376,7 +376,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
if self.cfginfo.block_entry[block.index()] < range.from {
|
if self.cfginfo.block_entry[block.index()] < range.from {
|
||||||
block = block.next();
|
block = block.next();
|
||||||
}
|
}
|
||||||
while block.is_valid() && block.index() < self.func.blocks() {
|
while block.is_valid() && block.index() < self.func.num_blocks() {
|
||||||
if self.cfginfo.block_entry[block.index()] >= range.to {
|
if self.cfginfo.block_entry[block.index()] >= range.to {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1114,12 +1114,14 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(vregs.len(), self.func.block_params(block).len());
|
assert_eq!(vregs.len(), self.func.block_params(block).len());
|
||||||
assert_eq!(allocs.len(), self.func.block_params(block).len());
|
assert_eq!(allocs.len(), self.func.block_params(block).len());
|
||||||
|
for (vreg, alloc) in vregs.into_iter().zip(allocs.into_iter()) {
|
||||||
self.add_edit(
|
self.add_edit(
|
||||||
self.cfginfo.block_entry[block.index()],
|
self.cfginfo.block_entry[block.index()],
|
||||||
InsertMovePrio::BlockParam,
|
InsertMovePrio::BlockParam,
|
||||||
Edit::BlockParams { vregs, allocs },
|
Edit::DefAlloc { alloc, vreg },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure edits are in sorted ProgPoint order. N.B.: this must
|
// Ensure edits are in sorted ProgPoint order. N.B.: this must
|
||||||
// be a stable sort! We have to keep the order produced by the
|
// be a stable sort! We have to keep the order produced by the
|
||||||
@@ -1139,13 +1141,6 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
format!("move {} -> {} ({:?})", from, to, to_vreg),
|
format!("move {} -> {} ({:?})", from, to, to_vreg),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
&Edit::BlockParams {
|
|
||||||
ref vregs,
|
|
||||||
ref allocs,
|
|
||||||
} => {
|
|
||||||
let s = format!("blockparams vregs:{:?} allocs:{:?}", vregs, allocs);
|
|
||||||
self.annotate(ProgPoint::from_index(pos), s);
|
|
||||||
}
|
|
||||||
&Edit::DefAlloc { alloc, vreg } => {
|
&Edit::DefAlloc { alloc, vreg } => {
|
||||||
let s = format!("defalloc {:?} := {:?}", alloc, vreg);
|
let s = format!("defalloc {:?} := {:?}", alloc, vreg);
|
||||||
self.annotate(ProgPoint::from_index(pos), s);
|
self.annotate(ProgPoint::from_index(pos), s);
|
||||||
|
|||||||
@@ -798,7 +798,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
loop {
|
loop {
|
||||||
attempts += 1;
|
attempts += 1;
|
||||||
log::trace!("attempt {}, req {:?}", attempts, req);
|
log::trace!("attempt {}, req {:?}", attempts, req);
|
||||||
debug_assert!(attempts < 100 * self.func.insts());
|
debug_assert!(attempts < 100 * self.func.num_insts());
|
||||||
|
|
||||||
let (class, fixed_preg) = match req {
|
let (class, fixed_preg) = match req {
|
||||||
Requirement::Fixed(preg) => (preg.class(), Some(preg)),
|
Requirement::Fixed(preg) => (preg.class(), Some(preg)),
|
||||||
|
|||||||
286
src/lib.rs
286
src/lib.rs
@@ -37,8 +37,24 @@ pub enum RegClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A physical register. Contains a physical register number and a class.
|
/// A physical register. Contains a physical register number and a class.
|
||||||
|
///
|
||||||
|
/// The `hw_enc` field contains the physical register number and is in
|
||||||
|
/// a logically separate index space per class; in other words, Int
|
||||||
|
/// register 0 is different than Float register 0.
|
||||||
|
///
|
||||||
|
/// Because of bit-packed encodings throughout the implementation,
|
||||||
|
/// `hw_enc` must fit in 5 bits, i.e., at most 32 registers per class.
|
||||||
|
///
|
||||||
|
/// The value returned by `index()`, in contrast, is in a single index
|
||||||
|
/// space shared by all classes, in order to enable uniform reasoning
|
||||||
|
/// about physical registers. This is done by putting the class bit at
|
||||||
|
/// the MSB, or equivalently, declaring that indices 0..31 are the 32
|
||||||
|
/// integer registers and indices 32..63 are the 32 float registers.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct PReg(u8, RegClass);
|
pub struct PReg {
|
||||||
|
hw_enc: u8,
|
||||||
|
class: RegClass,
|
||||||
|
}
|
||||||
|
|
||||||
impl PReg {
|
impl PReg {
|
||||||
pub const MAX_BITS: usize = 5;
|
pub const MAX_BITS: usize = 5;
|
||||||
@@ -48,21 +64,31 @@ impl PReg {
|
|||||||
/// Create a new PReg. The `hw_enc` range is 6 bits.
|
/// Create a new PReg. The `hw_enc` range is 6 bits.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn new(hw_enc: usize, class: RegClass) -> Self {
|
pub const fn new(hw_enc: usize, class: RegClass) -> Self {
|
||||||
PReg(hw_enc as u8, class)
|
// We don't have const panics yet (rust-lang/rust#85194) so we
|
||||||
|
// need to use a little indexing trick here. We unfortunately
|
||||||
|
// can't use the `static-assertions` crate because we need
|
||||||
|
// this to work both for const `hw_enc` and for runtime
|
||||||
|
// values.
|
||||||
|
const HW_ENC_MUST_BE_IN_BOUNDS: &[bool; PReg::MAX + 1] = &[true; PReg::MAX + 1];
|
||||||
|
let _ = HW_ENC_MUST_BE_IN_BOUNDS[hw_enc];
|
||||||
|
|
||||||
|
PReg {
|
||||||
|
hw_enc: hw_enc as u8,
|
||||||
|
class,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The physical register number, as encoded by the ISA for the particular register class.
|
/// The physical register number, as encoded by the ISA for the particular register class.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn hw_enc(self) -> usize {
|
pub fn hw_enc(self) -> usize {
|
||||||
let hw_enc = self.0 as usize;
|
let hw_enc = self.hw_enc as usize;
|
||||||
debug_assert!(hw_enc <= Self::MAX);
|
|
||||||
hw_enc
|
hw_enc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The register class.
|
/// The register class.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn class(self) -> RegClass {
|
pub fn class(self) -> RegClass {
|
||||||
self.1
|
self.class
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an index into the (not necessarily contiguous) index space of
|
/// Get an index into the (not necessarily contiguous) index space of
|
||||||
@@ -70,7 +96,7 @@ impl PReg {
|
|||||||
/// all PRegs and index it efficiently.
|
/// all PRegs and index it efficiently.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn index(self) -> usize {
|
pub fn index(self) -> usize {
|
||||||
((self.1 as u8 as usize) << 5) | (self.0 as usize)
|
((self.class as u8 as usize) << 5) | (self.hw_enc as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -115,7 +141,9 @@ impl std::fmt::Display for PReg {
|
|||||||
|
|
||||||
/// A virtual register. Contains a virtual register number and a class.
|
/// A virtual register. Contains a virtual register number and a class.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct VReg(u32);
|
pub struct VReg {
|
||||||
|
bits: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl VReg {
|
impl VReg {
|
||||||
pub const MAX_BITS: usize = 20;
|
pub const MAX_BITS: usize = 20;
|
||||||
@@ -123,19 +151,25 @@ impl VReg {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn new(virt_reg: usize, class: RegClass) -> Self {
|
pub const fn new(virt_reg: usize, class: RegClass) -> Self {
|
||||||
VReg(((virt_reg as u32) << 1) | (class as u8 as u32))
|
// See comment in `PReg::new()`: we are emulating a const
|
||||||
|
// assert here until const panics are stable.
|
||||||
|
const VIRT_REG_MUST_BE_IN_BOUNDS: &[bool; VReg::MAX + 1] = &[true; VReg::MAX + 1];
|
||||||
|
let _ = VIRT_REG_MUST_BE_IN_BOUNDS[virt_reg];
|
||||||
|
|
||||||
|
VReg {
|
||||||
|
bits: ((virt_reg as u32) << 1) | (class as u8 as u32),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn vreg(self) -> usize {
|
pub fn vreg(self) -> usize {
|
||||||
let vreg = (self.0 >> 1) as usize;
|
let vreg = (self.bits >> 1) as usize;
|
||||||
debug_assert!(vreg <= Self::MAX);
|
|
||||||
vreg
|
vreg
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn class(self) -> RegClass {
|
pub fn class(self) -> RegClass {
|
||||||
match self.0 & 1 {
|
match self.bits & 1 {
|
||||||
0 => RegClass::Int,
|
0 => RegClass::Int,
|
||||||
1 => RegClass::Float,
|
1 => RegClass::Float,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -166,21 +200,25 @@ impl std::fmt::Display for VReg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct SpillSlot(u32);
|
pub struct SpillSlot {
|
||||||
|
bits: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl SpillSlot {
|
impl SpillSlot {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(slot: usize, class: RegClass) -> Self {
|
pub fn new(slot: usize, class: RegClass) -> Self {
|
||||||
assert!(slot < (1 << 24));
|
assert!(slot < (1 << 24));
|
||||||
SpillSlot((slot as u32) | (class as u8 as u32) << 24)
|
SpillSlot {
|
||||||
|
bits: (slot as u32) | (class as u8 as u32) << 24,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn index(self) -> usize {
|
pub fn index(self) -> usize {
|
||||||
(self.0 & 0x00ffffff) as usize
|
(self.bits & 0x00ffffff) as usize
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn class(self) -> RegClass {
|
pub fn class(self) -> RegClass {
|
||||||
match (self.0 >> 24) as u8 {
|
match (self.bits >> 24) as u8 {
|
||||||
0 => RegClass::Int,
|
0 => RegClass::Int,
|
||||||
1 => RegClass::Float,
|
1 => RegClass::Float,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -193,7 +231,7 @@ impl SpillSlot {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn invalid() -> Self {
|
pub fn invalid() -> Self {
|
||||||
SpillSlot(0xffff_ffff)
|
SpillSlot { bits: 0xffff_ffff }
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_invalid(self) -> bool {
|
pub fn is_invalid(self) -> bool {
|
||||||
@@ -211,17 +249,78 @@ impl std::fmt::Display for SpillSlot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum OperandConstraint {
|
||||||
|
/// Any location is fine (register or stack slot).
|
||||||
|
Any,
|
||||||
|
/// Operand must be in a register. Register is read-only for Uses.
|
||||||
|
Reg,
|
||||||
|
/// Operand must be on the stack.
|
||||||
|
Stack,
|
||||||
|
/// Operand must be in a fixed register.
|
||||||
|
FixedReg(PReg),
|
||||||
|
/// On defs only: reuse a use's register.
|
||||||
|
Reuse(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for OperandConstraint {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Any => write!(f, "any"),
|
||||||
|
Self::Reg => write!(f, "reg"),
|
||||||
|
Self::Stack => write!(f, "stack"),
|
||||||
|
Self::FixedReg(preg) => write!(f, "fixed({})", preg),
|
||||||
|
Self::Reuse(idx) => write!(f, "reuse({})", idx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum OperandKind {
|
||||||
|
Def = 0,
|
||||||
|
Mod = 1,
|
||||||
|
Use = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum OperandPos {
|
||||||
|
Before = 0,
|
||||||
|
After = 1,
|
||||||
|
}
|
||||||
|
|
||||||
/// An `Operand` encodes everything about a mention of a register in
|
/// An `Operand` encodes everything about a mention of a register in
|
||||||
/// an instruction: virtual register number, and any constraint that
|
/// an instruction: virtual register number, and any constraint that
|
||||||
/// applies to the register at this program point.
|
/// applies to the register at this program point.
|
||||||
///
|
///
|
||||||
/// An Operand may be a use or def (this corresponds to `LUse` and
|
/// An Operand may be a use or def (this corresponds to `LUse` and
|
||||||
/// `LAllocation` in Ion).
|
/// `LAllocation` in Ion).
|
||||||
|
///
|
||||||
|
/// Generally, regalloc2 considers operands to have their effects at
|
||||||
|
/// one of two program points that surround an instruction: "Before"
|
||||||
|
/// or "After". All operands at a given program-point are assigned
|
||||||
|
/// non-conflicting locations based on their constraints. Each operand
|
||||||
|
/// has a "kind", one of use/def/mod, corresponding to
|
||||||
|
/// read/write/read-write, respectively.
|
||||||
|
///
|
||||||
|
/// Usually, an instruction's inputs will be uses-at-Before and
|
||||||
|
/// outputs will be defs-at-After, though there are valid use-cases
|
||||||
|
/// for other combinations too. For example, a single "instruction"
|
||||||
|
/// seen by the regalloc that lowers into multiple machine
|
||||||
|
/// instructions and reads some of its inputs after it starts to write
|
||||||
|
/// outputs must either make those input(s) uses-at-After or those
|
||||||
|
/// output(s) defs-at-Before so that the conflict (overlap) is
|
||||||
|
/// properly accounted for. See comments on the constructors below for
|
||||||
|
/// more.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Operand {
|
pub struct Operand {
|
||||||
/// Bit-pack into 32 bits.
|
/// Bit-pack into 32 bits.
|
||||||
///
|
///
|
||||||
/// constraint:3 kind:2 pos:1 class:1 preg:5 vreg:20
|
/// constraint:3 kind:2 pos:1 class:1 preg:5 vreg:20
|
||||||
|
///
|
||||||
|
/// where `constraint` is an `OperandConstraint`, `kind` is an
|
||||||
|
/// `OperandKind`, `pos` is an `OperandPos`, `class` is a
|
||||||
|
/// `RegClass`, `preg` is a `PReg` or an index for a reused-input
|
||||||
|
/// constraint, and `vreg` is a vreg index.
|
||||||
bits: u32,
|
bits: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +358,9 @@ impl Operand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `Operand` that designates a use of a VReg that must
|
||||||
|
/// be in a register, and that is used at the "before" point,
|
||||||
|
/// i.e., can be overwritten by a result.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reg_use(vreg: VReg) -> Self {
|
pub fn reg_use(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
@@ -268,6 +370,10 @@ impl Operand {
|
|||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `Operand` that designates a use of a VReg that must
|
||||||
|
/// be in a register, and that is used up until the "after" point,
|
||||||
|
/// i.e., must not conflict with any results.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reg_use_at_end(vreg: VReg) -> Self {
|
pub fn reg_use_at_end(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
@@ -277,6 +383,11 @@ impl Operand {
|
|||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `Operand` that designates a definition of a VReg
|
||||||
|
/// that must be in a register, and that occurs at the "after"
|
||||||
|
/// point, i.e. may reuse a register that carried a use into this
|
||||||
|
/// instruction.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reg_def(vreg: VReg) -> Self {
|
pub fn reg_def(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
@@ -286,6 +397,11 @@ impl Operand {
|
|||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `Operand` that designates a definition of a VReg
|
||||||
|
/// that must be in a register, and that occurs early at the
|
||||||
|
/// "before" point, i.e., must not conflict with any input to the
|
||||||
|
/// instruction.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reg_def_at_start(vreg: VReg) -> Self {
|
pub fn reg_def_at_start(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
@@ -295,8 +411,17 @@ impl Operand {
|
|||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `Operand` that designates a def (and use) of a
|
||||||
|
/// temporary *within* the instruction. This register is assumed
|
||||||
|
/// to be written by the instruction, and will not conflict with
|
||||||
|
/// any input or output, but should not be used after the
|
||||||
|
/// instruction completes.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reg_temp(vreg: VReg) -> Self {
|
pub fn reg_temp(vreg: VReg) -> Self {
|
||||||
|
// For now a temp is equivalent to a def-at-start operand,
|
||||||
|
// which gives the desired semantics but does not enforce the
|
||||||
|
// "not reused later" constraint.
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandConstraint::Reg,
|
OperandConstraint::Reg,
|
||||||
@@ -304,6 +429,12 @@ impl Operand {
|
|||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `Operand` that designates a def of a vreg that must
|
||||||
|
/// reuse the register assigned to an input to the
|
||||||
|
/// instruction. The input is identified by `idx` (is the `idx`th
|
||||||
|
/// `Operand` for the instruction) and must be constraint to a
|
||||||
|
/// register, i.e., be the result of `Operand::reg_use(vreg)`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reg_reuse_def(vreg: VReg, idx: usize) -> Self {
|
pub fn reg_reuse_def(vreg: VReg, idx: usize) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
@@ -313,6 +444,11 @@ impl Operand {
|
|||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `Operand` that designates a use of a vreg and
|
||||||
|
/// ensures that it is placed in the given, fixed PReg at the
|
||||||
|
/// use. It is guaranteed that the `Allocation` resulting for this
|
||||||
|
/// operand will be `preg`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reg_fixed_use(vreg: VReg, preg: PReg) -> Self {
|
pub fn reg_fixed_use(vreg: VReg, preg: PReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
@@ -322,6 +458,11 @@ impl Operand {
|
|||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `Operand` that designates a def of a vreg and
|
||||||
|
/// ensures that it is placed in the given, fixed PReg at the
|
||||||
|
/// def. It is guaranteed that the `Allocation` resulting for this
|
||||||
|
/// operand will be `preg`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reg_fixed_def(vreg: VReg, preg: PReg) -> Self {
|
pub fn reg_fixed_def(vreg: VReg, preg: PReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
@@ -332,12 +473,17 @@ impl Operand {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the virtual register designated by an operand. Every
|
||||||
|
/// operand must name some virtual register, even if it constrains
|
||||||
|
/// the operand to a fixed physical register as well; the vregs
|
||||||
|
/// are used to track dataflow.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn vreg(self) -> VReg {
|
pub fn vreg(self) -> VReg {
|
||||||
let vreg_idx = ((self.bits as usize) & VReg::MAX) as usize;
|
let vreg_idx = ((self.bits as usize) & VReg::MAX) as usize;
|
||||||
VReg::new(vreg_idx, self.class())
|
VReg::new(vreg_idx, self.class())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the register class used by this operand.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn class(self) -> RegClass {
|
pub fn class(self) -> RegClass {
|
||||||
let class_field = (self.bits >> 25) & 1;
|
let class_field = (self.bits >> 25) & 1;
|
||||||
@@ -348,6 +494,8 @@ impl Operand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the "kind" of this operand: a definition (write), a use
|
||||||
|
/// (read), or a "mod" / modify (a read followed by a write).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn kind(self) -> OperandKind {
|
pub fn kind(self) -> OperandKind {
|
||||||
let kind_field = (self.bits >> 27) & 3;
|
let kind_field = (self.bits >> 27) & 3;
|
||||||
@@ -359,6 +507,10 @@ impl Operand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the "position" of this operand, i.e., where its read
|
||||||
|
/// and/or write occurs: either before the instruction executes,
|
||||||
|
/// or after it does. Ordinarily, uses occur at "before" and defs
|
||||||
|
/// at "after", though there are cases where this is not true.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pos(self) -> OperandPos {
|
pub fn pos(self) -> OperandPos {
|
||||||
let pos_field = (self.bits >> 26) & 1;
|
let pos_field = (self.bits >> 26) & 1;
|
||||||
@@ -369,6 +521,8 @@ impl Operand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the "constraint" of this operand, i.e., what requirements
|
||||||
|
/// its allocation must fulfill.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn constraint(self) -> OperandConstraint {
|
pub fn constraint(self) -> OperandConstraint {
|
||||||
let constraint_field = (self.bits >> 29) & 7;
|
let constraint_field = (self.bits >> 29) & 7;
|
||||||
@@ -383,11 +537,14 @@ impl Operand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the raw 32-bit encoding of this operand's fields.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bits(self) -> u32 {
|
pub fn bits(self) -> u32 {
|
||||||
self.bits
|
self.bits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct an `Operand` from the raw 32-bit encoding returned
|
||||||
|
/// from `bits()`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_bits(bits: u32) -> Self {
|
pub fn from_bits(bits: u32) -> Self {
|
||||||
debug_assert!(bits >> 29 <= 4);
|
debug_assert!(bits >> 29 <= 4);
|
||||||
@@ -425,45 +582,6 @@ impl std::fmt::Display for Operand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum OperandConstraint {
|
|
||||||
/// Any location is fine (register or stack slot).
|
|
||||||
Any,
|
|
||||||
/// Operand must be in a register. Register is read-only for Uses.
|
|
||||||
Reg,
|
|
||||||
/// Operand must be on the stack.
|
|
||||||
Stack,
|
|
||||||
/// Operand must be in a fixed register.
|
|
||||||
FixedReg(PReg),
|
|
||||||
/// On defs only: reuse a use's register. Which use is given by `preg` field.
|
|
||||||
Reuse(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for OperandConstraint {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Any => write!(f, "any"),
|
|
||||||
Self::Reg => write!(f, "reg"),
|
|
||||||
Self::Stack => write!(f, "stack"),
|
|
||||||
Self::FixedReg(preg) => write!(f, "fixed({})", preg),
|
|
||||||
Self::Reuse(idx) => write!(f, "reuse({})", idx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum OperandKind {
|
|
||||||
Def = 0,
|
|
||||||
Mod = 1,
|
|
||||||
Use = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum OperandPos {
|
|
||||||
Before = 0,
|
|
||||||
After = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An Allocation represents the end result of regalloc for an
|
/// An Allocation represents the end result of regalloc for an
|
||||||
/// Operand.
|
/// Operand.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
@@ -511,7 +629,7 @@ impl Allocation {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn stack(slot: SpillSlot) -> Allocation {
|
pub fn stack(slot: SpillSlot) -> Allocation {
|
||||||
Allocation::new(AllocationKind::Stack, slot.0 as usize)
|
Allocation::new(AllocationKind::Stack, slot.bits as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -556,7 +674,9 @@ impl Allocation {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_stack(self) -> Option<SpillSlot> {
|
pub fn as_stack(self) -> Option<SpillSlot> {
|
||||||
if self.kind() == AllocationKind::Stack {
|
if self.kind() == AllocationKind::Stack {
|
||||||
Some(SpillSlot(self.index() as u32))
|
Some(SpillSlot {
|
||||||
|
bits: self.index() as u32,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -604,10 +724,10 @@ pub trait Function {
|
|||||||
// -------------
|
// -------------
|
||||||
|
|
||||||
/// How many instructions are there?
|
/// How many instructions are there?
|
||||||
fn insts(&self) -> usize;
|
fn num_insts(&self) -> usize;
|
||||||
|
|
||||||
/// How many blocks are there?
|
/// How many blocks are there?
|
||||||
fn blocks(&self) -> usize;
|
fn num_blocks(&self) -> usize;
|
||||||
|
|
||||||
/// Get the index of the entry block.
|
/// Get the index of the entry block.
|
||||||
fn entry_block(&self) -> Block;
|
fn entry_block(&self) -> Block;
|
||||||
@@ -649,6 +769,16 @@ pub trait Function {
|
|||||||
fn branch_blockparam_arg_offset(&self, block: Block, insn: Inst) -> usize;
|
fn branch_blockparam_arg_offset(&self, block: Block, insn: Inst) -> usize;
|
||||||
|
|
||||||
/// Determine whether an instruction is a safepoint and requires a stackmap.
|
/// Determine whether an instruction is a safepoint and requires a stackmap.
|
||||||
|
///
|
||||||
|
/// Strictly speaking, these two parts (is a safepoint, requires a
|
||||||
|
/// stackmap) are orthogonal. An instruction could want to see a
|
||||||
|
/// stackmap of refs on the stack (without forcing them), or it
|
||||||
|
/// could want all refs to be on the stack (without knowing where
|
||||||
|
/// they are). Only the latter strictly follows from "is a
|
||||||
|
/// safepoint". But in practice, both are true at the same time,
|
||||||
|
/// so we combine the two notions: for regalloc2, a "safepoint
|
||||||
|
/// instruction" is one that both forces refs onto the stack, and
|
||||||
|
/// provides a stackmap indicating where they are.
|
||||||
fn is_safepoint(&self, _: Inst) -> bool {
|
fn is_safepoint(&self, _: Inst) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -664,7 +794,16 @@ pub trait Function {
|
|||||||
/// Get the Operands for an instruction.
|
/// Get the Operands for an instruction.
|
||||||
fn inst_operands(&self, insn: Inst) -> &[Operand];
|
fn inst_operands(&self, insn: Inst) -> &[Operand];
|
||||||
|
|
||||||
/// Get the clobbers for an instruction.
|
/// Get the clobbers for an instruction; these are the registers
|
||||||
|
/// that the instruction is known to overwrite, separate from its
|
||||||
|
/// outputs described by its `Operand`s. This can be used to, for
|
||||||
|
/// example, describe ABI-specified registers that are not
|
||||||
|
/// preserved by a call instruction, or fixed physical registers
|
||||||
|
/// written by an instruction but not used as a vreg output, or
|
||||||
|
/// fixed physical registers used as temps within an instruction
|
||||||
|
/// out of necessity. Every register written to by an instruction
|
||||||
|
/// must either be described by an Operand of kind `Def` or `Mod`,
|
||||||
|
/// or else must be a "clobber".
|
||||||
fn inst_clobbers(&self, insn: Inst) -> &[PReg];
|
fn inst_clobbers(&self, insn: Inst) -> &[PReg];
|
||||||
|
|
||||||
/// Get the number of `VReg` in use in this function.
|
/// Get the number of `VReg` in use in this function.
|
||||||
@@ -743,7 +882,15 @@ pub trait Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A position before or after an instruction.
|
/// A position before or after an instruction at which we can make an
|
||||||
|
/// edit.
|
||||||
|
///
|
||||||
|
/// Note that this differs from `OperandPos` in that the former
|
||||||
|
/// describes specifically a constraint on an operand, while this
|
||||||
|
/// describes a program point. `OperandPos` could grow more options in
|
||||||
|
/// the future, for example if we decide that an "early write" or
|
||||||
|
/// "late read" phase makes sense, while `InstPosition` will always
|
||||||
|
/// describe these two insertion points.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum InstPosition {
|
pub enum InstPosition {
|
||||||
@@ -839,13 +986,7 @@ pub enum Edit {
|
|||||||
to: Allocation,
|
to: Allocation,
|
||||||
to_vreg: Option<VReg>,
|
to_vreg: Option<VReg>,
|
||||||
},
|
},
|
||||||
/// Define blockparams' locations. Note that this is not typically
|
|
||||||
/// turned into machine code, but can be useful metadata (e.g. for
|
|
||||||
/// the checker).
|
|
||||||
BlockParams {
|
|
||||||
vregs: Vec<VReg>,
|
|
||||||
allocs: Vec<Allocation>,
|
|
||||||
},
|
|
||||||
/// Define a particular Allocation to contain a particular VReg. Useful
|
/// Define a particular Allocation to contain a particular VReg. Useful
|
||||||
/// for the checker.
|
/// for the checker.
|
||||||
DefAlloc { alloc: Allocation, vreg: VReg },
|
DefAlloc { alloc: Allocation, vreg: VReg },
|
||||||
@@ -859,16 +1000,20 @@ pub enum Edit {
|
|||||||
pub struct MachineEnv {
|
pub struct MachineEnv {
|
||||||
/// Physical registers. Every register that might be mentioned in
|
/// Physical registers. Every register that might be mentioned in
|
||||||
/// any constraint must be listed here, even if it is not
|
/// any constraint must be listed here, even if it is not
|
||||||
/// allocatable under normal conditions.
|
/// allocatable (present in one of
|
||||||
|
/// `{preferred,non_preferred}_regs_by_class`).
|
||||||
pub regs: Vec<PReg>,
|
pub regs: Vec<PReg>,
|
||||||
|
|
||||||
/// Preferred physical registers for each class. These are the
|
/// Preferred physical registers for each class. These are the
|
||||||
/// registers that will be allocated first, if free.
|
/// registers that will be allocated first, if free.
|
||||||
pub preferred_regs_by_class: [Vec<PReg>; 2],
|
pub preferred_regs_by_class: [Vec<PReg>; 2],
|
||||||
|
|
||||||
/// Non-preferred physical registers for each class. These are the
|
/// Non-preferred physical registers for each class. These are the
|
||||||
/// registers that will be allocated if a preferred register is
|
/// registers that will be allocated if a preferred register is
|
||||||
/// not available; using one of these is considered suboptimal,
|
/// not available; using one of these is considered suboptimal,
|
||||||
/// but still better than spilling.
|
/// but still better than spilling.
|
||||||
pub non_preferred_regs_by_class: [Vec<PReg>; 2],
|
pub non_preferred_regs_by_class: [Vec<PReg>; 2],
|
||||||
|
|
||||||
/// One scratch register per class. This is needed to perform
|
/// One scratch register per class. This is needed to perform
|
||||||
/// moves between registers when cyclic move patterns occur. The
|
/// moves between registers when cyclic move patterns occur. The
|
||||||
/// register should not be placed in either the preferred or
|
/// register should not be placed in either the preferred or
|
||||||
@@ -888,12 +1033,15 @@ pub struct MachineEnv {
|
|||||||
pub struct Output {
|
pub struct Output {
|
||||||
/// How many spillslots are needed in the frame?
|
/// How many spillslots are needed in the frame?
|
||||||
pub num_spillslots: usize,
|
pub num_spillslots: usize,
|
||||||
|
|
||||||
/// Edits (insertions or removals). Guaranteed to be sorted by
|
/// Edits (insertions or removals). Guaranteed to be sorted by
|
||||||
/// program point.
|
/// program point.
|
||||||
pub edits: Vec<(ProgPoint, Edit)>,
|
pub edits: Vec<(ProgPoint, Edit)>,
|
||||||
|
|
||||||
/// Allocations for each operand. Mapping from instruction to
|
/// Allocations for each operand. Mapping from instruction to
|
||||||
/// allocations provided by `inst_alloc_offsets` below.
|
/// allocations provided by `inst_alloc_offsets` below.
|
||||||
pub allocs: Vec<Allocation>,
|
pub allocs: Vec<Allocation>,
|
||||||
|
|
||||||
/// Allocation offset in `allocs` for each instruction.
|
/// Allocation offset in `allocs` for each instruction.
|
||||||
pub inst_alloc_offsets: Vec<u32>,
|
pub inst_alloc_offsets: Vec<u32>,
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllo
|
|||||||
// dominates this one. Also check that for every block param and
|
// dominates this one. Also check that for every block param and
|
||||||
// inst def, that this is the only def.
|
// inst def, that this is the only def.
|
||||||
let mut defined = vec![false; f.num_vregs()];
|
let mut defined = vec![false; f.num_vregs()];
|
||||||
for block in 0..f.blocks() {
|
for block in 0..f.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
for blockparam in f.block_params(block) {
|
for blockparam in f.block_params(block) {
|
||||||
if defined[blockparam.vreg()] {
|
if defined[blockparam.vreg()] {
|
||||||
@@ -62,7 +62,7 @@ pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllo
|
|||||||
// number of blockparams in their succs, and that the end of every
|
// number of blockparams in their succs, and that the end of every
|
||||||
// block ends in this branch or in a ret, and that there are no
|
// block ends in this branch or in a ret, and that there are no
|
||||||
// other branches or rets in the middle of the block.
|
// other branches or rets in the middle of the block.
|
||||||
for block in 0..f.blocks() {
|
for block in 0..f.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
let insns = f.block_insns(block);
|
let insns = f.block_insns(block);
|
||||||
for insn in insns.iter() {
|
for insn in insns.iter() {
|
||||||
|
|||||||
Reference in New Issue
Block a user