Skip to content

Commit 23c8aa3

Browse files
committed
Reorder stack spills so that constants come later.
Currently constants are "pulled forward" and have their stack spills emitted first. This confuses LLVM as to where to place breakpoints at function entry, and results in argument values being wrong in the debugger. It's straightforward to avoid emitting the stack spills for constants until arguments/etc have been introduced in debug_introduce_locals, so do that. Example LLVM IR (irrelevant IR elided): Before: define internal void @_ZN11rust_1289457binding17h2c78f956ba4bd2c3E(i64 %a, i64 %b, double %c) unnamed_addr #0 !dbg !178 { start: %c.dbg.spill = alloca [8 x i8], align 8 %b.dbg.spill = alloca [8 x i8], align 8 %a.dbg.spill = alloca [8 x i8], align 8 %x.dbg.spill = alloca [4 x i8], align 4 store i32 0, ptr %x.dbg.spill, align 4, !dbg !192 ; LLVM places breakpoint here. #dbg_declare(ptr %x.dbg.spill, !190, !DIExpression(), !192) store i64 %a, ptr %a.dbg.spill, align 8 #dbg_declare(ptr %a.dbg.spill, !187, !DIExpression(), !193) store i64 %b, ptr %b.dbg.spill, align 8 #dbg_declare(ptr %b.dbg.spill, !188, !DIExpression(), !194) store double %c, ptr %c.dbg.spill, align 8 #dbg_declare(ptr %c.dbg.spill, !189, !DIExpression(), !195) ret void, !dbg !196 } After: define internal void @_ZN11rust_1289457binding17h2c78f956ba4bd2c3E(i64 %a, i64 %b, double %c) unnamed_addr #0 !dbg !178 { start: %x.dbg.spill = alloca [4 x i8], align 4 %c.dbg.spill = alloca [8 x i8], align 8 %b.dbg.spill = alloca [8 x i8], align 8 %a.dbg.spill = alloca [8 x i8], align 8 store i64 %a, ptr %a.dbg.spill, align 8 #dbg_declare(ptr %a.dbg.spill, !187, !DIExpression(), !192) store i64 %b, ptr %b.dbg.spill, align 8 #dbg_declare(ptr %b.dbg.spill, !188, !DIExpression(), !193) store double %c, ptr %c.dbg.spill, align 8 #dbg_declare(ptr %c.dbg.spill, !189, !DIExpression(), !194) store i32 0, ptr %x.dbg.spill, align 4, !dbg !195 ; LLVM places breakpoint here. #dbg_declare(ptr %x.dbg.spill, !190, !DIExpression(), !195) ret void, !dbg !196 } Note in particular the position of the "LLVM places breakpoint here" comment relative to the stack spills for the function arguments. LLVM assumes that the first instruction with with a debug location is the end of the prologue. As LLVM does not currently offer front ends any direct control over the placement of the prologue end reordering the IR is the only mechanism available to fix argument values at function entry in the presence of MIR optimizations like SingleUseConsts. Fixes rust-lang#128945
1 parent 0609062 commit 23c8aa3

File tree

3 files changed

+79
-18
lines changed

3 files changed

+79
-18
lines changed

Diff for: compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+36-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::collections::hash_map::Entry;
2+
use std::marker::PhantomData;
23
use std::ops::Range;
34

45
use rustc_data_structures::fx::FxHashMap;
@@ -14,7 +15,7 @@ use rustc_target::abi::{Abi, FieldIdx, FieldsShape, Size, VariantIdx};
1415

1516
use super::operand::{OperandRef, OperandValue};
1617
use super::place::{PlaceRef, PlaceValue};
17-
use super::{FunctionCx, LocalRef};
18+
use super::{FunctionCx, LocalRef, PerLocalVarDebugInfoIndexVec};
1819
use crate::traits::*;
1920

2021
pub struct FunctionDebugContext<'tcx, S, L> {
@@ -47,6 +48,17 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
4748
pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
4849
}
4950

51+
/// Information needed to emit a constant.
52+
pub struct ConstDebugInfo<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
53+
pub name: String,
54+
pub source_info: mir::SourceInfo,
55+
pub operand: OperandRef<'tcx, Bx::Value>,
56+
pub dbg_var: Bx::DIVariable,
57+
pub dbg_loc: Bx::DILocation,
58+
pub fragment: Option<Range<Size>>,
59+
pub _phantom: PhantomData<&'a ()>,
60+
}
61+
5062
#[derive(Clone, Copy, Debug)]
5163
pub struct DebugScope<S, L> {
5264
pub dbg_scope: S,
@@ -426,19 +438,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
426438
}
427439
}
428440

