Reconstruct locations of the original source variable

This commit is contained in:
Yury Delendik
2019-03-06 11:20:26 -06:00
committed by Dan Gohman
parent d6059d4605
commit 8f95c51730
21 changed files with 556 additions and 31 deletions

View File

@@ -29,6 +29,7 @@ use crate::simple_gvn::do_simple_gvn;
use crate::simple_preopt::do_preopt;
use crate::timing;
use crate::unreachable_code::eliminate_unreachable_code;
use crate::value_label::{build_value_labels_ranges, ComparableSourceLoc, ValueLabelsRanges};
use crate::verifier::{verify_context, verify_locations, VerifierErrors, VerifierResult};
use std::vec::Vec;
@@ -331,4 +332,13 @@ impl Context {
self.verify_locations_if(isa)?;
Ok(code_size)
}
/// Builds ranges and location for specified value labels.
pub fn build_value_labels_ranges(&self, isa: &TargetIsa) -> CodegenResult<ValueLabelsRanges> {
Ok(build_value_labels_ranges::<ComparableSourceLoc>(
&self.func,
&self.regalloc,
isa,
))
}
}

View File

@@ -6,7 +6,10 @@ use crate::ir::builder::ReplaceBuilder;
use crate::ir::extfunc::ExtFuncData;
use crate::ir::instructions::{BranchInfo, CallInfo, InstructionData};
use crate::ir::types;
use crate::ir::{Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueList, ValueListPool};
use crate::ir::{
Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueLabelAssignments, ValueList,
ValueListPool,
};
use crate::isa::TargetIsa;
use crate::packed_option::ReservedValue;
use crate::write::write_operands;
@@ -15,6 +18,7 @@ use core::iter;
use core::mem;
use core::ops::{Index, IndexMut};
use core::u16;
use std::collections::HashMap;
/// A data flow graph defines all instructions and extended basic blocks in a function as well as
/// the data flow dependencies between them. The DFG also tracks values which can be either
@@ -60,6 +64,9 @@ pub struct DataFlowGraph {
/// External function references. These are functions that can be called directly.
pub ext_funcs: PrimaryMap<FuncRef, ExtFuncData>,
/// Saves Value labels.
pub values_labels: Option<HashMap<Value, ValueLabelAssignments>>,
}
impl DataFlowGraph {
@@ -73,6 +80,7 @@ impl DataFlowGraph {
values: PrimaryMap::new(),
signatures: PrimaryMap::new(),
ext_funcs: PrimaryMap::new(),
values_labels: None,
}
}
@@ -85,6 +93,7 @@ impl DataFlowGraph {
self.values.clear();
self.signatures.clear();
self.ext_funcs.clear();
self.values_labels = None;
}
/// Get the total number of instructions created in this function, whether they are currently
@@ -117,6 +126,13 @@ impl DataFlowGraph {
pub fn num_values(&self) -> usize {
self.values.len()
}
/// Starts collection of debug information.
pub fn collect_debug_info(&mut self) {
if self.values_labels.is_none() {
self.values_labels = Some(HashMap::new());
}
}
}
/// Resolve value aliases.

View File

