Skip to content

rustc_codegen_ssa: introduce MIR VarDebugInfo, but only for codegen. #65718

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

Merged
merged 7 commits into from
Nov 1, 2019
58 changes: 21 additions & 37 deletions src/librustc_codegen_llvm/debuginfo/create_scope_map.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_codegen_ssa::debuginfo::{FunctionDebugContext, FunctionDebugContextData, MirDebugScope};
use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, DebugScope};
use super::metadata::file_metadata;
use super::utils::{DIB, span_start};

@@ -12,34 +12,20 @@ use libc::c_uint;
use syntax_pos::Pos;

use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};

use syntax_pos::BytePos;
use rustc_index::vec::Idx;

/// Produces DIScope DIEs for each MIR Scope which has variables defined in it.
/// If debuginfo is disabled, the returned vector is empty.
pub fn create_mir_scopes(
pub fn compute_mir_scopes(
cx: &CodegenCx<'ll, '_>,
mir: &Body<'_>,
debug_context: &FunctionDebugContext<&'ll DISubprogram>,
) -> IndexVec<SourceScope, MirDebugScope<&'ll DIScope>> {
let null_scope = MirDebugScope {
scope_metadata: None,
file_start_pos: BytePos(0),
file_end_pos: BytePos(0)
};
let mut scopes = IndexVec::from_elem(null_scope, &mir.source_scopes);

let debug_context = match *debug_context {
FunctionDebugContext::RegularContext(ref data) => data,
FunctionDebugContext::DebugInfoDisabled |
FunctionDebugContext::FunctionWithoutDebugInfo => {
return scopes;
}
};

fn_metadata: &'ll DISubprogram,
debug_context: &mut FunctionDebugContext<&'ll DIScope>,
) {
// Find all the scopes with variables defined in them.
let mut has_variables = BitSet::new_empty(mir.source_scopes.len());
// FIXME(eddyb) base this on `decl.name`, or even better, on debuginfo.
// FIXME(eddyb) take into account that arguments always have debuginfo,
// irrespective of their name (assuming full debuginfo is enabled).
for var in mir.vars_iter() {
let decl = &mir.local_decls[var];
has_variables.insert(decl.visibility_scope);
@@ -48,31 +34,29 @@ pub fn create_mir_scopes(
// Instantiate all scopes.
for idx in 0..mir.source_scopes.len() {
let scope = SourceScope::new(idx);
make_mir_scope(cx, &mir, &has_variables, debug_context, scope, &mut scopes);
make_mir_scope(cx, &mir, fn_metadata, &has_variables, debug_context, scope);
}

scopes
}

fn make_mir_scope(cx: &CodegenCx<'ll, '_>,
mir: &Body<'_>,
fn_metadata: &'ll DISubprogram,
has_variables: &BitSet<SourceScope>,
debug_context: &FunctionDebugContextData<&'ll DISubprogram>,
scope: SourceScope,
scopes: &mut IndexVec<SourceScope, MirDebugScope<&'ll DIScope>>) {
if scopes[scope].is_valid() {
debug_context: &mut FunctionDebugContext<&'ll DISubprogram>,
scope: SourceScope) {
if debug_context.scopes[scope].is_valid() {
return;
}

let scope_data = &mir.source_scopes[scope];
let parent_scope = if let Some(parent) = scope_data.parent_scope {
make_mir_scope(cx, mir, has_variables, debug_context, parent, scopes);
scopes[parent]
make_mir_scope(cx, mir, fn_metadata, has_variables, debug_context, parent);
debug_context.scopes[parent]
} else {
// The root is the function itself.
let loc = span_start(cx, mir.span);
scopes[scope] = MirDebugScope {
scope_metadata: Some(debug_context.fn_metadata),
debug_context.scopes[scope] = DebugScope {
scope_metadata: Some(fn_metadata),
file_start_pos: loc.file.start_pos,
file_end_pos: loc.file.end_pos,
};
@@ -86,8 +70,8 @@ fn make_mir_scope(cx: &CodegenCx<'ll, '_>,
// However, we don't skip creating a nested scope if
// our parent is the root, because we might want to
// put arguments in the root and not have shadowing.
if parent_scope.scope_metadata.unwrap() != debug_context.fn_metadata {
scopes[scope] = parent_scope;
if parent_scope.scope_metadata.unwrap() != fn_metadata {
debug_context.scopes[scope] = parent_scope;
return;
}
}
@@ -105,7 +89,7 @@ fn make_mir_scope(cx: &CodegenCx<'ll, '_>,
loc.line as c_uint,
loc.col.to_usize() as c_uint))
};
scopes[scope] = MirDebugScope {
debug_context.scopes[scope] = DebugScope {
scope_metadata,
file_start_pos: loc.file.start_pos,
file_end_pos: loc.file.end_pos,
159 changes: 83 additions & 76 deletions src/librustc_codegen_llvm/debuginfo/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// See doc.rs for documentation.
mod doc;

use rustc_codegen_ssa::debuginfo::VariableAccess::*;
use rustc_codegen_ssa::debuginfo::VariableKind::*;
use rustc_codegen_ssa::mir::debuginfo::VariableKind::*;

use self::utils::{DIB, span_start, create_DIArray, is_node_local_to_unit};
use self::namespace::mangled_name_of_instance;
@@ -11,7 +10,7 @@ use self::metadata::{type_metadata, file_metadata, TypeMap};
use self::source_loc::InternalDebugLocation::{self, UnknownLocation};

use crate::llvm;
use crate::llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilder, DISubprogram, DIArray, DIFlags,
use crate::llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilder, DIArray, DIFlags,
DISPFlags, DILexicalBlock};
use rustc::hir::CodegenFnAttrFlags;
use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
@@ -27,17 +26,19 @@ use rustc::session::config::{self, DebugInfo};
use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet};
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_index::vec::IndexVec;
use rustc_codegen_ssa::debuginfo::{FunctionDebugContext, MirDebugScope, VariableAccess,
VariableKind, FunctionDebugContextData, type_names};
use rustc_codegen_ssa::debuginfo::type_names;
use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, DebugScope,
VariableKind};

use libc::c_uint;
use std::cell::RefCell;
use std::ffi::{CStr, CString};

use syntax_pos::{self, Span, Pos};
use smallvec::SmallVec;
use syntax_pos::{self, BytePos, Span, Pos};
use syntax::ast;
use syntax::symbol::Symbol;
use rustc::ty::layout::{self, LayoutOf, HasTyCtxt};
use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, Size};
use rustc_codegen_ssa::traits::*;

pub mod gdb;
@@ -47,7 +48,7 @@ pub mod metadata;
mod create_scope_map;
mod source_loc;

pub use self::create_scope_map::{create_mir_scopes};
pub use self::create_scope_map::compute_mir_scopes;
pub use self::metadata::create_global_var_metadata;
pub use self::metadata::extend_scope_to_file;
pub use self::source_loc::set_source_location;
@@ -148,21 +149,23 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
impl DebugInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
fn declare_local(
&mut self,
dbg_context: &FunctionDebugContext<&'ll DISubprogram>,
dbg_context: &FunctionDebugContext<&'ll DIScope>,
variable_name: ast::Name,
variable_type: Ty<'tcx>,
scope_metadata: &'ll DIScope,
variable_access: VariableAccess<'_, &'ll Value>,
variable_alloca: Self::Value,
direct_offset: Size,
indirect_offsets: &[Size],
variable_kind: VariableKind,
span: Span,
) {
assert!(!dbg_context.get_ref(span).source_locations_enabled);
assert!(!dbg_context.source_locations_enabled);
let cx = self.cx();

let file = span_start(cx, span).file;
let file_metadata = file_metadata(cx,
&file.name,
dbg_context.get_ref(span).defining_crate);
dbg_context.defining_crate);

let loc = span_start(cx, span);
let type_metadata = type_metadata(cx, variable_type, span);
@@ -173,49 +176,61 @@ impl DebugInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
};
let align = cx.align_of(variable_type);

let name = SmallCStr::new(&variable_name.as_str());
match (variable_access, &[][..]) {
(DirectVariable { alloca }, address_operations) |
(IndirectVariable {alloca, address_operations}, _) => {
let metadata = unsafe {
llvm::LLVMRustDIBuilderCreateVariable(
DIB(cx),
dwarf_tag,
scope_metadata,
name.as_ptr(),
file_metadata,
loc.line as c_uint,
type_metadata,
cx.sess().opts.optimize != config::OptLevel::No,
DIFlags::FlagZero,
argument_index,
align.bytes() as u32,
)
};
source_loc::set_debug_location(self,
InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize()));
unsafe {
let debug_loc = llvm::LLVMGetCurrentDebugLocation(self.llbuilder);
let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
DIB(cx),
alloca,
metadata,
address_operations.as_ptr(),
address_operations.len() as c_uint,
debug_loc,
self.llbb());

llvm::LLVMSetInstDebugLocation(self.llbuilder, instr);
}
source_loc::set_debug_location(self, UnknownLocation);
// Convert the direct and indirect offsets to address ops.
let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() };
let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() };
let mut addr_ops = SmallVec::<[_; 8]>::new();

