diff --git a/src/fuzzing/func.rs b/src/fuzzing/func.rs index 89445b4..b0d7ac7 100644 --- a/src/fuzzing/func.rs +++ b/src/fuzzing/func.rs @@ -67,6 +67,7 @@ pub struct Func { block_params_out: Vec>>, num_vregs: usize, reftype_vregs: Vec, + 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::(0..=assumed_end_inst)?; + while start < assumed_end_inst { + let end = u.int_in_range::(start..=assumed_end_inst)?; + let label = u.int_in_range::(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()) } } diff --git a/src/ion/data_structures.rs b/src/ion/data_structures.rs index 2bc4e3b..c9d781a 100644 --- a/src/ion/data_structures.rs +++ b/src/ion/data_structures.rs @@ -336,10 +336,6 @@ pub struct Env<'a, F: Function> { pub liveouts: Vec, pub blockparam_outs: Vec, pub blockparam_ins: Vec, - /// 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, pub bundles: Vec, @@ -390,6 +386,7 @@ pub struct Env<'a, F: Function> { pub inst_alloc_offsets: Vec, 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, } diff --git a/src/ion/mod.rs b/src/ion/mod.rs index 5cd8945..778b229 100644 --- a/src/ion/mod.rs +++ b/src/ion/mod.rs @@ -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( 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, }) diff --git a/src/ion/moves.rs b/src/ion/moves.rs index c5964d0..8d39431 100644 --- a/src/ion/moves.rs +++ b/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 = 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 diff --git a/src/lib.rs b/src/lib.rs index 6a1fd66..acdab87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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)] { &[] }