diff --git a/src/librustc_codegen_llvm/mir/mod.rs b/src/librustc_codegen_llvm/mir/mod.rs index 312939408c62c..aaae7b3cf897e 100644 --- a/src/librustc_codegen_llvm/mir/mod.rs +++ b/src/librustc_codegen_llvm/mir/mod.rs @@ -572,6 +572,25 @@ fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, }; let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); + // Store the pointer to closure data in an alloca for debuginfo + // because that's what the llvm.dbg.declare intrinsic expects. + + // FIXME(eddyb) this shouldn't be necessary but SROA seems to + // mishandle DW_OP_plus not preceded by DW_OP_deref, i.e. it + // doesn't actually strip the offset when splitting the closure + // environment into its components so it ends up out of bounds. + // (cuviper) It seems to be fine without the alloca on LLVM 6 and later. + let env_alloca = !env_ref && unsafe { llvm::LLVMRustVersionMajor() < 6 }; + let env_ptr = if env_alloca { + let scratch = PlaceRef::alloca(bx, + bx.cx.layout_of(tcx.mk_mut_ptr(arg.layout.ty)), + "__debuginfo_env_ptr"); + bx.store(place.llval, scratch.llval, scratch.align); + scratch.llval + } else { + place.llval + }; + for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() { let byte_offset_of_var_in_env = closure_layout.fields.offset(i).bytes(); @@ -583,7 +602,10 @@ fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, }; // The environment and the capture can each be indirect. - let mut ops = if env_ref { &ops[..] } else { &ops[1..] }; + + // FIXME(eddyb) see above why we sometimes have to keep + // a pointer in an alloca for debuginfo atm. + let mut ops = if env_ref || env_alloca { &ops[..] } else { &ops[1..] }; let ty = if let (true, &ty::TyRef(_, ty, _)) = (decl.by_ref, &ty.sty) { ty @@ -593,7 +615,7 @@ fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, }; let variable_access = VariableAccess::IndirectVariable { - alloca: place.llval, + alloca: env_ptr, address_operations: &ops }; declare_local(