if direct_offset.bytes() > 0 {
addr_ops.push(op_plus_uconst());
addr_ops.push(direct_offset.bytes() as i64);
}
for &offset in indirect_offsets {
addr_ops.push(op_deref());
if offset.bytes() > 0 {
addr_ops.push(op_plus_uconst());
addr_ops.push(offset.bytes() as i64);
}
}

let name = SmallCStr::new(&variable_name.as_str());
let metadata = unsafe {
llvm::LLVMRustDIBuilderCreateVariable(
DIB(cx),
dwarf_tag,
scope_metadata,
name.as_ptr(),
file_metadata,
loc.line as c_uint,
type_metadata,
cx.sess().opts.optimize != config::OptLevel::No,
DIFlags::FlagZero,
argument_index,
align.bytes() as u32,
)
};
source_loc::set_debug_location(self,
InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize()));
unsafe {
let debug_loc = llvm::LLVMGetCurrentDebugLocation(self.llbuilder);
let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
DIB(cx),
variable_alloca,
metadata,
addr_ops.as_ptr(),
addr_ops.len() as c_uint,
debug_loc,
self.llbb());

llvm::LLVMSetInstDebugLocation(self.llbuilder, instr);
}
source_loc::set_debug_location(self, UnknownLocation);
}

fn set_source_location(
&mut self,
debug_context: &mut FunctionDebugContext<&'ll DISubprogram>,
scope: Option<&'ll DIScope>,
debug_context: &mut FunctionDebugContext<&'ll DIScope>,
scope: &'ll DIScope,
span: Span,
) {
set_source_location(debug_context, &self, scope, span)
@@ -224,7 +239,7 @@ impl DebugInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
gdb::insert_reference_to_gdb_debug_scripts_section_global(self)
}

