https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122968
Jakub Jelinek <jakub at gcc dot gnu.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |jakub at gcc dot gnu.org
--- Comment #4 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
There are many issues.
Better testcase:
// PR debug/122968
// { dg-do run { target int32plus } }
struct S { unsigned a : 5; unsigned b : 26; unsigned c : 1; } s = { 10,
53967718, 1 };
auto [ a, b, c ] = s;
[[gnu::noipa]] void
foo (S &s)
{
s.a = 26;
s.b = 65990711;
s.c = 0;
}
[[gnu::noipa]] void
bar (S &)
{
}
int
main ()
{
S s;
foo (s);
auto [ d, e, f ] = s;
unsigned g = d + 1;
unsigned h = e + 2;
unsigned i = f + 3;
bar (s);
bar (s);
// { dg-final { gdb-test 30 "a" "10" } }
// { dg-final { gdb-test 30 "b" "53967718" } }
// { dg-final { gdb-test 30 "c" "1" } }
// { dg-final { gdb-test 30 "d" "26" } }
// { dg-final { gdb-test 30 "e" "65990711" } }
// { dg-final { gdb-test 30 "f" "0" } }
// { dg-final { gdb-test 30 "g" "27" } }
// { dg-final { gdb-test 30 "h" "65990713" } }
// { dg-final { gdb-test 30 "i" "3" } }
}
This shows that both g++ and clang++ don't emit debug info for a, b, c (i.e.
the namespace scope structured bindings).
The rest works in clang++, but in a read-only way, i.e. you can query the
values of d/e/f and get correct values, but
one can't change them:
(gdb) set variable d = 28
Left operand of assignment is not an lvalue.
Haven't tried the g/h/i case with clang++ for -O2 because clang++ doesn't
understand noipa attribute and didn't bother
with inline asm or other tricks to hide the functions from optimizations.
Since Eric's change d prints but gets wrong value, 250 rather than the expected
26. That is because
the __unknown__ DW_TAG_base_type have just DW_AT_byte_size 4 rather than
DW_AT_bit_size 5 (etc.) instead.
I've tried to change gcc to emit DW_OP_bit_piece (obviously it is premature to
try to emit DWARF6 stuff), WIP patch:
--- gcc/dwarf2out.cc.jj 2025-11-30 15:52:11.748159233 +0100
+++ gcc/dwarf2out.cc 2025-12-03 12:42:45.193134909 +0100
@@ -19048,6 +19048,7 @@ loc_list_from_tree_1 (tree loc, int want
dw_loc_list_ref list_ret = NULL, list_ret1 = NULL;
int have_address = 0;
enum dwarf_location_atom op;
+ HOST_WIDE_INT bitoff = 0;
/* ??? Most of the time we do not take proper care for sign/zero
extending the values properly. Hopefully this won't be a real
@@ -19434,8 +19435,14 @@ loc_list_from_tree_1 (tree loc, int want
return 0;
if (!multiple_p (bitpos, BITS_PER_UNIT, &bytepos))
{
- expansion_failed (loc, NULL_RTX, "bitfield access");
- return 0;
+ poly_int64 bitpos2 = bitpos;
+ bitpos2.coeffs[0] -= bitpos.coeffs[0] % BITS_PER_UNIT;
+ if (!multiple_p (bitpos2, BITS_PER_UNIT, &bytepos))
+ {
+ expansion_failed (loc, NULL_RTX, "bitfield access");
+ return 0;
+ }
+ bitoff = bitpos.coeffs[0] % BITS_PER_UNIT;
}
if (offset != NULL_TREE)
@@ -19452,12 +19459,33 @@ loc_list_from_tree_1 (tree loc, int want
HOST_WIDE_INT value;
if (bytepos.is_constant (&value) && value > 0)
- add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_plus_uconst,
- value, 0));
+ {
+ if (bitoff && want_address == 2)
+ bitoff = bitoff + value * (unsigned HOST_WIDE_INT) BITS_PER_UNIT;
+ else
+ add_loc_descr_to_each (list_ret,
+ new_loc_descr (DW_OP_plus_uconst,
+ value, 0));
+ }
else if (maybe_ne (bytepos, 0))
loc_list_plus_const (list_ret, bytepos);
have_address = 1;
+ if (bitoff)
+ {
+ HOST_WIDE_INT bitsz;
+ if (want_address == 2
+ && (dwarf_version >= 3 || !dwarf_strict)
+ && bitsize.is_constant (&bitsz))
+ add_loc_descr_to_each (list_ret,
+ new_loc_descr (DW_OP_bit_piece,
+ bitsz, bitoff));
+ else if (want_address)
+ {
+ expansion_failed (loc, NULL_RTX, "bitfield access");
+ return 0;
+ }
+ }
break;
}
Note, this is unfinished, for the !want_address && bitoff case it needs either
also to punt or handle non-zero bitoff later on
by shifting the value (depending on endianity?).
Except this doesn't work, neither in gdb nor in lldb.
<2><ea>: Abbrev Number: 1 (DW_TAG_variable)
<eb> DW_AT_name : d
<ed> DW_AT_decl_file : 1
<ed> DW_AT_decl_line : 25
<ee> DW_AT_decl_column : 10
<ef> DW_AT_type : <0x140>
<f3> DW_AT_location : 2 byte block: 91 5c (DW_OP_fbreg: -36)
<2><f6>: Abbrev Number: 1 (DW_TAG_variable)
<f7> DW_AT_name : e
<f9> DW_AT_decl_file : 1
<f9> DW_AT_decl_line : 25
<fa> DW_AT_decl_column : 13
<fb> DW_AT_type : <0x146>
<ff> DW_AT_location : 5 byte block: 91 5c 9d 1a 5 (DW_OP_fbreg:
-36; DW_OP_bit_piece: size: 26 offset: 5 )
<2><105>: Abbrev Number: 1 (DW_TAG_variable)
<106> DW_AT_name : f
<108> DW_AT_decl_file : 1
<108> DW_AT_decl_line : 25
<109> DW_AT_decl_column : 16
<10a> DW_AT_type : <0x14c>
<10e> DW_AT_location : 5 byte block: 91 5c 9d 1 1f (DW_OP_fbreg:
-36; DW_OP_bit_piece: size: 1 offset: 31 )
So, perhaps we can't use DW_OP_bit_piece and have to live with the read-only
(i.e. DW_OP_stack_value) way with shifts.