mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 00:03:43 +00:00
When the required discriminator value exceeds LLVM's limits, drop the debug info for the function instead of panicking.
The maximum discriminator value LLVM can currently encode is 2^12. If macro use results in more than 2^12 calls to the same function attributed to the same callsite, and those calls are MIR-inlined, we will require more than the maximum discriminator value to completely represent the debug information. Once we reach that point drop the debug info instead.
This commit is contained in:
parent
1e4ebb0ccd
commit
f5b023bd9c
@ -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]
|
debug_context.scopes[parent].unwrap()
|
||||||
} 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] = DebugScope {
|
debug_context.scopes[scope] = Some(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]
|
..debug_context.scopes[scope].unwrap()
|
||||||
};
|
});
|
||||||
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] = parent_scope;
|
debug_context.scopes[scope] = Some(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] = DebugScope {
|
debug_context.scopes[scope] = Some(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 = DebugScope {
|
let empty_scope = Some(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(),
|
||||||
|
@ -85,15 +85,23 @@ fn make_mir_scope<'ll, 'tcx>(
|
|||||||
discriminators,
|
discriminators,
|
||||||
parent,
|
parent,
|
||||||
);
|
);
|
||||||
debug_context.scopes[parent]
|
if let Some(parent_scope) = 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] = DebugScope {
|
debug_context.scopes[scope] = Some(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]
|
..debug_context.scopes[scope].unwrap()
|
||||||
};
|
});
|
||||||
instantiated.insert(scope);
|
instantiated.insert(scope);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -104,7 +112,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] = parent_scope;
|
debug_context.scopes[scope] = Some(parent_scope);
|
||||||
instantiated.insert(scope);
|
instantiated.insert(scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -137,13 +145,20 @@ fn make_mir_scope<'ll, 'tcx>(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
|
let mut debug_scope = Some(DebugScope {
|
||||||
|
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);
|
||||||
|
|
||||||
// NB: In order to produce proper debug info for variables (particularly
|
// NB: In order to produce proper debug info for variables (particularly
|
||||||
// arguments) in multiply-inline functions, LLVM expects to see a single
|
// arguments) in multiply-inlined functions, LLVM expects to see a single
|
||||||
// DILocalVariable with multiple different DILocations in the IR. While
|
// DILocalVariable with multiple different DILocations in the IR. While
|
||||||
// the source information for each DILocation would be identical, their
|
// the source information for each DILocation would be identical, their
|
||||||
// inlinedAt attributes will be unique to the particular callsite.
|
// inlinedAt attributes will be unique to the particular callsite.
|
||||||
@ -151,7 +166,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
|||||||
// We generate DILocations here based on the callsite's location in the
|
// We generate DILocations here based on the callsite's location in the
|
||||||
// source code. A single location in the source code usually can't
|
// source code. A single location in the source code usually can't
|
||||||
// produce multiple distinct calls so this mostly works, until
|
// produce multiple distinct calls so this mostly works, until
|
||||||
// proc-macros get involved. A proc-macro can generate multiple calls
|
// macros get involved. A macro can generate multiple calls
|
||||||
// at the same span, which breaks the assumption that we're going to
|
// at the same span, which breaks the assumption that we're going to
|
||||||
// produce a unique DILocation for every scope we process here. We
|
// produce a unique DILocation for every scope we process here. We
|
||||||
// have to explicitly add discriminators if we see inlines into the
|
// have to explicitly add discriminators if we see inlines into the
|
||||||
@ -160,24 +175,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.
|
||||||
match discriminators.entry(callsite_span.lo()) {
|
let inlined_at = match discriminators.entry(callsite_span.lo()) {
|
||||||
Entry::Occupied(mut o) => {
|
Entry::Occupied(mut o) => {
|
||||||
*o.get_mut() += 1;
|
*o.get_mut() += 1;
|
||||||
unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
|
unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
|
||||||
.expect("Failed to encode discriminator in DILocation")
|
|
||||||
}
|
}
|
||||||
Entry::Vacant(v) => {
|
Entry::Vacant(v) => {
|
||||||
v.insert(0);
|
v.insert(0);
|
||||||
loc
|
Some(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] = DebugScope {
|
debug_context.scopes[scope] = debug_scope;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -294,12 +294,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 = DebugScope {
|
let empty_scope = Some(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(),
|
||||||
|
@ -20,7 +20,9 @@ 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.
|
||||||
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
|
/// May be None if the backend is not capable of representing the scope for
|
||||||
|
/// 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>,
|
||||||
@ -231,7 +233,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))
|
||||||
}
|
}
|
||||||
|
4122
tests/ui/debuginfo/debuginfo-inline-callsite-location-macro-2.rs
Normal file
4122
tests/ui/debuginfo/debuginfo-inline-callsite-location-macro-2.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user