Skip to content

Commit

Permalink
Use the enum2$ Natvis visualiser for repr128 C-style enums
Browse files Browse the repository at this point in the history
  • Loading branch information
beetrees committed Jul 21, 2024
1 parent 92c6c03 commit 98dabb6
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use std::borrow::Cow;

use libc::c_uint;
use rustc_codegen_ssa::{
debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo},
debuginfo::{
tag_base_type, type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo,
},
traits::ConstMethods,
};

Expand All @@ -23,7 +25,7 @@ use crate::{
debuginfo::{
metadata::{
build_field_di_node,
enums::{tag_base_type, DiscrResult},
enums::DiscrResult,
file_metadata, size_and_align_of, type_di_node,
type_map::{self, Stub, UniqueTypeId},
unknown_file_metadata, visibility_di_flags, DINodeCreationResult, SmallVec,
Expand Down Expand Up @@ -204,7 +206,10 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
let enum_type_and_layout = cx.layout_of(enum_type);
let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);

debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout));
debug_assert!(
!wants_c_like_enum_debuginfo(enum_type_and_layout)
|| tag_base_type(cx.tcx, enum_type_and_layout).primitive_size(cx.tcx).bits() == 128
);

type_map::build_type_with_children(
cx,
Expand Down Expand Up @@ -395,7 +400,7 @@ fn build_union_fields_for_enum<'ll, 'tcx>(
tag_field: usize,
untagged_variant_index: Option<VariantIdx>,
) -> SmallVec<&'ll DIType> {
let tag_base_type = super::tag_base_type(cx, enum_type_and_layout);
let tag_base_type = tag_base_type(cx.tcx, enum_type_and_layout);

let variant_names_type_di_node = build_variant_names_type_di_node(
cx,
Expand Down Expand Up @@ -690,7 +695,7 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>(
let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx);
let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len();

let tag_base_type = tag_base_type(cx, coroutine_type_and_layout);
let tag_base_type = tag_base_type(cx.tcx, coroutine_type_and_layout);

let variant_names_type_di_node = build_variant_names_type_di_node(
cx,
Expand Down Expand Up @@ -817,7 +822,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(

debug_assert_eq!(
cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
cx.size_and_align_of(super::tag_base_type(cx, enum_type_and_layout))
cx.size_and_align_of(self::tag_base_type(cx.tcx, enum_type_and_layout))
);

// ... and a field for the tag. If the tag is 128 bits wide, this will actually
Expand Down
57 changes: 10 additions & 47 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_codegen_ssa::debuginfo::{
tag_base_type,
type_names::{compute_debuginfo_type_name, cpp_like_debuginfo},
wants_c_like_enum_debuginfo,
};
Expand All @@ -9,14 +10,12 @@ use rustc_middle::{
mir::CoroutineLayout,
ty::{
self,
layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout},
layout::{LayoutOf, TyAndLayout},
AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef,
},
};
use rustc_span::Symbol;
use rustc_target::abi::{
FieldIdx, HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants,
};
use rustc_target::abi::{FieldIdx, TagEncoding, VariantIdx, Variants};
use std::borrow::Cow;

use crate::{
Expand Down Expand Up @@ -55,6 +54,12 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(

let enum_type_and_layout = cx.layout_of(enum_type);

if cpp_like_debuginfo(cx.tcx)
&& tag_base_type(cx.tcx, enum_type_and_layout).primitive_size(cx.tcx).bits() == 128
{
return cpp_like::build_enum_type_di_node(cx, unique_type_id);
}

if wants_c_like_enum_debuginfo(enum_type_and_layout) {
return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout);
}
Expand Down Expand Up @@ -90,7 +95,7 @@ fn build_c_style_enum_di_node<'ll, 'tcx>(
di_node: build_enumeration_type_di_node(
cx,
&compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false),
tag_base_type(cx, enum_type_and_layout),
tag_base_type(cx.tcx, enum_type_and_layout),
enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
(name, discr.val)
Expand All @@ -101,48 +106,6 @@ fn build_c_style_enum_di_node<'ll, 'tcx>(
}
}

/// Extract the type with which we want to describe the tag of the given enum or coroutine.
fn tag_base_type<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
enum_type_and_layout: TyAndLayout<'tcx>,
) -> Ty<'tcx> {
debug_assert!(match enum_type_and_layout.ty.kind() {
ty::Coroutine(..) => true,
ty::Adt(adt_def, _) => adt_def.is_enum(),
_ => false,
});

match enum_type_and_layout.layout.variants() {
// A single-variant enum has no discriminant.
Variants::Single { .. } => {
bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout)
}

Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
// Niche tags are always normalized to unsized integers of the correct size.
match tag.primitive() {
Primitive::Int(t, _) => t,
Primitive::Float(f) => Integer::from_size(f.size()).unwrap(),
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
Primitive::Pointer(_) => {
// If the niche is the NULL value of a reference, then `discr_enum_ty` will be
// a RawPtr. CodeView doesn't know what to do with enums whose base type is a
// pointer so we fix this up to just be `usize`.
// DWARF might be able to deal with this but with an integer type we are on
// the safe side there too.
cx.data_layout().ptr_sized_integer()
}
}
.to_ty(cx.tcx, false)
}

Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
// Direct tags preserve the sign.
tag.primitive().to_ty(cx.tcx)
}
}
}