fn set_var_name(&mut self, value: &'ll Value, name: impl ToString) {
fn set_var_name(&mut self, value: &'ll Value, name: &str) {
// Avoid wasting time if LLVM value names aren't even enabled.
if self.sess().fewer_names() {
return;
@@ -254,7 +269,7 @@ impl DebugInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
Err(_) => return,
}

let cname = CString::new(name.to_string()).unwrap();
let cname = SmallCStr::new(name);
unsafe {
llvm::LLVMSetValueName(value, cname.as_ptr());
}
@@ -268,14 +283,14 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
sig: ty::FnSig<'tcx>,
llfn: &'ll Value,
mir: &mir::Body<'_>,
) -> FunctionDebugContext<&'ll DISubprogram> {
) -> Option<FunctionDebugContext<&'ll DIScope>> {
if self.sess().opts.debuginfo == DebugInfo::None {
return FunctionDebugContext::DebugInfoDisabled;
return None;
}

if let InstanceDef::Item(def_id) = instance.def {
if self.tcx().codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_DEBUG) {
return FunctionDebugContext::FunctionWithoutDebugInfo;
return None;
}
}

@@ -284,7 +299,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
// This can be the case for functions inlined from another crate
if span.is_dummy() {
// FIXME(simulacrum): Probably can't happen; remove.
return FunctionDebugContext::FunctionWithoutDebugInfo;
return None;
}

let def_id = instance.def_id();
@@ -357,14 +372,23 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
None)
};

// Initialize fn debug context (including scope map and namespace map)
let fn_debug_context = FunctionDebugContextData {
fn_metadata,
// Initialize fn debug context (including scopes).
// FIXME(eddyb) figure out a way to not need `Option` for `scope_metadata`.
let null_scope = DebugScope {
scope_metadata: None,
file_start_pos: BytePos(0),
file_end_pos: BytePos(0)
};
let mut fn_debug_context = FunctionDebugContext {
scopes: IndexVec::from_elem(null_scope, &mir.source_scopes),
source_locations_enabled: false,
defining_crate: def_id.krate,
};

return FunctionDebugContext::RegularContext(fn_debug_context);
// Fill in all the scopes, with the information from the MIR body.
compute_mir_scopes(self, mir, fn_metadata, &mut fn_debug_context);

return Some(fn_debug_context);

fn get_function_signature<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
@@ -549,14 +573,6 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
metadata::create_vtable_metadata(self, ty, vtable)
}

fn create_mir_scopes(
&self,
mir: &mir::Body<'_>,
debug_context: &mut FunctionDebugContext<&'ll DISubprogram>,
) -> IndexVec<mir::SourceScope, MirDebugScope<&'ll DIScope>> {
create_scope_map::create_mir_scopes(self, mir, debug_context)
}

fn extend_scope_to_file(
&self,
scope_metadata: &'ll DIScope,
@@ -569,13 +585,4 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn debuginfo_finalize(&self) {
finalize(self)
}

fn debuginfo_upvar_ops_sequence(&self, byte_offset_of_var_in_env: u64) -> [i64; 4] {
unsafe {
[llvm::LLVMRustDIBuilderCreateOpDeref(),
llvm::LLVMRustDIBuilderCreateOpPlusUconst(),
byte_offset_of_var_in_env as i64,
llvm::LLVMRustDIBuilderCreateOpDeref()]
}
}
}
17 changes: 4 additions & 13 deletions src/librustc_codegen_llvm/debuginfo/source_loc.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ use self::InternalDebugLocation::*;

use super::utils::{debug_context, span_start};
use super::metadata::UNKNOWN_COLUMN_NUMBER;
use rustc_codegen_ssa::debuginfo::FunctionDebugContext;
use rustc_codegen_ssa::mir::debuginfo::FunctionDebugContext;

