[PATCH 03/34] rs6000: Add the rest of the [altivec] stanza to the builtins file

Bill Schmidt wschmidt@linux.ibm.com
Mon Aug 9 19:18:48 GMT 2021

Hi Segher,
>>>>> +  pcvoid_type_node
>>>>> +    = build_pointer_type (build_qualified_type (void_type_node,
>>>>> +						TYPE_QUAL_CONST));
>>>> A const void?  Interesting.  You are building a pointer to a const void
>>>> here, not a const pointer to void.  Is that what you wanted?
>>>> (And yes I do realise this is just moved, not new code).
>>> Sorry, I misdocumented this below.  I'll review and make sure this is
>>> correct everywhere.
>> "const void" is meaningless, and maybe even invalid C.  I think the code
>> is wrong, not (just) the documentation!  This wants to be
>>     void *const
>> but it is
>>     const void *
>> as far as I can see?
>> As I said, this isn't new code, but it seems very wrong!
I had to go back and remember where this fits in.  tl;dr:  This is fine. 
:-)  More details...

"const void *" is used as part of the overloading machinery.  It serves 
to reduce the number of built-in functions that we need to register with 
the front end.  Consider the built-in function that accesses the "lvebx" 
instruction.  In rs6000-builtin-new.def, we define it this way:

   pure vsc __builtin_altivec_lvebx (signed long, const void *);
     LVEBX altivec_lvebx {ldvec}

Note that this is a "pure" function (no side effects), and we 
contractually guarantee (through "const <type> *") that we will not 
modify the data pointed to by the second argument. Normally you might 
expect this to be "const char *" or similar. The purpose of the void 
pointer is to allow multiple overloaded functions with different type 
signatures to map to this built-in function, as follows.

In rs6000-overload.def, you'll see this as part of the overloading for 

[VEC_LDE, vec_lde, __builtin_vec_lde]
   vsc __builtin_vec_lde (signed long, const signed char *);
   vuc __builtin_vec_lde (signed long, const unsigned char *);

The two references to LVEBX here indicate that those two overloads of 
__builtin_vec_lde will map to __builtin_altivec_lvebx, above.  These two 
functions differ in their argument types and their return types.

The overload machinery will replace a call to one of the 
__builtin_vec_lde functions as follows:

  - Arguments to __builtin_vec_lde are cast to the types expected by 
  - __builtin_altivec_lvebx is called
  - The return value from __builtin_altivec_lvebx is cast to the type 
expected by the __builtin_vec_lde function.

For vector types, the altivec type semantics allow us to use 
reinterpret-cast semantics to interpret any vector type as another 
vector type.  That handles the return type coercion in this case.

However, we don't have that freedom with pointer types.  This is why the 
built-in function is defined with a void * argument.  Both "const signed 
char *" and "const unsigned char *" can be legitimately cast to a "const 
void *".

This isn't strictly necessary, but without such a trick, we would have 
to have two different __builtin_altivec_lvebx functions (with different 
names) to handle the different pointer types.  Defining multiple 
functions for each such situation is wasteful when defining functions 
and when looking them up, and a naming scheme would be needed for 
dealing with this.

This is the way the builtin structure has been working since the "dawn 
of time," and I'm not proposing changes to that.  I'm hopeful with the 
new system that it is a little clearer what is going on, though, since 
you can easily see the const void * arguments in the definitions.


More information about the Gcc-patches mailing list