Skip to content

Commit f5b023b

Browse files
committed
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.
1 parent 1e4ebb0 commit f5b023b

File tree

5 files changed

+4175
-31
lines changed

5 files changed

+4175
-31
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

+38-18
Original file line numberDiff line numberDiff line change
@@ -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,21 +145,28 @@ fn make_mir_scope<'ll, 'tcx>(
137145
},
138146
};
139147

140-
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
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 {
141156
let callsite_span = hygiene::walk_chain_collapsed(callsite_span, mir.span);
142157
let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
143158
let loc = cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span);
144159

145160
// NB: In order to produce proper debug info for variables (particularly
146-
// arguments) in multiply-inline functions, LLVM expects to see a single
161+
// arguments) in multiply-inlined functions, LLVM expects to see a single
147162
// DILocalVariable with multiple different DILocations in the IR. While
148163
// the source information for each DILocation would be identical, their
149164
// inlinedAt attributes will be unique to the particular callsite.
150165
//
151166
// We generate DILocations here based on the callsite's location in the
152167
// source code. A single location in the source code usually can't
153168
// produce multiple distinct calls so this mostly works, until
154-
// proc-macros get involved. A proc-macro can generate multiple calls
169+
// macros get involved. A macro can generate multiple calls
155170
// at the same span, which breaks the assumption that we're going to
156171
// produce a unique DILocation for every scope we process here. We
157172
// have to explicitly add discriminators if we see inlines into the
@@ -160,24 +175,29 @@ fn make_mir_scope<'ll, 'tcx>(
160175
// Note further that we can't key this hashtable on the span itself,
161176
// because these spans could have distinct SyntaxContexts. We have
162177
// to key on exactly what we're giving to LLVM.
163-
match discriminators.entry(callsite_span.lo()) {
178+
let inlined_at = match discriminators.entry(callsite_span.lo()) {
164179
Entry::Occupied(mut o) => {
165180
*o.get_mut() += 1;
166181
unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
167-
.expect("Failed to encode discriminator in DILocation")
168182
}
169183
Entry::Vacant(v) => {
170184
v.insert(0);
171-
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;
172197
}
173198
}
174-
});
199+
}
175200

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

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)