diff --git a/Cargo.lock b/Cargo.lock index 50d06317fe..6881d48021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1538,12 +1538,13 @@ dependencies = [ [[package]] name = "regalloc" -version = "0.0.17" +version = "0.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ce0cd835fa6e91bbf5d010beee19d0c2e97e4ad5e13c399a31122cfc83bdd6" +checksum = "c6d7df180ed313488abdbeec2e3398f16cbf5ea6ce20db968c8cd6fc410400a9" dependencies = [ "log", "rustc-hash", + "smallvec", ] [[package]] diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index 8bf10759c4..8986e85395 100644 --- a/cranelift/codegen/Cargo.toml +++ b/cranelift/codegen/Cargo.toml @@ -24,7 +24,7 @@ gimli = { version = "0.20.0", default-features = false, features = ["write"], op smallvec = { version = "1.0.0" } thiserror = "1.0.4" 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. # 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 diff --git a/cranelift/codegen/src/machinst/lower.rs b/cranelift/codegen/src/machinst/lower.rs index a2686d523c..5397ab0361 100644 --- a/cranelift/codegen/src/machinst/lower.rs +++ b/cranelift/codegen/src/machinst/lower.rs @@ -345,6 +345,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { "lower_branch_group: targets = {:?} branches = {:?}", targets, branches ); + self.vcode.set_srcloc(self.srcloc(branches[0])); backend.lower_branch_group( &mut self, &branches[..], @@ -361,6 +362,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { let num_uses = self.num_uses[inst]; let side_effect = has_side_effect(self.f, inst); if side_effect || num_uses > 0 { + self.vcode.set_srcloc(self.srcloc(inst)); backend.lower(&mut self, inst); self.vcode.end_ir_inst(); } else { @@ -389,6 +391,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> { "lower_branch_group: targets = {:?} branches = {:?}", targets, branches ); + self.vcode.set_srcloc(self.srcloc(branches[0])); backend.lower_branch_group(&mut self, &branches[..], &targets[..], fallthrough); self.vcode.end_ir_inst(); branches.clear(); diff --git a/cranelift/codegen/src/machinst/mod.rs b/cranelift/codegen/src/machinst/mod.rs index 8b3031345c..554b661300 100644 --- a/cranelift/codegen/src/machinst/mod.rs +++ b/cranelift/codegen/src/machinst/mod.rs @@ -97,7 +97,6 @@ //! ``` use crate::binemit::{CodeInfo, CodeOffset}; -use crate::entity::SecondaryMap; use crate::ir::condcodes::IntCC; use crate::ir::{Function, Type}; use crate::result::CodegenResult; diff --git a/cranelift/codegen/src/machinst/sections.rs b/cranelift/codegen/src/machinst/sections.rs index 247adf5cef..0bd97dcdb6 100644 --- a/cranelift/codegen/src/machinst/sections.rs +++ b/cranelift/codegen/src/machinst/sections.rs @@ -74,6 +74,11 @@ impl MachSections { 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. pub fn total_size(&self) -> CodeOffset { 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 /// receiver of section data. pub trait MachSectionOutput { @@ -143,6 +200,12 @@ pub trait MachSectionOutput { /// Add a call return address record at the current offset. 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. fn align_to(&mut self, align_to: CodeOffset) { assert!(align_to.is_power_of_two()); @@ -168,8 +231,13 @@ pub struct MachSection { pub relocs: Vec, /// Any trap records referring to this section. pub traps: Vec, - /// Any call site record referring to this section. + /// Any call site records referring to this section. pub call_sites: Vec, + /// Any source location mappings referring to this section. + pub srclocs: Vec, + /// 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 { @@ -182,6 +250,8 @@ impl MachSection { relocs: vec![], traps: vec![], call_sites: vec![], + srclocs: vec![], + cur_srcloc: None, } } @@ -266,6 +336,23 @@ impl MachSectionOutput for MachSection { 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. @@ -315,6 +402,10 @@ impl MachSectionOutput for MachSectionSize { fn add_trap(&mut self, _: SourceLoc, _: TrapCode) {} 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. @@ -352,3 +443,18 @@ pub struct MachCallSite { /// The call's 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, +} diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs index 2ff82072f0..6b77411d3c 100644 --- a/cranelift/codegen/src/machinst/vcode.rs +++ b/cranelift/codegen/src/machinst/vcode.rs @@ -17,7 +17,9 @@ //! See the main module comment in `mod.rs` for more details on the VCode-based //! backend pipeline. +use crate::entity::SecondaryMap; use crate::ir; +use crate::ir::SourceLoc; use crate::machinst::*; use crate::settings; @@ -59,6 +61,10 @@ pub struct VCode { /// Lowered machine instructions in order corresponding to the original IR. insts: Vec, + /// Source locations for each instruction. (`SourceLoc` is a `u32`, so it is + /// reasonable to keep one of these per instruction.) + srclocs: Vec, + /// Entry block. entry: BlockIndex, @@ -115,13 +121,16 @@ pub struct VCodeBuilder { /// Current basic block instructions, in reverse order (because blocks are /// built bottom-to-top). - bb_insns: SmallVec<[I; 32]>, + bb_insns: SmallVec<[(I, SourceLoc); 32]>, /// 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. succ_start: usize, + + /// Current source location. + cur_srcloc: SourceLoc, } impl VCodeBuilder { @@ -133,6 +142,7 @@ impl VCodeBuilder { bb_insns: SmallVec::new(), ir_inst_insns: SmallVec::new(), succ_start: 0, + cur_srcloc: SourceLoc::default(), } } @@ -179,8 +189,8 @@ impl VCodeBuilder { /// End the current IR instruction. Must be called after pushing any /// instructions and prior to ending the basic block. pub fn end_ir_inst(&mut self) { - while let Some(i) = self.ir_inst_insns.pop() { - self.bb_insns.push(i); + while let Some(pair) = self.ir_inst_insns.pop() { + self.bb_insns.push(pair); } } @@ -191,8 +201,9 @@ impl VCodeBuilder { let block_num = self.vcode.block_ranges.len() as BlockIndex; // Push the instructions. 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.srclocs.push(loc); } let end_idx = self.vcode.insts.len() as InsnIndex; // Add the instruction index range to the list of blocks. @@ -224,7 +235,12 @@ impl VCodeBuilder { } } } - 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. @@ -286,6 +302,7 @@ impl VCode { liveouts: abi.liveouts(), vreg_types: vec![], insts: vec![], + srclocs: vec![], entry: 0, block_ranges: vec![], block_succ_range: vec![], @@ -349,6 +366,7 @@ impl VCode { block_ranges(result.target_map.elems(), result.insns.len()); let mut final_insns = vec![]; let mut final_block_ranges = vec![(0, 0); self.num_blocks()]; + let mut final_srclocs = vec![]; for block in &self.final_block_order { let (start, end) = block_ranges[*block as usize]; @@ -356,7 +374,10 @@ impl VCode { if *block == self.entry { // 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 { @@ -368,13 +389,27 @@ impl VCode { 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 // with the epilogue. let is_ret = insn.is_term() == MachTerminator::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 { final_insns.push(insn.clone()); + final_srclocs.push(srcloc); } } @@ -382,7 +417,10 @@ impl VCode { final_block_ranges[*block as usize] = (final_start, final_end); } + debug_assert!(final_insns.len() == final_srclocs.len()); + self.insts = final_insns; + self.srclocs = final_srclocs; self.block_ranges = final_block_ranges; } @@ -512,6 +550,7 @@ impl VCode { let code_section = sections.get_section(code_idx); let flags = self.abi.flags(); + let mut cur_srcloc = SourceLoc::default(); for &block in &self.final_block_order { let new_offset = I::align_basic_block(code_section.cur_offset_from_start()); while new_offset > code_section.cur_offset_from_start() { @@ -523,8 +562,24 @@ impl VCode { let (start, end) = self.block_ranges[block as usize]; 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); } + + if !cur_srcloc.is_default() { + code_section.end_srcloc(); + cur_srcloc = SourceLoc::default(); + } } sections diff --git a/crates/environ/src/cranelift.rs b/crates/environ/src/cranelift.rs index 62ea3d85c3..8c9d64210f 100644 --- a/crates/environ/src/cranelift.rs +++ b/crates/environ/src/cranelift.rs @@ -93,6 +93,7 @@ use crate::compilation::{ use crate::func_environ::{get_func_name, FuncEnvironment}; use crate::{CacheConfig, FunctionBodyData, ModuleLocal, ModuleTranslation, Tunables}; use cranelift_codegen::ir::{self, ExternalName}; +use cranelift_codegen::machinst::sections::MachSrcLoc; use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::{binemit, isa, Context}; use cranelift_entity::PrimaryMap; @@ -207,13 +208,22 @@ fn get_function_address_map<'data>( ) -> FunctionAddressMap { let mut instructions = Vec::new(); - let func = &context.func; - let mut blocks = func.layout.blocks().collect::>(); - blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase + if let Some(ref mcr) = &context.mach_compile_result { + // New-style backend: we have a `MachCompileResult` that will give us `MachSrcLoc` mapping + // 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::>(); + 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(); for block in blocks { for (offset, inst, size) in func.inst_offsets(block, &encinfo) {