use std::ops::Range; use std::sync::Arc; use gccjit::{Location, RValue}; use rustc_abi::Size; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::{self, Body, SourceScope}; use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty}; use rustc_session::config::DebugInfo; use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span, Symbol}; use rustc_target::callconv::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>, ) { // 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; } fn get_dbg_loc(&self) -> Option { self.location } } /// 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 = DenseBitSet::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 = DenseBitSet::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>, debug_context: &mut FunctionDebugContext<'tcx, (), Location<'gcc>>, instantiated: &mut DenseBitSet, 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: Arc, /// 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>, _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> { 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>, ) -> Self::DIScope { // TODO(antoyo): implement. } fn dbg_loc( &self, _scope: Self::DIScope, _inlined_at: Option, 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 } }