Skip to content

Commit 76ed7a1

Browse files
committed
Auto merge of #130329 - khuey:reorder-constant-spills, r=davidtwco
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#128945 r? `@michaelwoerister`
2 parents 9e394f5 + 652b502 commit 76ed7a1

3 files changed

Lines changed: 83 additions & 18 deletions

File tree

‎compiler/rustc_codegen_ssa/src/mir/debuginfo.rs‎

Lines changed: 40 additions & 14 deletions
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
usesuper::operand::{OperandRef,OperandValue};
1617
usesuper::place::{PlaceRef,PlaceValue};
17-
usesuper::{FunctionCx,LocalRef};
18+
usesuper::{FunctionCx,LocalRef,PerLocalVarDebugInfoIndexVec};
1819
usecrate::traits::*;
1920

2021
pubstructFunctionDebugContext<'tcx,S,L>{
@@ -48,6 +49,17 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
4849
pubprojection:&'tcx ty::List<mir::PlaceElem<'tcx>>,
4950
}
5051

52+
/// Information needed to emit a constant.
53+
pubstructConstDebugInfo<'a,'tcx,Bx:BuilderMethods<'a,'tcx>>{
54+
pubname:String,
55+
pubsource_info: mir::SourceInfo,
56+
puboperand:OperandRef<'tcx,Bx::Value>,
57+
pubdbg_var:Bx::DIVariable,
58+
pubdbg_loc:Bx::DILocation,
59+
pubfragment:Option<Range<Size>>,
60+
pub_phantom:PhantomData<&'a()>,
61+
}
62+
5163
#[derive(Clone,Copy,Debug)]
5264
pubstructDebugScope<S,L>{
5365
pubdbg_scope:S,
@@ -427,19 +439,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
427439
}
428440
}
429441

430-
pub(crate)fndebug_introduce_locals(&self,bx:&mutBx){
442+
pub(crate)fndebug_introduce_locals(
443+
&self,
444+
bx:&mutBx,
445+
consts:Vec<ConstDebugInfo<'a,'tcx,Bx>>,
446+
){
431447
if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names(){
432448
for local inself.locals.indices(){
433449
self.debug_introduce_local(bx, local);
434450
}
451+
452+
forConstDebugInfo{ name, source_info, operand, dbg_var, dbg_loc, fragment, .. }in
453+
consts.into_iter()
454+
{
455+
self.set_debug_loc(bx, source_info);
456+
let base = FunctionCx::spill_operand_to_stack(operand,Some(name), bx);
457+
bx.clear_dbg_loc();
458+
459+
bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval,Size::ZERO,&[], fragment);
460+
}
435461
}
436462
}
437463

438464
/// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
439465
pub(crate)fncompute_per_local_var_debug_info(
440466
&self,
441467
bx:&mutBx,
442-
) -> Option<IndexVec<mir::Local,Vec<PerLocalVarDebugInfo<'tcx,Bx::DIVariable>>>>{
468+
) -> Option<(
469+
PerLocalVarDebugInfoIndexVec<'tcx,Bx::DIVariable>,
470+
Vec<ConstDebugInfo<'a,'tcx,Bx>>,
471+
)>{
443472
let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
444473

445474
let target_is_msvc = self.cx.sess().target.is_like_msvc;
@@ -449,6 +478,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
449478
}
450479

451480
letmut per_local = IndexVec::from_elem(vec![],&self.mir.local_decls);
481+
letmut constants = vec![];
452482
letmut params_seen:FxHashMap<_,Bx::DIVariable> = Default::default();
453483
for var in&self.mir.var_debug_info{
454484
let dbg_scope_and_span = if full_debug_info {
@@ -545,23 +575,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
545575
letSome(dbg_loc) = self.dbg_loc(var.source_info)else{continue};
546576

547577
let operand = self.eval_mir_constant_to_operand(bx,&c);
548-
self.set_debug_loc(bx, var.source_info);
549-
let base =
550-
Self::spill_operand_to_stack(operand,Some(var.name.to_string()), bx);
551-
bx.clear_dbg_loc();
552-
553-
bx.dbg_var_addr(
578+
constants.push(ConstDebugInfo{
579+
name: var.name.to_string(),
580+
source_info: var.source_info,
581+
operand,
554582
dbg_var,
555583
dbg_loc,
556-
base.val.llval,
557-
Size::ZERO,
558-
&[],
559584
fragment,
560-
);
585+
_phantom:PhantomData,
586+
});
561587
}
562588
}
563589
}
564590
}
565-
Some(per_local)
591+
Some((per_local, constants))
566592
}
567593
}

‎compiler/rustc_codegen_ssa/src/mir/mod.rs‎

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

44+
typePerLocalVarDebugInfoIndexVec<'tcx,V> =
45+
IndexVec<mir::Local,Vec<PerLocalVarDebugInfo<'tcx,V>>>;
46+
4447
/// Master context for codegenning from MIR.
4548
pubstructFunctionCx<'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 traversal_order = traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance);
222226
let memory_locals = analyze::non_ssa_locals(&fx,&traversal_order);
@@ -268,7 +272,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
268272
fx.initialize_locals(local_values);
269273

270274
// Apply debuginfo to the newly allocated locals.
271-
fx.debug_introduce_locals(&mut start_bx);
275+
fx.debug_introduce_locals(&mut start_bx, consts_debug_info.unwrap_or_default());
272276

273277
// If the backend supports coverage, and coverage is enabled for this function,
274278
// do any necessary start-of-function codegen (e.g. locals for MC/DC bitmaps).
Lines changed: 35 additions & 0 deletions
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+
fnbinding(a:i64,b:u64,c:f64){
30+
let x = 0;
31+
}
32+
33+
fnmain(){
34+
binding(19,20,21.5);
35+
}

0 commit comments

Comments
 (0)