use crate::llvm;
use crate::llvm::debuginfo::DIScope;
@@ -18,22 +18,13 @@ use syntax_pos::{Span, Pos};
pub fn set_source_location<D>(
debug_context: &FunctionDebugContext<D>,
bx: &Builder<'_, 'll, '_>,
scope: Option<&'ll DIScope>,
scope: &'ll DIScope,
span: Span,
) {
let function_debug_context = match *debug_context {
FunctionDebugContext::DebugInfoDisabled => return,
FunctionDebugContext::FunctionWithoutDebugInfo => {
set_debug_location(bx, UnknownLocation);
return;
}
FunctionDebugContext::RegularContext(ref data) => data
};

let dbg_loc = if function_debug_context.source_locations_enabled {
let dbg_loc = if debug_context.source_locations_enabled {
debug!("set_source_location: {}", bx.sess().source_map().span_to_string(span));
let loc = span_start(bx.cx(), span);
InternalDebugLocation::new(scope.unwrap(), loc.line, loc.col.to_usize())
InternalDebugLocation::new(scope, loc.line, loc.col.to_usize())
} else {
UnknownLocation
};
1 change: 1 addition & 0 deletions src/librustc_codegen_llvm/lib.rs
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@ extern crate rustc_fs_util;
extern crate rustc_driver as _;

#[macro_use] extern crate log;
extern crate smallvec;
extern crate syntax;
extern crate syntax_pos;
extern crate rustc_errors as errors;
82 changes: 1 addition & 81 deletions src/librustc_codegen_ssa/debuginfo/mod.rs
Original file line number Diff line number Diff line change
@@ -1,82 +1,2 @@
use syntax_pos::{BytePos, Span};
use rustc::hir::def_id::CrateNum;

// FIXME(eddyb) find a place for this (or a way to replace it).
pub mod type_names;

pub enum FunctionDebugContext<D> {
RegularContext(FunctionDebugContextData<D>),
DebugInfoDisabled,
FunctionWithoutDebugInfo,
}

impl<D> FunctionDebugContext<D> {
pub fn get_ref(&self, span: Span) -> &FunctionDebugContextData<D> {
match *self {
FunctionDebugContext::RegularContext(ref data) => data,
FunctionDebugContext::DebugInfoDisabled => {
span_bug!(
span,
"debuginfo: Error trying to access FunctionDebugContext \
although debug info is disabled!",
);
}
FunctionDebugContext::FunctionWithoutDebugInfo => {
span_bug!(
span,
"debuginfo: Error trying to access FunctionDebugContext \
for function that should be ignored by debug info!",
);
}
}
}
}

/// Enables emitting source locations for the given functions.
///
/// Since we don't want source locations to be emitted for the function prelude,
/// they are disabled when beginning to codegen a new function. This functions
/// switches source location emitting on and must therefore be called before the
/// first real statement/expression of the function is codegened.
pub fn start_emitting_source_locations<D>(dbg_context: &mut FunctionDebugContext<D>) {
match *dbg_context {
FunctionDebugContext::RegularContext(ref mut data) => {
data.source_locations_enabled = true;
},
_ => { /* safe to ignore */ }
}
}

pub struct FunctionDebugContextData<D> {
pub fn_metadata: D,
pub source_locations_enabled: bool,
pub defining_crate: CrateNum,
}

pub enum VariableAccess<'a, V> {
// The llptr given is an alloca containing the variable's value
DirectVariable { alloca: V },
// The llptr given is an alloca containing the start of some pointer chain
// leading to the variable's content.
IndirectVariable { alloca: V, address_operations: &'a [i64] }
}

pub enum VariableKind {
ArgumentVariable(usize /*index*/),
LocalVariable,
}


#[derive(Clone, Copy, Debug)]
pub struct MirDebugScope<D> {
pub scope_metadata: Option<D>,
// Start and end offsets of the file to which this DIScope belongs.
// These are used to quickly determine whether some span refers to the same file.
pub file_start_pos: BytePos,
pub file_end_pos: BytePos,
}

impl<D> MirDebugScope<D> {
pub fn is_valid(&self) -> bool {
!self.scope_metadata.is_none()
}
}
22 changes: 15 additions & 7 deletions src/librustc_codegen_ssa/mir/analyze.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ use rustc_index::vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::traversal;
use rustc::session::config::DebugInfo;
use rustc::ty;
use rustc::ty::layout::{LayoutOf, HasTyCtxt};
use syntax_pos::DUMMY_SP;
@@ -21,13 +22,20 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(

analyzer.visit_body(mir);

for (index, (ty, span)) in mir.local_decls.iter()
.map(|l| (l.ty, l.source_info.span))
.enumerate()
for (local, decl) in mir.local_decls.iter_enumerated()
{
let ty = fx.monomorphize(&ty);
debug!("local {} has type {:?}", index, ty);
let layout = fx.cx.spanned_layout_of(ty, span);
// FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
// of putting everything in allocas just so we can use llvm.dbg.declare.
if fx.cx.sess().opts.debuginfo == DebugInfo::Full {
if mir.local_kind(local) == mir::LocalKind::Arg || decl.name.is_some() {
analyzer.not_ssa(local);
continue;
}
}

let ty = fx.monomorphize(&decl.ty);
debug!("local {:?} has type `{}`", local, ty);
let layout = fx.cx.spanned_layout_of(ty, decl.source_info.span);
if fx.cx.is_backend_immediate(layout) {
// These sorts of types are immediates that we can store
// in an Value without an alloca.
@@ -40,7 +48,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// (e.g., structs) into an alloca unconditionally, just so
// that we don't have to deal with having two pathways
// (gep vs extractvalue etc).
analyzer.not_ssa(mir::Local::new(index));
analyzer.not_ssa(local);
}
}

385 changes: 385 additions & 0 deletions src/librustc_codegen_ssa/mir/debuginfo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,385 @@
use rustc_index::vec::{Idx, IndexVec};
use rustc::hir::def_id::CrateNum;
use rustc::mir;
use rustc::session::config::DebugInfo;
use rustc::ty::{self, TyCtxt};
use rustc::ty::layout::{LayoutOf, Size, VariantIdx};
use crate::traits::*;

use syntax_pos::{BytePos, Span, Symbol};
use syntax::symbol::kw;

use super::{FunctionCx, LocalRef};
use super::OperandValue;

pub struct FunctionDebugContext<D> {
pub scopes: IndexVec<mir::SourceScope, DebugScope<D>>,
pub source_locations_enabled: bool,
pub defining_crate: CrateNum,
}

#[derive(Copy, Clone)]
pub enum VariableKind {
ArgumentVariable(usize /*index*/),
LocalVariable,
}

#[derive(Clone, Copy, Debug)]
pub struct DebugScope<D> {
pub scope_metadata: Option<D>,
// Start and end offsets of the file to which this DIScope belongs.
// These are used to quickly determine whether some span refers to the same file.
pub file_start_pos: BytePos,
pub file_end_pos: BytePos,
}

impl<D> DebugScope<D> {
pub fn is_valid(&self) -> bool {
!self.scope_metadata.is_none()
}
}

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub fn set_debug_loc(
&mut self,
bx: &mut Bx,
source_info: mir::SourceInfo
) {
let (scope, span) = self.debug_loc(source_info);
if let Some(debug_context) = &mut self.debug_context {
// FIXME(eddyb) get rid of this unwrap somehow.
bx.set_source_location(debug_context, scope.unwrap(), span);
}
}

pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option<Bx::DIScope>, Span) {
// Bail out if debug info emission is not enabled.
match self.debug_context {
None => return (None, source_info.span),
Some(_) => {}
}

// In order to have a good line stepping behavior in debugger, we overwrite debug
// locations of macro expansions with that of the outermost expansion site
// (unless the crate is being compiled with `-Z debug-macros`).
if !source_info.span.from_expansion() ||
self.cx.sess().opts.debugging_opts.debug_macros {
let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo());
(scope, source_info.span)
} else {
// Walk up the macro expansion chain until we reach a non-expanded span.
// We also stop at the function body level because no line stepping can occur
// at the level above that.
let span = syntax_pos::hygiene::walk_chain(source_info.span, self.mir.span.ctxt());
let scope = self.scope_metadata_for_loc(source_info.scope, span.lo());
// Use span of the outermost expansion site, while keeping the original lexical scope.
(scope, span)
}
}

// DILocations inherit source file name from the parent DIScope. Due to macro expansions
// it may so happen that the current span belongs to a different file than the DIScope
// corresponding to span's containing source scope. If so, we need to create a DIScope
// "extension" into that file.
fn scope_metadata_for_loc(&self, scope_id: mir::SourceScope, pos: BytePos)
-> Option<Bx::DIScope> {
let debug_context = self.debug_context.as_ref()?;
let scope_metadata = debug_context.scopes[scope_id].scope_metadata;
if pos < debug_context.scopes[scope_id].file_start_pos ||
pos >= debug_context.scopes[scope_id].file_end_pos {
let sm = self.cx.sess().source_map();
let defining_crate = debug_context.defining_crate;
Some(self.cx.extend_scope_to_file(
scope_metadata.unwrap(),
&sm.lookup_char_pos(pos).file,
defining_crate
))
} else {
scope_metadata
}
}

/// Apply debuginfo and/or name, after creating the `alloca` for a local,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 for doc-comments

/// or initializing the local with an operand (whichever applies).
// FIXME(eddyb) use `llvm.dbg.value` (which would work for operands),
// not just `llvm.dbg.declare` (which requires `alloca`).
pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
// FIXME(eddyb) maybe name the return place as `_0` or `return`?
if local == mir::RETURN_PLACE {
return;
}

let vars = match &self.per_local_var_debug_info {
Some(per_local) => &per_local[local],
None => return,
};
let whole_local_var = vars.iter().find(|var| {
var.place.projection.is_empty()
});
let has_proj = || vars.iter().any(|var| {
!var.place.projection.is_empty()
});

let (fallback_var, kind) = if self.mir.local_kind(local) == mir::LocalKind::Arg {
let arg_index = local.index() - 1;

// Add debuginfo even to unnamed arguments.
// FIXME(eddyb) is this really needed?
let var = if arg_index == 0 && has_proj() {
// Hide closure environments from debuginfo.
// FIXME(eddyb) shouldn't `ArgumentVariable` indices
// be offset to account for the hidden environment?
None
} else {
Some(VarDebugInfo {
name: kw::Invalid,
source_info: self.mir.local_decls[local].source_info,
place: local.into(),
})
};
(var, VariableKind::ArgumentVariable(arg_index + 1))
} else {
(None, VariableKind::LocalVariable)
};

let local_ref = &self.locals[local];

if !bx.sess().fewer_names() {
let name = match whole_local_var.or(fallback_var.as_ref()) {
Some(var) if var.name != kw::Invalid => var.name.to_string(),
_ => format!("{:?}", local),
};
match local_ref {
LocalRef::Place(place) |
LocalRef::UnsizedPlace(place) => {
bx.set_var_name(place.llval, &name);
}
LocalRef::Operand(Some(operand)) => match operand.val {
OperandValue::Ref(x, ..) |
OperandValue::Immediate(x) => {
bx.set_var_name(x, &name);
}
OperandValue::Pair(a, b) => {
// FIXME(eddyb) these are scalar components,
// maybe extract the high-level fields?
bx.set_var_name(a, &(name.clone() + ".0"));
bx.set_var_name(b, &(name + ".1"));
}
}
LocalRef::Operand(None) => {}
}
}

if bx.sess().opts.debuginfo != DebugInfo::Full {
return;
}

let debug_context = match &self.debug_context {
Some(debug_context) => debug_context,
None => return,
};

// FIXME(eddyb) add debuginfo for unsized places too.
let base = match local_ref {
LocalRef::Place(place) => place,
_ => return,
};

let vars = vars.iter().chain(if whole_local_var.is_none() {
fallback_var.as_ref()
} else {
None
});

for var in vars {
let mut layout = base.layout;
let mut direct_offset = Size::ZERO;
// FIXME(eddyb) use smallvec here.
let mut indirect_offsets = vec![];

let kind = if var.place.projection.is_empty() {
kind
} else {
VariableKind::LocalVariable
};

for elem in &var.place.projection[..] {
match *elem {
mir::ProjectionElem::Deref => {
indirect_offsets.push(Size::ZERO);
layout = bx.cx().layout_of(
layout.ty.builtin_deref(true)
.unwrap_or_else(|| {
span_bug!(
var.source_info.span,
"cannot deref `{}`",
layout.ty,
)
}).ty,
);
}
mir::ProjectionElem::Field(field, _) => {
let i = field.index();
let offset = indirect_offsets.last_mut()
.unwrap_or(&mut direct_offset);
*offset += layout.fields.offset(i);
layout = layout.field(bx.cx(), i);
}
mir::ProjectionElem::Downcast(_, variant) => {
layout = layout.for_variant(bx.cx(), variant);
}
_ => span_bug!(
var.source_info.span,
"unsupported var debuginfo place `{:?}`",
var.place,
),
}
}

let (scope, span) = self.debug_loc(var.source_info);
if let Some(scope) = scope {
bx.declare_local(debug_context, var.name, layout.ty, scope,
base.llval, direct_offset, &indirect_offsets, kind, span);
}
}
}

