[Bug debug/101378] Negative DW_AT_data_member_location

2021-07-09 Thread alves.ped at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101378

--- Comment #3 from Pedro Alves  ---
BTW, I found it curious that empty2 and empty3 get their own addresses, even at
run time, when they could have all been squashed to the same address.  I.e.,
e.g., this ends up with sizeof == 3, when it could have been 1, I believe:

 struct Empty {};

 struct S
 {
   [[no_unique_address]] Empty empty1;
   [[no_unique_address]] Empty empty2;
   [[no_unique_address]] Empty empty3;
 };

Clang also gives empty2 and empty3 unique addresses and sizeof(S)==3.

ABI compatibility could prevent changing this, of course.

Clang also gets the DWARF right, BTW.  For the example above:

 <2><48>: Abbrev Number: 4 (DW_TAG_member)
<49>   DW_AT_name: (indirect string, offset: 0x55): empty1
...
<53>   DW_AT_data_member_location: 0
 <2><54>: Abbrev Number: 4 (DW_TAG_member)
<55>   DW_AT_name: (indirect string, offset: 0x62): empty2
...
<5f>   DW_AT_data_member_location: 1
 <2><60>: Abbrev Number: 4 (DW_TAG_member)
<61>   DW_AT_name: (indirect string, offset: 0x69): empty3
...
<6b>   DW_AT_data_member_location: 2

[Bug debug/101378] Negative DW_AT_data_member_location

2021-07-09 Thread alves.ped at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101378

Pedro Alves  changed:

   What|Removed |Added

 CC||alves.ped at gmail dot com

--- Comment #2 from Pedro Alves  ---
It's indeed about [[no_unique_address]], and empty objects.

Here's a smaller reproducer:

$ cat test.cpp
struct Empty {​​​}​​​;
struct S
{​​​
  [[no_unique_address]] Empty empty;
}​​​ s;

$ g++ test.cpp -g -c -o test.o
$ readelf --debug-dump test.o | grep "DW_AT_data_member_location:"
<3d>   DW_AT_data_member_location: -1

If you replace Empty with anything that isn't empty, you no longer get the -1.

Notice this too:

~~~
$ cat test1.cpp
#include 
#include 

struct Empty {};

struct S
{
  [[no_unique_address]] Empty empty1;
  [[no_unique_address]] Empty empty2;
  [[no_unique_address]] Empty empty3;
} s;

int main ()
{
  printf (" = %p\n", );
  printf (" = %p\n", );
  printf (" = %p\n", );
  printf ("s = %p\n", );
  printf ("offset empty1 = %ld\n", offsetof (struct S, empty1));
  printf ("offset empty2 = %ld\n", offsetof (struct S, empty2));
  printf ("offset empty3 = %ld\n", offsetof (struct S, empty3));
  return 0;
}
~~~

The debug info shows:

 <2>: Abbrev Number: 10 (DW_TAG_member)
   DW_AT_name: (indirect string, offset: 0x186f): empty1
...
   DW_AT_data_member_location: -1
 <2>: Abbrev Number: 2 (DW_TAG_member)
   DW_AT_name: (indirect string, offset: 0x1876): empty2
...
   DW_AT_data_member_location: 0
 <2>: Abbrev Number: 2 (DW_TAG_member)
   DW_AT_name: (indirect string, offset: 0x187d): empty3
...
   DW_AT_data_member_location: 1

While at run time, we get:

$ ./test1
 = 0x5627442ab011
 = 0x5627442ab012
 = 0x5627442ab013
s = 0x5627442ab011
offset empty1 = 0
offset empty2 = 1
offset empty3 = 2

So (0, 1, 2) at runtime, but (-1, 0, 1) in the DWARF.


And then note this, with a non-empty field added before
the empty ones:


$ cat test2.cpp 
#include 
#include 

struct Empty {};

struct S
{
  int i;
  [[no_unique_address]] Empty empty1;
  [[no_unique_address]] Empty empty2;
} s;

int main ()
{
  printf (" = %p\n", );
  printf (" = %p\n", );
  printf (" = %p\n", );
  printf ("s = %p\n", );
  printf ("offset i = %ld\n", offsetof (struct S, i));
  printf ("offset empty1 = %ld\n", offsetof (struct S, empty1));
  printf ("offset empty2 = %ld\n", offsetof (struct S, empty2));
  return 0;
}

we see:

 <2>: Abbrev Number: 9 (DW_TAG_member)
   DW_AT_name: i
...
   DW_AT_data_member_location: 0
 <2>: Abbrev Number: 10 (DW_TAG_member)
   DW_AT_name: (indirect string, offset: 0x1872): empty1
...
   DW_AT_data_member_location: -1
 <2>: Abbrev Number: 11 (DW_TAG_member)
   DW_AT_name: (indirect string, offset: 0x1879): empty2
...
   DW_AT_data_member_location: 3

while at run time:

 $ ./test2 
  = 0x561fac538038
  = 0x561fac538038
  = 0x561fac53803c
 s = 0x561fac538038
 offset i = 0
 offset empty1 = 0
 offset empty2 = 4

So it was (0, -1, 3) in the DWARF and (0, 0, 4) at run time.

This all seems to suggest an off by one for the empty fields in
the DWARF.