Merge pull request #23 from Amanieu/iter

Add a helper to iterate over insts and edits of a block in order
This commit is contained in:
Chris Fallin
2022-01-05 10:02:35 -08:00
committed by GitHub
2 changed files with 129 additions and 65 deletions

View File

@@ -77,8 +77,8 @@
#![allow(dead_code)]
use crate::{
Allocation, AllocationKind, Block, Edit, Function, Inst, InstPosition, Operand,
OperandConstraint, OperandKind, OperandPos, Output, PReg, ProgPoint, RegClass, VReg,
Allocation, AllocationKind, Block, Edit, Function, Inst, InstOrEdit, InstPosition, Operand,
OperandConstraint, OperandKind, OperandPos, Output, PReg, RegClass, VReg,
};
use std::collections::{HashMap, HashSet, VecDeque};
@@ -544,78 +544,78 @@ impl<'a, F: Function> Checker<'a, F> {
.push(slot);
}
// For each original instruction, create an `Op`.
let mut last_inst = None;
let mut insert_idx = 0;
for block in 0..self.f.num_blocks() {
let block = Block::new(block);
for inst in self.f.block_insns(block).iter() {
assert!(last_inst.is_none() || inst > last_inst.unwrap());
last_inst = Some(inst);
// Any inserted edits before instruction.
self.handle_edits(block, out, &mut insert_idx, ProgPoint::before(inst));
// If this is a safepoint, then check the spillslots at this point.
if self.f.requires_refs_on_stack(inst) {
let allocs = safepoint_slots.remove(&inst).unwrap_or_else(|| vec![]);
let checkinst = CheckerInst::Safepoint { inst, allocs };
self.bb_insts.get_mut(&block).unwrap().push(checkinst);
for inst_or_edit in out.block_insts_and_edits(self.f, block) {
match inst_or_edit {
InstOrEdit::Inst(inst) => {
assert!(last_inst.is_none() || inst > last_inst.unwrap());
last_inst = Some(inst);
self.handle_inst(block, inst, &mut safepoint_slots, out);
}
InstOrEdit::Edit(edit) => self.handle_edit(block, edit),
}
// Skip if this is a branch: the blockparams do not
// exist in post-regalloc code, and the edge-moves
// have to be inserted before the branch rather than
// after.
if !self.f.is_branch(inst) {
// Instruction itself.
let operands: Vec<_> = self.f.inst_operands(inst).iter().cloned().collect();
let allocs: Vec<_> = out.inst_allocs(inst).iter().cloned().collect();
let clobbers: Vec<_> = self.f.inst_clobbers(inst).iter().cloned().collect();
let checkinst = CheckerInst::Op {
inst,
operands,
allocs,
clobbers,
};
log::trace!("checker: adding inst {:?}", checkinst);
self.bb_insts.get_mut(&block).unwrap().push(checkinst);
}
// Any inserted edits after instruction.
self.handle_edits(block, out, &mut insert_idx, ProgPoint::after(inst));
}
}
}
fn handle_edits(&mut self, block: Block, out: &Output, idx: &mut usize, pos: ProgPoint) {
while *idx < out.edits.len() && out.edits[*idx].0 <= pos {
let &(edit_pos, ref edit) = &out.edits[*idx];
*idx += 1;
if edit_pos < pos {
continue;
/// For each original instruction, create an `Op`.
fn handle_inst(
&mut self,
block: Block,
inst: Inst,
safepoint_slots: &mut HashMap<Inst, Vec<Allocation>>,
out: &Output,
) {
// If this is a safepoint, then check the spillslots at this point.
if self.f.requires_refs_on_stack(inst) {
let allocs = safepoint_slots.remove(&inst).unwrap_or_else(|| vec![]);
let checkinst = CheckerInst::Safepoint { inst, allocs };
self.bb_insts.get_mut(&block).unwrap().push(checkinst);
}
// Skip if this is a branch: the blockparams do not
// exist in post-regalloc code, and the edge-moves
// have to be inserted before the branch rather than
// after.
if !self.f.is_branch(inst) {
// Instruction itself.
let operands: Vec<_> = self.f.inst_operands(inst).iter().cloned().collect();
let allocs: Vec<_> = out.inst_allocs(inst).iter().cloned().collect();
let clobbers: Vec<_> = self.f.inst_clobbers(inst).iter().cloned().collect();
let checkinst = CheckerInst::Op {
inst,
operands,
allocs,
clobbers,
};
log::trace!("checker: adding inst {:?}", checkinst);
self.bb_insts.get_mut(&block).unwrap().push(checkinst);
}
}
fn handle_edit(&mut self, block: Block, edit: &Edit) {
log::trace!("checker: adding edit {:?}", edit);
match edit {
&Edit::Move { from, to, to_vreg } => {
self.bb_insts
.get_mut(&block)
.unwrap()
.push(CheckerInst::Move { into: to, from });
if let Some(vreg) = to_vreg {
self.bb_insts
.get_mut(&block)
.unwrap()
.push(CheckerInst::DefAlloc { alloc: to, vreg });
}
}
log::trace!("checker: adding edit {:?} at pos {:?}", edit, pos);
match edit {
&Edit::Move { from, to, to_vreg } => {
self.bb_insts
.get_mut(&block)
.unwrap()
.push(CheckerInst::Move { into: to, from });
if let Some(vreg) = to_vreg {
self.bb_insts
.get_mut(&block)
.unwrap()
.push(CheckerInst::DefAlloc { alloc: to, vreg });
}
}
&Edit::DefAlloc { alloc, vreg } => {
self.bb_insts
.get_mut(&block)
.unwrap()
.push(CheckerInst::DefAlloc { alloc, vreg });
}
&Edit::DefAlloc { alloc, vreg } => {
self.bb_insts
.get_mut(&block)
.unwrap()
.push(CheckerInst::DefAlloc { alloc, vreg });
}
}
}

View File

@@ -1122,6 +1122,46 @@ pub enum Edit {
DefAlloc { alloc: Allocation, vreg: VReg },
}
/// Wrapper around either an original instruction or an inserted edit.
#[derive(Clone, Debug)]
pub enum InstOrEdit<'a> {
Inst(Inst),
Edit(&'a Edit),
}
/// Iterator over the instructions and edits in a block.
pub struct OutputIter<'a> {
/// List of edits starting at the first for the current block.
edits: &'a [(ProgPoint, Edit)],
/// Remaining instructions in the current block.
inst_range: InstRange,
}
impl<'a> Iterator for OutputIter<'a> {
type Item = InstOrEdit<'a>;
fn next(&mut self) -> Option<InstOrEdit<'a>> {
// There can't be any edits after the last instruction in a block, so
// we don't need to worry about that case.
if self.inst_range.len() == 0 {
return None;
}
// Return any edits that happen before the next instruction first.
let next_inst = self.inst_range.first();
if let Some((edit, remaining_edits)) = self.edits.split_first() {
if edit.0 <= ProgPoint::before(next_inst) {
self.edits = remaining_edits;
return Some(InstOrEdit::Edit(&edit.1));
}
}
self.inst_range = self.inst_range.rest();
Some(InstOrEdit::Inst(next_inst))
}
}
/// A machine envrionment tells the register allocator which registers
/// are available to allocate and what register may be used as a
/// scratch register for each class, and some other miscellaneous info
@@ -1206,6 +1246,30 @@ impl Output {
};
&self.allocs[start..end]
}
/// Returns an iterator over the instructions and edits in a block, in
/// order.
pub fn block_insts_and_edits(&self, func: &impl Function, block: Block) -> OutputIter<'_> {
let inst_range = func.block_insns(block);
let edit_idx = self
.edits
.binary_search_by(|&(pos, _)| {
// This predicate effectively searches for a point *just* before
// the first ProgPoint. This never returns Ordering::Equal, but
// binary_search_by returns the index of where it would have
// been inserted in Err.
if pos < ProgPoint::before(inst_range.first()) {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
})
.unwrap_err();
let edits = &self.edits[edit_idx..];
OutputIter { inst_range, edits }
}
}
/// An error that prevents allocation.