@@ -15,6 +15,7 @@ use crate::ir::{EbbOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocation
use crate::ir::{JumpTableOffsets, JumpTables};
use crate::isa::{CallConv, EncInfo, Encoding, Legalize, TargetIsa};
use crate::regalloc::RegDiversions;
use crate::value_label::ValueLabelsRanges;
use crate::write::write_function;
use core::fmt;
@@ -155,7 +156,15 @@ impl Function {
/// Return an object that can display this function with correct ISA-specific annotations.
pub fn display<'a, I: Into<Option<&'a TargetIsa>>>(&'a self, isa: I) -> DisplayFunction<'a> {
DisplayFunction(self, isa.into())
DisplayFunction(self, isa.into().into())
}
/// Return an object that can display this function with correct ISA-specific annotations.
pub fn display_with<'a>(
&'a self,
annotations: DisplayFunctionAnnotations<'a>,
) -> DisplayFunction<'a> {
DisplayFunction(self, annotations)
}
/// Find a presumed unique special-purpose function parameter value.
@@ -202,26 +211,58 @@ impl Function {
pub fn encode(&self, inst: ir::Inst, isa: &TargetIsa) -> Result<Encoding, Legalize> {
isa.encode(&self, &self.dfg[inst], self.dfg.ctrl_typevar(inst))
}
/// Starts collection of debug information.
pub fn collect_debug_info(&mut self) {
self.dfg.collect_debug_info();
}
}
/// Additional annotations for function display.
pub struct DisplayFunctionAnnotations<'a> {
/// Enable ISA annotations.
pub isa: Option<&'a TargetIsa>,
/// Enable value labels annotations.
pub value_ranges: Option<&'a ValueLabelsRanges>,
}
impl<'a> DisplayFunctionAnnotations<'a> {
fn default() -> Self {
DisplayFunctionAnnotations {
isa: None,
value_ranges: None,
}
}
}
impl<'a> From<Option<&'a TargetIsa>> for DisplayFunctionAnnotations<'a> {
fn from(isa: Option<&'a TargetIsa>) -> DisplayFunctionAnnotations {
DisplayFunctionAnnotations {
isa,
value_ranges: None,
}
}
}
/// Wrapper type capable of displaying a `Function` with correct ISA annotations.
pub struct DisplayFunction<'a>(&'a Function, Option<&'a TargetIsa>);
pub struct DisplayFunction<'a>(&'a Function, DisplayFunctionAnnotations<'a>);
impl<'a> fmt::Display for DisplayFunction<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self.0, self.1)
write_function(fmt, self.0, &self.1)
}
}
impl fmt::Display for Function {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self, None)
write_function(fmt, self, &DisplayFunctionAnnotations::default())
}
}
impl fmt::Debug for Function {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self, None)
write_function(fmt, self, &DisplayFunctionAnnotations::default())
}
}

View File

