diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs index 0cd1564c06..493e7a5261 100644 --- a/cranelift/codegen/src/context.rs +++ b/cranelift/codegen/src/context.rs @@ -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 { + Ok(build_value_labels_ranges::( + &self.func, + &self.regalloc, + isa, + )) + } } diff --git a/cranelift/codegen/src/ir/dfg.rs b/cranelift/codegen/src/ir/dfg.rs index 93ea2bbe8c..362a6cd772 100644 --- a/cranelift/codegen/src/ir/dfg.rs +++ b/cranelift/codegen/src/ir/dfg.rs @@ -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, + + /// Saves Value labels. + pub values_labels: Option>, } 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. diff --git a/cranelift/codegen/src/ir/function.rs b/cranelift/codegen/src/ir/function.rs index e0ad323d0a..a07b0318df 100644 --- a/cranelift/codegen/src/ir/function.rs +++ b/cranelift/codegen/src/ir/function.rs @@ -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>>(&'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 { 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> 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()) } } diff --git a/cranelift/codegen/src/ir/mod.rs b/cranelift/codegen/src/ir/mod.rs index 8167d3b607..0873561cf9 100644 --- a/cranelift/codegen/src/ir/mod.rs +++ b/cranelift/codegen/src/ir/mod.rs @@ -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; /// Source locations for instructions. pub type SourceLocs = SecondaryMap; + +/// 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), + + /// A value alias to original value. + Alias { + /// Source location when it is in effect + from: SourceLoc, + + /// The label index. + value: Value, + }, +} diff --git a/cranelift/codegen/src/legalizer/heap.rs b/cranelift/codegen/src/legalizer/heap.rs index 8ffd02ec5f..19217a8af9 100644 --- a/cranelift/codegen/src/legalizer/heap.rs +++ b/cranelift/codegen/src/legalizer/heap.rs @@ -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 diff --git a/cranelift/codegen/src/lib.rs b/cranelift/codegen/src/lib.rs index 48f5a27902..46ab39d971 100644 --- a/cranelift/codegen/src/lib.rs +++ b/cranelift/codegen/src/lib.rs @@ -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}; diff --git a/cranelift/codegen/src/print_errors.rs b/cranelift/codegen/src/print_errors.rs index 2361cb714a..2d09845c92 100644 --- a/cranelift/codegen/src/print_errors.rs +++ b/cranelift/codegen/src/print_errors.rs @@ -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(); diff --git a/cranelift/codegen/src/regalloc/context.rs b/cranelift/codegen/src/regalloc/context.rs index 7ad78743db..a12a09dee1 100644 --- a/cranelift/codegen/src/regalloc/context.rs +++ b/cranelift/codegen/src/regalloc/context.rs @@ -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 diff --git a/cranelift/codegen/src/regalloc/liveness.rs b/cranelift/codegen/src/regalloc/liveness.rs index 5e21c14c90..0befe50bf3 100644 --- a/cranelift/codegen/src/regalloc/liveness.rs +++ b/cranelift/codegen/src/regalloc/liveness.rs @@ -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) diff --git a/cranelift/codegen/src/value_label.rs b/cranelift/codegen/src/value_label.rs new file mode 100644 index 0000000000..f8efca7996 --- /dev/null +++ b/cranelift/codegen/src/value_label.rs @@ -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>; + +fn build_value_labels_index(func: &Function) -> BTreeMap +where + T: From + Deref + 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( + func: &Function, + regalloc: &Context, + isa: &TargetIsa, +) -> ValueLabelsRanges +where + T: From + Deref + Ord + Copy, +{ + let values_labels = build_value_labels_index::(func); + + let mut ebbs = func.layout.ebbs().collect::>(); + 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 = 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 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 { + 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 + } +} diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs index 7731918c59..f1810524d8 100644 --- a/cranelift/codegen/src/write.rs +++ b/cranelift/codegen/src/write.rs @@ -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( 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( 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( func_w: &mut FW, w: &mut Write, func: &Function, aliases: &SecondaryMap>, - isa: Option<&TargetIsa>, + annotations: &DisplayFunctionAnnotations, ebb: Ebb, ) -> fmt::Result { // Indent all instructions if any encodings are present. @@ -268,13 +318,28 @@ fn decorate_ebb( } 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(()) diff --git a/cranelift/frontend/src/frontend.rs b/cranelift/frontend/src/frontend.rs index cefdb5bfe7..c161b85f50 100644 --- a/cranelift/frontend/src/frontend.rs +++ b/cranelift/frontend/src/frontend.rs @@ -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) diff --git a/cranelift/fuzz/fuzz_translate_module.rs b/cranelift/fuzz/fuzz_translate_module.rs index abc742a62f..27854f329f 100644 --- a/cranelift/fuzz/fuzz_translate_module.rs +++ b/cranelift/fuzz/fuzz_translate_module.rs @@ -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(); }); diff --git a/cranelift/src/clif-util.rs b/cranelift/src/clif-util.rs index d51687b4fe..a2b248b0df 100755 --- a/cranelift/src/clif-util.rs +++ b/cranelift/src/clif-util.rs @@ -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"), ) }; diff --git a/cranelift/src/wasm.rs b/cranelift/src/wasm.rs index 67d0225f49..61a8d8b1a9 100644 --- a/cranelift/src/wasm.rs +++ b/cranelift/src/wasm.rs @@ -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, ""); } diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index 6c339f3e1f..d0b504dd69 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -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( * 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. diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs index e32a3260ab..7c6625f069 100644 --- a/cranelift/wasm/src/environ/dummy.rs +++ b/cranelift/wasm/src/environ/dummy.rs @@ -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 diff --git a/cranelift/wasm/src/func_translator.rs b/cranelift/wasm/src/func_translator.rs index efa63c15e9..578c12ef3f 100644 --- a/cranelift/wasm/src/func_translator.rs +++ b/cranelift/wasm/src/func_translator.rs @@ -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(); diff --git a/cranelift/wasm/src/lib.rs b/cranelift/wasm/src/lib.rs index c475b342bc..db10a7936c 100644 --- a/cranelift/wasm/src/lib.rs +++ b/cranelift/wasm/src/lib.rs @@ -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. diff --git a/cranelift/wasm/src/translation_utils.rs b/cranelift/wasm/src/translation_utils.rs index 7ea730c9cb..dc4336c397 100644 --- a/cranelift/wasm/src/translation_utils.rs +++ b/cranelift/wasm/src/translation_utils.rs @@ -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) +} diff --git a/cranelift/wasm/tests/wasm_testsuite.rs b/cranelift/wasm/tests/wasm_testsuite.rs index c496470ec0..f521828282 100644 --- a/cranelift/wasm/tests/wasm_testsuite.rs +++ b/cranelift/wasm/tests/wasm_testsuite.rs @@ -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();