On 3/28/23 13:17, David Blaikie wrote:
DW_AT[_GNU]_vector is best understood not as "a hardware vector register" but rather as a
marker that "this type is eligible to be passed in hardware vector registers at function
boundaries according to the platform ABI".
My 2c would not be to describe these in terms of
hardware/implementations (that gets confusing/blurs the line between
variable/types and locations - as you say, these things can be stored
in memory, so they aren't uniquely in registers - you might have a
member of this type in a struct passed in memory and need to know the
ABI/struct layout for that, etc), but at the source level - which the
ABI is defined in those same terms. Overloading, for instance, still
applies if these are different types - so other debugger features need
to work based on this type information.
So it seems like a simpler question is:
How should DWARF producers/consumers expect to encode the source
example Ben provided (well, simplified a bit):
#include <x86intrin.h>
void f( __m128 a){
}
What DWARF should be used to describe the type of 'a'? And how does
this encoding scale to all the other similar intrinsic types?
As a person who has spent a crazy amount of time doing ABI work and
static analysis this is what I would like: (I'm kind of assembling this
by cutting and pasting and editing so please excuse minor errors like
sizes and ignoring siblings. It is hand written hand-wavy DWARF)
*Factored out preamble to all of them:*
[0] base_type abbrev: 3
byte_size (data1) 4
encoding (data1) float (4)
name (strp) "float"
[5] base_type abbrev: 3
byte_size (data1) 4
encoding (data1) unsigned (4)
name (strp) "unsigned int"
[8] base_type abbrev: 3
byte_size (data1) 4
encoding (data1) signed (4)
name (strp) "int"
[10] base_type abbrev: 3
byte_size (data1) 8
encoding (data1) double (4)
name (strp) "double float"
[15] subprogram abbrev: 32
external (flag_present) yes
name (string) "f"
[20] formal_parameter abbrev: 15
name (string) "a"
type (ref4) [30]
*void f( float *a){}**
*[30] pointer_type abbrev: 5
byte_size (implicit_const) 8
type (ref4) [0]
*void f( float a[]){}*
[30] array_type abbrev: 5
type (ref4) [0]
*void f( float a[4]){}
*[30] array_type abbrev: 5
type (ref4) [0]
[40] subrange_type abbrev: 31
upper_bound (data1) 3*
*
*void f( float a[s], **unsigned s**){} // C only*
[30] array_type abbrev: 5
type (ref4) [0]
[40] subrange_type abbrev: 31
upper_bound (data1) DW_OP_reg reg4
[50]formal_parameter abbrev: 15
name (string) "s"
type (ref4) [5]
*void f( __m128 a){} // floats implied*
[30] typedef abbrev: 8
name (strp) "__m128"
decl_file (data1) xmmintrin.h (2)
decl_line (data1) 69
decl_column (data1) 15
type (ref4) [40]
[40] array_type abbrev: 5
vector (flag_present) yes
type (ref4) [0]
[50] subrange_type abbrev: 31
upper_bound (data1) 3*
*
*void f( __m128i a){} // integer*
[30] typedef abbrev: 8
name (strp) "__m128i"
decl_file (data1) xmmintrin.h (2)
decl_line (data1) 69
decl_column (data1) 15
type (ref4) [40]
[40] array_type abbrev: 5
vector (flag_present) yes
type (ref4) [8]
[50] subrange_type abbrev: 31
upper_bound (data1) 3
*void f( __m128d a){}*
[30] typedef abbrev: 8
name (strp) "__m128d"
decl_file (data1) xmmintrin.h (2)
decl_line (data1) 69
decl_column (data1) 15
type (ref4) [40]
[40] array_type abbrev: 5
vector (flag_present) yes
type (ref4) [10]
[50] subrange_type abbrev: 31
upper_bound (data1) 2
*void f( __m128u a){} // unsigned*
[30] typedef abbrev: 8
name (strp) "__m128u"
decl_file (data1) xmmintrin.h (2)
decl_line (data1) 69
decl_column (data1) 15
type (ref4) [40]
[40] array_type abbrev: 5
vector (flag_present) yes
type (ref4) [5]
[50] subrange_type abbrev: 31
upper_bound (data1) 3
Then I would continue on with all various encodings and base types that
can be in a the various kinds of vectors. Because the instructions that
load the base types into the vector register do not care about
encodings, they are just moving bytes, the Intel vector intrinsics type
names kind of merge encoding and length in a way that I'm not fond of as
someone who cares about ABI. For example I invented __m128u above to go
along with the __m128i already defined in intel's intrinsics manual.
I would also expand that the larger vector sizes. For example:
*void f( __mm256 a){} // floats implied*
[30] typedef abbrev: 8
name (strp) "__mm256"
decl_file (data1) xmmintrin.h (2)
decl_line (data1) 69
decl_column (data1) 15
type (ref4) [40]
[40] array_type abbrev: 5
vector (flag_present) yes
type (ref4) [0]
[50] subrange_type abbrev: 31
upper_bound (data1) 7
As you can see, much of what I really would like is to:
1. Not have arrays degenerate into pointers when the source code is
explicit about this. Consider a linked lists vs an arrays, both
could have the same ABI fingerprint. - I believe that this should be
written into the standard as a best practice example.I will write
this up and file it.
2. When it is an array and the bound is specified, this also should be
included in the ABI fingerprint. - I believe that this to should be
written into the standard as a best practice example.I will write
this up and file it.
3. In my ABI work I need a way to disambiguate "typedef float[4]
__m128" from "__m128" as it is now defined in xmmintrin.h. The
difference is important because in libabigail we do not look at the
location of the parameters, we just assume that the platform ABI as
implemented by the compiler takes care of that. Thus if arrays
didn't degenerate into pointers (introducing the ambiguity between
the head of a linked list and an array), then:
typedef float[4] __m128;
void f( __m128 a){}
Would stick a pointer to a in general purpose register. While:
#include "xmmintin.h"
void f( __m128 a){}
Would stick a in a vector register because calling convention is
different and libabigail wouldn't be able to tell the difference.
We don't process the formal parameter's location at least in part
because it is hard. We would have to add code in libabigail to process
the location list but also because the quality of location information
from different compilers has been inconsistent. And the purpose of
libabigail the tool was not to check how well the compilers implemented
the platform ABI but to test libraries for ABI compatibility.
I spent a notable portion of yesterday writing various bits of arguments
against Cary's DW_TAG_vector and then throwing them away because they
really were not at all convincing even to myself. The only argument that
I found convincing to even myself was parsimony. We currently have
DW_TAG_array and I couldn't come up with how it would be different in
any way from DW_TAG_array + DW_AT_vector. So based on that rather weak
argument, I'll say that I really don't care if it is:
DW_TAG_vector
or
DW_TAG_array
DW_AT_vector
Tony let me know he's become convinced that they do not need a vector
type for their GPU work and are planning to drop their vectors as base
type proposal. That leaves my needs around ABI as the only pending
concern and that may be handled by Kyle's proposal to make the location
of the return value something encoded in the DWARF rather than having to
infer it from the platform ABI.
If we didn't get something like DW_TAG_vector or DW_TAG_array +
DW_AT_vector, and instead only went with just Kyle's proposal specifying
the location of the return value, then libabigail would then need to be
taught to process location information.
-ben
**
--
Dwarf-discuss mailing list
Dwarf-discuss@lists.dwarfstd.org
https://lists.dwarfstd.org/mailman/listinfo/dwarf-discuss