Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 51 additions & 50 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::PathBuf;
use std::{assert_matches, iter, ptr};

use libc::{c_longlong, c_uint};
use rustc_abi::{Align, Layout, NumScalableVectors, Size};
use rustc_abi::{Align, Endian, Layout, NumScalableVectors, Size};
use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo};
use rustc_codegen_ssa::traits::*;
use rustc_hir::def::{CtorKind, DefKind};
Expand All @@ -20,7 +20,7 @@ use rustc_middle::ty::{
use rustc_session::config::{self, DebugInfo, Lto};
use rustc_span::{DUMMY_SP, FileName, RemapPathScopeComponents, SourceFile, Span, Symbol, hygiene};
use rustc_symbol_mangling::typeid_for_trait_ref;
use rustc_target::spec::{Arch, DebuginfoKind};
use rustc_target::spec::{Arch, DebuginfoKind, HasTargetSpec};
use smallvec::smallvec;
use tracing::{debug, instrument};

Expand Down Expand Up @@ -692,66 +692,56 @@ impl MsvcBasicName for ty::UintTy {
}
}

impl MsvcBasicName for ty::FloatTy {
fn msvc_basic_name(self) -> &'static str {
// FIXME(f128): `f128` has no MSVC representation. We could improve the debuginfo.
// See: <https://github.com/rust-lang/rust/issues/121837>
match self {
ty::FloatTy::F16 => {
bug!("`f16` should have been handled in `build_basic_type_di_node`")
}
ty::FloatTy::F32 => "float",
ty::FloatTy::F64 => "double",
ty::FloatTy::F128 => "fp128",
}
}
}

fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreationResult<'ll> {
// MSVC has no native support for `f16`. Instead, emit `struct f16 { bits: u16 }` to allow the
// `f16`'s value to be displayed using a Natvis visualiser in `intrinsic.natvis`.
let float_ty = cx.tcx.types.f16;
let bits_ty = cx.tcx.types.u16;
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
match float_ty.kind() {
ty::Adt(def, _) => Some(file_metadata_from_def_id(cx, Some(def.did()))),
_ => None,
}
/// `float_ty` must be a [`ty::Float`] and `bits_ty` must be a [`ty::Uint`].
/// `cx.size_of(bits_ty) * bits_names.len()` must equal `cx.size_of(float_ty)`.
fn build_cpp_float_struct_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
float_ty: Ty<'tcx>,
bits_ty: Ty<'tcx>,
bits_names: &[&str],
) -> DINodeCreationResult<'ll> {
debug_assert!(matches!(bits_ty.kind(), ty::Uint(_)));
debug_assert_eq!(cx.size_of(bits_ty) * (bits_names.len() as u64), cx.size_of(float_ty));
// MSVC has no native support for `f16` or `f128`. Instead, emit a struct containing the bits as
// field(s) to allow the value to be displayed using a Natvis visualiser in `intrinsic.natvis`.
let name = if let ty::Float(f) = float_ty.kind() {
f.name_str()
} else {
None
bug!("{float_ty:?} was not a float");
};
type_map::build_type_with_children(
cx,
type_map::stub(
cx,
Stub::Struct,
UniqueTypeId::for_ty(cx.tcx, float_ty),
"f16",
def_location,
name,
None,
cx.size_and_align_of(float_ty),
NO_SCOPE_METADATA,
DIFlags::FlagZero,
),
// Fields:
|cx, float_di_node| {
let def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
match bits_ty.kind() {
ty::Adt(def, _) => Some(def.did()),
_ => None,
}
} else {
None
};
smallvec![build_field_di_node(
cx,
float_di_node,
"bits",
cx.layout_of(bits_ty),
Size::ZERO,
DIFlags::FlagZero,
type_di_node(cx, bits_ty),
def_id,
)]
let bits_layout = cx.layout_of(bits_ty);
let bits_node = type_di_node(cx, bits_ty);
bits_names
.iter()
.copied()
.enumerate()
.map(|(i, field_name)| {
build_field_di_node(
cx,
float_di_node,
field_name,
bits_layout,
bits_layout.size * (i as u64),
DIFlags::FlagZero,
bits_node,
None,
)
})
.collect()
},
NO_GENERICS,
)
Expand Down Expand Up @@ -783,9 +773,20 @@ fn build_basic_type_di_node<'ll, 'tcx>(
ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed),
ty::Uint(uint_ty) if cpp_like_debuginfo => (uint_ty.msvc_basic_name(), DW_ATE_unsigned),
ty::Float(ty::FloatTy::F16) if cpp_like_debuginfo => {
return build_cpp_f16_di_node(cx);
return build_cpp_float_struct_di_node(cx, t, cx.tcx.types.u16, &["bits"]);
}
ty::Float(ty::FloatTy::F128) if cpp_like_debuginfo => {
// All MSVC architectures are little endian.
assert_eq!(cx.target_spec().endian, Endian::Little);
return build_cpp_float_struct_di_node(
cx,
t,
cx.tcx.types.u64,
&["low_bits", "high_bits"],
);
}
ty::Float(float_ty) if cpp_like_debuginfo => (float_ty.msvc_basic_name(), DW_ATE_float),
ty::Float(ty::FloatTy::F32) if cpp_like_debuginfo => ("float", DW_ATE_float),
ty::Float(ty::FloatTy::F64) if cpp_like_debuginfo => ("double", DW_ATE_float),
ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed),
ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned),
ty::Float(float_ty) => (float_ty.name_str(), DW_ATE_float),
Expand Down
112 changes: 112 additions & 0 deletions src/etc/natvis/intrinsic.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,118 @@
<!-- Normal -->
<DisplayString>{(float) (sign() * (raw_significand() + 1.0) * two_pow_exponent())}</DisplayString>
</Type>
<Type Name="f128">
<Intrinsic Name="sign_mask" Expression="(unsigned __int64) 0x8000000000000000" />
<Intrinsic Name="exponent_mask" Expression="(unsigned __int64) 0x7fff000000000000" />
<Intrinsic Name="high_significand_mask" Expression="(unsigned __int64) 0x0000ffffffffffff" />
<Intrinsic Name="sign_bit" Expression="(unsigned __int64) (high_bits &amp; sign_mask())" />
<Intrinsic Name="exponent_bits" Expression="(unsigned __int64) (high_bits &amp; exponent_mask())" />
<Intrinsic Name="high_significand_bits" Expression="(unsigned __int64) (high_bits &amp; high_significand_mask())" />
<Intrinsic Name="high_non_sign_bits" Expression="(unsigned __int64) (high_bits &amp; ~sign_mask())" />

<!-- Either "-" or an empty string for positive numbers -->
<Intrinsic Name="sign" Expression="sign_bit() == 0 ? &quot;&quot; : &quot;-&quot;" />
<Intrinsic Name="normal_exponent" Expression="(__int64) (((__int64) (exponent_bits() &gt;&gt; 48)) - 0x3fff)" />
<!-- "+" if the exponent is positive or zero, otherwise an empty string as converting a negative
exponent to a decimal string will already prepend a "-". -->
<Intrinsic Name="normal_exponent_sign" Expression="normal_exponent() >= 0 ? &quot;+&quot; : &quot;&quot;" />

