mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
316 lines
11 KiB
Rust
316 lines
11 KiB
Rust
use std::ops::Range;
|
|
|
|
use gccjit::{Location, RValue};
|
|
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind};
|
|
use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoCodegenMethods};
|
|
use rustc_data_structures::sync::Lrc;
|
|
use rustc_index::bit_set::BitSet;
|
|
use rustc_index::{Idx, IndexVec};
|
|
use rustc_middle::mir::{self, Body, SourceScope};
|
|
use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
|
|
use rustc_session::config::DebugInfo;
|
|
use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span, Symbol};
|
|
use rustc_target::abi::Size;
|
|
use rustc_target::abi::call::FnAbi;
|
|
|
|
use crate::builder::Builder;
|
|
use crate::context::CodegenCx;
|
|
|
|
pub(super) const UNKNOWN_LINE_NUMBER: u32 = 0;
|
|
pub(super) const UNKNOWN_COLUMN_NUMBER: u32 = 0;
|
|
|
|
impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
|
|
// FIXME(eddyb) find a common convention for all of the debuginfo-related
|
|
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
|
|
fn dbg_var_addr(
|
|
&mut self,
|
|
_dbg_var: Self::DIVariable,
|
|
_dbg_loc: Self::DILocation,
|
|
_variable_alloca: Self::Value,
|
|
_direct_offset: Size,
|
|
_indirect_offsets: &[Size],
|
|
_fragment: Option<Range<Size>>,
|
|
) {
|
|
// FIXME(tempdragon): Not sure if this is correct, probably wrong but still keep it here.
|
|
#[cfg(feature = "master")]
|
|
_variable_alloca.set_location(_dbg_loc);
|
|
}
|
|
|
|
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
|
|
// TODO(antoyo): insert reference to gdb debug scripts section global.
|
|
}
|
|
|
|
/// FIXME(tempdragon): Currently, this function is not yet implemented. It seems that the
|
|
/// debug name and the mangled name should both be included in the LValues.
|
|
/// Besides, a function to get the rvalue type(m_is_lvalue) should also be included.
|
|
fn set_var_name(&mut self, _value: RValue<'gcc>, _name: &str) {}
|
|
|
|
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation) {
|
|
self.location = Some(dbg_loc);
|
|
}
|
|
|
|
fn clear_dbg_loc(&mut self) {
|
|
self.location = None;
|
|
}
|
|
}
|
|
|
|
/// Generate the `debug_context` in an MIR Body.
|
|
/// # Source of Origin
|
|
/// Copied from `create_scope_map.rs` of rustc_codegen_llvm
|
|
fn compute_mir_scopes<'gcc, 'tcx>(
|
|
cx: &CodegenCx<'gcc, 'tcx>,
|
|
instance: Instance<'tcx>,
|
|
mir: &Body<'tcx>,
|
|
debug_context: &mut FunctionDebugContext<'tcx, (), Location<'gcc>>,
|
|
) {
|
|
// Find all scopes with variables defined in them.
|
|
let variables = if cx.sess().opts.debuginfo == DebugInfo::Full {
|
|
let mut vars = BitSet::new_empty(mir.source_scopes.len());
|
|
// FIXME(eddyb) take into account that arguments always have debuginfo,
|
|
// irrespective of their name (assuming full debuginfo is enabled).
|
|
// NOTE(eddyb) actually, on second thought, those are always in the
|
|
// function scope, which always exists.
|
|
for var_debug_info in &mir.var_debug_info {
|
|
vars.insert(var_debug_info.source_info.scope);
|
|
}
|
|
Some(vars)
|
|
} else {
|
|
// Nothing to emit, of course.
|
|
None
|
|
};
|
|
let mut instantiated = BitSet::new_empty(mir.source_scopes.len());
|
|
// Instantiate all scopes.
|
|
for idx in 0..mir.source_scopes.len() {
|
|
let scope = SourceScope::new(idx);
|
|
make_mir_scope(cx, instance, mir, &variables, debug_context, &mut instantiated, scope);
|
|
}
|
|
assert!(instantiated.count() == mir.source_scopes.len());
|
|
}
|
|
|
|
/// Update the `debug_context`, adding new scope to it,
|
|
/// if it's not added as is denoted in `instantiated`.
|
|
///
|
|
/// # Source of Origin
|
|
/// Copied from `create_scope_map.rs` of rustc_codegen_llvm
|
|
/// FIXME(tempdragon/?): Add Scope Support Here.
|
|
fn make_mir_scope<'gcc, 'tcx>(
|
|
cx: &CodegenCx<'gcc, 'tcx>,
|
|
_instance: Instance<'tcx>,
|
|
mir: &Body<'tcx>,
|
|
variables: &Option<BitSet<SourceScope>>,
|
|
debug_context: &mut FunctionDebugContext<'tcx, (), Location<'gcc>>,
|
|
instantiated: &mut BitSet<SourceScope>,
|
|
scope: SourceScope,
|
|
) {
|
|
if instantiated.contains(scope) {
|
|
return;
|
|
}
|
|
|
|
let scope_data = &mir.source_scopes[scope];
|
|
let parent_scope = if let Some(parent) = scope_data.parent_scope {
|
|
make_mir_scope(cx, _instance, mir, variables, debug_context, instantiated, parent);
|
|
debug_context.scopes[parent]
|
|
} else {
|
|
// The root is the function itself.
|
|
let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
|
|
debug_context.scopes[scope] = DebugScope {
|
|
file_start_pos: file.start_pos,
|
|
file_end_pos: file.end_position(),
|
|
..debug_context.scopes[scope]
|
|
};
|
|
instantiated.insert(scope);
|
|
return;
|
|
};
|
|
|
|
if let Some(ref vars) = *variables {
|
|
if !vars.contains(scope) && scope_data.inlined.is_none() {
|
|
// Do not create a DIScope if there are no variables defined in this
|
|
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
|
|
debug_context.scopes[scope] = parent_scope;
|
|
instantiated.insert(scope);
|
|
return;
|
|
}
|
|
}
|
|
|
|
let loc = cx.lookup_debug_loc(scope_data.span.lo());
|
|
|
|
// FIXME(tempdragon): Add the scope related code here if the scope is supported.
|
|
let dbg_scope = ();
|
|
|
|
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
|
|
// FIXME(eddyb) this doesn't account for the macro-related
|
|
// `Span` fixups that `rustc_codegen_ssa::mir::debuginfo` does.
|
|
|
|
// TODO(tempdragon): Add scope support and then revert to cg_llvm version of this closure
|
|
// NOTE: These variables passed () here.
|
|
// Changed to comply to clippy.
|
|
|
|
/* let callsite_scope = */
|
|
parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
|
|
cx.dbg_loc(/* callsite_scope */ (), parent_scope.inlined_at, callsite_span)
|
|
});
|
|
let p_inlined_at = parent_scope.inlined_at;
|
|
// TODO(tempdragon): dbg_scope: Add support for scope extension here.
|
|
inlined_at.or(p_inlined_at);
|
|
|
|
debug_context.scopes[scope] = DebugScope {
|
|
dbg_scope,
|
|
inlined_at,
|
|
file_start_pos: loc.file.start_pos,
|
|
file_end_pos: loc.file.end_position(),
|
|
};
|
|
instantiated.insert(scope);
|
|
}
|
|
|
|
/// A source code location used to generate debug information.
|
|
// FIXME(eddyb) rename this to better indicate it's a duplicate of
|
|
// `rustc_span::Loc` rather than `DILocation`, perhaps by making
|
|
// `lookup_char_pos` return the right information instead.
|
|
pub struct DebugLoc {
|
|
/// Information about the original source file.
|
|
pub file: Lrc<SourceFile>,
|
|
/// The (1-based) line number.
|
|
pub line: u32,
|
|
/// The (1-based) column number.
|
|
pub col: u32,
|
|
}
|
|
|
|
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|
/// Looks up debug source information about a `BytePos`.
|
|
// FIXME(eddyb) rename this to better indicate it's a duplicate of
|
|
// `lookup_char_pos` rather than `dbg_loc`, perhaps by making
|
|
// `lookup_char_pos` return the right information instead.
|
|
// Source of Origin: cg_llvm
|
|
pub fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc {
|
|
let (file, line, col) = match self.sess().source_map().lookup_line(pos) {
|
|
Ok(SourceFileAndLine { sf: file, line }) => {
|
|
let line_pos = file.lines()[line];
|
|
|
|
// Use 1-based indexing.
|
|
let line = (line + 1) as u32;
|
|
let col = (file.relative_position(pos) - line_pos).to_u32() + 1;
|
|
|
|
(file, line, col)
|
|
}
|
|
Err(file) => (file, UNKNOWN_LINE_NUMBER, UNKNOWN_COLUMN_NUMBER),
|
|
};
|
|
|
|
// For MSVC, omit the column number.
|
|
// Otherwise, emit it. This mimics clang behaviour.
|
|
// See discussion in https://github.com/rust-lang/rust/issues/42921
|
|
if self.sess().target.is_like_msvc {
|
|
DebugLoc { file, line, col: UNKNOWN_COLUMN_NUMBER }
|
|
} else {
|
|
DebugLoc { file, line, col }
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|
fn create_vtable_debuginfo(
|
|
&self,
|
|
_ty: Ty<'tcx>,
|
|
_trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
|
|
_vtable: Self::Value,
|
|
) {
|
|
// TODO(antoyo)
|
|
}
|
|
|
|
fn create_function_debug_context(
|
|
&self,
|
|
instance: Instance<'tcx>,
|
|
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
|
llfn: RValue<'gcc>,
|
|
mir: &mir::Body<'tcx>,
|
|
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>> {
|
|
if self.sess().opts.debuginfo == DebugInfo::None {
|
|
return None;
|
|
}
|
|
|
|
// Initialize fn debug context (including scopes).
|
|
let empty_scope = DebugScope {
|
|
dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
|
|
inlined_at: None,
|
|
file_start_pos: BytePos(0),
|
|
file_end_pos: BytePos(0),
|
|
};
|
|
let mut fn_debug_context = FunctionDebugContext {
|
|
scopes: IndexVec::from_elem(empty_scope, mir.source_scopes.as_slice()),
|
|
inlined_function_scopes: Default::default(),
|
|
};
|
|
|
|
// Fill in all the scopes, with the information from the MIR body.
|
|
compute_mir_scopes(self, instance, mir, &mut fn_debug_context);
|
|
|
|
Some(fn_debug_context)
|
|
}
|
|
|
|
fn extend_scope_to_file(
|
|
&self,
|
|
_scope_metadata: Self::DIScope,
|
|
_file: &SourceFile,
|
|
) -> Self::DIScope {
|
|
// TODO(antoyo): implement.
|
|
}
|
|
|
|
fn debuginfo_finalize(&self) {
|
|
self.context.set_debug_info(true)
|
|
}
|
|
|
|
fn create_dbg_var(
|
|
&self,
|
|
_variable_name: Symbol,
|
|
_variable_type: Ty<'tcx>,
|
|
_scope_metadata: Self::DIScope,
|
|
_variable_kind: VariableKind,
|
|
_span: Span,
|
|
) -> Self::DIVariable {
|
|
}
|
|
|
|
fn dbg_scope_fn(
|
|
&self,
|
|
_instance: Instance<'tcx>,
|
|
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
|
_maybe_definition_llfn: Option<RValue<'gcc>>,
|
|
) -> Self::DIScope {
|
|
// TODO(antoyo): implement.
|
|
}
|
|
|
|
fn dbg_loc(
|
|
&self,
|
|
_scope: Self::DIScope,
|
|
_inlined_at: Option<Self::DILocation>,
|
|
span: Span,
|
|
) -> Self::DILocation {
|
|
let pos = span.lo();
|
|
let DebugLoc { file, line, col } = self.lookup_debug_loc(pos);
|
|
let loc = match file.name {
|
|
rustc_span::FileName::Real(ref name) => match *name {
|
|
rustc_span::RealFileName::LocalPath(ref name) => {
|
|
if let Some(name) = name.to_str() {
|
|
self.context.new_location(name, line as i32, col as i32)
|
|
} else {
|
|
Location::null()
|
|
}
|
|
}
|
|
rustc_span::RealFileName::Remapped {
|
|
ref local_path,
|
|
virtual_name: ref _unused,
|
|
} => {
|
|
if let Some(name) = local_path.as_ref() {
|
|
if let Some(name) = name.to_str() {
|
|
self.context.new_location(name, line as i32, col as i32)
|
|
} else {
|
|
Location::null()
|
|
}
|
|
} else {
|
|
Location::null()
|
|
}
|
|
}
|
|
},
|
|
_ => Location::null(),
|
|
};
|
|
loc
|
|
}
|
|
}
|