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>>>,
|
block_params_out: Vec<Vec<Vec<VReg>>>,
|
||||||
num_vregs: usize,
|
num_vregs: usize,
|
||||||
reftype_vregs: Vec<VReg>,
|
reftype_vregs: Vec<VReg>,
|
||||||
|
debug_value_labels: Vec<(VReg, Inst, Inst, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function for Func {
|
impl Function for Func {
|
||||||
@@ -119,6 +120,10 @@ impl Function for Func {
|
|||||||
&self.reftype_vregs[..]
|
&self.reftype_vregs[..]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn debug_value_labels(&self) -> &[(VReg, Inst, Inst, u32)] {
|
||||||
|
&self.debug_value_labels[..]
|
||||||
|
}
|
||||||
|
|
||||||
fn is_move(&self, _: Inst) -> Option<(Operand, Operand)> {
|
fn is_move(&self, _: Inst) -> Option<(Operand, Operand)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -164,6 +169,7 @@ impl FuncBuilder {
|
|||||||
blocks: vec![],
|
blocks: vec![],
|
||||||
num_vregs: 0,
|
num_vregs: 0,
|
||||||
reftype_vregs: vec![],
|
reftype_vregs: vec![],
|
||||||
|
debug_value_labels: vec![],
|
||||||
},
|
},
|
||||||
insts_per_block: vec![],
|
insts_per_block: vec![],
|
||||||
}
|
}
|
||||||
@@ -375,6 +381,21 @@ impl Func {
|
|||||||
if opts.reftypes && bool::arbitrary(u)? {
|
if opts.reftypes && bool::arbitrary(u)? {
|
||||||
builder.f.reftype_vregs.push(vreg);
|
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.push(vregs.clone());
|
||||||
vregs_by_block_to_be_defined.push(vec![]);
|
vregs_by_block_to_be_defined.push(vec![]);
|
||||||
@@ -536,6 +557,8 @@ impl Func {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder.f.debug_value_labels.sort_unstable();
|
||||||
|
|
||||||
Ok(builder.finalize())
|
Ok(builder.finalize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -336,10 +336,6 @@ pub struct Env<'a, F: Function> {
|
|||||||
pub liveouts: Vec<IndexSet>,
|
pub liveouts: Vec<IndexSet>,
|
||||||
pub blockparam_outs: Vec<BlockparamOut>,
|
pub blockparam_outs: Vec<BlockparamOut>,
|
||||||
pub blockparam_ins: Vec<BlockparamIn>,
|
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 ranges: Vec<LiveRange>,
|
||||||
pub bundles: Vec<LiveBundle>,
|
pub bundles: Vec<LiveBundle>,
|
||||||
@@ -390,6 +386,7 @@ pub struct Env<'a, F: Function> {
|
|||||||
pub inst_alloc_offsets: Vec<u32>,
|
pub inst_alloc_offsets: Vec<u32>,
|
||||||
pub num_spillslots: u32,
|
pub num_spillslots: u32,
|
||||||
pub safepoint_slots: Vec<(ProgPoint, Allocation)>,
|
pub safepoint_slots: Vec<(ProgPoint, Allocation)>,
|
||||||
|
pub debug_locations: Vec<(u32, ProgPoint, ProgPoint, Allocation)>,
|
||||||
|
|
||||||
pub allocated_bundle_count: usize,
|
pub allocated_bundle_count: usize,
|
||||||
|
|
||||||
@@ -595,7 +592,6 @@ pub struct Stats {
|
|||||||
pub spill_bundle_reg_success: usize,
|
pub spill_bundle_reg_success: usize,
|
||||||
pub blockparam_ins_count: usize,
|
pub blockparam_ins_count: usize,
|
||||||
pub blockparam_outs_count: usize,
|
pub blockparam_outs_count: usize,
|
||||||
pub blockparam_allocs_count: usize,
|
|
||||||
pub halfmoves_count: usize,
|
pub halfmoves_count: usize,
|
||||||
pub edits_count: usize,
|
pub edits_count: usize,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
liveouts: Vec::with_capacity(func.num_blocks()),
|
liveouts: Vec::with_capacity(func.num_blocks()),
|
||||||
blockparam_outs: vec![],
|
blockparam_outs: vec![],
|
||||||
blockparam_ins: vec![],
|
blockparam_ins: vec![],
|
||||||
blockparam_allocs: vec![],
|
|
||||||
bundles: Vec::with_capacity(n),
|
bundles: Vec::with_capacity(n),
|
||||||
ranges: Vec::with_capacity(4 * n),
|
ranges: Vec::with_capacity(4 * n),
|
||||||
spillsets: Vec::with_capacity(n),
|
spillsets: Vec::with_capacity(n),
|
||||||
@@ -81,6 +80,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
inst_alloc_offsets: vec![],
|
inst_alloc_offsets: vec![],
|
||||||
num_spillslots: 0,
|
num_spillslots: 0,
|
||||||
safepoint_slots: vec![],
|
safepoint_slots: vec![],
|
||||||
|
debug_locations: vec![],
|
||||||
|
|
||||||
stats: Stats::default(),
|
stats: Stats::default(),
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ pub fn run<F: Function>(
|
|||||||
allocs: env.allocs,
|
allocs: env.allocs,
|
||||||
inst_alloc_offsets: env.inst_alloc_offsets,
|
inst_alloc_offsets: env.inst_alloc_offsets,
|
||||||
num_spillslots: env.num_spillslots as usize,
|
num_spillslots: env.num_spillslots as usize,
|
||||||
debug_locations: vec![],
|
debug_locations: env.debug_locations,
|
||||||
safepoint_slots: env.safepoint_slots,
|
safepoint_slots: env.safepoint_slots,
|
||||||
stats: env.stats,
|
stats: env.stats,
|
||||||
})
|
})
|
||||||
|
|||||||
131
src/ion/moves.rs
131
src/ion/moves.rs
@@ -17,7 +17,7 @@ use super::{
|
|||||||
VRegIndex, SLOT_NONE,
|
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::moves::ParallelMoves;
|
||||||
use crate::{
|
use crate::{
|
||||||
Allocation, Block, Edit, Function, Inst, InstPosition, OperandConstraint, OperandKind,
|
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 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);
|
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();
|
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.
|
// 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.
|
// Scan over program move srcs/dsts to fill in allocations.
|
||||||
|
|
||||||
// Move srcs happen at `After` of a given
|
// 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()]),
|
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) {
|
pub fn resolve_inserted_moves(&mut self) {
|
||||||
@@ -939,18 +1010,12 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
// regs, but this seems simpler.)
|
// regs, but this seems simpler.)
|
||||||
let mut int_moves: SmallVec<[InsertedMove; 8]> = smallvec![];
|
let mut int_moves: SmallVec<[InsertedMove; 8]> = smallvec![];
|
||||||
let mut float_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 {
|
for m in moves {
|
||||||
if m.from_alloc.is_reg() && m.to_alloc.is_reg() {
|
if m.from_alloc.is_reg() && m.to_alloc.is_reg() {
|
||||||
debug_assert_eq!(m.from_alloc.class(), m.to_alloc.class());
|
debug_assert_eq!(m.from_alloc.class(), m.to_alloc.class());
|
||||||
}
|
}
|
||||||
if m.from_alloc == m.to_alloc {
|
if m.from_alloc == m.to_alloc {
|
||||||
#[cfg(feature = "checker")]
|
|
||||||
if m.to_vreg.is_some() {
|
|
||||||
self_moves.push(m.clone());
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
match m.from_alloc.class() {
|
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
|
// 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
|
/// match -- specifically, the returned metadata may cover only a
|
||||||
/// subset of the requested ranges -- if the value is not live for
|
/// subset of the requested ranges -- if the value is not live for
|
||||||
/// the entire requested ranges.
|
/// 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