pub fn debug_introduce_locals(&self, bx: &mut Bx) {
if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
for local in self.locals.indices() {
self.debug_introduce_local(bx, local);
}
}
}
}

pub fn per_local_var_debug_info(
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
) -> Option<IndexVec<mir::Local, Vec<VarDebugInfo<'tcx>>>> {
if tcx.sess.opts.debuginfo == DebugInfo::Full || !tcx.sess.fewer_names() {
let mut per_local = IndexVec::from_elem(vec![], &body.local_decls);
for (local, decl) in body.local_decls.iter_enumerated() {
if let Some(name) = decl.name {
per_local[local].push(VarDebugInfo {
name,
source_info: mir::SourceInfo {
span: decl.source_info.span,
scope: decl.visibility_scope,
},
place: local.into(),
});
}
}

let upvar_debuginfo = &body.__upvar_debuginfo_codegen_only_do_not_use;
if !upvar_debuginfo.is_empty() {

let env_arg = mir::Local::new(1);
let mut env_projs = vec![];

let pin_did = tcx.lang_items().pin_type();
match body.local_decls[env_arg].ty.kind {
ty::RawPtr(_) |
ty::Ref(..) => {
env_projs.push(mir::ProjectionElem::Deref);
}
ty::Adt(def, substs) if Some(def.did) == pin_did => {
if let ty::Ref(..) = substs.type_at(0).kind {
env_projs.push(mir::ProjectionElem::Field(
mir::Field::new(0),
// HACK(eddyb) field types aren't used or needed here.
tcx.types.err,
));
env_projs.push(mir::ProjectionElem::Deref);
}
}
_ => {}
}

let extra_locals = {
let upvars = upvar_debuginfo
.iter()
.enumerate()
.map(|(i, upvar)| {
let source_info = mir::SourceInfo {
span: body.span,
scope: mir::OUTERMOST_SOURCE_SCOPE,
};
(None, i, upvar.debug_name, upvar.by_ref, source_info)
});

let generator_fields = body.generator_layout.as_ref().map(|generator_layout| {
generator_layout.variant_fields.iter()
.enumerate()
.flat_map(move |(variant_idx, fields)| {
let variant_idx = Some(VariantIdx::from(variant_idx));
fields.iter()
.enumerate()
.filter_map(move |(i, field)| {
let decl = &generator_layout.
__local_debuginfo_codegen_only_do_not_use[*field];
if let Some(name) = decl.name {
let source_info = mir::SourceInfo {
span: decl.source_info.span,
scope: decl.visibility_scope,
};
Some((variant_idx, i, name, false, source_info))
} else {
None
}
})
})
}).into_iter().flatten();

upvars.chain(generator_fields)
};

for (variant_idx, field, name, by_ref, source_info) in extra_locals {
let mut projs = env_projs.clone();

if let Some(variant_idx) = variant_idx {
projs.push(mir::ProjectionElem::Downcast(None, variant_idx));
}

projs.push(mir::ProjectionElem::Field(
mir::Field::new(field),
// HACK(eddyb) field types aren't used or needed here.
tcx.types.err,
));

if by_ref {
projs.push(mir::ProjectionElem::Deref);
}

per_local[env_arg].push(VarDebugInfo {
name,
source_info,
place: mir::Place {
base: mir::PlaceBase::Local(env_arg),
projection: tcx.intern_place_elems(&projs),
},
});
}
}

Some(per_local)
} else {
None
}
}

