mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 14:23:45 +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 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]
|
||||
debug_context.scopes[parent].unwrap()
|
||||
} else {
|
||||
// The root is the function itself.
|
||||
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_end_pos: file.end_position(),
|
||||
..debug_context.scopes[scope]
|
||||
};
|
||||
..debug_context.scopes[scope].unwrap()
|
||||
});
|
||||
instantiated.insert(scope);
|
||||
return;
|
||||
};
|
||||
@ -130,7 +130,7 @@ fn make_mir_scope<'gcc, 'tcx>(
|
||||
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;
|
||||
debug_context.scopes[scope] = Some(parent_scope);
|
||||
instantiated.insert(scope);
|
||||
return;
|
||||
}
|
||||
@ -157,12 +157,12 @@ fn make_mir_scope<'gcc, 'tcx>(
|
||||
// TODO(tempdragon): dbg_scope: Add support for scope extension here.
|
||||
inlined_at.or(p_inlined_at);
|
||||
|
||||
debug_context.scopes[scope] = DebugScope {
|
||||
debug_context.scopes[scope] = Some(DebugScope {
|
||||
dbg_scope,
|
||||
inlined_at,
|
||||
file_start_pos: loc.file.start_pos,
|
||||
file_end_pos: loc.file.end_position(),
|
||||
};
|
||||
});
|
||||
instantiated.insert(scope);
|
||||
}
|
||||
|
||||
@ -232,12 +232,12 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
}
|
||||
|
||||
// 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)),
|
||||
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(),
|
||||
|
@ -85,15 +85,23 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
discriminators,
|
||||
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 {
|
||||
// The root is the function itself.
|
||||
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_end_pos: file.end_position(),
|
||||
..debug_context.scopes[scope]
|
||||
};
|
||||
..debug_context.scopes[scope].unwrap()
|
||||
});
|
||||
instantiated.insert(scope);
|
||||
return;
|
||||
};
|
||||
@ -104,7 +112,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||
{
|
||||
// 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;
|
||||
debug_context.scopes[scope] = Some(parent_scope);
|
||||
instantiated.insert(scope);
|
||||
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_scope = parent_scope.adjust_dbg_scope_for_span(cx, 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
|
||||
// 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
|
||||
// the source information for each DILocation would be identical, their
|
||||
// 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
|
||||
// source code. A single location in the source code usually can't
|
||||
// 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
|
||||
// produce a unique DILocation for every scope we process here. We
|
||||
// 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,
|
||||
// because these spans could have distinct SyntaxContexts. We have
|
||||
// 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) => {
|
||||
*o.get_mut() += 1;
|
||||
unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
|
||||
.expect("Failed to encode discriminator in DILocation")
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
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 {
|
||||
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(),
|
||||
};
|
||||
debug_context.scopes[scope] = debug_scope;
|
||||
instantiated.insert(scope);
|
||||
}
|
||||
|
@ -294,12 +294,12 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
}
|
||||
|
||||
// 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)),
|
||||
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),
|
||||
inlined_function_scopes: Default::default(),
|
||||
|
@ -20,7 +20,9 @@ use crate::traits::*;
|
||||
|
||||
pub struct FunctionDebugContext<'tcx, S, L> {
|
||||
/// 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.
|
||||
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
|
||||
@ -231,7 +233,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
&self,
|
||||
source_info: mir::SourceInfo,
|
||||
) -> 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);
|
||||
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