Hi!

Jason, hopfully you'll be able to help with my question, please see
below.

Arsen, I've added you as you recently happened to work on a GCC/GCN issue
involving named address spaces, and maybe generally are able to help with
this C++ issue here.

For reference, this is in context of the last patch submission:
<https://inbox.sourceware.org/[email protected]>
"[REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++".

On 2026-05-21T21:51:09-0400, Jason Merrill <[email protected]> wrote:
> On 5/15/26 8:17 AM, Thomas Schwinge wrote:
>> If we get a general "go", I'll then offer to fix up a few places for GCC
>> coding style conformance (so please don't review for that, yet)

(Still to be done.)

>> and I'll
>> work on test cases some more.

(I've got a few more lined up.)

>> I'll also examine if, since then, any new
>> code has appeared where 'ADDR_SPACE_CONVERT_EXPR' needs to be handled.

(Still to be done.)

>> PR69549 "Named Address Spaces does not compile in C++" is going to be
>> resolved by this patch, so should get referenced in the Git log.

>> On 2022-11-10T16:42:22+0100, Paul Iannetta <[email protected]> wrote:
>>> It took a bit of time to rework the rough corners.  I tried to be
>>> mirror as much as possible the C front-end, especially when it comes
>>> to implicit conversions [...]

I just found one case where the C and C++ front ends behave differently;
please help me understand the desired behavior in C++ language as well as
GCC/C++ front end implementation.

This is not relevant for x86's '__seg_fs', '__seg_gs', but consider, for
example, the case of GCC/GCN, which has:

'gcc/config/gcn/gcn.h':

    /* Address spaces.  */
    enum gcn_address_spaces
    {
      ADDR_SPACE_DEFAULT = 0,
      ADDR_SPACE_FLAT,
    [...]
      ADDR_SPACE_LDS,
    [...]
    };
    #define REGISTER_TARGET_PRAGMAS() do {                               \
      c_register_addr_space ("__flat", ADDR_SPACE_FLAT);                 \
    [...]
      c_register_addr_space ("__lds", ADDR_SPACE_LDS);                   \
    [...]
    } while (0);
    [...]

'gcc/config/gcn/gcn.cc':

    /* Implement TARGET_ADDR_SPACE_SUBSET_P.
    
       Determine if one named address space is a subset of another.  */
    
    static bool
    gcn_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
    {
      if (subset == superset)
        return true;
      /* FIXME is this true?  */
      if (AS_FLAT_P (superset) || AS_SCALAR_FLAT_P (superset))
        return true;
      return false;
    }

..., and, for example, the test case
'gcc.target/gcn/addr-space-convert-1.c':

    void __flat *
    convert_lds_addr (void __lds *x)
    { return x; }
    
    /* { dg-final { scan-assembler "shared_base" } }  */

This means, per my not-too-in-depth knowledge of GCC/GCN back end
details, that there is a way to convert from '__lds' to '__flat', and per
'gcc/config/gcn/gcn.cc:gcn_addr_space_convert', this needs to emit some
code involving a 'SHARED_BASE_REG' ('shared_base' checked in the test
case).  (Details not important here.)  For this code path to be invoked,
we need a 'ADDR_SPACE_CONVERT_EXPR' ('gcc/expr.cc:expand_expr_real_2').

For GCC/C as well as GCC/C++ (with the current implementation, referenced
above), that works fine for the test case
'gcc.target/gcn/addr-space-convert-1.c'; we get '-fdump-tree-gimple-raw':

    <address-space-1> void * convert_lds_addr (<address-space-4> void * x)
    gimple_bind <
      <address-space-1> void * D.2346;
    
      gimple_assign <addr_space_convert_expr, D.2346, x, NULL, NULL>
      gimple_return <D.2346>
    >

A variant where we use an explicit cast, as in:

     void __flat *
     convert_lds_addr (void __lds *x)
    -{ return x; }
    +{ return (void __flat *) x; }

..., for GCC/C that likewise works fine (Arsen, that is the expected
behavior, correct?), but for GCC/C++, we get:

    -  gimple_assign <addr_space_convert_expr, D.2346, x, NULL, NULL>
    +  gimple_assign <nop_expr, D.2346, x, NULL, NULL>

..., and therefore don't get 'shared_base' via 'gcn_addr_space_convert'.
(See below for my current understand where/why that happens.)

Jason, Arsen, is my understanding correct that GCC/C++ should complile
the code with C-style cast (or 'return const_cast<void __flat *>(x);',
for that matter), in the same way as in the case of an implicit cast?
That is, all these different source code variants mentioned above are
well-defined and should behave in the same way?

>From Paul's email:

>>>    4. I left untouched same_type_ignoring_top_level_qualifiers_p, even
>>>    though that was very convenient and often lead to better error
>>>    messages since error were caught earlier.

(Might well be relevant, but I've not yet looked into that one.)  But:

>>>    5. The handling of conversions is done very late in the calling
>>>    chain, because I absolutely want to fold the conversion and force
>>>    the conversion to appear as an ADDR_SPACE_CONV_EXPR after
>>>    gimplification.

That certainly is relevant here.  (See below.)

Now, queue to a review comment by Jason (thanks!):

>>      * call.cc (convert_like_internal): Add support for implicit
>>        conversion between compatible address spaces.  This is done
>>        here (and not in a higher caller) because we want to force the
>>        use of cp_fold_convert, which later enable back-end writers to
>>        tune-up the fine details of the conversion.

(That's directly related to Paul's "5." mentioned just above.)

> This kind of explanation belongs in a code comment rather than the 
> ChangeLog.

(ACK; moved.)

> This comment suggests that we want to support these conversions

(That's the test case/behavior I've discussed above.)

> but the 
> change is to the part of the function that handles "bad" conversions.  I 
> would expect handling of conversions we want to support to go in the 
> ck_ptr case near the bottom of the function.

That suggestion doesn't (easily) work, as we'll then run into:

    complained = permerror (&richloc,
                            "invalid conversion from %qH to %qI",
                            TREE_TYPE (expr), totype);

But maybe that handling here generally isn't completely right?  Also
relevant should be the following remark:

> I was surprised that no change to standard_conversion was needed, but I 
> guess the change to comp_ptr_ttypes_real covers that.

(I've not yet looked in detail into 'comp_ptr_ttypes_real'.)  But let's
look at 'gcc/cp/call.cc:standard_conversion', in context of the test case
discussed above.  For the implicit cast, we take the following code path:

    [...]
    else if (ptr_reasonably_similar (to_pointee, from_pointee))
      {
        conv = build_conv (ck_ptr, to, conv);
        conv->bad_p = true;
      }

..., and then, instead of emitting an error due to 'conv->bad_p',
'convert_like_internal' "fixes this up" (via enforced 'cp_fold_convert'):

    else if (t->kind == ck_ptr
    [...]
            if (addr_space_superset (as_from, as_to, &as_common)
                && as_common == as_to)
              return cp_fold_convert (totype, expr);

However, in the case of the explicit cast ('return (void __flat *) x;'),
we take the following code path in 'gcc/cp/call.cc:standard_conversion':

    [...]
    else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_either))
      /* In a C-style cast, we ignore CV-qualification because we
         are allowed to perform a static_cast followed by a
         const_cast.  */
      conv = build_conv (ck_qual, to, conv);

Note 'ck_qual' instead of 'ck_ptr', and no 'conv->bad_p = true;', and
therefore neither 'convert_like_internal' nor anything else does the
'cp_fold_convert' (or whatever else) to get the necessary
'ADDR_SPACE_CONVERT_EXPR'.

> I would also expect compare_ics to have an opinion about such 
> conversions vs conversions within the same address space.

(Have not yet looked into that, either.)

To (properly) handle those implicit/explicit conversions, instead of the
proposed 'convert_like_internal' changes, do we maybe handle that in
'gcc/cp/call.cc:standard_conversion', maybe similar to:

     if (same_type_p (from, to))
       /* OK */;
    +else if (some_new_addr_space_conv_p ([...])) // irregardless of 'c_cast_p'
    +  conv = build_conv (ck_[...], to, conv);

Does that make any sense?  Would this be 'ck_ptr' or another existing
one, or maybe even require a new code?  How do we (properly) request an
'ADDR_SPACE_CONVERT_EXPR' to be generated, be it indeed via
'cp_fold_convert' or something else?

As you can easily tell, I'm currently quite lost in the GCC/C++ front end
code, to figure out where this should be handled, and how.  Will gladly
take pointers.


Grüße
 Thomas

Reply via email to