/// Debug information relatating to an user variable.
// FIXME(eddyb) move this to the MIR bodies themselves.
#[derive(Clone)]
pub struct VarDebugInfo<'tcx> {
pub name: Symbol,

/// Source info of the user variable, including the scope
/// within which the variable is visible (to debuginfo)
/// (see `LocalDecl`'s `source_info` field for more details).
pub source_info: mir::SourceInfo,

/// Where the data for this user variable is to be found.
pub place: mir::Place<'tcx>,
}
355 changes: 31 additions & 324 deletions src/librustc_codegen_ssa/mir/mod.rs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/librustc_codegen_ssa/mir/place.rs
Original file line number Diff line number Diff line change
@@ -68,6 +68,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
}
}

// FIXME(eddyb) pass something else for the name so no work is done
// unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`).
pub fn alloca<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx,
layout: TyLayout<'tcx>,
@@ -78,6 +80,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
}

/// Returns a place for an indirect reference to an unsized place.
// FIXME(eddyb) pass something else for the name so no work is done
// unless LLVM IR names are turned on (e.g. for `--emit=llvm-ir`).
pub fn alloca_unsized_indirect<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx,
layout: TyLayout<'tcx>,
15 changes: 1 addition & 14 deletions src/librustc_codegen_ssa/mir/statement.rs
Original file line number Diff line number Diff line change
@@ -27,21 +27,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
LocalRef::Operand(None) => {
let (mut bx, operand) = self.codegen_rvalue_operand(bx, rvalue);
if let Some(name) = self.mir.local_decls[index].name {
match operand.val {
OperandValue::Ref(x, ..) |
OperandValue::Immediate(x) => {
bx.set_var_name(x, name);
}
OperandValue::Pair(a, b) => {
// FIXME(eddyb) these are scalar components,
// maybe extract the high-level fields?
bx.set_var_name(a, format_args!("{}.0", name));
bx.set_var_name(b, format_args!("{}.1", name));
}
}
}
self.locals[index] = LocalRef::Operand(Some(operand));
self.debug_introduce_local(&mut bx, index);
bx
}
LocalRef::Operand(Some(op)) => {
25 changes: 10 additions & 15 deletions src/librustc_codegen_ssa/traits/debuginfo.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use super::BackendTypes;
use crate::debuginfo::{FunctionDebugContext, MirDebugScope, VariableAccess, VariableKind};
use crate::mir::debuginfo::{FunctionDebugContext, VariableKind};
use rustc::hir::def_id::CrateNum;
use rustc::mir;
use rustc::ty::{self, Ty, Instance};
use rustc_index::vec::IndexVec;
use rustc::ty::layout::Size;
use syntax::ast::Name;
use syntax_pos::{SourceFile, Span};

@@ -13,30 +13,22 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes {
/// Creates the function-specific debug context.
///
/// Returns the FunctionDebugContext for the function which holds state needed
/// for debug info creation. The function may also return another variant of the
/// FunctionDebugContext enum which indicates why no debuginfo should be created
/// for the function.
/// for debug info creation, if it is enabled.
fn create_function_debug_context(
&self,
instance: Instance<'tcx>,
sig: ty::FnSig<'tcx>,
llfn: Self::Function,
mir: &mir::Body<'_>,
) -> FunctionDebugContext<Self::DIScope>;
) -> Option<FunctionDebugContext<Self::DIScope>>;

fn create_mir_scopes(
&self,
mir: &mir::Body<'_>,
debug_context: &mut FunctionDebugContext<Self::DIScope>,
) -> IndexVec<mir::SourceScope, MirDebugScope<Self::DIScope>>;
fn extend_scope_to_file(
&self,
scope_metadata: Self::DIScope,
file: &SourceFile,
defining_crate: CrateNum,
) -> Self::DIScope;
fn debuginfo_finalize(&self);
fn debuginfo_upvar_ops_sequence(&self, byte_offset_of_var_in_env: u64) -> [i64; 4];
}