/// Build a DW_TAG_enumeration_type debuginfo node, with the given base type and variants.
/// This is a helper function and does not register anything in the type map by itself.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::{
common::CodegenCx,
debuginfo::{
metadata::{
enums::tag_base_type,
file_metadata, size_and_align_of, type_di_node,
type_map::{self, Stub, StubInfo, UniqueTypeId},
unknown_file_metadata, visibility_di_flags, DINodeCreationResult, SmallVec,
Expand All @@ -19,7 +18,9 @@ use crate::{
};
use libc::c_uint;
use rustc_codegen_ssa::{
debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo},
debuginfo::{
tag_base_type, type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo,
},
traits::ConstMethods,
};
use rustc_middle::{
Expand Down Expand Up @@ -332,7 +333,7 @@ fn build_discr_member_di_node<'ll, 'tcx>(
&Variants::Single { .. } => None,

&Variants::Multiple { tag_field, .. } => {
let tag_base_type = tag_base_type(cx, enum_or_coroutine_type_and_layout);
let tag_base_type = tag_base_type(cx.tcx, enum_or_coroutine_type_and_layout);
let (size, align) = cx.size_and_align_of(tag_base_type);

unsafe {
Expand Down
48 changes: 46 additions & 2 deletions compiler/rustc_codegen_ssa/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use rustc_middle::ty::{self, layout::TyAndLayout};
use rustc_target::abi::Size;
use rustc_middle::bug;
use rustc_middle::ty::{
self,
layout::{IntegerExt, PrimitiveExt, TyAndLayout},
Ty, TyCtxt,
};
use rustc_target::abi::{Integer, Primitive, Size, TagEncoding, Variants};

// FIXME(eddyb) find a place for this (or a way to replace it).
pub mod type_names;
Expand Down Expand Up @@ -32,3 +37,42 @@ pub fn wants_c_like_enum_debuginfo(enum_type_and_layout: TyAndLayout<'_>) -> boo
_ => false,
}
}

/// Extract the type with which we want to describe the tag of the given enum or coroutine.
pub fn tag_base_type<'tcx>(tcx: TyCtxt<'tcx>, enum_type_and_layout: TyAndLayout<'tcx>) -> Ty<'tcx> {
debug_assert!(match enum_type_and_layout.ty.kind() {
ty::Coroutine(..) => true,
ty::Adt(adt_def, _) => adt_def.is_enum(),
_ => false,
});

match enum_type_and_layout.layout.variants() {
// A single-variant enum has no discriminant.
Variants::Single { .. } => {
bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout)
}

Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
// Niche tags are always normalized to unsized integers of the correct size.
match tag.primitive() {
Primitive::Int(t, _) => t,
Primitive::Float(f) => Integer::from_size(f.size()).unwrap(),
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
Primitive::Pointer(_) => {
// If the niche is the NULL value of a reference, then `discr_enum_ty` will be
// a RawPtr. CodeView doesn't know what to do with enums whose base type is a
// pointer so we fix this up to just be `usize`.
// DWARF might be able to deal with this but with an integer type we are on
// the safe side there too.
tcx.data_layout.ptr_sized_integer()
}
}
.to_ty(tcx, false)
}

Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
// Direct tags preserve the sign.
tag.primitive().to_ty(tcx)
}
}
}
14 changes: 11 additions & 3 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use smallvec::SmallVec;

use std::fmt::Write;

use crate::debuginfo::wants_c_like_enum_debuginfo;
use crate::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};

