diff --git a/src/base.rs b/src/base.rs index 705629888..243852f1d 100644 --- a/src/base.rs +++ b/src/base.rs @@ -17,10 +17,11 @@ pub fn trans_fn<'clif, 'tcx, B: Backend + 'static>( let mut debug_context = cx .debug_context .as_mut() - .map(|debug_context| FunctionDebugContext::new(tcx, debug_context, mir, func_id, &name, &sig)); + .map(|debug_context| FunctionDebugContext::new(debug_context, instance, func_id, &name)); // Make FunctionBuilder let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig); + func.collect_debug_info(); let mut func_ctx = FunctionBuilderContext::new(); let mut bcx = FunctionBuilder::new(&mut func, &mut func_ctx); @@ -60,6 +61,7 @@ pub fn trans_fn<'clif, 'tcx, B: Backend + 'static>( let instance = fx.instance; let clif_comments = fx.clif_comments; let source_info_set = fx.source_info_set; + let local_map = fx.local_map; #[cfg(debug_assertions)] crate::pretty_clif::write_clif_file(cx.tcx, "unopt", instance, &func, &clif_comments, None); @@ -72,26 +74,28 @@ pub fn trans_fn<'clif, 'tcx, B: Backend + 'static>( context.func = func; cx.module.define_function(func_id, context).unwrap(); - let value_ranges = context - .build_value_labels_ranges(cx.module.isa()) - .expect("value location ranges"); - // Write optimized function to file for debugging #[cfg(debug_assertions)] - crate::pretty_clif::write_clif_file( - cx.tcx, - "opt", - instance, - &context.func, - &clif_comments, - Some(&value_ranges), - ); + { + let value_ranges = context + .build_value_labels_ranges(cx.module.isa()) + .expect("value location ranges"); + + crate::pretty_clif::write_clif_file( + cx.tcx, + "opt", + instance, + &context.func, + &clif_comments, + Some(&value_ranges), + ); + } // Define debuginfo for function let isa = cx.module.isa(); debug_context .as_mut() - .map(|x| x.define(tcx, context, isa, &source_info_set)); + .map(|x| x.define(context, isa, &source_info_set, local_map)); // Clear context to make it usable for the next function context.clear(); diff --git a/src/debuginfo.rs b/src/debuginfo.rs deleted file mode 100644 index e6d133c64..000000000 --- a/src/debuginfo.rs +++ /dev/null @@ -1,399 +0,0 @@ -use crate::prelude::*; - -use crate::backend::WriteDebugInfo; - -use std::marker::PhantomData; - -use syntax::source_map::FileName; - -use gimli::write::{ - Address, AttributeValue, DwarfUnit, EndianVec, FileId, LineProgram, LineString, - LineStringTable, Range, RangeList, Result, Sections, UnitEntryId, Writer, -}; -use gimli::{Encoding, Format, LineEncoding, RunTimeEndian, SectionId}; - -fn target_endian(tcx: TyCtxt) -> RunTimeEndian { - use rustc::ty::layout::Endian; - - match tcx.data_layout.endian { - Endian::Big => RunTimeEndian::Big, - Endian::Little => RunTimeEndian::Little, - } -} - -fn line_program_add_file( - line_program: &mut LineProgram, - line_strings: &mut LineStringTable, - file: &FileName, -) -> FileId { - match file { - FileName::Real(path) => { - let dir_name = path.parent().unwrap().to_str().unwrap().as_bytes(); - let dir_id = if !dir_name.is_empty() { - let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings); - line_program.add_directory(dir_name) - } else { - line_program.default_directory() - }; - let file_name = LineString::new( - path.file_name().unwrap().to_str().unwrap().as_bytes(), - line_program.encoding(), - line_strings, - ); - line_program.add_file(file_name, dir_id, None) - } - // FIXME give more appropriate file names - _ => { - let dir_id = line_program.default_directory(); - let dummy_file_name = LineString::new( - file.to_string().into_bytes(), - line_program.encoding(), - line_strings, - ); - line_program.add_file(dummy_file_name, dir_id, None) - } - } -} - -#[derive(Clone)] -pub struct DebugReloc { - pub offset: u32, - pub size: u8, - pub name: DebugRelocName, - pub addend: i64, -} - -#[derive(Clone)] -pub enum DebugRelocName { - Section(SectionId), - Symbol(usize), -} - -pub struct DebugContext<'tcx> { - endian: RunTimeEndian, - symbols: indexmap::IndexMap, - - dwarf: DwarfUnit, - unit_range_list: RangeList, - - _dummy: PhantomData<&'tcx ()>, -} - -impl<'tcx> DebugContext<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, address_size: u8) -> Self { - let encoding = Encoding { - format: Format::Dwarf32, - // TODO: this should be configurable - // macOS doesn't seem to support DWARF > 3 - version: 3, - address_size, - }; - - let mut dwarf = DwarfUnit::new(encoding); - - // FIXME: how to get version when building out of tree? - // Normally this would use option_env!("CFG_VERSION"). - let producer = format!("cranelift fn (rustc version {})", "unknown version"); - let comp_dir = tcx.sess.working_dir.0.to_string_lossy().into_owned(); - let name = match tcx.sess.local_crate_source_file { - Some(ref path) => path.to_string_lossy().into_owned(), - None => tcx.crate_name(LOCAL_CRATE).to_string(), - }; - - let line_program = LineProgram::new( - encoding, - LineEncoding::default(), - LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings), - LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings), - None, - ); - dwarf.unit.line_program = line_program; - - { - let name = dwarf.strings.add(&*name); - let comp_dir = dwarf.strings.add(&*comp_dir); - - let root = dwarf.unit.root(); - let root = dwarf.unit.get_mut(root); - root.set( - gimli::DW_AT_producer, - AttributeValue::StringRef(dwarf.strings.add(producer)), - ); - root.set( - gimli::DW_AT_language, - AttributeValue::Language(gimli::DW_LANG_Rust), - ); - root.set(gimli::DW_AT_name, AttributeValue::StringRef(name)); - root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir)); - root.set( - gimli::DW_AT_low_pc, - AttributeValue::Address(Address::Constant(0)), - ); - } - - DebugContext { - endian: target_endian(tcx), - symbols: indexmap::IndexMap::new(), - - dwarf, - unit_range_list: RangeList(Vec::new()), - - _dummy: PhantomData, - } - } - - fn emit_location(&mut self, tcx: TyCtxt<'tcx>, entry_id: UnitEntryId, span: Span) { - let loc = tcx.sess.source_map().lookup_char_pos(span.lo()); - - let file_id = line_program_add_file( - &mut self.dwarf.unit.line_program, - &mut self.dwarf.line_strings, - &loc.file.name, - ); - - let entry = self.dwarf.unit.get_mut(entry_id); - - entry.set( - gimli::DW_AT_decl_file, - AttributeValue::FileIndex(Some(file_id)), - ); - entry.set( - gimli::DW_AT_decl_line, - AttributeValue::Udata(loc.line as u64), - ); - // FIXME: probably omit this - entry.set( - gimli::DW_AT_decl_column, - AttributeValue::Udata(loc.col.to_usize() as u64), - ); - } - - pub fn emit(&mut self, product: &mut P) { - let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone()); - let root = self.dwarf.unit.root(); - let root = self.dwarf.unit.get_mut(root); - root.set( - gimli::DW_AT_ranges, - AttributeValue::RangeListRef(unit_range_list_id), - ); - - let mut sections = Sections::new(WriterRelocate::new(self)); - self.dwarf.write(&mut sections).unwrap(); - - let mut section_map = HashMap::new(); - let _: Result<()> = sections.for_each_mut(|id, section| { - if !section.writer.slice().is_empty() { - let section_id = product.add_debug_section(id, section.writer.take()); - section_map.insert(id, section_id); - } - Ok(()) - }); - - let _: Result<()> = sections.for_each(|id, section| { - if let Some(section_id) = section_map.get(&id) { - for reloc in §ion.relocs { - product.add_debug_reloc(§ion_map, &self.symbols, section_id, reloc); - } - } - Ok(()) - }); - } -} - -pub struct FunctionDebugContext<'a, 'tcx> { - debug_context: &'a mut DebugContext<'tcx>, - entry_id: UnitEntryId, - symbol: usize, - mir_span: Span, -} - -impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - debug_context: &'a mut DebugContext<'tcx>, - mir: &Body, - func_id: FuncId, - name: &str, - _sig: &Signature, - ) -> Self { - let (symbol, _) = debug_context.symbols.insert_full(func_id, name.to_string()); - - // FIXME: add to appropriate scope intead of root - let scope = debug_context.dwarf.unit.root(); - - let entry_id = debug_context - .dwarf - .unit - .add(scope, gimli::DW_TAG_subprogram); - let entry = debug_context.dwarf.unit.get_mut(entry_id); - let name_id = debug_context.dwarf.strings.add(name); - entry.set( - gimli::DW_AT_linkage_name, - AttributeValue::StringRef(name_id), - ); - - entry.set( - gimli::DW_AT_low_pc, - AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), - ); - - debug_context.emit_location(tcx, entry_id, mir.span); - - FunctionDebugContext { - debug_context, - entry_id, - symbol, - mir_span: mir.span, - } - } - - pub fn define( - &mut self, - tcx: TyCtxt, - context: &Context, - isa: &dyn cranelift::codegen::isa::TargetIsa, - source_info_set: &indexmap::IndexSet<(Span, mir::SourceScope)>, - ) { - let line_program = &mut self.debug_context.dwarf.unit.line_program; - - line_program.begin_sequence(Some(Address::Symbol { - symbol: self.symbol, - addend: 0, - })); - - let encinfo = isa.encoding_info(); - let func = &context.func; - let mut ebbs = func.layout.ebbs().collect::>(); - ebbs.sort_by_key(|ebb| func.offsets[*ebb]); // Ensure inst offsets always increase - - let line_strings = &mut self.debug_context.dwarf.line_strings; - let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| { - let loc = tcx.sess.source_map().lookup_char_pos(span.lo()); - let file_id = line_program_add_file(line_program, line_strings, &loc.file.name); - - /*println!( - "srcloc {:>04X} {}:{}:{}", - line_program.row().address_offset, - file.display(), - loc.line, - loc.col.to_u32() - );*/ - - line_program.row().file = file_id; - line_program.row().line = loc.line as u64; - line_program.row().column = loc.col.to_u32() as u64 + 1; - line_program.generate_row(); - }; - - let mut end = 0; - for ebb in ebbs { - for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) { - let srcloc = func.srclocs[inst]; - line_program.row().address_offset = offset as u64; - if !srcloc.is_default() { - let source_info = *source_info_set.get_index(srcloc.bits() as usize).unwrap(); - create_row_for_span(line_program, source_info.0); - } else { - create_row_for_span(line_program, self.mir_span); - } - end = offset + size; - } - } - - line_program.end_sequence(end as u64); - - let entry = self.debug_context.dwarf.unit.get_mut(self.entry_id); - entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(end as u64)); - - self.debug_context - .unit_range_list - .0 - .push(Range::StartLength { - begin: Address::Symbol { - symbol: self.symbol, - addend: 0, - }, - length: end as u64, - }); - } -} - -#[derive(Clone)] -struct WriterRelocate { - relocs: Vec, - writer: EndianVec, -} - -impl WriterRelocate { - fn new(ctx: &DebugContext) -> Self { - WriterRelocate { - relocs: Vec::new(), - writer: EndianVec::new(ctx.endian), - } - } -} - -impl Writer for WriterRelocate { - type Endian = RunTimeEndian; - - fn endian(&self) -> Self::Endian { - self.writer.endian() - } - - fn len(&self) -> usize { - self.writer.len() - } - - fn write(&mut self, bytes: &[u8]) -> Result<()> { - self.writer.write(bytes) - } - - fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { - self.writer.write_at(offset, bytes) - } - - fn write_address(&mut self, address: Address, size: u8) -> Result<()> { - match address { - Address::Constant(val) => self.write_udata(val, size), - Address::Symbol { symbol, addend } => { - let offset = self.len() as u64; - self.relocs.push(DebugReloc { - offset: offset as u32, - size, - name: DebugRelocName::Symbol(symbol), - addend: addend as i64, - }); - self.write_udata(0, size) - } - } - } - - // TODO: implement write_eh_pointer - - fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { - let offset = self.len() as u32; - self.relocs.push(DebugReloc { - offset, - size, - name: DebugRelocName::Section(section), - addend: val as i64, - }); - self.write_udata(0, size) - } - - fn write_offset_at( - &mut self, - offset: usize, - val: usize, - section: SectionId, - size: u8, - ) -> Result<()> { - self.relocs.push(DebugReloc { - offset: offset as u32, - size, - name: DebugRelocName::Section(section), - addend: val as i64, - }); - self.write_udata_at(offset, 0, size) - } -} diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs new file mode 100644 index 000000000..2b6dc9f66 --- /dev/null +++ b/src/debuginfo/emit.rs @@ -0,0 +1,135 @@ +use std::collections::HashMap; + +use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer}; +use gimli::{RunTimeEndian, SectionId}; + +use crate::backend::WriteDebugInfo; + +use super::DebugContext; + +impl DebugContext<'_> { + pub fn emit(&mut self, product: &mut P) { + let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone()); + let root = self.dwarf.unit.root(); + let root = self.dwarf.unit.get_mut(root); + root.set( + gimli::DW_AT_ranges, + AttributeValue::RangeListRef(unit_range_list_id), + ); + + let mut sections = Sections::new(WriterRelocate::new(self)); + self.dwarf.write(&mut sections).unwrap(); + + let mut section_map = HashMap::new(); + let _: Result<()> = sections.for_each_mut(|id, section| { + if !section.writer.slice().is_empty() { + let section_id = product.add_debug_section(id, section.writer.take()); + section_map.insert(id, section_id); + } + Ok(()) + }); + + let _: Result<()> = sections.for_each(|id, section| { + if let Some(section_id) = section_map.get(&id) { + for reloc in §ion.relocs { + product.add_debug_reloc(§ion_map, &self.symbols, section_id, reloc); + } + } + Ok(()) + }); + } +} + +#[derive(Clone)] +pub struct DebugReloc { + pub offset: u32, + pub size: u8, + pub name: DebugRelocName, + pub addend: i64, +} + +#[derive(Clone)] +pub enum DebugRelocName { + Section(SectionId), + Symbol(usize), +} + +#[derive(Clone)] +struct WriterRelocate { + relocs: Vec, + writer: EndianVec, +} + +impl WriterRelocate { + fn new(ctx: &DebugContext) -> Self { + WriterRelocate { + relocs: Vec::new(), + writer: EndianVec::new(ctx.endian), + } + } +} + +impl Writer for WriterRelocate { + type Endian = RunTimeEndian; + + fn endian(&self) -> Self::Endian { + self.writer.endian() + } + + fn len(&self) -> usize { + self.writer.len() + } + + fn write(&mut self, bytes: &[u8]) -> Result<()> { + self.writer.write(bytes) + } + + fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { + self.writer.write_at(offset, bytes) + } + + fn write_address(&mut self, address: Address, size: u8) -> Result<()> { + match address { + Address::Constant(val) => self.write_udata(val, size), + Address::Symbol { symbol, addend } => { + let offset = self.len() as u64; + self.relocs.push(DebugReloc { + offset: offset as u32, + size, + name: DebugRelocName::Symbol(symbol), + addend: addend as i64, + }); + self.write_udata(0, size) + } + } + } + + // TODO: implement write_eh_pointer + + fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { + let offset = self.len() as u32; + self.relocs.push(DebugReloc { + offset, + size, + name: DebugRelocName::Section(section), + addend: val as i64, + }); + self.write_udata(0, size) + } + + fn write_offset_at( + &mut self, + offset: usize, + val: usize, + section: SectionId, + size: u8, + ) -> Result<()> { + self.relocs.push(DebugReloc { + offset: offset as u32, + size, + name: DebugRelocName::Section(section), + addend: val as i64, + }); + self.write_udata_at(offset, 0, size) + } +} diff --git a/src/debuginfo/line_info.rs b/src/debuginfo/line_info.rs new file mode 100644 index 000000000..e64344bfe --- /dev/null +++ b/src/debuginfo/line_info.rs @@ -0,0 +1,145 @@ +use crate::prelude::*; + +use syntax::source_map::FileName; + +use cranelift::codegen::binemit::CodeOffset; + +use gimli::write::{ + Address, AttributeValue, FileId, LineProgram, LineString, LineStringTable, UnitEntryId, +}; + +fn line_program_add_file( + line_program: &mut LineProgram, + line_strings: &mut LineStringTable, + file: &FileName, +) -> FileId { + match file { + FileName::Real(path) => { + let dir_name = path.parent().unwrap().to_str().unwrap().as_bytes(); + let dir_id = if !dir_name.is_empty() { + let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings); + line_program.add_directory(dir_name) + } else { + line_program.default_directory() + }; + let file_name = LineString::new( + path.file_name().unwrap().to_str().unwrap().as_bytes(), + line_program.encoding(), + line_strings, + ); + line_program.add_file(file_name, dir_id, None) + } + // FIXME give more appropriate file names + _ => { + let dir_id = line_program.default_directory(); + let dummy_file_name = LineString::new( + file.to_string().into_bytes(), + line_program.encoding(), + line_strings, + ); + line_program.add_file(dummy_file_name, dir_id, None) + } + } +} + +impl<'tcx> DebugContext<'tcx> { + pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) { + let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo()); + + let file_id = line_program_add_file( + &mut self.dwarf.unit.line_program, + &mut self.dwarf.line_strings, + &loc.file.name, + ); + + let entry = self.dwarf.unit.get_mut(entry_id); + + entry.set( + gimli::DW_AT_decl_file, + AttributeValue::FileIndex(Some(file_id)), + ); + entry.set( + gimli::DW_AT_decl_line, + AttributeValue::Udata(loc.line as u64), + ); + // FIXME: probably omit this + entry.set( + gimli::DW_AT_decl_column, + AttributeValue::Udata(loc.col.to_usize() as u64), + ); + } +} + +impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> { + pub(crate) fn create_debug_lines( + &mut self, + context: &Context, + isa: &dyn cranelift::codegen::isa::TargetIsa, + source_info_set: &indexmap::IndexSet<(Span, mir::SourceScope)>, + ) -> CodeOffset { + let tcx = self.debug_context.tcx; + + let line_program = &mut self.debug_context.dwarf.unit.line_program; + + line_program.begin_sequence(Some(Address::Symbol { + symbol: self.symbol, + addend: 0, + })); + + let encinfo = isa.encoding_info(); + let func = &context.func; + let mut ebbs = func.layout.ebbs().collect::>(); + ebbs.sort_by_key(|ebb| func.offsets[*ebb]); // Ensure inst offsets always increase + + let line_strings = &mut self.debug_context.dwarf.line_strings; + let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| { + let loc = tcx.sess.source_map().lookup_char_pos(span.lo()); + let file_id = line_program_add_file(line_program, line_strings, &loc.file.name); + + /*println!( + "srcloc {:>04X} {}:{}:{}", + line_program.row().address_offset, + file.display(), + loc.line, + loc.col.to_u32() + );*/ + + line_program.row().file = file_id; + line_program.row().line = loc.line as u64; + line_program.row().column = loc.col.to_u32() as u64 + 1; + line_program.generate_row(); + }; + + let mut end = 0; + for ebb in ebbs { + for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) { + let srcloc = func.srclocs[inst]; + line_program.row().address_offset = offset as u64; + if !srcloc.is_default() { + let source_info = *source_info_set.get_index(srcloc.bits() as usize).unwrap(); + create_row_for_span(line_program, source_info.0); + } else { + create_row_for_span(line_program, self.mir.span); + } + end = offset + size; + } + } + + line_program.end_sequence(end as u64); + + let entry = self.debug_context.dwarf.unit.get_mut(self.entry_id); + entry.set( + gimli::DW_AT_low_pc, + AttributeValue::Address(Address::Symbol { + symbol: self.symbol, + addend: 0, + }), + ); + entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(end as u64)); + + self.debug_context + .emit_location(self.entry_id, self.mir.span); + + end + } +} diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs new file mode 100644 index 000000000..bb3f26f9f --- /dev/null +++ b/src/debuginfo/mod.rs @@ -0,0 +1,434 @@ +mod emit; +mod line_info; + +use crate::prelude::*; + +use cranelift::codegen::ir::{StackSlots, ValueLabel, ValueLoc}; +use cranelift::codegen::isa::RegUnit; +use cranelift::codegen::ValueLocRange; + +use gimli::write::{ + self, Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location, + LocationList, Range, RangeList, UnitEntryId, Writer, +}; +use gimli::{Encoding, Format, LineEncoding, Register, RunTimeEndian, X86_64}; + +pub use emit::{DebugReloc, DebugRelocName}; + +fn target_endian(tcx: TyCtxt) -> RunTimeEndian { + use rustc::ty::layout::Endian; + + match tcx.data_layout.endian { + Endian::Big => RunTimeEndian::Big, + Endian::Little => RunTimeEndian::Little, + } +} + +pub struct DebugContext<'tcx> { + tcx: TyCtxt<'tcx>, + + endian: RunTimeEndian, + symbols: indexmap::IndexMap, + + dwarf: DwarfUnit, + unit_range_list: RangeList, + + types: HashMap, UnitEntryId>, +} + +impl<'tcx> DebugContext<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, address_size: u8) -> Self { + let encoding = Encoding { + format: Format::Dwarf32, + // TODO: this should be configurable + // macOS doesn't seem to support DWARF > 3 + version: 3, + address_size, + }; + + let mut dwarf = DwarfUnit::new(encoding); + + // FIXME: how to get version when building out of tree? + // Normally this would use option_env!("CFG_VERSION"). + let producer = format!("cranelift fn (rustc version {})", "unknown version"); + let comp_dir = tcx.sess.working_dir.0.to_string_lossy().into_owned(); + let name = match tcx.sess.local_crate_source_file { + Some(ref path) => path.to_string_lossy().into_owned(), + None => tcx.crate_name(LOCAL_CRATE).to_string(), + }; + + let line_program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings), + LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings), + None, + ); + dwarf.unit.line_program = line_program; + + { + let name = dwarf.strings.add(&*name); + let comp_dir = dwarf.strings.add(&*comp_dir); + + let root = dwarf.unit.root(); + let root = dwarf.unit.get_mut(root); + root.set( + gimli::DW_AT_producer, + AttributeValue::StringRef(dwarf.strings.add(producer)), + ); + root.set( + gimli::DW_AT_language, + AttributeValue::Language(gimli::DW_LANG_Rust), + ); + root.set(gimli::DW_AT_name, AttributeValue::StringRef(name)); + root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir)); + root.set( + gimli::DW_AT_low_pc, + AttributeValue::Address(Address::Constant(0)), + ); + } + + DebugContext { + tcx, + + endian: target_endian(tcx), + symbols: indexmap::IndexMap::new(), + + dwarf, + unit_range_list: RangeList(Vec::new()), + + types: HashMap::new(), + } + } + + fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId { + if let Some(type_id) = self.types.get(ty) { + return *type_id; + } + + let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag); + + let primitive = |dwarf: &mut DwarfUnit, ate| { + let type_id = new_entry(dwarf, gimli::DW_TAG_base_type); + let type_entry = dwarf.unit.get_mut(type_id); + type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate)); + type_id + }; + + let name = format!("{}", ty); + let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap(); + + let type_id = match ty.kind { + ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean), + ty::Char => primitive(&mut self.dwarf, gimli::DW_ATE_UTF), + ty::Uint(_) => primitive(&mut self.dwarf, gimli::DW_ATE_unsigned), + ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed), + ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float), + ty::Ref(_, pointee_ty, mutbl) + | ty::RawPtr(ty::TypeAndMut { + ty: pointee_ty, + mutbl, + }) => { + let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type); + + // Ensure that type is inserted before recursing to avoid duplicates + self.types.insert(ty, type_id); + + let pointee = self.dwarf_ty(pointee_ty); + + let type_entry = self.dwarf.unit.get_mut(type_id); + + //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc::hir::Mutability::MutMutable)); + type_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(pointee)); + + type_id + } + ty::Adt(adt_def, _substs) if adt_def.is_struct() && !layout.is_unsized() => { + let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type); + + // Ensure that type is inserted before recursing to avoid duplicates + self.types.insert(ty, type_id); + + let variant = adt_def.non_enum_variant(); + + for (field_idx, field_def) in variant.fields.iter().enumerate() { + let field_offset = layout.fields.offset(field_idx); + let field_layout = layout.field(&layout::LayoutCx { + tcx: self.tcx, + param_env: ParamEnv::reveal_all(), + }, field_idx).unwrap(); + + let field_type = self.dwarf_ty(field_layout.ty); + + let field_id = self.dwarf.unit.add(type_id, gimli::DW_TAG_member); + let field_entry = self.dwarf.unit.get_mut(field_id); + + field_entry.set(gimli::DW_AT_name, AttributeValue::String(field_def.ident.as_str().to_string().into_bytes())); + field_entry.set(gimli::DW_AT_data_member_location, AttributeValue::Udata(field_offset.bytes())); + field_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(field_type)); + } + + type_id + } + _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type), + }; + + let type_entry = self.dwarf.unit.get_mut(type_id); + + type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); + type_entry.set( + gimli::DW_AT_byte_size, + AttributeValue::Udata(layout.size.bytes()), + ); + + self.types.insert(ty, type_id); + + type_id + } +} + +pub struct FunctionDebugContext<'a, 'tcx> { + debug_context: &'a mut DebugContext<'tcx>, + entry_id: UnitEntryId, + symbol: usize, + instance: Instance<'tcx>, + mir: &'tcx mir::Body<'tcx>, +} + +impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> { + pub fn new( + debug_context: &'a mut DebugContext<'tcx>, + instance: Instance<'tcx>, + func_id: FuncId, + name: &str, + ) -> Self { + let mir = *debug_context.tcx.instance_mir(instance.def); + + let (symbol, _) = debug_context.symbols.insert_full(func_id, name.to_string()); + + // FIXME: add to appropriate scope intead of root + let scope = debug_context.dwarf.unit.root(); + + let entry_id = debug_context + .dwarf + .unit + .add(scope, gimli::DW_TAG_subprogram); + let entry = debug_context.dwarf.unit.get_mut(entry_id); + let name_id = debug_context.dwarf.strings.add(name); + entry.set( + gimli::DW_AT_linkage_name, + AttributeValue::StringRef(name_id), + ); + + FunctionDebugContext { + debug_context, + entry_id, + symbol, + instance, + mir, + } + } + + fn define_local(&mut self, name: String, ty: Ty<'tcx>) -> UnitEntryId { + let ty = self.debug_context.tcx.subst_and_normalize_erasing_regions( + self.instance.substs, + ty::ParamEnv::reveal_all(), + &ty, + ); + let dw_ty = self.debug_context.dwarf_ty(ty); + + let var_id = self + .debug_context + .dwarf + .unit + .add(self.entry_id, gimli::DW_TAG_variable); + let var_entry = self.debug_context.dwarf.unit.get_mut(var_id); + + var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); + var_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(dw_ty)); + + var_id + } + + pub fn define( + &mut self, + context: &Context, + isa: &dyn cranelift::codegen::isa::TargetIsa, + source_info_set: &indexmap::IndexSet<(Span, mir::SourceScope)>, + local_map: HashMap>, + ) { + let end = self.create_debug_lines(context, isa, source_info_set); + + self.debug_context + .unit_range_list + .0 + .push(Range::StartLength { + begin: Address::Symbol { + symbol: self.symbol, + addend: 0, + }, + length: end as u64, + }); + + // FIXME make it more reliable and implement scopes before re-enabling this. + if false { + let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap(); + + for (local, _local_decl) in self.mir.local_decls.iter_enumerated() { + let var_id = self.define_local(format!("{:?}", local), &self.mir.local_decls[local].ty); + + let location = place_location( + self, + context, + &local_map, + &value_labels_ranges, + Place { + base: PlaceBase::Local(local), + projection: ty::List::empty(), + }, + ); + + let var_entry = self.debug_context.dwarf.unit.get_mut(var_id); + var_entry.set(gimli::DW_AT_location, location); + } + } + + // FIXME create locals for all entries in mir.var_debug_info + } +} + +fn place_location<'a, 'tcx>( + func_debug_ctx: &mut FunctionDebugContext<'a, 'tcx>, + context: &Context, + local_map: &HashMap>, + value_labels_ranges: &HashMap>, + place: Place<'tcx>, +) -> AttributeValue { + assert!(place.projection.is_empty()); // FIXME implement them + let cplace = match place.base { + PlaceBase::Local(local) => local_map[&local], + PlaceBase::Static(_) => bug!("Unenforced invariant that the place is based on a Local violated: {:?}", place), + }; + + match cplace.inner() { + CPlaceInner::Var(local) => { + let value_label = cranelift::codegen::ir::ValueLabel::from_u32(local.as_u32()); + if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) { + let loc_list = LocationList( + value_loc_ranges + .iter() + .map(|value_loc_range| Location::StartEnd { + begin: Address::Symbol { + symbol: func_debug_ctx.symbol, + addend: i64::from(value_loc_range.start), + }, + end: Address::Symbol { + symbol: func_debug_ctx.symbol, + addend: i64::from(value_loc_range.end), + }, + data: Expression( + translate_loc(value_loc_range.loc, &context.func.stack_slots).unwrap(), + ), + }) + .collect(), + ); + let loc_list_id = func_debug_ctx.debug_context.dwarf.unit.locations.add(loc_list); + + AttributeValue::LocationListRef(loc_list_id) + } else { + // FIXME set value labels for unused locals + + AttributeValue::Exprloc(Expression(vec![])) + } + } + CPlaceInner::Addr(_, _) => { + // FIXME implement this (used by arguments and returns) + + AttributeValue::Exprloc(Expression(vec![])) + } + CPlaceInner::Stack(stack_slot) => { + AttributeValue::Exprloc(Expression(translate_loc(ValueLoc::Stack(*stack_slot), &context.func.stack_slots).unwrap())) + } + CPlaceInner::NoPlace => AttributeValue::Exprloc(Expression(vec![])), + } +} + + + + + +// Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137 + +fn map_reg(reg: RegUnit) -> Register { + static mut REG_X86_MAP: Option> = None; + // FIXME lazy initialization? + unsafe { + if REG_X86_MAP.is_none() { + REG_X86_MAP = Some(HashMap::new()); + } + if let Some(val) = REG_X86_MAP.as_mut().unwrap().get(®) { + return *val; + } + let result = match reg { + 0 => X86_64::RAX, + 1 => X86_64::RCX, + 2 => X86_64::RDX, + 3 => X86_64::RBX, + 4 => X86_64::RSP, + 5 => X86_64::RBP, + 6 => X86_64::RSI, + 7 => X86_64::RDI, + 8 => X86_64::R8, + 9 => X86_64::R9, + 10 => X86_64::R10, + 11 => X86_64::R11, + 12 => X86_64::R12, + 13 => X86_64::R13, + 14 => X86_64::R14, + 15 => X86_64::R15, + 16 => X86_64::XMM0, + 17 => X86_64::XMM1, + 18 => X86_64::XMM2, + 19 => X86_64::XMM3, + 20 => X86_64::XMM4, + 21 => X86_64::XMM5, + 22 => X86_64::XMM6, + 23 => X86_64::XMM7, + 24 => X86_64::XMM8, + 25 => X86_64::XMM9, + 26 => X86_64::XMM10, + 27 => X86_64::XMM11, + 28 => X86_64::XMM12, + 29 => X86_64::XMM13, + 30 => X86_64::XMM14, + 31 => X86_64::XMM15, + _ => panic!("unknown x86_64 register {}", reg), + }; + REG_X86_MAP.as_mut().unwrap().insert(reg, result); + result + } +} + +fn translate_loc(loc: ValueLoc, stack_slots: &StackSlots) -> Option> { + match loc { + ValueLoc::Reg(reg) => { + let machine_reg = map_reg(reg).0 as u8; + assert!(machine_reg <= 32); // FIXME + Some(vec![gimli::constants::DW_OP_reg0.0 + machine_reg]) + } + ValueLoc::Stack(ss) => { + if let Some(ss_offset) = stack_slots[ss].offset { + let endian = gimli::RunTimeEndian::Little; + let mut writer = write::EndianVec::new(endian); + writer + .write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8) + .expect("bp wr"); + writer.write_sleb128(ss_offset as i64 + 16).expect("ss wr"); + let buf = writer.into_vec(); + return Some(buf); + } + None + } + _ => None, + } +} diff --git a/src/intrinsics.rs b/src/intrinsics.rs index 4ad6ae91f..840b4bf0d 100644 --- a/src/intrinsics.rs +++ b/src/intrinsics.rs @@ -625,6 +625,7 @@ pub fn codegen_intrinsic_call<'tcx>( } _ => panic!("clif_type returned {}", clif_ty), }; + fx.bcx.set_val_label(val, cranelift::codegen::ir::ValueLabel::from_u32(var.as_u32())); fx.bcx.def_var(mir_var(var), val); } _ => { @@ -656,6 +657,7 @@ pub fn codegen_intrinsic_call<'tcx>( } _ => panic!("clif_type returned {}", clif_ty), }; + fx.bcx.set_val_label(val, cranelift::codegen::ir::ValueLabel::from_u32(var.as_u32())); fx.bcx.def_var(mir_var(var), val); } CPlaceInner::Addr(_, _) | CPlaceInner::Stack(_) => { diff --git a/src/pretty_clif.rs b/src/pretty_clif.rs index f595d384f..64359f389 100644 --- a/src/pretty_clif.rs +++ b/src/pretty_clif.rs @@ -74,8 +74,8 @@ pub struct CommentWriter { impl CommentWriter { pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { - CommentWriter { - global_comments: vec![ + let mut global_comments = if cfg!(debug_assertions) { + vec![ format!("symbol {}", tcx.symbol_name(instance).name.as_str()), format!("instance {:?}", instance), format!( @@ -86,7 +86,13 @@ impl CommentWriter { ) ), String::new(), - ], + ] + } else { + vec![] + }; + + CommentWriter { + global_comments, entity_comments: HashMap::new(), inst_comments: HashMap::new(), } diff --git a/src/value_and_place.rs b/src/value_and_place.rs index 142795a91..64fd30def 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -299,7 +299,11 @@ impl<'tcx> CPlace<'tcx> { pub fn to_cvalue(self, fx: &mut FunctionCx<'_, 'tcx, impl Backend>) -> CValue<'tcx> { let layout = self.layout(); match self.inner { - CPlaceInner::Var(var) => CValue::by_val(fx.bcx.use_var(mir_var(var)), layout), + CPlaceInner::Var(var) => { + let val = fx.bcx.use_var(mir_var(var)); + fx.bcx.set_val_label(val, cranelift::codegen::ir::ValueLabel::from_u32(var.as_u32())); + CValue::by_val(val, layout) + } CPlaceInner::Addr(addr, extra) => { assert!(extra.is_none(), "unsized values are not yet supported"); CValue::by_ref(addr, layout) @@ -419,6 +423,7 @@ impl<'tcx> CPlace<'tcx> { let addr = match self.inner { CPlaceInner::Var(var) => { let data = from.load_scalar(fx); + fx.bcx.set_val_label(data, cranelift::codegen::ir::ValueLabel::from_u32(var.as_u32())); fx.bcx.def_var(mir_var(var), data); return; } diff --git a/test.sh b/test.sh index 05ba16ea3..4f393ad17 100755 --- a/test.sh +++ b/test.sh @@ -31,11 +31,16 @@ $RUSTC example/mini_core.rs --crate-name mini_core --crate-type lib,dylib echo "[BUILD] example" $RUSTC example/example.rs --crate-type lib -JIT_ARGS="abc bcd" jit mini_core_hello_world example/mini_core_hello_world.rs +#JIT_ARGS="abc bcd" jit mini_core_hello_world example/mini_core_hello_world.rs echo "[AOT] mini_core_hello_world" -$RUSTC example/mini_core_hello_world.rs --crate-name mini_core_hello_world --crate-type bin +$RUSTC example/mini_core_hello_world.rs --crate-name mini_core_hello_world --crate-type bin -g ./target/out/mini_core_hello_world abc bcd +if lldb -v; then +(echo "break set -n main"; echo "run"; sleep 1; echo "si -c 10"; sleep 1; echo "frame variable") | lldb -- ./target/out/mini_core_hello_world abc bcd +fi + +exit 1 echo "[AOT] arbitrary_self_types_pointers_and_wrappers" $RUSTC example/arbitrary_self_types_pointers_and_wrappers.rs --crate-name arbitrary_self_types_pointers_and_wrappers --crate-type bin