Don't generate pointer loads to spills unless necessary

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.
This commit is contained in:
Wesley Wiser 2022-12-07 13:48:34 -05:00
parent b33d1e26b2
commit 7253057887
3 changed files with 49 additions and 4 deletions

View file

@ -103,6 +103,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
}
}
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
for TyAndLayout<'tcx>
{
fn deref(&self, bx: &mut Bx) -> Self {
bx.cx().layout_of(
self.ty.builtin_deref(true).unwrap_or_else(|| bug!("cannot deref `{}`", self.ty)).ty,
)
}
fn layout(&self) -> TyAndLayout<'tcx> {
*self
}
fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
self.field(bx.cx(), field.index())
}
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
self.for_variant(bx.cx(), variant)
}
}
struct DebugInfoOffset<T> {
/// Offset from the `base` used to calculate the debuginfo offset.
direct_offset: Size,
@ -340,8 +362,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let Some(dbg_var) = var.dbg_var else { continue };
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
let DebugInfoOffset { direct_offset, indirect_offsets, result: place } =
calculate_debuginfo_offset(bx, local, &var, base);
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
calculate_debuginfo_offset(bx, local, &var, base.layout);
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
@ -359,6 +381,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|| !matches!(&indirect_offsets[..], [Size::ZERO] | []));
if should_create_individual_allocas {
let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
calculate_debuginfo_offset(bx, local, &var, base);
// Create a variable which will be a pointer to the actual value
let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut {
mutbl: mir::Mutability::Mut,

View file

@ -16,7 +16,7 @@ pub fn outer_function(x: S, y: S) -> usize {
// when generating debuginfo.
// CHECK-LABEL: @outer_function
// CHECK: [[spill:%.*]] = alloca %"[closure@{{.*.rs}}:9:23: 9:25]"
// CHECK: [[ptr_tmp:%.*]] = getelementptr inbounds %"[closure@{{.*.rs}}:9:23: 9:25]", ptr [[spill]]
// CHECK: [[load:%.*]] = load ptr, ptr [[ptr_tmp]]
// CHECK-NOT: [[ptr_tmp:%.*]] = getelementptr inbounds %"[closure@{{.*.rs}}:9:23: 9:25]", ptr [[spill]]
// CHECK-NOT: [[load:%.*]] = load ptr, ptr
// CHECK: call void @llvm.lifetime.start{{.*}}({{.*}}, ptr [[spill]])
// CHECK: call void @llvm.memcpy{{.*}}(ptr {{align .*}} [[spill]], ptr {{align .*}} %x

View file

@ -0,0 +1,20 @@
// run-pass
// compile-flags: --edition 2021 -Copt-level=3 -Cdebuginfo=2 -Zmir-opt-level=3
fn main() {
TranslatorI.visit_pre();
}
impl TranslatorI {
fn visit_pre(self) {
Some(())
.map(|_| self.flags())
.unwrap_or_else(|| self.flags());
}
}
struct TranslatorI;
impl TranslatorI {
fn flags(&self) {}
}