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

@@ -67,6 +67,7 @@ pub struct Func {
block_params_out: Vec<Vec<Vec<VReg>>>,
num_vregs: usize,
reftype_vregs: Vec<VReg>,
debug_value_labels: Vec<(VReg, Inst, Inst, u32)>,
}
impl Function for Func {
@@ -119,6 +120,10 @@ impl Function for Func {
&self.reftype_vregs[..]
}
fn debug_value_labels(&self) -> &[(VReg, Inst, Inst, u32)] {
&self.debug_value_labels[..]
}
fn is_move(&self, _: Inst) -> Option<(Operand, Operand)> {
None
}
@@ -164,6 +169,7 @@ impl FuncBuilder {
blocks: vec![],
num_vregs: 0,
reftype_vregs: vec![],
debug_value_labels: vec![],
},
insts_per_block: vec![],
}
@@ -375,6 +381,21 @@ impl Func {
if opts.reftypes && bool::arbitrary(u)? {
builder.f.reftype_vregs.push(vreg);
}
if bool::arbitrary(u)? {
let assumed_end_inst = 10 * num_blocks;
let mut start = u.int_in_range::<usize>(0..=assumed_end_inst)?;
while start < assumed_end_inst {
let end = u.int_in_range::<usize>(start..=assumed_end_inst)?;
let label = u.int_in_range::<u32>(0..=100)?;
builder.f.debug_value_labels.push((
vreg,
Inst::new(start),
Inst::new(end),
label,
));
start = end;
}
}
}
vregs_by_block.push(vregs.clone());
vregs_by_block_to_be_defined.push(vec![]);
@@ -536,6 +557,8 @@ impl Func {
}
}
builder.f.debug_value_labels.sort_unstable();
Ok(builder.finalize())
}
}

View File

@@ -336,10 +336,6 @@ pub struct Env<'a, F: Function> {
pub liveouts: Vec<IndexSet>,
pub blockparam_outs: Vec<BlockparamOut>,
pub blockparam_ins: Vec<BlockparamIn>,
/// Blockparam allocs: block, idx, vreg, alloc. Info to describe
/// blockparam locations at block entry, for metadata purposes
/// (e.g. for the checker).
pub blockparam_allocs: Vec<(Block, u32, VRegIndex, Allocation)>,
pub ranges: Vec<LiveRange>,
pub bundles: Vec<LiveBundle>,
@@ -390,6 +386,7 @@ pub struct Env<'a, F: Function> {
pub inst_alloc_offsets: Vec<u32>,
pub num_spillslots: u32,
pub safepoint_slots: Vec<(ProgPoint, Allocation)>,
pub debug_locations: Vec<(u32, ProgPoint, ProgPoint, Allocation)>,
pub allocated_bundle_count: usize,
@@ -595,7 +592,6 @@ pub struct Stats {
pub spill_bundle_reg_success: usize,
pub blockparam_ins_count: usize,
pub blockparam_outs_count: usize,
pub blockparam_allocs_count: usize,
pub halfmoves_count: usize,
pub edits_count: usize,
}

View File

@@ -53,7 +53,6 @@ impl<'a, F: Function> Env<'a, F> {
liveouts: Vec::with_capacity(func.num_blocks()),
blockparam_outs: vec![],
blockparam_ins: vec![],
blockparam_allocs: vec![],
bundles: Vec::with_capacity(n),
ranges: Vec::with_capacity(4 * n),
spillsets: Vec::with_capacity(n),
@@ -81,6 +80,7 @@ impl<'a, F: Function> Env<'a, F> {
inst_alloc_offsets: vec![],
num_spillslots: 0,
safepoint_slots: vec![],
debug_locations: vec![],
stats: Stats::default(),
@@ -138,7 +138,7 @@ pub fn run<F: Function>(
allocs: env.allocs,
inst_alloc_offsets: env.inst_alloc_offsets,
num_spillslots: env.num_spillslots as usize,
debug_locations: vec![],
debug_locations: env.debug_locations,
safepoint_slots: env.safepoint_slots,
stats: env.stats,
})

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

View File

@@ -958,7 +958,14 @@ pub trait Function {
/// match -- specifically, the returned metadata may cover only a
/// subset of the requested ranges -- if the value is not live for
/// the entire requested ranges.
fn debug_value_labels(&self) -> &[(Inst, Inst, VReg, u32)] {
///
/// The instruction indices imply a program point just *before*
/// the instruction.
///
/// Preconditions: we require this slice to be sorted in
/// lexicographic order (i.e., first by vreg, then by instruction
/// index), and we require the ranges to be non-overlapping.
fn debug_value_labels(&self) -> &[(VReg, Inst, Inst, u32)] {
&[]
}