pub trait DebugInfoBuilderMethods<'tcx>: BackendTypes {
@@ -46,16 +38,19 @@ pub trait DebugInfoBuilderMethods<'tcx>: BackendTypes {
variable_name: Name,
variable_type: Ty<'tcx>,
scope_metadata: Self::DIScope,
variable_access: VariableAccess<'_, Self::Value>,
variable_alloca: Self::Value,
direct_offset: Size,
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
indirect_offsets: &[Size],
variable_kind: VariableKind,
span: Span,
);
fn set_source_location(
&mut self,
debug_context: &mut FunctionDebugContext<Self::DIScope>,
scope: Option<Self::DIScope>,
scope: Self::DIScope,
span: Span,
);
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);
fn set_var_name(&mut self, value: Self::Value, name: impl ToString);
fn set_var_name(&mut self, value: Self::Value, name: &str);
}
6 changes: 3 additions & 3 deletions src/test/codegen/optimize-attr-1.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@

// CHECK-LABEL: define i32 @nothing
// CHECK-SAME: [[NOTHING_ATTRS:#[0-9]+]]
// NO-OPT: ret i32 %1
// NO-OPT: ret i32 %_1.0
// SIZE-OPT: ret i32 4
// SPEEC-OPT: ret i32 4
#[no_mangle]
@@ -18,7 +18,7 @@ pub fn nothing() -> i32 {

// CHECK-LABEL: define i32 @size
// CHECK-SAME: [[SIZE_ATTRS:#[0-9]+]]
// NO-OPT: ret i32 %1
// NO-OPT: ret i32 %_1.0
// SIZE-OPT: ret i32 6
// SPEED-OPT: ret i32 6
#[optimize(size)]
@@ -31,7 +31,7 @@ pub fn size() -> i32 {
// NO-OPT-SAME: [[NOTHING_ATTRS]]
// SPEED-OPT-SAME: [[NOTHING_ATTRS]]
// SIZE-OPT-SAME: [[SPEED_ATTRS:#[0-9]+]]
// NO-OPT: ret i32 %1
// NO-OPT: ret i32 %_1.0
// SIZE-OPT: ret i32 8
// SPEED-OPT: ret i32 8
#[optimize(speed)]

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -26,10 +26,16 @@ extern "platform-intrinsic" {
fn simd_bitmask<T, U>(x: T) -> U;
}

// NOTE(eddyb) `%{{x|_2}}` is used because on some targets (e.g. WASM)
// SIMD vectors are passed directly, resulting in `%x` being a vector,
// while on others they're passed indirectly, resulting in `%x` being
// a pointer to a vector, and `%_2` a vector loaded from that pointer.
// This is controlled by the target spec option `simd_types_indirect`.

// CHECK-LABEL: @bitmask_int
#[no_mangle]
pub unsafe fn bitmask_int(x: i32x2) -> u8 {
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{[0-9a-z]+}}, <i32 31, i32 31>
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|_2}}, <i32 31, i32 31>
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2
// CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8
@@ -39,7 +45,7 @@ pub unsafe fn bitmask_int(x: i32x2) -> u8 {
// CHECK-LABEL: @bitmask_uint
#[no_mangle]
pub unsafe fn bitmask_uint(x: u32x2) -> u8 {
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{[0-9a-z]+}}, <i32 31, i32 31>
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|_2}}, <i32 31, i32 31>
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2
// CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8
@@ -49,7 +55,7 @@ pub unsafe fn bitmask_uint(x: u32x2) -> u8 {
// CHECK-LABEL: @bitmask_int16
#[no_mangle]
pub unsafe fn bitmask_int16(x: i8x16) -> u16 {
// CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{[0-9a-z]+}}, <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>
// CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|_2}}, <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>
// CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1>
// CHECK: %{{[0-9]+}} = bitcast <16 x i1> [[B]] to i16
// CHECK-NOT: zext