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, vec_lde, __builtin_vec_lde]
  vsc __builtin_vec_lde (signed long, const signed char *);
    LVEBX  LVEBX_SC
  vuc __builtin_vec_lde (signed long, const unsigned char *);
    LVEBX  LVEBX_UC

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
 - __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.

Thanks,
Bill

Reply via email to