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.

Reply via email to