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:
Chris Fallin
2022-03-03 16:58:33 -08:00
committed by GitHub
parent d9d97451f8
commit 14442df3fc
5 changed files with 125 additions and 48 deletions

View File

@@ -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