Skip to content

Commit

Permalink
Rollup merge of rust-lang#137643 - beetrees:repr128-dwarf-variant-tes…
Browse files Browse the repository at this point in the history
…t, r=jieyouxu

Add DWARF test case for non-C-like `repr128` enums

LLVM 20 fixes DWARF debuginfo for non-C-like 128-bit enums: this PR adds a test case to the `repr128-dwarf` test to ensure that LLVM doesn't regress in the future.

Tracking issue: rust-lang#56071
  • Loading branch information
workingjubilee authored Mar 4, 2025
2 parents 25a392f + a7bd4a3 commit 1e4302e
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 21 deletions.
25 changes: 25 additions & 0 deletions tests/run-make/repr128-dwarf/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,33 @@ pub enum I128Enum {
I128D = i128::MAX.to_le(),
}

#[cfg(not(old_llvm))]
#[repr(u128)]
pub enum U128VariantEnum {
VariantU128A(u8) = 0_u128.to_le(),
VariantU128B = 1_u128.to_le(),
VariantU128C = (u64::MAX as u128 + 1).to_le(),
VariantU128D = u128::MAX.to_le(),
}

#[cfg(not(old_llvm))]
#[repr(i128)]
pub enum I128VariantEnum {
VariantI128A(u8) = 0_i128.to_le(),
VariantI128B = (-1_i128).to_le(),
VariantI128C = i128::MIN.to_le(),
VariantI128D = i128::MAX.to_le(),
}

pub fn f(_: U128Enum, _: I128Enum) {}

#[cfg(not(old_llvm))]
pub fn g(_: U128VariantEnum, _: I128VariantEnum) {}

fn main() {
f(U128Enum::U128A, I128Enum::I128A);
#[cfg(not(old_llvm))]
{
g(U128VariantEnum::VariantU128A(1), I128VariantEnum::VariantI128A(2));
}
}
115 changes: 94 additions & 21 deletions tests/run-make/repr128-dwarf/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,32 @@ use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;

use gimli::read::DebuggingInformationEntry;
use gimli::{AttributeValue, EndianRcSlice, Reader, RunTimeEndian};
use object::{Object, ObjectSection};
use run_make_support::{gimli, object, rfs, rustc};

fn main() {
// Before LLVM 20, 128-bit enums with variants didn't emit debuginfo correctly.
// This check can be removed once Rust no longer supports LLVM 18 and 19.
let llvm_version = rustc()
.verbose()
.arg("--version")
.run()
.stdout_utf8()
.lines()
.filter_map(|line| line.strip_prefix("LLVM version: "))
.map(|version| version.split(".").next().unwrap().parse::<u32>().unwrap())
.next()
.unwrap();
let is_old_llvm = llvm_version < 20;

let output = PathBuf::from("repr128");
rustc().input("main.rs").output(&output).arg("-Cdebuginfo=2").run();
let mut rustc = rustc();
if is_old_llvm {
rustc.cfg("old_llvm");
}
rustc.input("main.rs").output(&output).arg("-Cdebuginfo=2").run();
// Mach-O uses packed debug info
let dsym_location = output
.with_extension("dSYM")
Expand All @@ -29,7 +48,8 @@ fn main() {
})
.unwrap();
let mut iter = dwarf.units();
let mut still_to_find = HashMap::from([

let mut enumerators_to_find = HashMap::from([
("U128A", 0_u128),
("U128B", 1_u128),
("U128C", u64::MAX as u128 + 1),
Expand All @@ -39,35 +59,88 @@ fn main() {
("I128C", i128::MIN as u128),
("I128D", i128::MAX as u128),
]);
let mut variants_to_find = HashMap::from([
("VariantU128A", 0_u128),
("VariantU128B", 1_u128),
("VariantU128C", u64::MAX as u128 + 1),
("VariantU128D", u128::MAX),
("VariantI128A", 0_i128 as u128),
("VariantI128B", (-1_i128) as u128),
("VariantI128C", i128::MIN as u128),
("VariantI128D", i128::MAX as u128),
]);

while let Some(header) = iter.next().unwrap() {
let unit = dwarf.unit(header).unwrap();
let mut cursor = unit.entries();

let get_name = |entry: &DebuggingInformationEntry<'_, '_, _>| {
let name = dwarf
.attr_string(
&unit,
entry.attr(gimli::constants::DW_AT_name).unwrap().unwrap().value(),
)
.unwrap();
name.to_string().unwrap().to_string()
};

while let Some((_, entry)) = cursor.next_dfs().unwrap() {
if entry.tag() == gimli::constants::DW_TAG_enumerator {
let name = dwarf
.attr_string(
&unit,
entry.attr(gimli::constants::DW_AT_name).unwrap().unwrap().value(),
)
.unwrap();
let name = name.to_string().unwrap();
if let Some(expected) = still_to_find.remove(name.as_ref()) {
match entry.attr(gimli::constants::DW_AT_const_value).unwrap().unwrap().value()
match entry.tag() {
gimli::constants::DW_TAG_variant if !is_old_llvm => {
let value = match entry
.attr(gimli::constants::DW_AT_discr_value)
.unwrap()
.unwrap()
.value()
{
AttributeValue::Block(value) => {
assert_eq!(
value.to_slice().unwrap(),
expected.to_le_bytes().as_slice(),
"{name}"
);
AttributeValue::Block(value) => value.to_slice().unwrap().to_vec(),
value => panic!("unexpected DW_AT_discr_value of {value:?}"),
};
// The `DW_TAG_member` that is a child of `DW_TAG_variant` will contain the
// variant's name.
let Some((1, child_entry)) = cursor.next_dfs().unwrap() else {
panic!("Missing child of DW_TAG_variant");
};
assert_eq!(child_entry.tag(), gimli::constants::DW_TAG_member);
let name = get_name(child_entry);
if let Some(expected) = variants_to_find.remove(name.as_str()) {
// This test uses LE byte order is used for consistent values across
// architectures.
assert_eq!(value.as_slice(), expected.to_le_bytes().as_slice(), "{name}");
}
}

gimli::constants::DW_TAG_enumerator => {
let name = get_name(entry);
if let Some(expected) = enumerators_to_find.remove(name.as_str()) {
match entry
.attr(gimli::constants::DW_AT_const_value)
.unwrap()
.unwrap()
.value()
{
AttributeValue::Block(value) => {
// This test uses LE byte order is used for consistent values across
// architectures.
assert_eq!(
value.to_slice().unwrap(),
expected.to_le_bytes().as_slice(),
"{name}"
);
}
value => panic!("{name}: unexpected DW_AT_const_value of {value:?}"),
}
value => panic!("{name}: unexpected DW_AT_const_value of {value:?}"),
}
}

_ => {}
}
}
}
if !still_to_find.is_empty() {
panic!("Didn't find debug entries for {still_to_find:?}");
if !enumerators_to_find.is_empty() {
panic!("Didn't find debug enumerator entries for {enumerators_to_find:?}");
}
if !is_old_llvm && !variants_to_find.is_empty() {
panic!("Didn't find debug variant entries for {variants_to_find:?}");
}
}

0 comments on commit 1e4302e

Please sign in to comment.