When LLVM's location discriminator value limit is exceeded, emit locations with dummy spans instead of dropping them entirely
Revert most of #133194 (except the test and the comment fixes). Then refix not emitting locations at all when the correct location discriminator value exceeds LLVM's capacity.
This commit is contained in:
parent
af952c1abe
commit
45ef92731b
8 changed files with 111 additions and 52 deletions
|
@ -113,15 +113,15 @@ fn make_mir_scope<'gcc, 'tcx>(
|
||||||
let scope_data = &mir.source_scopes[scope];
|
let scope_data = &mir.source_scopes[scope];
|
||||||
let parent_scope = if let Some(parent) = scope_data.parent_scope {
|
let parent_scope = if let Some(parent) = scope_data.parent_scope {
|
||||||
make_mir_scope(cx, _instance, mir, variables, debug_context, instantiated, parent);
|
make_mir_scope(cx, _instance, mir, variables, debug_context, instantiated, parent);
|
||||||
debug_context.scopes[parent].unwrap()
|
debug_context.scopes[parent]
|
||||||
} else {
|
} else {
|
||||||
// The root is the function itself.
|
// The root is the function itself.
|
||||||
let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
|
let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
|
||||||
debug_context.scopes[scope] = Some(DebugScope {
|
debug_context.scopes[scope] = DebugScope {
|
||||||
file_start_pos: file.start_pos,
|
file_start_pos: file.start_pos,
|
||||||
file_end_pos: file.end_position(),
|
file_end_pos: file.end_position(),
|
||||||
..debug_context.scopes[scope].unwrap()
|
..debug_context.scopes[scope]
|
||||||
});
|
};
|
||||||
instantiated.insert(scope);
|
instantiated.insert(scope);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -130,7 +130,7 @@ fn make_mir_scope<'gcc, 'tcx>(
|
||||||
if !vars.contains(scope) && scope_data.inlined.is_none() {
|
if !vars.contains(scope) && scope_data.inlined.is_none() {
|
||||||
// Do not create a DIScope if there are no variables defined in this
|
// Do not create a DIScope if there are no variables defined in this
|
||||||
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
|
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
|
||||||
debug_context.scopes[scope] = Some(parent_scope);
|
debug_context.scopes[scope] = parent_scope;
|
||||||
instantiated.insert(scope);
|
instantiated.insert(scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -157,12 +157,12 @@ fn make_mir_scope<'gcc, 'tcx>(
|
||||||
// TODO(tempdragon): dbg_scope: Add support for scope extension here.
|
// TODO(tempdragon): dbg_scope: Add support for scope extension here.
|
||||||
inlined_at.or(p_inlined_at);
|
inlined_at.or(p_inlined_at);
|
||||||
|
|
||||||
debug_context.scopes[scope] = Some(DebugScope {
|
debug_context.scopes[scope] = DebugScope {
|
||||||
dbg_scope,
|
dbg_scope,
|
||||||
inlined_at,
|
inlined_at,
|
||||||
file_start_pos: loc.file.start_pos,
|
file_start_pos: loc.file.start_pos,
|
||||||
file_end_pos: loc.file.end_position(),
|
file_end_pos: loc.file.end_position(),
|
||||||
});
|
};
|
||||||
instantiated.insert(scope);
|
instantiated.insert(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,12 +232,12 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize fn debug context (including scopes).
|
// Initialize fn debug context (including scopes).
|
||||||
let empty_scope = Some(DebugScope {
|
let empty_scope = DebugScope {
|
||||||
dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
|
dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
|
||||||
inlined_at: None,
|
inlined_at: None,
|
||||||
file_start_pos: BytePos(0),
|
file_start_pos: BytePos(0),
|
||||||
file_end_pos: BytePos(0),
|
file_end_pos: BytePos(0),
|
||||||
});
|
};
|
||||||
let mut fn_debug_context = FunctionDebugContext {
|
let mut fn_debug_context = FunctionDebugContext {
|
||||||
scopes: IndexVec::from_elem(empty_scope, mir.source_scopes.as_slice()),
|
scopes: IndexVec::from_elem(empty_scope, mir.source_scopes.as_slice()),
|
||||||
inlined_function_scopes: Default::default(),
|
inlined_function_scopes: Default::default(),
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rustc_middle::mir::{Body, SourceScope};
|
||||||
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv};
|
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv};
|
||||||
use rustc_middle::ty::{self, Instance};
|
use rustc_middle::ty::{self, Instance};
|
||||||
use rustc_session::config::DebugInfo;
|
use rustc_session::config::DebugInfo;
|
||||||
use rustc_span::{BytePos, hygiene};
|
use rustc_span::{BytePos, DUMMY_SP, hygiene};
|
||||||
|
|
||||||
use super::metadata::file_metadata;
|
use super::metadata::file_metadata;
|
||||||
use super::utils::DIB;
|
use super::utils::DIB;
|
||||||
|
@ -85,23 +85,15 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||||
discriminators,
|
discriminators,
|
||||||
parent,
|
parent,
|
||||||
);
|
);
|
||||||
if let Some(parent_scope) = debug_context.scopes[parent] {
|
debug_context.scopes[parent]
|
||||||
parent_scope
|
|
||||||
} else {
|
|
||||||
// If the parent scope could not be represented then no children
|
|
||||||
// can be either.
|
|
||||||
debug_context.scopes[scope] = None;
|
|
||||||
instantiated.insert(scope);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// The root is the function itself.
|
// The root is the function itself.
|
||||||
let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
|
let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
|
||||||
debug_context.scopes[scope] = Some(DebugScope {
|
debug_context.scopes[scope] = DebugScope {
|
||||||
file_start_pos: file.start_pos,
|
file_start_pos: file.start_pos,
|
||||||
file_end_pos: file.end_position(),
|
file_end_pos: file.end_position(),
|
||||||
..debug_context.scopes[scope].unwrap()
|
..debug_context.scopes[scope]
|
||||||
});
|
};
|
||||||
instantiated.insert(scope);
|
instantiated.insert(scope);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -112,7 +104,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||||
{
|
{
|
||||||
// Do not create a DIScope if there are no variables defined in this
|
// Do not create a DIScope if there are no variables defined in this
|
||||||
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
|
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
|
||||||
debug_context.scopes[scope] = Some(parent_scope);
|
debug_context.scopes[scope] = parent_scope;
|
||||||
instantiated.insert(scope);
|
instantiated.insert(scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -145,14 +137,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut debug_scope = Some(DebugScope {
|
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
|
||||||
dbg_scope,
|
|
||||||
inlined_at: parent_scope.inlined_at,
|
|
||||||
file_start_pos: loc.file.start_pos,
|
|
||||||
file_end_pos: loc.file.end_position(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some((_, callsite_span)) = scope_data.inlined {
|
|
||||||
let callsite_span = hygiene::walk_chain_collapsed(callsite_span, mir.span);
|
let callsite_span = hygiene::walk_chain_collapsed(callsite_span, mir.span);
|
||||||
let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
|
let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
|
||||||
let loc = cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span);
|
let loc = cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span);
|
||||||
|
@ -175,29 +160,29 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||||
// Note further that we can't key this hashtable on the span itself,
|
// Note further that we can't key this hashtable on the span itself,
|
||||||
// because these spans could have distinct SyntaxContexts. We have
|
// because these spans could have distinct SyntaxContexts. We have
|
||||||
// to key on exactly what we're giving to LLVM.
|
// to key on exactly what we're giving to LLVM.
|
||||||
let inlined_at = match discriminators.entry(callsite_span.lo()) {
|
match discriminators.entry(callsite_span.lo()) {
|
||||||
Entry::Occupied(mut o) => {
|
Entry::Occupied(mut o) => {
|
||||||
*o.get_mut() += 1;
|
*o.get_mut() += 1;
|
||||||
|
// NB: We have to emit *something* here or we'll fail LLVM IR verification
|
||||||
|
// in at least some circumstances (see issue #135322) so if the required
|
||||||
|
// discriminant cannot be encoded fall back to the dummy location.
|
||||||
unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
|
unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
cx.dbg_loc(callsite_scope, parent_scope.inlined_at, DUMMY_SP)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Entry::Vacant(v) => {
|
Entry::Vacant(v) => {
|
||||||
v.insert(0);
|
v.insert(0);
|
||||||
Some(loc)
|
loc
|
||||||
}
|
|
||||||
};
|
|
||||||
match inlined_at {
|
|
||||||
Some(inlined_at) => {
|
|
||||||
debug_scope.as_mut().unwrap().inlined_at = Some(inlined_at);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// LLVM has a maximum discriminator that it can encode (currently
|
|
||||||
// it uses 12 bits for 4096 possible values). If we exceed that
|
|
||||||
// there is little we can do but drop the debug info.
|
|
||||||
debug_scope = None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
debug_context.scopes[scope] = debug_scope;
|
debug_context.scopes[scope] = DebugScope {
|
||||||
|
dbg_scope,
|
||||||
|
inlined_at: inlined_at.or(parent_scope.inlined_at),
|
||||||
|
file_start_pos: loc.file.start_pos,
|
||||||
|
file_end_pos: loc.file.end_position(),
|
||||||
|
};
|
||||||
instantiated.insert(scope);
|
instantiated.insert(scope);
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,12 +295,12 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize fn debug context (including scopes).
|
// Initialize fn debug context (including scopes).
|
||||||
let empty_scope = Some(DebugScope {
|
let empty_scope = DebugScope {
|
||||||
dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
|
dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
|
||||||
inlined_at: None,
|
inlined_at: None,
|
||||||
file_start_pos: BytePos(0),
|
file_start_pos: BytePos(0),
|
||||||
file_end_pos: BytePos(0),
|
file_end_pos: BytePos(0),
|
||||||
});
|
};
|
||||||
let mut fn_debug_context = FunctionDebugContext {
|
let mut fn_debug_context = FunctionDebugContext {
|
||||||
scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes),
|
scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes),
|
||||||
inlined_function_scopes: Default::default(),
|
inlined_function_scopes: Default::default(),
|
||||||
|
|
|
@ -19,9 +19,7 @@ use crate::traits::*;
|
||||||
|
|
||||||
pub struct FunctionDebugContext<'tcx, S, L> {
|
pub struct FunctionDebugContext<'tcx, S, L> {
|
||||||
/// Maps from source code to the corresponding debug info scope.
|
/// Maps from source code to the corresponding debug info scope.
|
||||||
/// May be None if the backend is not capable of representing the scope for
|
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
|
||||||
/// some reason.
|
|
||||||
pub scopes: IndexVec<mir::SourceScope, Option<DebugScope<S, L>>>,
|
|
||||||
|
|
||||||
/// Maps from an inlined function to its debug info declaration.
|
/// Maps from an inlined function to its debug info declaration.
|
||||||
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
|
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
|
||||||
|
@ -232,7 +230,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
&self,
|
&self,
|
||||||
source_info: mir::SourceInfo,
|
source_info: mir::SourceInfo,
|
||||||
) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
|
) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
|
||||||
let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]?;
|
let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
|
||||||
let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span);
|
let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span);
|
||||||
Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
|
Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
other::big_function();
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
proc::declare_big_function!();
|
|
@ -0,0 +1,7 @@
|
||||||
|
extern crate proc_macro;
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn declare_big_function(_input: TokenStream) -> TokenStream {
|
||||||
|
include_str!("./generated.rs").parse().unwrap()
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
//! Regression test for <https://github.com/rust-lang/rust/issues/135332>.
|
||||||
|
//!
|
||||||
|
//! We can't simply drop debuginfo location spans when LLVM's location discriminator value limit is
|
||||||
|
//! reached. Otherwise, with `-Z verify-llvm-ir` and fat LTO, LLVM will report a broken module for
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! inlinable function call in a function with debug info must have a !dbg location
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
//@ ignore-cross-compile
|
||||||
|
//@ needs-dynamic-linking
|
||||||
|
//@ only-nightly (requires unstable rustc flag)
|
||||||
|
|
||||||
|
#![deny(warnings)]
|
||||||
|
|
||||||
|
use run_make_support::{dynamic_lib_name, rfs, rust_lib_name, rustc};
|
||||||
|
|
||||||
|
// Synthesize a function that will have a large (`n`) number of functions
|
||||||
|
// MIR-inlined into it. When combined with a proc-macro, all of these inline
|
||||||
|
// callsites will have the same span, forcing rustc to use the DWARF
|
||||||
|
// discriminator to distinguish between them. LLVM's capacity to store that
|
||||||
|
// discriminator is not infinite (currently it allocates 12 bits for a
|
||||||
|
// maximum value of 4096) so if this function gets big enough rustc's error
|
||||||
|
// handling path will be exercised.
|
||||||
|
fn generate_program(n: u32) -> String {
|
||||||
|
let mut program = String::from("pub type BigType = Vec<Vec<String>>;\n\n");
|
||||||
|
program.push_str("pub fn big_function() -> BigType {\n");
|
||||||
|
program.push_str(" vec![\n");
|
||||||
|
for i in 1..=n {
|
||||||
|
program.push_str(&format!("vec![\"string{}\".to_owned()],\n", i));
|
||||||
|
}
|
||||||
|
program.push_str(" ]\n");
|
||||||
|
program.push_str("}\n");
|
||||||
|
program
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// The reported threshold is around 1366 (4096/3), but let's bump it to
|
||||||
|
// around 1500 to be less sensitive.
|
||||||
|
rfs::write("generated.rs", generate_program(1500));
|
||||||
|
|
||||||
|
rustc()
|
||||||
|
.input("proc.rs")
|
||||||
|
.crate_type("proc-macro")
|
||||||
|
.edition("2021")
|
||||||
|
.arg("-Cdebuginfo=line-tables-only")
|
||||||
|
.run();
|
||||||
|
rustc()
|
||||||
|
.extern_("proc", dynamic_lib_name("proc"))
|
||||||
|
.input("other.rs")
|
||||||
|
.crate_type("rlib")
|
||||||
|
.edition("2021")
|
||||||
|
.opt_level("3")
|
||||||
|
.arg("-Cdebuginfo=line-tables-only")
|
||||||
|
.run();
|
||||||
|
rustc()
|
||||||
|
.extern_("other", rust_lib_name("other"))
|
||||||
|
.input("main.rs")
|
||||||
|
.edition("2021")
|
||||||
|
.opt_level("3")
|
||||||
|
.arg("-Cdebuginfo=line-tables-only")
|
||||||
|
.arg("-Clto=fat")
|
||||||
|
.arg("-Zverify-llvm-ir")
|
||||||
|
.run();
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue