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(())
|
||||
|
||||
@@ -9,7 +9,7 @@ use cranelift_codegen::ir::{
|
||||
types, AbiParam, DataFlowGraph, Ebb, ExtFuncData, ExternalName, FuncRef, Function, GlobalValue,
|
||||
GlobalValueData, Heap, HeapData, Inst, InstBuilder, InstBuilderBase, InstructionData,
|
||||
JumpTable, JumpTableData, LibCall, MemFlags, SigRef, Signature, StackSlot, StackSlotData, Type,
|
||||
Value,
|
||||
Value, ValueLabel, ValueLabelAssignments, ValueLabelStart,
|
||||
};
|
||||
use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa};
|
||||
use cranelift_codegen::packed_option::PackedOption;
|
||||
@@ -333,6 +333,28 @@ impl<'a> FunctionBuilder<'a> {
|
||||
.def_var(var, val, self.position.basic_block.unwrap());
|
||||
}
|
||||
|
||||
/// Set label for Value
|
||||
pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
|
||||
if let Some(values_labels) = self.func.dfg.values_labels.as_mut() {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let start = ValueLabelStart {
|
||||
from: self.srcloc,
|
||||
label,
|
||||
};
|
||||
|
||||
match values_labels.entry(val) {
|
||||
Entry::Occupied(mut e) => match e.get_mut() {
|
||||
ValueLabelAssignments::Starts(starts) => starts.push(start),
|
||||
_ => panic!("Unexpected ValueLabelAssignments at this stage"),
|
||||
},
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(ValueLabelAssignments::Starts(vec![start]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a jump table in the function, to be used by `br_table` instructions.
|
||||
pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
|
||||
self.func.create_jump_table(data)
|
||||
|
||||
@@ -20,6 +20,6 @@ fuzz_target!(|data: &[u8]| {
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let triple = triple!("x86_64");
|
||||
let isa = isa::lookup(triple).unwrap().finish(flags);
|
||||
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), ReturnMode::NormalReturns);
|
||||
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), ReturnMode::NormalReturns, false);
|
||||
translate_module(&wasm, &mut dummy_environ).unwrap();
|
||||
});
|
||||
|
||||
@@ -184,7 +184,13 @@ fn main() {
|
||||
"Just checks the correctness of Cranelift IR translated from WebAssembly",
|
||||
)),
|
||||
)
|
||||
.subcommand(add_wasm_or_compile("wasm"))
|
||||
.subcommand(
|
||||
add_wasm_or_compile("wasm").arg(
|
||||
Arg::with_name("value-ranges")
|
||||
.long("value-ranges")
|
||||
.help("Display values ranges and their locations"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("pass")
|
||||
.about("Run specified pass(s) on an input file.")
|
||||
@@ -269,6 +275,7 @@ fn main() {
|
||||
target_val,
|
||||
rest_cmd.is_present("print-size"),
|
||||
rest_cmd.is_present("time-passes"),
|
||||
rest_cmd.is_present("value-ranges"),
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use cranelift_codegen::print_errors::{pretty_error, pretty_verifier_error};
|
||||
use cranelift_codegen::settings::FlagsOrIsa;
|
||||
use cranelift_codegen::timing;
|
||||
use cranelift_codegen::Context;
|
||||
use cranelift_codegen::ir::DisplayFunctionAnnotations;
|
||||
use cranelift_entity::EntityRef;
|
||||
use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode};
|
||||
use std::path::Path;
|
||||
@@ -47,6 +48,7 @@ pub fn run(
|
||||
flag_triple: &str,
|
||||
flag_print_size: bool,
|
||||
flag_report_times: bool,
|
||||
flag_calc_value_ranges: bool,
|
||||
) -> Result<(), String> {
|
||||
let parsed = parse_sets_and_triple(flag_set, flag_triple)?;
|
||||
|
||||
@@ -61,6 +63,7 @@ pub fn run(
|
||||
flag_print_size,
|
||||
flag_print_disasm,
|
||||
flag_report_times,
|
||||
flag_calc_value_ranges,
|
||||
&path.to_path_buf(),
|
||||
&name,
|
||||
parsed.as_fisa(),
|
||||
@@ -77,6 +80,7 @@ fn handle_module(
|
||||
flag_print_size: bool,
|
||||
flag_print_disasm: bool,
|
||||
flag_report_times: bool,
|
||||
flag_calc_value_ranges: bool,
|
||||
path: &PathBuf,
|
||||
name: &str,
|
||||
fisa: FlagsOrIsa,
|
||||
@@ -108,7 +112,8 @@ fn handle_module(
|
||||
}
|
||||
};
|
||||
|
||||
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), ReturnMode::NormalReturns);
|
||||
let debug_info = flag_calc_value_ranges;
|
||||
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), ReturnMode::NormalReturns, debug_info);
|
||||
translate_module(&module_binary, &mut dummy_environ).map_err(|e| e.to_string())?;
|
||||
|
||||
let _ = terminal.fg(term::color::GREEN);
|
||||
@@ -205,7 +210,15 @@ fn handle_module(
|
||||
{
|
||||
println!("; Exported as \"{}\"", export_name);
|
||||
}
|
||||
println!("{}", context.func.display(fisa.isa));
|
||||
let value_ranges = if flag_calc_value_ranges {
|
||||
Some(context.build_value_labels_ranges(isa).expect("value location ranges"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
println!("{}", context.func.display_with(DisplayFunctionAnnotations {
|
||||
isa: fisa.isa,
|
||||
value_ranges: value_ranges.as_ref(),
|
||||
}));
|
||||
vprintln!(flag_verbose, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ use crate::translation_utils::{FuncIndex, MemoryIndex, SignatureIndex, TableInde
|
||||
use core::{i32, u32};
|
||||
use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
|
||||
use cranelift_codegen::ir::types::*;
|
||||
use cranelift_codegen::ir::{self, InstBuilder, JumpTableData, MemFlags};
|
||||
use cranelift_codegen::ir::{self, InstBuilder, JumpTableData, MemFlags, ValueLabel};
|
||||
use cranelift_codegen::packed_option::ReservedValue;
|
||||
use cranelift_frontend::{FunctionBuilder, Variable};
|
||||
use wasmparser::{MemoryImmediate, Operator};
|
||||
@@ -57,15 +57,22 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
* disappear in the Cranelift Code
|
||||
***********************************************************************************/
|
||||
Operator::GetLocal { local_index } => {
|
||||
state.push1(builder.use_var(Variable::with_u32(local_index)))
|
||||
let val = builder.use_var(Variable::with_u32(local_index));
|
||||
state.push1(val);
|
||||
let label = ValueLabel::from_u32(local_index);
|
||||
builder.set_val_label(val, label);
|
||||
}
|
||||
Operator::SetLocal { local_index } => {
|
||||
let val = state.pop1();
|
||||
builder.def_var(Variable::with_u32(local_index), val);
|
||||
let label = ValueLabel::from_u32(local_index);
|
||||
builder.set_val_label(val, label);
|
||||
}
|
||||
Operator::TeeLocal { local_index } => {
|
||||
let val = state.peek1();
|
||||
builder.def_var(Variable::with_u32(local_index), val);
|
||||
let label = ValueLabel::from_u32(local_index);
|
||||
builder.set_val_label(val, label);
|
||||
}
|
||||
/********************************** Globals ****************************************
|
||||
* `get_global` and `set_global` are handled by the environment.
|
||||
|
||||
@@ -121,16 +121,20 @@ pub struct DummyEnvironment {
|
||||
|
||||
/// How to return from functions.
|
||||
return_mode: ReturnMode,
|
||||
|
||||
/// Instructs to collect debug data during translation.
|
||||
debug_info: bool,
|
||||
}
|
||||
|
||||
impl DummyEnvironment {
|
||||
/// Creates a new `DummyEnvironment` instance.
|
||||
pub fn new(config: TargetFrontendConfig, return_mode: ReturnMode) -> Self {
|
||||
pub fn new(config: TargetFrontendConfig, return_mode: ReturnMode, debug_info: bool) -> Self {
|
||||
Self {
|
||||
info: DummyModuleInfo::new(config),
|
||||
trans: FuncTranslator::new(),
|
||||
func_bytecode_sizes: Vec::new(),
|
||||
return_mode,
|
||||
debug_info,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,6 +486,9 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
||||
let name = get_func_name(func_index);
|
||||
let sig = func_environ.vmctx_sig(self.get_func_type(func_index));
|
||||
let mut func = ir::Function::with_name_signature(name, sig);
|
||||
if self.debug_info {
|
||||
func.collect_debug_info();
|
||||
}
|
||||
self.trans
|
||||
.translate(body_bytes, body_offset, &mut func, &mut func_environ)?;
|
||||
func
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
use crate::code_translator::translate_operator;
|
||||
use crate::environ::{FuncEnvironment, ReturnMode, WasmError, WasmResult};
|
||||
use crate::state::TranslationState;
|
||||
use crate::translation_utils::get_vmctx_value_label;
|
||||
use cranelift_codegen::entity::EntityRef;
|
||||
use cranelift_codegen::ir::{self, Ebb, InstBuilder};
|
||||
use cranelift_codegen::ir::{self, Ebb, InstBuilder, ValueLabel};
|
||||
use cranelift_codegen::timing;
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
|
||||
use log::info;
|
||||
@@ -84,6 +85,7 @@ impl FuncTranslator {
|
||||
|
||||
// This clears the `FunctionBuilderContext`.
|
||||
let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
|
||||
builder.set_srcloc(cur_srcloc(&reader));
|
||||
let entry_block = builder.create_ebb();
|
||||
builder.append_ebb_params_for_function_params(entry_block);
|
||||
builder.switch_to_block(entry_block); // This also creates values for the arguments.
|
||||
@@ -127,6 +129,10 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> u
|
||||
let param_value = builder.ebb_params(entry_block)[i];
|
||||
builder.def_var(local, param_value);
|
||||
}
|
||||
if param_type.purpose == ir::ArgumentPurpose::VMContext {
|
||||
let param_value = builder.ebb_params(entry_block)[i];
|
||||
builder.set_val_label(param_value, get_vmctx_value_label());
|
||||
}
|
||||
}
|
||||
|
||||
next_local
|
||||
@@ -177,6 +183,7 @@ fn declare_locals(
|
||||
let local = Variable::new(*next_local);
|
||||
builder.declare_var(local, ty);
|
||||
builder.def_var(local, zeroval);
|
||||
builder.set_val_label(zeroval, ValueLabel::new(*next_local));
|
||||
*next_local += 1;
|
||||
}
|
||||
Ok(())
|
||||
@@ -265,6 +272,7 @@ mod tests {
|
||||
pointer_width: PointerWidth::U64,
|
||||
},
|
||||
ReturnMode::NormalReturns,
|
||||
false,
|
||||
);
|
||||
|
||||
let mut ctx = Context::new();
|
||||
@@ -304,6 +312,7 @@ mod tests {
|
||||
pointer_width: PointerWidth::U64,
|
||||
},
|
||||
ReturnMode::NormalReturns,
|
||||
false,
|
||||
);
|
||||
let mut ctx = Context::new();
|
||||
|
||||
@@ -351,6 +360,7 @@ mod tests {
|
||||
pointer_width: PointerWidth::U64,
|
||||
},
|
||||
ReturnMode::NormalReturns,
|
||||
false,
|
||||
);
|
||||
let mut ctx = Context::new();
|
||||
|
||||
|
||||
@@ -64,9 +64,9 @@ pub use crate::environ::{
|
||||
pub use crate::func_translator::FuncTranslator;
|
||||
pub use crate::module_translator::translate_module;
|
||||
pub use crate::translation_utils::{
|
||||
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, Global,
|
||||
GlobalIndex, GlobalInit, Memory, MemoryIndex, SignatureIndex, Table, TableElementType,
|
||||
TableIndex,
|
||||
get_vmctx_value_label, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex,
|
||||
DefinedTableIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex,
|
||||
SignatureIndex, Table, TableElementType, TableIndex,
|
||||
};
|
||||
|
||||
/// Version number of this crate.
|
||||
|
||||
@@ -140,3 +140,9 @@ pub fn num_return_values(ty: wasmparser::Type) -> usize {
|
||||
_ => panic!("unsupported return value type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Special VMContext value label. It is tracked as 0xffff_fffe label.
|
||||
pub fn get_vmctx_value_label() -> ir::ValueLabel {
|
||||
const VMCTX_LABEL: u32 = 0xffff_fffe;
|
||||
ir::ValueLabel::from_u32(VMCTX_LABEL)
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ fn handle_module(path: &Path, flags: &Flags, return_mode: ReturnMode) {
|
||||
};
|
||||
let triple = triple!("riscv64");
|
||||
let isa = isa::lookup(triple).unwrap().finish(flags.clone());
|
||||
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode);
|
||||
let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode, false);
|
||||
|
||||
translate_module(&data, &mut dummy_environ).unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user