Skip to content
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

feat(debuginfo): Add support for debuginfo, without scope support #455

Merged
merged 14 commits into from Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion libgccjit.version
@@ -1 +1 @@
d24c8dae3
cf9554126
409 changes: 208 additions & 201 deletions src/builder.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/context.rs
@@ -1,6 +1,6 @@
use std::cell::{Cell, RefCell};

use gccjit::{Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, RValue, Type};
use gccjit::{Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type};
use rustc_codegen_ssa::base::wants_msvc_seh;
use rustc_codegen_ssa::traits::{
BackendTypes,
Expand Down Expand Up @@ -345,7 +345,7 @@ impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> {
type Funclet = (); // TODO(antoyo)

type DIScope = (); // TODO(antoyo)
type DILocation = (); // TODO(antoyo)
type DILocation = Location<'gcc>; // TODO(antoyo)
tempdragon marked this conversation as resolved.
Show resolved Hide resolved
type DIVariable = (); // TODO(antoyo)
}

Expand Down
223 changes: 202 additions & 21 deletions src/debuginfo.rs
@@ -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.
#[cfg(feature = "master")]
tempdragon marked this conversation as resolved.
Show resolved Hide resolved
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
/// debug name and the mangled name should both be included in the LValues.
tempdragon marked this conversation as resolved.
Show resolved Hide resolved
/// 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) {
match self.sess().source_map().lookup_line(pos) {
tempdragon marked this conversation as resolved.
Show resolved Hide resolved
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
}
)
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
Maybe you can also hint me in the certain interface of (debug)scope in GCC. I will handle it once I have time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 DIScope.

Copy link
Contributor

Choose a reason for hiding this comment

The 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 is_like_msvc was moved from after the match to inside it.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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),
}
}
}

Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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!();
let pos = span.lo();
tempdragon marked this conversation as resolved.
Show resolved Hide resolved
let (file, line, col) = self.lookup_debug_loc(pos);
tempdragon marked this conversation as resolved.
Show resolved Hide resolved
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{
Location::null()
tempdragon marked this conversation as resolved.
Show resolved Hide resolved
}
}
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{
Location::null()
tempdragon marked this conversation as resolved.
Show resolved Hide resolved
},
},
_ => Location::null(),
};
loc
}
}