Support for debug-labels. (#27)
Support for debug-labels. If the client adds labels to vregs across ranges of instructions in the input program, the regalloc will provide metadata in the `Output` that describes the `Allocation`s in which each such vreg is stored for those ranges. This allows the client to emit debug metadata telling a debugger where to find program values at each point in the program.
This commit is contained in:
131
src/ion/moves.rs
131
src/ion/moves.rs
@@ -17,7 +17,7 @@ use super::{
|
||||
VRegIndex, SLOT_NONE,
|
||||
};
|
||||
|
||||
use crate::ion::data_structures::{BlockparamIn, BlockparamOut, PosWithPrio};
|
||||
use crate::ion::data_structures::{BlockparamIn, BlockparamOut, CodeRange, PosWithPrio};
|
||||
use crate::moves::ParallelMoves;
|
||||
use crate::{
|
||||
Allocation, Block, Edit, Function, Inst, InstPosition, OperandConstraint, OperandKind,
|
||||
@@ -178,6 +178,27 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
}
|
||||
}
|
||||
|
||||
let debug_labels = self.func.debug_value_labels();
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
// Check the precondition that the debug-labels list is
|
||||
// sorted, and that ranges are non-overlapping.
|
||||
let mut last = None;
|
||||
for tuple in debug_labels {
|
||||
if let Some(last) = last {
|
||||
// Precondition 1: sorted.
|
||||
debug_assert!(last <= tuple);
|
||||
// Precondition 2: ranges for a given vreg are
|
||||
// non-overlapping. Note that `from`s are
|
||||
// inclusive and `to`s are exclusive.
|
||||
let &(last_vreg, _, last_to, _) = last;
|
||||
let &(tuple_vreg, tuple_from, _, _) = tuple;
|
||||
debug_assert!(last_vreg < tuple_vreg || last_to <= tuple_from);
|
||||
}
|
||||
last = Some(tuple);
|
||||
}
|
||||
}
|
||||
|
||||
let mut half_moves: Vec<HalfMove> = Vec::with_capacity(6 * self.func.num_insts());
|
||||
let mut reuse_input_insts = Vec::with_capacity(self.func.num_insts() / 2);
|
||||
|
||||
@@ -480,25 +501,6 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
|
||||
block = block.next();
|
||||
}
|
||||
|
||||
#[cfg(feature = "checker")]
|
||||
{
|
||||
// If this is a blockparam vreg and the start of block
|
||||
// is in this range, add to blockparam_allocs.
|
||||
let (blockparam_block, blockparam_idx) =
|
||||
self.cfginfo.vreg_def_blockparam[vreg.index()];
|
||||
if blockparam_block.is_valid()
|
||||
&& range
|
||||
.contains_point(self.cfginfo.block_entry[blockparam_block.index()])
|
||||
{
|
||||
self.blockparam_allocs.push((
|
||||
blockparam_block,
|
||||
blockparam_idx,
|
||||
vreg,
|
||||
alloc,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan over def/uses and apply allocations.
|
||||
@@ -519,6 +521,71 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
}
|
||||
}
|
||||
|
||||
// Scan debug-labels on this vreg that overlap with
|
||||
// this range, producing a debug-info output record
|
||||
// giving the allocation location for each label.
|
||||
if !debug_labels.is_empty() {
|
||||
// Do a binary search to find the start of any
|
||||
// relevant labels. Recall that we require
|
||||
// debug-label requests to be sorted by vreg then
|
||||
// by inst-range, with non-overlapping ranges, as
|
||||
// preconditions (which we verified above).
|
||||
let start = debug_labels
|
||||
.binary_search_by(|&(label_vreg, _label_from, label_to, _label)| {
|
||||
// Search for the point just before the first
|
||||
// tuple that could be for `vreg` overlapping
|
||||
// with `range`. Never return
|
||||
// `Ordering::Equal`; `binary_search_by` in
|
||||
// this case returns the index of the first
|
||||
// entry that is greater as an `Err`.
|
||||
label_vreg.vreg().cmp(&vreg.index()).then(
|
||||
if range.from < ProgPoint::before(label_to) {
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
std::cmp::Ordering::Greater
|
||||
},
|
||||
)
|
||||
})
|
||||
.unwrap_err();
|
||||
|
||||
for &(label_vreg, label_from, label_to, label) in &debug_labels[start..] {
|
||||
let label_from = ProgPoint::before(label_from);
|
||||
let label_to = ProgPoint::before(label_to);
|
||||
if label_to <= range.from {
|
||||
continue;
|
||||
}
|
||||
if label_from >= range.to {
|
||||
break;
|
||||
}
|
||||
if label_vreg.vreg() != vreg.index() {
|
||||
break;
|
||||
}
|
||||
if label_from == label_to {
|
||||
continue;
|
||||
}
|
||||
debug_assert!(label_from < label_to);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let label_range = CodeRange {
|
||||
from: label_from,
|
||||
to: label_to,
|
||||
};
|
||||
debug_assert!(
|
||||
label_range.overlaps(&range),
|
||||
"label_range = {:?} range = {:?}",
|
||||
label_range,
|
||||
range
|
||||
);
|
||||
}
|
||||
|
||||
let from = std::cmp::max(label_from, range.from);
|
||||
let to = std::cmp::min(label_to, range.to);
|
||||
|
||||
self.debug_locations.push((label, from, to, alloc));
|
||||
}
|
||||
}
|
||||
|
||||
// Scan over program move srcs/dsts to fill in allocations.
|
||||
|
||||
// Move srcs happen at `After` of a given
|
||||
@@ -857,6 +924,10 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
Some(self.vreg_regs[to_vreg.index()]),
|
||||
);
|
||||
}
|
||||
|
||||
// Sort the debug-locations vector; we provide this
|
||||
// invariant to the client.
|
||||
self.debug_locations.sort_unstable();
|
||||
}
|
||||
|
||||
pub fn resolve_inserted_moves(&mut self) {
|
||||
@@ -939,18 +1010,12 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
// regs, but this seems simpler.)
|
||||
let mut int_moves: SmallVec<[InsertedMove; 8]> = smallvec![];
|
||||
let mut float_moves: SmallVec<[InsertedMove; 8]> = smallvec![];
|
||||
#[cfg(feature = "checker")]
|
||||
let mut self_moves: SmallVec<[InsertedMove; 8]> = smallvec![];
|
||||
|
||||
for m in moves {
|
||||
if m.from_alloc.is_reg() && m.to_alloc.is_reg() {
|
||||
debug_assert_eq!(m.from_alloc.class(), m.to_alloc.class());
|
||||
}
|
||||
if m.from_alloc == m.to_alloc {
|
||||
#[cfg(feature = "checker")]
|
||||
if m.to_vreg.is_some() {
|
||||
self_moves.push(m.clone());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
match m.from_alloc.class() {
|
||||
@@ -1042,20 +1107,6 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "checker")]
|
||||
for m in &self_moves {
|
||||
trace!(
|
||||
"self move at pos {:?} prio {:?}: {} -> {} to_vreg {:?}",
|
||||
pos_prio.pos,
|
||||
pos_prio.prio,
|
||||
m.from_alloc,
|
||||
m.to_alloc,
|
||||
m.to_vreg
|
||||
);
|
||||
let action = redundant_moves.process_move(m.from_alloc, m.to_alloc, m.to_vreg);
|
||||
debug_assert!(action.elide);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure edits are in sorted ProgPoint order. N.B.: this must
|
||||
|
||||
Reference in New Issue
Block a user