Skip to content

Commit ab50529

Browse files
authored
Rollup merge of #105482 - wesleywiser:fix_debuginfo_ub, r=tmiasko
Fix invalid codegen during debuginfo lowering In order for LLVM to correctly generate debuginfo for msvc, we sometimes need to spill arguments to the stack and perform some direct & indirect offsets into the value. Previously, this code always performed those actions, even when not required as LLVM would clean it up during optimization. However, when MIR inlining is enabled, this can cause problems as the operations occur prior to the spilled value being initialized. To solve this, we first calculate the necessary offsets using just the type which is side-effect free and does not alter the LLVM IR. Then, if we are in a situation which requires us to generate the LLVM IR (and this situation only occurs for arguments, not local variables) then we perform the same calculation again, this time generating the appropriate LLVM IR as we go. r? `@tmiasko` but feel free to reassign if you want 🙂 Fixes #105386
2 parents 62160cb + 7253057 commit ab50529

File tree

3 files changed

+149
-29
lines changed

3 files changed

+149
-29
lines changed

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

+107-29
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ use rustc_index::vec::IndexVec;
33
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
44
use rustc_middle::mir;
55
use rustc_middle::ty;
6+
use rustc_middle::ty::layout::TyAndLayout;
67
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
78
use rustc_session::config::DebugInfo;
89
use rustc_span::symbol::{kw, Symbol};
910
use rustc_span::{BytePos, Span};
10-
use rustc_target::abi::Abi;
11-
use rustc_target::abi::Size;
11+
use rustc_target::abi::{Abi, Size, VariantIdx};
1212

1313
use super::operand::{OperandRef, OperandValue};
1414
use super::place::PlaceRef;
@@ -76,6 +76,106 @@ impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
7676
}
7777
}
7878

79+
trait DebugInfoOffsetLocation<'tcx, Bx> {
80+
fn deref(&self, bx: &mut Bx) -> Self;
81+
fn layout(&self) -> TyAndLayout<'tcx>;
82+
fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self;
83+
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self;
84+
}
85+
86+
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
87+
for PlaceRef<'tcx, Bx::Value>
88+
{
89+
fn deref(&self, bx: &mut Bx) -> Self {
90+
bx.load_operand(*self).deref(bx.cx())
91+
}
92+
93+
fn layout(&self) -> TyAndLayout<'tcx> {
94+
self.layout
95+
}
96+
97+
fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
98+
PlaceRef::project_field(*self, bx, field.index())
99+
}
100+
101+
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
102+
self.project_downcast(bx, variant)
103+
}
104+
}
105+
106+
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
107+
for TyAndLayout<'tcx>
108+
{
109+
fn deref(&self, bx: &mut Bx) -> Self {
110+
bx.cx().layout_of(
111+
self.ty.builtin_deref(true).unwrap_or_else(|| bug!("cannot deref `{}`", self.ty)).ty,
112+
)
113+
}
114+
115+
fn layout(&self) -> TyAndLayout<'tcx> {
116+
*self
117+
}
118+
119+
fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
120+
self.field(bx.cx(), field.index())
121+
}
122+
123+
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
124+
self.for_variant(bx.cx(), variant)
125+
}
126+
}
127+
128+
struct DebugInfoOffset<T> {
129+
/// Offset from the `base` used to calculate the debuginfo offset.
130+
direct_offset: Size,
131+
/// Each offset in this vector indicates one level of indirection from the base or previous
132+
/// indirect offset plus a dereference.
133+
indirect_offsets: Vec<Size>,
134+
/// The final location debuginfo should point to.
135+
result: T,
136+
}
137+
138+
fn calculate_debuginfo_offset<
139+
'a,
140+
'tcx,
141+
Bx: BuilderMethods<'a, 'tcx>,
142+
L: DebugInfoOffsetLocation<'tcx, Bx>,
143+
>(
144+
bx: &mut Bx,
145+
local: mir::Local,
146+
var: &PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
147+
base: L,
148+
) -> DebugInfoOffset<L> {
149+
let mut direct_offset = Size::ZERO;
150+
// FIXME(eddyb) use smallvec here.
151+
let mut indirect_offsets = vec![];
152+
let mut place = base;
153+
154+
for elem in &var.projection[..] {
155+
match *elem {
156+
mir::ProjectionElem::Deref => {
157+
indirect_offsets.push(Size::ZERO);
158+
place = place.deref(bx);
159+
}
160+
mir::ProjectionElem::Field(field, _) => {
161+
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
162+
*offset += place.layout().fields.offset(field.index());
163+
place = place.project_field(bx, field);
164+
}
165+
mir::ProjectionElem::Downcast(_, variant) => {
166+
place = place.downcast(bx, variant);
167+
}
168+
_ => span_bug!(
169+
var.source_info.span,
170+
"unsupported var debuginfo place `{:?}`",
171+
mir::Place { local, projection: var.projection },
172+
),
173+
}
174+
}
175+
176+
DebugInfoOffset { direct_offset, indirect_offsets, result: place }
177+
}
178+
79179
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
80180
pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
81181
bx.set_span(source_info.span);
@@ -262,33 +362,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
262362
let Some(dbg_var) = var.dbg_var else { continue };
263363
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
264364

