Add per-instruction source locations to the Cretonne IR.
Source locations are opaque 32-bit entities that can be used to represent WebAssembly byte-code positions or some other source identifier.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
use entity::{PrimaryMap, EntityMap};
|
||||
use ir;
|
||||
use ir::{FunctionName, CallConv, Signature, DataFlowGraph, Layout};
|
||||
use ir::{InstEncodings, ValueLocations, JumpTables, StackSlots, EbbOffsets};
|
||||
use ir::{InstEncodings, ValueLocations, JumpTables, StackSlots, EbbOffsets, SourceLocs};
|
||||
use ir::{Ebb, JumpTableData, JumpTable, StackSlotData, StackSlot, SigRef, ExtFuncData, FuncRef,
|
||||
GlobalVarData, GlobalVar, HeapData, Heap};
|
||||
use isa::TargetIsa;
|
||||
@@ -56,6 +56,12 @@ pub struct Function {
|
||||
/// computes it, and it can easily be recomputed by calling that function. It is not included
|
||||
/// in the textual IL format.
|
||||
pub offsets: EbbOffsets,
|
||||
|
||||
/// Source locations.
|
||||
///
|
||||
/// Track the original source location for each instruction. The source locations are not
|
||||
/// interpreted by Cretonne, only preserved.
|
||||
pub srclocs: SourceLocs,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
@@ -73,6 +79,7 @@ impl Function {
|
||||
encodings: EntityMap::new(),
|
||||
locations: EntityMap::new(),
|
||||
offsets: EntityMap::new(),
|
||||
srclocs: EntityMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +95,7 @@ impl Function {
|
||||
self.encodings.clear();
|
||||
self.locations.clear();
|
||||
self.offsets.clear();
|
||||
self.srclocs.clear();
|
||||
}
|
||||
|
||||
/// Create a new empty, anonymous function with a native calling convention.
|
||||
|
||||
@@ -17,6 +17,7 @@ mod globalvar;
|
||||
mod heap;
|
||||
mod memflags;
|
||||
mod progpoint;
|
||||
mod sourceloc;
|
||||
mod trapcode;
|
||||
mod valueloc;
|
||||
|
||||
@@ -34,6 +35,7 @@ pub use ir::jumptable::JumpTableData;
|
||||
pub use ir::layout::{Layout, CursorBase, Cursor};
|
||||
pub use ir::memflags::MemFlags;
|
||||
pub use ir::progpoint::{ProgramPoint, ProgramOrder, ExpandedProgramPoint};
|
||||
pub use ir::sourceloc::SourceLoc;
|
||||
pub use ir::stackslot::{StackSlots, StackSlotKind, StackSlotData};
|
||||
pub use ir::trapcode::TrapCode;
|
||||
pub use ir::types::Type;
|
||||
@@ -54,3 +56,6 @@ pub type InstEncodings = EntityMap<Inst, isa::Encoding>;
|
||||
|
||||
/// Code offsets for EBBs.
|
||||
pub type EbbOffsets = EntityMap<Ebb, binemit::CodeOffset>;
|
||||
|
||||
/// Source locations for instructions.
|
||||
pub type SourceLocs = EntityMap<Inst, SourceLoc>;
|
||||
|
||||
62
lib/cretonne/src/ir/sourceloc.rs
Normal file
62
lib/cretonne/src/ir/sourceloc.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
//! Source locations.
|
||||
//!
|
||||
//! Cretonne tracks the original source location of each instruction, and preserves the source
|
||||
//! location when instructions are transformed.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A source location.
|
||||
///
|
||||
/// This is an opaque 32-bit number attached to each Cretonne IL instruction. Cretonne does not
|
||||
/// interpret source locations in any way, they are simply preserved from the input to the output.
|
||||
///
|
||||
/// The default source location uses the all-ones bit pattern `!0`. It is used for instructions
|
||||
/// that can't be given a real source location.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct SourceLoc(u32);
|
||||
|
||||
impl SourceLoc {
|
||||
/// Create a new source location with the given bits.
|
||||
pub fn new(bits: u32) -> SourceLoc {
|
||||
SourceLoc(bits)
|
||||
}
|
||||
|
||||
/// Is this the default source location?
|
||||
pub fn is_default(self) -> bool {
|
||||
self == Default::default()
|
||||
}
|
||||
|
||||
/// Read the bits of this source location.
|
||||
pub fn bits(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SourceLoc {
|
||||
fn default() -> SourceLoc {
|
||||
SourceLoc(!0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SourceLoc {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_default() {
|
||||
write!(f, "@-")
|
||||
} else {
|
||||
write!(f, "@{:04x}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ir::SourceLoc;
|
||||
|
||||
#[test]
|
||||
fn display() {
|
||||
assert_eq!(SourceLoc::default().to_string(), "@-");
|
||||
assert_eq!(SourceLoc::new(0).to_string(), "@0000");
|
||||
assert_eq!(SourceLoc::new(16).to_string(), "@0010");
|
||||
assert_eq!(SourceLoc::new(0xabcdef).to_string(), "@abcdef");
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,7 @@ pub fn write_ebb_header(
|
||||
func: &Function,
|
||||
isa: Option<&TargetIsa>,
|
||||
ebb: Ebb,
|
||||
indent: usize,
|
||||
) -> Result {
|
||||
// Write out the basic block header, outdented:
|
||||
//
|
||||
@@ -114,19 +115,17 @@ pub fn write_ebb_header(
|
||||
// ebb10(v4: f64, v5: b1):
|
||||
//
|
||||
|
||||
// If we're writing encoding annotations, shift by 20.
|
||||
if !func.encodings.is_empty() {
|
||||
write!(w, " ")?;
|
||||
}
|
||||
// The `indent` is the instruction indentation. EBB headers are 4 spaces out from that.
|
||||
write!(w, "{1:0$}{2}", indent - 4, "", ebb)?;
|
||||
|
||||
let regs = isa.map(TargetIsa::register_info);
|
||||
let regs = regs.as_ref();
|
||||
|
||||
let mut args = func.dfg.ebb_args(ebb).iter().cloned();
|
||||
match args.next() {
|
||||
None => return writeln!(w, "{}:", ebb),
|
||||
None => return writeln!(w, ":"),
|
||||
Some(arg) => {
|
||||
write!(w, "{}(", ebb)?;
|
||||
write!(w, "(")?;
|
||||
write_arg(w, func, regs, arg)?;
|
||||
}
|
||||
}
|
||||
@@ -139,9 +138,16 @@ pub fn write_ebb_header(
|
||||
}
|
||||
|
||||
pub fn write_ebb(w: &mut Write, func: &Function, isa: Option<&TargetIsa>, ebb: Ebb) -> Result {
|
||||
write_ebb_header(w, func, isa, ebb)?;
|
||||
// Indent all instructions if any encodings are present.
|
||||
let indent = if func.encodings.is_empty() && func.srclocs.is_empty() {
|
||||
4
|
||||
} else {
|
||||
36
|
||||
};
|
||||
|
||||
write_ebb_header(w, func, isa, ebb, indent)?;
|
||||
for inst in func.layout.ebb_insts(ebb) {
|
||||
write_instruction(w, func, isa, inst)?;
|
||||
write_instruction(w, func, isa, inst, indent)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -203,16 +209,22 @@ fn write_instruction(
|
||||
func: &Function,
|
||||
isa: Option<&TargetIsa>,
|
||||
inst: Inst,
|
||||
indent: usize,
|
||||
) -> Result {
|
||||
// Indent all instructions to col 24 if any encodings are present.
|
||||
let indent = if func.encodings.is_empty() { 4 } else { 24 };
|
||||
|
||||
// Value aliases come out on lines before the instruction using them.
|
||||
write_value_aliases(w, func, inst, indent)?;
|
||||
|
||||
// Prefix containing source location, encoding, and value locations.
|
||||
let mut s = String::with_capacity(16);
|
||||
|
||||
// Source location goes first.
|
||||
let srcloc = func.srclocs[inst];
|
||||
if !srcloc.is_default() {
|
||||
write!(s, "{} ", srcloc)?;
|
||||
}
|
||||
|
||||
// Write out encoding info.
|
||||
if let Some(enc) = func.encodings.get(inst).cloned() {
|
||||
let mut s = String::with_capacity(16);
|
||||
if let Some(isa) = isa {
|
||||
write!(s, "[{}", isa.encoding_info().display(enc))?;
|
||||
// Write value locations, if we have them.
|
||||
@@ -222,17 +234,15 @@ fn write_instruction(
|
||||
write!(s, ",{}", func.locations[r].display(®s))?
|
||||
}
|
||||
}
|
||||
write!(s, "]")?;
|
||||
write!(s, "] ")?;
|
||||
} else {
|
||||
write!(s, "[{}]", enc)?;
|
||||
write!(s, "[{}] ", enc)?;
|
||||
}
|
||||
// Align instruction following ISA annotation to col 24.
|
||||
write!(w, "{:23} ", s)?;
|
||||
} else {
|
||||
// No annotations, simply indent.
|
||||
write!(w, "{1:0$}", indent, "")?;
|
||||
}
|
||||
|
||||
// Write out prefix and indent the instruction.
|
||||
write!(w, "{1:0$}", indent, s)?;
|
||||
|
||||
// Write out the result values, if any.
|
||||
let mut has_results = false;
|
||||
for r in func.dfg.inst_results(inst) {
|
||||
|
||||
Reference in New Issue
Block a user