429-
pub fn debug_introduce_locals(&self, bx: &mut Bx) {
441+
pub fn debug_introduce_locals(&self, bx: &mut Bx, consts: Vec<ConstDebugInfo<'a, 'tcx, Bx>>) {
430442
if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
431443
for local in self.locals.indices() {
432444
self.debug_introduce_local(bx, local);
433445
}
446+
447+
for ConstDebugInfo { name, source_info, operand, dbg_var, dbg_loc, fragment, .. } in
448+
consts.into_iter()
449+
{
450+
self.set_debug_loc(bx, source_info);
451+
let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
452+
bx.clear_dbg_loc();
453+
454+
bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment);
455+
}
434456
}
435457
}
436458

437459
/// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
438460
pub fn compute_per_local_var_debug_info(
439461
&self,
440462
bx: &mut Bx,
441-
) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
463+
) -> Option<(
464+
PerLocalVarDebugInfoIndexVec<'tcx, Bx::DIVariable>,
465+
Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
466+
)> {
442467
let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
443468

444469
let target_is_msvc = self.cx.sess().target.is_like_msvc;
@@ -448,6 +473,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
448473
}
449474

450475
let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
476+
let mut constants = vec![];
451477
let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default();
452478
for var in &self.mir.var_debug_info {
453479
let dbg_scope_and_span = if full_debug_info {
@@ -544,23 +570,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
544570
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
545571

546572
let operand = self.eval_mir_constant_to_operand(bx, &c);
547-
self.set_debug_loc(bx, var.source_info);
548-
let base =
549-
Self::spill_operand_to_stack(operand, Some(var.name.to_string()), bx);
550-
bx.clear_dbg_loc();
551-
552-
bx.dbg_var_addr(
573+
constants.push(ConstDebugInfo {
574+
name: var.name.to_string(),
575+
source_info: var.source_info,
576+
operand,
553577
dbg_var,
554578
dbg_loc,
555-
base.val.llval,
556-
Size::ZERO,
557-
&[],
558579
fragment,
559-
);
580+
_phantom: PhantomData,
581+
});
560582
}
561583
}
562584
}
563585
}
564-
Some(per_local)
586+
Some((per_local, constants))
565587
}
566588
}

Diff for: compiler/rustc_codegen_ssa/src/mir/mod.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ enum CachedLlbb<T> {
4141
Skip,
4242
}
4343

44+
type PerLocalVarDebugInfoIndexVec<'tcx, V> =
45+
IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, V>>>;
46+
4447
/// Master context for codegenning from MIR.
4548
pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
4649
instance: Instance<'tcx>,
@@ -107,8 +110,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
107110

108111
/// All `VarDebugInfo` from the MIR body, partitioned by `Local`.
109112
/// This is `None` if no variable debuginfo/names are needed.
110-
per_local_var_debug_info:
111-
Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>>,
113+
per_local_var_debug_info: Option<PerLocalVarDebugInfoIndexVec<'tcx, Bx::DIVariable>>,
112114

113115
/// Caller location propagated if this function has `#[track_caller]`.
114116
caller_location: Option<OperandRef<'tcx, Bx::Value>>,
@@ -216,7 +218,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
216218
// monomorphization, and if there is an error during collection then codegen never starts -- so
217219
// we don't have to do it again.
218220

219-
fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx);
221+
let (per_local_var_debug_info, consts_debug_info) =
222+
fx.compute_per_local_var_debug_info(&mut start_bx).unzip();
223+
fx.per_local_var_debug_info = per_local_var_debug_info;
220224

221225
let memory_locals = analyze::non_ssa_locals(&fx);
222226

@@ -267,7 +271,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
267271
fx.initialize_locals(local_values);
268272

269273
// Apply debuginfo to the newly allocated locals.
270-
fx.debug_introduce_locals(&mut start_bx);
274+
fx.debug_introduce_locals(&mut start_bx, consts_debug_info.unwrap_or_default());
271275

272276
// If the backend supports coverage, and coverage is enabled for this function,
273277
// do any necessary start-of-function codegen (e.g. locals for MC/DC bitmaps).

Diff for: tests/debuginfo/constant-ordering-prologue.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//@ min-lldb-version: 310
2+
3+
//@ compile-flags:-g
4+
5+
// === GDB TESTS ===================================================================================
6+
7+
// gdb-command:break constant_ordering_prologue::binding
8+
// gdb-command:run
9+
10+
// gdb-command:print a
11+
// gdb-check: = 19
12+
// gdb-command:print b
13+
// gdb-check: = 20
14+
// gdb-command:print c
15+
// gdb-check: = 21.5
16+
17+
// === LLDB TESTS ==================================================================================
18+
19+
// lldb-command:b "constant_ordering_prologue::binding"
20+
// lldb-command:run
21+
22+
// lldb-command:print a
23+
// lldb-check: = 19
24+
// lldb-command:print b
25+
// lldb-check: = 20
26+
// lldb-command:print c
27+
// lldb-check: = 21.5
28+
29+
fn binding(a: i64, b: u64, c: f64) {
30+
let x = 0;
31+
}
32+
33+
fn main() {
34+
binding(19, 20, 21.5);
35+
}

0 commit comments

Comments
 (0)