/// Compute the name of the type as it should be stored in debuginfo. Does not do
/// any caching, i.e., calling the function twice with the same type will also do
Expand Down Expand Up @@ -84,7 +84,9 @@ fn push_debuginfo_type_name<'tcx>(
let layout_for_cpp_like_fallback = if cpp_like_debuginfo && def.is_enum() {
match tcx.layout_of(ParamEnv::reveal_all().and(t)) {
Ok(layout) => {
if !wants_c_like_enum_debuginfo(layout) {
if !wants_c_like_enum_debuginfo(layout)
|| tag_base_type(tcx, layout).primitive_size(tcx).bits() == 128
{
Some(layout)
} else {
// This is a C-like enum so we don't want to use the fallback encoding
Expand All @@ -105,6 +107,7 @@ fn push_debuginfo_type_name<'tcx>(

if let Some(ty_and_layout) = layout_for_cpp_like_fallback {
msvc_enum_fallback(
tcx,
ty_and_layout,
&|output, visited| {
push_item_name(tcx, def.did(), true, output);
Expand Down Expand Up @@ -420,6 +423,7 @@ fn push_debuginfo_type_name<'tcx>(
if cpp_like_debuginfo && t.is_coroutine() {
let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap();
msvc_enum_fallback(
tcx,
ty_and_layout,
&|output, visited| {
push_closure_or_coroutine_name(tcx, def_id, args, true, output, visited);
Expand Down Expand Up @@ -454,12 +458,16 @@ fn push_debuginfo_type_name<'tcx>(
// debugger. For more information, look in
// rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs.
fn msvc_enum_fallback<'tcx>(
tcx: TyCtxt<'tcx>,
ty_and_layout: TyAndLayout<'tcx>,
push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
output: &mut String,
visited: &mut FxHashSet<Ty<'tcx>>,
) {
debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout));
debug_assert!(
!wants_c_like_enum_debuginfo(ty_and_layout)
|| super::tag_base_type(tcx, ty_and_layout).primitive_size(tcx).bits() == 128
);
output.push_str("enum2$<");
push_inner(output, visited);
push_close_angle_bracket(true, output);
Expand Down
50 changes: 50 additions & 0 deletions tests/debuginfo/msvc-pretty-enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,30 @@
// cdb-command: dx -r2 arbitrary_discr2,d
// cdb-check: arbitrary_discr2,d : Def [Type: enum2$<msvc_pretty_enums::ArbitraryDiscr>]
// cdb-check: [+0x[...]] __0 : 5678 [Type: unsigned int]
//
// cdb-command: dx c_style_u128_a
// cdb-check: c_style_u128_a : A [Type: enum2$<msvc_pretty_enums::CStyleU128>]
//
// cdb-command: dx c_style_u128_b
// cdb-check: c_style_u128_b : B [Type: enum2$<msvc_pretty_enums::CStyleU128>]
//
// cdb-command: dx c_style_u128_c
// cdb-check: c_style_u128_c : C [Type: enum2$<msvc_pretty_enums::CStyleU128>]
//
// cdb-command: dx c_style_u128_d
// cdb-check: c_style_u128_d : D [Type: enum2$<msvc_pretty_enums::CStyleU128>]
//
// cdb-command: dx c_style_i128_a
// cdb-check: c_style_i128_a : A [Type: enum2$<msvc_pretty_enums::CStyleI128>]
//
// cdb-command: dx c_style_i128_b
// cdb-check: c_style_i128_b : B [Type: enum2$<msvc_pretty_enums::CStyleI128>]
//
// cdb-command: dx c_style_i128_c
// cdb-check: c_style_i128_c : C [Type: enum2$<msvc_pretty_enums::CStyleI128>]
//
// cdb-command: dx c_style_i128_d
// cdb-check: c_style_i128_d : D [Type: enum2$<msvc_pretty_enums::CStyleI128>]
#![feature(rustc_attrs)]
#![feature(repr128)]
#![feature(arbitrary_enum_discriminant)]
Expand Down Expand Up @@ -270,6 +294,22 @@ enum ArbitraryDiscr {
Def(u32) = 5000_000,
}

#[repr(u128)]
pub enum CStyleU128 {
U128A = 0_u128,
U128B = 1_u128,
U128C = u64::MAX as u128 + 1,
U128D = u128::MAX,
}

#[repr(i128)]
pub enum CStyleI128 {
I128A = 0_i128,
I128B = -1_i128,
I128C = i128::MIN,
I128D = i128::MAX,
}

fn main() {
let a = Some(CStyleEnum::Low);
let b = Option::<CStyleEnum>::None;
Expand Down Expand Up @@ -313,6 +353,16 @@ fn main() {
let arbitrary_discr1 = ArbitraryDiscr::Abc(1234);
let arbitrary_discr2 = ArbitraryDiscr::Def(5678);

let c_style_u128_a = CStyleU128::A;
let c_style_u128_b = CStyleU128::B;
let c_style_u128_c = CStyleU128::C;
let c_style_u128_d = CStyleU128::D;

let c_style_i128_a = CStyleI128::A;
let c_style_i128_b = CStyleI128::B;
let c_style_i128_c = CStyleI128::C;
let c_style_i128_d = CStyleI128::D;

zzz(); // #break
}

Expand Down

0 comments on commit 98dabb6

Please sign in to comment.