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
