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:
parent
b33d1e26b2
commit
7253057887
3 changed files with 49 additions and 4 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
20
src/test/ui/debuginfo/issue-105386-debuginfo-ub.rs
Normal file
20
src/test/ui/debuginfo/issue-105386-debuginfo-ub.rs
Normal 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) {}
|
||||
}
|
Loading…
Add table
Reference in a new issue