@@ -32,7 +32,7 @@ pub use crate::ir::extfunc::{
AbiParam, ArgumentExtension, ArgumentPurpose, ExtFuncData, Signature,
};
pub use crate::ir::extname::ExternalName;
pub use crate::ir::function::Function;
pub use crate::ir::function::{DisplayFunctionAnnotations, Function};
pub use crate::ir::globalvalue::GlobalValueData;
pub use crate::ir::heap::{HeapData, HeapStyle};
pub use crate::ir::instructions::{
@@ -51,7 +51,7 @@ pub use crate::ir::types::Type;
pub use crate::ir::valueloc::{ArgumentLoc, ValueLoc};
use crate::binemit;
use crate::entity::{PrimaryMap, SecondaryMap};
use crate::entity::{entity_impl, PrimaryMap, SecondaryMap};
use crate::isa;
/// Map of value locations.
@@ -71,3 +71,34 @@ pub type JumpTableOffsets = SecondaryMap<JumpTable, binemit::CodeOffset>;
/// Source locations for instructions.
pub type SourceLocs = SecondaryMap<Inst, SourceLoc>;
/// Marked with a label value.
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ValueLabel(u32);
entity_impl!(ValueLabel, "val");
/// A label of a Value.
#[derive(Debug, Clone)]
pub struct ValueLabelStart {
/// Source location when it is in effect
pub from: SourceLoc,
/// The label index.
pub label: ValueLabel,
}
/// Value label assignements: label starts or value aliases.
#[derive(Debug, Clone)]
pub enum ValueLabelAssignments {
/// Original value labels assigned at transform.
Starts(std::vec::Vec<ValueLabelStart>),
/// A value alias to original value.
Alias {
/// Source location when it is in effect
from: SourceLoc,
/// The label index.
value: Value,
},
}

View File

@@ -151,7 +151,17 @@ fn compute_addr(
// Convert `offset` to `addr_ty`.
if offset_ty != addr_ty {
let labels_value = offset;
offset = pos.ins().uextend(addr_ty, offset);
if let Some(values_labels) = pos.func.dfg.values_labels.as_mut() {
values_labels.insert(
offset,
ir::ValueLabelAssignments::Alias {
from: pos.func.srclocs[inst],
value: labels_value,
},
);
}
}
// Add the heap base address base

View File

@@ -57,6 +57,7 @@ use std::collections::{hash_map, HashMap, HashSet};
pub use crate::context::Context;
pub use crate::legalizer::legalize_function;
pub use crate::value_label::ValueLabelsRanges;
pub use crate::verifier::verify_function;
pub use crate::write::write_function;
@@ -103,6 +104,7 @@ mod simple_preopt;
mod stack_layout;
mod topo_order;
mod unreachable_code;
mod value_label;
pub use crate::result::{CodegenError, CodegenResult};

View File

@@ -29,7 +29,7 @@ pub fn pretty_verifier_error<'a>(
&mut PrettyVerifierError(func_w.unwrap_or_else(|| Box::new(PlainWriter)), &mut errors),
&mut w,
func,
isa,
&isa.into(),
)
.unwrap();

View File

@@ -64,6 +64,11 @@ impl Context {
self.coloring.clear();
}
/// Current values liveness state.
pub fn liveness(&self) -> &Liveness {
&self.liveness
}
/// Allocate registers in `func`.
///
/// After register allocation, all values in `func` have been assigned to a register or stack

View File

@@ -314,6 +314,11 @@ impl Liveness {
}
}
/// Current live ranges.
pub fn ranges(&self) -> &LiveRangeSet {
&self.ranges
}
/// Get a context needed for working with a `LiveRange`.
pub fn context<'a>(&'a self, layout: &'a Layout) -> LiveRangeContext<'a, Layout> {
LiveRangeContext::new(layout, &self.forest)

View File

@@ -0,0 +1,268 @@
use crate::ir::{Function, SourceLoc, Value, ValueLabel, ValueLabelAssignments, ValueLoc};
use crate::isa::TargetIsa;
use crate::regalloc::{Context, RegDiversions};
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap};
use std::iter::Iterator;
use std::ops::Bound::*;
use std::ops::Deref;
use std::vec::Vec;
/// Value location range.
#[derive(Debug, Clone, Copy)]
pub struct ValueLocRange {
pub loc: ValueLoc,
pub start: u32,
pub end: u32,
}
/// Resulting map of Value labels and their ranges/locations.
pub type ValueLabelsRanges = HashMap<ValueLabel, Vec<ValueLocRange>>;
fn build_value_labels_index<T>(func: &Function) -> BTreeMap<T, (Value, ValueLabel)>
where
T: From<SourceLoc> + Deref<Target = SourceLoc> + Ord + Copy,
{
if func.dfg.values_labels.is_none() {
return BTreeMap::new();
}
let values_labels = func.dfg.values_labels.as_ref().unwrap();
// Index values_labels by srcloc/from
let mut sorted = BTreeMap::new();
for (val, assigns) in values_labels {
match assigns {
ValueLabelAssignments::Starts(labels) => {
for label in labels {
if label.from.is_default() {
continue;
}
let srcloc = T::from(label.from);
let label = label.label;
sorted.insert(srcloc, (*val, label));
}
}
ValueLabelAssignments::Alias { from, value } => {
if from.is_default() {
continue;
}
let mut aliased_value = *value;
while let Some(ValueLabelAssignments::Alias { value, .. }) =
values_labels.get(&aliased_value)
{
// TODO check/limit recursion?
aliased_value = *value;
}
let from = T::from(*from);
if let Some(ValueLabelAssignments::Starts(labels)) =
values_labels.get(&aliased_value)
{
for label in labels {
let srcloc = if label.from.is_default() {
from
} else {
from.max(T::from(label.from))
};
let label = label.label;
sorted.insert(srcloc, (*val, label));
}
}
}
}
}
sorted
}
/// Builds ranges and location for specified value labels.
/// The labels specified at DataFlowGraph's values_labels collection.
pub fn build_value_labels_ranges<T>(
func: &Function,
regalloc: &Context,
isa: &TargetIsa,
) -> ValueLabelsRanges
where
T: From<SourceLoc> + Deref<Target = SourceLoc> + Ord + Copy,
{
let values_labels = build_value_labels_index::<T>(func);
let mut ebbs = func.layout.ebbs().collect::<Vec<_>>();
ebbs.sort_by_key(|ebb| func.offsets[*ebb]); // Ensure inst offsets always increase
let encinfo = isa.encoding_info();
let values_locations = &func.locations;
let liveness_context = regalloc.liveness().context(&func.layout);
let liveness_ranges = regalloc.liveness().ranges();
let mut ranges = HashMap::new();
let mut add_range = |label, range: (u32, u32), loc: ValueLoc| {
if range.0 >= range.1 || !loc.is_assigned() {
return;
}
if !ranges.contains_key(&label) {
ranges.insert(label, Vec::new());
}
ranges.get_mut(&label).unwrap().push(ValueLocRange {
loc,
start: range.0,
end: range.1,
});
};
let mut end_offset = 0;
let mut tracked_values: Vec<(Value, ValueLabel, u32, ValueLoc)> = Vec::new();
let mut divert = RegDiversions::new();
for ebb in ebbs {
divert.clear();
let mut last_srcloc: Option<T> = None;
for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) {
divert.apply(&func.dfg[inst]);
end_offset = offset + size;
// Remove killed values.
tracked_values.retain(|(x, label, start_offset, last_loc)| {
let range = liveness_ranges.get(*x);
if range.expect("value").killed_at(inst, ebb, liveness_context) {
add_range(*label, (*start_offset, end_offset), *last_loc);
return false;
}
return true;
});
let srcloc = func.srclocs[inst];
if srcloc.is_default() {
// Don't process instructions without srcloc.
continue;
}
let srcloc = T::from(srcloc);
// Record and restart ranges if Value location was changed.
for (val, label, start_offset, last_loc) in &mut tracked_values {
let new_loc = divert.get(*val, values_locations);
if new_loc == *last_loc {
continue;
}
add_range(*label, (*start_offset, end_offset), *last_loc);
*start_offset = end_offset;
*last_loc = new_loc;
}
// New source locations range started: abandon all tracked values.
if last_srcloc.is_some() && last_srcloc.as_ref().unwrap() > &srcloc {
for (_, label, start_offset, last_loc) in &tracked_values {
add_range(*label, (*start_offset, end_offset), *last_loc);
}
tracked_values.clear();
last_srcloc = None;
}
// Get non-processed Values based on srcloc
let range = (
match last_srcloc {
Some(a) => Excluded(a),
None => Unbounded,
},
Included(srcloc),
);
let active_values = values_labels.range(range);
let active_values = active_values.filter(|(_, (v, _))| {
// Ignore dead/inactive Values.
let range = liveness_ranges.get(*v);
match range {
Some(r) => r.reaches_use(inst, ebb, liveness_context),
None => false,
}
});
// Append new Values to the tracked_values.
for (_, (val, label)) in active_values {
let loc = divert.get(*val, values_locations);
tracked_values.push((*val, *label, end_offset, loc));
}
last_srcloc = Some(srcloc);
}
// Finish all started ranges.
for (_, label, start_offset, last_loc) in &tracked_values {
add_range(*label, (*start_offset, end_offset), *last_loc);
}
}
// Optimize ranges in-place
for (_, label_ranges) in ranges.iter_mut() {
assert!(label_ranges.len() > 0);
label_ranges.sort_by(|a, b| a.start.cmp(&b.start).then_with(|| a.end.cmp(&b.end)));
// Merge ranges
let mut i = 1;
let mut j = 0;
while i < label_ranges.len() {
assert!(label_ranges[j].start <= label_ranges[i].end);
if label_ranges[j].loc != label_ranges[i].loc {
// Different location
if label_ranges[j].end >= label_ranges[i].end {
// Consumed by previous range, skipping
i += 1;
continue;
}
j += 1;
label_ranges[j] = label_ranges[i];
i += 1;
continue;
}
if label_ranges[j].end < label_ranges[i].start {
// Gap in the range location
j += 1;
label_ranges[j] = label_ranges[i];
i += 1;
continue;
}
// Merge i-th and j-th ranges
if label_ranges[j].end < label_ranges[i].end {
label_ranges[j].end = label_ranges[i].end;
}
i += 1;
}
label_ranges.truncate(j + 1);
// Cut/move start position of next range, if two neighbor ranges intersect.
for i in 0..j {
if label_ranges[i].end > label_ranges[i + 1].start {
label_ranges[i + 1].start = label_ranges[i].end;
assert!(label_ranges[i + 1].start < label_ranges[i + 1].end);
}
assert!(label_ranges[i].end <= label_ranges[i + 1].start);
}
}
ranges
}
#[derive(Eq, Clone, Copy)]
pub struct ComparableSourceLoc(SourceLoc);
impl From<SourceLoc> for ComparableSourceLoc {
fn from(s: SourceLoc) -> Self {
ComparableSourceLoc(s)
}
}
impl Deref for ComparableSourceLoc {
type Target = SourceLoc;
fn deref(&self) -> &SourceLoc {
&self.0
}
}
impl PartialOrd for ComparableSourceLoc {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ComparableSourceLoc {
fn cmp(&self, other: &Self) -> Ordering {
self.0.bits().cmp(&other.0.bits())
}
}
impl PartialEq for ComparableSourceLoc {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}

View File

@@ -5,10 +5,15 @@
use crate::entity::SecondaryMap;
use crate::ir::entities::AnyEntity;
use crate::ir::{DataFlowGraph, Ebb, Function, Inst, SigRef, Type, Value, ValueDef};
use crate::ir::{
DataFlowGraph, DisplayFunctionAnnotations, Ebb, Function, Inst, SigRef, Type, Value, ValueDef,
ValueLoc,
};
use crate::isa::{RegInfo, TargetIsa};
use crate::packed_option::ReservedValue;
use crate::value_label::ValueLabelsRanges;
use core::fmt::{self, Write};
use std::collections::HashSet;
use std::string::String;
use std::vec::Vec;
@@ -154,8 +159,12 @@ impl FuncWriter for PlainWriter {
/// Write `func` to `w` as equivalent text.
/// Use `isa` to emit ISA-dependent annotations.
pub fn write_function(w: &mut Write, func: &Function, isa: Option<&TargetIsa>) -> fmt::Result {
decorate_function(&mut PlainWriter, w, func, isa)
pub fn write_function(
w: &mut Write,
func: &Function,
annotations: &DisplayFunctionAnnotations,
) -> fmt::Result {
decorate_function(&mut PlainWriter, w, func, annotations)
}
/// Create a reverse-alias map from a value to all aliases having that value as a direct target
@@ -177,9 +186,9 @@ pub fn decorate_function<FW: FuncWriter>(
func_w: &mut FW,
w: &mut Write,
func: &Function,
isa: Option<&TargetIsa>,
annotations: &DisplayFunctionAnnotations,
) -> fmt::Result {
let regs = isa.map(TargetIsa::register_info);
let regs = annotations.isa.map(TargetIsa::register_info);
let regs = regs.as_ref();
write!(w, "function ")?;
@@ -191,7 +200,7 @@ pub fn decorate_function<FW: FuncWriter>(
if any {
writeln!(w)?;
}
decorate_ebb(func_w, w, func, &aliases, isa, ebb)?;
decorate_ebb(func_w, w, func, &aliases, annotations, ebb)?;
any = true;
}
writeln!(w, "}}")
@@ -254,12 +263,53 @@ pub fn write_ebb_header(
writeln!(w, "):")
}
fn write_valueloc(w: &mut Write, loc: &ValueLoc, regs: &RegInfo) -> fmt::Result {
match loc {
ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(*r)),
ValueLoc::Stack(ss) => write!(w, "{}", ss),
ValueLoc::Unassigned => write!(w, "?"),
}
}
fn write_value_range_markers(
w: &mut Write,
val_ranges: &ValueLabelsRanges,
regs: &RegInfo,
offset: u32,
indent: usize,
) -> fmt::Result {
let mut result = String::new();
let mut shown = HashSet::new();
for (val, rng) in val_ranges {
for i in (0..rng.len()).rev() {
if rng[i].start == offset {
write!(&mut result, " {}@", val)?;
write_valueloc(&mut result, &rng[i].loc, regs)?;
shown.insert(val);
break;
}
}
}
for (val, rng) in val_ranges {
for i in (0..rng.len()).rev() {
if rng[i].end == offset && !shown.contains(val) {
write!(&mut result, " {}\u{2620}", val)?;
break;
}
}
}
if result.len() > 0 {
writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?;
}
Ok(())
}
fn decorate_ebb<FW: FuncWriter>(
func_w: &mut FW,
w: &mut Write,
func: &Function,
aliases: &SecondaryMap<Value, Vec<Value>>,
isa: Option<&TargetIsa>,
annotations: &DisplayFunctionAnnotations,
ebb: Ebb,
) -> fmt::Result {
// Indent all instructions if any encodings are present.
@@ -268,13 +318,28 @@ fn decorate_ebb<FW: FuncWriter>(
} else {
36
};
let isa = annotations.isa;
func_w.write_ebb_header(w, func, isa, ebb, indent)?;
for a in func.dfg.ebb_params(ebb).iter().cloned() {
write_value_aliases(w, aliases, a, indent)?;
}
for inst in func.layout.ebb_insts(ebb) {
func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
if isa.is_some() && !func.offsets.is_empty() {
let encinfo = isa.unwrap().encoding_info();
let regs = &isa.unwrap().register_info();
for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) {
func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
if size > 0 {
if let Some(val_ranges) = annotations.value_ranges {
write_value_range_markers(w, val_ranges, regs, offset + size, indent)?;
}
}
}
} else {
for inst in func.layout.ebb_insts(ebb) {
func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
}
}
Ok(())