From d253e6e473c7614eb16f6144ab6a0f95e89db604 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 13 Jan 2022 18:13:54 +0100 Subject: [PATCH 1/6] [debuginfo] Fix and unify handling of fat pointers in debuginfo. --- .../src/debuginfo/metadata.rs | 353 +++++++++--------- .../rustc_codegen_llvm/src/debuginfo/utils.rs | 59 ++- compiler/rustc_codegen_llvm/src/lib.rs | 1 + src/test/debuginfo/unsized.rs | 40 +- 4 files changed, 273 insertions(+), 180 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index c90e43a4060f7..2f49e2087c31e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -10,6 +10,8 @@ use super::CrateDebugContext; use crate::abi; use crate::common::CodegenCx; +use crate::debuginfo::utils::fat_pointer_kind; +use crate::debuginfo::utils::FatPtrKind; use crate::llvm; use crate::llvm::debuginfo::{ DIArray, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, @@ -376,22 +378,24 @@ macro_rules! return_if_metadata_created_in_meantime { }; } -fn fixed_vec_metadata<'ll, 'tcx>( +/// Creates debuginfo for a fixed size array (e.g. `[u64; 123]`). +/// For slices (that is, "arrays" of unknown size) use [slice_type_metadata]. +fn fixed_size_array_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId, - array_or_slice_type: Ty<'tcx>, - element_type: Ty<'tcx>, + array_type: Ty<'tcx>, ) -> MetadataCreationResult<'ll> { + let ty::Array(element_type, len) = array_type.kind() else { + bug!("fixed_size_array_metadata() called with non-ty::Array type `{:?}`", array_type) + }; + let element_type_metadata = type_metadata(cx, element_type); return_if_metadata_created_in_meantime!(cx, unique_type_id); - let (size, align) = cx.size_and_align_of(array_or_slice_type); + let (size, align) = cx.size_and_align_of(array_type); - let upper_bound = match array_or_slice_type.kind() { - ty::Array(_, len) => len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong, - _ => -1, - }; + let upper_bound = len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong; let subrange = unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; @@ -410,55 +414,111 @@ fn fixed_vec_metadata<'ll, 'tcx>( MetadataCreationResult::new(metadata, false) } -fn vec_slice_metadata<'ll, 'tcx>( +/// Creates debuginfo for built-in pointer-like things: +/// +/// - ty::Ref +/// - ty::RawPtr +/// - ty::Adt in the case it's Box +/// +/// At some point we might want to remove the special handling of Box +/// and treat it the same as other smart pointers (like Rc, Arc, ...). +fn pointer_or_reference_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - slice_ptr_type: Ty<'tcx>, - element_type: Ty<'tcx>, + ptr_type: Ty<'tcx>, + pointee_type: Ty<'tcx>, unique_type_id: UniqueTypeId, ) -> MetadataCreationResult<'ll> { - let data_ptr_type = cx.tcx.mk_imm_ptr(element_type); - - let data_ptr_metadata = type_metadata(cx, data_ptr_type); + let pointee_type_metadata = type_metadata(cx, pointee_type); return_if_metadata_created_in_meantime!(cx, unique_type_id); - let slice_type_name = compute_debuginfo_type_name(cx.tcx, slice_ptr_type, true); - - let (pointer_size, pointer_align) = cx.size_and_align_of(data_ptr_type); - let (usize_size, usize_align) = cx.size_and_align_of(cx.tcx.types.usize); - - let member_descriptions = vec![ - MemberDescription { - name: "data_ptr".to_owned(), - type_metadata: data_ptr_metadata, - offset: Size::ZERO, - size: pointer_size, - align: pointer_align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - }, - MemberDescription { - name: "length".to_owned(), - type_metadata: type_metadata(cx, cx.tcx.types.usize), - offset: pointer_size, - size: usize_size, - align: usize_align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - }, - ]; + let (thin_pointer_size, thin_pointer_align) = + cx.size_and_align_of(cx.tcx.mk_imm_ptr(cx.tcx.types.unit)); + let ptr_type_debuginfo_name = compute_debuginfo_type_name(cx.tcx, ptr_type, true); - let metadata = composite_type_metadata( - cx, - slice_ptr_type, - &slice_type_name, - unique_type_id, - member_descriptions, - NO_SCOPE_METADATA, - ); - MetadataCreationResult::new(metadata, false) + let pointer_type_metadata = match fat_pointer_kind(cx, pointee_type) { + None => { + // This is a thin pointer. Create a regular pointer type and give it the correct name. + debug_assert_eq!( + (thin_pointer_size, thin_pointer_align), + cx.size_and_align_of(ptr_type) + ); + + unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_metadata, + thin_pointer_size.bits(), + thin_pointer_align.bits() as u32, + 0, // Ignore DWARF address space. + ptr_type_debuginfo_name.as_ptr().cast(), + ptr_type_debuginfo_name.len(), + ) + } + } + Some(fat_pointer_kind) => { + let layout = cx.layout_of(ptr_type); + + let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); + let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); + + let (addr_field_name, extra_field_name) = match fat_pointer_kind { + FatPtrKind::Dyn => ("pointer", "vtable"), + FatPtrKind::Slice => ("data_ptr", "length"), + }; + + debug_assert_eq!(abi::FAT_PTR_ADDR, 0); + debug_assert_eq!(abi::FAT_PTR_EXTRA, 1); + + // The data pointer type is a regular, thin pointer, regardless of whether this is a slice + // or a trait object. + let data_ptr_type_metadata = unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_metadata, + addr_field.size.bits(), + addr_field.align.abi.bits() as u32, + 0, // Ignore DWARF address space. + std::ptr::null(), + 0, + ) + }; + + let member_descriptions = vec![ + MemberDescription { + name: addr_field_name.into(), + type_metadata: data_ptr_type_metadata, + offset: layout.fields.offset(abi::FAT_PTR_ADDR), + size: addr_field.size, + align: addr_field.align.abi, + flags: DIFlags::FlagArtificial, + discriminant: None, + source_info: None, + }, + MemberDescription { + name: extra_field_name.into(), + type_metadata: type_metadata(cx, extra_field.ty), + offset: layout.fields.offset(abi::FAT_PTR_EXTRA), + size: extra_field.size, + align: extra_field.align.abi, + flags: DIFlags::FlagArtificial, + discriminant: None, + source_info: None, + }, + ]; + + composite_type_metadata( + cx, + ptr_type, + &ptr_type_debuginfo_name, + unique_type_id, + member_descriptions, + NO_SCOPE_METADATA, + ) + } + }; + + MetadataCreationResult { metadata: pointer_type_metadata, already_stored_in_typemap: false } } fn subroutine_type_metadata<'ll, 'tcx>( @@ -495,83 +555,57 @@ fn subroutine_type_metadata<'ll, 'tcx>( ) } -// FIXME(1563): This is all a bit of a hack because 'trait pointer' is an ill- -// defined concept. For the case of an actual trait pointer (i.e., `Box`, -// `&Trait`), `trait_object_type` should be the whole thing (e.g, `Box`) and -// `trait_type` should be the actual trait (e.g., `Trait`). Where the trait is part -// of a DST struct, there is no `trait_object_type` and the results of this -// function will be a little bit weird. -fn trait_pointer_metadata<'ll, 'tcx>( +// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs +// we with the correct type name (e.g. "dyn SomeTrait + Sync"). +fn dyn_type_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - trait_type: Ty<'tcx>, - trait_object_type: Option>, + dyn_type: Ty<'tcx>, unique_type_id: UniqueTypeId, ) -> &'ll DIType { - // The implementation provided here is a stub. It makes sure that the trait - // type is assigned the correct name, size, namespace, and source location. - // However, it does not describe the trait's methods. - - let (containing_scope, trait_type_name) = match trait_object_type { - Some(trait_object_type) => match trait_object_type.kind() { - ty::Adt(def, _) => ( - Some(get_namespace_for_item(cx, def.did)), - compute_debuginfo_type_name(cx.tcx, trait_object_type, false), - ), - ty::RawPtr(_) | ty::Ref(..) => { - (NO_SCOPE_METADATA, compute_debuginfo_type_name(cx.tcx, trait_object_type, true)) - } - _ => { - bug!( - "debuginfo: unexpected trait-object type in \ - trait_pointer_metadata(): {:?}", - trait_object_type - ); - } - }, + if let ty::Dynamic(..) = dyn_type.kind() { + let type_name = compute_debuginfo_type_name(cx.tcx, dyn_type, true); + composite_type_metadata(cx, dyn_type, &type_name, unique_type_id, vec![], NO_SCOPE_METADATA) + } else { + bug!("Only ty::Dynamic is valid for dyn_type_metadata(). Found {:?} instead.", dyn_type) + } +} - // No object type, use the trait type directly (no scope here since the type - // will be wrapped in the dyn$ synthetic type). - None => (NO_SCOPE_METADATA, compute_debuginfo_type_name(cx.tcx, trait_type, true)), +// Create debuginfo for `[T]` and `str`. These are unsized. +// +// Note: We currently emit just emit the debuginfo for the element type here +// (i.e. `T` for slices and `u8` for `str`), so that we end up with +// `*const T` for the `data_ptr` field of the corresponding fat-pointer +// debuginfo of `&[T]`. +// +// It would be preferable and more accurate if we emitted a DIArray of T +// without an upper bound instead. That is, LLVM already supports emitting +// debuginfo of arrays of unknown size. But GDB currently seems to end up +// in an infinite loop when confronted with such a type. +// +// As a side effect of the current encoding every instance of a type like +// `struct Foo { unsized_field: [u8] }` will look like +// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the +// slice is zero, then accessing `unsized_field` in the debugger would +// result in an out-of-bounds access. +fn slice_type_metadata<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + slice_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, +) -> MetadataCreationResult<'ll> { + let element_type = match slice_type.kind() { + ty::Slice(element_type) => element_type, + ty::Str => cx.tcx.types.u8, + _ => { + bug!( + "Only ty::Slice is valid for slice_type_metadata(). Found {:?} instead.", + slice_type + ) + } }; - let layout = cx.layout_of(cx.tcx.mk_mut_ptr(trait_type)); - - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - - let data_ptr_field = layout.field(cx, 0); - let vtable_field = layout.field(cx, 1); - let member_descriptions = vec![ - MemberDescription { - name: "pointer".to_owned(), - type_metadata: type_metadata(cx, cx.tcx.mk_mut_ptr(cx.tcx.types.u8)), - offset: layout.fields.offset(0), - size: data_ptr_field.size, - align: data_ptr_field.align.abi, - flags: DIFlags::FlagArtificial, - discriminant: None, - source_info: None, - }, - MemberDescription { - name: "vtable".to_owned(), - type_metadata: type_metadata(cx, vtable_field.ty), - offset: layout.fields.offset(1), - size: vtable_field.size, - align: vtable_field.align.abi, - flags: DIFlags::FlagArtificial, - discriminant: None, - source_info: None, - }, - ]; - - composite_type_metadata( - cx, - trait_object_type.unwrap_or(trait_type), - &trait_type_name, - unique_type_id, - member_descriptions, - containing_scope, - ) + let element_type_metadata = type_metadata(cx, element_type); + return_if_metadata_created_in_meantime!(cx, unique_type_id); + MetadataCreationResult { metadata: element_type_metadata, already_stored_in_typemap: false } } pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { @@ -610,26 +644,6 @@ pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll debug!("type_metadata: {:?}", t); - let ptr_metadata = |ty: Ty<'tcx>| match *ty.kind() { - ty::Slice(typ) => Ok(vec_slice_metadata(cx, t, typ, unique_type_id)), - ty::Str => Ok(vec_slice_metadata(cx, t, cx.tcx.types.u8, unique_type_id)), - ty::Dynamic(..) => Ok(MetadataCreationResult::new( - trait_pointer_metadata(cx, ty, Some(t), unique_type_id), - false, - )), - _ => { - let pointee_metadata = type_metadata(cx, ty); - - if let Some(metadata) = - debug_context(cx).type_map.borrow().find_metadata_for_unique_id(unique_type_id) - { - return Err(metadata); - } - - Ok(MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), false)) - } - }; - let MetadataCreationResult { metadata, already_stored_in_typemap } = match *t.kind() { ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { MetadataCreationResult::new(basic_type_metadata(cx, t), false) @@ -637,22 +651,20 @@ pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll ty::Tuple(elements) if elements.is_empty() => { MetadataCreationResult::new(basic_type_metadata(cx, t), false) } - ty::Array(typ, _) | ty::Slice(typ) => fixed_vec_metadata(cx, unique_type_id, t, typ), - ty::Str => fixed_vec_metadata(cx, unique_type_id, t, cx.tcx.types.i8), + ty::Array(..) => fixed_size_array_metadata(cx, unique_type_id, t), + ty::Slice(_) | ty::Str => slice_type_metadata(cx, t, unique_type_id), ty::Dynamic(..) => { - MetadataCreationResult::new(trait_pointer_metadata(cx, t, None, unique_type_id), false) + MetadataCreationResult::new(dyn_type_metadata(cx, t, unique_type_id), false) } ty::Foreign(..) => { MetadataCreationResult::new(foreign_type_metadata(cx, t, unique_type_id), false) } - ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => match ptr_metadata(ty) { - Ok(res) => res, - Err(metadata) => return metadata, - }, - ty::Adt(def, _) if def.is_box() => match ptr_metadata(t.boxed_ty()) { - Ok(res) => res, - Err(metadata) => return metadata, - }, + ty::RawPtr(ty::TypeAndMut { ty: pointee_type, .. }) | ty::Ref(_, pointee_type, _) => { + pointer_or_reference_metadata(cx, t, pointee_type, unique_type_id) + } + ty::Adt(def, _) if def.is_box() => { + pointer_or_reference_metadata(cx, t, t.boxed_ty(), unique_type_id) + } ty::FnDef(..) | ty::FnPtr(_) => { if let Some(metadata) = debug_context(cx).type_map.borrow().find_metadata_for_unique_id(unique_type_id) @@ -694,7 +706,22 @@ pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll type_map.borrow_mut().remove_type(t); // This is actually a function pointer, so wrap it in pointer DI. - MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) + let (pointer_size, pointer_align) = + cx.size_and_align_of(cx.tcx.mk_imm_ptr(cx.tcx.mk_unit())); + let name = compute_debuginfo_type_name(cx.tcx, t, false); + let md = unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + fn_metadata, + pointer_size.bits(), + pointer_align.bits() as u32, + 0, // Ignore DWARF address space. + name.as_ptr().cast(), + name.len(), + ) + }; + + MetadataCreationResult::new(md, false) } ty::Closure(def_id, substs) => { let upvar_tys: Vec<_> = substs.as_closure().upvar_tys().collect(); @@ -959,26 +986,6 @@ fn foreign_type_metadata<'ll, 'tcx>( create_struct_stub(cx, t, &name, unique_type_id, NO_SCOPE_METADATA, DIFlags::FlagZero) } -fn pointer_type_metadata<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - pointer_type: Ty<'tcx>, - pointee_type_metadata: &'ll DIType, -) -> &'ll DIType { - let (pointer_size, pointer_align) = cx.size_and_align_of(pointer_type); - let name = compute_debuginfo_type_name(cx.tcx, pointer_type, false); - unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_metadata, - pointer_size.bits(), - pointer_align.bits() as u32, - 0, // Ignore DWARF address space. - name.as_ptr().cast(), - name.len(), - ) - } -} - fn param_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { debug!("param_type_metadata: {:?}", t); let name = format!("{:?}", t); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 953b6765a4894..6dd0d58efe328 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -4,7 +4,9 @@ use super::namespace::item_namespace; use super::CrateDebugContext; use rustc_hir::def_id::DefId; -use rustc_middle::ty::DefIdTree; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, DefIdTree, Ty}; +use rustc_target::abi::VariantIdx; use crate::common::CodegenCx; use crate::llvm; @@ -46,3 +48,58 @@ pub fn DIB<'a, 'll>(cx: &'a CodegenCx<'ll, '_>) -> &'a DIBuilder<'ll> { pub fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { item_namespace(cx, cx.tcx.parent(def_id).expect("get_namespace_for_item: missing parent?")) } + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum FatPtrKind { + Slice, + Dyn, +} + +/// Determines if `pointee_ty` is slice-like or trait-object-like, i.e. +/// if the second field of the fat pointer is a length or a vtable-pointer. +/// If `pointee_ty` does not require a fat pointer (because it is Sized) then +/// the function returns `None`. +pub(crate) fn fat_pointer_kind<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + pointee_ty: Ty<'tcx>, +) -> Option { + let layout = cx.layout_of(pointee_ty); + + if !layout.is_unsized() { + return None; + } + + match *pointee_ty.kind() { + ty::Str | ty::Slice(_) => Some(FatPtrKind::Slice), + ty::Dynamic(..) => Some(FatPtrKind::Dyn), + ty::Adt(adt_def, _) => { + assert!(adt_def.is_struct()); + assert!(adt_def.variants.len() == 1); + let variant = &adt_def.variants[VariantIdx::from_usize(0)]; + assert!(!variant.fields.is_empty()); + let last_field_index = variant.fields.len() - 1; + + debug_assert!( + (0..last_field_index) + .all(|field_index| { !layout.field(cx, field_index).is_unsized() }) + ); + + let unsized_field = layout.field(cx, last_field_index); + assert!(unsized_field.is_unsized()); + fat_pointer_kind(cx, unsized_field.ty) + } + ty::Foreign(_) => { + // Assert that pointers to foreign types really are thin: + debug_assert_eq!( + cx.size_of(cx.tcx.mk_imm_ptr(pointee_ty)), + cx.size_of(cx.tcx.mk_imm_ptr(cx.tcx.types.u8)) + ); + None + } + _ => { + // For all other pointee types we should already have returned None + // at the beginning of the function. + panic!("fat_pointer_kind() - Encountered unexpected `pointee_ty`: {:?}", pointee_ty) + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index f0612eaba8089..272f1319ad180 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -10,6 +10,7 @@ #![feature(let_else)] #![feature(extern_types)] #![feature(nll)] +#![feature(let_else)] #![recursion_limit = "256"] use back::write::{create_informational_target_machine, create_target_machine}; diff --git a/src/test/debuginfo/unsized.rs b/src/test/debuginfo/unsized.rs index c27fe61dc8b47..1d1f3c0c76ece 100644 --- a/src/test/debuginfo/unsized.rs +++ b/src/test/debuginfo/unsized.rs @@ -4,13 +4,36 @@ // gdb-command:run -// gdb-command:print *a -// gdbg-check:$1 = {value = [...] "abc"} -// gdbr-check:$1 = unsized::Foo<[u8]> {value: [...]} +// gdb-command:print a +// gdbg-check:$1 = {data_ptr: [...], length: 4} +// gdbr-check:$1 = &unsized::Foo<[u8]> {data_ptr: [...], length: 4} -// gdb-command:print *b -// gdbg-check:$2 = {value = {value = [...] "abc"}} -// gdbr-check:$2 = unsized::Foo> {value: unsized::Foo<[u8]> {value: [...]}} +// gdb-command:print b +// gdbg-check:$2 = {data_ptr: [...], length: 4} +// gdbr-check:$2 = &unsized::Foo> {data_ptr: [...], length: 4} + +// gdb-command:print c +// gdbg-check:$3 = {pointer: [...], vtable: [...]} +// gdbr-check:$3 = &unsized::Foo {pointer: [...], vtable: [...]} + + +// === CDB TESTS =================================================================================== + +// cdb-command: g +// cdb-command:dx a +// cdb-check:a [Type: ref$ > >] +// cdb-check: [+0x000] data_ptr : 0x[...] [Type: unsized::Foo > *] +// cdb-check: [+0x008] length : 0x4 [Type: unsigned __int64] + +// cdb-command:dx b +// cdb-check:b [Type: ref$ > > >] +// cdb-check: [+0x000] data_ptr : 0x[...] [Type: unsized::Foo > > *] +// cdb-check: [+0x008] length : 0x4 [Type: unsigned __int64] + +// cdb-command:dx c +// cdb-check:c [Type: ref$ > >] +// cdb-check: [+0x000] pointer : 0x[...] [Type: unsized::Foo > *] +// cdb-check: [+0x008] vtable : 0x[...] [Type: unsigned __int64 (*)[3]] #![feature(omit_gdb_pretty_printer_section)] @@ -26,8 +49,13 @@ fn main() { value: *b"abc\0" } }; + + // We expect `a`, `b`, and `c` to all be fat pointers. + // `a` and `b` should be slice-like and thus have a `data_ptr` and `length` field. + // `c` should be trait-object-like and thus have a `pointer` and `vtable` field. let a: &Foo<[u8]> = &foo.value; let b: &Foo> = &foo; + let c: &Foo = &Foo { value: 7i32 }; zzz(); // #break } From e2367eeccec35a0ac58e12bc83bc6f639385b7df Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 17 Jan 2022 17:31:13 +0100 Subject: [PATCH 2/6] Fix vec-slices debuginfo test for GDB. --- src/test/debuginfo/vec-slices.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/test/debuginfo/vec-slices.rs b/src/test/debuginfo/vec-slices.rs index e109b1bf2aeae..3525e8d83b8e2 100644 --- a/src/test/debuginfo/vec-slices.rs +++ b/src/test/debuginfo/vec-slices.rs @@ -1,5 +1,4 @@ // ignore-windows -// ignore-gdb // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155 // min-lldb-version: 310 // compile-flags:-g @@ -13,21 +12,21 @@ // gdb-command:print singleton.length // gdb-check:$2 = 1 // gdbg-command:print *((i64[1]*)(singleton.data_ptr)) -// gdbr-command:print *(singleton.data_ptr as &[i64; 1]) +// gdbr-command:print *(singleton.data_ptr as *const [i64; 1]) // gdbg-check:$3 = {1} // gdbr-check:$3 = [1] // gdb-command:print multiple.length // gdb-check:$4 = 4 // gdbg-command:print *((i64[4]*)(multiple.data_ptr)) -// gdbr-command:print *(multiple.data_ptr as &[i64; 4]) +// gdbr-command:print *(multiple.data_ptr as *const [i64; 4]) // gdbg-check:$5 = {2, 3, 4, 5} // gdbr-check:$5 = [2, 3, 4, 5] // gdb-command:print slice_of_slice.length // gdb-check:$6 = 2 // gdbg-command:print *((i64[2]*)(slice_of_slice.data_ptr)) -// gdbr-command:print *(slice_of_slice.data_ptr as &[i64; 2]) +// gdbr-command:print *(slice_of_slice.data_ptr as *const [i64; 2]) // gdbg-check:$7 = {3, 4} // gdbr-check:$7 = [3, 4] @@ -53,16 +52,16 @@ // gdbr-command:print MUT_VECT_SLICE.length // gdb-check:$14 = 2 // gdbg-command:print *((i64[2]*)('vec_slices::MUT_VECT_SLICE'.data_ptr)) -// gdbr-command:print *(MUT_VECT_SLICE.data_ptr as &[i64; 2]) +// gdbr-command:print *(MUT_VECT_SLICE.data_ptr as *const [i64; 2]) // gdbg-check:$15 = {64, 65} // gdbr-check:$15 = [64, 65] -//gdb-command:print mut_slice.length -//gdb-check:$16 = 5 -//gdbg-command:print *((i64[5]*)(mut_slice.data_ptr)) -//gdbr-command:print *(mut_slice.data_ptr as &[i64; 5]) -//gdbg-check:$17 = {1, 2, 3, 4, 5} -//gdbr-check:$17 = [1, 2, 3, 4, 5] +// gdb-command:print mut_slice.length +// gdb-check:$16 = 5 +// gdbg-command:print *((i64[5]*)(mut_slice.data_ptr)) +// gdbr-command:print *(mut_slice.data_ptr as *const [i64; 5]) +// gdbg-check:$17 = {1, 2, 3, 4, 5} +// gdbr-check:$17 = [1, 2, 3, 4, 5] // === LLDB TESTS ================================================================================== From 5e577f71a0695b5e47fc6d62ab02b9d09cb269f8 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 21 Jan 2022 10:35:18 +0100 Subject: [PATCH 3/6] [debuginfo] Fix and unify handling of fat pointers in debuginfo: address review comments. --- .../src/debuginfo/metadata.rs | 38 +++++++++---------- compiler/rustc_codegen_llvm/src/lib.rs | 1 - 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 2f49e2087c31e..b5d86aab88019 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -555,8 +555,8 @@ fn subroutine_type_metadata<'ll, 'tcx>( ) } -// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs -// we with the correct type name (e.g. "dyn SomeTrait + Sync"). +/// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs +/// we with the correct type name (e.g. "dyn SomeTrait + Sync"). fn dyn_type_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, dyn_type: Ty<'tcx>, @@ -570,23 +570,23 @@ fn dyn_type_metadata<'ll, 'tcx>( } } -// Create debuginfo for `[T]` and `str`. These are unsized. -// -// Note: We currently emit just emit the debuginfo for the element type here -// (i.e. `T` for slices and `u8` for `str`), so that we end up with -// `*const T` for the `data_ptr` field of the corresponding fat-pointer -// debuginfo of `&[T]`. -// -// It would be preferable and more accurate if we emitted a DIArray of T -// without an upper bound instead. That is, LLVM already supports emitting -// debuginfo of arrays of unknown size. But GDB currently seems to end up -// in an infinite loop when confronted with such a type. -// -// As a side effect of the current encoding every instance of a type like -// `struct Foo { unsized_field: [u8] }` will look like -// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the -// slice is zero, then accessing `unsized_field` in the debugger would -// result in an out-of-bounds access. +/// Create debuginfo for `[T]` and `str`. These are unsized. +/// +/// Note: We currently emit just emit the debuginfo for the element type here +/// (i.e. `T` for slices and `u8` for `str`), so that we end up with +/// `*const T` for the `data_ptr` field of the corresponding fat-pointer +/// debuginfo of `&[T]`. +/// +/// It would be preferable and more accurate if we emitted a DIArray of T +/// without an upper bound instead. That is, LLVM already supports emitting +/// debuginfo of arrays of unknown size. But GDB currently seems to end up +/// in an infinite loop when confronted with such a type. +/// +/// As a side effect of the current encoding every instance of a type like +/// `struct Foo { unsized_field: [u8] }` will look like +/// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the +/// slice is zero, then accessing `unsized_field` in the debugger would +/// result in an out-of-bounds access. fn slice_type_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, slice_type: Ty<'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 272f1319ad180..f0612eaba8089 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -10,7 +10,6 @@ #![feature(let_else)] #![feature(extern_types)] #![feature(nll)] -#![feature(let_else)] #![recursion_limit = "256"] use back::write::{create_informational_target_machine, create_target_machine}; From 3c61d2e172becefe72a7468d2de0c67e69716163 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 25 Jan 2022 12:14:17 +0100 Subject: [PATCH 4/6] [debuginfo] Fix and unify handling of fat pointers in debuginfo: Fix some debuginfo tests for old GDB versions and 32-bit targets. --- src/test/debuginfo/unsized.rs | 26 ++++++++++------------ src/test/debuginfo/vec-slices.rs | 37 +++++++++++++++++--------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/test/debuginfo/unsized.rs b/src/test/debuginfo/unsized.rs index 1d1f3c0c76ece..ebd40f9dda21c 100644 --- a/src/test/debuginfo/unsized.rs +++ b/src/test/debuginfo/unsized.rs @@ -5,50 +5,44 @@ // gdb-command:run // gdb-command:print a -// gdbg-check:$1 = {data_ptr: [...], length: 4} +// gdbg-check:$1 = {data_ptr = [...], length = 4} // gdbr-check:$1 = &unsized::Foo<[u8]> {data_ptr: [...], length: 4} // gdb-command:print b -// gdbg-check:$2 = {data_ptr: [...], length: 4} +// gdbg-check:$2 = {data_ptr = [...], length = 4} // gdbr-check:$2 = &unsized::Foo> {data_ptr: [...], length: 4} // gdb-command:print c -// gdbg-check:$3 = {pointer: [...], vtable: [...]} +// gdbg-check:$3 = {pointer = [...], vtable = [...]} // gdbr-check:$3 = &unsized::Foo {pointer: [...], vtable: [...]} - // === CDB TESTS =================================================================================== // cdb-command: g // cdb-command:dx a // cdb-check:a [Type: ref$ > >] // cdb-check: [+0x000] data_ptr : 0x[...] [Type: unsized::Foo > *] -// cdb-check: [+0x008] length : 0x4 [Type: unsigned __int64] +// cdb-check: [...] length : 0x4 [Type: unsigned [...]int[...] // cdb-command:dx b // cdb-check:b [Type: ref$ > > >] // cdb-check: [+0x000] data_ptr : 0x[...] [Type: unsized::Foo > > *] -// cdb-check: [+0x008] length : 0x4 [Type: unsigned __int64] +// cdb-check: [...] length : 0x4 [Type: unsigned [...]int[...] // cdb-command:dx c // cdb-check:c [Type: ref$ > >] // cdb-check: [+0x000] pointer : 0x[...] [Type: unsized::Foo > *] -// cdb-check: [+0x008] vtable : 0x[...] [Type: unsigned __int64 (*)[3]] - +// cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] #![feature(omit_gdb_pretty_printer_section)] #![omit_gdb_pretty_printer_section] struct Foo { - value: T + value: T, } fn main() { - let foo: Foo> = Foo { - value: Foo { - value: *b"abc\0" - } - }; + let foo: Foo> = Foo { value: Foo { value: *b"abc\0" } }; // We expect `a`, `b`, and `c` to all be fat pointers. // `a` and `b` should be slice-like and thus have a `data_ptr` and `length` field. @@ -60,4 +54,6 @@ fn main() { zzz(); // #break } -fn zzz() { () } +fn zzz() { + () +} diff --git a/src/test/debuginfo/vec-slices.rs b/src/test/debuginfo/vec-slices.rs index 3525e8d83b8e2..7d88e45caf257 100644 --- a/src/test/debuginfo/vec-slices.rs +++ b/src/test/debuginfo/vec-slices.rs @@ -48,21 +48,24 @@ // gdbg-check:$13 = {x = 13, y = 14, z = 15} // gdbr-check:$13 = vec_slices::AStruct {x: 13, y: 14, z: 15} -// gdbg-command:print 'vec_slices::MUT_VECT_SLICE'.length -// gdbr-command:print MUT_VECT_SLICE.length -// gdb-check:$14 = 2 -// gdbg-command:print *((i64[2]*)('vec_slices::MUT_VECT_SLICE'.data_ptr)) -// gdbr-command:print *(MUT_VECT_SLICE.data_ptr as *const [i64; 2]) -// gdbg-check:$15 = {64, 65} -// gdbr-check:$15 = [64, 65] - // gdb-command:print mut_slice.length -// gdb-check:$16 = 5 +// gdb-check:$14 = 5 // gdbg-command:print *((i64[5]*)(mut_slice.data_ptr)) // gdbr-command:print *(mut_slice.data_ptr as *const [i64; 5]) -// gdbg-check:$17 = {1, 2, 3, 4, 5} -// gdbr-check:$17 = [1, 2, 3, 4, 5] +// gdbg-check:$15 = {1, 2, 3, 4, 5} +// gdbr-check:$15 = [1, 2, 3, 4, 5] +// Some lines below are marked with [ignored] because old GDB versions seem to have trouble +// accessing globals. + +// [ignored] gdbg-command:print 'vec_slices::MUT_VECT_SLICE'.length +// gdbr-command:print MUT_VECT_SLICE.length +// [ignored] gdbg-check:$16 = 2 +// gdbr-check:$16 = 2 +// [ignored] gdbg-command:print *((i64[2]*)('vec_slices::MUT_VECT_SLICE'.data_ptr)) +// gdbr-command:print *(MUT_VECT_SLICE.data_ptr as *const [i64; 2]) +// [ignored] gdbg-check:$17 = {64, 65} +// gdbr-check:$17 = [64, 65] // === LLDB TESTS ================================================================================== @@ -99,7 +102,7 @@ struct AStruct { x: i16, y: i32, - z: i16 + z: i16, } static VECT_SLICE: &'static [i64] = &[64, 65]; @@ -113,10 +116,8 @@ fn main() { let padded_tuple: &[(i32, i16)] = &[(6, 7), (8, 9)]; - let padded_struct: &[AStruct] = &[ - AStruct { x: 10, y: 11, z: 12 }, - AStruct { x: 13, y: 14, z: 15 } - ]; + let padded_struct: &[AStruct] = + &[AStruct { x: 10, y: 11, z: 12 }, AStruct { x: 13, y: 14, z: 15 }]; unsafe { MUT_VECT_SLICE = VECT_SLICE; @@ -127,4 +128,6 @@ fn main() { zzz(); // #break } -fn zzz() {()} +fn zzz() { + () +} From d33e317a72548ad9fb1439fcd1cfa53d17aefd84 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 27 Jan 2022 16:55:48 +0100 Subject: [PATCH 5/6] [debuginfo] Fix and unify handling of fat pointers in debuginfo: Change doc comment so it is not interpreted as doc-test. --- .../src/debuginfo/metadata.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index b5d86aab88019..dd9c83481afd6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -572,21 +572,21 @@ fn dyn_type_metadata<'ll, 'tcx>( /// Create debuginfo for `[T]` and `str`. These are unsized. /// -/// Note: We currently emit just emit the debuginfo for the element type here -/// (i.e. `T` for slices and `u8` for `str`), so that we end up with -/// `*const T` for the `data_ptr` field of the corresponding fat-pointer -/// debuginfo of `&[T]`. +/// NOTE: We currently emit just emit the debuginfo for the element type here +/// (i.e. `T` for slices and `u8` for `str`), so that we end up with +/// `*const T` for the `data_ptr` field of the corresponding fat-pointer +/// debuginfo of `&[T]`. /// -/// It would be preferable and more accurate if we emitted a DIArray of T -/// without an upper bound instead. That is, LLVM already supports emitting -/// debuginfo of arrays of unknown size. But GDB currently seems to end up -/// in an infinite loop when confronted with such a type. +/// It would be preferable and more accurate if we emitted a DIArray of T +/// without an upper bound instead. That is, LLVM already supports emitting +/// debuginfo of arrays of unknown size. But GDB currently seems to end up +/// in an infinite loop when confronted with such a type. /// -/// As a side effect of the current encoding every instance of a type like -/// `struct Foo { unsized_field: [u8] }` will look like -/// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the -/// slice is zero, then accessing `unsized_field` in the debugger would -/// result in an out-of-bounds access. +/// As a side effect of the current encoding every instance of a type like +/// `struct Foo { unsized_field: [u8] }` will look like +/// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the +/// slice is zero, then accessing `unsized_field` in the debugger would +/// result in an out-of-bounds access. fn slice_type_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, slice_type: Ty<'tcx>, From c10f9e7d1d6deba9797642fdd24a01ba9bb25d47 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 28 Jan 2022 10:41:36 +0100 Subject: [PATCH 6/6] [debuginfo] Fix and unify handling of fat pointers in debuginfo: Don't mark fat pointer fields as artificial. LLDB does not seem to see fields if they are marked with DW_AT_artificial which breaks pretty printers that use these fields for decoding fat pointers. --- compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index dd9c83481afd6..1266b540aaeb1 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -491,7 +491,7 @@ fn pointer_or_reference_metadata<'ll, 'tcx>( offset: layout.fields.offset(abi::FAT_PTR_ADDR), size: addr_field.size, align: addr_field.align.abi, - flags: DIFlags::FlagArtificial, + flags: DIFlags::FlagZero, discriminant: None, source_info: None, }, @@ -501,7 +501,7 @@ fn pointer_or_reference_metadata<'ll, 'tcx>( offset: layout.fields.offset(abi::FAT_PTR_EXTRA), size: extra_field.size, align: extra_field.align.abi, - flags: DIFlags::FlagArtificial, + flags: DIFlags::FlagZero, discriminant: None, source_info: None, },