MachInst backend: pass through SourceLoc information.

This change adds SourceLoc information per instruction in a `VCode<Inst>`
container, and keeps this information up-to-date across register allocation
and branch reordering. The information is initially collected during
instruction lowering, eventually collected on the MachSection, and finally
provided to the environment that wraps the codegen crate for wasmtime.
This commit is contained in:
Chris Fallin
2020-04-21 18:31:02 -07:00
parent 74eda8090c
commit b691770faa
7 changed files with 193 additions and 19 deletions

5
Cargo.lock generated
View File

@@ -1538,12 +1538,13 @@ dependencies = [
[[package]] [[package]]
name = "regalloc" name = "regalloc"
version = "0.0.17" version = "0.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ce0cd835fa6e91bbf5d010beee19d0c2e97e4ad5e13c399a31122cfc83bdd6" checksum = "c6d7df180ed313488abdbeec2e3398f16cbf5ea6ce20db968c8cd6fc410400a9"
dependencies = [ dependencies = [
"log", "log",
"rustc-hash", "rustc-hash",
"smallvec",
] ]
[[package]] [[package]]

View File

@@ -24,7 +24,7 @@ gimli = { version = "0.20.0", default-features = false, features = ["write"], op
smallvec = { version = "1.0.0" } smallvec = { version = "1.0.0" }
thiserror = "1.0.4" thiserror = "1.0.4"
byteorder = { version = "1.3.2", default-features = false } byteorder = { version = "1.3.2", default-features = false }
regalloc = "0.0.17" regalloc = "0.0.18"
# It is a goal of the cranelift-codegen crate to have minimal external dependencies. # It is a goal of the cranelift-codegen crate to have minimal external dependencies.
# Please don't add any unless they are essential to the task of creating binary # Please don't add any unless they are essential to the task of creating binary
# machine code. Integration tests that need external dependencies can be # machine code. Integration tests that need external dependencies can be

View File

@@ -345,6 +345,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
"lower_branch_group: targets = {:?} branches = {:?}", "lower_branch_group: targets = {:?} branches = {:?}",
targets, branches targets, branches
); );
self.vcode.set_srcloc(self.srcloc(branches[0]));
backend.lower_branch_group( backend.lower_branch_group(
&mut self, &mut self,
&branches[..], &branches[..],
@@ -361,6 +362,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
let num_uses = self.num_uses[inst]; let num_uses = self.num_uses[inst];
let side_effect = has_side_effect(self.f, inst); let side_effect = has_side_effect(self.f, inst);
if side_effect || num_uses > 0 { if side_effect || num_uses > 0 {
self.vcode.set_srcloc(self.srcloc(inst));
backend.lower(&mut self, inst); backend.lower(&mut self, inst);
self.vcode.end_ir_inst(); self.vcode.end_ir_inst();
} else { } else {
@@ -389,6 +391,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
"lower_branch_group: targets = {:?} branches = {:?}", "lower_branch_group: targets = {:?} branches = {:?}",
targets, branches targets, branches
); );
self.vcode.set_srcloc(self.srcloc(branches[0]));
backend.lower_branch_group(&mut self, &branches[..], &targets[..], fallthrough); backend.lower_branch_group(&mut self, &branches[..], &targets[..], fallthrough);
self.vcode.end_ir_inst(); self.vcode.end_ir_inst();
branches.clear(); branches.clear();

View File

@@ -97,7 +97,6 @@
//! ``` //! ```
use crate::binemit::{CodeInfo, CodeOffset}; use crate::binemit::{CodeInfo, CodeOffset};
use crate::entity::SecondaryMap;
use crate::ir::condcodes::IntCC; use crate::ir::condcodes::IntCC;
use crate::ir::{Function, Type}; use crate::ir::{Function, Type};
use crate::result::CodegenResult; use crate::result::CodegenResult;

View File

@@ -74,6 +74,11 @@ impl MachSections {
sink.end_codegen(); sink.end_codegen();
} }
/// Get a list of source location mapping tuples in sorted-by-start-offset order.
pub fn get_srclocs_sorted<'a>(&'a self) -> MachSectionsSrcLocs<'a> {
MachSectionsSrcLocs::new(&self.sections)
}
/// Get the total required size for these sections. /// Get the total required size for these sections.
pub fn total_size(&self) -> CodeOffset { pub fn total_size(&self) -> CodeOffset {
if self.sections.len() == 0 { if self.sections.len() == 0 {
@@ -90,6 +95,58 @@ impl MachSections {
} }
} }
/// An iterator over the srclocs in each section.
/// Returns MachSrcLocs in an order sorted by start location.
pub struct MachSectionsSrcLocs<'a> {
sections: &'a [MachSection],
cur_section: usize,
cur_srcloc: usize,
// For validation:
last_offset: CodeOffset,
}
impl<'a> MachSectionsSrcLocs<'a> {
fn new(sections: &'a [MachSection]) -> MachSectionsSrcLocs<'a> {
MachSectionsSrcLocs {
sections,
cur_section: 0,
cur_srcloc: 0,
last_offset: 0,
}
}
}
impl<'a> Iterator for MachSectionsSrcLocs<'a> {
type Item = &'a MachSrcLoc;
fn next(&mut self) -> Option<&'a MachSrcLoc> {
// We simply iterate through sections and srcloc records in order. This produces a
// sorted order naturally because sections are in starting-offset-order, and srclocs
// are produced as a section is emitted into, so are in order as well.
// If we're out of sections, we're done.
if self.cur_section >= self.sections.len() {
return None;
}
// Otherwise, make sure we have a srcloc in the current section left to return, and
// advance to the next section if not. Done if we run out of sections.
while self.cur_srcloc >= self.sections[self.cur_section].srclocs.len() {
self.cur_srcloc = 0;
self.cur_section += 1;
if self.cur_section >= self.sections.len() {
return None;
}
}
let loc = &self.sections[self.cur_section].srclocs[self.cur_srcloc];
self.cur_srcloc += 1;
debug_assert!(loc.start >= self.last_offset);
self.last_offset = loc.start;
Some(loc)
}
}
/// An abstraction over MachSection and MachSectionSize: some /// An abstraction over MachSection and MachSectionSize: some
/// receiver of section data. /// receiver of section data.
pub trait MachSectionOutput { pub trait MachSectionOutput {
@@ -143,6 +200,12 @@ pub trait MachSectionOutput {
/// Add a call return address record at the current offset. /// Add a call return address record at the current offset.
fn add_call_site(&mut self, loc: SourceLoc, opcode: Opcode); fn add_call_site(&mut self, loc: SourceLoc, opcode: Opcode);
/// Start the output for the given source-location at the current offset.
fn start_srcloc(&mut self, loc: SourceLoc);
/// End the output for the previously-given source-location at the current offset.
fn end_srcloc(&mut self);
/// Align up to the given alignment. /// Align up to the given alignment.
fn align_to(&mut self, align_to: CodeOffset) { fn align_to(&mut self, align_to: CodeOffset) {
assert!(align_to.is_power_of_two()); assert!(align_to.is_power_of_two());
@@ -168,8 +231,13 @@ pub struct MachSection {
pub relocs: Vec<MachReloc>, pub relocs: Vec<MachReloc>,
/// Any trap records referring to this section. /// Any trap records referring to this section.
pub traps: Vec<MachTrap>, pub traps: Vec<MachTrap>,
/// Any call site record referring to this section. /// Any call site records referring to this section.
pub call_sites: Vec<MachCallSite>, pub call_sites: Vec<MachCallSite>,
/// Any source location mappings referring to this section.
pub srclocs: Vec<MachSrcLoc>,
/// The current source location in progress (after `start_srcloc()` and before `end_srcloc()`).
/// This is a (start_offset, src_loc) tuple.
pub cur_srcloc: Option<(CodeOffset, SourceLoc)>,
} }
impl MachSection { impl MachSection {
@@ -182,6 +250,8 @@ impl MachSection {
relocs: vec![], relocs: vec![],
traps: vec![], traps: vec![],
call_sites: vec![], call_sites: vec![],
srclocs: vec![],
cur_srcloc: None,
} }
} }
@@ -266,6 +336,23 @@ impl MachSectionOutput for MachSection {
opcode, opcode,
}); });
} }
fn start_srcloc(&mut self, loc: SourceLoc) {
self.cur_srcloc = Some((self.cur_offset_from_start(), loc));
}
fn end_srcloc(&mut self) {
let (start, loc) = self
.cur_srcloc
.take()
.expect("end_srcloc() called without start_srcloc()");
let end = self.cur_offset_from_start();
// Skip zero-length extends.
debug_assert!(end >= start);
if end > start {
self.srclocs.push(MachSrcLoc { start, end, loc });
}
}
} }
/// A MachSectionOutput implementation that records only size. /// A MachSectionOutput implementation that records only size.
@@ -315,6 +402,10 @@ impl MachSectionOutput for MachSectionSize {
fn add_trap(&mut self, _: SourceLoc, _: TrapCode) {} fn add_trap(&mut self, _: SourceLoc, _: TrapCode) {}
fn add_call_site(&mut self, _: SourceLoc, _: Opcode) {} fn add_call_site(&mut self, _: SourceLoc, _: Opcode) {}
fn start_srcloc(&mut self, _: SourceLoc) {}
fn end_srcloc(&mut self) {}
} }
/// A relocation resulting from a compilation. /// A relocation resulting from a compilation.
@@ -352,3 +443,18 @@ pub struct MachCallSite {
/// The call's opcode. /// The call's opcode.
pub opcode: Opcode, pub opcode: Opcode,
} }
/// A source-location mapping resulting from a compilation.
#[derive(Clone, Debug)]
pub struct MachSrcLoc {
/// The start of the region of code corresponding to a source location.
/// This is relative to the start of the function, not to the start of the
/// section.
pub start: CodeOffset,
/// The end of the region of code corresponding to a source location.
/// This is relative to the start of the section, not to the start of the
/// section.
pub end: CodeOffset,
/// The source location.
pub loc: SourceLoc,
}

