Reconstruct locations of the original source variable

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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)

View File

@@ -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();
});

View File

@@ -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"),
)
};

View File

@@ -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, "");
}

View File

@@ -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.

View File

@@ -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

View File

@@ -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();

View File

@@ -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.

View File

@@ -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)
}

View File

@@ -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();