diff --git a/cranelift/filetests/parser/instruction_encoding.cton b/cranelift/filetests/parser/instruction_encoding.cton index d6dda30da5..6bc7dcd6ce 100644 --- a/cranelift/filetests/parser/instruction_encoding.cton +++ b/cranelift/filetests/parser/instruction_encoding.cton @@ -10,8 +10,8 @@ ebb1(v0: i32 [%x8], v1: i32): [-] trap heap_oob [R#1234, %x5, %x11] v6, v7 = iadd_cout v2, v0 [Rshamt#beef, %x25] v8 = ishl_imm v6, 2 - v9 = iadd v8, v7 - [Iret#5] return v0, v8 +@55 v9 = iadd v8, v7 +@a5 [Iret#5] return v0, v8 } ; sameln: function %foo(i32, i32) native { ; nextln: $ebb1($v0: i32 [%x8], $v1: i32): @@ -19,6 +19,6 @@ ebb1(v0: i32 [%x8], v1: i32): ; nextln: [-]$WS trap heap_oob ; nextln: [R#1234,%x5,%x11]$WS $v6, $v7 = iadd_cout $v2, $v0 ; nextln: [Rshamt#beef,%x25]$WS $v8 = ishl_imm $v6, 2 -; nextln: [-,-]$WS $v9 = iadd $v8, $v7 -; nextln: [Iret#05]$WS return $v0, $v8 +; nextln: @0055 [-,-]$WS $v9 = iadd $v8, $v7 +; nextln: @00a5 [Iret#05]$WS return $v0, $v8 ; nextln: } diff --git a/lib/cretonne/src/ir/function.rs b/lib/cretonne/src/ir/function.rs index f7ba244ae0..f8134b34d9 100644 --- a/lib/cretonne/src/ir/function.rs +++ b/lib/cretonne/src/ir/function.rs @@ -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. diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index 077db6537b..9fafd99b9a 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -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; /// Code offsets for EBBs. pub type EbbOffsets = EntityMap; + +/// Source locations for instructions. +pub type SourceLocs = EntityMap; diff --git a/lib/cretonne/src/ir/sourceloc.rs b/lib/cretonne/src/ir/sourceloc.rs new file mode 100644 index 0000000000..104bc541b6 --- /dev/null +++ b/lib/cretonne/src/ir/sourceloc.rs @@ -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"); + } +} diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 2cbc8766ab..d2f903cc7a 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -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) { diff --git a/lib/reader/src/lexer.rs b/lib/reader/src/lexer.rs index 1f78b27d92..357880ef4a 100644 --- a/lib/reader/src/lexer.rs +++ b/lib/reader/src/lexer.rs @@ -45,6 +45,7 @@ pub enum Token<'a> { Name(&'a str), // %9arbitrary_alphanum, %x3, %0, %function ... HexSequence(&'a str), // #89AF Identifier(&'a str), // Unrecognized identifier (opcode, enumerator, ...) + SourceLoc(&'a str), // @00c7 } /// A `Token` with an associated location. @@ -388,6 +389,22 @@ impl<'a> Lexer<'a> { token(Token::HexSequence(&self.source[begin..end]), loc) } + fn scan_srcloc(&mut self) -> Result, LocatedError> { + let loc = self.loc(); + let begin = self.pos + 1; + + assert_eq!(self.lookahead, Some('@')); + + while let Some(c) = self.next_ch() { + if !char::is_digit(c, 16) { + break; + } + } + + let end = self.pos; + token(Token::SourceLoc(&self.source[begin..end]), loc) + } + /// Get the next token or a lexical error. /// /// Return None when the end of the source is encountered. @@ -419,6 +436,7 @@ impl<'a> Lexer<'a> { Some(ch) if ch.is_alphabetic() => Some(self.scan_word()), Some('%') => Some(self.scan_name()), Some('#') => Some(self.scan_hex_sequence()), + Some('@') => Some(self.scan_srcloc()), Some(ch) if ch.is_whitespace() => { self.next_ch(); continue; diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 331ca5eb92..be86302645 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -13,6 +13,7 @@ use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, CallConv, S JumpTable, JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef, FuncRef, StackSlot, ValueLoc, ArgumentLoc, MemFlags, GlobalVar, GlobalVarData, Heap, HeapData, HeapStyle, HeapBase}; +use cretonne::ir; use cretonne::ir::types::VOID; use cretonne::ir::immediates::{Imm64, Uimm32, Offset32, Ieee32, Ieee64}; use cretonne::ir::entities::AnyEntity; @@ -696,6 +697,23 @@ impl<'a> Parser<'a> { } } + /// Parse an optional source location. + /// + /// Return an optional source location if no real location is present. + fn optional_srcloc(&mut self) -> Result { + if let Some(Token::SourceLoc(text)) = self.token() { + match u32::from_str_radix(text, 16) { + Ok(num) => { + self.consume(); + Ok(ir::SourceLoc::new(num)) + } + Err(_) => return err!(self.loc, "invalid source location: {}", text), + } + } else { + Ok(Default::default()) + } + } + /// Parse a list of test commands. pub fn parse_test_commands(&mut self) -> Vec> { let mut list = Vec::new(); @@ -1360,9 +1378,11 @@ impl<'a> Parser<'a> { Some(Token::Value(_)) => true, Some(Token::Identifier(_)) => true, Some(Token::LBracket) => true, + Some(Token::SourceLoc(_)) => true, _ => false, } { + let srcloc = self.optional_srcloc()?; let (encoding, result_locations) = self.parse_instruction_encoding(ctx)?; // We need to parse instruction results here because they are shared @@ -1380,6 +1400,7 @@ impl<'a> Parser<'a> { self.consume(); self.parse_instruction( results, + srcloc, encoding, result_locations, ctx, @@ -1390,6 +1411,7 @@ impl<'a> Parser<'a> { _ => { self.parse_instruction( results, + srcloc, encoding, result_locations, ctx, @@ -1587,6 +1609,7 @@ impl<'a> Parser<'a> { fn parse_instruction( &mut self, results: Vec, + srcloc: ir::SourceLoc, encoding: Option, result_locations: Option>, ctx: &mut Context, @@ -1636,6 +1659,10 @@ impl<'a> Parser<'a> { "duplicate inst references created", ); + if !srcloc.is_default() { + ctx.function.srclocs[inst] = srcloc; + } + if let Some(encoding) = encoding { ctx.function.encodings[inst] = encoding; } diff --git a/misc/vim/syntax/cton.vim b/misc/vim/syntax/cton.vim index d345742154..3bc6a4a5da 100644 --- a/misc/vim/syntax/cton.vim +++ b/misc/vim/syntax/cton.vim @@ -25,6 +25,7 @@ syn match ctonName /%\w\+\>/ syn match ctonNumber /-\?\<[0-9_]\+\>/ syn match ctonNumber /-\?\<0x[0-9a-fA-F_]\+\(\.[0-9a-fA-F_]*\)\?\(p[+-]\?\d\+\)\?\>/ syn match ctonHexSeq /#\x\+\>/ +syn match ctonSourceLoc /@[0-9a-f]\+\>/ syn region ctonCommentLine start=";" end="$" contains=ctonFilecheck @@ -38,5 +39,6 @@ hi def link ctonNumber Number hi def link ctonHexSeq Number hi def link ctonCommentLine Comment hi def link ctonFilecheck SpecialComment +hi def link ctonSourceLoc LineNr let b:current_syntax = "cton"