<!-- Converts a number between 0 and 15 (inclusive) to a single hexdecimal character -->
<Intrinsic Name="to_hex" Expression="v == 0x0 ? &quot;0&quot; : v == 0x1 ? &quot;1&quot; : v == 0x2 ? &quot;2&quot; : v == 0x3 ? &quot;3&quot; : v == 0x4 ? &quot;4&quot; : v == 0x5 ? &quot;5&quot; : v == 0x6 ? &quot;6&quot; : v == 0x7 ? &quot;7&quot; : v == 0x8 ? &quot;8&quot; : v == 0x9 ? &quot;9&quot; : v == 0xa ? &quot;a&quot; : v == 0xb ? &quot;b&quot; : v == 0xc ? &quot;c&quot; : v == 0xd ? &quot;d&quot; : v == 0xe ? &quot;e&quot; : v == 0xf ? &quot;f&quot; : &quot;ERROR&quot;">
<Parameter Name="v" Type="unsigned __int64" />
</Intrinsic>
<!-- hexN convert integers of size N to hexdecimal strings. `count` specifies how many bits should be displayed.
`count` is rounded up to the nearest hexdecimal digit, and is presumed to be non-zero -->
<Intrinsic Name="hex4" Expression="to_hex(value &amp; 0xf)">
<Parameter Name="value" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="hex8" Expression="count > 4 ? hex4(value >> 4) + hex4(value) : hex4(value)">
<Parameter Name="value" Type="unsigned __int64" />
<Parameter Name="count" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="hex16" Expression="count > 8 ? hex8(value >> 8, count - 8) + hex8(value, 8) : hex8(value, count)">
<Parameter Name="value" Type="unsigned __int64" />
<Parameter Name="count" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="hex32" Expression="count > 16 ? hex16(value >> 16, count - 16) + hex16(value, 16) : hex16(value, count)">
<Parameter Name="value" Type="unsigned __int64" />
<Parameter Name="count" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="hex64" Expression="count > 32 ? hex32(value >> 32, count - 32) + hex32(value, 32) : hex32(value, count)">
<Parameter Name="value" Type="unsigned __int64" />
<Parameter Name="count" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="hex128" Expression="count > 64 ? hex64(value_high, count - 64) + hex64(value_low, 64) : hex64(value_low, count)">
<Parameter Name="value_high" Type="unsigned __int64" />
<Parameter Name="value_low" Type="unsigned __int64" />
<Parameter Name="count" Type="unsigned __int64" />
</Intrinsic>

<!-- trailingN does the equivalent of `iN::trailing_zeros` in Rust. -->
<!-- We only need to count in whole hex digits (4 bits) -->
<Intrinsic Name="trailing4" Expression="(unsigned __int64) ((value &amp; 0xf) == 0 ? 4 : 0)">
<Parameter Name="value" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="trailing8" Expression="(unsigned __int64) ((value &amp; 0xf) == 0 ? 4 + trailing4(value &gt;&gt; 4) : trailing4(value))">
<Parameter Name="value" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="trailing16" Expression="(unsigned __int64) ((value &amp; 0xff) == 0 ? 8 + trailing8(value &gt;&gt; 8) : trailing8(value))">
<Parameter Name="value" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="trailing32" Expression="(unsigned __int64) ((value &amp; 0xffff) == 0 ? 16 + trailing16(value &gt;&gt; 16) : trailing16(value))">
<Parameter Name="value" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="trailing64" Expression="(unsigned __int64) ((value &amp; 0xffffffff) == 0 ? 32 + trailing32(value &gt;&gt; 32) : trailing32(value))">
<Parameter Name="value" Type="unsigned __int64" />
</Intrinsic>
<Intrinsic Name="trailing128" Expression="(unsigned __int64) (value_low == 0 ? 64 + trailing64(value_high) : trailing64(value_low))">
<Parameter Name="value_high" Type="unsigned __int64" />
<Parameter Name="value_low" Type="unsigned __int64" />
</Intrinsic>

<!-- Equivalent to `u128::funnel_shr` -->
<Intrinsic Name="funnel_shift_right" Expression="(unsigned __int64) (shift == 0 ? low : ((low &gt;&gt; shift) | (high &lt;&lt; (64 - shift))))">
<Parameter Name="high" Type="unsigned __int64" />
<Parameter Name="low" Type="unsigned __int64" />
<Parameter Name="shift" Type="unsigned __int64" />
</Intrinsic>
<!-- Equivalent to `u128::funnel_shl` -->
<Intrinsic Name="funnel_shift_left" Expression="(unsigned __int64) (shift == 0 ? high : ((high &lt;&lt; shift) | (low &gt;&gt; (64 - shift))))">
<Parameter Name="high" Type="unsigned __int64" />
<Parameter Name="low" Type="unsigned __int64" />
<Parameter Name="shift" Type="unsigned __int64" />
</Intrinsic>

