Skip to content

Commit bcfea1f

Browse files
committed
Auto merge of #133194 - khuey:master, r=jieyouxu
Drop debug info instead of panicking if we exceed LLVM's capability to represent it Recapping a bit of history here: In #128861 I made debug info correctly represent parameters to inline functions by removing a fake lexical block that had been inserted to suppress LLVM assertions and by deduplicating those parameters. LLVM, however, expects to see a single parameter _with distinct locations_, particularly distinct inlinedAt values on the DILocations. This generally worked because no matter how deep the chain of inlines it takes two different call sites in the original function to result in the same function being present multiple times, and a function call requires a non-zero number of characters, but macros threw a wrench in that in #131944. At the time I thought the issue there was limited to proc-macros, where an arbitrary amount of code can be generated at a single point in the source text. In #132613 I added discriminators to DILocations that would otherwise be the same to repair #131944[^1]. This works, but LLVM's capacity for discriminators is not infinite (LLVM actually only allocates 12 bits for this internally). At the time I thought it would be very rare for anyone to hit the limit, but #132900 proved me wrong. In the relatively-minimized test case it also became clear to me that the issue affects regular macros too, because the call to the inlined function will (without collapse_debuginfo on the macro) be attributed to the (repeated, if the macro is used more than once) textual callsite in the macro definition. This PR fixes the panic by dropping debug info when we exceed LLVM's maximum discriminator value. There's also a preceding commit for a related but distinct issue: macros that use collapse_debuginfo should in fact have their inlinedAts collapsed to the macro callsite and thus not need discriminators at all (and not panic/warn accordingly when the discriminator limit is exhausted). Fixes #132900 r? `@jieyouxu` [^1]: Editor's note: `fix` is a magic keyword in PR description that apparently will close the linked issue (it's closed already in this case, but still).
2 parents 875df37 + f5b023b commit bcfea1f

File tree

6 files changed

+8301
-34
lines changed

6 files changed

+8301
-34
lines changed

compiler/rustc_codegen_gcc/src/debuginfo.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,15 @@ fn make_mir_scope<'gcc, 'tcx>(
113113
let scope_data = &mir.source_scopes[scope];
114114
let parent_scope = if let Some(parent) = scope_data.parent_scope {
115115
make_mir_scope(cx, _instance, mir, variables, debug_context, instantiated, parent);
116-
debug_context.scopes[parent]
116+
debug_context.scopes[parent].unwrap()
117117
} else {
118118
// The root is the function itself.
119119
let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
120-
debug_context.scopes[scope] = DebugScope {
120+
debug_context.scopes[scope] = Some(DebugScope {
121121
file_start_pos: file.start_pos,
122122
file_end_pos: file.end_position(),
123-
..debug_context.scopes[scope]
124-
};
123+
..debug_context.scopes[scope].unwrap()
124+
});
125125
instantiated.insert(scope);
126126
return;
127127
};
@@ -130,7 +130,7 @@ fn make_mir_scope<'gcc, 'tcx>(
130130
if !vars.contains(scope) && scope_data.inlined.is_none() {
131131
// Do not create a DIScope if there are no variables defined in this
132132
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
133-
debug_context.scopes[scope] = parent_scope;
133+
debug_context.scopes[scope] = Some(parent_scope);
134134
instantiated.insert(scope);
135135
return;
136136
}
@@ -157,12 +157,12 @@ fn make_mir_scope<'gcc, 'tcx>(
157157
// TODO(tempdragon): dbg_scope: Add support for scope extension here.
158158
inlined_at.or(p_inlined_at);
159159

160-
debug_context.scopes[scope] = DebugScope {
160+
debug_context.scopes[scope] = Some(DebugScope {
161161
dbg_scope,
162162
inlined_at,
163163
file_start_pos: loc.file.start_pos,
164164
file_end_pos: loc.file.end_position(),
165-
};
165+
});
166166
instantiated.insert(scope);
167167
}
168168

@@ -232,12 +232,12 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
232232
}
233233

234234
// Initialize fn debug context (including scopes).
235-
let empty_scope = DebugScope {
235+
let empty_scope = Some(DebugScope {
236236
dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
237237
inlined_at: None,
238238
file_start_pos: BytePos(0),
239239
file_end_pos: BytePos(0),
240-
};
240+
});
241241
let mut fn_debug_context = FunctionDebugContext {
242242
scopes: IndexVec::from_elem(empty_scope, mir.source_scopes.as_slice()),
243243
inlined_function_scopes: Default::default(),

compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs

+40-21
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_middle::mir::{Body, SourceScope};
99
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv};
1010
use rustc_middle::ty::{self, Instance};
1111
use rustc_session::config::DebugInfo;
12-
use rustc_span::BytePos;
12+
use rustc_span::{BytePos, hygiene};
1313

1414
use super::metadata::file_metadata;
1515
use super::utils::DIB;
@@ -85,15 +85,23 @@ fn make_mir_scope<'ll, 'tcx>(
8585
discriminators,
8686
parent,
8787
);
88-
debug_context.scopes[parent]
88+
if let Some(parent_scope) = debug_context.scopes[parent] {
89+
parent_scope
90+
} else {
91+
// If the parent scope could not be represented then no children
92+
// can be either.
93+
debug_context.scopes[scope] = None;
94+
instantiated.insert(scope);
95+
return;
96+
}
8997
} else {
9098
// The root is the function itself.
9199
let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
92-
debug_context.scopes[scope] = DebugScope {
100+
debug_context.scopes[scope] = Some(DebugScope {
93101
file_start_pos: file.start_pos,
94102
file_end_pos: file.end_position(),
95-
..debug_context.scopes[scope]
96-
};
103+
..debug_context.scopes[scope].unwrap()
104+
});
97105
instantiated.insert(scope);
98106
return;
99107
};
@@ -104,7 +112,7 @@ fn make_mir_scope<'ll, 'tcx>(
104112
{
105113
// Do not create a DIScope if there are no variables defined in this
106114
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
107-
debug_context.scopes[scope] = parent_scope;
115+
debug_context.scopes[scope] = Some(parent_scope);
108116
instantiated.insert(scope);
109117
return;
110118
}
@@ -137,22 +145,28 @@ fn make_mir_scope<'ll, 'tcx>(
137145
},
138146
};
139147