265-
let mut direct_offset = Size::ZERO;
266-
// FIXME(eddyb) use smallvec here.
267-
let mut indirect_offsets = vec![];
268-
let mut place = base;
269-
270-
for elem in &var.projection[..] {
271-
match *elem {
272-
mir::ProjectionElem::Deref => {
273-
indirect_offsets.push(Size::ZERO);
274-
place = bx.load_operand(place).deref(bx.cx());
275-
}
276-
mir::ProjectionElem::Field(field, _) => {
277-
let i = field.index();
278-
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
279-
*offset += place.layout.fields.offset(i);
280-
place = place.project_field(bx, i);
281-
}
282-
mir::ProjectionElem::Downcast(_, variant) => {
283-
place = place.project_downcast(bx, variant);
284-
}
285-
_ => span_bug!(
286-
var.source_info.span,
287-
"unsupported var debuginfo place `{:?}`",
288-
mir::Place { local, projection: var.projection },
289-
),
290-
}
291-
}
365+
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
366+
calculate_debuginfo_offset(bx, local, &var, base.layout);
292367

293368
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
294369
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
@@ -306,6 +381,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
306381
|| !matches!(&indirect_offsets[..], [Size::ZERO] | []));
307382

308383
if should_create_individual_allocas {
384+
let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
385+
calculate_debuginfo_offset(bx, local, &var, base);
386+
309387
// Create a variable which will be a pointer to the actual value
310388
let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut {
311389
mutbl: mir::Mutability::Mut,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// compile-flags: --crate-type=lib -O -Cdebuginfo=2 -Cno-prepopulate-passes
2+
// min-llvm-version: 15.0 # this test uses opaque pointer notation
3+
#![feature(stmt_expr_attributes)]
4+
5+
pub struct S([usize; 8]);
6+
7+
#[no_mangle]
8+
pub fn outer_function(x: S, y: S) -> usize {
9+
(#[inline(always)]|| {
10+
let _z = x;
11+
y.0[0]
12+
})()
13+
}
14+
15+
// Check that we do not attempt to load from the spilled arg before it is assigned to
16+
// when generating debuginfo.
17+
// CHECK-LABEL: @outer_function
18+
// CHECK: [[spill:%.*]] = alloca %"[closure@{{.*.rs}}:9:23: 9:25]"
19+
// CHECK-NOT: [[ptr_tmp:%.*]] = getelementptr inbounds %"[closure@{{.*.rs}}:9:23: 9:25]", ptr [[spill]]
20+
// CHECK-NOT: [[load:%.*]] = load ptr, ptr
21+
// CHECK: call void @llvm.lifetime.start{{.*}}({{.*}}, ptr [[spill]])
22+
// CHECK: call void @llvm.memcpy{{.*}}(ptr {{align .*}} [[spill]], ptr {{align .*}} %x
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// run-pass
2+
// compile-flags: --edition 2021 -Copt-level=3 -Cdebuginfo=2 -Zmir-opt-level=3
3+
4+
fn main() {
5+
TranslatorI.visit_pre();
6+
}
7+
8+
impl TranslatorI {
9+
fn visit_pre(self) {
10+
Some(())
11+
.map(|_| self.flags())
12+
.unwrap_or_else(|| self.flags());
13+
}
14+
}
15+
16+
struct TranslatorI;
17+
18+
impl TranslatorI {
19+
fn flags(&self) {}
20+
}

0 commit comments

Comments
 (0)