<Intrinsic Name="bits_to_hex_inner" Expression="trailing >= 64 ? hex64(high &gt;&gt; (trailing - 64), 48 - (trailing - 64)) : hex128(high &gt;&gt; trailing, funnel_shift_right(high, low, trailing), 112 - trailing)">
<Parameter Name="high" Type="unsigned __int64" />
<Parameter Name="low" Type="unsigned __int64" />
<Parameter Name="trailing" Type="unsigned __int64" />
</Intrinsic>
<!-- Converts the low 48 bits of `high` and all 64 bits of `low` to a hexdecimal string, without
any trailing zeros. The result will start with a "." unless it is empty. -->
<!-- Trailing is precomputed here as an argument to `bits_to_hex_inner` avoid needing to
calculate it multiple times. -->
<Intrinsic Name="bits_to_hex" Expression="(high &amp; high_significand_mask()) == 0 &amp;&amp; low == 0 ? &quot;&quot; : &quot;.&quot; + bits_to_hex_inner(high, low, trailing128(high, low))">
<Parameter Name="high" Type="unsigned __int64" />
<Parameter Name="low" Type="unsigned __int64" />
</Intrinsic>

<Intrinsic Name="normal_hex" Expression="bits_to_hex(high_significand_bits(), low_bits)" />
<Intrinsic Name="subnormal_shift" Expression="(__int64) (high_significand_bits() == 0 ? 48 + (64 - __log2(low_bits)) : (48 - __log2(high_significand_bits())))" />
<Intrinsic Name="subnormal_hex" Expression="subnormal_shift() >= 64 ? bits_to_hex(low_bits &lt;&lt; (subnormal_shift() - 64), 0) : bits_to_hex(funnel_shift_left(high_significand_bits(), low_bits, subnormal_shift()), low_bits &lt;&lt; subnormal_shift())" />

<DisplayString Condition="high_non_sign_bits() == exponent_mask() &amp;&amp; low_bits == 0">{sign()}inf</DisplayString>
<DisplayString Condition="exponent_bits() == exponent_mask()">NaN</DisplayString>
<!-- Handle zeros separately so they use the conventional exponent "+0". -->
<DisplayString Condition="high_non_sign_bits() == 0 &amp;&amp; low_bits == 0">{sign()}0x0p+0</DisplayString>
<!-- Subnormals are shifted so that the integer portion is "1", with the exponent adjusted. -->
<DisplayString Condition="exponent_bits() == 0">{sign()}0x1{subnormal_hex()}p{-16382 - subnormal_shift(),d}</DisplayString>
<DisplayString>{sign()}0x1{normal_hex()}p{normal_exponent_sign()}{normal_exponent(),d}</DisplayString>
<Expand>
<!-- Display the bits as a single number instead of the two `u64` halves. -->
<Item Name="bits">"0x" + hex128(high_bits, low_bits, 128)</Item>
</Expand>
</Type>
<Type Name="tuple$&lt;&gt;">
<DisplayString>()</DisplayString>
</Type>
Expand Down
11 changes: 7 additions & 4 deletions tests/debuginfo/basic-types-globals-metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@
//@ gdb-check:type = f32
//@ gdb-command:whatis basic_types_globals_metadata::F64
//@ gdb-check:type = f64
//@ gdb-command:whatis basic_types_globals_metadata::F128
//@ gdb-check:type = f128
//@ gdb-command:continue

#![allow(unused_variables)]
#![allow(dead_code)]
#![feature(f16)]
#![feature(f16, f128)]

// N.B. These are `mut` only so they don't constant fold away.
static mut B: bool = false;
Expand All @@ -55,13 +57,14 @@ static mut U64: u64 = 64;
static mut F16: f16 = 1.5;
static mut F32: f32 = 2.5;
static mut F64: f64 = 3.5;
static mut F128: f128 = 4.5;