140-
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
141-
// FIXME(eddyb) this doesn't account for the macro-related
142-
// `Span` fixups that `rustc_codegen_ssa::mir::debuginfo` does.
148+
let mut debug_scope = Some(DebugScope {
149+
dbg_scope,
150+
inlined_at: parent_scope.inlined_at,
151+
file_start_pos: loc.file.start_pos,
152+
file_end_pos: loc.file.end_position(),
153+
});
154+
155+
if let Some((_, callsite_span)) = scope_data.inlined {
156+
let callsite_span = hygiene::walk_chain_collapsed(callsite_span, mir.span);
143157
let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
144158
let loc = cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span);
145159

146160
// NB: In order to produce proper debug info for variables (particularly
147-
// arguments) in multiply-inline functions, LLVM expects to see a single
161+
// arguments) in multiply-inlined functions, LLVM expects to see a single
148162
// DILocalVariable with multiple different DILocations in the IR. While
149163
// the source information for each DILocation would be identical, their
150164
// inlinedAt attributes will be unique to the particular callsite.
151165
//
152166
// We generate DILocations here based on the callsite's location in the
153167
// source code. A single location in the source code usually can't
154168
// produce multiple distinct calls so this mostly works, until
155-
// proc-macros get involved. A proc-macro can generate multiple calls
169+
// macros get involved. A macro can generate multiple calls
156170
// at the same span, which breaks the assumption that we're going to
157171
// produce a unique DILocation for every scope we process here. We
158172
// have to explicitly add discriminators if we see inlines into the
@@ -161,24 +175,29 @@ fn make_mir_scope<'ll, 'tcx>(
161175
// Note further that we can't key this hashtable on the span itself,
162176
// because these spans could have distinct SyntaxContexts. We have
163177
// to key on exactly what we're giving to LLVM.
164-
match discriminators.entry(callsite_span.lo()) {
178+
let inlined_at = match discriminators.entry(callsite_span.lo()) {
165179
Entry::Occupied(mut o) => {
166180
*o.get_mut() += 1;
167181
unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
168-
.expect("Failed to encode discriminator in DILocation")
169182
}
170183
Entry::Vacant(v) => {
171184
v.insert(0);
172-
loc
185+
Some(loc)
186+
}
187+
};
188+
match inlined_at {
189+
Some(inlined_at) => {
190+
debug_scope.as_mut().unwrap().inlined_at = Some(inlined_at);
191+
}
192+
None => {
193+
// LLVM has a maximum discriminator that it can encode (currently
194+
// it uses 12 bits for 4096 possible values). If we exceed that
195+
// there is little we can do but drop the debug info.
196+
debug_scope = None;
173197
}
174198
}
175-
});
199+
}
176200

177-
debug_context.scopes[scope] = DebugScope {
178-
dbg_scope,
179-
inlined_at: inlined_at.or(parent_scope.inlined_at),
180-
file_start_pos: loc.file.start_pos,
181-
file_end_pos: loc.file.end_position(),
182-
};
201+
debug_context.scopes[scope] = debug_scope;
183202
instantiated.insert(scope);
184203
}

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,12 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
294294
}
295295

296296
// Initialize fn debug context (including scopes).
297-
let empty_scope = DebugScope {
297+
let empty_scope = Some(DebugScope {
298298
dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
299299
inlined_at: None,
300300
file_start_pos: BytePos(0),
301301
file_end_pos: BytePos(0),
302-
};
302+
});
303303
let mut fn_debug_context = FunctionDebugContext {
304304
scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes),
305305
inlined_function_scopes: Default::default(),

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ use crate::traits::*;
2020

2121
pub struct FunctionDebugContext<'tcx, S, L> {
2222
/// Maps from source code to the corresponding debug info scope.
23-
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
23+
/// May be None if the backend is not capable of representing the scope for
24+
/// some reason.
25+
pub scopes: IndexVec<mir::SourceScope, Option<DebugScope<S, L>>>,
2426

2527
/// Maps from an inlined function to its debug info declaration.
2628
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
@@ -231,7 +233,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
231233
&self,
232234
source_info: mir::SourceInfo,
233235
) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
234-
let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
236+
let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]?;
235237
let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span);
236238
Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
237239
}

0 commit comments

Comments
 (0)