View File

@@ -17,7 +17,9 @@
//! See the main module comment in `mod.rs` for more details on the VCode-based //! See the main module comment in `mod.rs` for more details on the VCode-based
//! backend pipeline. //! backend pipeline.
use crate::entity::SecondaryMap;
use crate::ir; use crate::ir;
use crate::ir::SourceLoc;
use crate::machinst::*; use crate::machinst::*;
use crate::settings; use crate::settings;
@@ -59,6 +61,10 @@ pub struct VCode<I: VCodeInst> {
/// Lowered machine instructions in order corresponding to the original IR. /// Lowered machine instructions in order corresponding to the original IR.
insts: Vec<I>, insts: Vec<I>,
/// Source locations for each instruction. (`SourceLoc` is a `u32`, so it is
/// reasonable to keep one of these per instruction.)
srclocs: Vec<SourceLoc>,
/// Entry block. /// Entry block.
entry: BlockIndex, entry: BlockIndex,
@@ -115,13 +121,16 @@ pub struct VCodeBuilder<I: VCodeInst> {
/// Current basic block instructions, in reverse order (because blocks are /// Current basic block instructions, in reverse order (because blocks are
/// built bottom-to-top). /// built bottom-to-top).
bb_insns: SmallVec<[I; 32]>, bb_insns: SmallVec<[(I, SourceLoc); 32]>,
/// Current IR-inst instructions, in forward order. /// Current IR-inst instructions, in forward order.
ir_inst_insns: SmallVec<[I; 4]>, ir_inst_insns: SmallVec<[(I, SourceLoc); 4]>,
/// Start of succs for the current block in the concatenated succs list. /// Start of succs for the current block in the concatenated succs list.
succ_start: usize, succ_start: usize,
/// Current source location.
cur_srcloc: SourceLoc,
} }
impl<I: VCodeInst> VCodeBuilder<I> { impl<I: VCodeInst> VCodeBuilder<I> {
@@ -133,6 +142,7 @@ impl<I: VCodeInst> VCodeBuilder<I> {
bb_insns: SmallVec::new(), bb_insns: SmallVec::new(),
ir_inst_insns: SmallVec::new(), ir_inst_insns: SmallVec::new(),
succ_start: 0, succ_start: 0,
cur_srcloc: SourceLoc::default(),
} }
} }
@@ -179,8 +189,8 @@ impl<I: VCodeInst> VCodeBuilder<I> {
/// End the current IR instruction. Must be called after pushing any /// End the current IR instruction. Must be called after pushing any
/// instructions and prior to ending the basic block. /// instructions and prior to ending the basic block.
pub fn end_ir_inst(&mut self) { pub fn end_ir_inst(&mut self) {
while let Some(i) = self.ir_inst_insns.pop() { while let Some(pair) = self.ir_inst_insns.pop() {
self.bb_insns.push(i); self.bb_insns.push(pair);
} }
} }
@@ -191,8 +201,9 @@ impl<I: VCodeInst> VCodeBuilder<I> {
let block_num = self.vcode.block_ranges.len() as BlockIndex; let block_num = self.vcode.block_ranges.len() as BlockIndex;
// Push the instructions. // Push the instructions.
let start_idx = self.vcode.insts.len() as InsnIndex; let start_idx = self.vcode.insts.len() as InsnIndex;
while let Some(i) = self.bb_insns.pop() { while let Some((i, loc)) = self.bb_insns.pop() {
self.vcode.insts.push(i); self.vcode.insts.push(i);
self.vcode.srclocs.push(loc);
} }
let end_idx = self.vcode.insts.len() as InsnIndex; let end_idx = self.vcode.insts.len() as InsnIndex;
// Add the instruction index range to the list of blocks. // Add the instruction index range to the list of blocks.
@@ -224,7 +235,12 @@ impl<I: VCodeInst> VCodeBuilder<I> {
} }
} }
} }
self.ir_inst_insns.push(insn); self.ir_inst_insns.push((insn, self.cur_srcloc));
}
/// Set the current source location.
pub fn set_srcloc(&mut self, srcloc: SourceLoc) {
self.cur_srcloc = srcloc;
} }
/// Build the final VCode. /// Build the final VCode.
@@ -286,6 +302,7 @@ impl<I: VCodeInst> VCode<I> {
liveouts: abi.liveouts(), liveouts: abi.liveouts(),
vreg_types: vec![], vreg_types: vec![],
insts: vec![], insts: vec![],
srclocs: vec![],
entry: 0, entry: 0,
block_ranges: vec![], block_ranges: vec![],
block_succ_range: vec![], block_succ_range: vec![],
@@ -349,6 +366,7 @@ impl<I: VCodeInst> VCode<I> {
block_ranges(result.target_map.elems(), result.insns.len()); block_ranges(result.target_map.elems(), result.insns.len());
let mut final_insns = vec![]; let mut final_insns = vec![];
let mut final_block_ranges = vec![(0, 0); self.num_blocks()]; let mut final_block_ranges = vec![(0, 0); self.num_blocks()];
let mut final_srclocs = vec![];
for block in &self.final_block_order { for block in &self.final_block_order {
let (start, end) = block_ranges[*block as usize]; let (start, end) = block_ranges[*block as usize];
@@ -356,7 +374,10 @@ impl<I: VCodeInst> VCode<I> {
if *block == self.entry { if *block == self.entry {
// Start with the prologue. // Start with the prologue.
final_insns.extend(self.abi.gen_prologue().into_iter()); let prologue = self.abi.gen_prologue();
let len = prologue.len();
final_insns.extend(prologue.into_iter());
final_srclocs.extend(iter::repeat(SourceLoc::default()).take(len));
} }
for i in start..end { for i in start..end {
@@ -368,13 +389,27 @@ impl<I: VCodeInst> VCode<I> {
continue; continue;
} }
// Is there a srcloc associated with this insn? Look it up based on original
// instruction index (if new insn corresponds to some original insn, i.e., is not
// an inserted load/spill/move).
let orig_iix = result.orig_insn_map[InstIx::new(i as u32)];
let srcloc = if orig_iix.is_invalid() {
SourceLoc::default()
} else {
self.srclocs[orig_iix.get() as usize]
};
// Whenever encountering a return instruction, replace it // Whenever encountering a return instruction, replace it
// with the epilogue. // with the epilogue.
let is_ret = insn.is_term() == MachTerminator::Ret; let is_ret = insn.is_term() == MachTerminator::Ret;
if is_ret { if is_ret {
final_insns.extend(self.abi.gen_epilogue().into_iter()); let epilogue = self.abi.gen_epilogue();
let len = epilogue.len();
final_insns.extend(epilogue.into_iter());
final_srclocs.extend(iter::repeat(srcloc).take(len));
} else { } else {
final_insns.push(insn.clone()); final_insns.push(insn.clone());
final_srclocs.push(srcloc);
} }
} }
@@ -382,7 +417,10 @@ impl<I: VCodeInst> VCode<I> {
final_block_ranges[*block as usize] = (final_start, final_end); final_block_ranges[*block as usize] = (final_start, final_end);
} }
debug_assert!(final_insns.len() == final_srclocs.len());
self.insts = final_insns; self.insts = final_insns;
self.srclocs = final_srclocs;
self.block_ranges = final_block_ranges; self.block_ranges = final_block_ranges;
} }
@@ -512,6 +550,7 @@ impl<I: VCodeInst> VCode<I> {
let code_section = sections.get_section(code_idx); let code_section = sections.get_section(code_idx);
let flags = self.abi.flags(); let flags = self.abi.flags();
let mut cur_srcloc = SourceLoc::default();
for &block in &self.final_block_order { for &block in &self.final_block_order {
let new_offset = I::align_basic_block(code_section.cur_offset_from_start()); let new_offset = I::align_basic_block(code_section.cur_offset_from_start());
while new_offset > code_section.cur_offset_from_start() { while new_offset > code_section.cur_offset_from_start() {
@@ -523,8 +562,24 @@ impl<I: VCodeInst> VCode<I> {
let (start, end) = self.block_ranges[block as usize]; let (start, end) = self.block_ranges[block as usize];
for iix in start..end { for iix in start..end {
let srcloc = self.srclocs[iix as usize];
if srcloc != cur_srcloc {
if !cur_srcloc.is_default() {
code_section.end_srcloc();
}
if !srcloc.is_default() {
code_section.start_srcloc(srcloc);
}
cur_srcloc = srcloc;
}
self.insts[iix as usize].emit(code_section, flags); self.insts[iix as usize].emit(code_section, flags);
} }
if !cur_srcloc.is_default() {
code_section.end_srcloc();
cur_srcloc = SourceLoc::default();
}
} }
sections sections

View File

@@ -93,6 +93,7 @@ use crate::compilation::{
use crate::func_environ::{get_func_name, FuncEnvironment}; use crate::func_environ::{get_func_name, FuncEnvironment};
use crate::{CacheConfig, FunctionBodyData, ModuleLocal, ModuleTranslation, Tunables}; use crate::{CacheConfig, FunctionBodyData, ModuleLocal, ModuleTranslation, Tunables};
use cranelift_codegen::ir::{self, ExternalName}; use cranelift_codegen::ir::{self, ExternalName};
use cranelift_codegen::machinst::sections::MachSrcLoc;
use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::{binemit, isa, Context}; use cranelift_codegen::{binemit, isa, Context};
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
@@ -207,13 +208,22 @@ fn get_function_address_map<'data>(
) -> FunctionAddressMap { ) -> FunctionAddressMap {
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let func = &context.func; if let Some(ref mcr) = &context.mach_compile_result {
let mut blocks = func.layout.blocks().collect::<Vec<_>>(); // New-style backend: we have a `MachCompileResult` that will give us `MachSrcLoc` mapping
blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase // tuples.
for &MachSrcLoc { start, end, loc } in mcr.sections.get_srclocs_sorted() {
instructions.push(InstructionAddressMap {
srcloc: loc,
code_offset: start as usize,
code_len: (end - start) as usize,
});
}
} else {
// Old-style backend: we need to traverse the instruction/encoding info in the function.
let func = &context.func;
let mut blocks = func.layout.blocks().collect::<Vec<_>>();
blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase
// FIXME(#1523): New backend does not support debug info or instruction-address mapping
// yet.
if !isa.get_mach_backend().is_some() {
let encinfo = isa.encoding_info(); let encinfo = isa.encoding_info();
for block in blocks { for block in blocks {
for (offset, inst, size) in func.inst_offsets(block, &encinfo) { for (offset, inst, size) in func.inst_offsets(block, &encinfo) {