Skip to content

Commit 5c579a4

Browse files
Rollup merge of rust-lang#109466 - davidlattimore:inline-arg-via-var-debug-info, r=wesleywiser
Preserve argument indexes when inlining MIR We store argument indexes on VarDebugInfo. Unlike the previous method of relying on the variable index to know whether a variable is an argument, this survives MIR inlining. We also no longer check if var.source_info.scope is the outermost scope. When a function gets inlined, the arguments to the inner function will no longer be in the outermost scope. What we care about though is whether they were in the outermost scope prior to inlining, which we know by whether we assigned an argument index. Fixes rust-lang#83217 I considered using `Option<NonZeroU16>` instead of `Option<u16>` to store the index. I didn't because `TypeFoldable` isn't implemented for `NonZeroU16` and because it looks like due to padding, it currently wouldn't make any difference. But I indexed from 1 anyway because (a) it'll make it easier if later it becomes worthwhile to use a `NonZeroU16` and because the arguments were previously indexed from 1, so it made for a smaller change. This is my first PR on rust-lang/rust, so apologies if I've gotten anything not quite right.
2 parents e7271f4 + a629267 commit 5c579a4

File tree

8 files changed

+67
-7
lines changed

8 files changed

+67
-7
lines changed

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -442,11 +442,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
442442
let (var_ty, var_kind) = match var.value {
443443
mir::VarDebugInfoContents::Place(place) => {
444444
let var_ty = self.monomorphized_place_ty(place.as_ref());
445-
let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
445+
let var_kind = if let Some(arg_index) = var.argument_index
446446
&& place.projection.is_empty()
447-
&& var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
448447
{
449-
let arg_index = place.local.index() - 1;
448+
let arg_index = arg_index as usize;
450449
if target_is_msvc {
451450
// ScalarPair parameters are spilled to the stack so they need to
452451
// be marked as a `LocalVariable` for MSVC debuggers to visualize
@@ -455,13 +454,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
455454
if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
456455
VariableKind::LocalVariable
457456
} else {
458-
VariableKind::ArgumentVariable(arg_index + 1)
457+
VariableKind::ArgumentVariable(arg_index)
459458
}
460459
} else {
461460
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
462461
// offset in closures to account for the hidden environment?
463-
// Also, is this `+ 1` needed at all?
464-
VariableKind::ArgumentVariable(arg_index + 1)
462+
VariableKind::ArgumentVariable(arg_index)
465463
}
466464
} else {
467465
VariableKind::LocalVariable

compiler/rustc_middle/src/mir/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,11 @@ pub struct VarDebugInfo<'tcx> {
11151115

11161116
/// Where the data for this user variable is to be found.
11171117
pub value: VarDebugInfoContents<'tcx>,
1118+
1119+
/// When present, indicates what argument number this variable is in the function that it
1120+
/// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the
1121+
/// argument number in the original function before it was inlined.
1122+
pub argument_index: Option<u16>,
11181123
}
11191124

11201125
///////////////////////////////////////////////////////////////////////////

compiler/rustc_middle/src/mir/visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,7 @@ macro_rules! make_mir_visitor {
832832
name: _,
833833
source_info,
834834
value,
835+
argument_index: _,
835836
} = var_debug_info;
836837

837838
self.visit_source_info(source_info);

compiler/rustc_mir_build/src/build/matches/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2242,6 +2242,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22422242
name,
22432243
source_info: debug_source_info,
22442244
value: VarDebugInfoContents::Place(for_arm_body.into()),
2245+
argument_index: None,
22452246
});
22462247
let locals = if has_guard.0 {
22472248
let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
@@ -2260,6 +2261,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22602261
name,
22612262
source_info: debug_source_info,
22622263
value: VarDebugInfoContents::Place(ref_for_guard.into()),
2264+
argument_index: None,
22632265
});
22642266
LocalsForNode::ForGuard { ref_for_guard, for_arm_body }
22652267
} else {

compiler/rustc_mir_build/src/build/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
811811
name,
812812
source_info: SourceInfo::outermost(captured_place.var_ident.span),
813813
value: VarDebugInfoContents::Place(use_place),
814+
argument_index: None,
814815
});
815816

816817
let capture = Capture { captured_place, use_place, mutability };
@@ -827,7 +828,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
827828
expr: &Expr<'tcx>,
828829
) -> BlockAnd<()> {
829830
// Allocate locals for the function arguments
830-
for param in arguments.iter() {
831+
for (argument_index, param) in arguments.iter().enumerate() {
831832
let source_info =
832833
SourceInfo::outermost(param.pat.as_ref().map_or(self.fn_span, |pat| pat.span));
833834
let arg_local =
@@ -839,6 +840,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
839840
name,
840841
source_info,
841842
value: VarDebugInfoContents::Place(arg_local.into()),
843+
argument_index: Some(argument_index as u16 + 1),
842844
});
843845
}
844846
}

compiler/rustc_mir_transform/src/generator.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,13 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
15561556
body.arg_count = 2; // self, resume arg
15571557
body.spread_arg = None;
15581558

1559+
// The original arguments to the function are no longer arguments, mark them as such.
1560+
// Otherwise they'll conflict with our new arguments, which although they don't have
1561+
// argument_index set, will get emitted as unnamed arguments.
1562+
for var in &mut body.var_debug_info {
1563+
var.argument_index = None;
1564+
}
1565+
15591566
body.generator.as_mut().unwrap().yield_ty = None;
15601567
body.generator.as_mut().unwrap().generator_layout = Some(layout);
15611568

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// This test checks that debug information includes function argument indexes even if the function
2+
// gets inlined by MIR inlining. Without function argument indexes, `info args` in gdb won't show
3+
// arguments and their values for the current function.
4+
5+
// compile-flags: -Zinline-mir=yes -Cdebuginfo=2 --edition=2021
6+
7+
#![crate_type = "lib"]
8+
9+
pub fn outer_function(x: usize, y: usize) -> usize {
10+
inner_function(x, y) + 1
11+
}
12+
13+
#[inline]
14+
fn inner_function(aaaa: usize, bbbb: usize) -> usize {
15+
// CHECK: !DILocalVariable(name: "aaaa", arg: 1
16+
// CHECK-SAME: line: 14
17+
// CHECK: !DILocalVariable(name: "bbbb", arg: 2
18+
// CHECK-SAME: line: 14
19+
aaaa + bbbb
20+
}
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Checks that we don't get conflicting arguments in our debug info with a particular async function
2+
// structure.
3+
4+
// edition:2021
5+
// compile-flags: -Cdebuginfo=2
6+
// build-pass
7+
8+
#![crate_type = "lib"]
9+
10+
use std::future::Future;
11+
12+
// The compiler produces a closure as part of this function. That closure initially takes an
13+
// argument _task_context. Later, when the MIR for that closure is transformed into a generator
14+
// state machine, _task_context is demoted to not be an argument, but just part of an unnamed
15+
// argument. If we emit debug info saying that both _task_context and the unnamed argument are both
16+
// argument number 2, then LLVM will fail with "conflicting debug info for argument". See
17+
// https://github.com/rust-lang/rust/pull/109466#issuecomment-1500879195 for details.
18+
async fn recv_unit() {
19+
std::future::ready(()).await;
20+
}
21+
22+
pub fn poll_recv() {
23+
// This box is necessary in order to reproduce the problem.
24+
let _: Box<dyn Future<Output = ()>> = Box::new(recv_unit());
25+
}

0 commit comments

Comments
 (0)