-
Notifications
You must be signed in to change notification settings - Fork 77
feat(debuginfo): Add support for debuginfo, without scope support #455
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
c638def
2ffe9d1
8879155
5b053a3
6170f48
09fd908
51cd5f1
eaeb544
ef158f2
e18d3c3
fba0dae
8c975d9
9cc0a42
7c3565e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
d24c8dae3 | ||
cf9554126 |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,170 @@ | ||
use gccjit::RValue; | ||
use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind}; | ||
use gccjit::{Location, RValue}; | ||
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; | ||
use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods}; | ||
use rustc_middle::mir; | ||
use rustc_index::bit_set::BitSet; | ||
use rustc_index::IndexVec; | ||
use rustc_middle::mir::{Body, self, SourceScope}; | ||
use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty}; | ||
use rustc_span::{SourceFile, Span, Symbol}; | ||
use rustc_session::config::DebugInfo; | ||
use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span, Symbol}; | ||
use rustc_target::abi::call::FnAbi; | ||
use rustc_target::abi::Size; | ||
use rustc_data_structures::sync::Lrc; | ||
use crate::rustc_index::Idx; | ||
use std::ops::Range; | ||
|
||
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, | ||
_scope_metadata: Self::DIScope, | ||
_variable_alloca: Self::Value, | ||
dbg_loc: Self::DILocation, | ||
variable_alloca: Self::Value, | ||
_direct_offset: Size, | ||
_indirect_offsets: &[Size], | ||
_fragment: Option<Range<Size>>, | ||
) { | ||
unimplemented!(); | ||
// Not sure if this is correct, probably wrong but still keep it here. | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[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. | ||
} | ||
|
||
/// Currently, this function is not yet implemented. It seems that the | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// 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) { | ||
unimplemented!(); | ||
//unimplemented!(); | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation) { | ||
self.loc = Some(dbg_loc); | ||
} | ||
} | ||
|
||
pub 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()); | ||
} | ||
|
||
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(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; | ||
} | ||
} | ||
|
||
fn set_dbg_loc(&mut self, _dbg_loc: Self::DILocation) { | ||
unimplemented!(); | ||
let loc = cx.lookup_debug_loc(scope_data.span.lo()); | ||
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. | ||
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.0.start_pos, | ||
file_end_pos: loc.0.end_position(), | ||
}; | ||
instantiated.insert(scope); | ||
} | ||
|
||
/// Copied from LLVM backend | ||
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { | ||
pub fn lookup_debug_loc(&self, pos: BytePos) -> (Lrc<SourceFile>, u32, u32) { | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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, | ||
if ! self.sess().target.is_like_msvc { | ||
col } else { | ||
UNKNOWN_COLUMN_NUMBER | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use the exact same code as in the LLVM codegen since this will make it easier for people that wants to make changes in both versions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the code is actually not completely portable. Since the scope part is not currently supported(and I haven't found the corresponding structure & interface for a scope in GCC), I have to currently separate the code from the original LLVM code. I never made it to a correct scope implementation. This will be fixed when the scope support is ready, or maybe never, if it is proved unnecessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In ef158f2, I added the original code as a comment as it is currently not in use for an empty implementation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I understand the difference compared with the code from cg_llvm. It just seems to me that the condition And that the return type was changed from a struct to a tuple containing the same information. Could you please explain to me how this is different? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Short answer: It's not that different. I will switch back to the original implementation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I originally wrote this stuff by hand and end up noticing the function being reusable. So I switched to their implementation. I choose a tuple in place of a struct because it seems to me that it is easier to be done not defining a data structure for something that is used only a few times internally. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I plan to attempt to deduplicate some code between cg_gcc and cg_llvm in the future. Not sure if this specific function can be deduplicated, but I guess a couple a other functions are. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this should be a good idea to do it upstream, but it also depends on the attitude of developers of other backends such as those of cranelift. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it's the plan. Cranelift doesn't use the SSA traits as far as I know. |
||
) | ||
} | ||
Err(file) => (file, UNKNOWN_LINE_NUMBER, UNKNOWN_COLUMN_NUMBER), | ||
} | ||
} | ||
} | ||
|
||
|
@@ -51,25 +180,45 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { | |
|
||
fn create_function_debug_context( | ||
&self, | ||
_instance: Instance<'tcx>, | ||
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, | ||
_llfn: RValue<'gcc>, | ||
_mir: &mir::Body<'tcx>, | ||
instance: Instance<'tcx>, | ||
fn_abi: &FnAbi<'tcx, Ty<'tcx>>, | ||
llfn: RValue<'gcc>, | ||
mir: &mir::Body<'tcx>, | ||
) -> Option<FunctionDebugContext<'tcx, Self::DIScope, Self::DILocation>> { | ||
// TODO(antoyo) | ||
None | ||
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 { | ||
unimplemented!(); | ||
//unimplemented!(); | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
fn debuginfo_finalize(&self) { | ||
// TODO(antoyo) | ||
// TODO(antoyo): Get the debug flag/predicate to allow optional generation of debuginfo. | ||
self.context.set_debug_info(true) | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
fn create_dbg_var( | ||
|
@@ -80,7 +229,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { | |
_variable_kind: VariableKind, | ||
_span: Span, | ||
) -> Self::DIVariable { | ||
unimplemented!(); | ||
() | ||
} | ||
|
||
fn dbg_scope_fn( | ||
|
@@ -89,15 +238,47 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { | |
_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, | ||
_maybe_definition_llfn: Option<RValue<'gcc>>, | ||
) -> Self::DIScope { | ||
unimplemented!(); | ||
//unimplemented!(); | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
fn dbg_loc( | ||
&self, | ||
_scope: Self::DIScope, | ||
_inlined_at: Option<Self::DILocation>, | ||
_span: Span, | ||
span: Span, | ||
) -> Self::DILocation { | ||
unimplemented!(); | ||
//unimplemented!(); | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let pos = span.lo(); | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let (file, line, col) = self.lookup_debug_loc(pos); | ||
let loc = match &file.name { | ||
rustc_span::FileName::Real(name) => match name { | ||
rustc_span::RealFileName::LocalPath(name) => { | ||
if let Some(name) = name.to_str() { | ||
self.context | ||
.new_location(name, line as i32, col as i32) | ||
}else{ | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Location::null() | ||
} | ||
} | ||
rustc_span::RealFileName::Remapped { | ||
local_path, | ||
virtual_name:_, | ||
} => 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{ | ||
tempdragon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Location::null() | ||
}, | ||
}, | ||
_ => Location::null(), | ||
}; | ||
loc | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.