[Dwarf-discuss] ISSUE: CPU vector types.

Ben Woodard woodard@redhat.com
Wed Mar 29 19:55:24 GMT 2023


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


**
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.dwarfstd.org/pipermail/dwarf-discuss/attachments/20230329/f93cd99d/attachment-0001.htm>


More information about the Dwarf-discuss mailing list