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:
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
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
|
||||
|
||||
@@ -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)] {
|
||||
&[]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user