fn main() {
_zzz(); // #break

let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64) };
// FIXME: Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which
// does not exist on some targets like PowerPC.
let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64, F128) };
// FIXME(f16): Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which
// does not exist on some targets like PowerPC (fixed in llvm22).
// See https://github.com/llvm/llvm-project/issues/97981 and
// https://github.com/rust-lang/compiler-builtins/issues/655
let b = unsafe { F16 };
Expand Down
24 changes: 19 additions & 5 deletions tests/debuginfo/basic-types-globals.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
//@ revisions: lto no-lto
//@ revisions: lto no-lto lto-apple no-lto-apple

//@ compile-flags:-g --crate-name=basic_types_globals
//@ disable-gdb-pretty-printers

// FIXME(f128): Merge `-apple` revisions once Apple releases Xcode with LLVM 22.
//@ [lto] ignore-apple
//@ [no-lto] ignore-apple
//@ [lto-apple] only-apple
//@ [no-lto-apple] only-apple
//@ [lto] compile-flags:-C lto
//@ [lto] no-prefer-dynamic
//@ [lto-apple] compile-flags:-C lto
//@ [lto-apple] no-prefer-dynamic
//@ ignore-backends: gcc
// `f128` support was added to `lldb` in version 22.
//@ min-llvm-lldb-version: 22

//@ lldb-command:run
//@ lldb-command:v basic_types_globals::B
Expand Down Expand Up @@ -38,6 +47,9 @@
//@ lldb-check:[...]basic_types_globals::F32 = 2.5
//@ lldb-command:v basic_types_globals::F64
//@ lldb-check:[...]basic_types_globals::F64 = 3.5
//@ lldb-command:v F128
//@[no-lto] lldb-check:[...]basic_types_globals::F128 = 4.5
//@[lto] lldb-check:[...]basic_types_globals::F128 = 4.5

//@ gdb-command:run
//@ gdb-command:print B
Expand Down Expand Up @@ -70,10 +82,11 @@
//@ gdb-check:$14 = 2.5
//@ gdb-command:print F64
//@ gdb-check:$15 = 3.5
// FIXME(f128): gdb doesn't support Rust `f128` yet.
//@ gdb-command:continue

#![allow(unused_variables)]
#![feature(f16)]
#![feature(f16, f128)]

// N.B. These are `mut` only so they don't constant fold away.
static mut B: bool = false;
Expand All @@ -91,13 +104,14 @@ static mut U64: u64 = 64;
static mut F16: f16 = 1.5;
static mut F32: f32 = 2.5;
static mut F64: f64 = 3.5;
static mut F128: f128 = 4.5;

fn main() {
_zzz(); // #break

let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64) };
// FIXME: Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which
// does not exist on some targets like PowerPC.
let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64, F128) };
// FIXME(f16): Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which
// does not exist on some targets like PowerPC (fixed in llvm22).
// See https://github.com/llvm/llvm-project/issues/97981 and
// https://github.com/rust-lang/compiler-builtins/issues/655
let b = unsafe { F16 };
Expand Down
5 changes: 4 additions & 1 deletion tests/debuginfo/basic-types-metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
//@ gdb-check:type = f32
//@ gdb-command:whatis f64
//@ gdb-check:type = f64
//@ gdb-command:whatis f128
//@ gdb-check:type = f128
//@ gdb-command:whatis fnptr
//@ gdb-check:type = *mut fn ()
//@ gdb-command:info functions _yyy
Expand All @@ -54,7 +56,7 @@
//@ gdb-command:continue

#![allow(unused_variables)]
#![feature(f16)]
#![feature(f16, f128)]

fn main() {
let unit: () = ();
Expand All @@ -73,6 +75,7 @@ fn main() {
let f16: f16 = 1.5;
let f32: f32 = 2.5;
let f64: f64 = 3.5;
let f128: f128 = 4.5;
let fnptr : fn() = _zzz;
let closure_0 = || {};
let closure_1 = || { b; };
Expand Down
Loading
Loading