Reconstruct locations of the original source variable
This commit is contained in:
committed by
Dan Gohman
parent
d6059d4605
commit
8f95c51730
@@ -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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
268
cranelift/codegen/src/value_label.rs
Normal file
268
cranelift/codegen/src/value_label.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user