From a7bd4a309c2d154ab4bafee9b575d4196b5d3222 Mon Sep 17 00:00:00 2001 From: beetrees Date: Tue, 25 Feb 2025 22:26:52 +0000 Subject: [PATCH] Add DWARF test case for non-C-like `repr128` enums --- tests/run-make/repr128-dwarf/main.rs | 25 ++++++ tests/run-make/repr128-dwarf/rmake.rs | 115 +++++++++++++++++++++----- 2 files changed, 119 insertions(+), 21 deletions(-) diff --git a/tests/run-make/repr128-dwarf/main.rs b/tests/run-make/repr128-dwarf/main.rs index 57923a8386db9..9842ab4a3426f 100644 --- a/tests/run-make/repr128-dwarf/main.rs +++ b/tests/run-make/repr128-dwarf/main.rs @@ -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)); + } } diff --git a/tests/run-make/repr128-dwarf/rmake.rs b/tests/run-make/repr128-dwarf/rmake.rs index 2fd54c186b934..15eb186717f6d 100644 --- a/tests/run-make/repr128-dwarf/rmake.rs +++ b/tests/run-make/repr128-dwarf/rmake.rs @@ -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::().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") @@ -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), @@ -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:?}"); } }