* [RFC] c++: parser - Support for target address spaces in C++
@ 2022-10-06 14:34 Paul Iannetta
2022-10-06 17:34 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-10-06 14:34 UTC (permalink / raw)
To: gcc-patches; +Cc: jason
Hi,
Presently, GCC supports target address spaces as a GNU extension (cf.
`info -n "(gcc)Named Address Spaces"`). This is however supported
only by the C frontend, which is a bit sad, since all the GIMPLE
machinery is readily available and, moreover, LLVM supports this GNU
extension both for C and C++.
Here is a first attempt at a patch to enable the support for target
address spaces in C++. The basic idea is only to make the parser
recognize address spaces, and lower them into GIMPLE, in the same
fashion as the C parser. This also makes it possible to merge the
function `c_register_addr_space` in one place which is better than
before.
The patch still has some drawbacks compared to its C counterpart.
Namely, much like the `__restrict__` keyword, it is always enabled and
-std=c++11 won't disable it (whereas -std=c11) would reject address
spaces. Not also that the mangler ignores address spaces, much like
it ignores __restrict__.
Depending on the reception, I'll add further testcases and will update
the relevant part of the documentation.
Cheers,
Paul
Author: Paul Iannetta <piannetta@kalray.eu>
Date: Wed Oct 5 16:44:36 2022 +0200
Add support for custom address spaces in C++
gcc/
* tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
gcc/c/
* c-decl.c: Remove c_register_addr_space.
gcc/c-family/
* c-common.c (c_register_addr_space): Imported from c-decl.c
* c-common.h: Remove the FIXME.
gcc/cp/
* cp-tree.h (enum cp_decl_spec): Add addr_space support.
(struct cp_decl_specifier_seq): Likewise.
* decl.c (get_type_quals): Likewise.
* parser.c (cp_parser_type_specifier): Likewise.
(cp_parser_cv_qualifier_seq_opt): Likewise
* tree.c: Remove c_register_addr_space stub.
Signed-off-by: Paul Iannetta <piannetta@kalray.eu>
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 064c2f263f0..282ba54ab70 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -2615,6 +2615,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ TREE_LANG_FLAG_0 (id) = 1;
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index ed39b7764bf..f2c1df0c8de 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -808,9 +808,6 @@ extern const struct attribute_spec c_common_format_attribute_table[];
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.c and cp/tree.c. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.c. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
@@ -926,6 +923,7 @@ extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern FILE *get_dump_info (int, dump_flags_t *);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 8e24b522ee4..278d1248d1c 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -11927,25 +11927,6 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
-}
-
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 15ec4cd199f..4ae971ccb90 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5974,6 +5974,7 @@ enum cp_decl_spec {
ds_const,
ds_volatile,
ds_restrict,
+ ds_addr_space,
ds_inline,
ds_virtual,
ds_explicit,
@@ -6046,6 +6047,8 @@ struct cp_decl_specifier_seq {
/* True iff the alternate "__intN__" form of the __intN type has been
used. */
BOOL_BITFIELD int_n_alt: 1;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
};
/* The various kinds of declarators. */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 03cd0a3a238..b8fc8b58ac7 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -4954,6 +4954,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
return type_quals;
}
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index f48c856fa94..8d6b2a44086 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -17796,6 +17796,16 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+
+ if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ decl_specs->address_space = keyword - RID_FIRST_ADDR_SPACE;
+ }
+
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -21853,6 +21863,7 @@ cp_parser_ptr_operator (cp_parser* parser,
GNU Extension:
cv-qualifier:
+ address-space-qualifier
__restrict__
Returns a bitmask representing the cv-qualifiers. */
@@ -21889,6 +21900,13 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
+ if (RID_FIRST_ADDR_SPACE <= token->keyword &&
+ token->keyword <= RID_LAST_ADDR_SPACE)
+ {
+ cv_qualifier =
+ ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
+ }
+
if (!cv_qualifier)
break;
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 10b818d1370..55b57085618 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -5749,15 +5749,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.c.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/tree.h b/gcc/tree.h
index bb80e81d389..210ef7b85d7 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2108,7 +2108,7 @@ extern machine_mode vector_type_mode (const_tree);
/* Encode/decode the named memory support as part of the qualifier. If more
than 8 qualifiers are added, these macros need to be adjusted. */
-#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
+#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
#define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
/* Return all qualifiers except for the address space qualifiers. */
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [RFC] c++: parser - Support for target address spaces in C++
2022-10-06 14:34 [RFC] c++: parser - Support for target address spaces in C++ Paul Iannetta
@ 2022-10-06 17:34 ` Jason Merrill
2022-10-09 16:12 ` [PATCH] " Paul Iannetta
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-06 17:34 UTC (permalink / raw)
To: Paul Iannetta, gcc-patches
On 10/6/22 10:34, Paul Iannetta wrote:
> Hi,
>
> Presently, GCC supports target address spaces as a GNU extension (cf.
> `info -n "(gcc)Named Address Spaces"`). This is however supported
> only by the C frontend, which is a bit sad, since all the GIMPLE
> machinery is readily available and, moreover, LLVM supports this GNU
> extension both for C and C++.
>
> Here is a first attempt at a patch to enable the support for target
> address spaces in C++. The basic idea is only to make the parser
> recognize address spaces, and lower them into GIMPLE, in the same
> fashion as the C parser. This also makes it possible to merge the
> function `c_register_addr_space` in one place which is better than
> before.
Great.
> The patch still has some drawbacks compared to its C counterpart.
> Namely, much like the `__restrict__` keyword, it is always enabled and
> -std=c++11 won't disable it (whereas -std=c11) would reject address
> spaces. Not also that the mangler ignores address spaces, much like
> it ignores __restrict__.
Hmm? We mangle __restrict:
void f(int *__restrict *p) { } // _Z1fPrPi
but cv-qualifiers (including restrict) are dropped on the top-level type
of a parameter.
If address spaces should be mangled, it probably makes sense to use the
same <extended-qualifier> mangling that we already use for attributes in
write_CV_qualifiers_for_type.
> Depending on the reception, I'll add further testcases and will update
> the relevant part of the documentation.
Please.
> Cheers,
> Paul
>
> Author: Paul Iannetta <piannetta@kalray.eu>
> Date: Wed Oct 5 16:44:36 2022 +0200
>
> Add support for custom address spaces in C++
>
> gcc/
> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>
> gcc/c/
> * c-decl.c: Remove c_register_addr_space.
>
> gcc/c-family/
> * c-common.c (c_register_addr_space): Imported from c-decl.c
> * c-common.h: Remove the FIXME.
>
> gcc/cp/
> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> (struct cp_decl_specifier_seq): Likewise.
> * decl.c (get_type_quals): Likewise.
> * parser.c (cp_parser_type_specifier): Likewise.
> (cp_parser_cv_qualifier_seq_opt): Likewise
> * tree.c: Remove c_register_addr_space stub.
>
> Signed-off-by: Paul Iannetta <piannetta@kalray.eu>
>
> diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
> index 064c2f263f0..282ba54ab70 100644
> --- a/gcc/c-family/c-common.c
> +++ b/gcc/c-family/c-common.c
> @@ -2615,6 +2615,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> return build_nonstandard_integer_type (width, unsignedp);
> }
>
> +/* Register reserved keyword WORD as qualifier for address space AS. */
> +
> +void
> +c_register_addr_space (const char *word, addr_space_t as)
> +{
> + int rid = RID_FIRST_ADDR_SPACE + as;
> + tree id;
> +
> + /* Address space qualifiers are only supported
> + in C with GNU extensions enabled. */
> + if (c_dialect_objc () || flag_no_asm)
> + return;
> +
> + id = get_identifier (word);
> + C_SET_RID_CODE (id, rid);
> + TREE_LANG_FLAG_0 (id) = 1;
> + ridpointers[rid] = id;
> +}
> +
> /* The C version of the register_builtin_type langhook. */
>
> void
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index ed39b7764bf..f2c1df0c8de 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -808,9 +808,6 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.c and cp/tree.c. FIXME. */
> -extern void c_register_addr_space (const char *str, addr_space_t as);
> -
> /* In c-common.c. */
> extern bool in_late_binary_op;
> extern const char *c_addr_space_name (addr_space_t as);
> @@ -926,6 +923,7 @@ extern void c_common_finish (void);
> extern void c_common_parse_file (void);
> extern FILE *get_dump_info (int, dump_flags_t *);
> extern alias_set_type c_common_get_alias_set (tree);
> +extern void c_register_addr_space (const char *, addr_space_t);
> extern void c_register_builtin_type (tree, const char*);
> extern bool c_promoting_integer_type_p (const_tree);
> extern bool self_promoting_args_p (const_tree);
> diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> index 8e24b522ee4..278d1248d1c 100644
> --- a/gcc/c/c-decl.c
> +++ b/gcc/c/c-decl.c
> @@ -11927,25 +11927,6 @@ c_parse_final_cleanups (void)
> ext_block = NULL;
> }
>
> -/* Register reserved keyword WORD as qualifier for address space AS. */
> -
> -void
> -c_register_addr_space (const char *word, addr_space_t as)
> -{
> - int rid = RID_FIRST_ADDR_SPACE + as;
> - tree id;
> -
> - /* Address space qualifiers are only supported
> - in C with GNU extensions enabled. */
> - if (c_dialect_objc () || flag_no_asm)
> - return;
> -
> - id = get_identifier (word);
> - C_SET_RID_CODE (id, rid);
> - C_IS_RESERVED_WORD (id) = 1;
> - ridpointers [rid] = id;
> -}
> -
> /* Return identifier to look up for omp declare reduction. */
>
> tree
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 15ec4cd199f..4ae971ccb90 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5974,6 +5974,7 @@ enum cp_decl_spec {
> ds_const,
> ds_volatile,
> ds_restrict,
> + ds_addr_space,
> ds_inline,
> ds_virtual,
> ds_explicit,
> @@ -6046,6 +6047,8 @@ struct cp_decl_specifier_seq {
> /* True iff the alternate "__intN__" form of the __intN type has been
> used. */
> BOOL_BITFIELD int_n_alt: 1;
> + /* The address space that the declaration belongs to. */
> + addr_space_t address_space;
> };
>
> /* The various kinds of declarators. */
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 03cd0a3a238..b8fc8b58ac7 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -4954,6 +4954,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> type_quals |= TYPE_QUAL_VOLATILE;
> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> type_quals |= TYPE_QUAL_RESTRICT;
> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>
> return type_quals;
> }
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index f48c856fa94..8d6b2a44086 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -17796,6 +17796,16 @@ cp_parser_type_specifier (cp_parser* parser,
> break;
> }
>
> +
> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> + {
> + ds = ds_addr_space;
> + if (is_cv_qualifier)
> + *is_cv_qualifier = true;
> + decl_specs->address_space = keyword - RID_FIRST_ADDR_SPACE;
> + }
> +
> +
> /* Handle simple keywords. */
> if (ds != ds_last)
> {
> @@ -21853,6 +21863,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> GNU Extension:
>
> cv-qualifier:
> + address-space-qualifier
> __restrict__
>
> Returns a bitmask representing the cv-qualifiers. */
> @@ -21889,6 +21900,13 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> break;
> }
>
> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
> + token->keyword <= RID_LAST_ADDR_SPACE)
> + {
> + cv_qualifier =
> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> + }
> +
> if (!cv_qualifier)
> break;
>
> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> index 10b818d1370..55b57085618 100644
> --- a/gcc/cp/tree.c
> +++ b/gcc/cp/tree.c
> @@ -5749,15 +5749,6 @@ cp_free_lang_data (tree t)
> DECL_CHAIN (t) = NULL_TREE;
> }
>
> -/* Stub for c-common. Please keep in sync with c-decl.c.
> - FIXME: If address space support is target specific, then this
> - should be a C target hook. But currently this is not possible,
> - because this function is called via REGISTER_TARGET_PRAGMAS. */
> -void
> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> -{
> -}
> -
> /* Return the number of operands in T that we care about for things like
> mangling. */
>
> diff --git a/gcc/tree.h b/gcc/tree.h
> index bb80e81d389..210ef7b85d7 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -2108,7 +2108,7 @@ extern machine_mode vector_type_mode (const_tree);
>
> /* Encode/decode the named memory support as part of the qualifier. If more
> than 8 qualifiers are added, these macros need to be adjusted. */
> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>
> /* Return all qualifiers except for the address space qualifiers. */
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH] c++: parser - Support for target address spaces in C++
2022-10-06 17:34 ` Jason Merrill
@ 2022-10-09 16:12 ` Paul Iannetta
2022-10-10 19:20 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-10-09 16:12 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
Hi,
On Thu, Oct 06, 2022 at 01:34:40PM -0400, Jason Merrill wrote:
[snip]
>
> Hmm? We mangle __restrict:
>
> void f(int *__restrict *p) { } // _Z1fPrPi
>
Indeed, I have overlooked that point. Thank you for pointing it out.
> but cv-qualifiers (including restrict) are dropped on the top-level type of
> a parameter.
>
> If address spaces should be mangled, it probably makes sense to use the same
> <extended-qualifier> mangling that we already use for attributes in
> write_CV_qualifiers_for_type.
>
That's a nice feature! LLVM is using AS<number> to mangle the
address-name qualified types but that would have required an update of
libiberty's demangler in the binutils as well. Following your advice,
I implemented the mangling of address_names as U<len>__address_name
(eg., U8__seg_fs), underscores may appear but that does not seem to be
a problem.
I don't think that there should be anything to do on the template
side, but I am no expert of what is expected of templates when it
comes to strip or forward qualifiers. So, please tell me if I
overlooked something on that side.
Now concerning the meat of the patch itself.
The C part of the support of address spaces does not appear to have
dedicated tests, especially since it is mostly a target specific
feature. I, however, added a few tests, one for the mangling,
and others which test the different error messages that might
appear when using address spaces in an unsafe way.
Most of the patch tries to mirror the commit
309434d678721acedb299f20cdc2b4778062b180
which introduces address spaces to the C front-end. The C and C++
parsers share a lot but are subtly different so there might be some
discrepancies. In particular, I ensure that address spaces do not
conflict, that only one address space can be specified at a time, and
that the type conversion mechanism pick the superset of the address
spaces if available. I also forbid address spaces in compound literals.
I have only tested the patch on x86 and on our not-yet-upstream
architecture, without seeing any regressions.
NB: I do not add a DSO for the moment because I have started the process of
assigning copyright to the FSF.
# ------------------------ >8 ------------------------
Add support for custom address spaces in C++
gcc/
* tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
gcc/c/
* c-decl.cc: Remove c_register_addr_space.
gcc/c-family/
* c-common.cc (c_register_addr_space): Imported from c-decl.cc
(addr_space_superset): Imported from gcc/c/c-typecheck.cc
* c-common.h: Remove the FIXME.
(addr_space_superset): Add prototype.
gcc/cp/
* cp-tree.h (enum cp_decl_spec): Add addr_space support.
(struct cp_decl_specifier_seq): Likewise.
* decl.cc (get_type_quals): Likewise.
(check_tag_decl): Likewise.
* parser.cc (cp_parser_type_specifier): Likewise.
(cp_parser_cv_qualifier_seq_opt): Likewise.
(cp_parser_postfix_expression): Likewise.
(cp_parser_type_specifier): Likewise.
(set_and_check_decl_spec_loc): Likewise.
* typeck.cc (composite_pointer_type): Likewise
(comp_ptr_ttypes_real): Likewise.
* tree.cc: Remove c_register_addr_space stub.
* mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
using the extended qualifier notation.
gcc/doc
* extend.texi (Named Address Spaces): add a mention about C++
support.
gcc/testsuite/
* g++.dg/abi/mangle-addr-space1.C: New test.
* g++.dg/parse/addr-space.C: New test.
* g++.dg/parse/addr-space1.C: New test.
* g++.dg/parse/addr-space2.C: New test.
# ------------------------ >8 ------------------------
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index bb0544eeaea..ff1146ecc25 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
return IDENTIFIER_POINTER (ridpointers [rid]);
}
+/* Return true if between two named address spaces, whether there is a superset
+ named address space that encompasses both address spaces. If there is a
+ superset, return which address space is the superset. */
+
+bool
+addr_space_superset (addr_space_t as1, addr_space_t as2,
+ addr_space_t * common)
+{
+ if (as1 == as2)
+ {
+ *common = as1;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as1, as2))
+ {
+ *common = as2;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as2, as1))
+ {
+ *common = as1;
+ return true;
+ }
+ else
+ return false;
+}
+
/* Push current bindings for the function name VAR_DECLS. */
void
@@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ TREE_LANG_FLAG_0 (id) = 1;
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 52a85bfb783..d36f9e4975b 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
+extern const char *c_addr_space_name (addr_space_t as);
+extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
extern bool names_builtin_p (const char *);
@@ -952,6 +951,7 @@ extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern FILE *get_dump_info (int, dump_flags_t *);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index c701f07befe..e1bb4f1cf37 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
-}
-
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index e130196a3a7..17185fd3da4 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
return type;
}
-/* Return true if between two named address spaces, whether there is a superset
- named address space that encompasses both address spaces. If there is a
- superset, return which address space is the superset. */
-
-static bool
-addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
-{
- if (as1 == as2)
- {
- *common = as1;
- return true;
- }
- else if (targetm.addr_space.subset_p (as1, as2))
- {
- *common = as2;
- return true;
- }
- else if (targetm.addr_space.subset_p (as2, as1))
- {
- *common = as1;
- return true;
- }
- else
- return false;
-}
-
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 72f4398a8f9..f1da04c6c58 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6206,6 +6206,7 @@ enum cp_decl_spec {
ds_const,
ds_volatile,
ds_restrict,
+ ds_addr_space,
ds_inline,
ds_virtual,
ds_explicit,
@@ -6278,6 +6279,8 @@ struct cp_decl_specifier_seq {
/* True iff the alternate "__intN__" form of the __intN type has been
used. */
BOOL_BITFIELD int_n_alt: 1;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
};
/* The various kinds of declarators. */
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 9f78c500a15..16cc1917085 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
return type_quals;
}
@@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
error_at (declspecs->locations[ds_restrict],
"%<__restrict%> can only be specified for objects and "
"functions");
+ else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ error_at (declspecs->locations[ds_addr_space],
+ "address space can only be specified for objects and "
+ "functions");
else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
error_at (declspecs->locations[ds_thread],
"%<__thread%> can only be specified for objects "
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index eb53e0ebeb4..06625ad9a4b 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2509,6 +2509,15 @@ write_CV_qualifiers_for_type (const tree type)
array. */
cp_cv_quals quals = TYPE_QUALS (type);
+ if (DECODE_QUAL_ADDR_SPACE (quals))
+ {
+ addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals);
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 763df6f479b..110ceafc98b 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
postfix_expression = error_mark_node;
break;
}
+ if (type != error_mark_node
+ && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
+ && current_function_decl)
+ {
+ error
+ ("compound literal qualified by address-space qualifier");
+ type = error_mark_node;
+ }
/* Form the representation of the compound-literal. */
postfix_expression
= finish_compound_literal (type, initializer,
@@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+
+ if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ }
+
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
GNU Extension:
cv-qualifier:
+ address-space-qualifier
__restrict__
Returns a bitmask representing the cv-qualifiers. */
@@ -23812,6 +23830,13 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
+ if (RID_FIRST_ADDR_SPACE <= token->keyword &&
+ token->keyword <= RID_LAST_ADDR_SPACE)
+ {
+ cv_qualifier =
+ ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
+ }
+
if (!cv_qualifier)
break;
@@ -32705,6 +32730,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
decl_specs->locations[ds] = location;
if (ds == ds_thread)
decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ else if (ds == ds_addr_space)
+ decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
}
else
{
@@ -32737,6 +32764,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
error_at (&richloc, "duplicate %qD", token->u.value);
}
}
+ else if (ds == ds_addr_space)
+ {
+ addr_space_t as1 = decl_specs->address_space;
+ addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
+
+ gcc_rich_location richloc (location);
+ richloc.add_fixit_remove ();
+ if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
+ && as1 != as2)
+ error_at (&richloc,
+ "conflicting named address spaces (%s vs %s)",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
+ error_at (&richloc,
+ "duplicate named address space %s",
+ c_addr_space_name (as1));
+
+ decl_specs->address_space = as2;
+ }
else
{
static const char *const decl_spec_names[] = {
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 3b37567cbd7..5e14ac837fc 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index ceb80d9744f..5d48920e687 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
else
return error_mark_node;
}
+ /* If possible merge the address space into the superset of the address
+ spaces of t1 and t2, or raise an error. */
+ addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
+ addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as_t1, as_t2, &as_common))
+ {
+ as_common = as_t1;
+ error_at (location,
+ "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ }
result_type
= cp_build_qualified_type (void_type_node,
- (cp_type_quals (TREE_TYPE (t1))
- | cp_type_quals (TREE_TYPE (t2))));
+ (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
+ | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
+ | ENCODE_QUAL_ADDR_SPACE (as_common)));
result_type = build_pointer_type (result_type);
/* Merge the attributes. */
attributes = (*targetm.merge_type_attributes) (t1, t2);
@@ -10805,6 +10821,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
to_more_cv_qualified = true;
}
+ /* Warn about conversions between pointers to disjoint
+ address spaces. */
+ if (TREE_CODE (from) == POINTER_TYPE
+ && TREE_CODE (to) == POINTER_TYPE)
+ {
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common))
+ return false;
+ }
+
if (constp > 0)
constp &= TYPE_READONLY (to);
}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 84e6f6694ab..c89df8778b2 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
@section Named Address Spaces
@cindex Named Address Spaces
-As an extension, GNU C supports named address spaces as
+As an extension, GNU C and GNU C++ support named address spaces as
defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
address spaces in GCC will evolve as the draft technical report
changes. Calling conventions for any target might also change. At
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
new file mode 100644
index 00000000000..c01f8d6054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do run { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
+
+int f (int volatile __seg_fs *a)
+{
+ return *a;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
new file mode 100644
index 00000000000..ebb6316054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
new file mode 100644
index 00000000000..2e8ee32a885
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-std=gnu++98" }
+
+int
+main ()
+{
+ struct foo {int a; char b[2];} structure;
+ structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
new file mode 100644
index 00000000000..5b2c0f28078
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/tree.h b/gcc/tree.h
index 8844471e9a5..b7da4c5141a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
/* Encode/decode the named memory support as part of the qualifier. If more
than 8 qualifiers are added, these macros need to be adjusted. */
-#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
+#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
#define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
/* Return all qualifiers except for the address space qualifiers. */
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH] c++: parser - Support for target address spaces in C++
2022-10-09 16:12 ` [PATCH] " Paul Iannetta
@ 2022-10-10 19:20 ` Jason Merrill
2022-10-11 22:03 ` [PATCH v2] " Paul Iannetta
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-10 19:20 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/9/22 12:12, Paul Iannetta wrote:
> Hi,
>
> On Thu, Oct 06, 2022 at 01:34:40PM -0400, Jason Merrill wrote:
> [snip]
>>
>> Hmm? We mangle __restrict:
>>
>> void f(int *__restrict *p) { } // _Z1fPrPi
>>
>
> Indeed, I have overlooked that point. Thank you for pointing it out.
>
>> but cv-qualifiers (including restrict) are dropped on the top-level type of
>> a parameter.
>>
>> If address spaces should be mangled, it probably makes sense to use the same
>> <extended-qualifier> mangling that we already use for attributes in
>> write_CV_qualifiers_for_type.
>>
>
> That's a nice feature! LLVM is using AS<number> to mangle the
> address-name qualified types but that would have required an update of
> libiberty's demangler in the binutils as well.
And they haven't proposed this mangling to
https://github.com/itanium-cxx-abi/cxx-abi/
yet, either.
> Following your advice,
> I implemented the mangling of address_names as U<len>__address_name
> (eg., U8__seg_fs), underscores may appear but that does not seem to be
> a problem.
>
> I don't think that there should be anything to do on the template
> side, but I am no expert of what is expected of templates when it
> comes to strip or forward qualifiers. So, please tell me if I
> overlooked something on that side.
You certainly want some template tests, say
template <class T>
int f (T *p) { return *p; }
__seg_fs int *a;
int main() { f(a); }
// test for mangling of f<__seg_fs int>
-----
template <class T>
int f (T __seg_gs *p) { return *p; }
__seg_fs int *a;
int main() { f(a); } // error, conflicting address spaces
> Now concerning the meat of the patch itself.
>
> The C part of the support of address spaces does not appear to have
> dedicated tests, especially since it is mostly a target specific
> feature. I, however, added a few tests, one for the mangling,
> and others which test the different error messages that might
> appear when using address spaces in an unsafe way.
>
> Most of the patch tries to mirror the commit
> 309434d678721acedb299f20cdc2b4778062b180
> which introduces address spaces to the C front-end. The C and C++
> parsers share a lot but are subtly different so there might be some
> discrepancies. In particular, I ensure that address spaces do not
> conflict, that only one address space can be specified at a time, and
> that the type conversion mechanism pick the superset of the address
> spaces if available. I also forbid address spaces in compound literals.
>
> I have only tested the patch on x86 and on our not-yet-upstream
> architecture, without seeing any regressions.
>
> NB: I do not add a DSO for the moment because I have started the process of
> assigning copyright to the FSF.
>
> # ------------------------ >8 ------------------------
> Add support for custom address spaces in C++
>
> gcc/
> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>
> gcc/c/
> * c-decl.cc: Remove c_register_addr_space.
>
> gcc/c-family/
> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> * c-common.h: Remove the FIXME.
> (addr_space_superset): Add prototype.
>
> gcc/cp/
> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> (struct cp_decl_specifier_seq): Likewise.
> * decl.cc (get_type_quals): Likewise.
> (check_tag_decl): Likewise.
> * parser.cc (cp_parser_type_specifier): Likewise.
> (cp_parser_cv_qualifier_seq_opt): Likewise.
> (cp_parser_postfix_expression): Likewise.
> (cp_parser_type_specifier): Likewise.
> (set_and_check_decl_spec_loc): Likewise.
> * typeck.cc (composite_pointer_type): Likewise
> (comp_ptr_ttypes_real): Likewise.
> * tree.cc: Remove c_register_addr_space stub.
> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> using the extended qualifier notation.
>
> gcc/doc
> * extend.texi (Named Address Spaces): add a mention about C++
> support.
>
> gcc/testsuite/
> * g++.dg/abi/mangle-addr-space1.C: New test.
> * g++.dg/parse/addr-space.C: New test.
> * g++.dg/parse/addr-space1.C: New test.
> * g++.dg/parse/addr-space2.C: New test.
>
> # ------------------------ >8 ------------------------
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index bb0544eeaea..ff1146ecc25 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
> return IDENTIFIER_POINTER (ridpointers [rid]);
> }
>
> +/* Return true if between two named address spaces, whether there is a superset
> + named address space that encompasses both address spaces. If there is a
> + superset, return which address space is the superset. */
> +
> +bool
> +addr_space_superset (addr_space_t as1, addr_space_t as2,
> + addr_space_t * common)
> +{
> + if (as1 == as2)
> + {
> + *common = as1;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as1, as2))
> + {
> + *common = as2;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as2, as1))
> + {
> + *common = as1;
> + return true;
> + }
> + else
> + return false;
So it's not possible for the two spaces to both be subsets of a third?
> +}
> +
> /* Push current bindings for the function name VAR_DECLS. */
>
> void
> @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> return build_nonstandard_integer_type (width, unsignedp);
> }
>
> +/* Register reserved keyword WORD as qualifier for address space AS. */
> +
> +void
> +c_register_addr_space (const char *word, addr_space_t as)
> +{
> + int rid = RID_FIRST_ADDR_SPACE + as;
> + tree id;
> +
> + /* Address space qualifiers are only supported
> + in C with GNU extensions enabled. */
> + if (c_dialect_objc () || flag_no_asm)
> + return;
> +
> + id = get_identifier (word);
> + C_SET_RID_CODE (id, rid);
> + TREE_LANG_FLAG_0 (id) = 1;
> + ridpointers[rid] = id;
> +}
> +
> /* The C version of the register_builtin_type langhook. */
>
> void
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 52a85bfb783..d36f9e4975b 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.cc and cp/tree.cc. FIXME. */
> -extern void c_register_addr_space (const char *str, addr_space_t as);
> -
> /* In c-common.cc. */
> extern bool in_late_binary_op;
> extern const char *c_addr_space_name (addr_space_t as);
> +extern const char *c_addr_space_name (addr_space_t as);
> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> extern tree identifier_global_value (tree);
> extern tree identifier_global_tag (tree);
> extern bool names_builtin_p (const char *);
> @@ -952,6 +951,7 @@ extern void c_common_finish (void);
> extern void c_common_parse_file (void);
> extern FILE *get_dump_info (int, dump_flags_t *);
> extern alias_set_type c_common_get_alias_set (tree);
> +extern void c_register_addr_space (const char *, addr_space_t);
> extern void c_register_builtin_type (tree, const char*);
> extern bool c_promoting_integer_type_p (const_tree);
> extern bool self_promoting_args_p (const_tree);
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index c701f07befe..e1bb4f1cf37 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
> ext_block = NULL;
> }
>
> -/* Register reserved keyword WORD as qualifier for address space AS. */
> -
> -void
> -c_register_addr_space (const char *word, addr_space_t as)
> -{
> - int rid = RID_FIRST_ADDR_SPACE + as;
> - tree id;
> -
> - /* Address space qualifiers are only supported
> - in C with GNU extensions enabled. */
> - if (c_dialect_objc () || flag_no_asm)
> - return;
> -
> - id = get_identifier (word);
> - C_SET_RID_CODE (id, rid);
> - C_IS_RESERVED_WORD (id) = 1;
> - ridpointers [rid] = id;
> -}
> -
> /* Return identifier to look up for omp declare reduction. */
>
> tree
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index e130196a3a7..17185fd3da4 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
> return type;
> }
>
> -/* Return true if between two named address spaces, whether there is a superset
> - named address space that encompasses both address spaces. If there is a
> - superset, return which address space is the superset. */
> -
> -static bool
> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> -{
> - if (as1 == as2)
> - {
> - *common = as1;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as1, as2))
> - {
> - *common = as2;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as2, as1))
> - {
> - *common = as1;
> - return true;
> - }
> - else
> - return false;
> -}
> -
> /* Return a variant of TYPE which has all the type qualifiers of LIKE
> as well as those of TYPE. */
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 72f4398a8f9..f1da04c6c58 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
> ds_const,
> ds_volatile,
> ds_restrict,
> + ds_addr_space,
> ds_inline,
> ds_virtual,
> ds_explicit,
> @@ -6278,6 +6279,8 @@ struct cp_decl_specifier_seq {
> /* True iff the alternate "__intN__" form of the __intN type has been
> used. */
> BOOL_BITFIELD int_n_alt: 1;
> + /* The address space that the declaration belongs to. */
> + addr_space_t address_space;
New non-bit-fields should be added before the bit-fields.
> };
>
> /* The various kinds of declarators. */
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 9f78c500a15..16cc1917085 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> type_quals |= TYPE_QUAL_VOLATILE;
> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> type_quals |= TYPE_QUAL_RESTRICT;
> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>
> return type_quals;
> }
> @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> error_at (declspecs->locations[ds_restrict],
> "%<__restrict%> can only be specified for objects and "
> "functions");
> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + error_at (declspecs->locations[ds_addr_space],
> + "address space can only be specified for objects and "
> + "functions");
> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> error_at (declspecs->locations[ds_thread],
> "%<__thread%> can only be specified for objects "
> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> index eb53e0ebeb4..06625ad9a4b 100644
> --- a/gcc/cp/mangle.cc
> +++ b/gcc/cp/mangle.cc
> @@ -2509,6 +2509,15 @@ write_CV_qualifiers_for_type (const tree type)
> array. */
> cp_cv_quals quals = TYPE_QUALS (type);
>
> + if (DECODE_QUAL_ADDR_SPACE (quals))
> + {
> + addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals);
This can be
if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
so you don't need to repeat it.
> + const char *as_name = c_addr_space_name (as);
> + write_char ('U');
> + write_unsigned_number (strlen (as_name));
> + write_string (as_name);
> + ++num_qualifiers;
> + }
> if (quals & TYPE_QUAL_RESTRICT)
> {
> write_char ('r');
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 763df6f479b..110ceafc98b 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> postfix_expression = error_mark_node;
> break;
> }
> + if (type != error_mark_node
> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> + && current_function_decl)
> + {
> + error
> + ("compound literal qualified by address-space qualifier");
> + type = error_mark_node;
> + }
> /* Form the representation of the compound-literal. */
> postfix_expression
> = finish_compound_literal (type, initializer,
> @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
> break;
> }
>
> +
> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> + {
> + ds = ds_addr_space;
> + if (is_cv_qualifier)
> + *is_cv_qualifier = true;
> + }
> +
> +
> /* Handle simple keywords. */
> if (ds != ds_last)
> {
> @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> GNU Extension:
>
> cv-qualifier:
> + address-space-qualifier
> __restrict__
>
> Returns a bitmask representing the cv-qualifiers. */
> @@ -23812,6 +23830,13 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> break;
> }
>
> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
> + token->keyword <= RID_LAST_ADDR_SPACE)
> + {
> + cv_qualifier =
> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> + }
We usually omit braces around a single statement.
> +
> if (!cv_qualifier)
> break;
>
> @@ -32705,6 +32730,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> decl_specs->locations[ds] = location;
> if (ds == ds_thread)
> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> + else if (ds == ds_addr_space)
> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> }
> else
> {
> @@ -32737,6 +32764,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> error_at (&richloc, "duplicate %qD", token->u.value);
> }
> }
> + else if (ds == ds_addr_space)
> + {
> + addr_space_t as1 = decl_specs->address_space;
> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> +
> + gcc_rich_location richloc (location);
> + richloc.add_fixit_remove ();
> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> + && as1 != as2)
> + error_at (&richloc,
> + "conflicting named address spaces (%s vs %s)",
> + c_addr_space_name (as1), c_addr_space_name (as2));
> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> + error_at (&richloc,
> + "duplicate named address space %s",
> + c_addr_space_name (as1));
> +
> + decl_specs->address_space = as2;
> + }
> else
> {
> static const char *const decl_spec_names[] = {
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index 3b37567cbd7..5e14ac837fc 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
> DECL_CHAIN (t) = NULL_TREE;
> }
>
> -/* Stub for c-common. Please keep in sync with c-decl.cc.
> - FIXME: If address space support is target specific, then this
> - should be a C target hook. But currently this is not possible,
> - because this function is called via REGISTER_TARGET_PRAGMAS. */
> -void
> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> -{
> -}
> -
> /* Return the number of operands in T that we care about for things like
> mangling. */
>
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index ceb80d9744f..5d48920e687 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
> else
> return error_mark_node;
> }
> + /* If possible merge the address space into the superset of the address
> + spaces of t1 and t2, or raise an error. */
> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> + addr_space_t as_common;
> +
> + /* If the two named address spaces are different, determine the common
> + superset address space. If there isn't one, raise an error. */
> + if (!addr_space_superset (as_t1, as_t2, &as_common))
> + {
> + as_common = as_t1;
> + error_at (location,
> + "%qT and %qT are in disjoint named address spaces",
> + t1, t2);
> + }
> result_type
> = cp_build_qualified_type (void_type_node,
> - (cp_type_quals (TREE_TYPE (t1))
> - | cp_type_quals (TREE_TYPE (t2))));
> + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
> + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> result_type = build_pointer_type (result_type);
> /* Merge the attributes. */
> attributes = (*targetm.merge_type_attributes) (t1, t2);
> @@ -10805,6 +10821,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> to_more_cv_qualified = true;
> }
>
> + /* Warn about conversions between pointers to disjoint
> + address spaces. */
> + if (TREE_CODE (from) == POINTER_TYPE
> + && TREE_CODE (to) == POINTER_TYPE)
> + {
> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> + addr_space_t as_common;
> +
> + if (!addr_space_superset (as_to, as_from, &as_common))
> + return false;
> + }
> +
> if (constp > 0)
> constp &= TYPE_READONLY (to);
> }
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 84e6f6694ab..c89df8778b2 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> @section Named Address Spaces
> @cindex Named Address Spaces
>
> -As an extension, GNU C supports named address spaces as
> +As an extension, GNU C and GNU C++ support named address spaces as
> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> address spaces in GCC will evolve as the draft technical report
> changes. Calling conventions for any target might also change. At
> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> new file mode 100644
> index 00000000000..c01f8d6054a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> @@ -0,0 +1,10 @@
> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> +
> +int f (int volatile __seg_fs *a)
> +{
> + return *a;
> +}
> +
> +int main () {}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> new file mode 100644
> index 00000000000..ebb6316054a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> @@ -0,0 +1,9 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> +
> +int
> +main ()
> +{
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> new file mode 100644
> index 00000000000..2e8ee32a885
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> @@ -0,0 +1,10 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-std=gnu++98" }
> +
> +int
> +main ()
> +{
> + struct foo {int a; char b[2];} structure;
> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> new file mode 100644
> index 00000000000..5b2c0f28078
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> @@ -0,0 +1,9 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> +
> +int
> +main ()
> +{
> + return 0;
> +}
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 8844471e9a5..b7da4c5141a 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
>
> /* Encode/decode the named memory support as part of the qualifier. If more
> than 8 qualifiers are added, these macros need to be adjusted. */
> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>
> /* Return all qualifiers except for the address space qualifiers. */
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-10 19:20 ` Jason Merrill
@ 2022-10-11 22:03 ` Paul Iannetta
2022-10-12 1:49 ` Jason Merrill
2026-05-15 12:18 ` [PATCH v2] " Thomas Schwinge
0 siblings, 2 replies; 86+ messages in thread
From: Paul Iannetta @ 2022-10-11 22:03 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
Thank you very much for the comments.
On Mon, Oct 10, 2022 at 03:20:13PM -0400, Jason Merrill wrote:
> On 10/9/22 12:12, Paul Iannetta wrote:
> > That's a nice feature! LLVM is using AS<number> to mangle the
> > address-name qualified types but that would have required an update of
> > libiberty's demangler in the binutils as well.
>
> And they haven't proposed this mangling to
>
> https://github.com/itanium-cxx-abi/cxx-abi/
>
> yet, either.
When looking at clang/lib/AST/{Microsoft,Itamium}Mangle.cpp, the
comments may suggest that they wanted to implement them as extended
qualifiers prefixed by an `U' but that's not what they ended up doing.
> You certainly want some template tests, say
>
> template <class T>
> int f (T *p) { return *p; }
> __seg_fs int *a;
> int main() { f(a); }
> // test for mangling of f<__seg_fs int>
>
> -----
>
> template <class T>
> int f (T __seg_gs *p) { return *p; }
> __seg_fs int *a;
> int main() { f(a); } // error, conflicting address spaces
Indeed, I was getting the first one right by a stroke of luck but not
the second. I've consequently adapted the part which checks and
computes the unification of cv-qualifiers in the presence of address
spaces. The idea being that a type parameter T can match any address
spaces but an address-space qualified parameter can't accept more
general address spaces than itself.
> > +/* Return true if between two named address spaces, whether there is a superset
> > + named address space that encompasses both address spaces. If there is a
> > + superset, return which address space is the superset. */
> > +
> > +bool
> > +addr_space_superset (addr_space_t as1, addr_space_t as2,
> > + addr_space_t * common)
> > +{
> > + if (as1 == as2)
> > + {
> > + *common = as1;
> > + return true;
> > + }
> > + else if (targetm.addr_space.subset_p (as1, as2))
> > + {
> > + *common = as2;
> > + return true;
> > + }
> > + else if (targetm.addr_space.subset_p (as2, as1))
> > + {
> > + *common = as1;
> > + return true;
> > + }
> > + else
> > + return false;
>
> So it's not possible for the two spaces to both be subsets of a third?
>
According to the [N1275, page 38]:
Address spaces may overlap in a nested fashion. For any two address
spaces, either the address spaces must be disjoint, they must be
equivalent, or one must be a subset of the other. [...] There is no
requirement that named address spaces (intrinsic or otherwise) be
subsets of the generic address space.
[N1275]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1275.pdf
Hence, two disjoint address spaces can't be subsets of a third, per
the draft.
>
> New non-bit-fields should be added before the bit-fields.
>
Done.
> > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> > index eb53e0ebeb4..06625ad9a4b 100644
> > --- a/gcc/cp/mangle.cc
> > +++ b/gcc/cp/mangle.cc
> > @@ -2509,6 +2509,15 @@ write_CV_qualifiers_for_type (const tree type)
> > array. */
> > cp_cv_quals quals = TYPE_QUALS (type);
> > + if (DECODE_QUAL_ADDR_SPACE (quals))
> > + {
> > + addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals);
>
> This can be
>
> if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
>
> so you don't need to repeat it.
I thought this was c++17 only, but in fact c++17 only broadened the
idiom. Nice! (It would be even nicer to have this feature in C as
well)
> > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > index 763df6f479b..110ceafc98b 100644
> > --- a/gcc/cp/parser.cc
> > +++ b/gcc/cp/parser.cc
> > [...]
> > @@ -23812,6 +23830,13 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> > break;
> > }
> > + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
> > + token->keyword <= RID_LAST_ADDR_SPACE)
> > + {
> > + cv_qualifier =
> > + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> > + }
>
> We usually omit braces around a single statement.
>
Done.
# ------------------------ >8 ------------------------
Add support for custom address spaces in C++
gcc/
* tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
gcc/c/
* c-decl.cc: Remove c_register_addr_space.
gcc/c-family/
* c-common.cc (c_register_addr_space): Imported from c-decl.cc
(addr_space_superset): Imported from gcc/c/c-typecheck.cc
* c-common.h: Remove the FIXME.
(addr_space_superset): New declaration.
gcc/cp/
* cp-tree.h (enum cp_decl_spec): Add addr_space support.
(struct cp_decl_specifier_seq): Likewise.
* decl.cc (get_type_quals): Likewise.
(check_tag_decl): Likewise.
* parser.cc (cp_parser_type_specifier): Likewise.
(cp_parser_cv_qualifier_seq_opt): Likewise.
(cp_parser_postfix_expression): Likewise.
(cp_parser_type_specifier): Likewise.
(set_and_check_decl_spec_loc): Likewise.
* typeck.cc (composite_pointer_type): Likewise
(comp_ptr_ttypes_real): Likewise.
* pt.cc (check_cv_quals_for_unify): Likewise.
(unify): Likewise.
* tree.cc: Remove c_register_addr_space stub.
* mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
using the extended qualifier notation.
gcc/doc
* extend.texi (Named Address Spaces): add a mention about C++
support.
gcc/testsuite/
* g++.dg/abi/mangle-addr-space1.C: New test.
* g++.dg/abi/mangle-addr-space2.C: New test.
* g++.dg/parse/addr-space.C: New test.
* g++.dg/parse/addr-space1.C: New test.
* g++.dg/parse/addr-space2.C: New test.
* g++.dg/parse/template/spec-addr-space.C: New test.
# ------------------------ >8 ------------------------
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index bb0544eeaea..ff1146ecc25 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
return IDENTIFIER_POINTER (ridpointers [rid]);
}
+/* Return true if between two named address spaces, whether there is a superset
+ named address space that encompasses both address spaces. If there is a
+ superset, return which address space is the superset. */
+
+bool
+addr_space_superset (addr_space_t as1, addr_space_t as2,
+ addr_space_t * common)
+{
+ if (as1 == as2)
+ {
+ *common = as1;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as1, as2))
+ {
+ *common = as2;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as2, as1))
+ {
+ *common = as1;
+ return true;
+ }
+ else
+ return false;
+}
+
/* Push current bindings for the function name VAR_DECLS. */
void
@@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ TREE_LANG_FLAG_0 (id) = 1;
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 52a85bfb783..d36f9e4975b 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
+extern const char *c_addr_space_name (addr_space_t as);
+extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
extern bool names_builtin_p (const char *);
@@ -952,6 +951,7 @@ extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern FILE *get_dump_info (int, dump_flags_t *);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index c701f07befe..e1bb4f1cf37 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
-}
-
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index b271af9bedb..c4b01368534 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
return type;
}
-/* Return true if between two named address spaces, whether there is a superset
- named address space that encompasses both address spaces. If there is a
- superset, return which address space is the superset. */
-
-static bool
-addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
-{
- if (as1 == as2)
- {
- *common = as1;
- return true;
- }
- else if (targetm.addr_space.subset_p (as1, as2))
- {
- *common = as2;
- return true;
- }
- else if (targetm.addr_space.subset_p (as2, as1))
- {
- *common = as1;
- return true;
- }
- else
- return false;
-}
-
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 72f4398a8f9..82a6d72f5df 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6206,6 +6206,7 @@ enum cp_decl_spec {
ds_const,
ds_volatile,
ds_restrict,
+ ds_addr_space,
ds_inline,
ds_virtual,
ds_explicit,
@@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
cp_storage_class storage_class;
/* For the __intN declspec, this stores the index into the int_n_* arrays. */
int int_n_idx;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
/* True iff TYPE_SPEC defines a class or enum. */
BOOL_BITFIELD type_definition_p : 1;
/* True iff multiple types were (erroneously) specified for this
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 9f78c500a15..16cc1917085 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
return type_quals;
}
@@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
error_at (declspecs->locations[ds_restrict],
"%<__restrict%> can only be specified for objects and "
"functions");
+ else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ error_at (declspecs->locations[ds_addr_space],
+ "address space can only be specified for objects and "
+ "functions");
else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
error_at (declspecs->locations[ds_thread],
"%<__thread%> can only be specified for objects "
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index eb53e0ebeb4..8ea6b7c903e 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
array. */
cp_cv_quals quals = TYPE_QUALS (type);
+ if (addr_space_t as DECODE_QUAL_ADDR_SPACE (quals))
+ {
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 763df6f479b..f0169d83ab0 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
postfix_expression = error_mark_node;
break;
}
+ if (type != error_mark_node
+ && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
+ && current_function_decl)
+ {
+ error
+ ("compound literal qualified by address-space qualifier");
+ type = error_mark_node;
+ }
/* Form the representation of the compound-literal. */
postfix_expression
= finish_compound_literal (type, initializer,
@@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+
+ if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ }
+
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
GNU Extension:
cv-qualifier:
+ address-space-qualifier
__restrict__
Returns a bitmask representing the cv-qualifiers. */
@@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
+ if (RID_FIRST_ADDR_SPACE <= token->keyword &&
+ token->keyword <= RID_LAST_ADDR_SPACE)
+ cv_qualifier =
+ ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
+
if (!cv_qualifier)
break;
@@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
decl_specs->locations[ds] = location;
if (ds == ds_thread)
decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ else if (ds == ds_addr_space)
+ decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
}
else
{
@@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
error_at (&richloc, "duplicate %qD", token->u.value);
}
}
+ else if (ds == ds_addr_space)
+ {
+ addr_space_t as1 = decl_specs->address_space;
+ addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
+
+ gcc_rich_location richloc (location);
+ richloc.add_fixit_remove ();
+ if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
+ && as1 != as2)
+ error_at (&richloc,
+ "conflicting named address spaces (%s vs %s)",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
+ error_at (&richloc,
+ "duplicate named address space %s",
+ c_addr_space_name (as1));
+
+ decl_specs->address_space = as2;
+ }
else
{
static const char *const decl_spec_names[] = {
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index bef31416fb7..5bb5d657155 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
- int arg_quals = cp_type_quals (arg);
- int parm_quals = cp_type_quals (parm);
+ int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
+
+ /* Try to unify ARG's address space into PARM's address space.
+ If PARM does not have any address space qualifiers (ie., as_parm is 0),
+ there are no constraints on address spaces for this type. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
+ addr_space_t as_common;
+ addr_space_superset (as_arg, as_parm, &as_common);
+
+ if (!(as_parm == as_common || as_parm == 0))
+ return 0;
if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
@@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
arg, parm))
return unify_cv_qual_mismatch (explain_p, parm, arg);
+ int arg_cv_quals = cp_type_quals (arg);
+ int parm_cv_quals = cp_type_quals (parm);
+
+ /* If PARM does not contain any address spaces constraints it can
+ fully match the address space of ARG. However, if PARM contains an
+ address space constraints, it becomes the upper bound. That is,
+ AS_ARG may be promoted to AS_PARM but not the converse. If we
+ ended up here, it means that `check_cv_quals_for_unify' succeeded
+ and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
+ AS_PARM. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
+ addr_space_t as_common = !as_parm ? as_arg : as_parm;
+
/* Consider the case where ARG is `const volatile int' and
PARM is `const T'. Then, T should be `volatile int'. */
- arg = cp_build_qualified_type_real
- (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
+ int unified_cv =
+ CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
+ | ENCODE_QUAL_ADDR_SPACE (as_common);
+ arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
if (arg == error_mark_node)
return unify_invalid (explain_p);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 3b37567cbd7..5e14ac837fc 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index ceb80d9744f..5d48920e687 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
else
return error_mark_node;
}
+ /* If possible merge the address space into the superset of the address
+ spaces of t1 and t2, or raise an error. */
+ addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
+ addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as_t1, as_t2, &as_common))
+ {
+ as_common = as_t1;
+ error_at (location,
+ "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ }
result_type
= cp_build_qualified_type (void_type_node,
- (cp_type_quals (TREE_TYPE (t1))
- | cp_type_quals (TREE_TYPE (t2))));
+ (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
+ | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
+ | ENCODE_QUAL_ADDR_SPACE (as_common)));
result_type = build_pointer_type (result_type);
/* Merge the attributes. */
attributes = (*targetm.merge_type_attributes) (t1, t2);
@@ -10805,6 +10821,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
to_more_cv_qualified = true;
}
+ /* Warn about conversions between pointers to disjoint
+ address spaces. */
+ if (TREE_CODE (from) == POINTER_TYPE
+ && TREE_CODE (to) == POINTER_TYPE)
+ {
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common))
+ return false;
+ }
+
if (constp > 0)
constp &= TYPE_READONLY (to);
}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ac3db88566d..1e0d436c02c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
@section Named Address Spaces
@cindex Named Address Spaces
-As an extension, GNU C supports named address spaces as
+As an extension, GNU C and GNU C++ support named address spaces as
defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
address spaces in GCC will evolve as the draft technical report
changes. Calling conventions for any target might also change. At
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
new file mode 100644
index 00000000000..c01f8d6054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do run { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
+
+int f (int volatile __seg_fs *a)
+{
+ return *a;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
new file mode 100644
index 00000000000..862bbbdcdf2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do run { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
+
+template <class T>
+int f (T *p) { return *p; }
+int g (__seg_fs int *p) { return *p; }
+__seg_fs int *a;
+int main() { f(a); }
diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
new file mode 100644
index 00000000000..ebb6316054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
new file mode 100644
index 00000000000..2e8ee32a885
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-std=gnu++98" }
+
+int
+main ()
+{
+ struct foo {int a; char b[2];} structure;
+ structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
new file mode 100644
index 00000000000..5b2c0f28078
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
new file mode 100644
index 00000000000..ae9f4de0e1f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+template <class T>
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
+ // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
+__seg_fs int *a;
+int main() { f(a); } // { dg-error "no matching" }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
diff --git a/gcc/tree.h b/gcc/tree.h
index 8844471e9a5..b7da4c5141a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
/* Encode/decode the named memory support as part of the qualifier. If more
than 8 qualifiers are added, these macros need to be adjusted. */
-#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
+#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
#define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
/* Return all qualifiers except for the address space qualifiers. */
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-11 22:03 ` [PATCH v2] " Paul Iannetta
@ 2022-10-12 1:49 ` Jason Merrill
2022-10-13 0:52 ` Paul Iannetta
2026-05-15 12:18 ` [PATCH v2] " Thomas Schwinge
1 sibling, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-12 1:49 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/11/22 18:03, Paul Iannetta wrote:
> Thank you very much for the comments.
>
> On Mon, Oct 10, 2022 at 03:20:13PM -0400, Jason Merrill wrote:
>> On 10/9/22 12:12, Paul Iannetta wrote:
>>> That's a nice feature! LLVM is using AS<number> to mangle the
>>> address-name qualified types but that would have required an update of
>>> libiberty's demangler in the binutils as well.
>>
>> And they haven't proposed this mangling to
>>
>> https://github.com/itanium-cxx-abi/cxx-abi/
>>
>> yet, either.
>
> When looking at clang/lib/AST/{Microsoft,Itamium}Mangle.cpp, the
> comments may suggest that they wanted to implement them as extended
> qualifiers prefixed by an `U' but that's not what they ended up doing.
>
>> You certainly want some template tests, say
>>
>> template <class T>
>> int f (T *p) { return *p; }
>> __seg_fs int *a;
>> int main() { f(a); }
>> // test for mangling of f<__seg_fs int>
>>
>> -----
>>
>> template <class T>
>> int f (T __seg_gs *p) { return *p; }
>> __seg_fs int *a;
>> int main() { f(a); } // error, conflicting address spaces
>
> Indeed, I was getting the first one right by a stroke of luck but not
> the second. I've consequently adapted the part which checks and
> computes the unification of cv-qualifiers in the presence of address
> spaces. The idea being that a type parameter T can match any address
> spaces but an address-space qualified parameter can't accept more
> general address spaces than itself.
>
>>> +/* Return true if between two named address spaces, whether there is a superset
>>> + named address space that encompasses both address spaces. If there is a
>>> + superset, return which address space is the superset. */
>>> +
>>> +bool
>>> +addr_space_superset (addr_space_t as1, addr_space_t as2,
>>> + addr_space_t * common)
>>> +{
>>> + if (as1 == as2)
>>> + {
>>> + *common = as1;
>>> + return true;
>>> + }
>>> + else if (targetm.addr_space.subset_p (as1, as2))
>>> + {
>>> + *common = as2;
>>> + return true;
>>> + }
>>> + else if (targetm.addr_space.subset_p (as2, as1))
>>> + {
>>> + *common = as1;
>>> + return true;
>>> + }
>>> + else
>>> + return false;
>>
>> So it's not possible for the two spaces to both be subsets of a third?
>>
>
> According to the [N1275, page 38]:
> Address spaces may overlap in a nested fashion. For any two address
> spaces, either the address spaces must be disjoint, they must be
> equivalent, or one must be a subset of the other. [...] There is no
> requirement that named address spaces (intrinsic or otherwise) be
> subsets of the generic address space.
> [N1275]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1275.pdf
>
> Hence, two disjoint address spaces can't be subsets of a third, per
> the draft.
>
>>
>> New non-bit-fields should be added before the bit-fields.
>>
>
> Done.
>
>>> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
>>> index eb53e0ebeb4..06625ad9a4b 100644
>>> --- a/gcc/cp/mangle.cc
>>> +++ b/gcc/cp/mangle.cc
>>> @@ -2509,6 +2509,15 @@ write_CV_qualifiers_for_type (const tree type)
>>> array. */
>>> cp_cv_quals quals = TYPE_QUALS (type);
>>> + if (DECODE_QUAL_ADDR_SPACE (quals))
>>> + {
>>> + addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals);
>>
>> This can be
>>
>> if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
>>
>> so you don't need to repeat it.
>
> I thought this was c++17 only, but in fact c++17 only broadened the
> idiom. Nice! (It would be even nicer to have this feature in C as
> well)
>>> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
>>> index 763df6f479b..110ceafc98b 100644
>>> --- a/gcc/cp/parser.cc
>>> +++ b/gcc/cp/parser.cc
>>> [...]
>>> @@ -23812,6 +23830,13 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
>>> break;
>>> }
>>> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
>>> + token->keyword <= RID_LAST_ADDR_SPACE)
>>> + {
>>> + cv_qualifier =
>>> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>>> + }
>>
>> We usually omit braces around a single statement.
>>
> Done.
>
> # ------------------------ >8 ------------------------
> Add support for custom address spaces in C++
>
> gcc/
> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>
> gcc/c/
> * c-decl.cc: Remove c_register_addr_space.
>
> gcc/c-family/
> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> * c-common.h: Remove the FIXME.
> (addr_space_superset): New declaration.
>
> gcc/cp/
> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> (struct cp_decl_specifier_seq): Likewise.
> * decl.cc (get_type_quals): Likewise.
> (check_tag_decl): Likewise.
> * parser.cc (cp_parser_type_specifier): Likewise.
> (cp_parser_cv_qualifier_seq_opt): Likewise.
> (cp_parser_postfix_expression): Likewise.
> (cp_parser_type_specifier): Likewise.
> (set_and_check_decl_spec_loc): Likewise.
> * typeck.cc (composite_pointer_type): Likewise
> (comp_ptr_ttypes_real): Likewise.
> * pt.cc (check_cv_quals_for_unify): Likewise.
> (unify): Likewise.
> * tree.cc: Remove c_register_addr_space stub.
> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> using the extended qualifier notation.
>
> gcc/doc
> * extend.texi (Named Address Spaces): add a mention about C++
> support.
>
> gcc/testsuite/
> * g++.dg/abi/mangle-addr-space1.C: New test.
> * g++.dg/abi/mangle-addr-space2.C: New test.
> * g++.dg/parse/addr-space.C: New test.
> * g++.dg/parse/addr-space1.C: New test.
> * g++.dg/parse/addr-space2.C: New test.
> * g++.dg/parse/template/spec-addr-space.C: New test.
>
> # ------------------------ >8 ------------------------
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index bb0544eeaea..ff1146ecc25 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
> return IDENTIFIER_POINTER (ridpointers [rid]);
> }
>
> +/* Return true if between two named address spaces, whether there is a superset
> + named address space that encompasses both address spaces. If there is a
> + superset, return which address space is the superset. */
> +
> +bool
> +addr_space_superset (addr_space_t as1, addr_space_t as2,
> + addr_space_t * common)
> +{
> + if (as1 == as2)
> + {
> + *common = as1;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as1, as2))
> + {
> + *common = as2;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as2, as1))
> + {
> + *common = as1;
> + return true;
> + }
> + else
> + return false;
> +}
> +
> /* Push current bindings for the function name VAR_DECLS. */
>
> void
> @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> return build_nonstandard_integer_type (width, unsignedp);
> }
>
> +/* Register reserved keyword WORD as qualifier for address space AS. */
> +
> +void
> +c_register_addr_space (const char *word, addr_space_t as)
> +{
> + int rid = RID_FIRST_ADDR_SPACE + as;
> + tree id;
> +
> + /* Address space qualifiers are only supported
> + in C with GNU extensions enabled. */
> + if (c_dialect_objc () || flag_no_asm)
> + return;
> +
> + id = get_identifier (word);
> + C_SET_RID_CODE (id, rid);
> + TREE_LANG_FLAG_0 (id) = 1;
> + ridpointers[rid] = id;
> +}
> +
> /* The C version of the register_builtin_type langhook. */
>
> void
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 52a85bfb783..d36f9e4975b 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.cc and cp/tree.cc. FIXME. */
> -extern void c_register_addr_space (const char *str, addr_space_t as);
> -
> /* In c-common.cc. */
> extern bool in_late_binary_op;
> extern const char *c_addr_space_name (addr_space_t as);
> +extern const char *c_addr_space_name (addr_space_t as);
> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> extern tree identifier_global_value (tree);
> extern tree identifier_global_tag (tree);
> extern bool names_builtin_p (const char *);
> @@ -952,6 +951,7 @@ extern void c_common_finish (void);
> extern void c_common_parse_file (void);
> extern FILE *get_dump_info (int, dump_flags_t *);
> extern alias_set_type c_common_get_alias_set (tree);
> +extern void c_register_addr_space (const char *, addr_space_t);
> extern void c_register_builtin_type (tree, const char*);
> extern bool c_promoting_integer_type_p (const_tree);
> extern bool self_promoting_args_p (const_tree);
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index c701f07befe..e1bb4f1cf37 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
> ext_block = NULL;
> }
>
> -/* Register reserved keyword WORD as qualifier for address space AS. */
> -
> -void
> -c_register_addr_space (const char *word, addr_space_t as)
> -{
> - int rid = RID_FIRST_ADDR_SPACE + as;
> - tree id;
> -
> - /* Address space qualifiers are only supported
> - in C with GNU extensions enabled. */
> - if (c_dialect_objc () || flag_no_asm)
> - return;
> -
> - id = get_identifier (word);
> - C_SET_RID_CODE (id, rid);
> - C_IS_RESERVED_WORD (id) = 1;
> - ridpointers [rid] = id;
> -}
> -
> /* Return identifier to look up for omp declare reduction. */
>
> tree
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index b271af9bedb..c4b01368534 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
> return type;
> }
>
> -/* Return true if between two named address spaces, whether there is a superset
> - named address space that encompasses both address spaces. If there is a
> - superset, return which address space is the superset. */
> -
> -static bool
> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> -{
> - if (as1 == as2)
> - {
> - *common = as1;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as1, as2))
> - {
> - *common = as2;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as2, as1))
> - {
> - *common = as1;
> - return true;
> - }
> - else
> - return false;
> -}
> -
> /* Return a variant of TYPE which has all the type qualifiers of LIKE
> as well as those of TYPE. */
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 72f4398a8f9..82a6d72f5df 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
> ds_const,
> ds_volatile,
> ds_restrict,
> + ds_addr_space,
> ds_inline,
> ds_virtual,
> ds_explicit,
> @@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
> cp_storage_class storage_class;
> /* For the __intN declspec, this stores the index into the int_n_* arrays. */
> int int_n_idx;
> + /* The address space that the declaration belongs to. */
> + addr_space_t address_space;
> /* True iff TYPE_SPEC defines a class or enum. */
> BOOL_BITFIELD type_definition_p : 1;
> /* True iff multiple types were (erroneously) specified for this
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 9f78c500a15..16cc1917085 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> type_quals |= TYPE_QUAL_VOLATILE;
> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> type_quals |= TYPE_QUAL_RESTRICT;
> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>
> return type_quals;
> }
> @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> error_at (declspecs->locations[ds_restrict],
> "%<__restrict%> can only be specified for objects and "
> "functions");
> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + error_at (declspecs->locations[ds_addr_space],
> + "address space can only be specified for objects and "
> + "functions");
> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> error_at (declspecs->locations[ds_thread],
> "%<__thread%> can only be specified for objects "
> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> index eb53e0ebeb4..8ea6b7c903e 100644
> --- a/gcc/cp/mangle.cc
> +++ b/gcc/cp/mangle.cc
> @@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
> array. */
> cp_cv_quals quals = TYPE_QUALS (type);
>
> + if (addr_space_t as DECODE_QUAL_ADDR_SPACE (quals))
Let's still include the =, rather than rely on the parens from the macro.
> + {
> + const char *as_name = c_addr_space_name (as);
> + write_char ('U');
> + write_unsigned_number (strlen (as_name));
> + write_string (as_name);
> + ++num_qualifiers;
> + }
> if (quals & TYPE_QUAL_RESTRICT)
> {
> write_char ('r');
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 763df6f479b..f0169d83ab0 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> postfix_expression = error_mark_node;
> break;
> }
> + if (type != error_mark_node
> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> + && current_function_decl)
> + {
> + error
> + ("compound literal qualified by address-space qualifier");
> + type = error_mark_node;
> + }
It surprises that this is the only place we complain about an object
with an address-space qualifier. Shouldn't we also complain about e.g.
automatic variables/parameters or non-static data members with
address-space qualified type?
> /* Form the representation of the compound-literal. */
> postfix_expression
> = finish_compound_literal (type, initializer,
> @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
> break;
> }
>
> +
> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> + {
> + ds = ds_addr_space;
> + if (is_cv_qualifier)
> + *is_cv_qualifier = true;
> + }
> +
> +
> /* Handle simple keywords. */
> if (ds != ds_last)
> {
> @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> GNU Extension:
>
> cv-qualifier:
> + address-space-qualifier
> __restrict__
>
> Returns a bitmask representing the cv-qualifiers. */
> @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> break;
> }
>
> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
> + token->keyword <= RID_LAST_ADDR_SPACE)
> + cv_qualifier =
> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> +
> if (!cv_qualifier)
> break;
>
> @@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> decl_specs->locations[ds] = location;
> if (ds == ds_thread)
> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> + else if (ds == ds_addr_space)
> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> }
> else
> {
> @@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> error_at (&richloc, "duplicate %qD", token->u.value);
> }
> }
> + else if (ds == ds_addr_space)
> + {
> + addr_space_t as1 = decl_specs->address_space;
> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> +
> + gcc_rich_location richloc (location);
> + richloc.add_fixit_remove ();
> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> + && as1 != as2)
> + error_at (&richloc,
> + "conflicting named address spaces (%s vs %s)",
> + c_addr_space_name (as1), c_addr_space_name (as2));
> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> + error_at (&richloc,
> + "duplicate named address space %s",
> + c_addr_space_name (as1));
> +
> + decl_specs->address_space = as2;
> + }
> else
> {
> static const char *const decl_spec_names[] = {
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index bef31416fb7..5bb5d657155 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
> static int
> check_cv_quals_for_unify (int strict, tree arg, tree parm)
> {
> - int arg_quals = cp_type_quals (arg);
> - int parm_quals = cp_type_quals (parm);
> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> +
> + /* Try to unify ARG's address space into PARM's address space.
> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> + there are no constraints on address spaces for this type. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> + addr_space_t as_common;
> + addr_space_superset (as_arg, as_parm, &as_common);
> +
> + if (!(as_parm == as_common || as_parm == 0))
> + return 0;
>
> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> @@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> arg, parm))
> return unify_cv_qual_mismatch (explain_p, parm, arg);
>
> + int arg_cv_quals = cp_type_quals (arg);
> + int parm_cv_quals = cp_type_quals (parm);
> +
> + /* If PARM does not contain any address spaces constraints it can
> + fully match the address space of ARG. However, if PARM contains an
> + address space constraints, it becomes the upper bound. That is,
> + AS_ARG may be promoted to AS_PARM but not the converse. If we
> + ended up here, it means that `check_cv_quals_for_unify' succeeded
> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> + AS_PARM. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> + addr_space_t as_common = !as_parm ? as_arg : as_parm;
> +
> /* Consider the case where ARG is `const volatile int' and
> PARM is `const T'. Then, T should be `volatile int'. */
> - arg = cp_build_qualified_type_real
> - (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
> + int unified_cv =
> + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> + | ENCODE_QUAL_ADDR_SPACE (as_common);
> + arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
> if (arg == error_mark_node)
> return unify_invalid (explain_p);
>
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index 3b37567cbd7..5e14ac837fc 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
> DECL_CHAIN (t) = NULL_TREE;
> }
>
> -/* Stub for c-common. Please keep in sync with c-decl.cc.
> - FIXME: If address space support is target specific, then this
> - should be a C target hook. But currently this is not possible,
> - because this function is called via REGISTER_TARGET_PRAGMAS. */
> -void
> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> -{
> -}
> -
> /* Return the number of operands in T that we care about for things like
> mangling. */
>
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index ceb80d9744f..5d48920e687 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
> else
> return error_mark_node;
> }
> + /* If possible merge the address space into the superset of the address
> + spaces of t1 and t2, or raise an error. */
> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> + addr_space_t as_common;
> +
> + /* If the two named address spaces are different, determine the common
> + superset address space. If there isn't one, raise an error. */
> + if (!addr_space_superset (as_t1, as_t2, &as_common))
> + {
> + as_common = as_t1;
> + error_at (location,
> + "%qT and %qT are in disjoint named address spaces",
> + t1, t2);
> + }
> result_type
> = cp_build_qualified_type (void_type_node,
> - (cp_type_quals (TREE_TYPE (t1))
> - | cp_type_quals (TREE_TYPE (t2))));
> + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
> + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> result_type = build_pointer_type (result_type);
> /* Merge the attributes. */
> attributes = (*targetm.merge_type_attributes) (t1, t2);
> @@ -10805,6 +10821,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> to_more_cv_qualified = true;
> }
>
> + /* Warn about conversions between pointers to disjoint
> + address spaces. */
> + if (TREE_CODE (from) == POINTER_TYPE
> + && TREE_CODE (to) == POINTER_TYPE)
> + {
> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> + addr_space_t as_common;
> +
> + if (!addr_space_superset (as_to, as_from, &as_common))
> + return false;
> + }
> +
> if (constp > 0)
> constp &= TYPE_READONLY (to);
> }
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index ac3db88566d..1e0d436c02c 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> @section Named Address Spaces
> @cindex Named Address Spaces
>
> -As an extension, GNU C supports named address spaces as
> +As an extension, GNU C and GNU C++ support named address spaces as
> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> address spaces in GCC will evolve as the draft technical report
> changes. Calling conventions for any target might also change. At
> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> new file mode 100644
> index 00000000000..c01f8d6054a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> @@ -0,0 +1,10 @@
> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> +
> +int f (int volatile __seg_fs *a)
> +{
> + return *a;
> +}
> +
> +int main () {}
> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> new file mode 100644
> index 00000000000..862bbbdcdf2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> @@ -0,0 +1,9 @@
> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
> +
> +template <class T>
> +int f (T *p) { return *p; }
> +int g (__seg_fs int *p) { return *p; }
> +__seg_fs int *a;
> +int main() { f(a); }
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> new file mode 100644
> index 00000000000..ebb6316054a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> @@ -0,0 +1,9 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> +
> +int
> +main ()
> +{
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> new file mode 100644
> index 00000000000..2e8ee32a885
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> @@ -0,0 +1,10 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-std=gnu++98" }
> +
> +int
> +main ()
> +{
> + struct foo {int a; char b[2];} structure;
> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> new file mode 100644
> index 00000000000..5b2c0f28078
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> @@ -0,0 +1,9 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> +
> +int
> +main ()
> +{
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> new file mode 100644
> index 00000000000..ae9f4de0e1f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> @@ -0,0 +1,8 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +template <class T>
> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> +__seg_fs int *a;
> +int main() { f(a); } // { dg-error "no matching" }
> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 8844471e9a5..b7da4c5141a 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
>
> /* Encode/decode the named memory support as part of the qualifier. If more
> than 8 qualifiers are added, these macros need to be adjusted. */
> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>
> /* Return all qualifiers except for the address space qualifiers. */
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-12 1:49 ` Jason Merrill
@ 2022-10-13 0:52 ` Paul Iannetta
2022-10-13 5:46 ` Jakub Jelinek
2022-10-13 15:02 ` Jason Merrill
0 siblings, 2 replies; 86+ messages in thread
From: Paul Iannetta @ 2022-10-13 0:52 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
On Tue, Oct 11, 2022 at 09:49:43PM -0400, Jason Merrill wrote:
>
> It surprises that this is the only place we complain about an object with an
> address-space qualifier. Shouldn't we also complain about e.g. automatic
> variables/parameters or non-static data members with address-space qualified
> type?
>
Indeed, I was missing quite a few things here. Thanks.
I used the draft as basis this time and imported from the C
implementation the relevant parts. This time, the errors get properly
emitted when an address space is unduly specified; and comparisons,
assignments and comparisons are taken care of.
There are quite a few things I would like to clarify concerning some
implementation details.
- A variable with automatic storage (which is neither a pointer nor
a reference) cannot be qualified with an address space. I detect
this by the combination of `sc_none' and `! toplevel_bindings_p ()',
but I've also seen the use of `at_function_scope' at other places.
And I'm unsure which one is appropriate here.
This detection happens at the very end of grokdeclarator because I
need to know that the type is a pointer, which is not know until
very late in the function.
- I'm having some trouble deciding whether I include those three
stub programs as tests, they all compile fine and clang accepts
them as well.
Ex1:
```
int __seg_fs * fs1;
int __seg_gs * gs1;
template<typename T> struct strip;
template<typename T> struct strip<__seg_fs T *> { typedef T type; };
template<typename T> struct strip<__seg_gs T *> { typedef T type; };
int
main ()
{
*(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
return 0;
}
```
Ex2:
```
int __seg_fs * fs1;
int __seg_fs * fs2;
template<typename T, typename U> auto f (T __seg_fs * a, U __seg_gs * b) { return a; }
template<typename T, typename U> auto f (T __seg_gs * a, U __seg_fs * b) { return a; }
int
main ()
{
f (fs1, gs1);
f (gs1, fs1);
return 0;
}
```
Ex3:
```
int __seg_fs * fs1;
int __seg_gs * gs1;
template<typename T, typename U>
auto f (T __seg_fs * a, U __seg_gs * b)
{
return *(T *) a == *(U *) b;
}
int
main ()
{
return f (fs1, gs1);
}
```
Add support for custom address spaces in C++
gcc/
* tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
gcc/c/
* c-decl.cc: Remove c_register_addr_space.
gcc/c-family/
* c-common.cc (c_register_addr_space): Imported from c-decl.cc
(addr_space_superset): Imported from gcc/c/c-typecheck.cc
* c-common.h: Remove the FIXME.
(addr_space_superset): New declaration.
gcc/cp/
* cp-tree.h (enum cp_decl_spec): Add addr_space support.
(struct cp_decl_specifier_seq): Likewise.
* decl.cc (get_type_quals): Likewise.
(check_tag_decl): Likewise.
(grokdeclarator): Likewise.
* parser.cc (cp_parser_type_specifier): Likewise.
(cp_parser_cv_qualifier_seq_opt): Likewise.
(cp_parser_postfix_expression): Likewise.
(cp_parser_type_specifier): Likewise.
(set_and_check_decl_spec_loc): Likewise.
* typeck.cc (composite_pointer_type): Likewise
(comp_ptr_ttypes_real): Likewise.
(same_type_ignoring_top_level_qualifiers_p): Likewise.
* pt.cc (check_cv_quals_for_unify): Likewise.
(unify): Likewise.
* tree.cc: Remove c_register_addr_space stub.
* mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
using the extended qualifier notation.
gcc/doc
* extend.texi (Named Address Spaces): add a mention about C++
support.
gcc/testsuite/
* g++.dg/abi/mangle-addr-space1.C: New test.
* g++.dg/abi/mangle-addr-space2.C: New test.
* g++.dg/parse/addr-space.C: New test.
* g++.dg/parse/addr-space1.C: New test.
* g++.dg/parse/addr-space2.C: New test.
* g++.dg/parse/template/spec-addr-space.C: New test.
* g++.dg/ext/addr-space-decl.C: New test.
* g++.dg/ext/addr-space-ref.C: New test.
* g++.dg/ext/addr-space-ops.C: New test.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Sun Oct 9 16:02:22 2022 +0200
#
# On branch releases/gcc-12
# Your branch is ahead of 'origin/releases/gcc-12' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# modified: gcc/c-family/c-common.cc
# modified: gcc/c-family/c-common.h
# modified: gcc/c/c-decl.cc
# modified: gcc/c/c-typeck.cc
# modified: gcc/cp/cp-tree.h
# modified: gcc/cp/decl.cc
# modified: gcc/cp/mangle.cc
# modified: gcc/cp/parser.cc
# modified: gcc/cp/pt.cc
# modified: gcc/cp/tree.cc
# modified: gcc/cp/typeck.cc
# modified: gcc/doc/extend.texi
# new file: gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
# new file: gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
# new file: gcc/testsuite/g++.dg/ext/addr-space-decl.C
# new file: gcc/testsuite/g++.dg/ext/addr-space-ops.C
# new file: gcc/testsuite/g++.dg/ext/addr-space-ref.C
# new file: gcc/testsuite/g++.dg/parse/addr-space.C
# new file: gcc/testsuite/g++.dg/parse/addr-space1.C
# new file: gcc/testsuite/g++.dg/parse/addr-space2.C
# new file: gcc/testsuite/g++.dg/template/spec-addr-space.C
# modified: gcc/tree.h
#
# Untracked files:
# Makefile
# addr-space-decl.C
# addr-space-decl.s
# addr-space-ops.C
# addr-space-ref.C
# addr-space-template.C
# addr-space-template.s
# addr-space-template2.C
# addr-space-template2.C.006t.gimple
# addr-space-template2.s
# addr-space-traits.C
# addr-space4.C
# addr-space4.C.006t.gimple
# addr-space4.s
# build-x86_64-pc-linux-gnu/
# compare
# host-x86_64-pc-linux-gnu/
# mangle-addr-space1.s
# prev-x86_64-pc-linux-gnu/
# rename
# stage1-x86_64-pc-linux-gnu/
# stage_current
# stage_final
# stage_last
# x86_64-pc-linux-gnu/
#
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index bb0544eeaea..ff1146ecc25 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
return IDENTIFIER_POINTER (ridpointers [rid]);
}
+/* Return true if between two named address spaces, whether there is a superset
+ named address space that encompasses both address spaces. If there is a
+ superset, return which address space is the superset. */
+
+bool
+addr_space_superset (addr_space_t as1, addr_space_t as2,
+ addr_space_t * common)
+{
+ if (as1 == as2)
+ {
+ *common = as1;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as1, as2))
+ {
+ *common = as2;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as2, as1))
+ {
+ *common = as1;
+ return true;
+ }
+ else
+ return false;
+}
+
/* Push current bindings for the function name VAR_DECLS. */
void
@@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ TREE_LANG_FLAG_0 (id) = 1;
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 52a85bfb783..d36f9e4975b 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
+extern const char *c_addr_space_name (addr_space_t as);
+extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
extern bool names_builtin_p (const char *);
@@ -952,6 +951,7 @@ extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern FILE *get_dump_info (int, dump_flags_t *);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index c701f07befe..e1bb4f1cf37 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
-}
-
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index b271af9bedb..c4b01368534 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
return type;
}
-/* Return true if between two named address spaces, whether there is a superset
- named address space that encompasses both address spaces. If there is a
- superset, return which address space is the superset. */
-
-static bool
-addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
-{
- if (as1 == as2)
- {
- *common = as1;
- return true;
- }
- else if (targetm.addr_space.subset_p (as1, as2))
- {
- *common = as2;
- return true;
- }
- else if (targetm.addr_space.subset_p (as2, as1))
- {
- *common = as1;
- return true;
- }
- else
- return false;
-}
-
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 72f4398a8f9..82a6d72f5df 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6206,6 +6206,7 @@ enum cp_decl_spec {
ds_const,
ds_volatile,
ds_restrict,
+ ds_addr_space,
ds_inline,
ds_virtual,
ds_explicit,
@@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
cp_storage_class storage_class;
/* For the __intN declspec, this stores the index into the int_n_* arrays. */
int int_n_idx;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
/* True iff TYPE_SPEC defines a class or enum. */
BOOL_BITFIELD type_definition_p : 1;
/* True iff multiple types were (erroneously) specified for this
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 9f78c500a15..fa5a1ddb21d 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
return type_quals;
}
@@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
error_at (declspecs->locations[ds_restrict],
"%<__restrict%> can only be specified for objects and "
"functions");
+ else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ error_at (declspecs->locations[ds_addr_space],
+ "address space can only be specified for objects and "
+ "functions");
else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
error_at (declspecs->locations[ds_thread],
"%<__thread%> can only be specified for objects "
@@ -14572,6 +14578,57 @@ grokdeclarator (const cp_declarator *declarator,
if (!processing_template_decl)
cp_apply_type_quals_to_decl (type_quals, decl);
+ addr_space_t address_space = declspecs->address_space;
+ if (!ADDR_SPACE_GENERIC_P (address_space) && !INDIRECT_TYPE_P (type))
+ {
+ if (decl_context == NORMAL)
+ {
+ switch (storage_class)
+ {
+ case sc_auto:
+ error ("%qs combined with C++98 %<auto%> qualifier for %qs",
+ c_addr_space_name (address_space), name);
+ break;
+ case sc_register:
+ error ("%qs combined with %<register%> qualifier for %qs",
+ c_addr_space_name (address_space), name);
+ break;
+ case sc_none:
+ if (! toplevel_bindings_p ())
+ error ("%qs specified for auto variable %qs",
+ c_addr_space_name (address_space), name);
+ break;
+ case sc_mutable:
+ error ("%qs combined with %<mutable%> qualifier for %qs",
+ c_addr_space_name (address_space), name);
+ break;
+ case sc_static:
+ case sc_extern:
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
+ {
+ if (name)
+ error ("%qs specified for parameter %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for unnamed parameter",
+ c_addr_space_name (address_space));
+ }
+ else if (decl_context == FIELD)
+ {
+ if (name)
+ error ("%qs specified for structure field %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for structure field",
+ c_addr_space_name (address_space));
+ }
+ }
+
return decl;
}
}
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index eb53e0ebeb4..16cb2ff4332 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
array. */
cp_cv_quals quals = TYPE_QUALS (type);
+ if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
+ {
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 763df6f479b..f0169d83ab0 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
postfix_expression = error_mark_node;
break;
}
+ if (type != error_mark_node
+ && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
+ && current_function_decl)
+ {
+ error
+ ("compound literal qualified by address-space qualifier");
+ type = error_mark_node;
+ }
/* Form the representation of the compound-literal. */
postfix_expression
= finish_compound_literal (type, initializer,
@@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+
+ if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ }
+
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
GNU Extension:
cv-qualifier:
+ address-space-qualifier
__restrict__
Returns a bitmask representing the cv-qualifiers. */
@@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
+ if (RID_FIRST_ADDR_SPACE <= token->keyword &&
+ token->keyword <= RID_LAST_ADDR_SPACE)
+ cv_qualifier =
+ ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
+
if (!cv_qualifier)
break;
@@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
decl_specs->locations[ds] = location;
if (ds == ds_thread)
decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ else if (ds == ds_addr_space)
+ decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
}
else
{
@@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
error_at (&richloc, "duplicate %qD", token->u.value);
}
}
+ else if (ds == ds_addr_space)
+ {
+ addr_space_t as1 = decl_specs->address_space;
+ addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
+
+ gcc_rich_location richloc (location);
+ richloc.add_fixit_remove ();
+ if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
+ && as1 != as2)
+ error_at (&richloc,
+ "conflicting named address spaces (%s vs %s)",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
+ error_at (&richloc,
+ "duplicate named address space %s",
+ c_addr_space_name (as1));
+
+ decl_specs->address_space = as2;
+ }
else
{
static const char *const decl_spec_names[] = {
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index bef31416fb7..c9d1c01f4ce 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
- int arg_quals = cp_type_quals (arg);
- int parm_quals = cp_type_quals (parm);
+ int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
+
+ /* Try to unify ARG's address space into PARM's address space.
+ If PARM does not have any address space qualifiers (ie., as_parm is 0),
+ there are no constraints on address spaces for this type. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
+ addr_space_t as_common;
+ addr_space_superset (as_arg, as_parm, &as_common);
+
+ if (!(as_parm == as_common || as_parm == 0))
+ return 0;
if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
@@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
arg, parm))
return unify_cv_qual_mismatch (explain_p, parm, arg);
+ int arg_cv_quals = cp_type_quals (arg);
+ int parm_cv_quals = cp_type_quals (parm);
+
+ /* If PARM does not contain any address space constraints it can
+ fully match the address space of ARG. However, if PARM contains an
+ address space constraint, it becomes the upper bound. That is,
+ AS_ARG may be promoted to AS_PARM but not the converse. If we
+ ended up here, it means that `check_cv_quals_for_unify' succeeded
+ and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
+ AS_PARM. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
+ addr_space_t as_common = as_parm ? 0 : as_arg;
+
/* Consider the case where ARG is `const volatile int' and
PARM is `const T'. Then, T should be `volatile int'. */
- arg = cp_build_qualified_type_real
- (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
+ int unified_cv =
+ CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
+ | ENCODE_QUAL_ADDR_SPACE (as_common);
+ arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
if (arg == error_mark_node)
return unify_invalid (explain_p);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 3b37567cbd7..5e14ac837fc 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index ceb80d9744f..7d810c6a12c 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
else
return error_mark_node;
}
+ /* If possible merge the address space into the superset of the address
+ spaces of t1 and t2, or raise an error. */
+ addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
+ addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as_t1, as_t2, &as_common))
+ {
+ as_common = as_t1;
+ error_at (location,
+ "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ }
result_type
= cp_build_qualified_type (void_type_node,
- (cp_type_quals (TREE_TYPE (t1))
- | cp_type_quals (TREE_TYPE (t2))));
+ (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
+ | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
+ | ENCODE_QUAL_ADDR_SPACE (as_common)));
result_type = build_pointer_type (result_type);
/* Merge the attributes. */
attributes = (*targetm.merge_type_attributes) (t1, t2);
@@ -1579,7 +1595,9 @@ comptypes (tree t1, tree t2, int strict)
}
/* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
- top-level qualifiers. */
+ top-level qualifiers, except for named address spaces. If the pointers point
+ to different named addresses spaces, then we must determine if one address
+ space is a subset of the other. */
bool
same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
@@ -1589,6 +1607,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
if (type1 == type2)
return true;
+ addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
+ addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
+ addr_space_t as_common;
+
+ /* Fail if pointers point to incompatible address spaces. */
+ if (!addr_space_superset (as_type1, as_type2, &as_common))
+ return false;
+
type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
return same_type_p (type1, type2);
@@ -6374,10 +6400,32 @@ static tree
pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
tsubst_flags_t complain, tree *instrument_expr)
{
- tree result, inttype;
tree restype = ptrdiff_type_node;
+ tree result, inttype;
+
+ addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
+ addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
tree target_type = TREE_TYPE (ptrtype);
+ /* If the operands point into different address spaces, we need to
+ explicitly convert them to pointers into the common address space
+ before we can subtract the numerical address values. */
+ if (as0 != as1)
+ {
+ addr_space_t as_common;
+ tree common_type;
+
+ /* Determine the common superset address space. This is guaranteed
+ to exist because the caller verified that comp_target_types
+ returned non-zero. */
+ if (!addr_space_superset (as0, as1, &as_common))
+ gcc_unreachable ();
+
+ common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
+ op0 = convert (common_type, op0);
+ op1 = convert (common_type, op1);
+ }
+
if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
return error_mark_node;
@@ -10805,6 +10853,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
to_more_cv_qualified = true;
}
+ /* Warn about conversions between pointers to disjoint
+ address spaces. */
+ if (TREE_CODE (from) == POINTER_TYPE
+ && TREE_CODE (to) == POINTER_TYPE)
+ {
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common))
+ return false;
+ }
+
if (constp > 0)
constp &= TYPE_READONLY (to);
}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ac3db88566d..1e0d436c02c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
@section Named Address Spaces
@cindex Named Address Spaces
-As an extension, GNU C supports named address spaces as
+As an extension, GNU C and GNU C++ support named address spaces as
defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
address spaces in GCC will evolve as the draft technical report
changes. Calling conventions for any target might also change. At
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
new file mode 100644
index 00000000000..c01f8d6054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do run { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
+
+int f (int volatile __seg_fs *a)
+{
+ return *a;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
new file mode 100644
index 00000000000..862bbbdcdf2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do run { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
+
+template <class T>
+int f (T *p) { return *p; }
+int g (__seg_fs int *p) { return *p; }
+__seg_fs int *a;
+int main() { f(a); }
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
new file mode 100644
index 00000000000..c04d2f497da
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+__seg_fs char a, b, c;
+__seg_fs const int *p;
+static /* give internal linkage to the following anonymous struct */
+__seg_fs struct { int a; char b; } * __seg_gs q;
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
new file mode 100644
index 00000000000..86c02d1e7f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+int __seg_fs * fs1;
+int __seg_fs * fs2;
+float __seg_gs * gs1;
+float __seg_gs * gs2;
+
+int
+main ()
+{
+ fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
+ fs1 - fs2;
+ fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
+ fs1 == fs2;
+ fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
+ fs1 = fs2;
+ fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
+ fs1 > fs2;
+ fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
new file mode 100644
index 00000000000..12d7975e560
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
@@ -0,0 +1,31 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-prune-output "does not allow .register. storage class specifier" }
+int __seg_fs * outer_b;
+
+struct s {
+ __seg_fs int * ok;
+ __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
+};
+
+int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
+
+namespace ns_a
+{
+ int __seg_fs * inner_b;
+
+ template<typename T>
+ int f (T &a) { return a; }
+ int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
+ int h (__seg_fs int *a) { return *a; }
+}
+
+int
+main ()
+{
+ int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
+ static __seg_gs int static_gs;
+ __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
+ __seg_fs int *pa = outer_b;
+ __seg_fs int& ra = *ns_a::inner_b;
+ return ns_a::f(ra) + ns_a::f(*pa);
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
new file mode 100644
index 00000000000..ebb6316054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
new file mode 100644
index 00000000000..2e8ee32a885
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-std=gnu++98" }
+
+int
+main ()
+{
+ struct foo {int a; char b[2];} structure;
+ structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
new file mode 100644
index 00000000000..5b2c0f28078
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
new file mode 100644
index 00000000000..ae9f4de0e1f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+template <class T>
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
+ // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
+__seg_fs int *a;
+int main() { f(a); } // { dg-error "no matching" }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
diff --git a/gcc/tree.h b/gcc/tree.h
index 8844471e9a5..b7da4c5141a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
/* Encode/decode the named memory support as part of the qualifier. If more
than 8 qualifiers are added, these macros need to be adjusted. */
-#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
+#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
#define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
/* Return all qualifiers except for the address space qualifiers. */
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 0:52 ` Paul Iannetta
@ 2022-10-13 5:46 ` Jakub Jelinek
2022-10-13 15:14 ` Paul Iannetta
2022-10-13 15:02 ` Jason Merrill
1 sibling, 1 reply; 86+ messages in thread
From: Jakub Jelinek @ 2022-10-13 5:46 UTC (permalink / raw)
To: Paul Iannetta; +Cc: Jason Merrill, gcc-patches
On Thu, Oct 13, 2022 at 02:52:59AM +0200, Paul Iannetta via Gcc-patches wrote:
> + if (type != error_mark_node
> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> + && current_function_decl)
> + {
> + error
> + ("compound literal qualified by address-space qualifier");
> + type = error_mark_node;
Can you please write this as:
error ("compound literal qualified by address-space "
"qualifier");
? That is how diagnostics that don't fit on one line are usually written.
> @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> break;
> }
>
> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
&& should never go at the end of line.
> + token->keyword <= RID_LAST_ADDR_SPACE)
> + cv_qualifier =
and similarly = (except for aggregate initializers).
> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
So:
if (RID_FIRST_ADDR_SPACE <= token->keyword
&& token->keyword <= RID_LAST_ADDR_SPACE)
cv_qualifier
= ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> + int unified_cv =
> + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> + | ENCODE_QUAL_ADDR_SPACE (as_common);
Similarly (but this time with ()s added to ensure correct formatting in
some editors).
int unified_cv
= (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
| ENCODE_QUAL_ADDR_SPACE (as_common));
> result_type
> = cp_build_qualified_type (void_type_node,
> - (cp_type_quals (TREE_TYPE (t1))
> - | cp_type_quals (TREE_TYPE (t2))));
> + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
> + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
The above 2 lines are way too long.
I'd suggest to use temporaries, say
int quals1 = cp_type_quals (TREE_TYPE (t1));
int quals2 = cp_type_quals (TREE_TYPE (t2));
and use those.
Jakub
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 0:52 ` Paul Iannetta
2022-10-13 5:46 ` Jakub Jelinek
@ 2022-10-13 15:02 ` Jason Merrill
2022-10-13 15:23 ` Paul Iannetta
1 sibling, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-13 15:02 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/12/22 20:52, Paul Iannetta wrote:
> On Tue, Oct 11, 2022 at 09:49:43PM -0400, Jason Merrill wrote:
>>
>> It surprises that this is the only place we complain about an object with an
>> address-space qualifier. Shouldn't we also complain about e.g. automatic
>> variables/parameters or non-static data members with address-space qualified
>> type?
>>
>
> Indeed, I was missing quite a few things here. Thanks.
> I used the draft as basis this time and imported from the C
> implementation the relevant parts. This time, the errors get properly
> emitted when an address space is unduly specified; and comparisons,
> assignments and comparisons are taken care of.
>
> There are quite a few things I would like to clarify concerning some
> implementation details.
> - A variable with automatic storage (which is neither a pointer nor
> a reference) cannot be qualified with an address space. I detect
> this by the combination of `sc_none' and `! toplevel_bindings_p ()',
> but I've also seen the use of `at_function_scope' at other places.
> And I'm unsure which one is appropriate here.
> This detection happens at the very end of grokdeclarator because I
> need to know that the type is a pointer, which is not know until
> very late in the function.
At that point you have the decl, and you can ask directly what its
storage duration is, perhaps using decl_storage_duration.
But why do you need to know whether the type is a pointer? The
attribute applies to the target type of the pointer, not the pointer
type. I think the problem is that you're looking at declspecs when you
ought to be looking at type_quals.
> - I'm having some trouble deciding whether I include those three
> stub programs as tests, they all compile fine and clang accepts
> them as well.
Why not?
> Ex1:
> ```
> int __seg_fs * fs1;
> int __seg_gs * gs1;
>
> template<typename T> struct strip;
> template<typename T> struct strip<__seg_fs T *> { typedef T type; };
> template<typename T> struct strip<__seg_gs T *> { typedef T type; };
>
> int
> main ()
> {
> *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
> return 0;
> }
> ```
>
> Ex2:
> ```
> int __seg_fs * fs1;
> int __seg_fs * fs2;
>
> template<typename T, typename U> auto f (T __seg_fs * a, U __seg_gs * b) { return a; }
> template<typename T, typename U> auto f (T __seg_gs * a, U __seg_fs * b) { return a; }
>
> int
> main ()
> {
> f (fs1, gs1);
> f (gs1, fs1);
> return 0;
> }
> ```
>
> Ex3:
> ```
> int __seg_fs * fs1;
> int __seg_gs * gs1;
>
> template<typename T, typename U>
> auto f (T __seg_fs * a, U __seg_gs * b)
> {
> return *(T *) a == *(U *) b;
> }
>
> int
> main ()
> {
> return f (fs1, gs1);
> }
> ```
>
>
> Add support for custom address spaces in C++
>
> gcc/
> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>
> gcc/c/
> * c-decl.cc: Remove c_register_addr_space.
>
> gcc/c-family/
> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> * c-common.h: Remove the FIXME.
> (addr_space_superset): New declaration.
>
> gcc/cp/
> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> (struct cp_decl_specifier_seq): Likewise.
> * decl.cc (get_type_quals): Likewise.
> (check_tag_decl): Likewise.
> (grokdeclarator): Likewise.
> * parser.cc (cp_parser_type_specifier): Likewise.
> (cp_parser_cv_qualifier_seq_opt): Likewise.
> (cp_parser_postfix_expression): Likewise.
> (cp_parser_type_specifier): Likewise.
> (set_and_check_decl_spec_loc): Likewise.
> * typeck.cc (composite_pointer_type): Likewise
> (comp_ptr_ttypes_real): Likewise.
> (same_type_ignoring_top_level_qualifiers_p): Likewise.
> * pt.cc (check_cv_quals_for_unify): Likewise.
> (unify): Likewise.
> * tree.cc: Remove c_register_addr_space stub.
> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> using the extended qualifier notation.
>
> gcc/doc
> * extend.texi (Named Address Spaces): add a mention about C++
> support.
>
> gcc/testsuite/
> * g++.dg/abi/mangle-addr-space1.C: New test.
> * g++.dg/abi/mangle-addr-space2.C: New test.
> * g++.dg/parse/addr-space.C: New test.
> * g++.dg/parse/addr-space1.C: New test.
> * g++.dg/parse/addr-space2.C: New test.
> * g++.dg/parse/template/spec-addr-space.C: New test.
> * g++.dg/ext/addr-space-decl.C: New test.
> * g++.dg/ext/addr-space-ref.C: New test.
> * g++.dg/ext/addr-space-ops.C: New test.
>
> # Please enter the commit message for your changes. Lines starting
> # with '#' will be ignored, and an empty message aborts the commit.
> #
> # Date: Sun Oct 9 16:02:22 2022 +0200
> #
> # On branch releases/gcc-12
> # Your branch is ahead of 'origin/releases/gcc-12' by 2 commits.
> # (use "git push" to publish your local commits)
> #
> # Changes to be committed:
> # modified: gcc/c-family/c-common.cc
> # modified: gcc/c-family/c-common.h
> # modified: gcc/c/c-decl.cc
> # modified: gcc/c/c-typeck.cc
> # modified: gcc/cp/cp-tree.h
> # modified: gcc/cp/decl.cc
> # modified: gcc/cp/mangle.cc
> # modified: gcc/cp/parser.cc
> # modified: gcc/cp/pt.cc
> # modified: gcc/cp/tree.cc
> # modified: gcc/cp/typeck.cc
> # modified: gcc/doc/extend.texi
> # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> # new file: gcc/testsuite/g++.dg/ext/addr-space-decl.C
> # new file: gcc/testsuite/g++.dg/ext/addr-space-ops.C
> # new file: gcc/testsuite/g++.dg/ext/addr-space-ref.C
> # new file: gcc/testsuite/g++.dg/parse/addr-space.C
> # new file: gcc/testsuite/g++.dg/parse/addr-space1.C
> # new file: gcc/testsuite/g++.dg/parse/addr-space2.C
> # new file: gcc/testsuite/g++.dg/template/spec-addr-space.C
> # modified: gcc/tree.h
> #
> # Untracked files:
> # Makefile
> # addr-space-decl.C
> # addr-space-decl.s
> # addr-space-ops.C
> # addr-space-ref.C
> # addr-space-template.C
> # addr-space-template.s
> # addr-space-template2.C
> # addr-space-template2.C.006t.gimple
> # addr-space-template2.s
> # addr-space-traits.C
> # addr-space4.C
> # addr-space4.C.006t.gimple
> # addr-space4.s
> # build-x86_64-pc-linux-gnu/
> # compare
> # host-x86_64-pc-linux-gnu/
> # mangle-addr-space1.s
> # prev-x86_64-pc-linux-gnu/
> # rename
> # stage1-x86_64-pc-linux-gnu/
> # stage_current
> # stage_final
> # stage_last
> # x86_64-pc-linux-gnu/
> #
> # ------------------------ >8 ------------------------
> # Do not modify or remove the line above.
> # Everything below it will be ignored.
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index bb0544eeaea..ff1146ecc25 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
> return IDENTIFIER_POINTER (ridpointers [rid]);
> }
>
> +/* Return true if between two named address spaces, whether there is a superset
> + named address space that encompasses both address spaces. If there is a
> + superset, return which address space is the superset. */
> +
> +bool
> +addr_space_superset (addr_space_t as1, addr_space_t as2,
> + addr_space_t * common)
> +{
> + if (as1 == as2)
> + {
> + *common = as1;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as1, as2))
> + {
> + *common = as2;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as2, as1))
> + {
> + *common = as1;
> + return true;
> + }
> + else
> + return false;
> +}
> +
> /* Push current bindings for the function name VAR_DECLS. */
>
> void
> @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> return build_nonstandard_integer_type (width, unsignedp);
> }
>
> +/* Register reserved keyword WORD as qualifier for address space AS. */
> +
> +void
> +c_register_addr_space (const char *word, addr_space_t as)
> +{
> + int rid = RID_FIRST_ADDR_SPACE + as;
> + tree id;
> +
> + /* Address space qualifiers are only supported
> + in C with GNU extensions enabled. */
> + if (c_dialect_objc () || flag_no_asm)
> + return;
> +
> + id = get_identifier (word);
> + C_SET_RID_CODE (id, rid);
> + TREE_LANG_FLAG_0 (id) = 1;
> + ridpointers[rid] = id;
> +}
> +
> /* The C version of the register_builtin_type langhook. */
>
> void
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 52a85bfb783..d36f9e4975b 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.cc and cp/tree.cc. FIXME. */
> -extern void c_register_addr_space (const char *str, addr_space_t as);
> -
> /* In c-common.cc. */
> extern bool in_late_binary_op;
> extern const char *c_addr_space_name (addr_space_t as);
> +extern const char *c_addr_space_name (addr_space_t as);
> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> extern tree identifier_global_value (tree);
> extern tree identifier_global_tag (tree);
> extern bool names_builtin_p (const char *);
> @@ -952,6 +951,7 @@ extern void c_common_finish (void);
> extern void c_common_parse_file (void);
> extern FILE *get_dump_info (int, dump_flags_t *);
> extern alias_set_type c_common_get_alias_set (tree);
> +extern void c_register_addr_space (const char *, addr_space_t);
> extern void c_register_builtin_type (tree, const char*);
> extern bool c_promoting_integer_type_p (const_tree);
> extern bool self_promoting_args_p (const_tree);
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index c701f07befe..e1bb4f1cf37 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
> ext_block = NULL;
> }
>
> -/* Register reserved keyword WORD as qualifier for address space AS. */
> -
> -void
> -c_register_addr_space (const char *word, addr_space_t as)
> -{
> - int rid = RID_FIRST_ADDR_SPACE + as;
> - tree id;
> -
> - /* Address space qualifiers are only supported
> - in C with GNU extensions enabled. */
> - if (c_dialect_objc () || flag_no_asm)
> - return;
> -
> - id = get_identifier (word);
> - C_SET_RID_CODE (id, rid);
> - C_IS_RESERVED_WORD (id) = 1;
> - ridpointers [rid] = id;
> -}
> -
> /* Return identifier to look up for omp declare reduction. */
>
> tree
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index b271af9bedb..c4b01368534 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
> return type;
> }
>
> -/* Return true if between two named address spaces, whether there is a superset
> - named address space that encompasses both address spaces. If there is a
> - superset, return which address space is the superset. */
> -
> -static bool
> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> -{
> - if (as1 == as2)
> - {
> - *common = as1;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as1, as2))
> - {
> - *common = as2;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as2, as1))
> - {
> - *common = as1;
> - return true;
> - }
> - else
> - return false;
> -}
> -
> /* Return a variant of TYPE which has all the type qualifiers of LIKE
> as well as those of TYPE. */
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 72f4398a8f9..82a6d72f5df 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
> ds_const,
> ds_volatile,
> ds_restrict,
> + ds_addr_space,
> ds_inline,
> ds_virtual,
> ds_explicit,
> @@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
> cp_storage_class storage_class;
> /* For the __intN declspec, this stores the index into the int_n_* arrays. */
> int int_n_idx;
> + /* The address space that the declaration belongs to. */
> + addr_space_t address_space;
> /* True iff TYPE_SPEC defines a class or enum. */
> BOOL_BITFIELD type_definition_p : 1;
> /* True iff multiple types were (erroneously) specified for this
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 9f78c500a15..fa5a1ddb21d 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> type_quals |= TYPE_QUAL_VOLATILE;
> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> type_quals |= TYPE_QUAL_RESTRICT;
> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>
> return type_quals;
> }
> @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> error_at (declspecs->locations[ds_restrict],
> "%<__restrict%> can only be specified for objects and "
> "functions");
> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + error_at (declspecs->locations[ds_addr_space],
> + "address space can only be specified for objects and "
> + "functions");
> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> error_at (declspecs->locations[ds_thread],
> "%<__thread%> can only be specified for objects "
> @@ -14572,6 +14578,57 @@ grokdeclarator (const cp_declarator *declarator,
> if (!processing_template_decl)
> cp_apply_type_quals_to_decl (type_quals, decl);
>
> + addr_space_t address_space = declspecs->address_space;
> + if (!ADDR_SPACE_GENERIC_P (address_space) && !INDIRECT_TYPE_P (type))
> + {
> + if (decl_context == NORMAL)
> + {
> + switch (storage_class)
> + {
> + case sc_auto:
> + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
> + c_addr_space_name (address_space), name);
> + break;
> + case sc_register:
> + error ("%qs combined with %<register%> qualifier for %qs",
> + c_addr_space_name (address_space), name);
> + break;
> + case sc_none:
> + if (! toplevel_bindings_p ())
> + error ("%qs specified for auto variable %qs",
> + c_addr_space_name (address_space), name);
> + break;
> + case sc_mutable:
> + error ("%qs combined with %<mutable%> qualifier for %qs",
> + c_addr_space_name (address_space), name);
> + break;
> + case sc_static:
> + case sc_extern:
> + break;
> + default:
> + gcc_unreachable ();
> + }
> + }
> + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
> + {
> + if (name)
> + error ("%qs specified for parameter %qs",
> + c_addr_space_name (address_space), name);
> + else
> + error ("%qs specified for unnamed parameter",
> + c_addr_space_name (address_space));
> + }
> + else if (decl_context == FIELD)
> + {
> + if (name)
> + error ("%qs specified for structure field %qs",
> + c_addr_space_name (address_space), name);
> + else
> + error ("%qs specified for structure field",
> + c_addr_space_name (address_space));
> + }
> + }
> +
> return decl;
> }
> }
> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> index eb53e0ebeb4..16cb2ff4332 100644
> --- a/gcc/cp/mangle.cc
> +++ b/gcc/cp/mangle.cc
> @@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
> array. */
> cp_cv_quals quals = TYPE_QUALS (type);
>
> + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
> + {
> + const char *as_name = c_addr_space_name (as);
> + write_char ('U');
> + write_unsigned_number (strlen (as_name));
> + write_string (as_name);
> + ++num_qualifiers;
> + }
> if (quals & TYPE_QUAL_RESTRICT)
> {
> write_char ('r');
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 763df6f479b..f0169d83ab0 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> postfix_expression = error_mark_node;
> break;
> }
> + if (type != error_mark_node
> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> + && current_function_decl)
> + {
> + error
> + ("compound literal qualified by address-space qualifier");
> + type = error_mark_node;
> + }
> /* Form the representation of the compound-literal. */
> postfix_expression
> = finish_compound_literal (type, initializer,
> @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
> break;
> }
>
> +
> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> + {
> + ds = ds_addr_space;
> + if (is_cv_qualifier)
> + *is_cv_qualifier = true;
> + }
> +
> +
> /* Handle simple keywords. */
> if (ds != ds_last)
> {
> @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> GNU Extension:
>
> cv-qualifier:
> + address-space-qualifier
> __restrict__
>
> Returns a bitmask representing the cv-qualifiers. */
> @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> break;
> }
>
> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
> + token->keyword <= RID_LAST_ADDR_SPACE)
> + cv_qualifier =
> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> +
> if (!cv_qualifier)
> break;
>
> @@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> decl_specs->locations[ds] = location;
> if (ds == ds_thread)
> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> + else if (ds == ds_addr_space)
> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> }
> else
> {
> @@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> error_at (&richloc, "duplicate %qD", token->u.value);
> }
> }
> + else if (ds == ds_addr_space)
> + {
> + addr_space_t as1 = decl_specs->address_space;
> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> +
> + gcc_rich_location richloc (location);
> + richloc.add_fixit_remove ();
> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> + && as1 != as2)
> + error_at (&richloc,
> + "conflicting named address spaces (%s vs %s)",
> + c_addr_space_name (as1), c_addr_space_name (as2));
> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> + error_at (&richloc,
> + "duplicate named address space %s",
> + c_addr_space_name (as1));
> +
> + decl_specs->address_space = as2;
> + }
> else
> {
> static const char *const decl_spec_names[] = {
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index bef31416fb7..c9d1c01f4ce 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
> static int
> check_cv_quals_for_unify (int strict, tree arg, tree parm)
> {
> - int arg_quals = cp_type_quals (arg);
> - int parm_quals = cp_type_quals (parm);
> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> +
> + /* Try to unify ARG's address space into PARM's address space.
> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> + there are no constraints on address spaces for this type. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> + addr_space_t as_common;
> + addr_space_superset (as_arg, as_parm, &as_common);
> +
> + if (!(as_parm == as_common || as_parm == 0))
> + return 0;
>
> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> @@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> arg, parm))
> return unify_cv_qual_mismatch (explain_p, parm, arg);
>
> + int arg_cv_quals = cp_type_quals (arg);
> + int parm_cv_quals = cp_type_quals (parm);
> +
> + /* If PARM does not contain any address space constraints it can
> + fully match the address space of ARG. However, if PARM contains an
> + address space constraint, it becomes the upper bound. That is,
> + AS_ARG may be promoted to AS_PARM but not the converse. If we
> + ended up here, it means that `check_cv_quals_for_unify' succeeded
> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> + AS_PARM. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> + addr_space_t as_common = as_parm ? 0 : as_arg;
> +
> /* Consider the case where ARG is `const volatile int' and
> PARM is `const T'. Then, T should be `volatile int'. */
> - arg = cp_build_qualified_type_real
> - (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
> + int unified_cv =
> + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> + | ENCODE_QUAL_ADDR_SPACE (as_common);
> + arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
> if (arg == error_mark_node)
> return unify_invalid (explain_p);
>
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index 3b37567cbd7..5e14ac837fc 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
> DECL_CHAIN (t) = NULL_TREE;
> }
>
> -/* Stub for c-common. Please keep in sync with c-decl.cc.
> - FIXME: If address space support is target specific, then this
> - should be a C target hook. But currently this is not possible,
> - because this function is called via REGISTER_TARGET_PRAGMAS. */
> -void
> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> -{
> -}
> -
> /* Return the number of operands in T that we care about for things like
> mangling. */
>
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index ceb80d9744f..7d810c6a12c 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
> else
> return error_mark_node;
> }
> + /* If possible merge the address space into the superset of the address
> + spaces of t1 and t2, or raise an error. */
> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> + addr_space_t as_common;
> +
> + /* If the two named address spaces are different, determine the common
> + superset address space. If there isn't one, raise an error. */
> + if (!addr_space_superset (as_t1, as_t2, &as_common))
> + {
> + as_common = as_t1;
> + error_at (location,
> + "%qT and %qT are in disjoint named address spaces",
> + t1, t2);
> + }
> result_type
> = cp_build_qualified_type (void_type_node,
> - (cp_type_quals (TREE_TYPE (t1))
> - | cp_type_quals (TREE_TYPE (t2))));
> + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
> + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> result_type = build_pointer_type (result_type);
> /* Merge the attributes. */
> attributes = (*targetm.merge_type_attributes) (t1, t2);
> @@ -1579,7 +1595,9 @@ comptypes (tree t1, tree t2, int strict)
> }
>
> /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
> - top-level qualifiers. */
> + top-level qualifiers, except for named address spaces. If the pointers point
> + to different named addresses spaces, then we must determine if one address
> + space is a subset of the other. */
>
> bool
> same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> @@ -1589,6 +1607,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> if (type1 == type2)
> return true;
>
> + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
> + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
> + addr_space_t as_common;
> +
> + /* Fail if pointers point to incompatible address spaces. */
> + if (!addr_space_superset (as_type1, as_type2, &as_common))
> + return false;
> +
> type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
> type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
> return same_type_p (type1, type2);
> @@ -6374,10 +6400,32 @@ static tree
> pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
> tsubst_flags_t complain, tree *instrument_expr)
> {
> - tree result, inttype;
> tree restype = ptrdiff_type_node;
> + tree result, inttype;
> +
> + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
> + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
> tree target_type = TREE_TYPE (ptrtype);
>
> + /* If the operands point into different address spaces, we need to
> + explicitly convert them to pointers into the common address space
> + before we can subtract the numerical address values. */
> + if (as0 != as1)
> + {
> + addr_space_t as_common;
> + tree common_type;
> +
> + /* Determine the common superset address space. This is guaranteed
> + to exist because the caller verified that comp_target_types
> + returned non-zero. */
> + if (!addr_space_superset (as0, as1, &as_common))
> + gcc_unreachable ();
> +
> + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
> + op0 = convert (common_type, op0);
> + op1 = convert (common_type, op1);
> + }
> +
> if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
> return error_mark_node;
>
> @@ -10805,6 +10853,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> to_more_cv_qualified = true;
> }
>
> + /* Warn about conversions between pointers to disjoint
> + address spaces. */
> + if (TREE_CODE (from) == POINTER_TYPE
> + && TREE_CODE (to) == POINTER_TYPE)
> + {
> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> + addr_space_t as_common;
> +
> + if (!addr_space_superset (as_to, as_from, &as_common))
> + return false;
> + }
> +
> if (constp > 0)
> constp &= TYPE_READONLY (to);
> }
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index ac3db88566d..1e0d436c02c 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> @section Named Address Spaces
> @cindex Named Address Spaces
>
> -As an extension, GNU C supports named address spaces as
> +As an extension, GNU C and GNU C++ support named address spaces as
> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> address spaces in GCC will evolve as the draft technical report
> changes. Calling conventions for any target might also change. At
> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> new file mode 100644
> index 00000000000..c01f8d6054a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> @@ -0,0 +1,10 @@
> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> +
> +int f (int volatile __seg_fs *a)
> +{
> + return *a;
> +}
> +
> +int main () {}
> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> new file mode 100644
> index 00000000000..862bbbdcdf2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> @@ -0,0 +1,9 @@
> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
> +
> +template <class T>
> +int f (T *p) { return *p; }
> +int g (__seg_fs int *p) { return *p; }
> +__seg_fs int *a;
> +int main() { f(a); }
> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> new file mode 100644
> index 00000000000..c04d2f497da
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> @@ -0,0 +1,5 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +__seg_fs char a, b, c;
> +__seg_fs const int *p;
> +static /* give internal linkage to the following anonymous struct */
> +__seg_fs struct { int a; char b; } * __seg_gs q;
> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> new file mode 100644
> index 00000000000..86c02d1e7f5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> @@ -0,0 +1,20 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +int __seg_fs * fs1;
> +int __seg_fs * fs2;
> +float __seg_gs * gs1;
> +float __seg_gs * gs2;
> +
> +int
> +main ()
> +{
> + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
> + fs1 - fs2;
> + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
> + fs1 == fs2;
> + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> + fs1 = fs2;
> + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
> + fs1 > fs2;
> + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> new file mode 100644
> index 00000000000..12d7975e560
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> @@ -0,0 +1,31 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +// { dg-prune-output "does not allow .register. storage class specifier" }
> +int __seg_fs * outer_b;
> +
> +struct s {
> + __seg_fs int * ok;
> + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
> +};
> +
> +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
> +
> +namespace ns_a
> +{
> + int __seg_fs * inner_b;
> +
> + template<typename T>
> + int f (T &a) { return a; }
> + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
> + int h (__seg_fs int *a) { return *a; }
> +}
> +
> +int
> +main ()
> +{
> + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
> + static __seg_gs int static_gs;
> + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
> + __seg_fs int *pa = outer_b;
> + __seg_fs int& ra = *ns_a::inner_b;
> + return ns_a::f(ra) + ns_a::f(*pa);
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> new file mode 100644
> index 00000000000..ebb6316054a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> @@ -0,0 +1,9 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> +
> +int
> +main ()
> +{
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> new file mode 100644
> index 00000000000..2e8ee32a885
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> @@ -0,0 +1,10 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-std=gnu++98" }
> +
> +int
> +main ()
> +{
> + struct foo {int a; char b[2];} structure;
> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> new file mode 100644
> index 00000000000..5b2c0f28078
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> @@ -0,0 +1,9 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> +
> +int
> +main ()
> +{
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> new file mode 100644
> index 00000000000..ae9f4de0e1f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> @@ -0,0 +1,8 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +template <class T>
> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> +__seg_fs int *a;
> +int main() { f(a); } // { dg-error "no matching" }
> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 8844471e9a5..b7da4c5141a 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
>
> /* Encode/decode the named memory support as part of the qualifier. If more
> than 8 qualifiers are added, these macros need to be adjusted. */
> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>
> /* Return all qualifiers except for the address space qualifiers. */
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 5:46 ` Jakub Jelinek
@ 2022-10-13 15:14 ` Paul Iannetta
0 siblings, 0 replies; 86+ messages in thread
From: Paul Iannetta @ 2022-10-13 15:14 UTC (permalink / raw)
To: Jakub Jelinek; +Cc: Jason Merrill, gcc-patches
On Thu, Oct 13, 2022 at 07:46:46AM +0200, Jakub Jelinek wrote:
> On Thu, Oct 13, 2022 at 02:52:59AM +0200, Paul Iannetta via Gcc-patches wrote:
> > + if (type != error_mark_node
> > + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> > + && current_function_decl)
> > + {
> > + error
> > + ("compound literal qualified by address-space qualifier");
> > + type = error_mark_node;
>
> Can you please write this as:
> error ("compound literal qualified by address-space "
> "qualifier");
> ? That is how diagnostics that don't fit on one line are usually written.
>
> > @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> > break;
> > }
> >
> > + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
>
> && should never go at the end of line.
>
> > + token->keyword <= RID_LAST_ADDR_SPACE)
> > + cv_qualifier =
>
> and similarly = (except for aggregate initializers).
>
> > + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>
> So:
>
> if (RID_FIRST_ADDR_SPACE <= token->keyword
> && token->keyword <= RID_LAST_ADDR_SPACE)
> cv_qualifier
> = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>
> > + int unified_cv =
> > + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> > + | ENCODE_QUAL_ADDR_SPACE (as_common);
>
> Similarly (but this time with ()s added to ensure correct formatting in
> some editors).
>
> int unified_cv
> = (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> | ENCODE_QUAL_ADDR_SPACE (as_common));
>
> > result_type
> > = cp_build_qualified_type (void_type_node,
> > - (cp_type_quals (TREE_TYPE (t1))
> > - | cp_type_quals (TREE_TYPE (t2))));
> > + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
> > + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
>
> The above 2 lines are way too long.
> I'd suggest to use temporaries, say
> int quals1 = cp_type_quals (TREE_TYPE (t1));
> int quals2 = cp_type_quals (TREE_TYPE (t2));
> and use those.
>
> Jakub
Thank you for the style review, I'll apply them in the next iteration.
Paul
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 15:02 ` Jason Merrill
@ 2022-10-13 15:23 ` Paul Iannetta
2022-10-13 15:47 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-10-13 15:23 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
> On 10/12/22 20:52, Paul Iannetta wrote:
> > On Tue, Oct 11, 2022 at 09:49:43PM -0400, Jason Merrill wrote:
> > >
> > > It surprises that this is the only place we complain about an object with an
> > > address-space qualifier. Shouldn't we also complain about e.g. automatic
> > > variables/parameters or non-static data members with address-space qualified
> > > type?
> > >
> >
> > Indeed, I was missing quite a few things here. Thanks.
> > I used the draft as basis this time and imported from the C
> > implementation the relevant parts. This time, the errors get properly
> > emitted when an address space is unduly specified; and comparisons,
> > assignments and comparisons are taken care of.
> >
> > There are quite a few things I would like to clarify concerning some
> > implementation details.
> > - A variable with automatic storage (which is neither a pointer nor
> > a reference) cannot be qualified with an address space. I detect
> > this by the combination of `sc_none' and `! toplevel_bindings_p ()',
> > but I've also seen the use of `at_function_scope' at other places.
> > And I'm unsure which one is appropriate here.
> > This detection happens at the very end of grokdeclarator because I
> > need to know that the type is a pointer, which is not know until
> > very late in the function.
>
> At that point you have the decl, and you can ask directly what its storage
> duration is, perhaps using decl_storage_duration.
>
> But why do you need to know whether the type is a pointer? The attribute
> applies to the target type of the pointer, not the pointer type. I think
> the problem is that you're looking at declspecs when you ought to be looking
> at type_quals.
>
I need to know that the base type is a pointer to reject invalid
declarations such as:
int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
because parameters and auto variables can have an address space
qualifier only if they are pointer or reference type, which I can't
tell only from type_quals.
> > - I'm having some trouble deciding whether I include those three
> > stub programs as tests, they all compile fine and clang accepts
> > them as well.
>
> Why not?
I thought they were pretty contrived, since it does not make much
sense to strip address space qualifiers, even though it does prove
that the implementation support those contrived but valid uses.
>
> > Ex1:
> > ```
> > int __seg_fs * fs1;
> > int __seg_gs * gs1;
> >
> > template<typename T> struct strip;
> > template<typename T> struct strip<__seg_fs T *> { typedef T type; };
> > template<typename T> struct strip<__seg_gs T *> { typedef T type; };
> >
> > int
> > main ()
> > {
> > *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
> > return 0;
> > }
> > ```
> >
> > Ex2:
> > ```
> > int __seg_fs * fs1;
> > int __seg_fs * fs2;
> >
> > template<typename T, typename U> auto f (T __seg_fs * a, U __seg_gs * b) { return a; }
> > template<typename T, typename U> auto f (T __seg_gs * a, U __seg_fs * b) { return a; }
> >
> > int
> > main ()
> > {
> > f (fs1, gs1);
> > f (gs1, fs1);
> > return 0;
> > }
> > ```
> >
> > Ex3:
> > ```
> > int __seg_fs * fs1;
> > int __seg_gs * gs1;
> >
> > template<typename T, typename U>
> > auto f (T __seg_fs * a, U __seg_gs * b)
> > {
> > return *(T *) a == *(U *) b;
> > }
> >
> > int
> > main ()
> > {
> > return f (fs1, gs1);
> > }
> > ```
> >
> >
> > Add support for custom address spaces in C++
> >
> > gcc/
> > * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
> >
> > gcc/c/
> > * c-decl.cc: Remove c_register_addr_space.
> >
> > gcc/c-family/
> > * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> > (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> > * c-common.h: Remove the FIXME.
> > (addr_space_superset): New declaration.
> >
> > gcc/cp/
> > * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> > (struct cp_decl_specifier_seq): Likewise.
> > * decl.cc (get_type_quals): Likewise.
> > (check_tag_decl): Likewise.
> > (grokdeclarator): Likewise.
> > * parser.cc (cp_parser_type_specifier): Likewise.
> > (cp_parser_cv_qualifier_seq_opt): Likewise.
> > (cp_parser_postfix_expression): Likewise.
> > (cp_parser_type_specifier): Likewise.
> > (set_and_check_decl_spec_loc): Likewise.
> > * typeck.cc (composite_pointer_type): Likewise
> > (comp_ptr_ttypes_real): Likewise.
> > (same_type_ignoring_top_level_qualifiers_p): Likewise.
> > * pt.cc (check_cv_quals_for_unify): Likewise.
> > (unify): Likewise.
> > * tree.cc: Remove c_register_addr_space stub.
> > * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> > using the extended qualifier notation.
> >
> > gcc/doc
> > * extend.texi (Named Address Spaces): add a mention about C++
> > support.
> >
> > gcc/testsuite/
> > * g++.dg/abi/mangle-addr-space1.C: New test.
> > * g++.dg/abi/mangle-addr-space2.C: New test.
> > * g++.dg/parse/addr-space.C: New test.
> > * g++.dg/parse/addr-space1.C: New test.
> > * g++.dg/parse/addr-space2.C: New test.
> > * g++.dg/parse/template/spec-addr-space.C: New test.
> > * g++.dg/ext/addr-space-decl.C: New test.
> > * g++.dg/ext/addr-space-ref.C: New test.
> > * g++.dg/ext/addr-space-ops.C: New test.
> >
> > # Please enter the commit message for your changes. Lines starting
> > # with '#' will be ignored, and an empty message aborts the commit.
> > #
> > # Date: Sun Oct 9 16:02:22 2022 +0200
> > #
> > # On branch releases/gcc-12
> > # Your branch is ahead of 'origin/releases/gcc-12' by 2 commits.
> > # (use "git push" to publish your local commits)
> > #
> > # Changes to be committed:
> > # modified: gcc/c-family/c-common.cc
> > # modified: gcc/c-family/c-common.h
> > # modified: gcc/c/c-decl.cc
> > # modified: gcc/c/c-typeck.cc
> > # modified: gcc/cp/cp-tree.h
> > # modified: gcc/cp/decl.cc
> > # modified: gcc/cp/mangle.cc
> > # modified: gcc/cp/parser.cc
> > # modified: gcc/cp/pt.cc
> > # modified: gcc/cp/tree.cc
> > # modified: gcc/cp/typeck.cc
> > # modified: gcc/doc/extend.texi
> > # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > # new file: gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > # new file: gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > # new file: gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > # new file: gcc/testsuite/g++.dg/parse/addr-space.C
> > # new file: gcc/testsuite/g++.dg/parse/addr-space1.C
> > # new file: gcc/testsuite/g++.dg/parse/addr-space2.C
> > # new file: gcc/testsuite/g++.dg/template/spec-addr-space.C
> > # modified: gcc/tree.h
> > #
> > # Untracked files:
> > # Makefile
> > # addr-space-decl.C
> > # addr-space-decl.s
> > # addr-space-ops.C
> > # addr-space-ref.C
> > # addr-space-template.C
> > # addr-space-template.s
> > # addr-space-template2.C
> > # addr-space-template2.C.006t.gimple
> > # addr-space-template2.s
> > # addr-space-traits.C
> > # addr-space4.C
> > # addr-space4.C.006t.gimple
> > # addr-space4.s
> > # build-x86_64-pc-linux-gnu/
> > # compare
> > # host-x86_64-pc-linux-gnu/
> > # mangle-addr-space1.s
> > # prev-x86_64-pc-linux-gnu/
> > # rename
> > # stage1-x86_64-pc-linux-gnu/
> > # stage_current
> > # stage_final
> > # stage_last
> > # x86_64-pc-linux-gnu/
> > #
> > # ------------------------ >8 ------------------------
> > # Do not modify or remove the line above.
> > # Everything below it will be ignored.
> > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> > index bb0544eeaea..ff1146ecc25 100644
> > --- a/gcc/c-family/c-common.cc
> > +++ b/gcc/c-family/c-common.cc
> > @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
> > return IDENTIFIER_POINTER (ridpointers [rid]);
> > }
> > +/* Return true if between two named address spaces, whether there is a superset
> > + named address space that encompasses both address spaces. If there is a
> > + superset, return which address space is the superset. */
> > +
> > +bool
> > +addr_space_superset (addr_space_t as1, addr_space_t as2,
> > + addr_space_t * common)
> > +{
> > + if (as1 == as2)
> > + {
> > + *common = as1;
> > + return true;
> > + }
> > + else if (targetm.addr_space.subset_p (as1, as2))
> > + {
> > + *common = as2;
> > + return true;
> > + }
> > + else if (targetm.addr_space.subset_p (as2, as1))
> > + {
> > + *common = as1;
> > + return true;
> > + }
> > + else
> > + return false;
> > +}
> > +
> > /* Push current bindings for the function name VAR_DECLS. */
> > void
> > @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> > return build_nonstandard_integer_type (width, unsignedp);
> > }
> > +/* Register reserved keyword WORD as qualifier for address space AS. */
> > +
> > +void
> > +c_register_addr_space (const char *word, addr_space_t as)
> > +{
> > + int rid = RID_FIRST_ADDR_SPACE + as;
> > + tree id;
> > +
> > + /* Address space qualifiers are only supported
> > + in C with GNU extensions enabled. */
> > + if (c_dialect_objc () || flag_no_asm)
> > + return;
> > +
> > + id = get_identifier (word);
> > + C_SET_RID_CODE (id, rid);
> > + TREE_LANG_FLAG_0 (id) = 1;
> > + ridpointers[rid] = id;
> > +}
> > +
> > /* The C version of the register_builtin_type langhook. */
> > void
> > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> > index 52a85bfb783..d36f9e4975b 100644
> > --- a/gcc/c-family/c-common.h
> > +++ b/gcc/c-family/c-common.h
> > @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
> > extern tree (*make_fname_decl) (location_t, tree, int);
> > -/* In c-decl.cc and cp/tree.cc. FIXME. */
> > -extern void c_register_addr_space (const char *str, addr_space_t as);
> > -
> > /* In c-common.cc. */
> > extern bool in_late_binary_op;
> > extern const char *c_addr_space_name (addr_space_t as);
> > +extern const char *c_addr_space_name (addr_space_t as);
> > +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> > extern tree identifier_global_value (tree);
> > extern tree identifier_global_tag (tree);
> > extern bool names_builtin_p (const char *);
> > @@ -952,6 +951,7 @@ extern void c_common_finish (void);
> > extern void c_common_parse_file (void);
> > extern FILE *get_dump_info (int, dump_flags_t *);
> > extern alias_set_type c_common_get_alias_set (tree);
> > +extern void c_register_addr_space (const char *, addr_space_t);
> > extern void c_register_builtin_type (tree, const char*);
> > extern bool c_promoting_integer_type_p (const_tree);
> > extern bool self_promoting_args_p (const_tree);
> > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> > index c701f07befe..e1bb4f1cf37 100644
> > --- a/gcc/c/c-decl.cc
> > +++ b/gcc/c/c-decl.cc
> > @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
> > ext_block = NULL;
> > }
> > -/* Register reserved keyword WORD as qualifier for address space AS. */
> > -
> > -void
> > -c_register_addr_space (const char *word, addr_space_t as)
> > -{
> > - int rid = RID_FIRST_ADDR_SPACE + as;
> > - tree id;
> > -
> > - /* Address space qualifiers are only supported
> > - in C with GNU extensions enabled. */
> > - if (c_dialect_objc () || flag_no_asm)
> > - return;
> > -
> > - id = get_identifier (word);
> > - C_SET_RID_CODE (id, rid);
> > - C_IS_RESERVED_WORD (id) = 1;
> > - ridpointers [rid] = id;
> > -}
> > -
> > /* Return identifier to look up for omp declare reduction. */
> > tree
> > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> > index b271af9bedb..c4b01368534 100644
> > --- a/gcc/c/c-typeck.cc
> > +++ b/gcc/c/c-typeck.cc
> > @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
> > return type;
> > }
> > -/* Return true if between two named address spaces, whether there is a superset
> > - named address space that encompasses both address spaces. If there is a
> > - superset, return which address space is the superset. */
> > -
> > -static bool
> > -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> > -{
> > - if (as1 == as2)
> > - {
> > - *common = as1;
> > - return true;
> > - }
> > - else if (targetm.addr_space.subset_p (as1, as2))
> > - {
> > - *common = as2;
> > - return true;
> > - }
> > - else if (targetm.addr_space.subset_p (as2, as1))
> > - {
> > - *common = as1;
> > - return true;
> > - }
> > - else
> > - return false;
> > -}
> > -
> > /* Return a variant of TYPE which has all the type qualifiers of LIKE
> > as well as those of TYPE. */
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 72f4398a8f9..82a6d72f5df 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
> > ds_const,
> > ds_volatile,
> > ds_restrict,
> > + ds_addr_space,
> > ds_inline,
> > ds_virtual,
> > ds_explicit,
> > @@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
> > cp_storage_class storage_class;
> > /* For the __intN declspec, this stores the index into the int_n_* arrays. */
> > int int_n_idx;
> > + /* The address space that the declaration belongs to. */
> > + addr_space_t address_space;
> > /* True iff TYPE_SPEC defines a class or enum. */
> > BOOL_BITFIELD type_definition_p : 1;
> > /* True iff multiple types were (erroneously) specified for this
> > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> > index 9f78c500a15..fa5a1ddb21d 100644
> > --- a/gcc/cp/decl.cc
> > +++ b/gcc/cp/decl.cc
> > @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> > type_quals |= TYPE_QUAL_VOLATILE;
> > if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> > type_quals |= TYPE_QUAL_RESTRICT;
> > + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
> > return type_quals;
> > }
> > @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> > error_at (declspecs->locations[ds_restrict],
> > "%<__restrict%> can only be specified for objects and "
> > "functions");
> > + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > + error_at (declspecs->locations[ds_addr_space],
> > + "address space can only be specified for objects and "
> > + "functions");
> > else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> > error_at (declspecs->locations[ds_thread],
> > "%<__thread%> can only be specified for objects "
> > @@ -14572,6 +14578,57 @@ grokdeclarator (const cp_declarator *declarator,
> > if (!processing_template_decl)
> > cp_apply_type_quals_to_decl (type_quals, decl);
> > + addr_space_t address_space = declspecs->address_space;
> > + if (!ADDR_SPACE_GENERIC_P (address_space) && !INDIRECT_TYPE_P (type))
> > + {
> > + if (decl_context == NORMAL)
> > + {
> > + switch (storage_class)
> > + {
> > + case sc_auto:
> > + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
> > + c_addr_space_name (address_space), name);
> > + break;
> > + case sc_register:
> > + error ("%qs combined with %<register%> qualifier for %qs",
> > + c_addr_space_name (address_space), name);
> > + break;
> > + case sc_none:
> > + if (! toplevel_bindings_p ())
> > + error ("%qs specified for auto variable %qs",
> > + c_addr_space_name (address_space), name);
> > + break;
> > + case sc_mutable:
> > + error ("%qs combined with %<mutable%> qualifier for %qs",
> > + c_addr_space_name (address_space), name);
> > + break;
> > + case sc_static:
> > + case sc_extern:
> > + break;
> > + default:
> > + gcc_unreachable ();
> > + }
> > + }
> > + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
> > + {
> > + if (name)
> > + error ("%qs specified for parameter %qs",
> > + c_addr_space_name (address_space), name);
> > + else
> > + error ("%qs specified for unnamed parameter",
> > + c_addr_space_name (address_space));
> > + }
> > + else if (decl_context == FIELD)
> > + {
> > + if (name)
> > + error ("%qs specified for structure field %qs",
> > + c_addr_space_name (address_space), name);
> > + else
> > + error ("%qs specified for structure field",
> > + c_addr_space_name (address_space));
> > + }
> > + }
> > +
> > return decl;
> > }
> > }
> > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> > index eb53e0ebeb4..16cb2ff4332 100644
> > --- a/gcc/cp/mangle.cc
> > +++ b/gcc/cp/mangle.cc
> > @@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
> > array. */
> > cp_cv_quals quals = TYPE_QUALS (type);
> > + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
> > + {
> > + const char *as_name = c_addr_space_name (as);
> > + write_char ('U');
> > + write_unsigned_number (strlen (as_name));
> > + write_string (as_name);
> > + ++num_qualifiers;
> > + }
> > if (quals & TYPE_QUAL_RESTRICT)
> > {
> > write_char ('r');
> > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > index 763df6f479b..f0169d83ab0 100644
> > --- a/gcc/cp/parser.cc
> > +++ b/gcc/cp/parser.cc
> > @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> > postfix_expression = error_mark_node;
> > break;
> > }
> > + if (type != error_mark_node
> > + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> > + && current_function_decl)
> > + {
> > + error
> > + ("compound literal qualified by address-space qualifier");
> > + type = error_mark_node;
> > + }
> > /* Form the representation of the compound-literal. */
> > postfix_expression
> > = finish_compound_literal (type, initializer,
> > @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
> > break;
> > }
> > +
> > + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> > + {
> > + ds = ds_addr_space;
> > + if (is_cv_qualifier)
> > + *is_cv_qualifier = true;
> > + }
> > +
> > +
> > /* Handle simple keywords. */
> > if (ds != ds_last)
> > {
> > @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> > GNU Extension:
> > cv-qualifier:
> > + address-space-qualifier
> > __restrict__
> > Returns a bitmask representing the cv-qualifiers. */
> > @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> > break;
> > }
> > + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
> > + token->keyword <= RID_LAST_ADDR_SPACE)
> > + cv_qualifier =
> > + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> > +
> > if (!cv_qualifier)
> > break;
> > @@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > decl_specs->locations[ds] = location;
> > if (ds == ds_thread)
> > decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> > + else if (ds == ds_addr_space)
> > + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> > }
> > else
> > {
> > @@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > error_at (&richloc, "duplicate %qD", token->u.value);
> > }
> > }
> > + else if (ds == ds_addr_space)
> > + {
> > + addr_space_t as1 = decl_specs->address_space;
> > + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> > +
> > + gcc_rich_location richloc (location);
> > + richloc.add_fixit_remove ();
> > + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> > + && as1 != as2)
> > + error_at (&richloc,
> > + "conflicting named address spaces (%s vs %s)",
> > + c_addr_space_name (as1), c_addr_space_name (as2));
> > + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> > + error_at (&richloc,
> > + "duplicate named address space %s",
> > + c_addr_space_name (as1));
> > +
> > + decl_specs->address_space = as2;
> > + }
> > else
> > {
> > static const char *const decl_spec_names[] = {
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index bef31416fb7..c9d1c01f4ce 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
> > static int
> > check_cv_quals_for_unify (int strict, tree arg, tree parm)
> > {
> > - int arg_quals = cp_type_quals (arg);
> > - int parm_quals = cp_type_quals (parm);
> > + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > +
> > + /* Try to unify ARG's address space into PARM's address space.
> > + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> > + there are no constraints on address spaces for this type. */
> > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > + addr_space_t as_common;
> > + addr_space_superset (as_arg, as_parm, &as_common);
> > +
> > + if (!(as_parm == as_common || as_parm == 0))
> > + return 0;
> > if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> > && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> > @@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> > arg, parm))
> > return unify_cv_qual_mismatch (explain_p, parm, arg);
> > + int arg_cv_quals = cp_type_quals (arg);
> > + int parm_cv_quals = cp_type_quals (parm);
> > +
> > + /* If PARM does not contain any address space constraints it can
> > + fully match the address space of ARG. However, if PARM contains an
> > + address space constraint, it becomes the upper bound. That is,
> > + AS_ARG may be promoted to AS_PARM but not the converse. If we
> > + ended up here, it means that `check_cv_quals_for_unify' succeeded
> > + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> > + AS_PARM. */
> > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> > + addr_space_t as_common = as_parm ? 0 : as_arg;
> > +
> > /* Consider the case where ARG is `const volatile int' and
> > PARM is `const T'. Then, T should be `volatile int'. */
> > - arg = cp_build_qualified_type_real
> > - (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
> > + int unified_cv =
> > + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> > + | ENCODE_QUAL_ADDR_SPACE (as_common);
> > + arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
> > if (arg == error_mark_node)
> > return unify_invalid (explain_p);
> > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> > index 3b37567cbd7..5e14ac837fc 100644
> > --- a/gcc/cp/tree.cc
> > +++ b/gcc/cp/tree.cc
> > @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
> > DECL_CHAIN (t) = NULL_TREE;
> > }
> > -/* Stub for c-common. Please keep in sync with c-decl.cc.
> > - FIXME: If address space support is target specific, then this
> > - should be a C target hook. But currently this is not possible,
> > - because this function is called via REGISTER_TARGET_PRAGMAS. */
> > -void
> > -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> > -{
> > -}
> > -
> > /* Return the number of operands in T that we care about for things like
> > mangling. */
> > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> > index ceb80d9744f..7d810c6a12c 100644
> > --- a/gcc/cp/typeck.cc
> > +++ b/gcc/cp/typeck.cc
> > @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
> > else
> > return error_mark_node;
> > }
> > + /* If possible merge the address space into the superset of the address
> > + spaces of t1 and t2, or raise an error. */
> > + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> > + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> > + addr_space_t as_common;
> > +
> > + /* If the two named address spaces are different, determine the common
> > + superset address space. If there isn't one, raise an error. */
> > + if (!addr_space_superset (as_t1, as_t2, &as_common))
> > + {
> > + as_common = as_t1;
> > + error_at (location,
> > + "%qT and %qT are in disjoint named address spaces",
> > + t1, t2);
> > + }
> > result_type
> > = cp_build_qualified_type (void_type_node,
> > - (cp_type_quals (TREE_TYPE (t1))
> > - | cp_type_quals (TREE_TYPE (t2))));
> > + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
> > + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
> > + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> > result_type = build_pointer_type (result_type);
> > /* Merge the attributes. */
> > attributes = (*targetm.merge_type_attributes) (t1, t2);
> > @@ -1579,7 +1595,9 @@ comptypes (tree t1, tree t2, int strict)
> > }
> > /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
> > - top-level qualifiers. */
> > + top-level qualifiers, except for named address spaces. If the pointers point
> > + to different named addresses spaces, then we must determine if one address
> > + space is a subset of the other. */
> > bool
> > same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > @@ -1589,6 +1607,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > if (type1 == type2)
> > return true;
> > + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
> > + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
> > + addr_space_t as_common;
> > +
> > + /* Fail if pointers point to incompatible address spaces. */
> > + if (!addr_space_superset (as_type1, as_type2, &as_common))
> > + return false;
> > +
> > type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
> > type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
> > return same_type_p (type1, type2);
> > @@ -6374,10 +6400,32 @@ static tree
> > pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
> > tsubst_flags_t complain, tree *instrument_expr)
> > {
> > - tree result, inttype;
> > tree restype = ptrdiff_type_node;
> > + tree result, inttype;
> > +
> > + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
> > + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
> > tree target_type = TREE_TYPE (ptrtype);
> > + /* If the operands point into different address spaces, we need to
> > + explicitly convert them to pointers into the common address space
> > + before we can subtract the numerical address values. */
> > + if (as0 != as1)
> > + {
> > + addr_space_t as_common;
> > + tree common_type;
> > +
> > + /* Determine the common superset address space. This is guaranteed
> > + to exist because the caller verified that comp_target_types
> > + returned non-zero. */
> > + if (!addr_space_superset (as0, as1, &as_common))
> > + gcc_unreachable ();
> > +
> > + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
> > + op0 = convert (common_type, op0);
> > + op1 = convert (common_type, op1);
> > + }
> > +
> > if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
> > return error_mark_node;
> > @@ -10805,6 +10853,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> > to_more_cv_qualified = true;
> > }
> > + /* Warn about conversions between pointers to disjoint
> > + address spaces. */
> > + if (TREE_CODE (from) == POINTER_TYPE
> > + && TREE_CODE (to) == POINTER_TYPE)
> > + {
> > + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> > + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> > + addr_space_t as_common;
> > +
> > + if (!addr_space_superset (as_to, as_from, &as_common))
> > + return false;
> > + }
> > +
> > if (constp > 0)
> > constp &= TYPE_READONLY (to);
> > }
> > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > index ac3db88566d..1e0d436c02c 100644
> > --- a/gcc/doc/extend.texi
> > +++ b/gcc/doc/extend.texi
> > @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> > @section Named Address Spaces
> > @cindex Named Address Spaces
> > -As an extension, GNU C supports named address spaces as
> > +As an extension, GNU C and GNU C++ support named address spaces as
> > defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> > address spaces in GCC will evolve as the draft technical report
> > changes. Calling conventions for any target might also change. At
> > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > new file mode 100644
> > index 00000000000..c01f8d6054a
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > @@ -0,0 +1,10 @@
> > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> > +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> > +
> > +int f (int volatile __seg_fs *a)
> > +{
> > + return *a;
> > +}
> > +
> > +int main () {}
> > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > new file mode 100644
> > index 00000000000..862bbbdcdf2
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > @@ -0,0 +1,9 @@
> > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> > +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
> > +
> > +template <class T>
> > +int f (T *p) { return *p; }
> > +int g (__seg_fs int *p) { return *p; }
> > +__seg_fs int *a;
> > +int main() { f(a); }
> > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > new file mode 100644
> > index 00000000000..c04d2f497da
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > @@ -0,0 +1,5 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +__seg_fs char a, b, c;
> > +__seg_fs const int *p;
> > +static /* give internal linkage to the following anonymous struct */
> > +__seg_fs struct { int a; char b; } * __seg_gs q;
> > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > new file mode 100644
> > index 00000000000..86c02d1e7f5
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > @@ -0,0 +1,20 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +int __seg_fs * fs1;
> > +int __seg_fs * fs2;
> > +float __seg_gs * gs1;
> > +float __seg_gs * gs2;
> > +
> > +int
> > +main ()
> > +{
> > + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
> > + fs1 - fs2;
> > + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
> > + fs1 == fs2;
> > + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > + fs1 = fs2;
> > + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
> > + fs1 > fs2;
> > + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > new file mode 100644
> > index 00000000000..12d7975e560
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > @@ -0,0 +1,31 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +// { dg-prune-output "does not allow .register. storage class specifier" }
> > +int __seg_fs * outer_b;
> > +
> > +struct s {
> > + __seg_fs int * ok;
> > + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
> > +};
> > +
> > +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
> > +
> > +namespace ns_a
> > +{
> > + int __seg_fs * inner_b;
> > +
> > + template<typename T>
> > + int f (T &a) { return a; }
> > + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
> > + int h (__seg_fs int *a) { return *a; }
> > +}
> > +
> > +int
> > +main ()
> > +{
> > + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
> > + static __seg_gs int static_gs;
> > + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
> > + __seg_fs int *pa = outer_b;
> > + __seg_fs int& ra = *ns_a::inner_b;
> > + return ns_a::f(ra) + ns_a::f(*pa);
> > +}
> > diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> > new file mode 100644
> > index 00000000000..ebb6316054a
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> > @@ -0,0 +1,9 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +
> > +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> > +
> > +int
> > +main ()
> > +{
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > new file mode 100644
> > index 00000000000..2e8ee32a885
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > @@ -0,0 +1,10 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +// { dg-options "-std=gnu++98" }
> > +
> > +int
> > +main ()
> > +{
> > + struct foo {int a; char b[2];} structure;
> > + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > new file mode 100644
> > index 00000000000..5b2c0f28078
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > @@ -0,0 +1,9 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +
> > +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> > +
> > +int
> > +main ()
> > +{
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > new file mode 100644
> > index 00000000000..ae9f4de0e1f
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > @@ -0,0 +1,8 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +
> > +template <class T>
> > +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> > + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> > +__seg_fs int *a;
> > +int main() { f(a); } // { dg-error "no matching" }
> > +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> > diff --git a/gcc/tree.h b/gcc/tree.h
> > index 8844471e9a5..b7da4c5141a 100644
> > --- a/gcc/tree.h
> > +++ b/gcc/tree.h
> > @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
> > /* Encode/decode the named memory support as part of the qualifier. If more
> > than 8 qualifiers are added, these macros need to be adjusted. */
> > -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> > +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> > #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
> > /* Return all qualifiers except for the address space qualifiers. */
> >
> >
> >
> >
>
>
>
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 15:23 ` Paul Iannetta
@ 2022-10-13 15:47 ` Jason Merrill
2022-10-13 16:02 ` Paul Iannetta
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-13 15:47 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/13/22 11:23, Paul Iannetta wrote:
> On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
>> On 10/12/22 20:52, Paul Iannetta wrote:
>>> On Tue, Oct 11, 2022 at 09:49:43PM -0400, Jason Merrill wrote:
>>>>
>>>> It surprises that this is the only place we complain about an object with an
>>>> address-space qualifier. Shouldn't we also complain about e.g. automatic
>>>> variables/parameters or non-static data members with address-space qualified
>>>> type?
>>>>
>>>
>>> Indeed, I was missing quite a few things here. Thanks.
>>> I used the draft as basis this time and imported from the C
>>> implementation the relevant parts. This time, the errors get properly
>>> emitted when an address space is unduly specified; and comparisons,
>>> assignments and comparisons are taken care of.
>>>
>>> There are quite a few things I would like to clarify concerning some
>>> implementation details.
>>> - A variable with automatic storage (which is neither a pointer nor
>>> a reference) cannot be qualified with an address space. I detect
>>> this by the combination of `sc_none' and `! toplevel_bindings_p ()',
>>> but I've also seen the use of `at_function_scope' at other places.
>>> And I'm unsure which one is appropriate here.
>>> This detection happens at the very end of grokdeclarator because I
>>> need to know that the type is a pointer, which is not know until
>>> very late in the function.
>>
>> At that point you have the decl, and you can ask directly what its storage
>> duration is, perhaps using decl_storage_duration.
>>
>> But why do you need to know whether the type is a pointer? The attribute
>> applies to the target type of the pointer, not the pointer type. I think
>> the problem is that you're looking at declspecs when you ought to be looking
>> at type_quals.
>
> I need to know that the base type is a pointer to reject invalid
> declarations such as:
>
> int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
>
> because parameters and auto variables can have an address space
> qualifier only if they are pointer or reference type, which I can't
> tell only from type_quals.
But "int *__seg_fs a" is just as invalid as the above; the difference is
not whether a is a pointer, but whether the address-space-qualified is
the type of a itself or some sub-type.
You need to look at the qualifiers on type (which should also be the
ones in type_quals), not the qualifiers in the declspecs.
>>> - I'm having some trouble deciding whether I include those three
>>> stub programs as tests, they all compile fine and clang accepts
>>> them as well.
>>
>> Why not?
>
> I thought they were pretty contrived, since it does not make much
> sense to strip address space qualifiers, even though it does prove
> that the implementation support those contrived but valid uses.
Testcases are full of contrived examples testing corner cases. :)
>>
>>> Ex1:
>>> ```
>>> int __seg_fs * fs1;
>>> int __seg_gs * gs1;
>>>
>>> template<typename T> struct strip;
>>> template<typename T> struct strip<__seg_fs T *> { typedef T type; };
>>> template<typename T> struct strip<__seg_gs T *> { typedef T type; };
>>>
>>> int
>>> main ()
>>> {
>>> *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
>>> return 0;
>>> }
>>> ```
>>>
>>> Ex2:
>>> ```
>>> int __seg_fs * fs1;
>>> int __seg_fs * fs2;
>>>
>>> template<typename T, typename U> auto f (T __seg_fs * a, U __seg_gs * b) { return a; }
>>> template<typename T, typename U> auto f (T __seg_gs * a, U __seg_fs * b) { return a; }
>>>
>>> int
>>> main ()
>>> {
>>> f (fs1, gs1);
>>> f (gs1, fs1);
>>> return 0;
>>> }
>>> ```
>>>
>>> Ex3:
>>> ```
>>> int __seg_fs * fs1;
>>> int __seg_gs * gs1;
>>>
>>> template<typename T, typename U>
>>> auto f (T __seg_fs * a, U __seg_gs * b)
>>> {
>>> return *(T *) a == *(U *) b;
>>> }
>>>
>>> int
>>> main ()
>>> {
>>> return f (fs1, gs1);
>>> }
>>> ```
>>>
>>>
>>> Add support for custom address spaces in C++
>>>
>>> gcc/
>>> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>>>
>>> gcc/c/
>>> * c-decl.cc: Remove c_register_addr_space.
>>>
>>> gcc/c-family/
>>> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
>>> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
>>> * c-common.h: Remove the FIXME.
>>> (addr_space_superset): New declaration.
>>>
>>> gcc/cp/
>>> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
>>> (struct cp_decl_specifier_seq): Likewise.
>>> * decl.cc (get_type_quals): Likewise.
>>> (check_tag_decl): Likewise.
>>> (grokdeclarator): Likewise.
>>> * parser.cc (cp_parser_type_specifier): Likewise.
>>> (cp_parser_cv_qualifier_seq_opt): Likewise.
>>> (cp_parser_postfix_expression): Likewise.
>>> (cp_parser_type_specifier): Likewise.
>>> (set_and_check_decl_spec_loc): Likewise.
>>> * typeck.cc (composite_pointer_type): Likewise
>>> (comp_ptr_ttypes_real): Likewise.
>>> (same_type_ignoring_top_level_qualifiers_p): Likewise.
>>> * pt.cc (check_cv_quals_for_unify): Likewise.
>>> (unify): Likewise.
>>> * tree.cc: Remove c_register_addr_space stub.
>>> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
>>> using the extended qualifier notation.
>>>
>>> gcc/doc
>>> * extend.texi (Named Address Spaces): add a mention about C++
>>> support.
>>>
>>> gcc/testsuite/
>>> * g++.dg/abi/mangle-addr-space1.C: New test.
>>> * g++.dg/abi/mangle-addr-space2.C: New test.
>>> * g++.dg/parse/addr-space.C: New test.
>>> * g++.dg/parse/addr-space1.C: New test.
>>> * g++.dg/parse/addr-space2.C: New test.
>>> * g++.dg/parse/template/spec-addr-space.C: New test.
>>> * g++.dg/ext/addr-space-decl.C: New test.
>>> * g++.dg/ext/addr-space-ref.C: New test.
>>> * g++.dg/ext/addr-space-ops.C: New test.
>>>
>>> # Please enter the commit message for your changes. Lines starting
>>> # with '#' will be ignored, and an empty message aborts the commit.
>>> #
>>> # Date: Sun Oct 9 16:02:22 2022 +0200
>>> #
>>> # On branch releases/gcc-12
>>> # Your branch is ahead of 'origin/releases/gcc-12' by 2 commits.
>>> # (use "git push" to publish your local commits)
>>> #
>>> # Changes to be committed:
>>> # modified: gcc/c-family/c-common.cc
>>> # modified: gcc/c-family/c-common.h
>>> # modified: gcc/c/c-decl.cc
>>> # modified: gcc/c/c-typeck.cc
>>> # modified: gcc/cp/cp-tree.h
>>> # modified: gcc/cp/decl.cc
>>> # modified: gcc/cp/mangle.cc
>>> # modified: gcc/cp/parser.cc
>>> # modified: gcc/cp/pt.cc
>>> # modified: gcc/cp/tree.cc
>>> # modified: gcc/cp/typeck.cc
>>> # modified: gcc/doc/extend.texi
>>> # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>> # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>> # new file: gcc/testsuite/g++.dg/parse/addr-space.C
>>> # new file: gcc/testsuite/g++.dg/parse/addr-space1.C
>>> # new file: gcc/testsuite/g++.dg/parse/addr-space2.C
>>> # new file: gcc/testsuite/g++.dg/template/spec-addr-space.C
>>> # modified: gcc/tree.h
>>> #
>>> # Untracked files:
>>> # Makefile
>>> # addr-space-decl.C
>>> # addr-space-decl.s
>>> # addr-space-ops.C
>>> # addr-space-ref.C
>>> # addr-space-template.C
>>> # addr-space-template.s
>>> # addr-space-template2.C
>>> # addr-space-template2.C.006t.gimple
>>> # addr-space-template2.s
>>> # addr-space-traits.C
>>> # addr-space4.C
>>> # addr-space4.C.006t.gimple
>>> # addr-space4.s
>>> # build-x86_64-pc-linux-gnu/
>>> # compare
>>> # host-x86_64-pc-linux-gnu/
>>> # mangle-addr-space1.s
>>> # prev-x86_64-pc-linux-gnu/
>>> # rename
>>> # stage1-x86_64-pc-linux-gnu/
>>> # stage_current
>>> # stage_final
>>> # stage_last
>>> # x86_64-pc-linux-gnu/
>>> #
>>> # ------------------------ >8 ------------------------
>>> # Do not modify or remove the line above.
>>> # Everything below it will be ignored.
>>> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
>>> index bb0544eeaea..ff1146ecc25 100644
>>> --- a/gcc/c-family/c-common.cc
>>> +++ b/gcc/c-family/c-common.cc
>>> @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
>>> return IDENTIFIER_POINTER (ridpointers [rid]);
>>> }
>>> +/* Return true if between two named address spaces, whether there is a superset
>>> + named address space that encompasses both address spaces. If there is a
>>> + superset, return which address space is the superset. */
>>> +
>>> +bool
>>> +addr_space_superset (addr_space_t as1, addr_space_t as2,
>>> + addr_space_t * common)
>>> +{
>>> + if (as1 == as2)
>>> + {
>>> + *common = as1;
>>> + return true;
>>> + }
>>> + else if (targetm.addr_space.subset_p (as1, as2))
>>> + {
>>> + *common = as2;
>>> + return true;
>>> + }
>>> + else if (targetm.addr_space.subset_p (as2, as1))
>>> + {
>>> + *common = as1;
>>> + return true;
>>> + }
>>> + else
>>> + return false;
>>> +}
>>> +
>>> /* Push current bindings for the function name VAR_DECLS. */
>>> void
>>> @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
>>> return build_nonstandard_integer_type (width, unsignedp);
>>> }
>>> +/* Register reserved keyword WORD as qualifier for address space AS. */
>>> +
>>> +void
>>> +c_register_addr_space (const char *word, addr_space_t as)
>>> +{
>>> + int rid = RID_FIRST_ADDR_SPACE + as;
>>> + tree id;
>>> +
>>> + /* Address space qualifiers are only supported
>>> + in C with GNU extensions enabled. */
>>> + if (c_dialect_objc () || flag_no_asm)
>>> + return;
>>> +
>>> + id = get_identifier (word);
>>> + C_SET_RID_CODE (id, rid);
>>> + TREE_LANG_FLAG_0 (id) = 1;
>>> + ridpointers[rid] = id;
>>> +}
>>> +
>>> /* The C version of the register_builtin_type langhook. */
>>> void
>>> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
>>> index 52a85bfb783..d36f9e4975b 100644
>>> --- a/gcc/c-family/c-common.h
>>> +++ b/gcc/c-family/c-common.h
>>> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>>> extern tree (*make_fname_decl) (location_t, tree, int);
>>> -/* In c-decl.cc and cp/tree.cc. FIXME. */
>>> -extern void c_register_addr_space (const char *str, addr_space_t as);
>>> -
>>> /* In c-common.cc. */
>>> extern bool in_late_binary_op;
>>> extern const char *c_addr_space_name (addr_space_t as);
>>> +extern const char *c_addr_space_name (addr_space_t as);
>>> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
>>> extern tree identifier_global_value (tree);
>>> extern tree identifier_global_tag (tree);
>>> extern bool names_builtin_p (const char *);
>>> @@ -952,6 +951,7 @@ extern void c_common_finish (void);
>>> extern void c_common_parse_file (void);
>>> extern FILE *get_dump_info (int, dump_flags_t *);
>>> extern alias_set_type c_common_get_alias_set (tree);
>>> +extern void c_register_addr_space (const char *, addr_space_t);
>>> extern void c_register_builtin_type (tree, const char*);
>>> extern bool c_promoting_integer_type_p (const_tree);
>>> extern bool self_promoting_args_p (const_tree);
>>> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
>>> index c701f07befe..e1bb4f1cf37 100644
>>> --- a/gcc/c/c-decl.cc
>>> +++ b/gcc/c/c-decl.cc
>>> @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
>>> ext_block = NULL;
>>> }
>>> -/* Register reserved keyword WORD as qualifier for address space AS. */
>>> -
>>> -void
>>> -c_register_addr_space (const char *word, addr_space_t as)
>>> -{
>>> - int rid = RID_FIRST_ADDR_SPACE + as;
>>> - tree id;
>>> -
>>> - /* Address space qualifiers are only supported
>>> - in C with GNU extensions enabled. */
>>> - if (c_dialect_objc () || flag_no_asm)
>>> - return;
>>> -
>>> - id = get_identifier (word);
>>> - C_SET_RID_CODE (id, rid);
>>> - C_IS_RESERVED_WORD (id) = 1;
>>> - ridpointers [rid] = id;
>>> -}
>>> -
>>> /* Return identifier to look up for omp declare reduction. */
>>> tree
>>> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
>>> index b271af9bedb..c4b01368534 100644
>>> --- a/gcc/c/c-typeck.cc
>>> +++ b/gcc/c/c-typeck.cc
>>> @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
>>> return type;
>>> }
>>> -/* Return true if between two named address spaces, whether there is a superset
>>> - named address space that encompasses both address spaces. If there is a
>>> - superset, return which address space is the superset. */
>>> -
>>> -static bool
>>> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
>>> -{
>>> - if (as1 == as2)
>>> - {
>>> - *common = as1;
>>> - return true;
>>> - }
>>> - else if (targetm.addr_space.subset_p (as1, as2))
>>> - {
>>> - *common = as2;
>>> - return true;
>>> - }
>>> - else if (targetm.addr_space.subset_p (as2, as1))
>>> - {
>>> - *common = as1;
>>> - return true;
>>> - }
>>> - else
>>> - return false;
>>> -}
>>> -
>>> /* Return a variant of TYPE which has all the type qualifiers of LIKE
>>> as well as those of TYPE. */
>>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>>> index 72f4398a8f9..82a6d72f5df 100644
>>> --- a/gcc/cp/cp-tree.h
>>> +++ b/gcc/cp/cp-tree.h
>>> @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
>>> ds_const,
>>> ds_volatile,
>>> ds_restrict,
>>> + ds_addr_space,
>>> ds_inline,
>>> ds_virtual,
>>> ds_explicit,
>>> @@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
>>> cp_storage_class storage_class;
>>> /* For the __intN declspec, this stores the index into the int_n_* arrays. */
>>> int int_n_idx;
>>> + /* The address space that the declaration belongs to. */
>>> + addr_space_t address_space;
>>> /* True iff TYPE_SPEC defines a class or enum. */
>>> BOOL_BITFIELD type_definition_p : 1;
>>> /* True iff multiple types were (erroneously) specified for this
>>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
>>> index 9f78c500a15..fa5a1ddb21d 100644
>>> --- a/gcc/cp/decl.cc
>>> +++ b/gcc/cp/decl.cc
>>> @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
>>> type_quals |= TYPE_QUAL_VOLATILE;
>>> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
>>> type_quals |= TYPE_QUAL_RESTRICT;
>>> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>>> return type_quals;
>>> }
>>> @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
>>> error_at (declspecs->locations[ds_restrict],
>>> "%<__restrict%> can only be specified for objects and "
>>> "functions");
>>> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>> + error_at (declspecs->locations[ds_addr_space],
>>> + "address space can only be specified for objects and "
>>> + "functions");
>>> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
>>> error_at (declspecs->locations[ds_thread],
>>> "%<__thread%> can only be specified for objects "
>>> @@ -14572,6 +14578,57 @@ grokdeclarator (const cp_declarator *declarator,
>>> if (!processing_template_decl)
>>> cp_apply_type_quals_to_decl (type_quals, decl);
>>> + addr_space_t address_space = declspecs->address_space;
>>> + if (!ADDR_SPACE_GENERIC_P (address_space) && !INDIRECT_TYPE_P (type))
>>> + {
>>> + if (decl_context == NORMAL)
>>> + {
>>> + switch (storage_class)
>>> + {
>>> + case sc_auto:
>>> + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
>>> + c_addr_space_name (address_space), name);
>>> + break;
>>> + case sc_register:
>>> + error ("%qs combined with %<register%> qualifier for %qs",
>>> + c_addr_space_name (address_space), name);
>>> + break;
>>> + case sc_none:
>>> + if (! toplevel_bindings_p ())
>>> + error ("%qs specified for auto variable %qs",
>>> + c_addr_space_name (address_space), name);
>>> + break;
>>> + case sc_mutable:
>>> + error ("%qs combined with %<mutable%> qualifier for %qs",
>>> + c_addr_space_name (address_space), name);
>>> + break;
>>> + case sc_static:
>>> + case sc_extern:
>>> + break;
>>> + default:
>>> + gcc_unreachable ();
>>> + }
>>> + }
>>> + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
>>> + {
>>> + if (name)
>>> + error ("%qs specified for parameter %qs",
>>> + c_addr_space_name (address_space), name);
>>> + else
>>> + error ("%qs specified for unnamed parameter",
>>> + c_addr_space_name (address_space));
>>> + }
>>> + else if (decl_context == FIELD)
>>> + {
>>> + if (name)
>>> + error ("%qs specified for structure field %qs",
>>> + c_addr_space_name (address_space), name);
>>> + else
>>> + error ("%qs specified for structure field",
>>> + c_addr_space_name (address_space));
>>> + }
>>> + }
>>> +
>>> return decl;
>>> }
>>> }
>>> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
>>> index eb53e0ebeb4..16cb2ff4332 100644
>>> --- a/gcc/cp/mangle.cc
>>> +++ b/gcc/cp/mangle.cc
>>> @@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
>>> array. */
>>> cp_cv_quals quals = TYPE_QUALS (type);
>>> + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
>>> + {
>>> + const char *as_name = c_addr_space_name (as);
>>> + write_char ('U');
>>> + write_unsigned_number (strlen (as_name));
>>> + write_string (as_name);
>>> + ++num_qualifiers;
>>> + }
>>> if (quals & TYPE_QUAL_RESTRICT)
>>> {
>>> write_char ('r');
>>> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
>>> index 763df6f479b..f0169d83ab0 100644
>>> --- a/gcc/cp/parser.cc
>>> +++ b/gcc/cp/parser.cc
>>> @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
>>> postfix_expression = error_mark_node;
>>> break;
>>> }
>>> + if (type != error_mark_node
>>> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
>>> + && current_function_decl)
>>> + {
>>> + error
>>> + ("compound literal qualified by address-space qualifier");
>>> + type = error_mark_node;
>>> + }
>>> /* Form the representation of the compound-literal. */
>>> postfix_expression
>>> = finish_compound_literal (type, initializer,
>>> @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
>>> break;
>>> }
>>> +
>>> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
>>> + {
>>> + ds = ds_addr_space;
>>> + if (is_cv_qualifier)
>>> + *is_cv_qualifier = true;
>>> + }
>>> +
>>> +
>>> /* Handle simple keywords. */
>>> if (ds != ds_last)
>>> {
>>> @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
>>> GNU Extension:
>>> cv-qualifier:
>>> + address-space-qualifier
>>> __restrict__
>>> Returns a bitmask representing the cv-qualifiers. */
>>> @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
>>> break;
>>> }
>>> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
>>> + token->keyword <= RID_LAST_ADDR_SPACE)
>>> + cv_qualifier =
>>> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>>> +
>>> if (!cv_qualifier)
>>> break;
>>> @@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>> decl_specs->locations[ds] = location;
>>> if (ds == ds_thread)
>>> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
>>> + else if (ds == ds_addr_space)
>>> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
>>> }
>>> else
>>> {
>>> @@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>> error_at (&richloc, "duplicate %qD", token->u.value);
>>> }
>>> }
>>> + else if (ds == ds_addr_space)
>>> + {
>>> + addr_space_t as1 = decl_specs->address_space;
>>> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
>>> +
>>> + gcc_rich_location richloc (location);
>>> + richloc.add_fixit_remove ();
>>> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
>>> + && as1 != as2)
>>> + error_at (&richloc,
>>> + "conflicting named address spaces (%s vs %s)",
>>> + c_addr_space_name (as1), c_addr_space_name (as2));
>>> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
>>> + error_at (&richloc,
>>> + "duplicate named address space %s",
>>> + c_addr_space_name (as1));
>>> +
>>> + decl_specs->address_space = as2;
>>> + }
>>> else
>>> {
>>> static const char *const decl_spec_names[] = {
>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>> index bef31416fb7..c9d1c01f4ce 100644
>>> --- a/gcc/cp/pt.cc
>>> +++ b/gcc/cp/pt.cc
>>> @@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
>>> static int
>>> check_cv_quals_for_unify (int strict, tree arg, tree parm)
>>> {
>>> - int arg_quals = cp_type_quals (arg);
>>> - int parm_quals = cp_type_quals (parm);
>>> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>> +
>>> + /* Try to unify ARG's address space into PARM's address space.
>>> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
>>> + there are no constraints on address spaces for this type. */
>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>> + addr_space_t as_common;
>>> + addr_space_superset (as_arg, as_parm, &as_common);
>>> +
>>> + if (!(as_parm == as_common || as_parm == 0))
>>> + return 0;
>>> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
>>> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
>>> @@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
>>> arg, parm))
>>> return unify_cv_qual_mismatch (explain_p, parm, arg);
>>> + int arg_cv_quals = cp_type_quals (arg);
>>> + int parm_cv_quals = cp_type_quals (parm);
>>> +
>>> + /* If PARM does not contain any address space constraints it can
>>> + fully match the address space of ARG. However, if PARM contains an
>>> + address space constraint, it becomes the upper bound. That is,
>>> + AS_ARG may be promoted to AS_PARM but not the converse. If we
>>> + ended up here, it means that `check_cv_quals_for_unify' succeeded
>>> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
>>> + AS_PARM. */
>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
>>> + addr_space_t as_common = as_parm ? 0 : as_arg;
>>> +
>>> /* Consider the case where ARG is `const volatile int' and
>>> PARM is `const T'. Then, T should be `volatile int'. */
>>> - arg = cp_build_qualified_type_real
>>> - (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
>>> + int unified_cv =
>>> + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
>>> + | ENCODE_QUAL_ADDR_SPACE (as_common);
>>> + arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
>>> if (arg == error_mark_node)
>>> return unify_invalid (explain_p);
>>> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
>>> index 3b37567cbd7..5e14ac837fc 100644
>>> --- a/gcc/cp/tree.cc
>>> +++ b/gcc/cp/tree.cc
>>> @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
>>> DECL_CHAIN (t) = NULL_TREE;
>>> }
>>> -/* Stub for c-common. Please keep in sync with c-decl.cc.
>>> - FIXME: If address space support is target specific, then this
>>> - should be a C target hook. But currently this is not possible,
>>> - because this function is called via REGISTER_TARGET_PRAGMAS. */
>>> -void
>>> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
>>> -{
>>> -}
>>> -
>>> /* Return the number of operands in T that we care about for things like
>>> mangling. */
>>> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
>>> index ceb80d9744f..7d810c6a12c 100644
>>> --- a/gcc/cp/typeck.cc
>>> +++ b/gcc/cp/typeck.cc
>>> @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
>>> else
>>> return error_mark_node;
>>> }
>>> + /* If possible merge the address space into the superset of the address
>>> + spaces of t1 and t2, or raise an error. */
>>> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
>>> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
>>> + addr_space_t as_common;
>>> +
>>> + /* If the two named address spaces are different, determine the common
>>> + superset address space. If there isn't one, raise an error. */
>>> + if (!addr_space_superset (as_t1, as_t2, &as_common))
>>> + {
>>> + as_common = as_t1;
>>> + error_at (location,
>>> + "%qT and %qT are in disjoint named address spaces",
>>> + t1, t2);
>>> + }
>>> result_type
>>> = cp_build_qualified_type (void_type_node,
>>> - (cp_type_quals (TREE_TYPE (t1))
>>> - | cp_type_quals (TREE_TYPE (t2))));
>>> + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
>>> + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
>>> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
>>> result_type = build_pointer_type (result_type);
>>> /* Merge the attributes. */
>>> attributes = (*targetm.merge_type_attributes) (t1, t2);
>>> @@ -1579,7 +1595,9 @@ comptypes (tree t1, tree t2, int strict)
>>> }
>>> /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
>>> - top-level qualifiers. */
>>> + top-level qualifiers, except for named address spaces. If the pointers point
>>> + to different named addresses spaces, then we must determine if one address
>>> + space is a subset of the other. */
>>> bool
>>> same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>> @@ -1589,6 +1607,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>> if (type1 == type2)
>>> return true;
>>> + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
>>> + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
>>> + addr_space_t as_common;
>>> +
>>> + /* Fail if pointers point to incompatible address spaces. */
>>> + if (!addr_space_superset (as_type1, as_type2, &as_common))
>>> + return false;
>>> +
>>> type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
>>> type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
>>> return same_type_p (type1, type2);
>>> @@ -6374,10 +6400,32 @@ static tree
>>> pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
>>> tsubst_flags_t complain, tree *instrument_expr)
>>> {
>>> - tree result, inttype;
>>> tree restype = ptrdiff_type_node;
>>> + tree result, inttype;
>>> +
>>> + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
>>> + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
>>> tree target_type = TREE_TYPE (ptrtype);
>>> + /* If the operands point into different address spaces, we need to
>>> + explicitly convert them to pointers into the common address space
>>> + before we can subtract the numerical address values. */
>>> + if (as0 != as1)
>>> + {
>>> + addr_space_t as_common;
>>> + tree common_type;
>>> +
>>> + /* Determine the common superset address space. This is guaranteed
>>> + to exist because the caller verified that comp_target_types
>>> + returned non-zero. */
>>> + if (!addr_space_superset (as0, as1, &as_common))
>>> + gcc_unreachable ();
>>> +
>>> + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
>>> + op0 = convert (common_type, op0);
>>> + op1 = convert (common_type, op1);
>>> + }
>>> +
>>> if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
>>> return error_mark_node;
>>> @@ -10805,6 +10853,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>>> to_more_cv_qualified = true;
>>> }
>>> + /* Warn about conversions between pointers to disjoint
>>> + address spaces. */
>>> + if (TREE_CODE (from) == POINTER_TYPE
>>> + && TREE_CODE (to) == POINTER_TYPE)
>>> + {
>>> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
>>> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
>>> + addr_space_t as_common;
>>> +
>>> + if (!addr_space_superset (as_to, as_from, &as_common))
>>> + return false;
>>> + }
>>> +
>>> if (constp > 0)
>>> constp &= TYPE_READONLY (to);
>>> }
>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>> index ac3db88566d..1e0d436c02c 100644
>>> --- a/gcc/doc/extend.texi
>>> +++ b/gcc/doc/extend.texi
>>> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
>>> @section Named Address Spaces
>>> @cindex Named Address Spaces
>>> -As an extension, GNU C supports named address spaces as
>>> +As an extension, GNU C and GNU C++ support named address spaces as
>>> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
>>> address spaces in GCC will evolve as the draft technical report
>>> changes. Calling conventions for any target might also change. At
>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>> new file mode 100644
>>> index 00000000000..c01f8d6054a
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>> @@ -0,0 +1,10 @@
>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
>>> +
>>> +int f (int volatile __seg_fs *a)
>>> +{
>>> + return *a;
>>> +}
>>> +
>>> +int main () {}
>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>> new file mode 100644
>>> index 00000000000..862bbbdcdf2
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>> @@ -0,0 +1,9 @@
>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>> +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
>>> +
>>> +template <class T>
>>> +int f (T *p) { return *p; }
>>> +int g (__seg_fs int *p) { return *p; }
>>> +__seg_fs int *a;
>>> +int main() { f(a); }
>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>> new file mode 100644
>>> index 00000000000..c04d2f497da
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>> @@ -0,0 +1,5 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +__seg_fs char a, b, c;
>>> +__seg_fs const int *p;
>>> +static /* give internal linkage to the following anonymous struct */
>>> +__seg_fs struct { int a; char b; } * __seg_gs q;
>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>> new file mode 100644
>>> index 00000000000..86c02d1e7f5
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>> @@ -0,0 +1,20 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +int __seg_fs * fs1;
>>> +int __seg_fs * fs2;
>>> +float __seg_gs * gs1;
>>> +float __seg_gs * gs2;
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
>>> + fs1 - fs2;
>>> + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
>>> + fs1 == fs2;
>>> + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>> + fs1 = fs2;
>>> + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
>>> + fs1 > fs2;
>>> + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>> new file mode 100644
>>> index 00000000000..12d7975e560
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>> @@ -0,0 +1,31 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +// { dg-prune-output "does not allow .register. storage class specifier" }
>>> +int __seg_fs * outer_b;
>>> +
>>> +struct s {
>>> + __seg_fs int * ok;
>>> + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
>>> +};
>>> +
>>> +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
>>> +
>>> +namespace ns_a
>>> +{
>>> + int __seg_fs * inner_b;
>>> +
>>> + template<typename T>
>>> + int f (T &a) { return a; }
>>> + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
>>> + int h (__seg_fs int *a) { return *a; }
>>> +}
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
>>> + static __seg_gs int static_gs;
>>> + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
>>> + __seg_fs int *pa = outer_b;
>>> + __seg_fs int& ra = *ns_a::inner_b;
>>> + return ns_a::f(ra) + ns_a::f(*pa);
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
>>> new file mode 100644
>>> index 00000000000..ebb6316054a
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
>>> @@ -0,0 +1,9 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +
>>> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>> new file mode 100644
>>> index 00000000000..2e8ee32a885
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>> @@ -0,0 +1,10 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +// { dg-options "-std=gnu++98" }
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + struct foo {int a; char b[2];} structure;
>>> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>> new file mode 100644
>>> index 00000000000..5b2c0f28078
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>> @@ -0,0 +1,9 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +
>>> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>> new file mode 100644
>>> index 00000000000..ae9f4de0e1f
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>> @@ -0,0 +1,8 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +
>>> +template <class T>
>>> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
>>> + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
>>> +__seg_fs int *a;
>>> +int main() { f(a); } // { dg-error "no matching" }
>>> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
>>> diff --git a/gcc/tree.h b/gcc/tree.h
>>> index 8844471e9a5..b7da4c5141a 100644
>>> --- a/gcc/tree.h
>>> +++ b/gcc/tree.h
>>> @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
>>> /* Encode/decode the named memory support as part of the qualifier. If more
>>> than 8 qualifiers are added, these macros need to be adjusted. */
>>> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
>>> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
>>> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>>> /* Return all qualifiers except for the address space qualifiers. */
>>>
>>>
>>>
>>>
>>
>>
>>
>>
>>
>>
>>
>
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 15:47 ` Jason Merrill
@ 2022-10-13 16:02 ` Paul Iannetta
2022-10-13 19:41 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-10-13 16:02 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
> On 10/13/22 11:23, Paul Iannetta wrote:
> > On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
> > > On 10/12/22 20:52, Paul Iannetta wrote:
> > > > On Tue, Oct 11, 2022 at 09:49:43PM -0400, Jason Merrill wrote:
> > > > >
> > > > > It surprises that this is the only place we complain about an object with an
> > > > > address-space qualifier. Shouldn't we also complain about e.g. automatic
> > > > > variables/parameters or non-static data members with address-space qualified
> > > > > type?
> > > > >
> > > >
> > > > Indeed, I was missing quite a few things here. Thanks.
> > > > I used the draft as basis this time and imported from the C
> > > > implementation the relevant parts. This time, the errors get properly
> > > > emitted when an address space is unduly specified; and comparisons,
> > > > assignments and comparisons are taken care of.
> > > >
> > > > There are quite a few things I would like to clarify concerning some
> > > > implementation details.
> > > > - A variable with automatic storage (which is neither a pointer nor
> > > > a reference) cannot be qualified with an address space. I detect
> > > > this by the combination of `sc_none' and `! toplevel_bindings_p ()',
> > > > but I've also seen the use of `at_function_scope' at other places.
> > > > And I'm unsure which one is appropriate here.
> > > > This detection happens at the very end of grokdeclarator because I
> > > > need to know that the type is a pointer, which is not know until
> > > > very late in the function.
> > >
> > > At that point you have the decl, and you can ask directly what its storage
> > > duration is, perhaps using decl_storage_duration.
> > >
> > > But why do you need to know whether the type is a pointer? The attribute
> > > applies to the target type of the pointer, not the pointer type. I think
> > > the problem is that you're looking at declspecs when you ought to be looking
> > > at type_quals.
> >
> > I need to know that the base type is a pointer to reject invalid
> > declarations such as:
> >
> > int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
> >
> > because parameters and auto variables can have an address space
> > qualifier only if they are pointer or reference type, which I can't
> > tell only from type_quals.
>
> But "int *__seg_fs a" is just as invalid as the above; the difference is not
> whether a is a pointer, but whether the address-space-qualified is the type
> of a itself or some sub-type.
I agree that "int * __seg_fs a" is invalid but it is accepted by the C
front-end, and by clang (both C and C++), the behavior is that the
address-name is silently ignored.
> You need to look at the qualifiers on type (which should also be the ones in
> type_quals), not the qualifiers in the declspecs.
I'll have another look, thanks.
> > > > - I'm having some trouble deciding whether I include those three
> > > > stub programs as tests, they all compile fine and clang accepts
> > > > them as well.
> > >
> > > Why not?
> >
> > I thought they were pretty contrived, since it does not make much
> > sense to strip address space qualifiers, even though it does prove
> > that the implementation support those contrived but valid uses.
>
> Testcases are full of contrived examples testing corner cases. :)
>
> > >
> > > > Ex1:
> > > > ```
> > > > int __seg_fs * fs1;
> > > > int __seg_gs * gs1;
> > > >
> > > > template<typename T> struct strip;
> > > > template<typename T> struct strip<__seg_fs T *> { typedef T type; };
> > > > template<typename T> struct strip<__seg_gs T *> { typedef T type; };
> > > >
> > > > int
> > > > main ()
> > > > {
> > > > *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
> > > > return 0;
> > > > }
> > > > ```
> > > >
> > > > Ex2:
> > > > ```
> > > > int __seg_fs * fs1;
> > > > int __seg_fs * fs2;
> > > >
> > > > template<typename T, typename U> auto f (T __seg_fs * a, U __seg_gs * b) { return a; }
> > > > template<typename T, typename U> auto f (T __seg_gs * a, U __seg_fs * b) { return a; }
> > > >
> > > > int
> > > > main ()
> > > > {
> > > > f (fs1, gs1);
> > > > f (gs1, fs1);
> > > > return 0;
> > > > }
> > > > ```
> > > >
> > > > Ex3:
> > > > ```
> > > > int __seg_fs * fs1;
> > > > int __seg_gs * gs1;
> > > >
> > > > template<typename T, typename U>
> > > > auto f (T __seg_fs * a, U __seg_gs * b)
> > > > {
> > > > return *(T *) a == *(U *) b;
> > > > }
> > > >
> > > > int
> > > > main ()
> > > > {
> > > > return f (fs1, gs1);
> > > > }
> > > > ```
> > > >
> > > >
> > > > Add support for custom address spaces in C++
> > > >
> > > > gcc/
> > > > * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
> > > >
> > > > gcc/c/
> > > > * c-decl.cc: Remove c_register_addr_space.
> > > >
> > > > gcc/c-family/
> > > > * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> > > > (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> > > > * c-common.h: Remove the FIXME.
> > > > (addr_space_superset): New declaration.
> > > >
> > > > gcc/cp/
> > > > * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> > > > (struct cp_decl_specifier_seq): Likewise.
> > > > * decl.cc (get_type_quals): Likewise.
> > > > (check_tag_decl): Likewise.
> > > > (grokdeclarator): Likewise.
> > > > * parser.cc (cp_parser_type_specifier): Likewise.
> > > > (cp_parser_cv_qualifier_seq_opt): Likewise.
> > > > (cp_parser_postfix_expression): Likewise.
> > > > (cp_parser_type_specifier): Likewise.
> > > > (set_and_check_decl_spec_loc): Likewise.
> > > > * typeck.cc (composite_pointer_type): Likewise
> > > > (comp_ptr_ttypes_real): Likewise.
> > > > (same_type_ignoring_top_level_qualifiers_p): Likewise.
> > > > * pt.cc (check_cv_quals_for_unify): Likewise.
> > > > (unify): Likewise.
> > > > * tree.cc: Remove c_register_addr_space stub.
> > > > * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> > > > using the extended qualifier notation.
> > > >
> > > > gcc/doc
> > > > * extend.texi (Named Address Spaces): add a mention about C++
> > > > support.
> > > >
> > > > gcc/testsuite/
> > > > * g++.dg/abi/mangle-addr-space1.C: New test.
> > > > * g++.dg/abi/mangle-addr-space2.C: New test.
> > > > * g++.dg/parse/addr-space.C: New test.
> > > > * g++.dg/parse/addr-space1.C: New test.
> > > > * g++.dg/parse/addr-space2.C: New test.
> > > > * g++.dg/parse/template/spec-addr-space.C: New test.
> > > > * g++.dg/ext/addr-space-decl.C: New test.
> > > > * g++.dg/ext/addr-space-ref.C: New test.
> > > > * g++.dg/ext/addr-space-ops.C: New test.
> > > >
> > > > # Please enter the commit message for your changes. Lines starting
> > > > # with '#' will be ignored, and an empty message aborts the commit.
> > > > #
> > > > # Date: Sun Oct 9 16:02:22 2022 +0200
> > > > #
> > > > # On branch releases/gcc-12
> > > > # Your branch is ahead of 'origin/releases/gcc-12' by 2 commits.
> > > > # (use "git push" to publish your local commits)
> > > > #
> > > > # Changes to be committed:
> > > > # modified: gcc/c-family/c-common.cc
> > > > # modified: gcc/c-family/c-common.h
> > > > # modified: gcc/c/c-decl.cc
> > > > # modified: gcc/c/c-typeck.cc
> > > > # modified: gcc/cp/cp-tree.h
> > > > # modified: gcc/cp/decl.cc
> > > > # modified: gcc/cp/mangle.cc
> > > > # modified: gcc/cp/parser.cc
> > > > # modified: gcc/cp/pt.cc
> > > > # modified: gcc/cp/tree.cc
> > > > # modified: gcc/cp/typeck.cc
> > > > # modified: gcc/doc/extend.texi
> > > > # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > > > # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > > > # new file: gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > > > # new file: gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > > > # new file: gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > > > # new file: gcc/testsuite/g++.dg/parse/addr-space.C
> > > > # new file: gcc/testsuite/g++.dg/parse/addr-space1.C
> > > > # new file: gcc/testsuite/g++.dg/parse/addr-space2.C
> > > > # new file: gcc/testsuite/g++.dg/template/spec-addr-space.C
> > > > # modified: gcc/tree.h
> > > > #
> > > > # Untracked files:
> > > > # Makefile
> > > > # addr-space-decl.C
> > > > # addr-space-decl.s
> > > > # addr-space-ops.C
> > > > # addr-space-ref.C
> > > > # addr-space-template.C
> > > > # addr-space-template.s
> > > > # addr-space-template2.C
> > > > # addr-space-template2.C.006t.gimple
> > > > # addr-space-template2.s
> > > > # addr-space-traits.C
> > > > # addr-space4.C
> > > > # addr-space4.C.006t.gimple
> > > > # addr-space4.s
> > > > # build-x86_64-pc-linux-gnu/
> > > > # compare
> > > > # host-x86_64-pc-linux-gnu/
> > > > # mangle-addr-space1.s
> > > > # prev-x86_64-pc-linux-gnu/
> > > > # rename
> > > > # stage1-x86_64-pc-linux-gnu/
> > > > # stage_current
> > > > # stage_final
> > > > # stage_last
> > > > # x86_64-pc-linux-gnu/
> > > > #
> > > > # ------------------------ >8 ------------------------
> > > > # Do not modify or remove the line above.
> > > > # Everything below it will be ignored.
> > > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> > > > index bb0544eeaea..ff1146ecc25 100644
> > > > --- a/gcc/c-family/c-common.cc
> > > > +++ b/gcc/c-family/c-common.cc
> > > > @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
> > > > return IDENTIFIER_POINTER (ridpointers [rid]);
> > > > }
> > > > +/* Return true if between two named address spaces, whether there is a superset
> > > > + named address space that encompasses both address spaces. If there is a
> > > > + superset, return which address space is the superset. */
> > > > +
> > > > +bool
> > > > +addr_space_superset (addr_space_t as1, addr_space_t as2,
> > > > + addr_space_t * common)
> > > > +{
> > > > + if (as1 == as2)
> > > > + {
> > > > + *common = as1;
> > > > + return true;
> > > > + }
> > > > + else if (targetm.addr_space.subset_p (as1, as2))
> > > > + {
> > > > + *common = as2;
> > > > + return true;
> > > > + }
> > > > + else if (targetm.addr_space.subset_p (as2, as1))
> > > > + {
> > > > + *common = as1;
> > > > + return true;
> > > > + }
> > > > + else
> > > > + return false;
> > > > +}
> > > > +
> > > > /* Push current bindings for the function name VAR_DECLS. */
> > > > void
> > > > @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> > > > return build_nonstandard_integer_type (width, unsignedp);
> > > > }
> > > > +/* Register reserved keyword WORD as qualifier for address space AS. */
> > > > +
> > > > +void
> > > > +c_register_addr_space (const char *word, addr_space_t as)
> > > > +{
> > > > + int rid = RID_FIRST_ADDR_SPACE + as;
> > > > + tree id;
> > > > +
> > > > + /* Address space qualifiers are only supported
> > > > + in C with GNU extensions enabled. */
> > > > + if (c_dialect_objc () || flag_no_asm)
> > > > + return;
> > > > +
> > > > + id = get_identifier (word);
> > > > + C_SET_RID_CODE (id, rid);
> > > > + TREE_LANG_FLAG_0 (id) = 1;
> > > > + ridpointers[rid] = id;
> > > > +}
> > > > +
> > > > /* The C version of the register_builtin_type langhook. */
> > > > void
> > > > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> > > > index 52a85bfb783..d36f9e4975b 100644
> > > > --- a/gcc/c-family/c-common.h
> > > > +++ b/gcc/c-family/c-common.h
> > > > @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
> > > > extern tree (*make_fname_decl) (location_t, tree, int);
> > > > -/* In c-decl.cc and cp/tree.cc. FIXME. */
> > > > -extern void c_register_addr_space (const char *str, addr_space_t as);
> > > > -
> > > > /* In c-common.cc. */
> > > > extern bool in_late_binary_op;
> > > > extern const char *c_addr_space_name (addr_space_t as);
> > > > +extern const char *c_addr_space_name (addr_space_t as);
> > > > +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> > > > extern tree identifier_global_value (tree);
> > > > extern tree identifier_global_tag (tree);
> > > > extern bool names_builtin_p (const char *);
> > > > @@ -952,6 +951,7 @@ extern void c_common_finish (void);
> > > > extern void c_common_parse_file (void);
> > > > extern FILE *get_dump_info (int, dump_flags_t *);
> > > > extern alias_set_type c_common_get_alias_set (tree);
> > > > +extern void c_register_addr_space (const char *, addr_space_t);
> > > > extern void c_register_builtin_type (tree, const char*);
> > > > extern bool c_promoting_integer_type_p (const_tree);
> > > > extern bool self_promoting_args_p (const_tree);
> > > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> > > > index c701f07befe..e1bb4f1cf37 100644
> > > > --- a/gcc/c/c-decl.cc
> > > > +++ b/gcc/c/c-decl.cc
> > > > @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
> > > > ext_block = NULL;
> > > > }
> > > > -/* Register reserved keyword WORD as qualifier for address space AS. */
> > > > -
> > > > -void
> > > > -c_register_addr_space (const char *word, addr_space_t as)
> > > > -{
> > > > - int rid = RID_FIRST_ADDR_SPACE + as;
> > > > - tree id;
> > > > -
> > > > - /* Address space qualifiers are only supported
> > > > - in C with GNU extensions enabled. */
> > > > - if (c_dialect_objc () || flag_no_asm)
> > > > - return;
> > > > -
> > > > - id = get_identifier (word);
> > > > - C_SET_RID_CODE (id, rid);
> > > > - C_IS_RESERVED_WORD (id) = 1;
> > > > - ridpointers [rid] = id;
> > > > -}
> > > > -
> > > > /* Return identifier to look up for omp declare reduction. */
> > > > tree
> > > > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> > > > index b271af9bedb..c4b01368534 100644
> > > > --- a/gcc/c/c-typeck.cc
> > > > +++ b/gcc/c/c-typeck.cc
> > > > @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
> > > > return type;
> > > > }
> > > > -/* Return true if between two named address spaces, whether there is a superset
> > > > - named address space that encompasses both address spaces. If there is a
> > > > - superset, return which address space is the superset. */
> > > > -
> > > > -static bool
> > > > -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> > > > -{
> > > > - if (as1 == as2)
> > > > - {
> > > > - *common = as1;
> > > > - return true;
> > > > - }
> > > > - else if (targetm.addr_space.subset_p (as1, as2))
> > > > - {
> > > > - *common = as2;
> > > > - return true;
> > > > - }
> > > > - else if (targetm.addr_space.subset_p (as2, as1))
> > > > - {
> > > > - *common = as1;
> > > > - return true;
> > > > - }
> > > > - else
> > > > - return false;
> > > > -}
> > > > -
> > > > /* Return a variant of TYPE which has all the type qualifiers of LIKE
> > > > as well as those of TYPE. */
> > > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > > > index 72f4398a8f9..82a6d72f5df 100644
> > > > --- a/gcc/cp/cp-tree.h
> > > > +++ b/gcc/cp/cp-tree.h
> > > > @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
> > > > ds_const,
> > > > ds_volatile,
> > > > ds_restrict,
> > > > + ds_addr_space,
> > > > ds_inline,
> > > > ds_virtual,
> > > > ds_explicit,
> > > > @@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
> > > > cp_storage_class storage_class;
> > > > /* For the __intN declspec, this stores the index into the int_n_* arrays. */
> > > > int int_n_idx;
> > > > + /* The address space that the declaration belongs to. */
> > > > + addr_space_t address_space;
> > > > /* True iff TYPE_SPEC defines a class or enum. */
> > > > BOOL_BITFIELD type_definition_p : 1;
> > > > /* True iff multiple types were (erroneously) specified for this
> > > > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> > > > index 9f78c500a15..fa5a1ddb21d 100644
> > > > --- a/gcc/cp/decl.cc
> > > > +++ b/gcc/cp/decl.cc
> > > > @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> > > > type_quals |= TYPE_QUAL_VOLATILE;
> > > > if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> > > > type_quals |= TYPE_QUAL_RESTRICT;
> > > > + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > > > + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
> > > > return type_quals;
> > > > }
> > > > @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> > > > error_at (declspecs->locations[ds_restrict],
> > > > "%<__restrict%> can only be specified for objects and "
> > > > "functions");
> > > > + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > > > + error_at (declspecs->locations[ds_addr_space],
> > > > + "address space can only be specified for objects and "
> > > > + "functions");
> > > > else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> > > > error_at (declspecs->locations[ds_thread],
> > > > "%<__thread%> can only be specified for objects "
> > > > @@ -14572,6 +14578,57 @@ grokdeclarator (const cp_declarator *declarator,
> > > > if (!processing_template_decl)
> > > > cp_apply_type_quals_to_decl (type_quals, decl);
> > > > + addr_space_t address_space = declspecs->address_space;
> > > > + if (!ADDR_SPACE_GENERIC_P (address_space) && !INDIRECT_TYPE_P (type))
> > > > + {
> > > > + if (decl_context == NORMAL)
> > > > + {
> > > > + switch (storage_class)
> > > > + {
> > > > + case sc_auto:
> > > > + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + break;
> > > > + case sc_register:
> > > > + error ("%qs combined with %<register%> qualifier for %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + break;
> > > > + case sc_none:
> > > > + if (! toplevel_bindings_p ())
> > > > + error ("%qs specified for auto variable %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + break;
> > > > + case sc_mutable:
> > > > + error ("%qs combined with %<mutable%> qualifier for %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + break;
> > > > + case sc_static:
> > > > + case sc_extern:
> > > > + break;
> > > > + default:
> > > > + gcc_unreachable ();
> > > > + }
> > > > + }
> > > > + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
> > > > + {
> > > > + if (name)
> > > > + error ("%qs specified for parameter %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + else
> > > > + error ("%qs specified for unnamed parameter",
> > > > + c_addr_space_name (address_space));
> > > > + }
> > > > + else if (decl_context == FIELD)
> > > > + {
> > > > + if (name)
> > > > + error ("%qs specified for structure field %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + else
> > > > + error ("%qs specified for structure field",
> > > > + c_addr_space_name (address_space));
> > > > + }
> > > > + }
> > > > +
> > > > return decl;
> > > > }
> > > > }
> > > > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> > > > index eb53e0ebeb4..16cb2ff4332 100644
> > > > --- a/gcc/cp/mangle.cc
> > > > +++ b/gcc/cp/mangle.cc
> > > > @@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
> > > > array. */
> > > > cp_cv_quals quals = TYPE_QUALS (type);
> > > > + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
> > > > + {
> > > > + const char *as_name = c_addr_space_name (as);
> > > > + write_char ('U');
> > > > + write_unsigned_number (strlen (as_name));
> > > > + write_string (as_name);
> > > > + ++num_qualifiers;
> > > > + }
> > > > if (quals & TYPE_QUAL_RESTRICT)
> > > > {
> > > > write_char ('r');
> > > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > > > index 763df6f479b..f0169d83ab0 100644
> > > > --- a/gcc/cp/parser.cc
> > > > +++ b/gcc/cp/parser.cc
> > > > @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> > > > postfix_expression = error_mark_node;
> > > > break;
> > > > }
> > > > + if (type != error_mark_node
> > > > + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> > > > + && current_function_decl)
> > > > + {
> > > > + error
> > > > + ("compound literal qualified by address-space qualifier");
> > > > + type = error_mark_node;
> > > > + }
> > > > /* Form the representation of the compound-literal. */
> > > > postfix_expression
> > > > = finish_compound_literal (type, initializer,
> > > > @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
> > > > break;
> > > > }
> > > > +
> > > > + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> > > > + {
> > > > + ds = ds_addr_space;
> > > > + if (is_cv_qualifier)
> > > > + *is_cv_qualifier = true;
> > > > + }
> > > > +
> > > > +
> > > > /* Handle simple keywords. */
> > > > if (ds != ds_last)
> > > > {
> > > > @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> > > > GNU Extension:
> > > > cv-qualifier:
> > > > + address-space-qualifier
> > > > __restrict__
> > > > Returns a bitmask representing the cv-qualifiers. */
> > > > @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> > > > break;
> > > > }
> > > > + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
> > > > + token->keyword <= RID_LAST_ADDR_SPACE)
> > > > + cv_qualifier =
> > > > + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> > > > +
> > > > if (!cv_qualifier)
> > > > break;
> > > > @@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > > > decl_specs->locations[ds] = location;
> > > > if (ds == ds_thread)
> > > > decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> > > > + else if (ds == ds_addr_space)
> > > > + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> > > > }
> > > > else
> > > > {
> > > > @@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > > > error_at (&richloc, "duplicate %qD", token->u.value);
> > > > }
> > > > }
> > > > + else if (ds == ds_addr_space)
> > > > + {
> > > > + addr_space_t as1 = decl_specs->address_space;
> > > > + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> > > > +
> > > > + gcc_rich_location richloc (location);
> > > > + richloc.add_fixit_remove ();
> > > > + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> > > > + && as1 != as2)
> > > > + error_at (&richloc,
> > > > + "conflicting named address spaces (%s vs %s)",
> > > > + c_addr_space_name (as1), c_addr_space_name (as2));
> > > > + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> > > > + error_at (&richloc,
> > > > + "duplicate named address space %s",
> > > > + c_addr_space_name (as1));
> > > > +
> > > > + decl_specs->address_space = as2;
> > > > + }
> > > > else
> > > > {
> > > > static const char *const decl_spec_names[] = {
> > > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > > > index bef31416fb7..c9d1c01f4ce 100644
> > > > --- a/gcc/cp/pt.cc
> > > > +++ b/gcc/cp/pt.cc
> > > > @@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
> > > > static int
> > > > check_cv_quals_for_unify (int strict, tree arg, tree parm)
> > > > {
> > > > - int arg_quals = cp_type_quals (arg);
> > > > - int parm_quals = cp_type_quals (parm);
> > > > + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > > > + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > > > +
> > > > + /* Try to unify ARG's address space into PARM's address space.
> > > > + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> > > > + there are no constraints on address spaces for this type. */
> > > > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > > > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > > > + addr_space_t as_common;
> > > > + addr_space_superset (as_arg, as_parm, &as_common);
> > > > +
> > > > + if (!(as_parm == as_common || as_parm == 0))
> > > > + return 0;
> > > > if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> > > > && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> > > > @@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> > > > arg, parm))
> > > > return unify_cv_qual_mismatch (explain_p, parm, arg);
> > > > + int arg_cv_quals = cp_type_quals (arg);
> > > > + int parm_cv_quals = cp_type_quals (parm);
> > > > +
> > > > + /* If PARM does not contain any address space constraints it can
> > > > + fully match the address space of ARG. However, if PARM contains an
> > > > + address space constraint, it becomes the upper bound. That is,
> > > > + AS_ARG may be promoted to AS_PARM but not the converse. If we
> > > > + ended up here, it means that `check_cv_quals_for_unify' succeeded
> > > > + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> > > > + AS_PARM. */
> > > > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> > > > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> > > > + addr_space_t as_common = as_parm ? 0 : as_arg;
> > > > +
> > > > /* Consider the case where ARG is `const volatile int' and
> > > > PARM is `const T'. Then, T should be `volatile int'. */
> > > > - arg = cp_build_qualified_type_real
> > > > - (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
> > > > + int unified_cv =
> > > > + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> > > > + | ENCODE_QUAL_ADDR_SPACE (as_common);
> > > > + arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
> > > > if (arg == error_mark_node)
> > > > return unify_invalid (explain_p);
> > > > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> > > > index 3b37567cbd7..5e14ac837fc 100644
> > > > --- a/gcc/cp/tree.cc
> > > > +++ b/gcc/cp/tree.cc
> > > > @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
> > > > DECL_CHAIN (t) = NULL_TREE;
> > > > }
> > > > -/* Stub for c-common. Please keep in sync with c-decl.cc.
> > > > - FIXME: If address space support is target specific, then this
> > > > - should be a C target hook. But currently this is not possible,
> > > > - because this function is called via REGISTER_TARGET_PRAGMAS. */
> > > > -void
> > > > -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> > > > -{
> > > > -}
> > > > -
> > > > /* Return the number of operands in T that we care about for things like
> > > > mangling. */
> > > > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> > > > index ceb80d9744f..7d810c6a12c 100644
> > > > --- a/gcc/cp/typeck.cc
> > > > +++ b/gcc/cp/typeck.cc
> > > > @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
> > > > else
> > > > return error_mark_node;
> > > > }
> > > > + /* If possible merge the address space into the superset of the address
> > > > + spaces of t1 and t2, or raise an error. */
> > > > + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> > > > + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> > > > + addr_space_t as_common;
> > > > +
> > > > + /* If the two named address spaces are different, determine the common
> > > > + superset address space. If there isn't one, raise an error. */
> > > > + if (!addr_space_superset (as_t1, as_t2, &as_common))
> > > > + {
> > > > + as_common = as_t1;
> > > > + error_at (location,
> > > > + "%qT and %qT are in disjoint named address spaces",
> > > > + t1, t2);
> > > > + }
> > > > result_type
> > > > = cp_build_qualified_type (void_type_node,
> > > > - (cp_type_quals (TREE_TYPE (t1))
> > > > - | cp_type_quals (TREE_TYPE (t2))));
> > > > + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
> > > > + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
> > > > + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> > > > result_type = build_pointer_type (result_type);
> > > > /* Merge the attributes. */
> > > > attributes = (*targetm.merge_type_attributes) (t1, t2);
> > > > @@ -1579,7 +1595,9 @@ comptypes (tree t1, tree t2, int strict)
> > > > }
> > > > /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
> > > > - top-level qualifiers. */
> > > > + top-level qualifiers, except for named address spaces. If the pointers point
> > > > + to different named addresses spaces, then we must determine if one address
> > > > + space is a subset of the other. */
> > > > bool
> > > > same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > > > @@ -1589,6 +1607,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > > > if (type1 == type2)
> > > > return true;
> > > > + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
> > > > + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
> > > > + addr_space_t as_common;
> > > > +
> > > > + /* Fail if pointers point to incompatible address spaces. */
> > > > + if (!addr_space_superset (as_type1, as_type2, &as_common))
> > > > + return false;
> > > > +
> > > > type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
> > > > type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
> > > > return same_type_p (type1, type2);
> > > > @@ -6374,10 +6400,32 @@ static tree
> > > > pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
> > > > tsubst_flags_t complain, tree *instrument_expr)
> > > > {
> > > > - tree result, inttype;
> > > > tree restype = ptrdiff_type_node;
> > > > + tree result, inttype;
> > > > +
> > > > + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
> > > > + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
> > > > tree target_type = TREE_TYPE (ptrtype);
> > > > + /* If the operands point into different address spaces, we need to
> > > > + explicitly convert them to pointers into the common address space
> > > > + before we can subtract the numerical address values. */
> > > > + if (as0 != as1)
> > > > + {
> > > > + addr_space_t as_common;
> > > > + tree common_type;
> > > > +
> > > > + /* Determine the common superset address space. This is guaranteed
> > > > + to exist because the caller verified that comp_target_types
> > > > + returned non-zero. */
> > > > + if (!addr_space_superset (as0, as1, &as_common))
> > > > + gcc_unreachable ();
> > > > +
> > > > + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
> > > > + op0 = convert (common_type, op0);
> > > > + op1 = convert (common_type, op1);
> > > > + }
> > > > +
> > > > if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
> > > > return error_mark_node;
> > > > @@ -10805,6 +10853,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> > > > to_more_cv_qualified = true;
> > > > }
> > > > + /* Warn about conversions between pointers to disjoint
> > > > + address spaces. */
> > > > + if (TREE_CODE (from) == POINTER_TYPE
> > > > + && TREE_CODE (to) == POINTER_TYPE)
> > > > + {
> > > > + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> > > > + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> > > > + addr_space_t as_common;
> > > > +
> > > > + if (!addr_space_superset (as_to, as_from, &as_common))
> > > > + return false;
> > > > + }
> > > > +
> > > > if (constp > 0)
> > > > constp &= TYPE_READONLY (to);
> > > > }
> > > > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > > > index ac3db88566d..1e0d436c02c 100644
> > > > --- a/gcc/doc/extend.texi
> > > > +++ b/gcc/doc/extend.texi
> > > > @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> > > > @section Named Address Spaces
> > > > @cindex Named Address Spaces
> > > > -As an extension, GNU C supports named address spaces as
> > > > +As an extension, GNU C and GNU C++ support named address spaces as
> > > > defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> > > > address spaces in GCC will evolve as the draft technical report
> > > > changes. Calling conventions for any target might also change. At
> > > > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > > > new file mode 100644
> > > > index 00000000000..c01f8d6054a
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > > > @@ -0,0 +1,10 @@
> > > > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > > > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> > > > +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> > > > +
> > > > +int f (int volatile __seg_fs *a)
> > > > +{
> > > > + return *a;
> > > > +}
> > > > +
> > > > +int main () {}
> > > > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > > > new file mode 100644
> > > > index 00000000000..862bbbdcdf2
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > > > @@ -0,0 +1,9 @@
> > > > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > > > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> > > > +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
> > > > +
> > > > +template <class T>
> > > > +int f (T *p) { return *p; }
> > > > +int g (__seg_fs int *p) { return *p; }
> > > > +__seg_fs int *a;
> > > > +int main() { f(a); }
> > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > > > new file mode 100644
> > > > index 00000000000..c04d2f497da
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > > > @@ -0,0 +1,5 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +__seg_fs char a, b, c;
> > > > +__seg_fs const int *p;
> > > > +static /* give internal linkage to the following anonymous struct */
> > > > +__seg_fs struct { int a; char b; } * __seg_gs q;
> > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > > > new file mode 100644
> > > > index 00000000000..86c02d1e7f5
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > > > @@ -0,0 +1,20 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +int __seg_fs * fs1;
> > > > +int __seg_fs * fs2;
> > > > +float __seg_gs * gs1;
> > > > +float __seg_gs * gs2;
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
> > > > + fs1 - fs2;
> > > > + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
> > > > + fs1 == fs2;
> > > > + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > > > + fs1 = fs2;
> > > > + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
> > > > + fs1 > fs2;
> > > > + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > > > new file mode 100644
> > > > index 00000000000..12d7975e560
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > > > @@ -0,0 +1,31 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +// { dg-prune-output "does not allow .register. storage class specifier" }
> > > > +int __seg_fs * outer_b;
> > > > +
> > > > +struct s {
> > > > + __seg_fs int * ok;
> > > > + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
> > > > +};
> > > > +
> > > > +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
> > > > +
> > > > +namespace ns_a
> > > > +{
> > > > + int __seg_fs * inner_b;
> > > > +
> > > > + template<typename T>
> > > > + int f (T &a) { return a; }
> > > > + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
> > > > + int h (__seg_fs int *a) { return *a; }
> > > > +}
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
> > > > + static __seg_gs int static_gs;
> > > > + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
> > > > + __seg_fs int *pa = outer_b;
> > > > + __seg_fs int& ra = *ns_a::inner_b;
> > > > + return ns_a::f(ra) + ns_a::f(*pa);
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> > > > new file mode 100644
> > > > index 00000000000..ebb6316054a
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> > > > @@ -0,0 +1,9 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +
> > > > +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > > > new file mode 100644
> > > > index 00000000000..2e8ee32a885
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > > > @@ -0,0 +1,10 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +// { dg-options "-std=gnu++98" }
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + struct foo {int a; char b[2];} structure;
> > > > + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > > > new file mode 100644
> > > > index 00000000000..5b2c0f28078
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > > > @@ -0,0 +1,9 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +
> > > > +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > > > new file mode 100644
> > > > index 00000000000..ae9f4de0e1f
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > > > @@ -0,0 +1,8 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +
> > > > +template <class T>
> > > > +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> > > > + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> > > > +__seg_fs int *a;
> > > > +int main() { f(a); } // { dg-error "no matching" }
> > > > +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> > > > diff --git a/gcc/tree.h b/gcc/tree.h
> > > > index 8844471e9a5..b7da4c5141a 100644
> > > > --- a/gcc/tree.h
> > > > +++ b/gcc/tree.h
> > > > @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
> > > > /* Encode/decode the named memory support as part of the qualifier. If more
> > > > than 8 qualifiers are added, these macros need to be adjusted. */
> > > > -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> > > > +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> > > > #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
> > > > /* Return all qualifiers except for the address space qualifiers. */
> > > >
> > > >
> > > >
> > > >
> > >
> > >
> > >
> > >
> > >
> > >
> > >
> >
> >
> >
> >
> >
>
>
>
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 16:02 ` Paul Iannetta
@ 2022-10-13 19:41 ` Jason Merrill
2022-10-13 21:57 ` Paul Iannetta
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-13 19:41 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/13/22 12:02, Paul Iannetta wrote:
> On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
>> On 10/13/22 11:23, Paul Iannetta wrote:
>>> On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
>>>> On 10/12/22 20:52, Paul Iannetta wrote:
>>>>> On Tue, Oct 11, 2022 at 09:49:43PM -0400, Jason Merrill wrote:
>>>>>>
>>>>>> It surprises that this is the only place we complain about an object with an
>>>>>> address-space qualifier. Shouldn't we also complain about e.g. automatic
>>>>>> variables/parameters or non-static data members with address-space qualified
>>>>>> type?
>>>>>>
>>>>>
>>>>> Indeed, I was missing quite a few things here. Thanks.
>>>>> I used the draft as basis this time and imported from the C
>>>>> implementation the relevant parts. This time, the errors get properly
>>>>> emitted when an address space is unduly specified; and comparisons,
>>>>> assignments and comparisons are taken care of.
>>>>>
>>>>> There are quite a few things I would like to clarify concerning some
>>>>> implementation details.
>>>>> - A variable with automatic storage (which is neither a pointer nor
>>>>> a reference) cannot be qualified with an address space. I detect
>>>>> this by the combination of `sc_none' and `! toplevel_bindings_p ()',
>>>>> but I've also seen the use of `at_function_scope' at other places.
>>>>> And I'm unsure which one is appropriate here.
>>>>> This detection happens at the very end of grokdeclarator because I
>>>>> need to know that the type is a pointer, which is not know until
>>>>> very late in the function.
>>>>
>>>> At that point you have the decl, and you can ask directly what its storage
>>>> duration is, perhaps using decl_storage_duration.
>>>>
>>>> But why do you need to know whether the type is a pointer? The attribute
>>>> applies to the target type of the pointer, not the pointer type. I think
>>>> the problem is that you're looking at declspecs when you ought to be looking
>>>> at type_quals.
>>>
>>> I need to know that the base type is a pointer to reject invalid
>>> declarations such as:
>>>
>>> int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
>>>
>>> because parameters and auto variables can have an address space
>>> qualifier only if they are pointer or reference type, which I can't
>>> tell only from type_quals.
>>
>> But "int *__seg_fs a" is just as invalid as the above; the difference is not
>> whether a is a pointer, but whether the address-space-qualified is the type
>> of a itself or some sub-type.
>
> I agree that "int * __seg_fs a" is invalid but it is accepted by the C
> front-end, and by clang (both C and C++), the behavior is that the
> address-name is silently ignored.
Hmm, that sounds like a bug; in that case, presumably the user meant to
qualify the pointed-to type, and silently ignoring seems unlikely to
give the effect they want.
>> You need to look at the qualifiers on type (which should also be the ones in
>> type_quals), not the qualifiers in the declspecs.
>
> I'll have another look, thanks.
>
>>>>> - I'm having some trouble deciding whether I include those three
>>>>> stub programs as tests, they all compile fine and clang accepts
>>>>> them as well.
>>>>
>>>> Why not?
>>>
>>> I thought they were pretty contrived, since it does not make much
>>> sense to strip address space qualifiers, even though it does prove
>>> that the implementation support those contrived but valid uses.
>>
>> Testcases are full of contrived examples testing corner cases. :)
>>
>>>>
>>>>> Ex1:
>>>>> ```
>>>>> int __seg_fs * fs1;
>>>>> int __seg_gs * gs1;
>>>>>
>>>>> template<typename T> struct strip;
>>>>> template<typename T> struct strip<__seg_fs T *> { typedef T type; };
>>>>> template<typename T> struct strip<__seg_gs T *> { typedef T type; };
>>>>>
>>>>> int
>>>>> main ()
>>>>> {
>>>>> *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
>>>>> return 0;
>>>>> }
>>>>> ```
>>>>>
>>>>> Ex2:
>>>>> ```
>>>>> int __seg_fs * fs1;
>>>>> int __seg_fs * fs2;
>>>>>
>>>>> template<typename T, typename U> auto f (T __seg_fs * a, U __seg_gs * b) { return a; }
>>>>> template<typename T, typename U> auto f (T __seg_gs * a, U __seg_fs * b) { return a; }
>>>>>
>>>>> int
>>>>> main ()
>>>>> {
>>>>> f (fs1, gs1);
>>>>> f (gs1, fs1);
>>>>> return 0;
>>>>> }
>>>>> ```
>>>>>
>>>>> Ex3:
>>>>> ```
>>>>> int __seg_fs * fs1;
>>>>> int __seg_gs * gs1;
>>>>>
>>>>> template<typename T, typename U>
>>>>> auto f (T __seg_fs * a, U __seg_gs * b)
>>>>> {
>>>>> return *(T *) a == *(U *) b;
>>>>> }
>>>>>
>>>>> int
>>>>> main ()
>>>>> {
>>>>> return f (fs1, gs1);
>>>>> }
>>>>> ```
>>>>>
>>>>>
>>>>> Add support for custom address spaces in C++
>>>>>
>>>>> gcc/
>>>>> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>>>>>
>>>>> gcc/c/
>>>>> * c-decl.cc: Remove c_register_addr_space.
>>>>>
>>>>> gcc/c-family/
>>>>> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
>>>>> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
>>>>> * c-common.h: Remove the FIXME.
>>>>> (addr_space_superset): New declaration.
>>>>>
>>>>> gcc/cp/
>>>>> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
>>>>> (struct cp_decl_specifier_seq): Likewise.
>>>>> * decl.cc (get_type_quals): Likewise.
>>>>> (check_tag_decl): Likewise.
>>>>> (grokdeclarator): Likewise.
>>>>> * parser.cc (cp_parser_type_specifier): Likewise.
>>>>> (cp_parser_cv_qualifier_seq_opt): Likewise.
>>>>> (cp_parser_postfix_expression): Likewise.
>>>>> (cp_parser_type_specifier): Likewise.
>>>>> (set_and_check_decl_spec_loc): Likewise.
>>>>> * typeck.cc (composite_pointer_type): Likewise
>>>>> (comp_ptr_ttypes_real): Likewise.
>>>>> (same_type_ignoring_top_level_qualifiers_p): Likewise.
>>>>> * pt.cc (check_cv_quals_for_unify): Likewise.
>>>>> (unify): Likewise.
>>>>> * tree.cc: Remove c_register_addr_space stub.
>>>>> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
>>>>> using the extended qualifier notation.
>>>>>
>>>>> gcc/doc
>>>>> * extend.texi (Named Address Spaces): add a mention about C++
>>>>> support.
>>>>>
>>>>> gcc/testsuite/
>>>>> * g++.dg/abi/mangle-addr-space1.C: New test.
>>>>> * g++.dg/abi/mangle-addr-space2.C: New test.
>>>>> * g++.dg/parse/addr-space.C: New test.
>>>>> * g++.dg/parse/addr-space1.C: New test.
>>>>> * g++.dg/parse/addr-space2.C: New test.
>>>>> * g++.dg/parse/template/spec-addr-space.C: New test.
>>>>> * g++.dg/ext/addr-space-decl.C: New test.
>>>>> * g++.dg/ext/addr-space-ref.C: New test.
>>>>> * g++.dg/ext/addr-space-ops.C: New test.
>>>>>
>>>>> # Please enter the commit message for your changes. Lines starting
>>>>> # with '#' will be ignored, and an empty message aborts the commit.
>>>>> #
>>>>> # Date: Sun Oct 9 16:02:22 2022 +0200
>>>>> #
>>>>> # On branch releases/gcc-12
>>>>> # Your branch is ahead of 'origin/releases/gcc-12' by 2 commits.
>>>>> # (use "git push" to publish your local commits)
>>>>> #
>>>>> # Changes to be committed:
>>>>> # modified: gcc/c-family/c-common.cc
>>>>> # modified: gcc/c-family/c-common.h
>>>>> # modified: gcc/c/c-decl.cc
>>>>> # modified: gcc/c/c-typeck.cc
>>>>> # modified: gcc/cp/cp-tree.h
>>>>> # modified: gcc/cp/decl.cc
>>>>> # modified: gcc/cp/mangle.cc
>>>>> # modified: gcc/cp/parser.cc
>>>>> # modified: gcc/cp/pt.cc
>>>>> # modified: gcc/cp/tree.cc
>>>>> # modified: gcc/cp/typeck.cc
>>>>> # modified: gcc/doc/extend.texi
>>>>> # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>>>> # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>>>> # new file: gcc/testsuite/g++.dg/parse/addr-space.C
>>>>> # new file: gcc/testsuite/g++.dg/parse/addr-space1.C
>>>>> # new file: gcc/testsuite/g++.dg/parse/addr-space2.C
>>>>> # new file: gcc/testsuite/g++.dg/template/spec-addr-space.C
>>>>> # modified: gcc/tree.h
>>>>> #
>>>>> # Untracked files:
>>>>> # Makefile
>>>>> # addr-space-decl.C
>>>>> # addr-space-decl.s
>>>>> # addr-space-ops.C
>>>>> # addr-space-ref.C
>>>>> # addr-space-template.C
>>>>> # addr-space-template.s
>>>>> # addr-space-template2.C
>>>>> # addr-space-template2.C.006t.gimple
>>>>> # addr-space-template2.s
>>>>> # addr-space-traits.C
>>>>> # addr-space4.C
>>>>> # addr-space4.C.006t.gimple
>>>>> # addr-space4.s
>>>>> # build-x86_64-pc-linux-gnu/
>>>>> # compare
>>>>> # host-x86_64-pc-linux-gnu/
>>>>> # mangle-addr-space1.s
>>>>> # prev-x86_64-pc-linux-gnu/
>>>>> # rename
>>>>> # stage1-x86_64-pc-linux-gnu/
>>>>> # stage_current
>>>>> # stage_final
>>>>> # stage_last
>>>>> # x86_64-pc-linux-gnu/
>>>>> #
>>>>> # ------------------------ >8 ------------------------
>>>>> # Do not modify or remove the line above.
>>>>> # Everything below it will be ignored.
>>>>> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
>>>>> index bb0544eeaea..ff1146ecc25 100644
>>>>> --- a/gcc/c-family/c-common.cc
>>>>> +++ b/gcc/c-family/c-common.cc
>>>>> @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
>>>>> return IDENTIFIER_POINTER (ridpointers [rid]);
>>>>> }
>>>>> +/* Return true if between two named address spaces, whether there is a superset
>>>>> + named address space that encompasses both address spaces. If there is a
>>>>> + superset, return which address space is the superset. */
>>>>> +
>>>>> +bool
>>>>> +addr_space_superset (addr_space_t as1, addr_space_t as2,
>>>>> + addr_space_t * common)
>>>>> +{
>>>>> + if (as1 == as2)
>>>>> + {
>>>>> + *common = as1;
>>>>> + return true;
>>>>> + }
>>>>> + else if (targetm.addr_space.subset_p (as1, as2))
>>>>> + {
>>>>> + *common = as2;
>>>>> + return true;
>>>>> + }
>>>>> + else if (targetm.addr_space.subset_p (as2, as1))
>>>>> + {
>>>>> + *common = as1;
>>>>> + return true;
>>>>> + }
>>>>> + else
>>>>> + return false;
>>>>> +}
>>>>> +
>>>>> /* Push current bindings for the function name VAR_DECLS. */
>>>>> void
>>>>> @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
>>>>> return build_nonstandard_integer_type (width, unsignedp);
>>>>> }
>>>>> +/* Register reserved keyword WORD as qualifier for address space AS. */
>>>>> +
>>>>> +void
>>>>> +c_register_addr_space (const char *word, addr_space_t as)
>>>>> +{
>>>>> + int rid = RID_FIRST_ADDR_SPACE + as;
>>>>> + tree id;
>>>>> +
>>>>> + /* Address space qualifiers are only supported
>>>>> + in C with GNU extensions enabled. */
>>>>> + if (c_dialect_objc () || flag_no_asm)
>>>>> + return;
>>>>> +
>>>>> + id = get_identifier (word);
>>>>> + C_SET_RID_CODE (id, rid);
>>>>> + TREE_LANG_FLAG_0 (id) = 1;
>>>>> + ridpointers[rid] = id;
>>>>> +}
>>>>> +
>>>>> /* The C version of the register_builtin_type langhook. */
>>>>> void
>>>>> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
>>>>> index 52a85bfb783..d36f9e4975b 100644
>>>>> --- a/gcc/c-family/c-common.h
>>>>> +++ b/gcc/c-family/c-common.h
>>>>> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>>>>> extern tree (*make_fname_decl) (location_t, tree, int);
>>>>> -/* In c-decl.cc and cp/tree.cc. FIXME. */
>>>>> -extern void c_register_addr_space (const char *str, addr_space_t as);
>>>>> -
>>>>> /* In c-common.cc. */
>>>>> extern bool in_late_binary_op;
>>>>> extern const char *c_addr_space_name (addr_space_t as);
>>>>> +extern const char *c_addr_space_name (addr_space_t as);
>>>>> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
>>>>> extern tree identifier_global_value (tree);
>>>>> extern tree identifier_global_tag (tree);
>>>>> extern bool names_builtin_p (const char *);
>>>>> @@ -952,6 +951,7 @@ extern void c_common_finish (void);
>>>>> extern void c_common_parse_file (void);
>>>>> extern FILE *get_dump_info (int, dump_flags_t *);
>>>>> extern alias_set_type c_common_get_alias_set (tree);
>>>>> +extern void c_register_addr_space (const char *, addr_space_t);
>>>>> extern void c_register_builtin_type (tree, const char*);
>>>>> extern bool c_promoting_integer_type_p (const_tree);
>>>>> extern bool self_promoting_args_p (const_tree);
>>>>> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
>>>>> index c701f07befe..e1bb4f1cf37 100644
>>>>> --- a/gcc/c/c-decl.cc
>>>>> +++ b/gcc/c/c-decl.cc
>>>>> @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
>>>>> ext_block = NULL;
>>>>> }
>>>>> -/* Register reserved keyword WORD as qualifier for address space AS. */
>>>>> -
>>>>> -void
>>>>> -c_register_addr_space (const char *word, addr_space_t as)
>>>>> -{
>>>>> - int rid = RID_FIRST_ADDR_SPACE + as;
>>>>> - tree id;
>>>>> -
>>>>> - /* Address space qualifiers are only supported
>>>>> - in C with GNU extensions enabled. */
>>>>> - if (c_dialect_objc () || flag_no_asm)
>>>>> - return;
>>>>> -
>>>>> - id = get_identifier (word);
>>>>> - C_SET_RID_CODE (id, rid);
>>>>> - C_IS_RESERVED_WORD (id) = 1;
>>>>> - ridpointers [rid] = id;
>>>>> -}
>>>>> -
>>>>> /* Return identifier to look up for omp declare reduction. */
>>>>> tree
>>>>> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
>>>>> index b271af9bedb..c4b01368534 100644
>>>>> --- a/gcc/c/c-typeck.cc
>>>>> +++ b/gcc/c/c-typeck.cc
>>>>> @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
>>>>> return type;
>>>>> }
>>>>> -/* Return true if between two named address spaces, whether there is a superset
>>>>> - named address space that encompasses both address spaces. If there is a
>>>>> - superset, return which address space is the superset. */
>>>>> -
>>>>> -static bool
>>>>> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
>>>>> -{
>>>>> - if (as1 == as2)
>>>>> - {
>>>>> - *common = as1;
>>>>> - return true;
>>>>> - }
>>>>> - else if (targetm.addr_space.subset_p (as1, as2))
>>>>> - {
>>>>> - *common = as2;
>>>>> - return true;
>>>>> - }
>>>>> - else if (targetm.addr_space.subset_p (as2, as1))
>>>>> - {
>>>>> - *common = as1;
>>>>> - return true;
>>>>> - }
>>>>> - else
>>>>> - return false;
>>>>> -}
>>>>> -
>>>>> /* Return a variant of TYPE which has all the type qualifiers of LIKE
>>>>> as well as those of TYPE. */
>>>>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>>>>> index 72f4398a8f9..82a6d72f5df 100644
>>>>> --- a/gcc/cp/cp-tree.h
>>>>> +++ b/gcc/cp/cp-tree.h
>>>>> @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
>>>>> ds_const,
>>>>> ds_volatile,
>>>>> ds_restrict,
>>>>> + ds_addr_space,
>>>>> ds_inline,
>>>>> ds_virtual,
>>>>> ds_explicit,
>>>>> @@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
>>>>> cp_storage_class storage_class;
>>>>> /* For the __intN declspec, this stores the index into the int_n_* arrays. */
>>>>> int int_n_idx;
>>>>> + /* The address space that the declaration belongs to. */
>>>>> + addr_space_t address_space;
>>>>> /* True iff TYPE_SPEC defines a class or enum. */
>>>>> BOOL_BITFIELD type_definition_p : 1;
>>>>> /* True iff multiple types were (erroneously) specified for this
>>>>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
>>>>> index 9f78c500a15..fa5a1ddb21d 100644
>>>>> --- a/gcc/cp/decl.cc
>>>>> +++ b/gcc/cp/decl.cc
>>>>> @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
>>>>> type_quals |= TYPE_QUAL_VOLATILE;
>>>>> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
>>>>> type_quals |= TYPE_QUAL_RESTRICT;
>>>>> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>>>> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>>>>> return type_quals;
>>>>> }
>>>>> @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
>>>>> error_at (declspecs->locations[ds_restrict],
>>>>> "%<__restrict%> can only be specified for objects and "
>>>>> "functions");
>>>>> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>>>> + error_at (declspecs->locations[ds_addr_space],
>>>>> + "address space can only be specified for objects and "
>>>>> + "functions");
>>>>> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
>>>>> error_at (declspecs->locations[ds_thread],
>>>>> "%<__thread%> can only be specified for objects "
>>>>> @@ -14572,6 +14578,57 @@ grokdeclarator (const cp_declarator *declarator,
>>>>> if (!processing_template_decl)
>>>>> cp_apply_type_quals_to_decl (type_quals, decl);
>>>>> + addr_space_t address_space = declspecs->address_space;
>>>>> + if (!ADDR_SPACE_GENERIC_P (address_space) && !INDIRECT_TYPE_P (type))
>>>>> + {
>>>>> + if (decl_context == NORMAL)
>>>>> + {
>>>>> + switch (storage_class)
>>>>> + {
>>>>> + case sc_auto:
>>>>> + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + break;
>>>>> + case sc_register:
>>>>> + error ("%qs combined with %<register%> qualifier for %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + break;
>>>>> + case sc_none:
>>>>> + if (! toplevel_bindings_p ())
>>>>> + error ("%qs specified for auto variable %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + break;
>>>>> + case sc_mutable:
>>>>> + error ("%qs combined with %<mutable%> qualifier for %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + break;
>>>>> + case sc_static:
>>>>> + case sc_extern:
>>>>> + break;
>>>>> + default:
>>>>> + gcc_unreachable ();
>>>>> + }
>>>>> + }
>>>>> + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
>>>>> + {
>>>>> + if (name)
>>>>> + error ("%qs specified for parameter %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + else
>>>>> + error ("%qs specified for unnamed parameter",
>>>>> + c_addr_space_name (address_space));
>>>>> + }
>>>>> + else if (decl_context == FIELD)
>>>>> + {
>>>>> + if (name)
>>>>> + error ("%qs specified for structure field %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + else
>>>>> + error ("%qs specified for structure field",
>>>>> + c_addr_space_name (address_space));
>>>>> + }
>>>>> + }
>>>>> +
>>>>> return decl;
>>>>> }
>>>>> }
>>>>> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
>>>>> index eb53e0ebeb4..16cb2ff4332 100644
>>>>> --- a/gcc/cp/mangle.cc
>>>>> +++ b/gcc/cp/mangle.cc
>>>>> @@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
>>>>> array. */
>>>>> cp_cv_quals quals = TYPE_QUALS (type);
>>>>> + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
>>>>> + {
>>>>> + const char *as_name = c_addr_space_name (as);
>>>>> + write_char ('U');
>>>>> + write_unsigned_number (strlen (as_name));
>>>>> + write_string (as_name);
>>>>> + ++num_qualifiers;
>>>>> + }
>>>>> if (quals & TYPE_QUAL_RESTRICT)
>>>>> {
>>>>> write_char ('r');
>>>>> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
>>>>> index 763df6f479b..f0169d83ab0 100644
>>>>> --- a/gcc/cp/parser.cc
>>>>> +++ b/gcc/cp/parser.cc
>>>>> @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
>>>>> postfix_expression = error_mark_node;
>>>>> break;
>>>>> }
>>>>> + if (type != error_mark_node
>>>>> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
>>>>> + && current_function_decl)
>>>>> + {
>>>>> + error
>>>>> + ("compound literal qualified by address-space qualifier");
>>>>> + type = error_mark_node;
>>>>> + }
>>>>> /* Form the representation of the compound-literal. */
>>>>> postfix_expression
>>>>> = finish_compound_literal (type, initializer,
>>>>> @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
>>>>> break;
>>>>> }
>>>>> +
>>>>> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
>>>>> + {
>>>>> + ds = ds_addr_space;
>>>>> + if (is_cv_qualifier)
>>>>> + *is_cv_qualifier = true;
>>>>> + }
>>>>> +
>>>>> +
>>>>> /* Handle simple keywords. */
>>>>> if (ds != ds_last)
>>>>> {
>>>>> @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
>>>>> GNU Extension:
>>>>> cv-qualifier:
>>>>> + address-space-qualifier
>>>>> __restrict__
>>>>> Returns a bitmask representing the cv-qualifiers. */
>>>>> @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
>>>>> break;
>>>>> }
>>>>> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
>>>>> + token->keyword <= RID_LAST_ADDR_SPACE)
>>>>> + cv_qualifier =
>>>>> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>>>>> +
>>>>> if (!cv_qualifier)
>>>>> break;
>>>>> @@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>>>> decl_specs->locations[ds] = location;
>>>>> if (ds == ds_thread)
>>>>> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
>>>>> + else if (ds == ds_addr_space)
>>>>> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
>>>>> }
>>>>> else
>>>>> {
>>>>> @@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>>>> error_at (&richloc, "duplicate %qD", token->u.value);
>>>>> }
>>>>> }
>>>>> + else if (ds == ds_addr_space)
>>>>> + {
>>>>> + addr_space_t as1 = decl_specs->address_space;
>>>>> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
>>>>> +
>>>>> + gcc_rich_location richloc (location);
>>>>> + richloc.add_fixit_remove ();
>>>>> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
>>>>> + && as1 != as2)
>>>>> + error_at (&richloc,
>>>>> + "conflicting named address spaces (%s vs %s)",
>>>>> + c_addr_space_name (as1), c_addr_space_name (as2));
>>>>> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
>>>>> + error_at (&richloc,
>>>>> + "duplicate named address space %s",
>>>>> + c_addr_space_name (as1));
>>>>> +
>>>>> + decl_specs->address_space = as2;
>>>>> + }
>>>>> else
>>>>> {
>>>>> static const char *const decl_spec_names[] = {
>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>> index bef31416fb7..c9d1c01f4ce 100644
>>>>> --- a/gcc/cp/pt.cc
>>>>> +++ b/gcc/cp/pt.cc
>>>>> @@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
>>>>> static int
>>>>> check_cv_quals_for_unify (int strict, tree arg, tree parm)
>>>>> {
>>>>> - int arg_quals = cp_type_quals (arg);
>>>>> - int parm_quals = cp_type_quals (parm);
>>>>> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>>>> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>>>> +
>>>>> + /* Try to unify ARG's address space into PARM's address space.
>>>>> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
>>>>> + there are no constraints on address spaces for this type. */
>>>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>>>> + addr_space_t as_common;
>>>>> + addr_space_superset (as_arg, as_parm, &as_common);
>>>>> +
>>>>> + if (!(as_parm == as_common || as_parm == 0))
>>>>> + return 0;
>>>>> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
>>>>> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
>>>>> @@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
>>>>> arg, parm))
>>>>> return unify_cv_qual_mismatch (explain_p, parm, arg);
>>>>> + int arg_cv_quals = cp_type_quals (arg);
>>>>> + int parm_cv_quals = cp_type_quals (parm);
>>>>> +
>>>>> + /* If PARM does not contain any address space constraints it can
>>>>> + fully match the address space of ARG. However, if PARM contains an
>>>>> + address space constraint, it becomes the upper bound. That is,
>>>>> + AS_ARG may be promoted to AS_PARM but not the converse. If we
>>>>> + ended up here, it means that `check_cv_quals_for_unify' succeeded
>>>>> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
>>>>> + AS_PARM. */
>>>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
>>>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
>>>>> + addr_space_t as_common = as_parm ? 0 : as_arg;
>>>>> +
>>>>> /* Consider the case where ARG is `const volatile int' and
>>>>> PARM is `const T'. Then, T should be `volatile int'. */
>>>>> - arg = cp_build_qualified_type_real
>>>>> - (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
>>>>> + int unified_cv =
>>>>> + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
>>>>> + | ENCODE_QUAL_ADDR_SPACE (as_common);
>>>>> + arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
>>>>> if (arg == error_mark_node)
>>>>> return unify_invalid (explain_p);
>>>>> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
>>>>> index 3b37567cbd7..5e14ac837fc 100644
>>>>> --- a/gcc/cp/tree.cc
>>>>> +++ b/gcc/cp/tree.cc
>>>>> @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
>>>>> DECL_CHAIN (t) = NULL_TREE;
>>>>> }
>>>>> -/* Stub for c-common. Please keep in sync with c-decl.cc.
>>>>> - FIXME: If address space support is target specific, then this
>>>>> - should be a C target hook. But currently this is not possible,
>>>>> - because this function is called via REGISTER_TARGET_PRAGMAS. */
>>>>> -void
>>>>> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
>>>>> -{
>>>>> -}
>>>>> -
>>>>> /* Return the number of operands in T that we care about for things like
>>>>> mangling. */
>>>>> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
>>>>> index ceb80d9744f..7d810c6a12c 100644
>>>>> --- a/gcc/cp/typeck.cc
>>>>> +++ b/gcc/cp/typeck.cc
>>>>> @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
>>>>> else
>>>>> return error_mark_node;
>>>>> }
>>>>> + /* If possible merge the address space into the superset of the address
>>>>> + spaces of t1 and t2, or raise an error. */
>>>>> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
>>>>> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
>>>>> + addr_space_t as_common;
>>>>> +
>>>>> + /* If the two named address spaces are different, determine the common
>>>>> + superset address space. If there isn't one, raise an error. */
>>>>> + if (!addr_space_superset (as_t1, as_t2, &as_common))
>>>>> + {
>>>>> + as_common = as_t1;
>>>>> + error_at (location,
>>>>> + "%qT and %qT are in disjoint named address spaces",
>>>>> + t1, t2);
>>>>> + }
>>>>> result_type
>>>>> = cp_build_qualified_type (void_type_node,
>>>>> - (cp_type_quals (TREE_TYPE (t1))
>>>>> - | cp_type_quals (TREE_TYPE (t2))));
>>>>> + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
>>>>> + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
>>>>> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
>>>>> result_type = build_pointer_type (result_type);
>>>>> /* Merge the attributes. */
>>>>> attributes = (*targetm.merge_type_attributes) (t1, t2);
>>>>> @@ -1579,7 +1595,9 @@ comptypes (tree t1, tree t2, int strict)
>>>>> }
>>>>> /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
>>>>> - top-level qualifiers. */
>>>>> + top-level qualifiers, except for named address spaces. If the pointers point
>>>>> + to different named addresses spaces, then we must determine if one address
>>>>> + space is a subset of the other. */
>>>>> bool
>>>>> same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>>>> @@ -1589,6 +1607,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>>>> if (type1 == type2)
>>>>> return true;
>>>>> + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
>>>>> + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
>>>>> + addr_space_t as_common;
>>>>> +
>>>>> + /* Fail if pointers point to incompatible address spaces. */
>>>>> + if (!addr_space_superset (as_type1, as_type2, &as_common))
>>>>> + return false;
>>>>> +
>>>>> type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
>>>>> type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
>>>>> return same_type_p (type1, type2);
>>>>> @@ -6374,10 +6400,32 @@ static tree
>>>>> pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
>>>>> tsubst_flags_t complain, tree *instrument_expr)
>>>>> {
>>>>> - tree result, inttype;
>>>>> tree restype = ptrdiff_type_node;
>>>>> + tree result, inttype;
>>>>> +
>>>>> + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
>>>>> + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
>>>>> tree target_type = TREE_TYPE (ptrtype);
>>>>> + /* If the operands point into different address spaces, we need to
>>>>> + explicitly convert them to pointers into the common address space
>>>>> + before we can subtract the numerical address values. */
>>>>> + if (as0 != as1)
>>>>> + {
>>>>> + addr_space_t as_common;
>>>>> + tree common_type;
>>>>> +
>>>>> + /* Determine the common superset address space. This is guaranteed
>>>>> + to exist because the caller verified that comp_target_types
>>>>> + returned non-zero. */
>>>>> + if (!addr_space_superset (as0, as1, &as_common))
>>>>> + gcc_unreachable ();
>>>>> +
>>>>> + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
>>>>> + op0 = convert (common_type, op0);
>>>>> + op1 = convert (common_type, op1);
>>>>> + }
>>>>> +
>>>>> if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
>>>>> return error_mark_node;
>>>>> @@ -10805,6 +10853,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>>>>> to_more_cv_qualified = true;
>>>>> }
>>>>> + /* Warn about conversions between pointers to disjoint
>>>>> + address spaces. */
>>>>> + if (TREE_CODE (from) == POINTER_TYPE
>>>>> + && TREE_CODE (to) == POINTER_TYPE)
>>>>> + {
>>>>> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
>>>>> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
>>>>> + addr_space_t as_common;
>>>>> +
>>>>> + if (!addr_space_superset (as_to, as_from, &as_common))
>>>>> + return false;
>>>>> + }
>>>>> +
>>>>> if (constp > 0)
>>>>> constp &= TYPE_READONLY (to);
>>>>> }
>>>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>>>> index ac3db88566d..1e0d436c02c 100644
>>>>> --- a/gcc/doc/extend.texi
>>>>> +++ b/gcc/doc/extend.texi
>>>>> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
>>>>> @section Named Address Spaces
>>>>> @cindex Named Address Spaces
>>>>> -As an extension, GNU C supports named address spaces as
>>>>> +As an extension, GNU C and GNU C++ support named address spaces as
>>>>> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
>>>>> address spaces in GCC will evolve as the draft technical report
>>>>> changes. Calling conventions for any target might also change. At
>>>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>>>> new file mode 100644
>>>>> index 00000000000..c01f8d6054a
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>>>> @@ -0,0 +1,10 @@
>>>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>>>> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
>>>>> +
>>>>> +int f (int volatile __seg_fs *a)
>>>>> +{
>>>>> + return *a;
>>>>> +}
>>>>> +
>>>>> +int main () {}
>>>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>>>> new file mode 100644
>>>>> index 00000000000..862bbbdcdf2
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>>>> @@ -0,0 +1,9 @@
>>>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>>>> +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
>>>>> +
>>>>> +template <class T>
>>>>> +int f (T *p) { return *p; }
>>>>> +int g (__seg_fs int *p) { return *p; }
>>>>> +__seg_fs int *a;
>>>>> +int main() { f(a); }
>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>>>> new file mode 100644
>>>>> index 00000000000..c04d2f497da
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>>>> @@ -0,0 +1,5 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +__seg_fs char a, b, c;
>>>>> +__seg_fs const int *p;
>>>>> +static /* give internal linkage to the following anonymous struct */
>>>>> +__seg_fs struct { int a; char b; } * __seg_gs q;
>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>>>> new file mode 100644
>>>>> index 00000000000..86c02d1e7f5
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>>>> @@ -0,0 +1,20 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +int __seg_fs * fs1;
>>>>> +int __seg_fs * fs2;
>>>>> +float __seg_gs * gs1;
>>>>> +float __seg_gs * gs2;
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
>>>>> + fs1 - fs2;
>>>>> + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
>>>>> + fs1 == fs2;
>>>>> + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>>>> + fs1 = fs2;
>>>>> + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
>>>>> + fs1 > fs2;
>>>>> + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>>>> new file mode 100644
>>>>> index 00000000000..12d7975e560
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>>>> @@ -0,0 +1,31 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +// { dg-prune-output "does not allow .register. storage class specifier" }
>>>>> +int __seg_fs * outer_b;
>>>>> +
>>>>> +struct s {
>>>>> + __seg_fs int * ok;
>>>>> + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
>>>>> +};
>>>>> +
>>>>> +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
>>>>> +
>>>>> +namespace ns_a
>>>>> +{
>>>>> + int __seg_fs * inner_b;
>>>>> +
>>>>> + template<typename T>
>>>>> + int f (T &a) { return a; }
>>>>> + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
>>>>> + int h (__seg_fs int *a) { return *a; }
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
>>>>> + static __seg_gs int static_gs;
>>>>> + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
>>>>> + __seg_fs int *pa = outer_b;
>>>>> + __seg_fs int& ra = *ns_a::inner_b;
>>>>> + return ns_a::f(ra) + ns_a::f(*pa);
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
>>>>> new file mode 100644
>>>>> index 00000000000..ebb6316054a
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
>>>>> @@ -0,0 +1,9 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +
>>>>> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>>>> new file mode 100644
>>>>> index 00000000000..2e8ee32a885
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>>>> @@ -0,0 +1,10 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +// { dg-options "-std=gnu++98" }
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + struct foo {int a; char b[2];} structure;
>>>>> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>>>> new file mode 100644
>>>>> index 00000000000..5b2c0f28078
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>>>> @@ -0,0 +1,9 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +
>>>>> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>>>> new file mode 100644
>>>>> index 00000000000..ae9f4de0e1f
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>>>> @@ -0,0 +1,8 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +
>>>>> +template <class T>
>>>>> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
>>>>> + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
>>>>> +__seg_fs int *a;
>>>>> +int main() { f(a); } // { dg-error "no matching" }
>>>>> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
>>>>> diff --git a/gcc/tree.h b/gcc/tree.h
>>>>> index 8844471e9a5..b7da4c5141a 100644
>>>>> --- a/gcc/tree.h
>>>>> +++ b/gcc/tree.h
>>>>> @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
>>>>> /* Encode/decode the named memory support as part of the qualifier. If more
>>>>> than 8 qualifiers are added, these macros need to be adjusted. */
>>>>> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
>>>>> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
>>>>> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>>>>> /* Return all qualifiers except for the address space qualifiers. */
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>
>>>
>>>
>>>
>>>
>>
>>
>>
>>
>>
>>
>>
>
>
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 19:41 ` Jason Merrill
@ 2022-10-13 21:57 ` Paul Iannetta
2022-10-14 15:19 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-10-13 21:57 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
On Thu, Oct 13, 2022 at 03:41:16PM -0400, Jason Merrill wrote:
> On 10/13/22 12:02, Paul Iannetta wrote:
> > On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
> > > On 10/13/22 11:23, Paul Iannetta wrote:
> > > > On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
> > > > > On 10/12/22 20:52, Paul Iannetta wrote:
> > > > > > On Tue, Oct 11, 2022 at 09:49:43PM -0400, Jason Merrill wrote:
> > > > > > >
> > > > > > > It surprises that this is the only place we complain about an object with an
> > > > > > > address-space qualifier. Shouldn't we also complain about e.g. automatic
> > > > > > > variables/parameters or non-static data members with address-space qualified
> > > > > > > type?
> > > > > > >
> > > > > >
> > > > > > Indeed, I was missing quite a few things here. Thanks.
> > > > > > I used the draft as basis this time and imported from the C
> > > > > > implementation the relevant parts. This time, the errors get properly
> > > > > > emitted when an address space is unduly specified; and comparisons,
> > > > > > assignments and comparisons are taken care of.
> > > > > >
> > > > > > There are quite a few things I would like to clarify concerning some
> > > > > > implementation details.
> > > > > > - A variable with automatic storage (which is neither a pointer nor
> > > > > > a reference) cannot be qualified with an address space. I detect
> > > > > > this by the combination of `sc_none' and `! toplevel_bindings_p ()',
> > > > > > but I've also seen the use of `at_function_scope' at other places.
> > > > > > And I'm unsure which one is appropriate here.
> > > > > > This detection happens at the very end of grokdeclarator because I
> > > > > > need to know that the type is a pointer, which is not know until
> > > > > > very late in the function.
> > > > >
> > > > > At that point you have the decl, and you can ask directly what its storage
> > > > > duration is, perhaps using decl_storage_duration.
> > > > >
> > > > > But why do you need to know whether the type is a pointer? The attribute
> > > > > applies to the target type of the pointer, not the pointer type. I think
> > > > > the problem is that you're looking at declspecs when you ought to be looking
> > > > > at type_quals.
> > > >
> > > > I need to know that the base type is a pointer to reject invalid
> > > > declarations such as:
> > > >
> > > > int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
> > > >
> > > > because parameters and auto variables can have an address space
> > > > qualifier only if they are pointer or reference type, which I can't
> > > > tell only from type_quals.
> > >
> > > But "int *__seg_fs a" is just as invalid as the above; the difference is not
> > > whether a is a pointer, but whether the address-space-qualified is the type
> > > of a itself or some sub-type.
> >
> > I agree that "int * __seg_fs a" is invalid but it is accepted by the C
> > front-end, and by clang (both C and C++), the behavior is that the
> > address-name is silently ignored.
>
> Hmm, that sounds like a bug; in that case, presumably the user meant to
> qualify the pointed-to type, and silently ignoring seems unlikely to give
> the effect they want.
>
Well, actually, I'm re-reading the draft and "int * __seg_fs a" is
valid. It means "pointer in address space __seg_fs pointing to an
object in the generic address space", whereas "__seg_fs int * a" means
"pointer in the generic address space pointing to an object in the
__seg_fs address-space".
Oddities such as, "__seg_fs int * __seg_gs a" are also perfectly
valid.
The reason why I wrongly assumed that the address space was silently
ignored is that I made a simple test which only relied on the mangled
function name...
```
int * __seg_fs a;
template <typename T> int f (T *a) { return *a; }
int main () { return f (a); } // f<int>(int*), since a is in __seg_fs
// but the pointer is to the generic
// address-space.
```
Implementation-wise, I think I handle that correctly but I'll do a recheck.
> > > You need to look at the qualifiers on type (which should also be the ones in
> > > type_quals), not the qualifiers in the declspecs.
> >
> > I'll have another look, thanks.
> >
> > > > > > - I'm having some trouble deciding whether I include those three
> > > > > > stub programs as tests, they all compile fine and clang accepts
> > > > > > them as well.
> > > > >
> > > > > Why not?
> > > >
> > > > I thought they were pretty contrived, since it does not make much
> > > > sense to strip address space qualifiers, even though it does prove
> > > > that the implementation support those contrived but valid uses.
> > >
> > > Testcases are full of contrived examples testing corner cases. :)
> > >
> > > > >
> > > > > > Ex1:
> > > > > > ```
> > > > > > int __seg_fs * fs1;
> > > > > > int __seg_gs * gs1;
> > > > > >
> > > > > > template<typename T> struct strip;
> > > > > > template<typename T> struct strip<__seg_fs T *> { typedef T type; };
> > > > > > template<typename T> struct strip<__seg_gs T *> { typedef T type; };
> > > > > >
> > > > > > int
> > > > > > main ()
> > > > > > {
> > > > > > *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
> > > > > > return 0;
> > > > > > }
> > > > > > ```
> > > > > >
> > > > > > Ex2:
> > > > > > ```
> > > > > > int __seg_fs * fs1;
> > > > > > int __seg_fs * fs2;
> > > > > >
> > > > > > template<typename T, typename U> auto f (T __seg_fs * a, U __seg_gs * b) { return a; }
> > > > > > template<typename T, typename U> auto f (T __seg_gs * a, U __seg_fs * b) { return a; }
> > > > > >
> > > > > > int
> > > > > > main ()
> > > > > > {
> > > > > > f (fs1, gs1);
> > > > > > f (gs1, fs1);
> > > > > > return 0;
> > > > > > }
> > > > > > ```
> > > > > >
> > > > > > Ex3:
> > > > > > ```
> > > > > > int __seg_fs * fs1;
> > > > > > int __seg_gs * gs1;
> > > > > >
> > > > > > template<typename T, typename U>
> > > > > > auto f (T __seg_fs * a, U __seg_gs * b)
> > > > > > {
> > > > > > return *(T *) a == *(U *) b;
> > > > > > }
> > > > > >
> > > > > > int
> > > > > > main ()
> > > > > > {
> > > > > > return f (fs1, gs1);
> > > > > > }
> > > > > > ```
> > > > > >
> > > > > >
> > > > > > Add support for custom address spaces in C++
> > > > > >
> > > > > > gcc/
> > > > > > * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
> > > > > >
> > > > > > gcc/c/
> > > > > > * c-decl.cc: Remove c_register_addr_space.
> > > > > >
> > > > > > gcc/c-family/
> > > > > > * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> > > > > > (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> > > > > > * c-common.h: Remove the FIXME.
> > > > > > (addr_space_superset): New declaration.
> > > > > >
> > > > > > gcc/cp/
> > > > > > * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> > > > > > (struct cp_decl_specifier_seq): Likewise.
> > > > > > * decl.cc (get_type_quals): Likewise.
> > > > > > (check_tag_decl): Likewise.
> > > > > > (grokdeclarator): Likewise.
> > > > > > * parser.cc (cp_parser_type_specifier): Likewise.
> > > > > > (cp_parser_cv_qualifier_seq_opt): Likewise.
> > > > > > (cp_parser_postfix_expression): Likewise.
> > > > > > (cp_parser_type_specifier): Likewise.
> > > > > > (set_and_check_decl_spec_loc): Likewise.
> > > > > > * typeck.cc (composite_pointer_type): Likewise
> > > > > > (comp_ptr_ttypes_real): Likewise.
> > > > > > (same_type_ignoring_top_level_qualifiers_p): Likewise.
> > > > > > * pt.cc (check_cv_quals_for_unify): Likewise.
> > > > > > (unify): Likewise.
> > > > > > * tree.cc: Remove c_register_addr_space stub.
> > > > > > * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> > > > > > using the extended qualifier notation.
> > > > > >
> > > > > > gcc/doc
> > > > > > * extend.texi (Named Address Spaces): add a mention about C++
> > > > > > support.
> > > > > >
> > > > > > gcc/testsuite/
> > > > > > * g++.dg/abi/mangle-addr-space1.C: New test.
> > > > > > * g++.dg/abi/mangle-addr-space2.C: New test.
> > > > > > * g++.dg/parse/addr-space.C: New test.
> > > > > > * g++.dg/parse/addr-space1.C: New test.
> > > > > > * g++.dg/parse/addr-space2.C: New test.
> > > > > > * g++.dg/parse/template/spec-addr-space.C: New test.
> > > > > > * g++.dg/ext/addr-space-decl.C: New test.
> > > > > > * g++.dg/ext/addr-space-ref.C: New test.
> > > > > > * g++.dg/ext/addr-space-ops.C: New test.
> > > > > >
> > > > > > # Please enter the commit message for your changes. Lines starting
> > > > > > # with '#' will be ignored, and an empty message aborts the commit.
> > > > > > #
> > > > > > # Date: Sun Oct 9 16:02:22 2022 +0200
> > > > > > #
> > > > > > # On branch releases/gcc-12
> > > > > > # Your branch is ahead of 'origin/releases/gcc-12' by 2 commits.
> > > > > > # (use "git push" to publish your local commits)
> > > > > > #
> > > > > > # Changes to be committed:
> > > > > > # modified: gcc/c-family/c-common.cc
> > > > > > # modified: gcc/c-family/c-common.h
> > > > > > # modified: gcc/c/c-decl.cc
> > > > > > # modified: gcc/c/c-typeck.cc
> > > > > > # modified: gcc/cp/cp-tree.h
> > > > > > # modified: gcc/cp/decl.cc
> > > > > > # modified: gcc/cp/mangle.cc
> > > > > > # modified: gcc/cp/parser.cc
> > > > > > # modified: gcc/cp/pt.cc
> > > > > > # modified: gcc/cp/tree.cc
> > > > > > # modified: gcc/cp/typeck.cc
> > > > > > # modified: gcc/doc/extend.texi
> > > > > > # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > > > > > # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > > > > > # new file: gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > > > > > # new file: gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > > > > > # new file: gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > > > > > # new file: gcc/testsuite/g++.dg/parse/addr-space.C
> > > > > > # new file: gcc/testsuite/g++.dg/parse/addr-space1.C
> > > > > > # new file: gcc/testsuite/g++.dg/parse/addr-space2.C
> > > > > > # new file: gcc/testsuite/g++.dg/template/spec-addr-space.C
> > > > > > # modified: gcc/tree.h
> > > > > > #
> > > > > > # Untracked files:
> > > > > > # Makefile
> > > > > > # addr-space-decl.C
> > > > > > # addr-space-decl.s
> > > > > > # addr-space-ops.C
> > > > > > # addr-space-ref.C
> > > > > > # addr-space-template.C
> > > > > > # addr-space-template.s
> > > > > > # addr-space-template2.C
> > > > > > # addr-space-template2.C.006t.gimple
> > > > > > # addr-space-template2.s
> > > > > > # addr-space-traits.C
> > > > > > # addr-space4.C
> > > > > > # addr-space4.C.006t.gimple
> > > > > > # addr-space4.s
> > > > > > # build-x86_64-pc-linux-gnu/
> > > > > > # compare
> > > > > > # host-x86_64-pc-linux-gnu/
> > > > > > # mangle-addr-space1.s
> > > > > > # prev-x86_64-pc-linux-gnu/
> > > > > > # rename
> > > > > > # stage1-x86_64-pc-linux-gnu/
> > > > > > # stage_current
> > > > > > # stage_final
> > > > > > # stage_last
> > > > > > # x86_64-pc-linux-gnu/
> > > > > > #
> > > > > > # ------------------------ >8 ------------------------
> > > > > > # Do not modify or remove the line above.
> > > > > > # Everything below it will be ignored.
> > > > > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> > > > > > index bb0544eeaea..ff1146ecc25 100644
> > > > > > --- a/gcc/c-family/c-common.cc
> > > > > > +++ b/gcc/c-family/c-common.cc
> > > > > > @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
> > > > > > return IDENTIFIER_POINTER (ridpointers [rid]);
> > > > > > }
> > > > > > +/* Return true if between two named address spaces, whether there is a superset
> > > > > > + named address space that encompasses both address spaces. If there is a
> > > > > > + superset, return which address space is the superset. */
> > > > > > +
> > > > > > +bool
> > > > > > +addr_space_superset (addr_space_t as1, addr_space_t as2,
> > > > > > + addr_space_t * common)
> > > > > > +{
> > > > > > + if (as1 == as2)
> > > > > > + {
> > > > > > + *common = as1;
> > > > > > + return true;
> > > > > > + }
> > > > > > + else if (targetm.addr_space.subset_p (as1, as2))
> > > > > > + {
> > > > > > + *common = as2;
> > > > > > + return true;
> > > > > > + }
> > > > > > + else if (targetm.addr_space.subset_p (as2, as1))
> > > > > > + {
> > > > > > + *common = as1;
> > > > > > + return true;
> > > > > > + }
> > > > > > + else
> > > > > > + return false;
> > > > > > +}
> > > > > > +
> > > > > > /* Push current bindings for the function name VAR_DECLS. */
> > > > > > void
> > > > > > @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> > > > > > return build_nonstandard_integer_type (width, unsignedp);
> > > > > > }
> > > > > > +/* Register reserved keyword WORD as qualifier for address space AS. */
> > > > > > +
> > > > > > +void
> > > > > > +c_register_addr_space (const char *word, addr_space_t as)
> > > > > > +{
> > > > > > + int rid = RID_FIRST_ADDR_SPACE + as;
> > > > > > + tree id;
> > > > > > +
> > > > > > + /* Address space qualifiers are only supported
> > > > > > + in C with GNU extensions enabled. */
> > > > > > + if (c_dialect_objc () || flag_no_asm)
> > > > > > + return;
> > > > > > +
> > > > > > + id = get_identifier (word);
> > > > > > + C_SET_RID_CODE (id, rid);
> > > > > > + TREE_LANG_FLAG_0 (id) = 1;
> > > > > > + ridpointers[rid] = id;
> > > > > > +}
> > > > > > +
> > > > > > /* The C version of the register_builtin_type langhook. */
> > > > > > void
> > > > > > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> > > > > > index 52a85bfb783..d36f9e4975b 100644
> > > > > > --- a/gcc/c-family/c-common.h
> > > > > > +++ b/gcc/c-family/c-common.h
> > > > > > @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
> > > > > > extern tree (*make_fname_decl) (location_t, tree, int);
> > > > > > -/* In c-decl.cc and cp/tree.cc. FIXME. */
> > > > > > -extern void c_register_addr_space (const char *str, addr_space_t as);
> > > > > > -
> > > > > > /* In c-common.cc. */
> > > > > > extern bool in_late_binary_op;
> > > > > > extern const char *c_addr_space_name (addr_space_t as);
> > > > > > +extern const char *c_addr_space_name (addr_space_t as);
> > > > > > +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> > > > > > extern tree identifier_global_value (tree);
> > > > > > extern tree identifier_global_tag (tree);
> > > > > > extern bool names_builtin_p (const char *);
> > > > > > @@ -952,6 +951,7 @@ extern void c_common_finish (void);
> > > > > > extern void c_common_parse_file (void);
> > > > > > extern FILE *get_dump_info (int, dump_flags_t *);
> > > > > > extern alias_set_type c_common_get_alias_set (tree);
> > > > > > +extern void c_register_addr_space (const char *, addr_space_t);
> > > > > > extern void c_register_builtin_type (tree, const char*);
> > > > > > extern bool c_promoting_integer_type_p (const_tree);
> > > > > > extern bool self_promoting_args_p (const_tree);
> > > > > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> > > > > > index c701f07befe..e1bb4f1cf37 100644
> > > > > > --- a/gcc/c/c-decl.cc
> > > > > > +++ b/gcc/c/c-decl.cc
> > > > > > @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
> > > > > > ext_block = NULL;
> > > > > > }
> > > > > > -/* Register reserved keyword WORD as qualifier for address space AS. */
> > > > > > -
> > > > > > -void
> > > > > > -c_register_addr_space (const char *word, addr_space_t as)
> > > > > > -{
> > > > > > - int rid = RID_FIRST_ADDR_SPACE + as;
> > > > > > - tree id;
> > > > > > -
> > > > > > - /* Address space qualifiers are only supported
> > > > > > - in C with GNU extensions enabled. */
> > > > > > - if (c_dialect_objc () || flag_no_asm)
> > > > > > - return;
> > > > > > -
> > > > > > - id = get_identifier (word);
> > > > > > - C_SET_RID_CODE (id, rid);
> > > > > > - C_IS_RESERVED_WORD (id) = 1;
> > > > > > - ridpointers [rid] = id;
> > > > > > -}
> > > > > > -
> > > > > > /* Return identifier to look up for omp declare reduction. */
> > > > > > tree
> > > > > > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> > > > > > index b271af9bedb..c4b01368534 100644
> > > > > > --- a/gcc/c/c-typeck.cc
> > > > > > +++ b/gcc/c/c-typeck.cc
> > > > > > @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
> > > > > > return type;
> > > > > > }
> > > > > > -/* Return true if between two named address spaces, whether there is a superset
> > > > > > - named address space that encompasses both address spaces. If there is a
> > > > > > - superset, return which address space is the superset. */
> > > > > > -
> > > > > > -static bool
> > > > > > -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> > > > > > -{
> > > > > > - if (as1 == as2)
> > > > > > - {
> > > > > > - *common = as1;
> > > > > > - return true;
> > > > > > - }
> > > > > > - else if (targetm.addr_space.subset_p (as1, as2))
> > > > > > - {
> > > > > > - *common = as2;
> > > > > > - return true;
> > > > > > - }
> > > > > > - else if (targetm.addr_space.subset_p (as2, as1))
> > > > > > - {
> > > > > > - *common = as1;
> > > > > > - return true;
> > > > > > - }
> > > > > > - else
> > > > > > - return false;
> > > > > > -}
> > > > > > -
> > > > > > /* Return a variant of TYPE which has all the type qualifiers of LIKE
> > > > > > as well as those of TYPE. */
> > > > > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > > > > > index 72f4398a8f9..82a6d72f5df 100644
> > > > > > --- a/gcc/cp/cp-tree.h
> > > > > > +++ b/gcc/cp/cp-tree.h
> > > > > > @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
> > > > > > ds_const,
> > > > > > ds_volatile,
> > > > > > ds_restrict,
> > > > > > + ds_addr_space,
> > > > > > ds_inline,
> > > > > > ds_virtual,
> > > > > > ds_explicit,
> > > > > > @@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
> > > > > > cp_storage_class storage_class;
> > > > > > /* For the __intN declspec, this stores the index into the int_n_* arrays. */
> > > > > > int int_n_idx;
> > > > > > + /* The address space that the declaration belongs to. */
> > > > > > + addr_space_t address_space;
> > > > > > /* True iff TYPE_SPEC defines a class or enum. */
> > > > > > BOOL_BITFIELD type_definition_p : 1;
> > > > > > /* True iff multiple types were (erroneously) specified for this
> > > > > > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> > > > > > index 9f78c500a15..fa5a1ddb21d 100644
> > > > > > --- a/gcc/cp/decl.cc
> > > > > > +++ b/gcc/cp/decl.cc
> > > > > > @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> > > > > > type_quals |= TYPE_QUAL_VOLATILE;
> > > > > > if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> > > > > > type_quals |= TYPE_QUAL_RESTRICT;
> > > > > > + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > > > > > + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
> > > > > > return type_quals;
> > > > > > }
> > > > > > @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> > > > > > error_at (declspecs->locations[ds_restrict],
> > > > > > "%<__restrict%> can only be specified for objects and "
> > > > > > "functions");
> > > > > > + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > > > > > + error_at (declspecs->locations[ds_addr_space],
> > > > > > + "address space can only be specified for objects and "
> > > > > > + "functions");
> > > > > > else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> > > > > > error_at (declspecs->locations[ds_thread],
> > > > > > "%<__thread%> can only be specified for objects "
> > > > > > @@ -14572,6 +14578,57 @@ grokdeclarator (const cp_declarator *declarator,
> > > > > > if (!processing_template_decl)
> > > > > > cp_apply_type_quals_to_decl (type_quals, decl);
> > > > > > + addr_space_t address_space = declspecs->address_space;
> > > > > > + if (!ADDR_SPACE_GENERIC_P (address_space) && !INDIRECT_TYPE_P (type))
> > > > > > + {
> > > > > > + if (decl_context == NORMAL)
> > > > > > + {
> > > > > > + switch (storage_class)
> > > > > > + {
> > > > > > + case sc_auto:
> > > > > > + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
> > > > > > + c_addr_space_name (address_space), name);
> > > > > > + break;
> > > > > > + case sc_register:
> > > > > > + error ("%qs combined with %<register%> qualifier for %qs",
> > > > > > + c_addr_space_name (address_space), name);
> > > > > > + break;
> > > > > > + case sc_none:
> > > > > > + if (! toplevel_bindings_p ())
> > > > > > + error ("%qs specified for auto variable %qs",
> > > > > > + c_addr_space_name (address_space), name);
> > > > > > + break;
> > > > > > + case sc_mutable:
> > > > > > + error ("%qs combined with %<mutable%> qualifier for %qs",
> > > > > > + c_addr_space_name (address_space), name);
> > > > > > + break;
> > > > > > + case sc_static:
> > > > > > + case sc_extern:
> > > > > > + break;
> > > > > > + default:
> > > > > > + gcc_unreachable ();
> > > > > > + }
> > > > > > + }
> > > > > > + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
> > > > > > + {
> > > > > > + if (name)
> > > > > > + error ("%qs specified for parameter %qs",
> > > > > > + c_addr_space_name (address_space), name);
> > > > > > + else
> > > > > > + error ("%qs specified for unnamed parameter",
> > > > > > + c_addr_space_name (address_space));
> > > > > > + }
> > > > > > + else if (decl_context == FIELD)
> > > > > > + {
> > > > > > + if (name)
> > > > > > + error ("%qs specified for structure field %qs",
> > > > > > + c_addr_space_name (address_space), name);
> > > > > > + else
> > > > > > + error ("%qs specified for structure field",
> > > > > > + c_addr_space_name (address_space));
> > > > > > + }
> > > > > > + }
> > > > > > +
> > > > > > return decl;
> > > > > > }
> > > > > > }
> > > > > > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> > > > > > index eb53e0ebeb4..16cb2ff4332 100644
> > > > > > --- a/gcc/cp/mangle.cc
> > > > > > +++ b/gcc/cp/mangle.cc
> > > > > > @@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
> > > > > > array. */
> > > > > > cp_cv_quals quals = TYPE_QUALS (type);
> > > > > > + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
> > > > > > + {
> > > > > > + const char *as_name = c_addr_space_name (as);
> > > > > > + write_char ('U');
> > > > > > + write_unsigned_number (strlen (as_name));
> > > > > > + write_string (as_name);
> > > > > > + ++num_qualifiers;
> > > > > > + }
> > > > > > if (quals & TYPE_QUAL_RESTRICT)
> > > > > > {
> > > > > > write_char ('r');
> > > > > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > > > > > index 763df6f479b..f0169d83ab0 100644
> > > > > > --- a/gcc/cp/parser.cc
> > > > > > +++ b/gcc/cp/parser.cc
> > > > > > @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> > > > > > postfix_expression = error_mark_node;
> > > > > > break;
> > > > > > }
> > > > > > + if (type != error_mark_node
> > > > > > + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> > > > > > + && current_function_decl)
> > > > > > + {
> > > > > > + error
> > > > > > + ("compound literal qualified by address-space qualifier");
> > > > > > + type = error_mark_node;
> > > > > > + }
> > > > > > /* Form the representation of the compound-literal. */
> > > > > > postfix_expression
> > > > > > = finish_compound_literal (type, initializer,
> > > > > > @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
> > > > > > break;
> > > > > > }
> > > > > > +
> > > > > > + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> > > > > > + {
> > > > > > + ds = ds_addr_space;
> > > > > > + if (is_cv_qualifier)
> > > > > > + *is_cv_qualifier = true;
> > > > > > + }
> > > > > > +
> > > > > > +
> > > > > > /* Handle simple keywords. */
> > > > > > if (ds != ds_last)
> > > > > > {
> > > > > > @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> > > > > > GNU Extension:
> > > > > > cv-qualifier:
> > > > > > + address-space-qualifier
> > > > > > __restrict__
> > > > > > Returns a bitmask representing the cv-qualifiers. */
> > > > > > @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> > > > > > break;
> > > > > > }
> > > > > > + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
> > > > > > + token->keyword <= RID_LAST_ADDR_SPACE)
> > > > > > + cv_qualifier =
> > > > > > + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> > > > > > +
> > > > > > if (!cv_qualifier)
> > > > > > break;
> > > > > > @@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > > > > > decl_specs->locations[ds] = location;
> > > > > > if (ds == ds_thread)
> > > > > > decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> > > > > > + else if (ds == ds_addr_space)
> > > > > > + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> > > > > > }
> > > > > > else
> > > > > > {
> > > > > > @@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > > > > > error_at (&richloc, "duplicate %qD", token->u.value);
> > > > > > }
> > > > > > }
> > > > > > + else if (ds == ds_addr_space)
> > > > > > + {
> > > > > > + addr_space_t as1 = decl_specs->address_space;
> > > > > > + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> > > > > > +
> > > > > > + gcc_rich_location richloc (location);
> > > > > > + richloc.add_fixit_remove ();
> > > > > > + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> > > > > > + && as1 != as2)
> > > > > > + error_at (&richloc,
> > > > > > + "conflicting named address spaces (%s vs %s)",
> > > > > > + c_addr_space_name (as1), c_addr_space_name (as2));
> > > > > > + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> > > > > > + error_at (&richloc,
> > > > > > + "duplicate named address space %s",
> > > > > > + c_addr_space_name (as1));
> > > > > > +
> > > > > > + decl_specs->address_space = as2;
> > > > > > + }
> > > > > > else
> > > > > > {
> > > > > > static const char *const decl_spec_names[] = {
> > > > > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > > > > > index bef31416fb7..c9d1c01f4ce 100644
> > > > > > --- a/gcc/cp/pt.cc
> > > > > > +++ b/gcc/cp/pt.cc
> > > > > > @@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
> > > > > > static int
> > > > > > check_cv_quals_for_unify (int strict, tree arg, tree parm)
> > > > > > {
> > > > > > - int arg_quals = cp_type_quals (arg);
> > > > > > - int parm_quals = cp_type_quals (parm);
> > > > > > + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > > > > > + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > > > > > +
> > > > > > + /* Try to unify ARG's address space into PARM's address space.
> > > > > > + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> > > > > > + there are no constraints on address spaces for this type. */
> > > > > > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > > > > > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > > > > > + addr_space_t as_common;
> > > > > > + addr_space_superset (as_arg, as_parm, &as_common);
> > > > > > +
> > > > > > + if (!(as_parm == as_common || as_parm == 0))
> > > > > > + return 0;
> > > > > > if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> > > > > > && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> > > > > > @@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> > > > > > arg, parm))
> > > > > > return unify_cv_qual_mismatch (explain_p, parm, arg);
> > > > > > + int arg_cv_quals = cp_type_quals (arg);
> > > > > > + int parm_cv_quals = cp_type_quals (parm);
> > > > > > +
> > > > > > + /* If PARM does not contain any address space constraints it can
> > > > > > + fully match the address space of ARG. However, if PARM contains an
> > > > > > + address space constraint, it becomes the upper bound. That is,
> > > > > > + AS_ARG may be promoted to AS_PARM but not the converse. If we
> > > > > > + ended up here, it means that `check_cv_quals_for_unify' succeeded
> > > > > > + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> > > > > > + AS_PARM. */
> > > > > > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> > > > > > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> > > > > > + addr_space_t as_common = as_parm ? 0 : as_arg;
> > > > > > +
> > > > > > /* Consider the case where ARG is `const volatile int' and
> > > > > > PARM is `const T'. Then, T should be `volatile int'. */
> > > > > > - arg = cp_build_qualified_type_real
> > > > > > - (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
> > > > > > + int unified_cv =
> > > > > > + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> > > > > > + | ENCODE_QUAL_ADDR_SPACE (as_common);
> > > > > > + arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
> > > > > > if (arg == error_mark_node)
> > > > > > return unify_invalid (explain_p);
> > > > > > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> > > > > > index 3b37567cbd7..5e14ac837fc 100644
> > > > > > --- a/gcc/cp/tree.cc
> > > > > > +++ b/gcc/cp/tree.cc
> > > > > > @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
> > > > > > DECL_CHAIN (t) = NULL_TREE;
> > > > > > }
> > > > > > -/* Stub for c-common. Please keep in sync with c-decl.cc.
> > > > > > - FIXME: If address space support is target specific, then this
> > > > > > - should be a C target hook. But currently this is not possible,
> > > > > > - because this function is called via REGISTER_TARGET_PRAGMAS. */
> > > > > > -void
> > > > > > -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> > > > > > -{
> > > > > > -}
> > > > > > -
> > > > > > /* Return the number of operands in T that we care about for things like
> > > > > > mangling. */
> > > > > > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> > > > > > index ceb80d9744f..7d810c6a12c 100644
> > > > > > --- a/gcc/cp/typeck.cc
> > > > > > +++ b/gcc/cp/typeck.cc
> > > > > > @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
> > > > > > else
> > > > > > return error_mark_node;
> > > > > > }
> > > > > > + /* If possible merge the address space into the superset of the address
> > > > > > + spaces of t1 and t2, or raise an error. */
> > > > > > + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> > > > > > + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> > > > > > + addr_space_t as_common;
> > > > > > +
> > > > > > + /* If the two named address spaces are different, determine the common
> > > > > > + superset address space. If there isn't one, raise an error. */
> > > > > > + if (!addr_space_superset (as_t1, as_t2, &as_common))
> > > > > > + {
> > > > > > + as_common = as_t1;
> > > > > > + error_at (location,
> > > > > > + "%qT and %qT are in disjoint named address spaces",
> > > > > > + t1, t2);
> > > > > > + }
> > > > > > result_type
> > > > > > = cp_build_qualified_type (void_type_node,
> > > > > > - (cp_type_quals (TREE_TYPE (t1))
> > > > > > - | cp_type_quals (TREE_TYPE (t2))));
> > > > > > + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
> > > > > > + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
> > > > > > + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> > > > > > result_type = build_pointer_type (result_type);
> > > > > > /* Merge the attributes. */
> > > > > > attributes = (*targetm.merge_type_attributes) (t1, t2);
> > > > > > @@ -1579,7 +1595,9 @@ comptypes (tree t1, tree t2, int strict)
> > > > > > }
> > > > > > /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
> > > > > > - top-level qualifiers. */
> > > > > > + top-level qualifiers, except for named address spaces. If the pointers point
> > > > > > + to different named addresses spaces, then we must determine if one address
> > > > > > + space is a subset of the other. */
> > > > > > bool
> > > > > > same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > > > > > @@ -1589,6 +1607,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > > > > > if (type1 == type2)
> > > > > > return true;
> > > > > > + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
> > > > > > + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
> > > > > > + addr_space_t as_common;
> > > > > > +
> > > > > > + /* Fail if pointers point to incompatible address spaces. */
> > > > > > + if (!addr_space_superset (as_type1, as_type2, &as_common))
> > > > > > + return false;
> > > > > > +
> > > > > > type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
> > > > > > type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
> > > > > > return same_type_p (type1, type2);
> > > > > > @@ -6374,10 +6400,32 @@ static tree
> > > > > > pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
> > > > > > tsubst_flags_t complain, tree *instrument_expr)
> > > > > > {
> > > > > > - tree result, inttype;
> > > > > > tree restype = ptrdiff_type_node;
> > > > > > + tree result, inttype;
> > > > > > +
> > > > > > + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
> > > > > > + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
> > > > > > tree target_type = TREE_TYPE (ptrtype);
> > > > > > + /* If the operands point into different address spaces, we need to
> > > > > > + explicitly convert them to pointers into the common address space
> > > > > > + before we can subtract the numerical address values. */
> > > > > > + if (as0 != as1)
> > > > > > + {
> > > > > > + addr_space_t as_common;
> > > > > > + tree common_type;
> > > > > > +
> > > > > > + /* Determine the common superset address space. This is guaranteed
> > > > > > + to exist because the caller verified that comp_target_types
> > > > > > + returned non-zero. */
> > > > > > + if (!addr_space_superset (as0, as1, &as_common))
> > > > > > + gcc_unreachable ();
> > > > > > +
> > > > > > + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
> > > > > > + op0 = convert (common_type, op0);
> > > > > > + op1 = convert (common_type, op1);
> > > > > > + }
> > > > > > +
> > > > > > if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
> > > > > > return error_mark_node;
> > > > > > @@ -10805,6 +10853,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> > > > > > to_more_cv_qualified = true;
> > > > > > }
> > > > > > + /* Warn about conversions between pointers to disjoint
> > > > > > + address spaces. */
> > > > > > + if (TREE_CODE (from) == POINTER_TYPE
> > > > > > + && TREE_CODE (to) == POINTER_TYPE)
> > > > > > + {
> > > > > > + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> > > > > > + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> > > > > > + addr_space_t as_common;
> > > > > > +
> > > > > > + if (!addr_space_superset (as_to, as_from, &as_common))
> > > > > > + return false;
> > > > > > + }
> > > > > > +
> > > > > > if (constp > 0)
> > > > > > constp &= TYPE_READONLY (to);
> > > > > > }
> > > > > > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > > > > > index ac3db88566d..1e0d436c02c 100644
> > > > > > --- a/gcc/doc/extend.texi
> > > > > > +++ b/gcc/doc/extend.texi
> > > > > > @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> > > > > > @section Named Address Spaces
> > > > > > @cindex Named Address Spaces
> > > > > > -As an extension, GNU C supports named address spaces as
> > > > > > +As an extension, GNU C and GNU C++ support named address spaces as
> > > > > > defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> > > > > > address spaces in GCC will evolve as the draft technical report
> > > > > > changes. Calling conventions for any target might also change. At
> > > > > > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..c01f8d6054a
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > > > > > @@ -0,0 +1,10 @@
> > > > > > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > > > > > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> > > > > > +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> > > > > > +
> > > > > > +int f (int volatile __seg_fs *a)
> > > > > > +{
> > > > > > + return *a;
> > > > > > +}
> > > > > > +
> > > > > > +int main () {}
> > > > > > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..862bbbdcdf2
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > > > > > @@ -0,0 +1,9 @@
> > > > > > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > > > > > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> > > > > > +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
> > > > > > +
> > > > > > +template <class T>
> > > > > > +int f (T *p) { return *p; }
> > > > > > +int g (__seg_fs int *p) { return *p; }
> > > > > > +__seg_fs int *a;
> > > > > > +int main() { f(a); }
> > > > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..c04d2f497da
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > > > > > @@ -0,0 +1,5 @@
> > > > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > > > +__seg_fs char a, b, c;
> > > > > > +__seg_fs const int *p;
> > > > > > +static /* give internal linkage to the following anonymous struct */
> > > > > > +__seg_fs struct { int a; char b; } * __seg_gs q;
> > > > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..86c02d1e7f5
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > > > > > @@ -0,0 +1,20 @@
> > > > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > > > +int __seg_fs * fs1;
> > > > > > +int __seg_fs * fs2;
> > > > > > +float __seg_gs * gs1;
> > > > > > +float __seg_gs * gs2;
> > > > > > +
> > > > > > +int
> > > > > > +main ()
> > > > > > +{
> > > > > > + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
> > > > > > + fs1 - fs2;
> > > > > > + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
> > > > > > + fs1 == fs2;
> > > > > > + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > > > > > + fs1 = fs2;
> > > > > > + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
> > > > > > + fs1 > fs2;
> > > > > > + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > > > > > + return 0;
> > > > > > +}
> > > > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..12d7975e560
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > > > > > @@ -0,0 +1,31 @@
> > > > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > > > +// { dg-prune-output "does not allow .register. storage class specifier" }
> > > > > > +int __seg_fs * outer_b;
> > > > > > +
> > > > > > +struct s {
> > > > > > + __seg_fs int * ok;
> > > > > > + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
> > > > > > +};
> > > > > > +
> > > > > > +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
> > > > > > +
> > > > > > +namespace ns_a
> > > > > > +{
> > > > > > + int __seg_fs * inner_b;
> > > > > > +
> > > > > > + template<typename T>
> > > > > > + int f (T &a) { return a; }
> > > > > > + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
> > > > > > + int h (__seg_fs int *a) { return *a; }
> > > > > > +}
> > > > > > +
> > > > > > +int
> > > > > > +main ()
> > > > > > +{
> > > > > > + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
> > > > > > + static __seg_gs int static_gs;
> > > > > > + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
> > > > > > + __seg_fs int *pa = outer_b;
> > > > > > + __seg_fs int& ra = *ns_a::inner_b;
> > > > > > + return ns_a::f(ra) + ns_a::f(*pa);
> > > > > > +}
> > > > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..ebb6316054a
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> > > > > > @@ -0,0 +1,9 @@
> > > > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > > > +
> > > > > > +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> > > > > > +
> > > > > > +int
> > > > > > +main ()
> > > > > > +{
> > > > > > + return 0;
> > > > > > +}
> > > > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..2e8ee32a885
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > > > > > @@ -0,0 +1,10 @@
> > > > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > > > +// { dg-options "-std=gnu++98" }
> > > > > > +
> > > > > > +int
> > > > > > +main ()
> > > > > > +{
> > > > > > + struct foo {int a; char b[2];} structure;
> > > > > > + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> > > > > > + return 0;
> > > > > > +}
> > > > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..5b2c0f28078
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > > > > > @@ -0,0 +1,9 @@
> > > > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > > > +
> > > > > > +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> > > > > > +
> > > > > > +int
> > > > > > +main ()
> > > > > > +{
> > > > > > + return 0;
> > > > > > +}
> > > > > > diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > > > > > new file mode 100644
> > > > > > index 00000000000..ae9f4de0e1f
> > > > > > --- /dev/null
> > > > > > +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > > > > > @@ -0,0 +1,8 @@
> > > > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > > > +
> > > > > > +template <class T>
> > > > > > +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> > > > > > + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> > > > > > +__seg_fs int *a;
> > > > > > +int main() { f(a); } // { dg-error "no matching" }
> > > > > > +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> > > > > > diff --git a/gcc/tree.h b/gcc/tree.h
> > > > > > index 8844471e9a5..b7da4c5141a 100644
> > > > > > --- a/gcc/tree.h
> > > > > > +++ b/gcc/tree.h
> > > > > > @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
> > > > > > /* Encode/decode the named memory support as part of the qualifier. If more
> > > > > > than 8 qualifiers are added, these macros need to be adjusted. */
> > > > > > -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> > > > > > +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> > > > > > #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
> > > > > > /* Return all qualifiers except for the address space qualifiers. */
> > > > > >
> > > > > >
> > > > > >
> > > > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > >
> > > >
> > > >
> > > >
> > > >
> > >
> > >
> > >
> > >
> > >
> > >
> > >
> >
> >
> >
> >
> >
> >
>
>
>
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-13 21:57 ` Paul Iannetta
@ 2022-10-14 15:19 ` Jason Merrill
2022-10-18 7:37 ` [PATCH v3] " Paul Iannetta
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-14 15:19 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/13/22 17:57, Paul Iannetta wrote:
> On Thu, Oct 13, 2022 at 03:41:16PM -0400, Jason Merrill wrote:
>> On 10/13/22 12:02, Paul Iannetta wrote:
>>> On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
>>>> On 10/13/22 11:23, Paul Iannetta wrote:
>>>>> On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
>>>>>> On 10/12/22 20:52, Paul Iannetta wrote:
>>>>>>> On Tue, Oct 11, 2022 at 09:49:43PM -0400, Jason Merrill wrote:
>>>>>>>>
>>>>>>>> It surprises that this is the only place we complain about an object with an
>>>>>>>> address-space qualifier. Shouldn't we also complain about e.g. automatic
>>>>>>>> variables/parameters or non-static data members with address-space qualified
>>>>>>>> type?
>>>>>>>>
>>>>>>>
>>>>>>> Indeed, I was missing quite a few things here. Thanks.
>>>>>>> I used the draft as basis this time and imported from the C
>>>>>>> implementation the relevant parts. This time, the errors get properly
>>>>>>> emitted when an address space is unduly specified; and comparisons,
>>>>>>> assignments and comparisons are taken care of.
>>>>>>>
>>>>>>> There are quite a few things I would like to clarify concerning some
>>>>>>> implementation details.
>>>>>>> - A variable with automatic storage (which is neither a pointer nor
>>>>>>> a reference) cannot be qualified with an address space. I detect
>>>>>>> this by the combination of `sc_none' and `! toplevel_bindings_p ()',
>>>>>>> but I've also seen the use of `at_function_scope' at other places.
>>>>>>> And I'm unsure which one is appropriate here.
>>>>>>> This detection happens at the very end of grokdeclarator because I
>>>>>>> need to know that the type is a pointer, which is not know until
>>>>>>> very late in the function.
>>>>>>
>>>>>> At that point you have the decl, and you can ask directly what its storage
>>>>>> duration is, perhaps using decl_storage_duration.
>>>>>>
>>>>>> But why do you need to know whether the type is a pointer? The attribute
>>>>>> applies to the target type of the pointer, not the pointer type. I think
>>>>>> the problem is that you're looking at declspecs when you ought to be looking
>>>>>> at type_quals.
>>>>>
>>>>> I need to know that the base type is a pointer to reject invalid
>>>>> declarations such as:
>>>>>
>>>>> int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
>>>>>
>>>>> because parameters and auto variables can have an address space
>>>>> qualifier only if they are pointer or reference type, which I can't
>>>>> tell only from type_quals.
>>>>
>>>> But "int *__seg_fs a" is just as invalid as the above; the difference is not
>>>> whether a is a pointer, but whether the address-space-qualified is the type
>>>> of a itself or some sub-type.
>>>
>>> I agree that "int * __seg_fs a" is invalid but it is accepted by the C
>>> front-end, and by clang (both C and C++), the behavior is that the
>>> address-name is silently ignored.
>>
>> Hmm, that sounds like a bug; in that case, presumably the user meant to
>> qualify the pointed-to type, and silently ignoring seems unlikely to give
>> the effect they want.
>>
>
> Well, actually, I'm re-reading the draft and "int * __seg_fs a" is
> valid. It means "pointer in address space __seg_fs pointing to an
> object in the generic address space", whereas "__seg_fs int * a" means
> "pointer in the generic address space pointing to an object in the
> __seg_fs address-space".
>
> Oddities such as, "__seg_fs int * __seg_gs a" are also perfectly
> valid.
If a has static storage duration, sure; I was still thinking about
declarations with automatic storage duration such as in your example above.
> The reason why I wrongly assumed that the address space was silently
> ignored is that I made a simple test which only relied on the mangled
> function name...
>
> ```
> int * __seg_fs a;
> template <typename T> int f (T *a) { return *a; }
> int main () { return f (a); } // f<int>(int*), since a is in __seg_fs
> // but the pointer is to the generic
> // address-space.
> ```
>
> Implementation-wise, I think I handle that correctly but I'll do a recheck.
>
>>>> You need to look at the qualifiers on type (which should also be the ones in
>>>> type_quals), not the qualifiers in the declspecs.
>>>
>>> I'll have another look, thanks.
>>>
>>>>>>> - I'm having some trouble deciding whether I include those three
>>>>>>> stub programs as tests, they all compile fine and clang accepts
>>>>>>> them as well.
>>>>>>
>>>>>> Why not?
>>>>>
>>>>> I thought they were pretty contrived, since it does not make much
>>>>> sense to strip address space qualifiers, even though it does prove
>>>>> that the implementation support those contrived but valid uses.
>>>>
>>>> Testcases are full of contrived examples testing corner cases. :)
>>>>
>>>>>>
>>>>>>> Ex1:
>>>>>>> ```
>>>>>>> int __seg_fs * fs1;
>>>>>>> int __seg_gs * gs1;
>>>>>>>
>>>>>>> template<typename T> struct strip;
>>>>>>> template<typename T> struct strip<__seg_fs T *> { typedef T type; };
>>>>>>> template<typename T> struct strip<__seg_gs T *> { typedef T type; };
>>>>>>>
>>>>>>> int
>>>>>>> main ()
>>>>>>> {
>>>>>>> *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
>>>>>>> return 0;
>>>>>>> }
>>>>>>> ```
>>>>>>>
>>>>>>> Ex2:
>>>>>>> ```
>>>>>>> int __seg_fs * fs1;
>>>>>>> int __seg_fs * fs2;
>>>>>>>
>>>>>>> template<typename T, typename U> auto f (T __seg_fs * a, U __seg_gs * b) { return a; }
>>>>>>> template<typename T, typename U> auto f (T __seg_gs * a, U __seg_fs * b) { return a; }
>>>>>>>
>>>>>>> int
>>>>>>> main ()
>>>>>>> {
>>>>>>> f (fs1, gs1);
>>>>>>> f (gs1, fs1);
>>>>>>> return 0;
>>>>>>> }
>>>>>>> ```
>>>>>>>
>>>>>>> Ex3:
>>>>>>> ```
>>>>>>> int __seg_fs * fs1;
>>>>>>> int __seg_gs * gs1;
>>>>>>>
>>>>>>> template<typename T, typename U>
>>>>>>> auto f (T __seg_fs * a, U __seg_gs * b)
>>>>>>> {
>>>>>>> return *(T *) a == *(U *) b;
>>>>>>> }
>>>>>>>
>>>>>>> int
>>>>>>> main ()
>>>>>>> {
>>>>>>> return f (fs1, gs1);
>>>>>>> }
>>>>>>> ```
>>>>>>>
>>>>>>>
>>>>>>> Add support for custom address spaces in C++
>>>>>>>
>>>>>>> gcc/
>>>>>>> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>>>>>>>
>>>>>>> gcc/c/
>>>>>>> * c-decl.cc: Remove c_register_addr_space.
>>>>>>>
>>>>>>> gcc/c-family/
>>>>>>> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
>>>>>>> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
>>>>>>> * c-common.h: Remove the FIXME.
>>>>>>> (addr_space_superset): New declaration.
>>>>>>>
>>>>>>> gcc/cp/
>>>>>>> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
>>>>>>> (struct cp_decl_specifier_seq): Likewise.
>>>>>>> * decl.cc (get_type_quals): Likewise.
>>>>>>> (check_tag_decl): Likewise.
>>>>>>> (grokdeclarator): Likewise.
>>>>>>> * parser.cc (cp_parser_type_specifier): Likewise.
>>>>>>> (cp_parser_cv_qualifier_seq_opt): Likewise.
>>>>>>> (cp_parser_postfix_expression): Likewise.
>>>>>>> (cp_parser_type_specifier): Likewise.
>>>>>>> (set_and_check_decl_spec_loc): Likewise.
>>>>>>> * typeck.cc (composite_pointer_type): Likewise
>>>>>>> (comp_ptr_ttypes_real): Likewise.
>>>>>>> (same_type_ignoring_top_level_qualifiers_p): Likewise.
>>>>>>> * pt.cc (check_cv_quals_for_unify): Likewise.
>>>>>>> (unify): Likewise.
>>>>>>> * tree.cc: Remove c_register_addr_space stub.
>>>>>>> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
>>>>>>> using the extended qualifier notation.
>>>>>>>
>>>>>>> gcc/doc
>>>>>>> * extend.texi (Named Address Spaces): add a mention about C++
>>>>>>> support.
>>>>>>>
>>>>>>> gcc/testsuite/
>>>>>>> * g++.dg/abi/mangle-addr-space1.C: New test.
>>>>>>> * g++.dg/abi/mangle-addr-space2.C: New test.
>>>>>>> * g++.dg/parse/addr-space.C: New test.
>>>>>>> * g++.dg/parse/addr-space1.C: New test.
>>>>>>> * g++.dg/parse/addr-space2.C: New test.
>>>>>>> * g++.dg/parse/template/spec-addr-space.C: New test.
>>>>>>> * g++.dg/ext/addr-space-decl.C: New test.
>>>>>>> * g++.dg/ext/addr-space-ref.C: New test.
>>>>>>> * g++.dg/ext/addr-space-ops.C: New test.
>>>>>>>
>>>>>>> # Please enter the commit message for your changes. Lines starting
>>>>>>> # with '#' will be ignored, and an empty message aborts the commit.
>>>>>>> #
>>>>>>> # Date: Sun Oct 9 16:02:22 2022 +0200
>>>>>>> #
>>>>>>> # On branch releases/gcc-12
>>>>>>> # Your branch is ahead of 'origin/releases/gcc-12' by 2 commits.
>>>>>>> # (use "git push" to publish your local commits)
>>>>>>> #
>>>>>>> # Changes to be committed:
>>>>>>> # modified: gcc/c-family/c-common.cc
>>>>>>> # modified: gcc/c-family/c-common.h
>>>>>>> # modified: gcc/c/c-decl.cc
>>>>>>> # modified: gcc/c/c-typeck.cc
>>>>>>> # modified: gcc/cp/cp-tree.h
>>>>>>> # modified: gcc/cp/decl.cc
>>>>>>> # modified: gcc/cp/mangle.cc
>>>>>>> # modified: gcc/cp/parser.cc
>>>>>>> # modified: gcc/cp/pt.cc
>>>>>>> # modified: gcc/cp/tree.cc
>>>>>>> # modified: gcc/cp/typeck.cc
>>>>>>> # modified: gcc/doc/extend.texi
>>>>>>> # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>>>>>> # new file: gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>>>>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>>>>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>>>>>> # new file: gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>>>>>> # new file: gcc/testsuite/g++.dg/parse/addr-space.C
>>>>>>> # new file: gcc/testsuite/g++.dg/parse/addr-space1.C
>>>>>>> # new file: gcc/testsuite/g++.dg/parse/addr-space2.C
>>>>>>> # new file: gcc/testsuite/g++.dg/template/spec-addr-space.C
>>>>>>> # modified: gcc/tree.h
>>>>>>> #
>>>>>>> # Untracked files:
>>>>>>> # Makefile
>>>>>>> # addr-space-decl.C
>>>>>>> # addr-space-decl.s
>>>>>>> # addr-space-ops.C
>>>>>>> # addr-space-ref.C
>>>>>>> # addr-space-template.C
>>>>>>> # addr-space-template.s
>>>>>>> # addr-space-template2.C
>>>>>>> # addr-space-template2.C.006t.gimple
>>>>>>> # addr-space-template2.s
>>>>>>> # addr-space-traits.C
>>>>>>> # addr-space4.C
>>>>>>> # addr-space4.C.006t.gimple
>>>>>>> # addr-space4.s
>>>>>>> # build-x86_64-pc-linux-gnu/
>>>>>>> # compare
>>>>>>> # host-x86_64-pc-linux-gnu/
>>>>>>> # mangle-addr-space1.s
>>>>>>> # prev-x86_64-pc-linux-gnu/
>>>>>>> # rename
>>>>>>> # stage1-x86_64-pc-linux-gnu/
>>>>>>> # stage_current
>>>>>>> # stage_final
>>>>>>> # stage_last
>>>>>>> # x86_64-pc-linux-gnu/
>>>>>>> #
>>>>>>> # ------------------------ >8 ------------------------
>>>>>>> # Do not modify or remove the line above.
>>>>>>> # Everything below it will be ignored.
>>>>>>> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
>>>>>>> index bb0544eeaea..ff1146ecc25 100644
>>>>>>> --- a/gcc/c-family/c-common.cc
>>>>>>> +++ b/gcc/c-family/c-common.cc
>>>>>>> @@ -615,6 +615,33 @@ c_addr_space_name (addr_space_t as)
>>>>>>> return IDENTIFIER_POINTER (ridpointers [rid]);
>>>>>>> }
>>>>>>> +/* Return true if between two named address spaces, whether there is a superset
>>>>>>> + named address space that encompasses both address spaces. If there is a
>>>>>>> + superset, return which address space is the superset. */
>>>>>>> +
>>>>>>> +bool
>>>>>>> +addr_space_superset (addr_space_t as1, addr_space_t as2,
>>>>>>> + addr_space_t * common)
>>>>>>> +{
>>>>>>> + if (as1 == as2)
>>>>>>> + {
>>>>>>> + *common = as1;
>>>>>>> + return true;
>>>>>>> + }
>>>>>>> + else if (targetm.addr_space.subset_p (as1, as2))
>>>>>>> + {
>>>>>>> + *common = as2;
>>>>>>> + return true;
>>>>>>> + }
>>>>>>> + else if (targetm.addr_space.subset_p (as2, as1))
>>>>>>> + {
>>>>>>> + *common = as1;
>>>>>>> + return true;
>>>>>>> + }
>>>>>>> + else
>>>>>>> + return false;
>>>>>>> +}
>>>>>>> +
>>>>>>> /* Push current bindings for the function name VAR_DECLS. */
>>>>>>> void
>>>>>>> @@ -2809,6 +2836,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
>>>>>>> return build_nonstandard_integer_type (width, unsignedp);
>>>>>>> }
>>>>>>> +/* Register reserved keyword WORD as qualifier for address space AS. */
>>>>>>> +
>>>>>>> +void
>>>>>>> +c_register_addr_space (const char *word, addr_space_t as)
>>>>>>> +{
>>>>>>> + int rid = RID_FIRST_ADDR_SPACE + as;
>>>>>>> + tree id;
>>>>>>> +
>>>>>>> + /* Address space qualifiers are only supported
>>>>>>> + in C with GNU extensions enabled. */
>>>>>>> + if (c_dialect_objc () || flag_no_asm)
>>>>>>> + return;
>>>>>>> +
>>>>>>> + id = get_identifier (word);
>>>>>>> + C_SET_RID_CODE (id, rid);
>>>>>>> + TREE_LANG_FLAG_0 (id) = 1;
>>>>>>> + ridpointers[rid] = id;
>>>>>>> +}
>>>>>>> +
>>>>>>> /* The C version of the register_builtin_type langhook. */
>>>>>>> void
>>>>>>> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
>>>>>>> index 52a85bfb783..d36f9e4975b 100644
>>>>>>> --- a/gcc/c-family/c-common.h
>>>>>>> +++ b/gcc/c-family/c-common.h
>>>>>>> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>>>>>>> extern tree (*make_fname_decl) (location_t, tree, int);
>>>>>>> -/* In c-decl.cc and cp/tree.cc. FIXME. */
>>>>>>> -extern void c_register_addr_space (const char *str, addr_space_t as);
>>>>>>> -
>>>>>>> /* In c-common.cc. */
>>>>>>> extern bool in_late_binary_op;
>>>>>>> extern const char *c_addr_space_name (addr_space_t as);
>>>>>>> +extern const char *c_addr_space_name (addr_space_t as);
>>>>>>> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
>>>>>>> extern tree identifier_global_value (tree);
>>>>>>> extern tree identifier_global_tag (tree);
>>>>>>> extern bool names_builtin_p (const char *);
>>>>>>> @@ -952,6 +951,7 @@ extern void c_common_finish (void);
>>>>>>> extern void c_common_parse_file (void);
>>>>>>> extern FILE *get_dump_info (int, dump_flags_t *);
>>>>>>> extern alias_set_type c_common_get_alias_set (tree);
>>>>>>> +extern void c_register_addr_space (const char *, addr_space_t);
>>>>>>> extern void c_register_builtin_type (tree, const char*);
>>>>>>> extern bool c_promoting_integer_type_p (const_tree);
>>>>>>> extern bool self_promoting_args_p (const_tree);
>>>>>>> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
>>>>>>> index c701f07befe..e1bb4f1cf37 100644
>>>>>>> --- a/gcc/c/c-decl.cc
>>>>>>> +++ b/gcc/c/c-decl.cc
>>>>>>> @@ -12337,25 +12337,6 @@ c_parse_final_cleanups (void)
>>>>>>> ext_block = NULL;
>>>>>>> }
>>>>>>> -/* Register reserved keyword WORD as qualifier for address space AS. */
>>>>>>> -
>>>>>>> -void
>>>>>>> -c_register_addr_space (const char *word, addr_space_t as)
>>>>>>> -{
>>>>>>> - int rid = RID_FIRST_ADDR_SPACE + as;
>>>>>>> - tree id;
>>>>>>> -
>>>>>>> - /* Address space qualifiers are only supported
>>>>>>> - in C with GNU extensions enabled. */
>>>>>>> - if (c_dialect_objc () || flag_no_asm)
>>>>>>> - return;
>>>>>>> -
>>>>>>> - id = get_identifier (word);
>>>>>>> - C_SET_RID_CODE (id, rid);
>>>>>>> - C_IS_RESERVED_WORD (id) = 1;
>>>>>>> - ridpointers [rid] = id;
>>>>>>> -}
>>>>>>> -
>>>>>>> /* Return identifier to look up for omp declare reduction. */
>>>>>>> tree
>>>>>>> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
>>>>>>> index b271af9bedb..c4b01368534 100644
>>>>>>> --- a/gcc/c/c-typeck.cc
>>>>>>> +++ b/gcc/c/c-typeck.cc
>>>>>>> @@ -296,32 +296,6 @@ c_type_promotes_to (tree type)
>>>>>>> return type;
>>>>>>> }
>>>>>>> -/* Return true if between two named address spaces, whether there is a superset
>>>>>>> - named address space that encompasses both address spaces. If there is a
>>>>>>> - superset, return which address space is the superset. */
>>>>>>> -
>>>>>>> -static bool
>>>>>>> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
>>>>>>> -{
>>>>>>> - if (as1 == as2)
>>>>>>> - {
>>>>>>> - *common = as1;
>>>>>>> - return true;
>>>>>>> - }
>>>>>>> - else if (targetm.addr_space.subset_p (as1, as2))
>>>>>>> - {
>>>>>>> - *common = as2;
>>>>>>> - return true;
>>>>>>> - }
>>>>>>> - else if (targetm.addr_space.subset_p (as2, as1))
>>>>>>> - {
>>>>>>> - *common = as1;
>>>>>>> - return true;
>>>>>>> - }
>>>>>>> - else
>>>>>>> - return false;
>>>>>>> -}
>>>>>>> -
>>>>>>> /* Return a variant of TYPE which has all the type qualifiers of LIKE
>>>>>>> as well as those of TYPE. */
>>>>>>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>>>>>>> index 72f4398a8f9..82a6d72f5df 100644
>>>>>>> --- a/gcc/cp/cp-tree.h
>>>>>>> +++ b/gcc/cp/cp-tree.h
>>>>>>> @@ -6206,6 +6206,7 @@ enum cp_decl_spec {
>>>>>>> ds_const,
>>>>>>> ds_volatile,
>>>>>>> ds_restrict,
>>>>>>> + ds_addr_space,
>>>>>>> ds_inline,
>>>>>>> ds_virtual,
>>>>>>> ds_explicit,
>>>>>>> @@ -6252,6 +6253,8 @@ struct cp_decl_specifier_seq {
>>>>>>> cp_storage_class storage_class;
>>>>>>> /* For the __intN declspec, this stores the index into the int_n_* arrays. */
>>>>>>> int int_n_idx;
>>>>>>> + /* The address space that the declaration belongs to. */
>>>>>>> + addr_space_t address_space;
>>>>>>> /* True iff TYPE_SPEC defines a class or enum. */
>>>>>>> BOOL_BITFIELD type_definition_p : 1;
>>>>>>> /* True iff multiple types were (erroneously) specified for this
>>>>>>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
>>>>>>> index 9f78c500a15..fa5a1ddb21d 100644
>>>>>>> --- a/gcc/cp/decl.cc
>>>>>>> +++ b/gcc/cp/decl.cc
>>>>>>> @@ -5280,6 +5280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
>>>>>>> type_quals |= TYPE_QUAL_VOLATILE;
>>>>>>> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
>>>>>>> type_quals |= TYPE_QUAL_RESTRICT;
>>>>>>> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>>>>>> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>>>>>>> return type_quals;
>>>>>>> }
>>>>>>> @@ -5402,6 +5404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
>>>>>>> error_at (declspecs->locations[ds_restrict],
>>>>>>> "%<__restrict%> can only be specified for objects and "
>>>>>>> "functions");
>>>>>>> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>>>>>> + error_at (declspecs->locations[ds_addr_space],
>>>>>>> + "address space can only be specified for objects and "
>>>>>>> + "functions");
>>>>>>> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
>>>>>>> error_at (declspecs->locations[ds_thread],
>>>>>>> "%<__thread%> can only be specified for objects "
>>>>>>> @@ -14572,6 +14578,57 @@ grokdeclarator (const cp_declarator *declarator,
>>>>>>> if (!processing_template_decl)
>>>>>>> cp_apply_type_quals_to_decl (type_quals, decl);
>>>>>>> + addr_space_t address_space = declspecs->address_space;
>>>>>>> + if (!ADDR_SPACE_GENERIC_P (address_space) && !INDIRECT_TYPE_P (type))
>>>>>>> + {
>>>>>>> + if (decl_context == NORMAL)
>>>>>>> + {
>>>>>>> + switch (storage_class)
>>>>>>> + {
>>>>>>> + case sc_auto:
>>>>>>> + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
>>>>>>> + c_addr_space_name (address_space), name);
>>>>>>> + break;
>>>>>>> + case sc_register:
>>>>>>> + error ("%qs combined with %<register%> qualifier for %qs",
>>>>>>> + c_addr_space_name (address_space), name);
>>>>>>> + break;
>>>>>>> + case sc_none:
>>>>>>> + if (! toplevel_bindings_p ())
>>>>>>> + error ("%qs specified for auto variable %qs",
>>>>>>> + c_addr_space_name (address_space), name);
>>>>>>> + break;
>>>>>>> + case sc_mutable:
>>>>>>> + error ("%qs combined with %<mutable%> qualifier for %qs",
>>>>>>> + c_addr_space_name (address_space), name);
>>>>>>> + break;
>>>>>>> + case sc_static:
>>>>>>> + case sc_extern:
>>>>>>> + break;
>>>>>>> + default:
>>>>>>> + gcc_unreachable ();
>>>>>>> + }
>>>>>>> + }
>>>>>>> + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
>>>>>>> + {
>>>>>>> + if (name)
>>>>>>> + error ("%qs specified for parameter %qs",
>>>>>>> + c_addr_space_name (address_space), name);
>>>>>>> + else
>>>>>>> + error ("%qs specified for unnamed parameter",
>>>>>>> + c_addr_space_name (address_space));
>>>>>>> + }
>>>>>>> + else if (decl_context == FIELD)
>>>>>>> + {
>>>>>>> + if (name)
>>>>>>> + error ("%qs specified for structure field %qs",
>>>>>>> + c_addr_space_name (address_space), name);
>>>>>>> + else
>>>>>>> + error ("%qs specified for structure field",
>>>>>>> + c_addr_space_name (address_space));
>>>>>>> + }
>>>>>>> + }
>>>>>>> +
>>>>>>> return decl;
>>>>>>> }
>>>>>>> }
>>>>>>> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
>>>>>>> index eb53e0ebeb4..16cb2ff4332 100644
>>>>>>> --- a/gcc/cp/mangle.cc
>>>>>>> +++ b/gcc/cp/mangle.cc
>>>>>>> @@ -2509,6 +2509,14 @@ write_CV_qualifiers_for_type (const tree type)
>>>>>>> array. */
>>>>>>> cp_cv_quals quals = TYPE_QUALS (type);
>>>>>>> + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
>>>>>>> + {
>>>>>>> + const char *as_name = c_addr_space_name (as);
>>>>>>> + write_char ('U');
>>>>>>> + write_unsigned_number (strlen (as_name));
>>>>>>> + write_string (as_name);
>>>>>>> + ++num_qualifiers;
>>>>>>> + }
>>>>>>> if (quals & TYPE_QUAL_RESTRICT)
>>>>>>> {
>>>>>>> write_char ('r');
>>>>>>> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
>>>>>>> index 763df6f479b..f0169d83ab0 100644
>>>>>>> --- a/gcc/cp/parser.cc
>>>>>>> +++ b/gcc/cp/parser.cc
>>>>>>> @@ -7640,6 +7640,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
>>>>>>> postfix_expression = error_mark_node;
>>>>>>> break;
>>>>>>> }
>>>>>>> + if (type != error_mark_node
>>>>>>> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
>>>>>>> + && current_function_decl)
>>>>>>> + {
>>>>>>> + error
>>>>>>> + ("compound literal qualified by address-space qualifier");
>>>>>>> + type = error_mark_node;
>>>>>>> + }
>>>>>>> /* Form the representation of the compound-literal. */
>>>>>>> postfix_expression
>>>>>>> = finish_compound_literal (type, initializer,
>>>>>>> @@ -19408,6 +19416,15 @@ cp_parser_type_specifier (cp_parser* parser,
>>>>>>> break;
>>>>>>> }
>>>>>>> +
>>>>>>> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
>>>>>>> + {
>>>>>>> + ds = ds_addr_space;
>>>>>>> + if (is_cv_qualifier)
>>>>>>> + *is_cv_qualifier = true;
>>>>>>> + }
>>>>>>> +
>>>>>>> +
>>>>>>> /* Handle simple keywords. */
>>>>>>> if (ds != ds_last)
>>>>>>> {
>>>>>>> @@ -23776,6 +23793,7 @@ cp_parser_ptr_operator (cp_parser* parser,
>>>>>>> GNU Extension:
>>>>>>> cv-qualifier:
>>>>>>> + address-space-qualifier
>>>>>>> __restrict__
>>>>>>> Returns a bitmask representing the cv-qualifiers. */
>>>>>>> @@ -23812,6 +23830,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
>>>>>>> break;
>>>>>>> }
>>>>>>> + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
>>>>>>> + token->keyword <= RID_LAST_ADDR_SPACE)
>>>>>>> + cv_qualifier =
>>>>>>> + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>>>>>>> +
>>>>>>> if (!cv_qualifier)
>>>>>>> break;
>>>>>>> @@ -32705,6 +32728,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>>>>>> decl_specs->locations[ds] = location;
>>>>>>> if (ds == ds_thread)
>>>>>>> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
>>>>>>> + else if (ds == ds_addr_space)
>>>>>>> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
>>>>>>> }
>>>>>>> else
>>>>>>> {
>>>>>>> @@ -32737,6 +32762,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>>>>>> error_at (&richloc, "duplicate %qD", token->u.value);
>>>>>>> }
>>>>>>> }
>>>>>>> + else if (ds == ds_addr_space)
>>>>>>> + {
>>>>>>> + addr_space_t as1 = decl_specs->address_space;
>>>>>>> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
>>>>>>> +
>>>>>>> + gcc_rich_location richloc (location);
>>>>>>> + richloc.add_fixit_remove ();
>>>>>>> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
>>>>>>> + && as1 != as2)
>>>>>>> + error_at (&richloc,
>>>>>>> + "conflicting named address spaces (%s vs %s)",
>>>>>>> + c_addr_space_name (as1), c_addr_space_name (as2));
>>>>>>> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
>>>>>>> + error_at (&richloc,
>>>>>>> + "duplicate named address space %s",
>>>>>>> + c_addr_space_name (as1));
>>>>>>> +
>>>>>>> + decl_specs->address_space = as2;
>>>>>>> + }
>>>>>>> else
>>>>>>> {
>>>>>>> static const char *const decl_spec_names[] = {
>>>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>>>> index bef31416fb7..c9d1c01f4ce 100644
>>>>>>> --- a/gcc/cp/pt.cc
>>>>>>> +++ b/gcc/cp/pt.cc
>>>>>>> @@ -23655,8 +23655,19 @@ template_decl_level (tree decl)
>>>>>>> static int
>>>>>>> check_cv_quals_for_unify (int strict, tree arg, tree parm)
>>>>>>> {
>>>>>>> - int arg_quals = cp_type_quals (arg);
>>>>>>> - int parm_quals = cp_type_quals (parm);
>>>>>>> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>>>>>> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>>>>>> +
>>>>>>> + /* Try to unify ARG's address space into PARM's address space.
>>>>>>> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
>>>>>>> + there are no constraints on address spaces for this type. */
>>>>>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>>>>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>>>>>> + addr_space_t as_common;
>>>>>>> + addr_space_superset (as_arg, as_parm, &as_common);
>>>>>>> +
>>>>>>> + if (!(as_parm == as_common || as_parm == 0))
>>>>>>> + return 0;
>>>>>>> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
>>>>>>> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
>>>>>>> @@ -24292,10 +24303,26 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
>>>>>>> arg, parm))
>>>>>>> return unify_cv_qual_mismatch (explain_p, parm, arg);
>>>>>>> + int arg_cv_quals = cp_type_quals (arg);
>>>>>>> + int parm_cv_quals = cp_type_quals (parm);
>>>>>>> +
>>>>>>> + /* If PARM does not contain any address space constraints it can
>>>>>>> + fully match the address space of ARG. However, if PARM contains an
>>>>>>> + address space constraint, it becomes the upper bound. That is,
>>>>>>> + AS_ARG may be promoted to AS_PARM but not the converse. If we
>>>>>>> + ended up here, it means that `check_cv_quals_for_unify' succeeded
>>>>>>> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
>>>>>>> + AS_PARM. */
>>>>>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
>>>>>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
>>>>>>> + addr_space_t as_common = as_parm ? 0 : as_arg;
>>>>>>> +
>>>>>>> /* Consider the case where ARG is `const volatile int' and
>>>>>>> PARM is `const T'. Then, T should be `volatile int'. */
>>>>>>> - arg = cp_build_qualified_type_real
>>>>>>> - (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
>>>>>>> + int unified_cv =
>>>>>>> + CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
>>>>>>> + | ENCODE_QUAL_ADDR_SPACE (as_common);
>>>>>>> + arg = cp_build_qualified_type_real (arg, unified_cv, tf_none);
>>>>>>> if (arg == error_mark_node)
>>>>>>> return unify_invalid (explain_p);
>>>>>>> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
>>>>>>> index 3b37567cbd7..5e14ac837fc 100644
>>>>>>> --- a/gcc/cp/tree.cc
>>>>>>> +++ b/gcc/cp/tree.cc
>>>>>>> @@ -6048,15 +6048,6 @@ cp_free_lang_data (tree t)
>>>>>>> DECL_CHAIN (t) = NULL_TREE;
>>>>>>> }
>>>>>>> -/* Stub for c-common. Please keep in sync with c-decl.cc.
>>>>>>> - FIXME: If address space support is target specific, then this
>>>>>>> - should be a C target hook. But currently this is not possible,
>>>>>>> - because this function is called via REGISTER_TARGET_PRAGMAS. */
>>>>>>> -void
>>>>>>> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
>>>>>>> -{
>>>>>>> -}
>>>>>>> -
>>>>>>> /* Return the number of operands in T that we care about for things like
>>>>>>> mangling. */
>>>>>>> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
>>>>>>> index ceb80d9744f..7d810c6a12c 100644
>>>>>>> --- a/gcc/cp/typeck.cc
>>>>>>> +++ b/gcc/cp/typeck.cc
>>>>>>> @@ -656,10 +656,26 @@ composite_pointer_type (const op_location_t &location,
>>>>>>> else
>>>>>>> return error_mark_node;
>>>>>>> }
>>>>>>> + /* If possible merge the address space into the superset of the address
>>>>>>> + spaces of t1 and t2, or raise an error. */
>>>>>>> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
>>>>>>> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
>>>>>>> + addr_space_t as_common;
>>>>>>> +
>>>>>>> + /* If the two named address spaces are different, determine the common
>>>>>>> + superset address space. If there isn't one, raise an error. */
>>>>>>> + if (!addr_space_superset (as_t1, as_t2, &as_common))
>>>>>>> + {
>>>>>>> + as_common = as_t1;
>>>>>>> + error_at (location,
>>>>>>> + "%qT and %qT are in disjoint named address spaces",
>>>>>>> + t1, t2);
>>>>>>> + }
>>>>>>> result_type
>>>>>>> = cp_build_qualified_type (void_type_node,
>>>>>>> - (cp_type_quals (TREE_TYPE (t1))
>>>>>>> - | cp_type_quals (TREE_TYPE (t2))));
>>>>>>> + (CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t1)))
>>>>>>> + | CLEAR_QUAL_ADDR_SPACE (cp_type_quals (TREE_TYPE (t2)))
>>>>>>> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
>>>>>>> result_type = build_pointer_type (result_type);
>>>>>>> /* Merge the attributes. */
>>>>>>> attributes = (*targetm.merge_type_attributes) (t1, t2);
>>>>>>> @@ -1579,7 +1595,9 @@ comptypes (tree t1, tree t2, int strict)
>>>>>>> }
>>>>>>> /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
>>>>>>> - top-level qualifiers. */
>>>>>>> + top-level qualifiers, except for named address spaces. If the pointers point
>>>>>>> + to different named addresses spaces, then we must determine if one address
>>>>>>> + space is a subset of the other. */
>>>>>>> bool
>>>>>>> same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>>>>>> @@ -1589,6 +1607,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>>>>>> if (type1 == type2)
>>>>>>> return true;
>>>>>>> + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
>>>>>>> + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
>>>>>>> + addr_space_t as_common;
>>>>>>> +
>>>>>>> + /* Fail if pointers point to incompatible address spaces. */
>>>>>>> + if (!addr_space_superset (as_type1, as_type2, &as_common))
>>>>>>> + return false;
>>>>>>> +
>>>>>>> type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
>>>>>>> type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
>>>>>>> return same_type_p (type1, type2);
>>>>>>> @@ -6374,10 +6400,32 @@ static tree
>>>>>>> pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
>>>>>>> tsubst_flags_t complain, tree *instrument_expr)
>>>>>>> {
>>>>>>> - tree result, inttype;
>>>>>>> tree restype = ptrdiff_type_node;
>>>>>>> + tree result, inttype;
>>>>>>> +
>>>>>>> + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
>>>>>>> + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
>>>>>>> tree target_type = TREE_TYPE (ptrtype);
>>>>>>> + /* If the operands point into different address spaces, we need to
>>>>>>> + explicitly convert them to pointers into the common address space
>>>>>>> + before we can subtract the numerical address values. */
>>>>>>> + if (as0 != as1)
>>>>>>> + {
>>>>>>> + addr_space_t as_common;
>>>>>>> + tree common_type;
>>>>>>> +
>>>>>>> + /* Determine the common superset address space. This is guaranteed
>>>>>>> + to exist because the caller verified that comp_target_types
>>>>>>> + returned non-zero. */
>>>>>>> + if (!addr_space_superset (as0, as1, &as_common))
>>>>>>> + gcc_unreachable ();
>>>>>>> +
>>>>>>> + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
>>>>>>> + op0 = convert (common_type, op0);
>>>>>>> + op1 = convert (common_type, op1);
>>>>>>> + }
>>>>>>> +
>>>>>>> if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
>>>>>>> return error_mark_node;
>>>>>>> @@ -10805,6 +10853,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>>>>>>> to_more_cv_qualified = true;
>>>>>>> }
>>>>>>> + /* Warn about conversions between pointers to disjoint
>>>>>>> + address spaces. */
>>>>>>> + if (TREE_CODE (from) == POINTER_TYPE
>>>>>>> + && TREE_CODE (to) == POINTER_TYPE)
>>>>>>> + {
>>>>>>> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
>>>>>>> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
>>>>>>> + addr_space_t as_common;
>>>>>>> +
>>>>>>> + if (!addr_space_superset (as_to, as_from, &as_common))
>>>>>>> + return false;
>>>>>>> + }
>>>>>>> +
>>>>>>> if (constp > 0)
>>>>>>> constp &= TYPE_READONLY (to);
>>>>>>> }
>>>>>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>>>>>> index ac3db88566d..1e0d436c02c 100644
>>>>>>> --- a/gcc/doc/extend.texi
>>>>>>> +++ b/gcc/doc/extend.texi
>>>>>>> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
>>>>>>> @section Named Address Spaces
>>>>>>> @cindex Named Address Spaces
>>>>>>> -As an extension, GNU C supports named address spaces as
>>>>>>> +As an extension, GNU C and GNU C++ support named address spaces as
>>>>>>> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
>>>>>>> address spaces in GCC will evolve as the draft technical report
>>>>>>> changes. Calling conventions for any target might also change. At
>>>>>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..c01f8d6054a
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>>>>>> @@ -0,0 +1,10 @@
>>>>>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>>>>>> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
>>>>>>> +
>>>>>>> +int f (int volatile __seg_fs *a)
>>>>>>> +{
>>>>>>> + return *a;
>>>>>>> +}
>>>>>>> +
>>>>>>> +int main () {}
>>>>>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..862bbbdcdf2
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>>>>>> @@ -0,0 +1,9 @@
>>>>>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>>>>>> +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
>>>>>>> +
>>>>>>> +template <class T>
>>>>>>> +int f (T *p) { return *p; }
>>>>>>> +int g (__seg_fs int *p) { return *p; }
>>>>>>> +__seg_fs int *a;
>>>>>>> +int main() { f(a); }
>>>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..c04d2f497da
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>>>>>> @@ -0,0 +1,5 @@
>>>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +__seg_fs char a, b, c;
>>>>>>> +__seg_fs const int *p;
>>>>>>> +static /* give internal linkage to the following anonymous struct */
>>>>>>> +__seg_fs struct { int a; char b; } * __seg_gs q;
>>>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..86c02d1e7f5
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>>>>>> @@ -0,0 +1,20 @@
>>>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +int __seg_fs * fs1;
>>>>>>> +int __seg_fs * fs2;
>>>>>>> +float __seg_gs * gs1;
>>>>>>> +float __seg_gs * gs2;
>>>>>>> +
>>>>>>> +int
>>>>>>> +main ()
>>>>>>> +{
>>>>>>> + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
>>>>>>> + fs1 - fs2;
>>>>>>> + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
>>>>>>> + fs1 == fs2;
>>>>>>> + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>>>>>> + fs1 = fs2;
>>>>>>> + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
>>>>>>> + fs1 > fs2;
>>>>>>> + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..12d7975e560
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>>>>>> @@ -0,0 +1,31 @@
>>>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +// { dg-prune-output "does not allow .register. storage class specifier" }
>>>>>>> +int __seg_fs * outer_b;
>>>>>>> +
>>>>>>> +struct s {
>>>>>>> + __seg_fs int * ok;
>>>>>>> + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
>>>>>>> +};
>>>>>>> +
>>>>>>> +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
>>>>>>> +
>>>>>>> +namespace ns_a
>>>>>>> +{
>>>>>>> + int __seg_fs * inner_b;
>>>>>>> +
>>>>>>> + template<typename T>
>>>>>>> + int f (T &a) { return a; }
>>>>>>> + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
>>>>>>> + int h (__seg_fs int *a) { return *a; }
>>>>>>> +}
>>>>>>> +
>>>>>>> +int
>>>>>>> +main ()
>>>>>>> +{
>>>>>>> + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
>>>>>>> + static __seg_gs int static_gs;
>>>>>>> + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
>>>>>>> + __seg_fs int *pa = outer_b;
>>>>>>> + __seg_fs int& ra = *ns_a::inner_b;
>>>>>>> + return ns_a::f(ra) + ns_a::f(*pa);
>>>>>>> +}
>>>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..ebb6316054a
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
>>>>>>> @@ -0,0 +1,9 @@
>>>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +
>>>>>>> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
>>>>>>> +
>>>>>>> +int
>>>>>>> +main ()
>>>>>>> +{
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..2e8ee32a885
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>>>>>> @@ -0,0 +1,10 @@
>>>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +// { dg-options "-std=gnu++98" }
>>>>>>> +
>>>>>>> +int
>>>>>>> +main ()
>>>>>>> +{
>>>>>>> + struct foo {int a; char b[2];} structure;
>>>>>>> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..5b2c0f28078
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>>>>>> @@ -0,0 +1,9 @@
>>>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +
>>>>>>> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
>>>>>>> +
>>>>>>> +int
>>>>>>> +main ()
>>>>>>> +{
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>>>>>> new file mode 100644
>>>>>>> index 00000000000..ae9f4de0e1f
>>>>>>> --- /dev/null
>>>>>>> +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>>>>>> @@ -0,0 +1,8 @@
>>>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>>>> +
>>>>>>> +template <class T>
>>>>>>> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
>>>>>>> + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
>>>>>>> +__seg_fs int *a;
>>>>>>> +int main() { f(a); } // { dg-error "no matching" }
>>>>>>> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
>>>>>>> diff --git a/gcc/tree.h b/gcc/tree.h
>>>>>>> index 8844471e9a5..b7da4c5141a 100644
>>>>>>> --- a/gcc/tree.h
>>>>>>> +++ b/gcc/tree.h
>>>>>>> @@ -2229,7 +2229,7 @@ extern tree vector_element_bits_tree (const_tree);
>>>>>>> /* Encode/decode the named memory support as part of the qualifier. If more
>>>>>>> than 8 qualifiers are added, these macros need to be adjusted. */
>>>>>>> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
>>>>>>> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
>>>>>>> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>>>>>>> /* Return all qualifiers except for the address space qualifiers. */
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>
>>
>>
>>
>>
>>
>>
>
>
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-10-14 15:19 ` Jason Merrill
@ 2022-10-18 7:37 ` Paul Iannetta
2022-10-18 14:24 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-10-18 7:37 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
On Fri, Oct 14, 2022 at 11:19:50AM -0400, Jason Merrill wrote:
> On 10/13/22 17:57, Paul Iannetta wrote:
> > On Thu, Oct 13, 2022 at 03:41:16PM -0400, Jason Merrill wrote:
> > > On 10/13/22 12:02, Paul Iannetta wrote:
> > > > On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
> > > > > On 10/13/22 11:23, Paul Iannetta wrote:
> > > > > > On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
> > > > > > > On 10/12/22 20:52, Paul Iannetta wrote:
> > > > > > > > There are quite a few things I would like to clarify concerning some
> > > > > > > > implementation details.
> > > > > > > > - A variable with automatic storage (which is neither a pointer nor
> > > > > > > > a reference) cannot be qualified with an address space. I detect
> > > > > > > > this by the combination of `sc_none' and `! toplevel_bindings_p ()',
> > > > > > > > but I've also seen the use of `at_function_scope' at other places.
> > > > > > > > And I'm unsure which one is appropriate here.
> > > > > > > > This detection happens at the very end of grokdeclarator because I
> > > > > > > > need to know that the type is a pointer, which is not know until
> > > > > > > > very late in the function.
> > > > > > >
> > > > > > > At that point you have the decl, and you can ask directly what its storage
> > > > > > > duration is, perhaps using decl_storage_duration.
> > > > > > >
> > > > > > > But why do you need to know whether the type is a pointer? The attribute
> > > > > > > applies to the target type of the pointer, not the pointer type. I think
> > > > > > > the problem is that you're looking at declspecs when you ought to be looking
> > > > > > > at type_quals.
> > > > > >
> > > > > > I need to know that the base type is a pointer to reject invalid
> > > > > > declarations such as:
> > > > > >
> > > > > > int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
> > > > > >
> > > > > > because parameters and auto variables can have an address space
> > > > > > qualifier only if they are pointer or reference type, which I can't
> > > > > > tell only from type_quals.
> > > > >
> > > > > But "int *__seg_fs a" is just as invalid as the above; the difference is not
> > > > > whether a is a pointer, but whether the address-space-qualified is the type
> > > > > of a itself or some sub-type.
> > > >
> > > > I agree that "int * __seg_fs a" is invalid but it is accepted by the C
> > > > front-end, and by clang (both C and C++), the behavior is that the
> > > > address-name is silently ignored.
> > >
> > > Hmm, that sounds like a bug; in that case, presumably the user meant to
> > > qualify the pointed-to type, and silently ignoring seems unlikely to give
> > > the effect they want.
> > >
> >
> > Well, actually, I'm re-reading the draft and "int * __seg_fs a" is
> > valid. It means "pointer in address space __seg_fs pointing to an
> > object in the generic address space", whereas "__seg_fs int * a" means
> > "pointer in the generic address space pointing to an object in the
> > __seg_fs address-space".
> >
> > Oddities such as, "__seg_fs int * __seg_gs a" are also perfectly
> > valid.
>
> If a has static storage duration, sure; I was still thinking about
> declarations with automatic storage duration such as in your example above.
>
Thanks, I only use type_quals now. I also took into account the style
recommendations from Jakub, and included the other template tests.
I rebased over trunk, bootstrapped the compiler and run the "make
check-gcc" with no regressions on x86.
Paul
# ------------------------ >8 ------------------------
Add support for custom address spaces in C++
gcc/
* tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
gcc/c/
* c-decl.cc: Remove c_register_addr_space.
gcc/c-family/
* c-common.cc (c_register_addr_space): Imported from c-decl.cc
(addr_space_superset): Imported from gcc/c/c-typecheck.cc
* c-common.h: Remove the FIXME.
(addr_space_superset): New declaration.
gcc/cp/
* cp-tree.h (enum cp_decl_spec): Add addr_space support.
(struct cp_decl_specifier_seq): Likewise.
* decl.cc (get_type_quals): Likewise.
(check_tag_decl): Likewise.
(grokdeclarator): Likewise.
* parser.cc (cp_parser_type_specifier): Likewise.
(cp_parser_cv_qualifier_seq_opt): Likewise.
(cp_parser_postfix_expression): Likewise.
(cp_parser_type_specifier): Likewise.
(set_and_check_decl_spec_loc): Likewise.
* typeck.cc (composite_pointer_type): Likewise
(comp_ptr_ttypes_real): Likewise.
(same_type_ignoring_top_level_qualifiers_p): Likewise.
* pt.cc (check_cv_quals_for_unify): Likewise.
(unify): Likewise.
* tree.cc: Remove c_register_addr_space stub.
* mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
using the extended qualifier notation.
gcc/doc
* extend.texi (Named Address Spaces): add a mention about C++
support.
gcc/testsuite/
* g++.dg/abi/mangle-addr-space1.C: New test.
* g++.dg/abi/mangle-addr-space2.C: New test.
* g++.dg/parse/addr-space.C: New test.
* g++.dg/parse/addr-space1.C: New test.
* g++.dg/parse/addr-space2.C: New test.
* g++.dg/parse/template/spec-addr-space.C: New test.
* g++.dg/ext/addr-space-decl.C: New test.
* g++.dg/ext/addr-space-ref.C: New test.
* g++.dg/ext/addr-space-ops.C: New test.
* g++.dg/template/addr-space-overload.C: New test.
* g++.dg/template/addr-space-strip1.C: New test.
* g++.dg/template/addr-space-strip2.C: New test.
# ------------------------ >8 ------------------------
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 9ec9100cc90..3b79dc47515 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -588,6 +588,33 @@ c_addr_space_name (addr_space_t as)
return IDENTIFIER_POINTER (ridpointers [rid]);
}
+/* Return true if between two named address spaces, whether there is a superset
+ named address space that encompasses both address spaces. If there is a
+ superset, return which address space is the superset. */
+
+bool
+addr_space_superset (addr_space_t as1, addr_space_t as2,
+ addr_space_t * common)
+{
+ if (as1 == as2)
+ {
+ *common = as1;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as1, as2))
+ {
+ *common = as2;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as2, as1))
+ {
+ *common = as1;
+ return true;
+ }
+ else
+ return false;
+}
+
/* Push current bindings for the function name VAR_DECLS. */
void
@@ -2785,6 +2812,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ TREE_LANG_FLAG_0 (id) = 1;
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 62ab4ba437b..a3864d874aa 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
+extern const char *c_addr_space_name (addr_space_t as);
+extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
extern bool names_builtin_p (const char *);
@@ -951,6 +950,7 @@ extern bool c_common_init (void);
extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index a7571cc7542..b1f69997ff7 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -12531,25 +12531,6 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
-}
-
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index fdb96c28c51..2a700bbaff3 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -303,32 +303,6 @@ c_type_promotes_to (tree type)
return type;
}
-/* Return true if between two named address spaces, whether there is a superset
- named address space that encompasses both address spaces. If there is a
- superset, return which address space is the superset. */
-
-static bool
-addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
-{
- if (as1 == as2)
- {
- *common = as1;
- return true;
- }
- else if (targetm.addr_space.subset_p (as1, as2))
- {
- *common = as2;
- return true;
- }
- else if (targetm.addr_space.subset_p (as2, as1))
- {
- *common = as1;
- return true;
- }
- else
- return false;
-}
-
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e2607f09c19..0248569a95b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6235,6 +6235,7 @@ enum cp_decl_spec {
ds_const,
ds_volatile,
ds_restrict,
+ ds_addr_space,
ds_inline,
ds_virtual,
ds_explicit,
@@ -6281,6 +6282,8 @@ struct cp_decl_specifier_seq {
cp_storage_class storage_class;
/* For the __intN declspec, this stores the index into the int_n_* arrays. */
int int_n_idx;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
/* True iff TYPE_SPEC defines a class or enum. */
BOOL_BITFIELD type_definition_p : 1;
/* True iff multiple types were (erroneously) specified for this
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 85b892cddf0..a87fed04529 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -5290,6 +5290,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
return type_quals;
}
@@ -5412,6 +5414,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
error_at (declspecs->locations[ds_restrict],
"%<__restrict%> can only be specified for objects and "
"functions");
+ else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ error_at (declspecs->locations[ds_addr_space],
+ "address space can only be specified for objects and "
+ "functions");
else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
error_at (declspecs->locations[ds_thread],
"%<__thread%> can only be specified for objects "
@@ -14608,6 +14614,59 @@ grokdeclarator (const cp_declarator *declarator,
if (!processing_template_decl)
cp_apply_type_quals_to_decl (type_quals, decl);
+ /* Warn about address space used for things other than static memory or
+ pointers. */
+ addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
+ if (!ADDR_SPACE_GENERIC_P (address_space))
+ {
+ if (decl_context == NORMAL)
+ {
+ switch (storage_class)
+ {
+ case sc_auto:
+ error ("%qs combined with C++98 %<auto%> qualifier for %qs",
+ c_addr_space_name (address_space), name);
+ break;
+ case sc_register:
+ error ("%qs combined with %<register%> qualifier for %qs",
+ c_addr_space_name (address_space), name);
+ break;
+ case sc_none:
+ if (! toplevel_bindings_p ())
+ error ("%qs specified for auto variable %qs",
+ c_addr_space_name (address_space), name);
+ break;
+ case sc_mutable:
+ error ("%qs combined with %<mutable%> qualifier for %qs",
+ c_addr_space_name (address_space), name);
+ break;
+ case sc_static:
+ case sc_extern:
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
+ {
+ if (name)
+ error ("%qs specified for parameter %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for unnamed parameter",
+ c_addr_space_name (address_space));
+ }
+ else if (decl_context == FIELD)
+ {
+ if (name)
+ error ("%qs specified for structure field %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for structure field",
+ c_addr_space_name (address_space));
+ }
+ }
+
return decl;
}
}
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 1215463089b..aafff98f05a 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2520,6 +2520,14 @@ write_CV_qualifiers_for_type (const tree type)
array. */
cp_cv_quals quals = TYPE_QUALS (type);
+ if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
+ {
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 9ddfb027ff9..c82059d1efd 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -7703,6 +7703,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
postfix_expression = error_mark_node;
break;
}
+ if (type != error_mark_node
+ && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
+ && current_function_decl)
+ {
+ error
+ ("compound literal qualified by address-space "
+ "qualifier");
+ type = error_mark_node;
+ }
/* Form the representation of the compound-literal. */
postfix_expression
= finish_compound_literal (type, initializer,
@@ -19445,6 +19454,15 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+
+ if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ }
+
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -23837,6 +23855,7 @@ cp_parser_ptr_operator (cp_parser* parser,
GNU Extension:
cv-qualifier:
+ address-space-qualifier
__restrict__
Returns a bitmask representing the cv-qualifiers. */
@@ -23873,6 +23892,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
+ if (RID_FIRST_ADDR_SPACE <= token->keyword
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ cv_qualifier
+ = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
+
if (!cv_qualifier)
break;
@@ -32893,6 +32917,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
decl_specs->locations[ds] = location;
if (ds == ds_thread)
decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ else if (ds == ds_addr_space)
+ decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
}
else
{
@@ -32925,6 +32951,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
error_at (&richloc, "duplicate %qD", token->u.value);
}
}
+ else if (ds == ds_addr_space)
+ {
+ addr_space_t as1 = decl_specs->address_space;
+ addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
+
+ gcc_rich_location richloc (location);
+ richloc.add_fixit_remove ();
+ if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
+ && as1 != as2)
+ error_at (&richloc,
+ "conflicting named address spaces (%s vs %s)",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
+ error_at (&richloc,
+ "duplicate named address space %s",
+ c_addr_space_name (as1));
+
+ decl_specs->address_space = as2;
+ }
else
{
static const char *const decl_spec_names[] = {
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index e4dca9d4f9d..7b73a57091e 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -23778,8 +23778,19 @@ template_decl_level (tree decl)
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
- int arg_quals = cp_type_quals (arg);
- int parm_quals = cp_type_quals (parm);
+ int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
+
+ /* Try to unify ARG's address space into PARM's address space.
+ If PARM does not have any address space qualifiers (ie., as_parm is 0),
+ there are no constraints on address spaces for this type. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
+ addr_space_t as_common;
+ addr_space_superset (as_arg, as_parm, &as_common);
+
+ if (!(as_parm == as_common || as_parm == 0))
+ return 0;
if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
@@ -24415,10 +24426,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
arg, parm))
return unify_cv_qual_mismatch (explain_p, parm, arg);
+ int arg_cv_quals = cp_type_quals (arg);
+ int parm_cv_quals = cp_type_quals (parm);
+
+ /* If PARM does not contain any address spaces constraints it can
+ fully match the address space of ARG. However, if PARM contains an
+ address space constraints, it becomes the upper bound. That is,
+ AS_ARG may be promoted to AS_PARM but not the converse. If we
+ ended up here, it means that `check_cv_quals_for_unify' succeeded
+ and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
+ AS_PARM. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
+ addr_space_t as_common = as_parm ? 0 : as_arg;
+
/* Consider the case where ARG is `const volatile int' and
PARM is `const T'. Then, T should be `volatile int'. */
arg = cp_build_qualified_type
(arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
+ int unified_cv =
+ (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
+ | ENCODE_QUAL_ADDR_SPACE (as_common));
+ arg = cp_build_qualified_type (arg, unified_cv, tf_none);
if (arg == error_mark_node)
return unify_invalid (explain_p);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 45348c58bb6..1f330ca93ed 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -6072,15 +6072,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index da0e1427b97..93cfdc70e2d 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -803,10 +803,28 @@ composite_pointer_type (const op_location_t &location,
else
return error_mark_node;
}
+ /* If possible merge the address space into the superset of the address
+ spaces of t1 and t2, or raise an error. */
+ addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
+ addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as_t1, as_t2, &as_common))
+ {
+ as_common = as_t1;
+ error_at (location,
+ "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ }
+ int quals_t1 = cp_type_quals (TREE_TYPE (t1));
+ int quals_t2 = cp_type_quals (TREE_TYPE (t2));
result_type
= cp_build_qualified_type (void_type_node,
- (cp_type_quals (TREE_TYPE (t1))
- | cp_type_quals (TREE_TYPE (t2))));
+ (CLEAR_QUAL_ADDR_SPACE (quals_t1)
+ | CLEAR_QUAL_ADDR_SPACE (quals_t2)
+ | ENCODE_QUAL_ADDR_SPACE (as_common)));
result_type = build_pointer_type (result_type);
/* Merge the attributes. */
attributes = (*targetm.merge_type_attributes) (t1, t2);
@@ -1731,7 +1749,9 @@ comptypes (tree t1, tree t2, int strict)
}
/* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
- top-level qualifiers. */
+ top-level qualifiers, except for named address spaces. If the pointers point
+ to different named addresses spaces, then we must determine if one address
+ space is a subset of the other. */
bool
same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
@@ -1741,6 +1761,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
if (type1 == type2)
return true;
+ addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
+ addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
+ addr_space_t as_common;
+
+ /* Fail if pointers point to incompatible address spaces. */
+ if (!addr_space_superset (as_type1, as_type2, &as_common))
+ return false;
+
type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
return same_type_p (type1, type2);
@@ -6672,10 +6700,32 @@ static tree
pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
tsubst_flags_t complain, tree *instrument_expr)
{
- tree result, inttype;
tree restype = ptrdiff_type_node;
+ tree result, inttype;
+
+ addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
+ addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
tree target_type = TREE_TYPE (ptrtype);
+ /* If the operands point into different address spaces, we need to
+ explicitly convert them to pointers into the common address space
+ before we can subtract the numerical address values. */
+ if (as0 != as1)
+ {
+ addr_space_t as_common;
+ tree common_type;
+
+ /* Determine the common superset address space. This is guaranteed
+ to exist because the caller verified that comp_target_types
+ returned non-zero. */
+ if (!addr_space_superset (as0, as1, &as_common))
+ gcc_unreachable ();
+
+ common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
+ op0 = convert (common_type, op0);
+ op1 = convert (common_type, op1);
+ }
+
if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
return error_mark_node;
@@ -11286,6 +11336,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
to_more_cv_qualified = true;
}
+ /* Warn about conversions between pointers to disjoint
+ address spaces. */
+ if (TREE_CODE (from) == POINTER_TYPE
+ && TREE_CODE (to) == POINTER_TYPE)
+ {
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common))
+ return false;
+ }
+
if (constp > 0)
constp &= TYPE_READONLY (to);
}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index cfbe32afce9..ef75f6b83a2 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
@section Named Address Spaces
@cindex Named Address Spaces
-As an extension, GNU C supports named address spaces as
+As an extension, GNU C and GNU C++ support named address spaces as
defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
address spaces in GCC will evolve as the draft technical report
changes. Calling conventions for any target might also change. At
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
new file mode 100644
index 00000000000..c01f8d6054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do run { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
+
+int f (int volatile __seg_fs *a)
+{
+ return *a;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
new file mode 100644
index 00000000000..862bbbdcdf2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do run { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
+
+template <class T>
+int f (T *p) { return *p; }
+int g (__seg_fs int *p) { return *p; }
+__seg_fs int *a;
+int main() { f(a); }
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
new file mode 100644
index 00000000000..c04d2f497da
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+__seg_fs char a, b, c;
+__seg_fs const int *p;
+static /* give internal linkage to the following anonymous struct */
+__seg_fs struct { int a; char b; } * __seg_gs q;
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
new file mode 100644
index 00000000000..86c02d1e7f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+int __seg_fs * fs1;
+int __seg_fs * fs2;
+float __seg_gs * gs1;
+float __seg_gs * gs2;
+
+int
+main ()
+{
+ fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
+ fs1 - fs2;
+ fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
+ fs1 == fs2;
+ fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
+ fs1 = fs2;
+ fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
+ fs1 > fs2;
+ fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
new file mode 100644
index 00000000000..12d7975e560
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
@@ -0,0 +1,31 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-prune-output "does not allow .register. storage class specifier" }
+int __seg_fs * outer_b;
+
+struct s {
+ __seg_fs int * ok;
+ __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
+};
+
+int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
+
+namespace ns_a
+{
+ int __seg_fs * inner_b;
+
+ template<typename T>
+ int f (T &a) { return a; }
+ int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
+ int h (__seg_fs int *a) { return *a; }
+}
+
+int
+main ()
+{
+ int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
+ static __seg_gs int static_gs;
+ __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
+ __seg_fs int *pa = outer_b;
+ __seg_fs int& ra = *ns_a::inner_b;
+ return ns_a::f(ra) + ns_a::f(*pa);
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
new file mode 100644
index 00000000000..ebb6316054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
new file mode 100644
index 00000000000..2e8ee32a885
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-std=gnu++98" }
+
+int
+main ()
+{
+ struct foo {int a; char b[2];} structure;
+ structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
new file mode 100644
index 00000000000..5b2c0f28078
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
new file mode 100644
index 00000000000..70dfcce53fa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
+template<typename T, typename U>
+__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
+
+int
+main ()
+{
+ f (fs1, gs1);
+ f (gs1, fs1);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
new file mode 100644
index 00000000000..5df115db939
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-skip-if "" { *-*-* } { "-std=c++98" "-std=c++03" "-std=gnu++98" "-std=gnu++03" } { "" } }
+// decltype is ony available since c++11
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T> struct strip;
+template<typename T> struct strip<__seg_fs T *> { typedef T type; };
+template<typename T> struct strip<__seg_gs T *> { typedef T type; };
+
+int
+main ()
+{
+ *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
new file mode 100644
index 00000000000..526bbaa56b7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+bool f (T __seg_fs * a, U __seg_gs * b)
+{
+ return *(T *) a == *(U *) b;
+}
+
+int
+main ()
+{
+ return f (fs1, gs1);
+}
diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
new file mode 100644
index 00000000000..ae9f4de0e1f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+template <class T>
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
+ // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
+__seg_fs int *a;
+int main() { f(a); } // { dg-error "no matching" }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
diff --git a/gcc/tree.h b/gcc/tree.h
index 9af971cf401..4aebfef854b 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2292,7 +2292,7 @@ extern tree vector_element_bits_tree (const_tree);
/* Encode/decode the named memory support as part of the qualifier. If more
than 8 qualifiers are added, these macros need to be adjusted. */
-#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
+#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
#define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
/* Return all qualifiers except for the address space qualifiers. */
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-10-18 7:37 ` [PATCH v3] " Paul Iannetta
@ 2022-10-18 14:24 ` Jason Merrill
2022-10-18 17:01 ` Paul Iannetta
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-18 14:24 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/18/22 03:37, Paul Iannetta wrote:
> On Fri, Oct 14, 2022 at 11:19:50AM -0400, Jason Merrill wrote:
>> On 10/13/22 17:57, Paul Iannetta wrote:
>>> On Thu, Oct 13, 2022 at 03:41:16PM -0400, Jason Merrill wrote:
>>>> On 10/13/22 12:02, Paul Iannetta wrote:
>>>>> On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
>>>>>> On 10/13/22 11:23, Paul Iannetta wrote:
>>>>>>> On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
>>>>>>>> On 10/12/22 20:52, Paul Iannetta wrote:
>>>>>>>>> There are quite a few things I would like to clarify concerning some
>>>>>>>>> implementation details.
>>>>>>>>> - A variable with automatic storage (which is neither a pointer nor
>>>>>>>>> a reference) cannot be qualified with an address space. I detect
>>>>>>>>> this by the combination of `sc_none' and `! toplevel_bindings_p ()',
>>>>>>>>> but I've also seen the use of `at_function_scope' at other places.
>>>>>>>>> And I'm unsure which one is appropriate here.
>>>>>>>>> This detection happens at the very end of grokdeclarator because I
>>>>>>>>> need to know that the type is a pointer, which is not know until
>>>>>>>>> very late in the function.
>>>>>>>>
>>>>>>>> At that point you have the decl, and you can ask directly what its storage
>>>>>>>> duration is, perhaps using decl_storage_duration.
>>>>>>>>
>>>>>>>> But why do you need to know whether the type is a pointer? The attribute
>>>>>>>> applies to the target type of the pointer, not the pointer type. I think
>>>>>>>> the problem is that you're looking at declspecs when you ought to be looking
>>>>>>>> at type_quals.
>>>>>>>
>>>>>>> I need to know that the base type is a pointer to reject invalid
>>>>>>> declarations such as:
>>>>>>>
>>>>>>> int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
>>>>>>>
>>>>>>> because parameters and auto variables can have an address space
>>>>>>> qualifier only if they are pointer or reference type, which I can't
>>>>>>> tell only from type_quals.
>>>>>>
>>>>>> But "int *__seg_fs a" is just as invalid as the above; the difference is not
>>>>>> whether a is a pointer, but whether the address-space-qualified is the type
>>>>>> of a itself or some sub-type.
>>>>>
>>>>> I agree that "int * __seg_fs a" is invalid but it is accepted by the C
>>>>> front-end, and by clang (both C and C++), the behavior is that the
>>>>> address-name is silently ignored.
>>>>
>>>> Hmm, that sounds like a bug; in that case, presumably the user meant to
>>>> qualify the pointed-to type, and silently ignoring seems unlikely to give
>>>> the effect they want.
>>>>
>>>
>>> Well, actually, I'm re-reading the draft and "int * __seg_fs a" is
>>> valid. It means "pointer in address space __seg_fs pointing to an
>>> object in the generic address space", whereas "__seg_fs int * a" means
>>> "pointer in the generic address space pointing to an object in the
>>> __seg_fs address-space".
>>>
>>> Oddities such as, "__seg_fs int * __seg_gs a" are also perfectly
>>> valid.
>>
>> If a has static storage duration, sure; I was still thinking about
>> declarations with automatic storage duration such as in your example above.
>>
>
> Thanks, I only use type_quals now. I also took into account the style
> recommendations from Jakub, and included the other template tests.
> I rebased over trunk, bootstrapped the compiler and run the "make
> check-gcc" with no regressions on x86.
>
> Paul
>
> # ------------------------ >8 ------------------------
> Add support for custom address spaces in C++
>
> gcc/
> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>
> gcc/c/
> * c-decl.cc: Remove c_register_addr_space.
>
> gcc/c-family/
> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> * c-common.h: Remove the FIXME.
> (addr_space_superset): New declaration.
>
> gcc/cp/
> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> (struct cp_decl_specifier_seq): Likewise.
> * decl.cc (get_type_quals): Likewise.
> (check_tag_decl): Likewise.
> (grokdeclarator): Likewise.
> * parser.cc (cp_parser_type_specifier): Likewise.
> (cp_parser_cv_qualifier_seq_opt): Likewise.
> (cp_parser_postfix_expression): Likewise.
> (cp_parser_type_specifier): Likewise.
> (set_and_check_decl_spec_loc): Likewise.
> * typeck.cc (composite_pointer_type): Likewise
> (comp_ptr_ttypes_real): Likewise.
> (same_type_ignoring_top_level_qualifiers_p): Likewise.
> * pt.cc (check_cv_quals_for_unify): Likewise.
> (unify): Likewise.
> * tree.cc: Remove c_register_addr_space stub.
> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> using the extended qualifier notation.
>
> gcc/doc
> * extend.texi (Named Address Spaces): add a mention about C++
> support.
>
> gcc/testsuite/
> * g++.dg/abi/mangle-addr-space1.C: New test.
> * g++.dg/abi/mangle-addr-space2.C: New test.
> * g++.dg/parse/addr-space.C: New test.
> * g++.dg/parse/addr-space1.C: New test.
> * g++.dg/parse/addr-space2.C: New test.
> * g++.dg/parse/template/spec-addr-space.C: New test.
> * g++.dg/ext/addr-space-decl.C: New test.
> * g++.dg/ext/addr-space-ref.C: New test.
> * g++.dg/ext/addr-space-ops.C: New test.
> * g++.dg/template/addr-space-overload.C: New test.
> * g++.dg/template/addr-space-strip1.C: New test.
> * g++.dg/template/addr-space-strip2.C: New test.
>
> # ------------------------ >8 ------------------------
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index 9ec9100cc90..3b79dc47515 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -588,6 +588,33 @@ c_addr_space_name (addr_space_t as)
> return IDENTIFIER_POINTER (ridpointers [rid]);
> }
>
> +/* Return true if between two named address spaces, whether there is a superset
> + named address space that encompasses both address spaces. If there is a
> + superset, return which address space is the superset. */
> +
> +bool
> +addr_space_superset (addr_space_t as1, addr_space_t as2,
> + addr_space_t * common)
> +{
> + if (as1 == as2)
> + {
> + *common = as1;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as1, as2))
> + {
> + *common = as2;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as2, as1))
> + {
> + *common = as1;
> + return true;
> + }
> + else
> + return false;
> +}
> +
> /* Push current bindings for the function name VAR_DECLS. */
>
> void
> @@ -2785,6 +2812,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> return build_nonstandard_integer_type (width, unsignedp);
> }
>
> +/* Register reserved keyword WORD as qualifier for address space AS. */
> +
> +void
> +c_register_addr_space (const char *word, addr_space_t as)
> +{
> + int rid = RID_FIRST_ADDR_SPACE + as;
> + tree id;
> +
> + /* Address space qualifiers are only supported
> + in C with GNU extensions enabled. */
> + if (c_dialect_objc () || flag_no_asm)
> + return;
> +
> + id = get_identifier (word);
> + C_SET_RID_CODE (id, rid);
> + TREE_LANG_FLAG_0 (id) = 1;
> + ridpointers[rid] = id;
> +}
> +
> /* The C version of the register_builtin_type langhook. */
>
> void
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 62ab4ba437b..a3864d874aa 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.cc and cp/tree.cc. FIXME. */
> -extern void c_register_addr_space (const char *str, addr_space_t as);
> -
> /* In c-common.cc. */
> extern bool in_late_binary_op;
> extern const char *c_addr_space_name (addr_space_t as);
> +extern const char *c_addr_space_name (addr_space_t as);
> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> extern tree identifier_global_value (tree);
> extern tree identifier_global_tag (tree);
> extern bool names_builtin_p (const char *);
> @@ -951,6 +950,7 @@ extern bool c_common_init (void);
> extern void c_common_finish (void);
> extern void c_common_parse_file (void);
> extern alias_set_type c_common_get_alias_set (tree);
> +extern void c_register_addr_space (const char *, addr_space_t);
> extern void c_register_builtin_type (tree, const char*);
> extern bool c_promoting_integer_type_p (const_tree);
> extern bool self_promoting_args_p (const_tree);
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index a7571cc7542..b1f69997ff7 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -12531,25 +12531,6 @@ c_parse_final_cleanups (void)
> ext_block = NULL;
> }
>
> -/* Register reserved keyword WORD as qualifier for address space AS. */
> -
> -void
> -c_register_addr_space (const char *word, addr_space_t as)
> -{
> - int rid = RID_FIRST_ADDR_SPACE + as;
> - tree id;
> -
> - /* Address space qualifiers are only supported
> - in C with GNU extensions enabled. */
> - if (c_dialect_objc () || flag_no_asm)
> - return;
> -
> - id = get_identifier (word);
> - C_SET_RID_CODE (id, rid);
> - C_IS_RESERVED_WORD (id) = 1;
> - ridpointers [rid] = id;
> -}
> -
> /* Return identifier to look up for omp declare reduction. */
>
> tree
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index fdb96c28c51..2a700bbaff3 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -303,32 +303,6 @@ c_type_promotes_to (tree type)
> return type;
> }
>
> -/* Return true if between two named address spaces, whether there is a superset
> - named address space that encompasses both address spaces. If there is a
> - superset, return which address space is the superset. */
> -
> -static bool
> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> -{
> - if (as1 == as2)
> - {
> - *common = as1;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as1, as2))
> - {
> - *common = as2;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as2, as1))
> - {
> - *common = as1;
> - return true;
> - }
> - else
> - return false;
> -}
> -
> /* Return a variant of TYPE which has all the type qualifiers of LIKE
> as well as those of TYPE. */
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index e2607f09c19..0248569a95b 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6235,6 +6235,7 @@ enum cp_decl_spec {
> ds_const,
> ds_volatile,
> ds_restrict,
> + ds_addr_space,
> ds_inline,
> ds_virtual,
> ds_explicit,
> @@ -6281,6 +6282,8 @@ struct cp_decl_specifier_seq {
> cp_storage_class storage_class;
> /* For the __intN declspec, this stores the index into the int_n_* arrays. */
> int int_n_idx;
> + /* The address space that the declaration belongs to. */
> + addr_space_t address_space;
> /* True iff TYPE_SPEC defines a class or enum. */
> BOOL_BITFIELD type_definition_p : 1;
> /* True iff multiple types were (erroneously) specified for this
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 85b892cddf0..a87fed04529 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -5290,6 +5290,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> type_quals |= TYPE_QUAL_VOLATILE;
> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> type_quals |= TYPE_QUAL_RESTRICT;
> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>
> return type_quals;
> }
> @@ -5412,6 +5414,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> error_at (declspecs->locations[ds_restrict],
> "%<__restrict%> can only be specified for objects and "
> "functions");
> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> + error_at (declspecs->locations[ds_addr_space],
> + "address space can only be specified for objects and "
> + "functions");
> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> error_at (declspecs->locations[ds_thread],
> "%<__thread%> can only be specified for objects "
> @@ -14608,6 +14614,59 @@ grokdeclarator (const cp_declarator *declarator,
> if (!processing_template_decl)
> cp_apply_type_quals_to_decl (type_quals, decl);
>
> + /* Warn about address space used for things other than static memory or
> + pointers. */
> + addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
> + if (!ADDR_SPACE_GENERIC_P (address_space))
> + {
> + if (decl_context == NORMAL)
> + {
> + switch (storage_class)
I would still suggest checking decl_storage_duration at this point
rather than the storage_class specifier.
> + {
> + case sc_auto:
> + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
> + c_addr_space_name (address_space), name);
> + break;
> + case sc_register:
> + error ("%qs combined with %<register%> qualifier for %qs",
> + c_addr_space_name (address_space), name);
> + break;
> + case sc_none:
> + if (! toplevel_bindings_p ())
> + error ("%qs specified for auto variable %qs",
And let's refer to automatic storage duration rather than shorten to 'auto'.
> + c_addr_space_name (address_space), name);
> + break;
> + case sc_mutable:
> + error ("%qs combined with %<mutable%> qualifier for %qs",
> + c_addr_space_name (address_space), name);
> + break;
> + case sc_static:
> + case sc_extern:
> + break;
> + default:
> + gcc_unreachable ();
> + }
> + }
> + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
> + {
> + if (name)
> + error ("%qs specified for parameter %qs",
> + c_addr_space_name (address_space), name);
> + else
> + error ("%qs specified for unnamed parameter",
> + c_addr_space_name (address_space));
> + }
> + else if (decl_context == FIELD)
> + {
> + if (name)
> + error ("%qs specified for structure field %qs",
> + c_addr_space_name (address_space), name);
> + else
> + error ("%qs specified for structure field",
> + c_addr_space_name (address_space));
> + }
> + }
> +
> return decl;
> }
> }
> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> index 1215463089b..aafff98f05a 100644
> --- a/gcc/cp/mangle.cc
> +++ b/gcc/cp/mangle.cc
> @@ -2520,6 +2520,14 @@ write_CV_qualifiers_for_type (const tree type)
> array. */
> cp_cv_quals quals = TYPE_QUALS (type);
>
> + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
> + {
> + const char *as_name = c_addr_space_name (as);
> + write_char ('U');
> + write_unsigned_number (strlen (as_name));
> + write_string (as_name);
> + ++num_qualifiers;
> + }
> if (quals & TYPE_QUAL_RESTRICT)
> {
> write_char ('r');
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 9ddfb027ff9..c82059d1efd 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -7703,6 +7703,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> postfix_expression = error_mark_node;
> break;
> }
> + if (type != error_mark_node
> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> + && current_function_decl)
> + {
> + error
> + ("compound literal qualified by address-space "
> + "qualifier");
> + type = error_mark_node;
> + }
> /* Form the representation of the compound-literal. */
> postfix_expression
> = finish_compound_literal (type, initializer,
> @@ -19445,6 +19454,15 @@ cp_parser_type_specifier (cp_parser* parser,
> break;
> }
>
> +
> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> + {
> + ds = ds_addr_space;
> + if (is_cv_qualifier)
> + *is_cv_qualifier = true;
> + }
> +
> +
I don't think we need two blank lines before and after this block, one
each should be enough.
> /* Handle simple keywords. */
> if (ds != ds_last)
> {
> @@ -23837,6 +23855,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> GNU Extension:
>
> cv-qualifier:
> + address-space-qualifier
> __restrict__
>
> Returns a bitmask representing the cv-qualifiers. */
> @@ -23873,6 +23892,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> break;
> }
>
> + if (RID_FIRST_ADDR_SPACE <= token->keyword
> + && token->keyword <= RID_LAST_ADDR_SPACE)
> + cv_qualifier
> + = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> +
> if (!cv_qualifier)
> break;
>
> @@ -32893,6 +32917,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> decl_specs->locations[ds] = location;
> if (ds == ds_thread)
> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> + else if (ds == ds_addr_space)
> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> }
> else
> {
> @@ -32925,6 +32951,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> error_at (&richloc, "duplicate %qD", token->u.value);
> }
> }
> + else if (ds == ds_addr_space)
> + {
> + addr_space_t as1 = decl_specs->address_space;
> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> +
> + gcc_rich_location richloc (location);
> + richloc.add_fixit_remove ();
> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> + && as1 != as2)
> + error_at (&richloc,
> + "conflicting named address spaces (%s vs %s)",
> + c_addr_space_name (as1), c_addr_space_name (as2));
> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> + error_at (&richloc,
> + "duplicate named address space %s",
> + c_addr_space_name (as1));
> +
> + decl_specs->address_space = as2;
> + }
> else
> {
> static const char *const decl_spec_names[] = {
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index e4dca9d4f9d..7b73a57091e 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -23778,8 +23778,19 @@ template_decl_level (tree decl)
> static int
> check_cv_quals_for_unify (int strict, tree arg, tree parm)
> {
> - int arg_quals = cp_type_quals (arg);
> - int parm_quals = cp_type_quals (parm);
> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> +
> + /* Try to unify ARG's address space into PARM's address space.
> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> + there are no constraints on address spaces for this type. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> + addr_space_t as_common;
> + addr_space_superset (as_arg, as_parm, &as_common);
> +
> + if (!(as_parm == as_common || as_parm == 0))
> + return 0;
I'd expect address space qualifiers to follow the 'strict' parameter
like the other qualifiers; the above test seems to assume
UNIFY_ALLOW_{OUTER_,}LESS_CV_QUAL.
> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> @@ -24415,10 +24426,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> arg, parm))
> return unify_cv_qual_mismatch (explain_p, parm, arg);
>
> + int arg_cv_quals = cp_type_quals (arg);
> + int parm_cv_quals = cp_type_quals (parm);
> +
> + /* If PARM does not contain any address spaces constraints it can
> + fully match the address space of ARG. However, if PARM contains an
> + address space constraints, it becomes the upper bound. That is,
> + AS_ARG may be promoted to AS_PARM but not the converse. If we
> + ended up here, it means that `check_cv_quals_for_unify' succeeded
> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> + AS_PARM. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> + addr_space_t as_common = as_parm ? 0 : as_arg;
Hmm, I'd think we also want as_common = as_arg when it's a subset of
as_parm.
> /* Consider the case where ARG is `const volatile int' and
> PARM is `const T'. Then, T should be `volatile int'. */
> arg = cp_build_qualified_type
> (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
> + int unified_cv =
> + (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> + | ENCODE_QUAL_ADDR_SPACE (as_common));
> + arg = cp_build_qualified_type (arg, unified_cv, tf_none);
> if (arg == error_mark_node)
> return unify_invalid (explain_p);
>
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index 45348c58bb6..1f330ca93ed 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -6072,15 +6072,6 @@ cp_free_lang_data (tree t)
> DECL_CHAIN (t) = NULL_TREE;
> }
>
> -/* Stub for c-common. Please keep in sync with c-decl.cc.
> - FIXME: If address space support is target specific, then this
> - should be a C target hook. But currently this is not possible,
> - because this function is called via REGISTER_TARGET_PRAGMAS. */
> -void
> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> -{
> -}
> -
> /* Return the number of operands in T that we care about for things like
> mangling. */
>
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index da0e1427b97..93cfdc70e2d 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -803,10 +803,28 @@ composite_pointer_type (const op_location_t &location,
> else
> return error_mark_node;
> }
> + /* If possible merge the address space into the superset of the address
> + spaces of t1 and t2, or raise an error. */
> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> + addr_space_t as_common;
> +
> + /* If the two named address spaces are different, determine the common
> + superset address space. If there isn't one, raise an error. */
> + if (!addr_space_superset (as_t1, as_t2, &as_common))
> + {
> + as_common = as_t1;
> + error_at (location,
> + "%qT and %qT are in disjoint named address spaces",
> + t1, t2);
Why not return error_mark_node here?
> + }
> + int quals_t1 = cp_type_quals (TREE_TYPE (t1));
> + int quals_t2 = cp_type_quals (TREE_TYPE (t2));
> result_type
> = cp_build_qualified_type (void_type_node,
> - (cp_type_quals (TREE_TYPE (t1))
> - | cp_type_quals (TREE_TYPE (t2))));
> + (CLEAR_QUAL_ADDR_SPACE (quals_t1)
> + | CLEAR_QUAL_ADDR_SPACE (quals_t2)
> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> result_type = build_pointer_type (result_type);
> /* Merge the attributes. */
> attributes = (*targetm.merge_type_attributes) (t1, t2);
> @@ -1731,7 +1749,9 @@ comptypes (tree t1, tree t2, int strict)
> }
>
> /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
> - top-level qualifiers. */
> + top-level qualifiers, except for named address spaces. If the pointers point
> + to different named addresses spaces, then we must determine if one address
> + space is a subset of the other. */
>
> bool
> same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> @@ -1741,6 +1761,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> if (type1 == type2)
> return true;
>
> + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
> + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
> + addr_space_t as_common;
> +
> + /* Fail if pointers point to incompatible address spaces. */
> + if (!addr_space_superset (as_type1, as_type2, &as_common))
> + return false;
Why do you need this change? I'd expect this function to ignore top
level address space qualifiers like the other qualifiers.
> type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
> type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
> return same_type_p (type1, type2);
> @@ -6672,10 +6700,32 @@ static tree
> pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
> tsubst_flags_t complain, tree *instrument_expr)
> {
> - tree result, inttype;
> tree restype = ptrdiff_type_node;
> + tree result, inttype;
> +
> + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
> + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
> tree target_type = TREE_TYPE (ptrtype);
>
> + /* If the operands point into different address spaces, we need to
> + explicitly convert them to pointers into the common address space
> + before we can subtract the numerical address values. */
> + if (as0 != as1)
> + {
> + addr_space_t as_common;
> + tree common_type;
> +
> + /* Determine the common superset address space. This is guaranteed
> + to exist because the caller verified that comp_target_types
> + returned non-zero. */
> + if (!addr_space_superset (as0, as1, &as_common))
> + gcc_unreachable ();
> +
> + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
> + op0 = convert (common_type, op0);
> + op1 = convert (common_type, op1);
> + }
I think you shouldn't need to change pointer_diff if
composite_pointer_type returns error_mark_node above.
> if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
> return error_mark_node;
>
> @@ -11286,6 +11336,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> to_more_cv_qualified = true;
> }
>
> + /* Warn about conversions between pointers to disjoint
> + address spaces. */
> + if (TREE_CODE (from) == POINTER_TYPE
> + && TREE_CODE (to) == POINTER_TYPE)
> + {
> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> + addr_space_t as_common;
> +
> + if (!addr_space_superset (as_to, as_from, &as_common))
> + return false;
I think you also want to check that as_common == as_to here?
> + }
> +
> if (constp > 0)
> constp &= TYPE_READONLY (to);
> }
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index cfbe32afce9..ef75f6b83a2 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> @section Named Address Spaces
> @cindex Named Address Spaces
>
> -As an extension, GNU C supports named address spaces as
> +As an extension, GNU C and GNU C++ support named address spaces as
> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> address spaces in GCC will evolve as the draft technical report
> changes. Calling conventions for any target might also change. At
> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> new file mode 100644
> index 00000000000..c01f8d6054a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> @@ -0,0 +1,10 @@
> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
This can be dg-do compile, I don't think you get anything from running
an empty main.
> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
And then you don't need -save-temps. What are the other options for?
> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> +
> +int f (int volatile __seg_fs *a)
> +{
> + return *a;
> +}
> +
> +int main () {}
> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> new file mode 100644
> index 00000000000..862bbbdcdf2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> @@ -0,0 +1,9 @@
> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
Also not clear that running is important for this test.
> +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
> +
> +template <class T>
> +int f (T *p) { return *p; }
> +int g (__seg_fs int *p) { return *p; }
> +__seg_fs int *a;
> +int main() { f(a); }
> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> new file mode 100644
> index 00000000000..c04d2f497da
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> @@ -0,0 +1,5 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +__seg_fs char a, b, c;
> +__seg_fs const int *p;
> +static /* give internal linkage to the following anonymous struct */
Hmm, this 'static' gives internal linkage to the variable q, not the
type. What do you want it for?
> +__seg_fs struct { int a; char b; } * __seg_gs q;
> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> new file mode 100644
> index 00000000000..86c02d1e7f5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> @@ -0,0 +1,20 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +int __seg_fs * fs1;
> +int __seg_fs * fs2;
> +float __seg_gs * gs1;
> +float __seg_gs * gs2;
> +
> +int
> +main ()
> +{
> + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
> + fs1 - fs2;
> + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
> + fs1 == fs2;
> + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> + fs1 = fs2;
> + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
> + fs1 > fs2;
> + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> new file mode 100644
> index 00000000000..12d7975e560
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> @@ -0,0 +1,31 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +// { dg-prune-output "does not allow .register. storage class specifier" }
> +int __seg_fs * outer_b;
> +
> +struct s {
> + __seg_fs int * ok;
> + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
> +};
> +
> +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
> +
> +namespace ns_a
> +{
> + int __seg_fs * inner_b;
> +
> + template<typename T>
> + int f (T &a) { return a; }
> + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
> + int h (__seg_fs int *a) { return *a; }
> +}
> +
> +int
> +main ()
> +{
> + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
> + static __seg_gs int static_gs;
> + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
> + __seg_fs int *pa = outer_b;
> + __seg_fs int& ra = *ns_a::inner_b;
> + return ns_a::f(ra) + ns_a::f(*pa);
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> new file mode 100644
> index 00000000000..ebb6316054a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> @@ -0,0 +1,9 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> +
> +int
> +main ()
> +{
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> new file mode 100644
> index 00000000000..2e8ee32a885
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> @@ -0,0 +1,10 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +// { dg-options "-std=gnu++98" }
> +
> +int
> +main ()
> +{
> + struct foo {int a; char b[2];} structure;
> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> new file mode 100644
> index 00000000000..5b2c0f28078
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> @@ -0,0 +1,9 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> +
> +int
> +main ()
> +{
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
> new file mode 100644
> index 00000000000..70dfcce53fa
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
> @@ -0,0 +1,17 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +int __seg_fs * fs1;
> +int __seg_gs * gs1;
> +
> +template<typename T, typename U>
> +__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
> +template<typename T, typename U>
> +__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
> +
> +int
> +main ()
> +{
> + f (fs1, gs1);
> + f (gs1, fs1);
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
> new file mode 100644
> index 00000000000..5df115db939
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
> @@ -0,0 +1,17 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +// { dg-skip-if "" { *-*-* } { "-std=c++98" "-std=c++03" "-std=gnu++98" "-std=gnu++03" } { "" } }
This can be { dg-require-effective-target c++11 }
> +// decltype is ony available since c++11
"only"
> +
> +int __seg_fs * fs1;
> +int __seg_gs * gs1;
> +
> +template<typename T> struct strip;
> +template<typename T> struct strip<__seg_fs T *> { typedef T type; };
> +template<typename T> struct strip<__seg_gs T *> { typedef T type; };
> +
> +int
> +main ()
> +{
> + *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
> + return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
> new file mode 100644
> index 00000000000..526bbaa56b7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
> @@ -0,0 +1,16 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +int __seg_fs * fs1;
> +int __seg_gs * gs1;
> +
> +template<typename T, typename U>
> +bool f (T __seg_fs * a, U __seg_gs * b)
> +{
> + return *(T *) a == *(U *) b;
> +}
> +
> +int
> +main ()
> +{
> + return f (fs1, gs1);
> +}
> diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> new file mode 100644
> index 00000000000..ae9f4de0e1f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> @@ -0,0 +1,8 @@
> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> +
> +template <class T>
> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> +__seg_fs int *a;
> +int main() { f(a); } // { dg-error "no matching" }
> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 9af971cf401..4aebfef854b 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -2292,7 +2292,7 @@ extern tree vector_element_bits_tree (const_tree);
>
> /* Encode/decode the named memory support as part of the qualifier. If more
> than 8 qualifiers are added, these macros need to be adjusted. */
> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>
> /* Return all qualifiers except for the address space qualifiers. */
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-10-18 14:24 ` Jason Merrill
@ 2022-10-18 17:01 ` Paul Iannetta
2022-10-19 18:55 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-10-18 17:01 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
Thank you very much for the detailed review.
On Tue, Oct 18, 2022 at 10:24:23AM -0400, Jason Merrill wrote:
> On 10/18/22 03:37, Paul Iannetta wrote:
> > On Fri, Oct 14, 2022 at 11:19:50AM -0400, Jason Merrill wrote:
> > > On 10/13/22 17:57, Paul Iannetta wrote:
> > > > On Thu, Oct 13, 2022 at 03:41:16PM -0400, Jason Merrill wrote:
> > > > > On 10/13/22 12:02, Paul Iannetta wrote:
> > > > > > On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
> > > > > > > On 10/13/22 11:23, Paul Iannetta wrote:
> > > > > > > > On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
> > > > > > > > > On 10/12/22 20:52, Paul Iannetta wrote:
> > > > > > > > > > There are quite a few things I would like to clarify concerning some
> > > > > > > > > > implementation details.
> > > > > > > > > > - A variable with automatic storage (which is neither a pointer nor
> > > > > > > > > > a reference) cannot be qualified with an address space. I detect
> > > > > > > > > > this by the combination of `sc_none' and `! toplevel_bindings_p ()',
> > > > > > > > > > but I've also seen the use of `at_function_scope' at other places.
> > > > > > > > > > And I'm unsure which one is appropriate here.
> > > > > > > > > > This detection happens at the very end of grokdeclarator because I
> > > > > > > > > > need to know that the type is a pointer, which is not know until
> > > > > > > > > > very late in the function.
> > > > > > > > >
> > > > > > > > > At that point you have the decl, and you can ask directly what its storage
> > > > > > > > > duration is, perhaps using decl_storage_duration.
> > > > > > > > >
> > > > > > > > > But why do you need to know whether the type is a pointer? The attribute
> > > > > > > > > applies to the target type of the pointer, not the pointer type. I think
> > > > > > > > > the problem is that you're looking at declspecs when you ought to be looking
> > > > > > > > > at type_quals.
> > > > > > > >
> > > > > > > > I need to know that the base type is a pointer to reject invalid
> > > > > > > > declarations such as:
> > > > > > > >
> > > > > > > > int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
> > > > > > > >
> > > > > > > > because parameters and auto variables can have an address space
> > > > > > > > qualifier only if they are pointer or reference type, which I can't
> > > > > > > > tell only from type_quals.
> > > > > > >
> > > > > > > But "int *__seg_fs a" is just as invalid as the above; the difference is not
> > > > > > > whether a is a pointer, but whether the address-space-qualified is the type
> > > > > > > of a itself or some sub-type.
> > > > > >
> > > > > > I agree that "int * __seg_fs a" is invalid but it is accepted by the C
> > > > > > front-end, and by clang (both C and C++), the behavior is that the
> > > > > > address-name is silently ignored.
> > > > >
> > > > > Hmm, that sounds like a bug; in that case, presumably the user meant to
> > > > > qualify the pointed-to type, and silently ignoring seems unlikely to give
> > > > > the effect they want.
> > > > >
> > > >
> > > > Well, actually, I'm re-reading the draft and "int * __seg_fs a" is
> > > > valid. It means "pointer in address space __seg_fs pointing to an
> > > > object in the generic address space", whereas "__seg_fs int * a" means
> > > > "pointer in the generic address space pointing to an object in the
> > > > __seg_fs address-space".
> > > >
> > > > Oddities such as, "__seg_fs int * __seg_gs a" are also perfectly
> > > > valid.
> > >
> > > If a has static storage duration, sure; I was still thinking about
> > > declarations with automatic storage duration such as in your example above.
> > >
> >
> > Thanks, I only use type_quals now. I also took into account the style
> > recommendations from Jakub, and included the other template tests.
> > I rebased over trunk, bootstrapped the compiler and run the "make
> > check-gcc" with no regressions on x86.
> >
> > Paul
> >
> > # ------------------------ >8 ------------------------
> > Add support for custom address spaces in C++
> >
> > gcc/
> > * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
> >
> > gcc/c/
> > * c-decl.cc: Remove c_register_addr_space.
> >
> > gcc/c-family/
> > * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> > (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> > * c-common.h: Remove the FIXME.
> > (addr_space_superset): New declaration.
> >
> > gcc/cp/
> > * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> > (struct cp_decl_specifier_seq): Likewise.
> > * decl.cc (get_type_quals): Likewise.
> > (check_tag_decl): Likewise.
> > (grokdeclarator): Likewise.
> > * parser.cc (cp_parser_type_specifier): Likewise.
> > (cp_parser_cv_qualifier_seq_opt): Likewise.
> > (cp_parser_postfix_expression): Likewise.
> > (cp_parser_type_specifier): Likewise.
> > (set_and_check_decl_spec_loc): Likewise.
> > * typeck.cc (composite_pointer_type): Likewise
> > (comp_ptr_ttypes_real): Likewise.
> > (same_type_ignoring_top_level_qualifiers_p): Likewise.
> > * pt.cc (check_cv_quals_for_unify): Likewise.
> > (unify): Likewise.
> > * tree.cc: Remove c_register_addr_space stub.
> > * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> > using the extended qualifier notation.
> >
> > gcc/doc
> > * extend.texi (Named Address Spaces): add a mention about C++
> > support.
> >
> > gcc/testsuite/
> > * g++.dg/abi/mangle-addr-space1.C: New test.
> > * g++.dg/abi/mangle-addr-space2.C: New test.
> > * g++.dg/parse/addr-space.C: New test.
> > * g++.dg/parse/addr-space1.C: New test.
> > * g++.dg/parse/addr-space2.C: New test.
> > * g++.dg/parse/template/spec-addr-space.C: New test.
> > * g++.dg/ext/addr-space-decl.C: New test.
> > * g++.dg/ext/addr-space-ref.C: New test.
> > * g++.dg/ext/addr-space-ops.C: New test.
> > * g++.dg/template/addr-space-overload.C: New test.
> > * g++.dg/template/addr-space-strip1.C: New test.
> > * g++.dg/template/addr-space-strip2.C: New test.
> >
> > # ------------------------ >8 ------------------------
> > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> > index 9ec9100cc90..3b79dc47515 100644
> > --- a/gcc/c-family/c-common.cc
> > +++ b/gcc/c-family/c-common.cc
> > @@ -588,6 +588,33 @@ c_addr_space_name (addr_space_t as)
> > return IDENTIFIER_POINTER (ridpointers [rid]);
> > }
> > +/* Return true if between two named address spaces, whether there is a superset
> > + named address space that encompasses both address spaces. If there is a
> > + superset, return which address space is the superset. */
> > +
> > +bool
> > +addr_space_superset (addr_space_t as1, addr_space_t as2,
> > + addr_space_t * common)
> > +{
> > + if (as1 == as2)
> > + {
> > + *common = as1;
> > + return true;
> > + }
> > + else if (targetm.addr_space.subset_p (as1, as2))
> > + {
> > + *common = as2;
> > + return true;
> > + }
> > + else if (targetm.addr_space.subset_p (as2, as1))
> > + {
> > + *common = as1;
> > + return true;
> > + }
> > + else
> > + return false;
> > +}
> > +
> > /* Push current bindings for the function name VAR_DECLS. */
> > void
> > @@ -2785,6 +2812,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> > return build_nonstandard_integer_type (width, unsignedp);
> > }
> > +/* Register reserved keyword WORD as qualifier for address space AS. */
> > +
> > +void
> > +c_register_addr_space (const char *word, addr_space_t as)
> > +{
> > + int rid = RID_FIRST_ADDR_SPACE + as;
> > + tree id;
> > +
> > + /* Address space qualifiers are only supported
> > + in C with GNU extensions enabled. */
> > + if (c_dialect_objc () || flag_no_asm)
> > + return;
> > +
> > + id = get_identifier (word);
> > + C_SET_RID_CODE (id, rid);
> > + TREE_LANG_FLAG_0 (id) = 1;
> > + ridpointers[rid] = id;
> > +}
> > +
> > /* The C version of the register_builtin_type langhook. */
> > void
> > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> > index 62ab4ba437b..a3864d874aa 100644
> > --- a/gcc/c-family/c-common.h
> > +++ b/gcc/c-family/c-common.h
> > @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
> > extern tree (*make_fname_decl) (location_t, tree, int);
> > -/* In c-decl.cc and cp/tree.cc. FIXME. */
> > -extern void c_register_addr_space (const char *str, addr_space_t as);
> > -
> > /* In c-common.cc. */
> > extern bool in_late_binary_op;
> > extern const char *c_addr_space_name (addr_space_t as);
> > +extern const char *c_addr_space_name (addr_space_t as);
> > +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> > extern tree identifier_global_value (tree);
> > extern tree identifier_global_tag (tree);
> > extern bool names_builtin_p (const char *);
> > @@ -951,6 +950,7 @@ extern bool c_common_init (void);
> > extern void c_common_finish (void);
> > extern void c_common_parse_file (void);
> > extern alias_set_type c_common_get_alias_set (tree);
> > +extern void c_register_addr_space (const char *, addr_space_t);
> > extern void c_register_builtin_type (tree, const char*);
> > extern bool c_promoting_integer_type_p (const_tree);
> > extern bool self_promoting_args_p (const_tree);
> > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> > index a7571cc7542..b1f69997ff7 100644
> > --- a/gcc/c/c-decl.cc
> > +++ b/gcc/c/c-decl.cc
> > @@ -12531,25 +12531,6 @@ c_parse_final_cleanups (void)
> > ext_block = NULL;
> > }
> > -/* Register reserved keyword WORD as qualifier for address space AS. */
> > -
> > -void
> > -c_register_addr_space (const char *word, addr_space_t as)
> > -{
> > - int rid = RID_FIRST_ADDR_SPACE + as;
> > - tree id;
> > -
> > - /* Address space qualifiers are only supported
> > - in C with GNU extensions enabled. */
> > - if (c_dialect_objc () || flag_no_asm)
> > - return;
> > -
> > - id = get_identifier (word);
> > - C_SET_RID_CODE (id, rid);
> > - C_IS_RESERVED_WORD (id) = 1;
> > - ridpointers [rid] = id;
> > -}
> > -
> > /* Return identifier to look up for omp declare reduction. */
> > tree
> > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> > index fdb96c28c51..2a700bbaff3 100644
> > --- a/gcc/c/c-typeck.cc
> > +++ b/gcc/c/c-typeck.cc
> > @@ -303,32 +303,6 @@ c_type_promotes_to (tree type)
> > return type;
> > }
> > -/* Return true if between two named address spaces, whether there is a superset
> > - named address space that encompasses both address spaces. If there is a
> > - superset, return which address space is the superset. */
> > -
> > -static bool
> > -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> > -{
> > - if (as1 == as2)
> > - {
> > - *common = as1;
> > - return true;
> > - }
> > - else if (targetm.addr_space.subset_p (as1, as2))
> > - {
> > - *common = as2;
> > - return true;
> > - }
> > - else if (targetm.addr_space.subset_p (as2, as1))
> > - {
> > - *common = as1;
> > - return true;
> > - }
> > - else
> > - return false;
> > -}
> > -
> > /* Return a variant of TYPE which has all the type qualifiers of LIKE
> > as well as those of TYPE. */
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index e2607f09c19..0248569a95b 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -6235,6 +6235,7 @@ enum cp_decl_spec {
> > ds_const,
> > ds_volatile,
> > ds_restrict,
> > + ds_addr_space,
> > ds_inline,
> > ds_virtual,
> > ds_explicit,
> > @@ -6281,6 +6282,8 @@ struct cp_decl_specifier_seq {
> > cp_storage_class storage_class;
> > /* For the __intN declspec, this stores the index into the int_n_* arrays. */
> > int int_n_idx;
> > + /* The address space that the declaration belongs to. */
> > + addr_space_t address_space;
> > /* True iff TYPE_SPEC defines a class or enum. */
> > BOOL_BITFIELD type_definition_p : 1;
> > /* True iff multiple types were (erroneously) specified for this
> > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> > index 85b892cddf0..a87fed04529 100644
> > --- a/gcc/cp/decl.cc
> > +++ b/gcc/cp/decl.cc
> > @@ -5290,6 +5290,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> > type_quals |= TYPE_QUAL_VOLATILE;
> > if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> > type_quals |= TYPE_QUAL_RESTRICT;
> > + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
> > return type_quals;
> > }
> > @@ -5412,6 +5414,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> > error_at (declspecs->locations[ds_restrict],
> > "%<__restrict%> can only be specified for objects and "
> > "functions");
> > + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > + error_at (declspecs->locations[ds_addr_space],
> > + "address space can only be specified for objects and "
> > + "functions");
> > else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> > error_at (declspecs->locations[ds_thread],
> > "%<__thread%> can only be specified for objects "
> > @@ -14608,6 +14614,59 @@ grokdeclarator (const cp_declarator *declarator,
> > if (!processing_template_decl)
> > cp_apply_type_quals_to_decl (type_quals, decl);
> > + /* Warn about address space used for things other than static memory or
> > + pointers. */
> > + addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
> > + if (!ADDR_SPACE_GENERIC_P (address_space))
> > + {
> > + if (decl_context == NORMAL)
> > + {
> > + switch (storage_class)
>
> I would still suggest checking decl_storage_duration at this point rather
> than the storage_class specifier.
Unless I misunderstand something, I can't weed out register variables
if I rely on decl_storage_duration.
> > + {
> > + case sc_auto:
> > + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
> > + c_addr_space_name (address_space), name);
> > + break;
> > + case sc_register:
> > + error ("%qs combined with %<register%> qualifier for %qs",
> > + c_addr_space_name (address_space), name);
> > + break;
> > + case sc_none:
> > + if (! toplevel_bindings_p ())
> > + error ("%qs specified for auto variable %qs",
>
> And let's refer to automatic storage duration rather than shorten to 'auto'.
>
Right.
> > + c_addr_space_name (address_space), name);
> > + break;
> > + case sc_mutable:
> > + error ("%qs combined with %<mutable%> qualifier for %qs",
> > + c_addr_space_name (address_space), name);
> > + break;
> > + case sc_static:
> > + case sc_extern:
> > + break;
> > + default:
> > + gcc_unreachable ();
> > + }
> > + }
> > + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
> > + {
> > + if (name)
> > + error ("%qs specified for parameter %qs",
> > + c_addr_space_name (address_space), name);
> > + else
> > + error ("%qs specified for unnamed parameter",
> > + c_addr_space_name (address_space));
> > + }
> > + else if (decl_context == FIELD)
> > + {
> > + if (name)
> > + error ("%qs specified for structure field %qs",
> > + c_addr_space_name (address_space), name);
> > + else
> > + error ("%qs specified for structure field",
> > + c_addr_space_name (address_space));
> > + }
> > + }
> > +
> > return decl;
> > }
> > }
> > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> > index 1215463089b..aafff98f05a 100644
> > --- a/gcc/cp/mangle.cc
> > +++ b/gcc/cp/mangle.cc
> > @@ -2520,6 +2520,14 @@ write_CV_qualifiers_for_type (const tree type)
> > array. */
> > cp_cv_quals quals = TYPE_QUALS (type);
> > + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
> > + {
> > + const char *as_name = c_addr_space_name (as);
> > + write_char ('U');
> > + write_unsigned_number (strlen (as_name));
> > + write_string (as_name);
> > + ++num_qualifiers;
> > + }
> > if (quals & TYPE_QUAL_RESTRICT)
> > {
> > write_char ('r');
> > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > index 9ddfb027ff9..c82059d1efd 100644
> > --- a/gcc/cp/parser.cc
> > +++ b/gcc/cp/parser.cc
> > @@ -7703,6 +7703,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> > postfix_expression = error_mark_node;
> > break;
> > }
> > + if (type != error_mark_node
> > + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> > + && current_function_decl)
> > + {
> > + error
> > + ("compound literal qualified by address-space "
> > + "qualifier");
> > + type = error_mark_node;
> > + }
> > /* Form the representation of the compound-literal. */
> > postfix_expression
> > = finish_compound_literal (type, initializer,
> > @@ -19445,6 +19454,15 @@ cp_parser_type_specifier (cp_parser* parser,
> > break;
> > }
> > +
> > + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> > + {
> > + ds = ds_addr_space;
> > + if (is_cv_qualifier)
> > + *is_cv_qualifier = true;
> > + }
> > +
> > +
>
> I don't think we need two blank lines before and after this block, one each
> should be enough.
>
Indeed.
> > /* Handle simple keywords. */
> > if (ds != ds_last)
> > {
> > @@ -23837,6 +23855,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> > GNU Extension:
> > cv-qualifier:
> > + address-space-qualifier
> > __restrict__
> > Returns a bitmask representing the cv-qualifiers. */
> > @@ -23873,6 +23892,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> > break;
> > }
> > + if (RID_FIRST_ADDR_SPACE <= token->keyword
> > + && token->keyword <= RID_LAST_ADDR_SPACE)
> > + cv_qualifier
> > + = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> > +
> > if (!cv_qualifier)
> > break;
> > @@ -32893,6 +32917,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > decl_specs->locations[ds] = location;
> > if (ds == ds_thread)
> > decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> > + else if (ds == ds_addr_space)
> > + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> > }
> > else
> > {
> > @@ -32925,6 +32951,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > error_at (&richloc, "duplicate %qD", token->u.value);
> > }
> > }
> > + else if (ds == ds_addr_space)
> > + {
> > + addr_space_t as1 = decl_specs->address_space;
> > + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> > +
> > + gcc_rich_location richloc (location);
> > + richloc.add_fixit_remove ();
> > + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> > + && as1 != as2)
> > + error_at (&richloc,
> > + "conflicting named address spaces (%s vs %s)",
> > + c_addr_space_name (as1), c_addr_space_name (as2));
> > + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> > + error_at (&richloc,
> > + "duplicate named address space %s",
> > + c_addr_space_name (as1));
> > +
> > + decl_specs->address_space = as2;
> > + }
> > else
> > {
> > static const char *const decl_spec_names[] = {
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index e4dca9d4f9d..7b73a57091e 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -23778,8 +23778,19 @@ template_decl_level (tree decl)
> > static int
> > check_cv_quals_for_unify (int strict, tree arg, tree parm)
> > {
> > - int arg_quals = cp_type_quals (arg);
> > - int parm_quals = cp_type_quals (parm);
> > + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > +
> > + /* Try to unify ARG's address space into PARM's address space.
> > + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> > + there are no constraints on address spaces for this type. */
> > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > + addr_space_t as_common;
> > + addr_space_superset (as_arg, as_parm, &as_common);
> > +
> > + if (!(as_parm == as_common || as_parm == 0))
> > + return 0;
>
> I'd expect address space qualifiers to follow the 'strict' parameter like
> the other qualifiers; the above test seems to assume
> UNIFY_ALLOW_{OUTER_,}LESS_CV_QUAL.
>
The reason I ignored strict was to enforce that the deduced address
space is always at most "as_parm" unless "as_parm" is the generic address
space, and prevent unifying if the two address spaces are disjoint
unless "parm" does not have any address space constraints; and avoid the
addition/deletion of an address space to "arg" during the unifying
process.
Since I don't really understand the whole picture behind strict, and when
check_cv_quals_for_unify gets called with which variant of restrict it
might be me who tried to be overcareful when unifying the address
spaces.
> > if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> > && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> > @@ -24415,10 +24426,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> > arg, parm))
> > return unify_cv_qual_mismatch (explain_p, parm, arg);
> > + int arg_cv_quals = cp_type_quals (arg);
> > + int parm_cv_quals = cp_type_quals (parm);
> > +
> > + /* If PARM does not contain any address spaces constraints it can
> > + fully match the address space of ARG. However, if PARM contains an
> > + address space constraints, it becomes the upper bound. That is,
> > + AS_ARG may be promoted to AS_PARM but not the converse. If we
> > + ended up here, it means that `check_cv_quals_for_unify' succeeded
> > + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> > + AS_PARM. */
> > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> > + addr_space_t as_common = as_parm ? 0 : as_arg;
>
> Hmm, I'd think we also want as_common = as_arg when it's a subset of
> as_parm.
>
Let's assume that "PARM" is "__as1 T", and since the call to
check_cv_quals_for_unify succeeded we know that "as_common" is
"__as1". That is ARG is of the form "__as2 U" with "__as2" a
subset of "__as1", hence we are trying to unify
__as1 T = __as1 U
which does not give any constraints over PARM since it alreay contains
the common address space, hence there is no more constraints on T and
as_common = 0.
However, if PARM's address space is 0, we are trying to unify
T = __as1 U
and we need to add __addr_space1 to the constraints of T.
If as_parm is not the generic address space (ie, as_parm != 0)
> > /* Consider the case where ARG is `const volatile int' and
> > PARM is `const T'. Then, T should be `volatile int'. */
> > arg = cp_build_qualified_type
> > (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
> > + int unified_cv =
> > + (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> > + | ENCODE_QUAL_ADDR_SPACE (as_common));
> > + arg = cp_build_qualified_type (arg, unified_cv, tf_none);
> > if (arg == error_mark_node)
> > return unify_invalid (explain_p);
> > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> > index 45348c58bb6..1f330ca93ed 100644
> > --- a/gcc/cp/tree.cc
> > +++ b/gcc/cp/tree.cc
> > @@ -6072,15 +6072,6 @@ cp_free_lang_data (tree t)
> > DECL_CHAIN (t) = NULL_TREE;
> > }
> > -/* Stub for c-common. Please keep in sync with c-decl.cc.
> > - FIXME: If address space support is target specific, then this
> > - should be a C target hook. But currently this is not possible,
> > - because this function is called via REGISTER_TARGET_PRAGMAS. */
> > -void
> > -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> > -{
> > -}
> > -
> > /* Return the number of operands in T that we care about for things like
> > mangling. */
> > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> > index da0e1427b97..93cfdc70e2d 100644
> > --- a/gcc/cp/typeck.cc
> > +++ b/gcc/cp/typeck.cc
> > @@ -803,10 +803,28 @@ composite_pointer_type (const op_location_t &location,
> > else
> > return error_mark_node;
> > }
> > + /* If possible merge the address space into the superset of the address
> > + spaces of t1 and t2, or raise an error. */
> > + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> > + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> > + addr_space_t as_common;
> > +
> > + /* If the two named address spaces are different, determine the common
> > + superset address space. If there isn't one, raise an error. */
> > + if (!addr_space_superset (as_t1, as_t2, &as_common))
> > + {
> > + as_common = as_t1;
> > + error_at (location,
> > + "%qT and %qT are in disjoint named address spaces",
> > + t1, t2);
>
> Why not return error_mark_node here?
>
That's a mistake. Thanks.
> > + }
> > + int quals_t1 = cp_type_quals (TREE_TYPE (t1));
> > + int quals_t2 = cp_type_quals (TREE_TYPE (t2));
> > result_type
> > = cp_build_qualified_type (void_type_node,
> > - (cp_type_quals (TREE_TYPE (t1))
> > - | cp_type_quals (TREE_TYPE (t2))));
> > + (CLEAR_QUAL_ADDR_SPACE (quals_t1)
> > + | CLEAR_QUAL_ADDR_SPACE (quals_t2)
> > + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> > result_type = build_pointer_type (result_type);
> > /* Merge the attributes. */
> > attributes = (*targetm.merge_type_attributes) (t1, t2);
> > @@ -1731,7 +1749,9 @@ comptypes (tree t1, tree t2, int strict)
> > }
> > /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
> > - top-level qualifiers. */
> > + top-level qualifiers, except for named address spaces. If the pointers point
> > + to different named addresses spaces, then we must determine if one address
> > + space is a subset of the other. */
> > bool
> > same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > @@ -1741,6 +1761,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > if (type1 == type2)
> > return true;
> > + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
> > + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
> > + addr_space_t as_common;
> > +
> > + /* Fail if pointers point to incompatible address spaces. */
> > + if (!addr_space_superset (as_type1, as_type2, &as_common))
> > + return false;
>
> Why do you need this change? I'd expect this function to ignore top level
> address space qualifiers like the other qualifiers.
>
I am mirroring the C front-end here, which does the same thing in
"comp_target_types" (gcc/c/c-typeck.cc), which ignores qualifiers but
not address spaces when checking if two pointer types are equivalent.
> > type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
> > type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
> > return same_type_p (type1, type2);
> > @@ -6672,10 +6700,32 @@ static tree
> > pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
> > tsubst_flags_t complain, tree *instrument_expr)
> > {
> > - tree result, inttype;
> > tree restype = ptrdiff_type_node;
> > + tree result, inttype;
> > +
> > + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
> > + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
> > tree target_type = TREE_TYPE (ptrtype);
> > + /* If the operands point into different address spaces, we need to
> > + explicitly convert them to pointers into the common address space
> > + before we can subtract the numerical address values. */
> > + if (as0 != as1)
> > + {
> > + addr_space_t as_common;
> > + tree common_type;
> > +
> > + /* Determine the common superset address space. This is guaranteed
> > + to exist because the caller verified that comp_target_types
> > + returned non-zero. */
> > + if (!addr_space_superset (as0, as1, &as_common))
> > + gcc_unreachable ();
> > +
> > + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
> > + op0 = convert (common_type, op0);
> > + op1 = convert (common_type, op1);
> > + }
>
> I think you shouldn't need to change pointer_diff if composite_pointer_type
> returns error_mark_node above.
I'll have a look, the idea here is to prevent "a - b" with "a" and "b"
from different address spaces.
>
> > if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
> > return error_mark_node;
> > @@ -11286,6 +11336,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> > to_more_cv_qualified = true;
> > }
> > + /* Warn about conversions between pointers to disjoint
> > + address spaces. */
> > + if (TREE_CODE (from) == POINTER_TYPE
> > + && TREE_CODE (to) == POINTER_TYPE)
> > + {
> > + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> > + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> > + addr_space_t as_common;
> > +
> > + if (!addr_space_superset (as_to, as_from, &as_common))
> > + return false;
>
> I think you also want to check that as_common == as_to here?
>
Yes.
> > + }
> > +
> > if (constp > 0)
> > constp &= TYPE_READONLY (to);
> > }
> > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > index cfbe32afce9..ef75f6b83a2 100644
> > --- a/gcc/doc/extend.texi
> > +++ b/gcc/doc/extend.texi
> > @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> > @section Named Address Spaces
> > @cindex Named Address Spaces
> > -As an extension, GNU C supports named address spaces as
> > +As an extension, GNU C and GNU C++ support named address spaces as
> > defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> > address spaces in GCC will evolve as the draft technical report
> > changes. Calling conventions for any target might also change. At
> > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > new file mode 100644
> > index 00000000000..c01f8d6054a
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > @@ -0,0 +1,10 @@
> > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>
> This can be dg-do compile, I don't think you get anything from running an
> empty main.
>
Yes.
> > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>
> And then you don't need -save-temps. What are the other options for?
>
I forgot to remove -Wabi and -fabi-version, this was from my first
attempt when I used AS<number> to mangle which changed the ABI. I'll
remove them.
> > +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> > +
> > +int f (int volatile __seg_fs *a)
> > +{
> > + return *a;
> > +}
> > +
> > +int main () {}
> > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > new file mode 100644
> > index 00000000000..862bbbdcdf2
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > @@ -0,0 +1,9 @@
> > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>
> Also not clear that running is important for this test.
>
Noted.
> > +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
> > +
> > +template <class T>
> > +int f (T *p) { return *p; }
> > +int g (__seg_fs int *p) { return *p; }
> > +__seg_fs int *a;
> > +int main() { f(a); }
> > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > new file mode 100644
> > index 00000000000..c04d2f497da
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > @@ -0,0 +1,5 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +__seg_fs char a, b, c;
> > +__seg_fs const int *p;
> > +static /* give internal linkage to the following anonymous struct */
>
> Hmm, this 'static' gives internal linkage to the variable q, not the type.
> What do you want it for?
>
Yes, the idea is to give internal linkage to q, otherwise g++
complains in -std=c++98 mode because q is externally visible but it
can't be reffered from anywhere else since there is no tag for this
structure.
> > +__seg_fs struct { int a; char b; } * __seg_gs q;
> > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > new file mode 100644
> > index 00000000000..86c02d1e7f5
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > @@ -0,0 +1,20 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +int __seg_fs * fs1;
> > +int __seg_fs * fs2;
> > +float __seg_gs * gs1;
> > +float __seg_gs * gs2;
> > +
> > +int
> > +main ()
> > +{
> > + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
> > + fs1 - fs2;
> > + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
> > + fs1 == fs2;
> > + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > + fs1 = fs2;
> > + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
> > + fs1 > fs2;
> > + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > new file mode 100644
> > index 00000000000..12d7975e560
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > @@ -0,0 +1,31 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +// { dg-prune-output "does not allow .register. storage class specifier" }
> > +int __seg_fs * outer_b;
> > +
> > +struct s {
> > + __seg_fs int * ok;
> > + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
> > +};
> > +
> > +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
> > +
> > +namespace ns_a
> > +{
> > + int __seg_fs * inner_b;
> > +
> > + template<typename T>
> > + int f (T &a) { return a; }
> > + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
> > + int h (__seg_fs int *a) { return *a; }
> > +}
> > +
> > +int
> > +main ()
> > +{
> > + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
> > + static __seg_gs int static_gs;
> > + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
> > + __seg_fs int *pa = outer_b;
> > + __seg_fs int& ra = *ns_a::inner_b;
> > + return ns_a::f(ra) + ns_a::f(*pa);
> > +}
> > diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> > new file mode 100644
> > index 00000000000..ebb6316054a
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> > @@ -0,0 +1,9 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +
> > +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> > +
> > +int
> > +main ()
> > +{
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > new file mode 100644
> > index 00000000000..2e8ee32a885
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > @@ -0,0 +1,10 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +// { dg-options "-std=gnu++98" }
> > +
> > +int
> > +main ()
> > +{
> > + struct foo {int a; char b[2];} structure;
> > + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > new file mode 100644
> > index 00000000000..5b2c0f28078
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > @@ -0,0 +1,9 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +
> > +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> > +
> > +int
> > +main ()
> > +{
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
> > new file mode 100644
> > index 00000000000..70dfcce53fa
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
> > @@ -0,0 +1,17 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +
> > +int __seg_fs * fs1;
> > +int __seg_gs * gs1;
> > +
> > +template<typename T, typename U>
> > +__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
> > +template<typename T, typename U>
> > +__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
> > +
> > +int
> > +main ()
> > +{
> > + f (fs1, gs1);
> > + f (gs1, fs1);
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
> > new file mode 100644
> > index 00000000000..5df115db939
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
> > @@ -0,0 +1,17 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +// { dg-skip-if "" { *-*-* } { "-std=c++98" "-std=c++03" "-std=gnu++98" "-std=gnu++03" } { "" } }
>
> This can be { dg-require-effective-target c++11 }
>
> > +// decltype is ony available since c++11
>
> "only"
>
> > +
> > +int __seg_fs * fs1;
> > +int __seg_gs * gs1;
> > +
> > +template<typename T> struct strip;
> > +template<typename T> struct strip<__seg_fs T *> { typedef T type; };
> > +template<typename T> struct strip<__seg_gs T *> { typedef T type; };
> > +
> > +int
> > +main ()
> > +{
> > + *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
> > + return 0;
> > +}
> > diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
> > new file mode 100644
> > index 00000000000..526bbaa56b7
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
> > @@ -0,0 +1,16 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +
> > +int __seg_fs * fs1;
> > +int __seg_gs * gs1;
> > +
> > +template<typename T, typename U>
> > +bool f (T __seg_fs * a, U __seg_gs * b)
> > +{
> > + return *(T *) a == *(U *) b;
> > +}
> > +
> > +int
> > +main ()
> > +{
> > + return f (fs1, gs1);
> > +}
> > diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > new file mode 100644
> > index 00000000000..ae9f4de0e1f
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > @@ -0,0 +1,8 @@
> > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > +
> > +template <class T>
> > +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> > + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> > +__seg_fs int *a;
> > +int main() { f(a); } // { dg-error "no matching" }
> > +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> > diff --git a/gcc/tree.h b/gcc/tree.h
> > index 9af971cf401..4aebfef854b 100644
> > --- a/gcc/tree.h
> > +++ b/gcc/tree.h
> > @@ -2292,7 +2292,7 @@ extern tree vector_element_bits_tree (const_tree);
> > /* Encode/decode the named memory support as part of the qualifier. If more
> > than 8 qualifiers are added, these macros need to be adjusted. */
> > -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> > +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> > #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
> > /* Return all qualifiers except for the address space qualifiers. */
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-10-18 17:01 ` Paul Iannetta
@ 2022-10-19 18:55 ` Jason Merrill
2022-10-26 7:18 ` Paul Iannetta
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-19 18:55 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/18/22 13:01, Paul Iannetta wrote:
> Thank you very much for the detailed review.
>
> On Tue, Oct 18, 2022 at 10:24:23AM -0400, Jason Merrill wrote:
>> On 10/18/22 03:37, Paul Iannetta wrote:
>>> On Fri, Oct 14, 2022 at 11:19:50AM -0400, Jason Merrill wrote:
>>>> On 10/13/22 17:57, Paul Iannetta wrote:
>>>>> On Thu, Oct 13, 2022 at 03:41:16PM -0400, Jason Merrill wrote:
>>>>>> On 10/13/22 12:02, Paul Iannetta wrote:
>>>>>>> On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
>>>>>>>> On 10/13/22 11:23, Paul Iannetta wrote:
>>>>>>>>> On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
>>>>>>>>>> On 10/12/22 20:52, Paul Iannetta wrote:
>>>>>>>>>>> There are quite a few things I would like to clarify concerning some
>>>>>>>>>>> implementation details.
>>>>>>>>>>> - A variable with automatic storage (which is neither a pointer nor
>>>>>>>>>>> a reference) cannot be qualified with an address space. I detect
>>>>>>>>>>> this by the combination of `sc_none' and `! toplevel_bindings_p ()',
>>>>>>>>>>> but I've also seen the use of `at_function_scope' at other places.
>>>>>>>>>>> And I'm unsure which one is appropriate here.
>>>>>>>>>>> This detection happens at the very end of grokdeclarator because I
>>>>>>>>>>> need to know that the type is a pointer, which is not know until
>>>>>>>>>>> very late in the function.
>>>>>>>>>>
>>>>>>>>>> At that point you have the decl, and you can ask directly what its storage
>>>>>>>>>> duration is, perhaps using decl_storage_duration.
>>>>>>>>>>
>>>>>>>>>> But why do you need to know whether the type is a pointer? The attribute
>>>>>>>>>> applies to the target type of the pointer, not the pointer type. I think
>>>>>>>>>> the problem is that you're looking at declspecs when you ought to be looking
>>>>>>>>>> at type_quals.
>>>>>>>>>
>>>>>>>>> I need to know that the base type is a pointer to reject invalid
>>>>>>>>> declarations such as:
>>>>>>>>>
>>>>>>>>> int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
>>>>>>>>>
>>>>>>>>> because parameters and auto variables can have an address space
>>>>>>>>> qualifier only if they are pointer or reference type, which I can't
>>>>>>>>> tell only from type_quals.
>>>>>>>>
>>>>>>>> But "int *__seg_fs a" is just as invalid as the above; the difference is not
>>>>>>>> whether a is a pointer, but whether the address-space-qualified is the type
>>>>>>>> of a itself or some sub-type.
>>>>>>>
>>>>>>> I agree that "int * __seg_fs a" is invalid but it is accepted by the C
>>>>>>> front-end, and by clang (both C and C++), the behavior is that the
>>>>>>> address-name is silently ignored.
>>>>>>
>>>>>> Hmm, that sounds like a bug; in that case, presumably the user meant to
>>>>>> qualify the pointed-to type, and silently ignoring seems unlikely to give
>>>>>> the effect they want.
>>>>>>
>>>>>
>>>>> Well, actually, I'm re-reading the draft and "int * __seg_fs a" is
>>>>> valid. It means "pointer in address space __seg_fs pointing to an
>>>>> object in the generic address space", whereas "__seg_fs int * a" means
>>>>> "pointer in the generic address space pointing to an object in the
>>>>> __seg_fs address-space".
>>>>>
>>>>> Oddities such as, "__seg_fs int * __seg_gs a" are also perfectly
>>>>> valid.
>>>>
>>>> If a has static storage duration, sure; I was still thinking about
>>>> declarations with automatic storage duration such as in your example above.
>>>>
>>>
>>> Thanks, I only use type_quals now. I also took into account the style
>>> recommendations from Jakub, and included the other template tests.
>>> I rebased over trunk, bootstrapped the compiler and run the "make
>>> check-gcc" with no regressions on x86.
>>>
>>> Paul
>>>
>>> # ------------------------ >8 ------------------------
>>> Add support for custom address spaces in C++
>>>
>>> gcc/
>>> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>>>
>>> gcc/c/
>>> * c-decl.cc: Remove c_register_addr_space.
>>>
>>> gcc/c-family/
>>> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
>>> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
>>> * c-common.h: Remove the FIXME.
>>> (addr_space_superset): New declaration.
>>>
>>> gcc/cp/
>>> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
>>> (struct cp_decl_specifier_seq): Likewise.
>>> * decl.cc (get_type_quals): Likewise.
>>> (check_tag_decl): Likewise.
>>> (grokdeclarator): Likewise.
>>> * parser.cc (cp_parser_type_specifier): Likewise.
>>> (cp_parser_cv_qualifier_seq_opt): Likewise.
>>> (cp_parser_postfix_expression): Likewise.
>>> (cp_parser_type_specifier): Likewise.
>>> (set_and_check_decl_spec_loc): Likewise.
>>> * typeck.cc (composite_pointer_type): Likewise
>>> (comp_ptr_ttypes_real): Likewise.
>>> (same_type_ignoring_top_level_qualifiers_p): Likewise.
>>> * pt.cc (check_cv_quals_for_unify): Likewise.
>>> (unify): Likewise.
>>> * tree.cc: Remove c_register_addr_space stub.
>>> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
>>> using the extended qualifier notation.
>>>
>>> gcc/doc
>>> * extend.texi (Named Address Spaces): add a mention about C++
>>> support.
>>>
>>> gcc/testsuite/
>>> * g++.dg/abi/mangle-addr-space1.C: New test.
>>> * g++.dg/abi/mangle-addr-space2.C: New test.
>>> * g++.dg/parse/addr-space.C: New test.
>>> * g++.dg/parse/addr-space1.C: New test.
>>> * g++.dg/parse/addr-space2.C: New test.
>>> * g++.dg/parse/template/spec-addr-space.C: New test.
>>> * g++.dg/ext/addr-space-decl.C: New test.
>>> * g++.dg/ext/addr-space-ref.C: New test.
>>> * g++.dg/ext/addr-space-ops.C: New test.
>>> * g++.dg/template/addr-space-overload.C: New test.
>>> * g++.dg/template/addr-space-strip1.C: New test.
>>> * g++.dg/template/addr-space-strip2.C: New test.
>>>
>>> # ------------------------ >8 ------------------------
>>> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
>>> index 9ec9100cc90..3b79dc47515 100644
>>> --- a/gcc/c-family/c-common.cc
>>> +++ b/gcc/c-family/c-common.cc
>>> @@ -588,6 +588,33 @@ c_addr_space_name (addr_space_t as)
>>> return IDENTIFIER_POINTER (ridpointers [rid]);
>>> }
>>> +/* Return true if between two named address spaces, whether there is a superset
>>> + named address space that encompasses both address spaces. If there is a
>>> + superset, return which address space is the superset. */
>>> +
>>> +bool
>>> +addr_space_superset (addr_space_t as1, addr_space_t as2,
>>> + addr_space_t * common)
>>> +{
>>> + if (as1 == as2)
>>> + {
>>> + *common = as1;
>>> + return true;
>>> + }
>>> + else if (targetm.addr_space.subset_p (as1, as2))
>>> + {
>>> + *common = as2;
>>> + return true;
>>> + }
>>> + else if (targetm.addr_space.subset_p (as2, as1))
>>> + {
>>> + *common = as1;
>>> + return true;
>>> + }
>>> + else
>>> + return false;
>>> +}
>>> +
>>> /* Push current bindings for the function name VAR_DECLS. */
>>> void
>>> @@ -2785,6 +2812,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
>>> return build_nonstandard_integer_type (width, unsignedp);
>>> }
>>> +/* Register reserved keyword WORD as qualifier for address space AS. */
>>> +
>>> +void
>>> +c_register_addr_space (const char *word, addr_space_t as)
>>> +{
>>> + int rid = RID_FIRST_ADDR_SPACE + as;
>>> + tree id;
>>> +
>>> + /* Address space qualifiers are only supported
>>> + in C with GNU extensions enabled. */
>>> + if (c_dialect_objc () || flag_no_asm)
>>> + return;
>>> +
>>> + id = get_identifier (word);
>>> + C_SET_RID_CODE (id, rid);
>>> + TREE_LANG_FLAG_0 (id) = 1;
>>> + ridpointers[rid] = id;
>>> +}
>>> +
>>> /* The C version of the register_builtin_type langhook. */
>>> void
>>> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
>>> index 62ab4ba437b..a3864d874aa 100644
>>> --- a/gcc/c-family/c-common.h
>>> +++ b/gcc/c-family/c-common.h
>>> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>>> extern tree (*make_fname_decl) (location_t, tree, int);
>>> -/* In c-decl.cc and cp/tree.cc. FIXME. */
>>> -extern void c_register_addr_space (const char *str, addr_space_t as);
>>> -
>>> /* In c-common.cc. */
>>> extern bool in_late_binary_op;
>>> extern const char *c_addr_space_name (addr_space_t as);
>>> +extern const char *c_addr_space_name (addr_space_t as);
>>> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
>>> extern tree identifier_global_value (tree);
>>> extern tree identifier_global_tag (tree);
>>> extern bool names_builtin_p (const char *);
>>> @@ -951,6 +950,7 @@ extern bool c_common_init (void);
>>> extern void c_common_finish (void);
>>> extern void c_common_parse_file (void);
>>> extern alias_set_type c_common_get_alias_set (tree);
>>> +extern void c_register_addr_space (const char *, addr_space_t);
>>> extern void c_register_builtin_type (tree, const char*);
>>> extern bool c_promoting_integer_type_p (const_tree);
>>> extern bool self_promoting_args_p (const_tree);
>>> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
>>> index a7571cc7542..b1f69997ff7 100644
>>> --- a/gcc/c/c-decl.cc
>>> +++ b/gcc/c/c-decl.cc
>>> @@ -12531,25 +12531,6 @@ c_parse_final_cleanups (void)
>>> ext_block = NULL;
>>> }
>>> -/* Register reserved keyword WORD as qualifier for address space AS. */
>>> -
>>> -void
>>> -c_register_addr_space (const char *word, addr_space_t as)
>>> -{
>>> - int rid = RID_FIRST_ADDR_SPACE + as;
>>> - tree id;
>>> -
>>> - /* Address space qualifiers are only supported
>>> - in C with GNU extensions enabled. */
>>> - if (c_dialect_objc () || flag_no_asm)
>>> - return;
>>> -
>>> - id = get_identifier (word);
>>> - C_SET_RID_CODE (id, rid);
>>> - C_IS_RESERVED_WORD (id) = 1;
>>> - ridpointers [rid] = id;
>>> -}
>>> -
>>> /* Return identifier to look up for omp declare reduction. */
>>> tree
>>> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
>>> index fdb96c28c51..2a700bbaff3 100644
>>> --- a/gcc/c/c-typeck.cc
>>> +++ b/gcc/c/c-typeck.cc
>>> @@ -303,32 +303,6 @@ c_type_promotes_to (tree type)
>>> return type;
>>> }
>>> -/* Return true if between two named address spaces, whether there is a superset
>>> - named address space that encompasses both address spaces. If there is a
>>> - superset, return which address space is the superset. */
>>> -
>>> -static bool
>>> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
>>> -{
>>> - if (as1 == as2)
>>> - {
>>> - *common = as1;
>>> - return true;
>>> - }
>>> - else if (targetm.addr_space.subset_p (as1, as2))
>>> - {
>>> - *common = as2;
>>> - return true;
>>> - }
>>> - else if (targetm.addr_space.subset_p (as2, as1))
>>> - {
>>> - *common = as1;
>>> - return true;
>>> - }
>>> - else
>>> - return false;
>>> -}
>>> -
>>> /* Return a variant of TYPE which has all the type qualifiers of LIKE
>>> as well as those of TYPE. */
>>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>>> index e2607f09c19..0248569a95b 100644
>>> --- a/gcc/cp/cp-tree.h
>>> +++ b/gcc/cp/cp-tree.h
>>> @@ -6235,6 +6235,7 @@ enum cp_decl_spec {
>>> ds_const,
>>> ds_volatile,
>>> ds_restrict,
>>> + ds_addr_space,
>>> ds_inline,
>>> ds_virtual,
>>> ds_explicit,
>>> @@ -6281,6 +6282,8 @@ struct cp_decl_specifier_seq {
>>> cp_storage_class storage_class;
>>> /* For the __intN declspec, this stores the index into the int_n_* arrays. */
>>> int int_n_idx;
>>> + /* The address space that the declaration belongs to. */
>>> + addr_space_t address_space;
>>> /* True iff TYPE_SPEC defines a class or enum. */
>>> BOOL_BITFIELD type_definition_p : 1;
>>> /* True iff multiple types were (erroneously) specified for this
>>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
>>> index 85b892cddf0..a87fed04529 100644
>>> --- a/gcc/cp/decl.cc
>>> +++ b/gcc/cp/decl.cc
>>> @@ -5290,6 +5290,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
>>> type_quals |= TYPE_QUAL_VOLATILE;
>>> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
>>> type_quals |= TYPE_QUAL_RESTRICT;
>>> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>>> return type_quals;
>>> }
>>> @@ -5412,6 +5414,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
>>> error_at (declspecs->locations[ds_restrict],
>>> "%<__restrict%> can only be specified for objects and "
>>> "functions");
>>> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>> + error_at (declspecs->locations[ds_addr_space],
>>> + "address space can only be specified for objects and "
>>> + "functions");
>>> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
>>> error_at (declspecs->locations[ds_thread],
>>> "%<__thread%> can only be specified for objects "
>>> @@ -14608,6 +14614,59 @@ grokdeclarator (const cp_declarator *declarator,
>>> if (!processing_template_decl)
>>> cp_apply_type_quals_to_decl (type_quals, decl);
>>> + /* Warn about address space used for things other than static memory or
>>> + pointers. */
>>> + addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
>>> + if (!ADDR_SPACE_GENERIC_P (address_space))
>>> + {
>>> + if (decl_context == NORMAL)
>>> + {
>>> + switch (storage_class)
>>
>> I would still suggest checking decl_storage_duration at this point rather
>> than the storage_class specifier.
>
> Unless I misunderstand something, I can't weed out register variables
> if I rely on decl_storage_duration.
Yes, but register variables are automatic, so they'll get that error; I
don't think they need their own specific error.
>>> + {
>>> + case sc_auto:
>>> + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
>>> + c_addr_space_name (address_space), name);
>>> + break;
>>> + case sc_register:
>>> + error ("%qs combined with %<register%> qualifier for %qs",
>>> + c_addr_space_name (address_space), name);
>>> + break;
>>> + case sc_none:
>>> + if (! toplevel_bindings_p ())
>>> + error ("%qs specified for auto variable %qs",
>>
>> And let's refer to automatic storage duration rather than shorten to 'auto'.
>>
> Right.
>>> + c_addr_space_name (address_space), name);
>>> + break;
>>> + case sc_mutable:
>>> + error ("%qs combined with %<mutable%> qualifier for %qs",
>>> + c_addr_space_name (address_space), name);
>>> + break;
>>> + case sc_static:
>>> + case sc_extern:
>>> + break;
>>> + default:
>>> + gcc_unreachable ();
>>> + }
>>> + }
>>> + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
>>> + {
>>> + if (name)
>>> + error ("%qs specified for parameter %qs",
>>> + c_addr_space_name (address_space), name);
>>> + else
>>> + error ("%qs specified for unnamed parameter",
>>> + c_addr_space_name (address_space));
>>> + }
>>> + else if (decl_context == FIELD)
>>> + {
>>> + if (name)
>>> + error ("%qs specified for structure field %qs",
>>> + c_addr_space_name (address_space), name);
>>> + else
>>> + error ("%qs specified for structure field",
>>> + c_addr_space_name (address_space));
>>> + }
>>> + }
>>> +
>>> return decl;
>>> }
>>> }
>>> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
>>> index 1215463089b..aafff98f05a 100644
>>> --- a/gcc/cp/mangle.cc
>>> +++ b/gcc/cp/mangle.cc
>>> @@ -2520,6 +2520,14 @@ write_CV_qualifiers_for_type (const tree type)
>>> array. */
>>> cp_cv_quals quals = TYPE_QUALS (type);
>>> + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
>>> + {
>>> + const char *as_name = c_addr_space_name (as);
>>> + write_char ('U');
>>> + write_unsigned_number (strlen (as_name));
>>> + write_string (as_name);
>>> + ++num_qualifiers;
>>> + }
>>> if (quals & TYPE_QUAL_RESTRICT)
>>> {
>>> write_char ('r');
>>> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
>>> index 9ddfb027ff9..c82059d1efd 100644
>>> --- a/gcc/cp/parser.cc
>>> +++ b/gcc/cp/parser.cc
>>> @@ -7703,6 +7703,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
>>> postfix_expression = error_mark_node;
>>> break;
>>> }
>>> + if (type != error_mark_node
>>> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
>>> + && current_function_decl)
>>> + {
>>> + error
>>> + ("compound literal qualified by address-space "
>>> + "qualifier");
>>> + type = error_mark_node;
>>> + }
>>> /* Form the representation of the compound-literal. */
>>> postfix_expression
>>> = finish_compound_literal (type, initializer,
>>> @@ -19445,6 +19454,15 @@ cp_parser_type_specifier (cp_parser* parser,
>>> break;
>>> }
>>> +
>>> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
>>> + {
>>> + ds = ds_addr_space;
>>> + if (is_cv_qualifier)
>>> + *is_cv_qualifier = true;
>>> + }
>>> +
>>> +
>>
>> I don't think we need two blank lines before and after this block, one each
>> should be enough.
>>
> Indeed.
>>> /* Handle simple keywords. */
>>> if (ds != ds_last)
>>> {
>>> @@ -23837,6 +23855,7 @@ cp_parser_ptr_operator (cp_parser* parser,
>>> GNU Extension:
>>> cv-qualifier:
>>> + address-space-qualifier
>>> __restrict__
>>> Returns a bitmask representing the cv-qualifiers. */
>>> @@ -23873,6 +23892,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
>>> break;
>>> }
>>> + if (RID_FIRST_ADDR_SPACE <= token->keyword
>>> + && token->keyword <= RID_LAST_ADDR_SPACE)
>>> + cv_qualifier
>>> + = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>>> +
>>> if (!cv_qualifier)
>>> break;
>>> @@ -32893,6 +32917,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>> decl_specs->locations[ds] = location;
>>> if (ds == ds_thread)
>>> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
>>> + else if (ds == ds_addr_space)
>>> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
>>> }
>>> else
>>> {
>>> @@ -32925,6 +32951,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>> error_at (&richloc, "duplicate %qD", token->u.value);
>>> }
>>> }
>>> + else if (ds == ds_addr_space)
>>> + {
>>> + addr_space_t as1 = decl_specs->address_space;
>>> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
>>> +
>>> + gcc_rich_location richloc (location);
>>> + richloc.add_fixit_remove ();
>>> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
>>> + && as1 != as2)
>>> + error_at (&richloc,
>>> + "conflicting named address spaces (%s vs %s)",
>>> + c_addr_space_name (as1), c_addr_space_name (as2));
>>> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
>>> + error_at (&richloc,
>>> + "duplicate named address space %s",
>>> + c_addr_space_name (as1));
>>> +
>>> + decl_specs->address_space = as2;
>>> + }
>>> else
>>> {
>>> static const char *const decl_spec_names[] = {
>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>> index e4dca9d4f9d..7b73a57091e 100644
>>> --- a/gcc/cp/pt.cc
>>> +++ b/gcc/cp/pt.cc
>>> @@ -23778,8 +23778,19 @@ template_decl_level (tree decl)
>>> static int
>>> check_cv_quals_for_unify (int strict, tree arg, tree parm)
>>> {
>>> - int arg_quals = cp_type_quals (arg);
>>> - int parm_quals = cp_type_quals (parm);
>>> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>> +
>>> + /* Try to unify ARG's address space into PARM's address space.
>>> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
>>> + there are no constraints on address spaces for this type. */
>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>> + addr_space_t as_common;
>>> + addr_space_superset (as_arg, as_parm, &as_common);
>>> +
>>> + if (!(as_parm == as_common || as_parm == 0))
>>> + return 0;
>>
>> I'd expect address space qualifiers to follow the 'strict' parameter like
>> the other qualifiers; the above test seems to assume
>> UNIFY_ALLOW_{OUTER_,}LESS_CV_QUAL.
>>
> The reason I ignored strict was to enforce that the deduced address
> space is always at most "as_parm" unless "as_parm" is the generic address
> space, and prevent unifying if the two address spaces are disjoint
> unless "parm" does not have any address space constraints; and avoid the
> addition/deletion of an address space to "arg" during the unifying
> process.
>
> Since I don't really understand the whole picture behind strict, and when
> check_cv_quals_for_unify gets called with which variant of restrict it
> might be me who tried to be overcareful when unifying the address
> spaces.
How we need to handle differing qualifiers varies between different
template argument deduction contexts.
The code you wrote above is correct for the function call context, since
https://eel.is/c++draft/temp.deduct.call#4.2 says the deduced type can
be convertable by qualification conversion, i.e. parm more qualified
than arg (and my "LESS" above was backwards). This is a bit different
for address space qualifiers given that the qualification conversion
would be removing the address space qualifier or changing it to a more
general one, but the principle is the same.
But the allowance for qualifier changes doesn't apply to all deduction
contexts: for instance,
template <class T> void f(T * const *);
struct A {
template <class T> operator T**();
};
int main()
{
f((void**)0); // void** -> void*const* is a valid qualification conv
(void *const*)A(); // same conversion
void (*p)(void **) = f; // error, type mismatch
}
so similarly,
template <class T> void f(T **);
struct A {
template <class T> operator T*__seg_fs*();
};
int main()
{
f((void* __seg_fs *)0); // void*__seg_fs* -> void** should be OK
(void **)A(); // same conversion
void (*p)(void * __seg_fs *) = f; // error
}
>>> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
>>> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
>>> @@ -24415,10 +24426,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
>>> arg, parm))
>>> return unify_cv_qual_mismatch (explain_p, parm, arg);
>>> + int arg_cv_quals = cp_type_quals (arg);
>>> + int parm_cv_quals = cp_type_quals (parm);
>>> +
>>> + /* If PARM does not contain any address spaces constraints it can
>>> + fully match the address space of ARG. However, if PARM contains an
>>> + address space constraints, it becomes the upper bound. That is,
>>> + AS_ARG may be promoted to AS_PARM but not the converse. If we
>>> + ended up here, it means that `check_cv_quals_for_unify' succeeded
>>> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
>>> + AS_PARM. */
>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
>>> + addr_space_t as_common = as_parm ? 0 : as_arg;
>>
>> Hmm, I'd think we also want as_common = as_arg when it's a subset of
>> as_parm.
>
> Let's assume that "PARM" is "__as1 T", and since the call to
> check_cv_quals_for_unify succeeded we know that "as_common" is
> "__as1". That is ARG is of the form "__as2 U" with "__as2" a
> subset of "__as1", hence we are trying to unify
> __as1 T = __as1 U
> which does not give any constraints over PARM since it alreay contains
> the common address space, hence there is no more constraints on T and
> as_common = 0.
Agreed.
> However, if PARM's address space is 0, we are trying to unify
> T = __as1 U
> and we need to add __addr_space1 to the constraints of T.
Agreed.
> If as_parm is not the generic address space (ie, as_parm != 0)
Looks like this comment got cut off? This is the case I was talking
about. When we are trying to unify
__as1 T = __as2 U
and __as2 is a subset of __as1, I think we want T to be deduced to __as2
U, and then substitution will need to handle substituting __as2 U for T
into __as1 T to get __as2 U.
>>> /* Consider the case where ARG is `const volatile int' and
>>> PARM is `const T'. Then, T should be `volatile int'. */
>>> arg = cp_build_qualified_type
>>> (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
>>> + int unified_cv =
>>> + (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
>>> + | ENCODE_QUAL_ADDR_SPACE (as_common));
>>> + arg = cp_build_qualified_type (arg, unified_cv, tf_none);
>>> if (arg == error_mark_node)
>>> return unify_invalid (explain_p);
>>> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
>>> index 45348c58bb6..1f330ca93ed 100644
>>> --- a/gcc/cp/tree.cc
>>> +++ b/gcc/cp/tree.cc
>>> @@ -6072,15 +6072,6 @@ cp_free_lang_data (tree t)
>>> DECL_CHAIN (t) = NULL_TREE;
>>> }
>>> -/* Stub for c-common. Please keep in sync with c-decl.cc.
>>> - FIXME: If address space support is target specific, then this
>>> - should be a C target hook. But currently this is not possible,
>>> - because this function is called via REGISTER_TARGET_PRAGMAS. */
>>> -void
>>> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
>>> -{
>>> -}
>>> -
>>> /* Return the number of operands in T that we care about for things like
>>> mangling. */
>>> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
>>> index da0e1427b97..93cfdc70e2d 100644
>>> --- a/gcc/cp/typeck.cc
>>> +++ b/gcc/cp/typeck.cc
>>> @@ -803,10 +803,28 @@ composite_pointer_type (const op_location_t &location,
>>> else
>>> return error_mark_node;
>>> }
>>> + /* If possible merge the address space into the superset of the address
>>> + spaces of t1 and t2, or raise an error. */
>>> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
>>> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
>>> + addr_space_t as_common;
>>> +
>>> + /* If the two named address spaces are different, determine the common
>>> + superset address space. If there isn't one, raise an error. */
>>> + if (!addr_space_superset (as_t1, as_t2, &as_common))
>>> + {
>>> + as_common = as_t1;
>>> + error_at (location,
>>> + "%qT and %qT are in disjoint named address spaces",
>>> + t1, t2);
>>
>> Why not return error_mark_node here?
>>
> That's a mistake. Thanks.
>>> + }
>>> + int quals_t1 = cp_type_quals (TREE_TYPE (t1));
>>> + int quals_t2 = cp_type_quals (TREE_TYPE (t2));
>>> result_type
>>> = cp_build_qualified_type (void_type_node,
>>> - (cp_type_quals (TREE_TYPE (t1))
>>> - | cp_type_quals (TREE_TYPE (t2))));
>>> + (CLEAR_QUAL_ADDR_SPACE (quals_t1)
>>> + | CLEAR_QUAL_ADDR_SPACE (quals_t2)
>>> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
>>> result_type = build_pointer_type (result_type);
>>> /* Merge the attributes. */
>>> attributes = (*targetm.merge_type_attributes) (t1, t2);
>>> @@ -1731,7 +1749,9 @@ comptypes (tree t1, tree t2, int strict)
>>> }
>>> /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
>>> - top-level qualifiers. */
>>> + top-level qualifiers, except for named address spaces. If the pointers point
>>> + to different named addresses spaces, then we must determine if one address
>>> + space is a subset of the other. */
>>> bool
>>> same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>> @@ -1741,6 +1761,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>> if (type1 == type2)
>>> return true;
>>> + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
>>> + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
>>> + addr_space_t as_common;
>>> +
>>> + /* Fail if pointers point to incompatible address spaces. */
>>> + if (!addr_space_superset (as_type1, as_type2, &as_common))
>>> + return false;
>>
>> Why do you need this change? I'd expect this function to ignore top level
>> address space qualifiers like the other qualifiers.
>
> I am mirroring the C front-end here, which does the same thing in
> "comp_target_types" (gcc/c/c-typeck.cc), which ignores qualifiers but
> not address spaces when checking if two pointer types are equivalent.
This function serves a very different function from comp_target_types,
which deals with the types that pointers point to; this function is
ignoring top-level qualifiers that should not affect the type.
...except now I see that cp_build_binary_op is wierdly using this
function for pointer subtraction. I'd think it should use
composite_pointer_type instead, like EQ_EXPR does.
>>> type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
>>> type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
>>> return same_type_p (type1, type2);
>>> @@ -6672,10 +6700,32 @@ static tree
>>> pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
>>> tsubst_flags_t complain, tree *instrument_expr)
>>> {
>>> - tree result, inttype;
>>> tree restype = ptrdiff_type_node;
>>> + tree result, inttype;
>>> +
>>> + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
>>> + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
>>> tree target_type = TREE_TYPE (ptrtype);
>>> + /* If the operands point into different address spaces, we need to
>>> + explicitly convert them to pointers into the common address space
>>> + before we can subtract the numerical address values. */
>>> + if (as0 != as1)
>>> + {
>>> + addr_space_t as_common;
>>> + tree common_type;
>>> +
>>> + /* Determine the common superset address space. This is guaranteed
>>> + to exist because the caller verified that comp_target_types
>>> + returned non-zero. */
>>> + if (!addr_space_superset (as0, as1, &as_common))
>>> + gcc_unreachable ();
>>> +
>>> + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
>>> + op0 = convert (common_type, op0);
>>> + op1 = convert (common_type, op1);
>>> + }
>>
>> I think you shouldn't need to change pointer_diff if composite_pointer_type
>> returns error_mark_node above.
>
> I'll have a look, the idea here is to prevent "a - b" with "a" and "b"
> from different address spaces.
As above, I think this should have been handled in cp_build_binary_op.
>>> if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
>>> return error_mark_node;
>>> @@ -11286,6 +11336,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>>> to_more_cv_qualified = true;
>>> }
>>> + /* Warn about conversions between pointers to disjoint
>>> + address spaces. */
>>> + if (TREE_CODE (from) == POINTER_TYPE
>>> + && TREE_CODE (to) == POINTER_TYPE)
>>> + {
>>> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
>>> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
>>> + addr_space_t as_common;
>>> +
>>> + if (!addr_space_superset (as_to, as_from, &as_common))
>>> + return false;
>>
>> I think you also want to check that as_common == as_to here?
>>
> Yes.
>>> + }
>>> +
>>> if (constp > 0)
>>> constp &= TYPE_READONLY (to);
>>> }
>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>> index cfbe32afce9..ef75f6b83a2 100644
>>> --- a/gcc/doc/extend.texi
>>> +++ b/gcc/doc/extend.texi
>>> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
>>> @section Named Address Spaces
>>> @cindex Named Address Spaces
>>> -As an extension, GNU C supports named address spaces as
>>> +As an extension, GNU C and GNU C++ support named address spaces as
>>> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
>>> address spaces in GCC will evolve as the draft technical report
>>> changes. Calling conventions for any target might also change. At
>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>> new file mode 100644
>>> index 00000000000..c01f8d6054a
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>> @@ -0,0 +1,10 @@
>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>
>> This can be dg-do compile, I don't think you get anything from running an
>> empty main.
>>
> Yes.
>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>
>> And then you don't need -save-temps. What are the other options for?
>>
> I forgot to remove -Wabi and -fabi-version, this was from my first
> attempt when I used AS<number> to mangle which changed the ABI. I'll
> remove them.
>>> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
>>> +
>>> +int f (int volatile __seg_fs *a)
>>> +{
>>> + return *a;
>>> +}
>>> +
>>> +int main () {}
>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>> new file mode 100644
>>> index 00000000000..862bbbdcdf2
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>> @@ -0,0 +1,9 @@
>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>
>> Also not clear that running is important for this test.
>>
> Noted.
>>> +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
>>> +
>>> +template <class T>
>>> +int f (T *p) { return *p; }
>>> +int g (__seg_fs int *p) { return *p; }
>>> +__seg_fs int *a;
>>> +int main() { f(a); }
>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>> new file mode 100644
>>> index 00000000000..c04d2f497da
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>> @@ -0,0 +1,5 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +__seg_fs char a, b, c;
>>> +__seg_fs const int *p;
>>> +static /* give internal linkage to the following anonymous struct */
>>
>> Hmm, this 'static' gives internal linkage to the variable q, not the type.
>> What do you want it for?
>>
> Yes, the idea is to give internal linkage to q, otherwise g++
> complains in -std=c++98 mode because q is externally visible but it
> can't be reffered from anywhere else since there is no tag for this
> structure.
Then let's change the comment to /* give internal linkage to q */
>>> +__seg_fs struct { int a; char b; } * __seg_gs q;
>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>> new file mode 100644
>>> index 00000000000..86c02d1e7f5
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>> @@ -0,0 +1,20 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +int __seg_fs * fs1;
>>> +int __seg_fs * fs2;
>>> +float __seg_gs * gs1;
>>> +float __seg_gs * gs2;
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
>>> + fs1 - fs2;
>>> + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
>>> + fs1 == fs2;
>>> + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>> + fs1 = fs2;
>>> + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
>>> + fs1 > fs2;
>>> + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>> new file mode 100644
>>> index 00000000000..12d7975e560
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>> @@ -0,0 +1,31 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +// { dg-prune-output "does not allow .register. storage class specifier" }
>>> +int __seg_fs * outer_b;
>>> +
>>> +struct s {
>>> + __seg_fs int * ok;
>>> + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
>>> +};
>>> +
>>> +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
>>> +
>>> +namespace ns_a
>>> +{
>>> + int __seg_fs * inner_b;
>>> +
>>> + template<typename T>
>>> + int f (T &a) { return a; }
>>> + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
>>> + int h (__seg_fs int *a) { return *a; }
>>> +}
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
>>> + static __seg_gs int static_gs;
>>> + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
>>> + __seg_fs int *pa = outer_b;
>>> + __seg_fs int& ra = *ns_a::inner_b;
>>> + return ns_a::f(ra) + ns_a::f(*pa);
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
>>> new file mode 100644
>>> index 00000000000..ebb6316054a
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
>>> @@ -0,0 +1,9 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +
>>> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>> new file mode 100644
>>> index 00000000000..2e8ee32a885
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>> @@ -0,0 +1,10 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +// { dg-options "-std=gnu++98" }
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + struct foo {int a; char b[2];} structure;
>>> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>> new file mode 100644
>>> index 00000000000..5b2c0f28078
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>> @@ -0,0 +1,9 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +
>>> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
>>> new file mode 100644
>>> index 00000000000..70dfcce53fa
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
>>> @@ -0,0 +1,17 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +
>>> +int __seg_fs * fs1;
>>> +int __seg_gs * gs1;
>>> +
>>> +template<typename T, typename U>
>>> +__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
>>> +template<typename T, typename U>
>>> +__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + f (fs1, gs1);
>>> + f (gs1, fs1);
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
>>> new file mode 100644
>>> index 00000000000..5df115db939
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
>>> @@ -0,0 +1,17 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +// { dg-skip-if "" { *-*-* } { "-std=c++98" "-std=c++03" "-std=gnu++98" "-std=gnu++03" } { "" } }
>>
>> This can be { dg-require-effective-target c++11 }
Or put the x86 requirement in dg-require-effective-target, and put c++11
in the dg-do target spec, either way.
>>> +// decltype is ony available since c++11
>>
>> "only"
>>
>>> +
>>> +int __seg_fs * fs1;
>>> +int __seg_gs * gs1;
>>> +
>>> +template<typename T> struct strip;
>>> +template<typename T> struct strip<__seg_fs T *> { typedef T type; };
>>> +template<typename T> struct strip<__seg_gs T *> { typedef T type; };
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
>>> + return 0;
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
>>> new file mode 100644
>>> index 00000000000..526bbaa56b7
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
>>> @@ -0,0 +1,16 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +
>>> +int __seg_fs * fs1;
>>> +int __seg_gs * gs1;
>>> +
>>> +template<typename T, typename U>
>>> +bool f (T __seg_fs * a, U __seg_gs * b)
>>> +{
>>> + return *(T *) a == *(U *) b;
>>> +}
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + return f (fs1, gs1);
>>> +}
>>> diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>> new file mode 100644
>>> index 00000000000..ae9f4de0e1f
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>> @@ -0,0 +1,8 @@
>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>> +
>>> +template <class T>
>>> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
>>> + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
>>> +__seg_fs int *a;
>>> +int main() { f(a); } // { dg-error "no matching" }
>>> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
>>> diff --git a/gcc/tree.h b/gcc/tree.h
>>> index 9af971cf401..4aebfef854b 100644
>>> --- a/gcc/tree.h
>>> +++ b/gcc/tree.h
>>> @@ -2292,7 +2292,7 @@ extern tree vector_element_bits_tree (const_tree);
>>> /* Encode/decode the named memory support as part of the qualifier. If more
>>> than 8 qualifiers are added, these macros need to be adjusted. */
>>> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
>>> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
>>> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>>> /* Return all qualifiers except for the address space qualifiers. */
>
>
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-10-19 18:55 ` Jason Merrill
@ 2022-10-26 7:18 ` Paul Iannetta
2022-10-26 16:28 ` Jason Merrill
2022-11-03 13:38 ` [PATCH v3] " Georg-Johann Lay
0 siblings, 2 replies; 86+ messages in thread
From: Paul Iannetta @ 2022-10-26 7:18 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
On Wed, Oct 19, 2022 at 02:55:21PM -0400, Jason Merrill wrote:
> On 10/18/22 13:01, Paul Iannetta wrote:
> > Thank you very much for the detailed review.
> >
> > On Tue, Oct 18, 2022 at 10:24:23AM -0400, Jason Merrill wrote:
> > > On 10/18/22 03:37, Paul Iannetta wrote:
> > > > On Fri, Oct 14, 2022 at 11:19:50AM -0400, Jason Merrill wrote:
> > > > > On 10/13/22 17:57, Paul Iannetta wrote:
> > > > > > On Thu, Oct 13, 2022 at 03:41:16PM -0400, Jason Merrill wrote:
> > > > > > > On 10/13/22 12:02, Paul Iannetta wrote:
> > > > > > > > On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
> > > > > > > > > On 10/13/22 11:23, Paul Iannetta wrote:
> > > > > > > > > > On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
> > > > > > > > > > > On 10/12/22 20:52, Paul Iannetta wrote:
> > > > > > > > > > > > There are quite a few things I would like to clarify concerning some
> > > > > > > > > > > > implementation details.
> > > > > > > > > > > > - A variable with automatic storage (which is neither a pointer nor
> > > > > > > > > > > > a reference) cannot be qualified with an address space. I detect
> > > > > > > > > > > > this by the combination of `sc_none' and `! toplevel_bindings_p ()',
> > > > > > > > > > > > but I've also seen the use of `at_function_scope' at other places.
> > > > > > > > > > > > And I'm unsure which one is appropriate here.
> > > > > > > > > > > > This detection happens at the very end of grokdeclarator because I
> > > > > > > > > > > > need to know that the type is a pointer, which is not know until
> > > > > > > > > > > > very late in the function.
> > > > > > > > > > >
> > > > > > > > > > > At that point you have the decl, and you can ask directly what its storage
> > > > > > > > > > > duration is, perhaps using decl_storage_duration.
> > > > > > > > > > >
> > > > > > > > > > > But why do you need to know whether the type is a pointer? The attribute
> > > > > > > > > > > applies to the target type of the pointer, not the pointer type. I think
> > > > > > > > > > > the problem is that you're looking at declspecs when you ought to be looking
> > > > > > > > > > > at type_quals.
> > > > > > > > > >
> > > > > > > > > > I need to know that the base type is a pointer to reject invalid
> > > > > > > > > > declarations such as:
> > > > > > > > > >
> > > > > > > > > > int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
> > > > > > > > > >
> > > > > > > > > > because parameters and auto variables can have an address space
> > > > > > > > > > qualifier only if they are pointer or reference type, which I can't
> > > > > > > > > > tell only from type_quals.
> > > > > > > > >
> > > > > > > > > But "int *__seg_fs a" is just as invalid as the above; the difference is not
> > > > > > > > > whether a is a pointer, but whether the address-space-qualified is the type
> > > > > > > > > of a itself or some sub-type.
> > > > > > > >
> > > > > > > > I agree that "int * __seg_fs a" is invalid but it is accepted by the C
> > > > > > > > front-end, and by clang (both C and C++), the behavior is that the
> > > > > > > > address-name is silently ignored.
> > > > > > >
> > > > > > > Hmm, that sounds like a bug; in that case, presumably the user meant to
> > > > > > > qualify the pointed-to type, and silently ignoring seems unlikely to give
> > > > > > > the effect they want.
> > > > > > >
> > > > > >
> > > > > > Well, actually, I'm re-reading the draft and "int * __seg_fs a" is
> > > > > > valid. It means "pointer in address space __seg_fs pointing to an
> > > > > > object in the generic address space", whereas "__seg_fs int * a" means
> > > > > > "pointer in the generic address space pointing to an object in the
> > > > > > __seg_fs address-space".
> > > > > >
> > > > > > Oddities such as, "__seg_fs int * __seg_gs a" are also perfectly
> > > > > > valid.
> > > > >
> > > > > If a has static storage duration, sure; I was still thinking about
> > > > > declarations with automatic storage duration such as in your example above.
> > > > >
> > > >
> > > > Thanks, I only use type_quals now. I also took into account the style
> > > > recommendations from Jakub, and included the other template tests.
> > > > I rebased over trunk, bootstrapped the compiler and run the "make
> > > > check-gcc" with no regressions on x86.
> > > >
> > > > Paul
> > > >
> > > > # ------------------------ >8 ------------------------
> > > > Add support for custom address spaces in C++
> > > >
> > > > gcc/
> > > > * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
> > > >
> > > > gcc/c/
> > > > * c-decl.cc: Remove c_register_addr_space.
> > > >
> > > > gcc/c-family/
> > > > * c-common.cc (c_register_addr_space): Imported from c-decl.cc
> > > > (addr_space_superset): Imported from gcc/c/c-typecheck.cc
> > > > * c-common.h: Remove the FIXME.
> > > > (addr_space_superset): New declaration.
> > > >
> > > > gcc/cp/
> > > > * cp-tree.h (enum cp_decl_spec): Add addr_space support.
> > > > (struct cp_decl_specifier_seq): Likewise.
> > > > * decl.cc (get_type_quals): Likewise.
> > > > (check_tag_decl): Likewise.
> > > > (grokdeclarator): Likewise.
> > > > * parser.cc (cp_parser_type_specifier): Likewise.
> > > > (cp_parser_cv_qualifier_seq_opt): Likewise.
> > > > (cp_parser_postfix_expression): Likewise.
> > > > (cp_parser_type_specifier): Likewise.
> > > > (set_and_check_decl_spec_loc): Likewise.
> > > > * typeck.cc (composite_pointer_type): Likewise
> > > > (comp_ptr_ttypes_real): Likewise.
> > > > (same_type_ignoring_top_level_qualifiers_p): Likewise.
> > > > * pt.cc (check_cv_quals_for_unify): Likewise.
> > > > (unify): Likewise.
> > > > * tree.cc: Remove c_register_addr_space stub.
> > > > * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
> > > > using the extended qualifier notation.
> > > >
> > > > gcc/doc
> > > > * extend.texi (Named Address Spaces): add a mention about C++
> > > > support.
> > > >
> > > > gcc/testsuite/
> > > > * g++.dg/abi/mangle-addr-space1.C: New test.
> > > > * g++.dg/abi/mangle-addr-space2.C: New test.
> > > > * g++.dg/parse/addr-space.C: New test.
> > > > * g++.dg/parse/addr-space1.C: New test.
> > > > * g++.dg/parse/addr-space2.C: New test.
> > > > * g++.dg/parse/template/spec-addr-space.C: New test.
> > > > * g++.dg/ext/addr-space-decl.C: New test.
> > > > * g++.dg/ext/addr-space-ref.C: New test.
> > > > * g++.dg/ext/addr-space-ops.C: New test.
> > > > * g++.dg/template/addr-space-overload.C: New test.
> > > > * g++.dg/template/addr-space-strip1.C: New test.
> > > > * g++.dg/template/addr-space-strip2.C: New test.
> > > >
> > > > # ------------------------ >8 ------------------------
> > > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> > > > index 9ec9100cc90..3b79dc47515 100644
> > > > --- a/gcc/c-family/c-common.cc
> > > > +++ b/gcc/c-family/c-common.cc
> > > > @@ -588,6 +588,33 @@ c_addr_space_name (addr_space_t as)
> > > > return IDENTIFIER_POINTER (ridpointers [rid]);
> > > > }
> > > > +/* Return true if between two named address spaces, whether there is a superset
> > > > + named address space that encompasses both address spaces. If there is a
> > > > + superset, return which address space is the superset. */
> > > > +
> > > > +bool
> > > > +addr_space_superset (addr_space_t as1, addr_space_t as2,
> > > > + addr_space_t * common)
> > > > +{
> > > > + if (as1 == as2)
> > > > + {
> > > > + *common = as1;
> > > > + return true;
> > > > + }
> > > > + else if (targetm.addr_space.subset_p (as1, as2))
> > > > + {
> > > > + *common = as2;
> > > > + return true;
> > > > + }
> > > > + else if (targetm.addr_space.subset_p (as2, as1))
> > > > + {
> > > > + *common = as1;
> > > > + return true;
> > > > + }
> > > > + else
> > > > + return false;
> > > > +}
> > > > +
> > > > /* Push current bindings for the function name VAR_DECLS. */
> > > > void
> > > > @@ -2785,6 +2812,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> > > > return build_nonstandard_integer_type (width, unsignedp);
> > > > }
> > > > +/* Register reserved keyword WORD as qualifier for address space AS. */
> > > > +
> > > > +void
> > > > +c_register_addr_space (const char *word, addr_space_t as)
> > > > +{
> > > > + int rid = RID_FIRST_ADDR_SPACE + as;
> > > > + tree id;
> > > > +
> > > > + /* Address space qualifiers are only supported
> > > > + in C with GNU extensions enabled. */
> > > > + if (c_dialect_objc () || flag_no_asm)
> > > > + return;
> > > > +
> > > > + id = get_identifier (word);
> > > > + C_SET_RID_CODE (id, rid);
> > > > + TREE_LANG_FLAG_0 (id) = 1;
> > > > + ridpointers[rid] = id;
> > > > +}
> > > > +
> > > > /* The C version of the register_builtin_type langhook. */
> > > > void
> > > > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> > > > index 62ab4ba437b..a3864d874aa 100644
> > > > --- a/gcc/c-family/c-common.h
> > > > +++ b/gcc/c-family/c-common.h
> > > > @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
> > > > extern tree (*make_fname_decl) (location_t, tree, int);
> > > > -/* In c-decl.cc and cp/tree.cc. FIXME. */
> > > > -extern void c_register_addr_space (const char *str, addr_space_t as);
> > > > -
> > > > /* In c-common.cc. */
> > > > extern bool in_late_binary_op;
> > > > extern const char *c_addr_space_name (addr_space_t as);
> > > > +extern const char *c_addr_space_name (addr_space_t as);
> > > > +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> > > > extern tree identifier_global_value (tree);
> > > > extern tree identifier_global_tag (tree);
> > > > extern bool names_builtin_p (const char *);
> > > > @@ -951,6 +950,7 @@ extern bool c_common_init (void);
> > > > extern void c_common_finish (void);
> > > > extern void c_common_parse_file (void);
> > > > extern alias_set_type c_common_get_alias_set (tree);
> > > > +extern void c_register_addr_space (const char *, addr_space_t);
> > > > extern void c_register_builtin_type (tree, const char*);
> > > > extern bool c_promoting_integer_type_p (const_tree);
> > > > extern bool self_promoting_args_p (const_tree);
> > > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> > > > index a7571cc7542..b1f69997ff7 100644
> > > > --- a/gcc/c/c-decl.cc
> > > > +++ b/gcc/c/c-decl.cc
> > > > @@ -12531,25 +12531,6 @@ c_parse_final_cleanups (void)
> > > > ext_block = NULL;
> > > > }
> > > > -/* Register reserved keyword WORD as qualifier for address space AS. */
> > > > -
> > > > -void
> > > > -c_register_addr_space (const char *word, addr_space_t as)
> > > > -{
> > > > - int rid = RID_FIRST_ADDR_SPACE + as;
> > > > - tree id;
> > > > -
> > > > - /* Address space qualifiers are only supported
> > > > - in C with GNU extensions enabled. */
> > > > - if (c_dialect_objc () || flag_no_asm)
> > > > - return;
> > > > -
> > > > - id = get_identifier (word);
> > > > - C_SET_RID_CODE (id, rid);
> > > > - C_IS_RESERVED_WORD (id) = 1;
> > > > - ridpointers [rid] = id;
> > > > -}
> > > > -
> > > > /* Return identifier to look up for omp declare reduction. */
> > > > tree
> > > > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> > > > index fdb96c28c51..2a700bbaff3 100644
> > > > --- a/gcc/c/c-typeck.cc
> > > > +++ b/gcc/c/c-typeck.cc
> > > > @@ -303,32 +303,6 @@ c_type_promotes_to (tree type)
> > > > return type;
> > > > }
> > > > -/* Return true if between two named address spaces, whether there is a superset
> > > > - named address space that encompasses both address spaces. If there is a
> > > > - superset, return which address space is the superset. */
> > > > -
> > > > -static bool
> > > > -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> > > > -{
> > > > - if (as1 == as2)
> > > > - {
> > > > - *common = as1;
> > > > - return true;
> > > > - }
> > > > - else if (targetm.addr_space.subset_p (as1, as2))
> > > > - {
> > > > - *common = as2;
> > > > - return true;
> > > > - }
> > > > - else if (targetm.addr_space.subset_p (as2, as1))
> > > > - {
> > > > - *common = as1;
> > > > - return true;
> > > > - }
> > > > - else
> > > > - return false;
> > > > -}
> > > > -
> > > > /* Return a variant of TYPE which has all the type qualifiers of LIKE
> > > > as well as those of TYPE. */
> > > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > > > index e2607f09c19..0248569a95b 100644
> > > > --- a/gcc/cp/cp-tree.h
> > > > +++ b/gcc/cp/cp-tree.h
> > > > @@ -6235,6 +6235,7 @@ enum cp_decl_spec {
> > > > ds_const,
> > > > ds_volatile,
> > > > ds_restrict,
> > > > + ds_addr_space,
> > > > ds_inline,
> > > > ds_virtual,
> > > > ds_explicit,
> > > > @@ -6281,6 +6282,8 @@ struct cp_decl_specifier_seq {
> > > > cp_storage_class storage_class;
> > > > /* For the __intN declspec, this stores the index into the int_n_* arrays. */
> > > > int int_n_idx;
> > > > + /* The address space that the declaration belongs to. */
> > > > + addr_space_t address_space;
> > > > /* True iff TYPE_SPEC defines a class or enum. */
> > > > BOOL_BITFIELD type_definition_p : 1;
> > > > /* True iff multiple types were (erroneously) specified for this
> > > > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> > > > index 85b892cddf0..a87fed04529 100644
> > > > --- a/gcc/cp/decl.cc
> > > > +++ b/gcc/cp/decl.cc
> > > > @@ -5290,6 +5290,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
> > > > type_quals |= TYPE_QUAL_VOLATILE;
> > > > if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
> > > > type_quals |= TYPE_QUAL_RESTRICT;
> > > > + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > > > + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
> > > > return type_quals;
> > > > }
> > > > @@ -5412,6 +5414,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
> > > > error_at (declspecs->locations[ds_restrict],
> > > > "%<__restrict%> can only be specified for objects and "
> > > > "functions");
> > > > + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
> > > > + error_at (declspecs->locations[ds_addr_space],
> > > > + "address space can only be specified for objects and "
> > > > + "functions");
> > > > else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
> > > > error_at (declspecs->locations[ds_thread],
> > > > "%<__thread%> can only be specified for objects "
> > > > @@ -14608,6 +14614,59 @@ grokdeclarator (const cp_declarator *declarator,
> > > > if (!processing_template_decl)
> > > > cp_apply_type_quals_to_decl (type_quals, decl);
> > > > + /* Warn about address space used for things other than static memory or
> > > > + pointers. */
> > > > + addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
> > > > + if (!ADDR_SPACE_GENERIC_P (address_space))
> > > > + {
> > > > + if (decl_context == NORMAL)
> > > > + {
> > > > + switch (storage_class)
> > >
> > > I would still suggest checking decl_storage_duration at this point rather
> > > than the storage_class specifier.
> >
> > Unless I misunderstand something, I can't weed out register variables
> > if I rely on decl_storage_duration.
>
> Yes, but register variables are automatic, so they'll get that error; I
> don't think they need their own specific error.
>
Noted.
> > > > + {
> > > > + case sc_auto:
> > > > + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + break;
> > > > + case sc_register:
> > > > + error ("%qs combined with %<register%> qualifier for %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + break;
> > > > + case sc_none:
> > > > + if (! toplevel_bindings_p ())
> > > > + error ("%qs specified for auto variable %qs",
> > >
> > > And let's refer to automatic storage duration rather than shorten to 'auto'.
> > >
> > Right.
> > > > + c_addr_space_name (address_space), name);
> > > > + break;
> > > > + case sc_mutable:
> > > > + error ("%qs combined with %<mutable%> qualifier for %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + break;
> > > > + case sc_static:
> > > > + case sc_extern:
> > > > + break;
> > > > + default:
> > > > + gcc_unreachable ();
> > > > + }
> > > > + }
> > > > + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
> > > > + {
> > > > + if (name)
> > > > + error ("%qs specified for parameter %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + else
> > > > + error ("%qs specified for unnamed parameter",
> > > > + c_addr_space_name (address_space));
> > > > + }
> > > > + else if (decl_context == FIELD)
> > > > + {
> > > > + if (name)
> > > > + error ("%qs specified for structure field %qs",
> > > > + c_addr_space_name (address_space), name);
> > > > + else
> > > > + error ("%qs specified for structure field",
> > > > + c_addr_space_name (address_space));
> > > > + }
> > > > + }
> > > > +
> > > > return decl;
> > > > }
> > > > }
> > > > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> > > > index 1215463089b..aafff98f05a 100644
> > > > --- a/gcc/cp/mangle.cc
> > > > +++ b/gcc/cp/mangle.cc
> > > > @@ -2520,6 +2520,14 @@ write_CV_qualifiers_for_type (const tree type)
> > > > array. */
> > > > cp_cv_quals quals = TYPE_QUALS (type);
> > > > + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
> > > > + {
> > > > + const char *as_name = c_addr_space_name (as);
> > > > + write_char ('U');
> > > > + write_unsigned_number (strlen (as_name));
> > > > + write_string (as_name);
> > > > + ++num_qualifiers;
> > > > + }
> > > > if (quals & TYPE_QUAL_RESTRICT)
> > > > {
> > > > write_char ('r');
> > > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > > > index 9ddfb027ff9..c82059d1efd 100644
> > > > --- a/gcc/cp/parser.cc
> > > > +++ b/gcc/cp/parser.cc
> > > > @@ -7703,6 +7703,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
> > > > postfix_expression = error_mark_node;
> > > > break;
> > > > }
> > > > + if (type != error_mark_node
> > > > + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
> > > > + && current_function_decl)
> > > > + {
> > > > + error
> > > > + ("compound literal qualified by address-space "
> > > > + "qualifier");
> > > > + type = error_mark_node;
> > > > + }
> > > > /* Form the representation of the compound-literal. */
> > > > postfix_expression
> > > > = finish_compound_literal (type, initializer,
> > > > @@ -19445,6 +19454,15 @@ cp_parser_type_specifier (cp_parser* parser,
> > > > break;
> > > > }
> > > > +
> > > > + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
> > > > + {
> > > > + ds = ds_addr_space;
> > > > + if (is_cv_qualifier)
> > > > + *is_cv_qualifier = true;
> > > > + }
> > > > +
> > > > +
> > >
> > > I don't think we need two blank lines before and after this block, one each
> > > should be enough.
> > >
> > Indeed.
> > > > /* Handle simple keywords. */
> > > > if (ds != ds_last)
> > > > {
> > > > @@ -23837,6 +23855,7 @@ cp_parser_ptr_operator (cp_parser* parser,
> > > > GNU Extension:
> > > > cv-qualifier:
> > > > + address-space-qualifier
> > > > __restrict__
> > > > Returns a bitmask representing the cv-qualifiers. */
> > > > @@ -23873,6 +23892,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
> > > > break;
> > > > }
> > > > + if (RID_FIRST_ADDR_SPACE <= token->keyword
> > > > + && token->keyword <= RID_LAST_ADDR_SPACE)
> > > > + cv_qualifier
> > > > + = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
> > > > +
> > > > if (!cv_qualifier)
> > > > break;
> > > > @@ -32893,6 +32917,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > > > decl_specs->locations[ds] = location;
> > > > if (ds == ds_thread)
> > > > decl_specs->gnu_thread_keyword_p = token_is__thread (token);
> > > > + else if (ds == ds_addr_space)
> > > > + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
> > > > }
> > > > else
> > > > {
> > > > @@ -32925,6 +32951,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
> > > > error_at (&richloc, "duplicate %qD", token->u.value);
> > > > }
> > > > }
> > > > + else if (ds == ds_addr_space)
> > > > + {
> > > > + addr_space_t as1 = decl_specs->address_space;
> > > > + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
> > > > +
> > > > + gcc_rich_location richloc (location);
> > > > + richloc.add_fixit_remove ();
> > > > + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
> > > > + && as1 != as2)
> > > > + error_at (&richloc,
> > > > + "conflicting named address spaces (%s vs %s)",
> > > > + c_addr_space_name (as1), c_addr_space_name (as2));
> > > > + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
> > > > + error_at (&richloc,
> > > > + "duplicate named address space %s",
> > > > + c_addr_space_name (as1));
> > > > +
> > > > + decl_specs->address_space = as2;
> > > > + }
> > > > else
> > > > {
> > > > static const char *const decl_spec_names[] = {
> > > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > > > index e4dca9d4f9d..7b73a57091e 100644
> > > > --- a/gcc/cp/pt.cc
> > > > +++ b/gcc/cp/pt.cc
> > > > @@ -23778,8 +23778,19 @@ template_decl_level (tree decl)
> > > > static int
> > > > check_cv_quals_for_unify (int strict, tree arg, tree parm)
> > > > {
> > > > - int arg_quals = cp_type_quals (arg);
> > > > - int parm_quals = cp_type_quals (parm);
> > > > + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > > > + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > > > +
> > > > + /* Try to unify ARG's address space into PARM's address space.
> > > > + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> > > > + there are no constraints on address spaces for this type. */
> > > > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> > > > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> > > > + addr_space_t as_common;
> > > > + addr_space_superset (as_arg, as_parm, &as_common);
> > > > +
> > > > + if (!(as_parm == as_common || as_parm == 0))
> > > > + return 0;
> > >
> > > I'd expect address space qualifiers to follow the 'strict' parameter like
> > > the other qualifiers; the above test seems to assume
> > > UNIFY_ALLOW_{OUTER_,}LESS_CV_QUAL.
> > >
> > The reason I ignored strict was to enforce that the deduced address
> > space is always at most "as_parm" unless "as_parm" is the generic address
> > space, and prevent unifying if the two address spaces are disjoint
> > unless "parm" does not have any address space constraints; and avoid the
> > addition/deletion of an address space to "arg" during the unifying
> > process.
> >
> > Since I don't really understand the whole picture behind strict, and when
> > check_cv_quals_for_unify gets called with which variant of restrict it
> > might be me who tried to be overcareful when unifying the address
> > spaces.
>
> How we need to handle differing qualifiers varies between different template
> argument deduction contexts.
>
> The code you wrote above is correct for the function call context, since
> https://eel.is/c++draft/temp.deduct.call#4.2 says the deduced type can be
> convertable by qualification conversion, i.e. parm more qualified than arg
> (and my "LESS" above was backwards). This is a bit different for address
> space qualifiers given that the qualification conversion would be removing
> the address space qualifier or changing it to a more general one, but the
> principle is the same.
>
> But the allowance for qualifier changes doesn't apply to all deduction
> contexts: for instance,
>
> template <class T> void f(T * const *);
> struct A {
> template <class T> operator T**();
> };
> int main()
> {
> f((void**)0); // void** -> void*const* is a valid qualification conv
> (void *const*)A(); // same conversion
> void (*p)(void **) = f; // error, type mismatch
> }
>
> so similarly,
>
> template <class T> void f(T **);
> struct A {
> template <class T> operator T*__seg_fs*();
> };
> int main()
> {
> f((void* __seg_fs *)0); // void*__seg_fs* -> void** should be OK
> void (*p)(void * __seg_fs *) = f; // error
> }
>
>
I do not completely agree here. Currently, my implementation rejects
all deductions which would change or remove an address space no matter
the context, which is very conservative.
I tried using "strict" as the other qualifiers do, and as I expected,
it keeps rejecting
f((void* __seg_fs *)0); // void*__seg_fs* -> void** should be OK
which is to be expected, since a pointer can't jump from an address
space to another unless there is a common superset and here __seg_fs
is disjoint from the generic address space.
I don't really understand what is done in
(void **)A(); // same conversion
but it is similarly rejected (implicit conversion from A to (void**))
The third one is strangely accepted, and clang accept is as well (only
the address space variant, the one with const is duly rejected).
I will investigate what clang does here, as I think it would be better
if the behavior of clang and gcc concerning this feature matches as
much as possible from a user standpoint, since the C++ side of this
feature is, to my knowledge, completely undocumented.
> > > > if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> > > > && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> > > > @@ -24415,10 +24426,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> > > > arg, parm))
> > > > return unify_cv_qual_mismatch (explain_p, parm, arg);
> > > > + int arg_cv_quals = cp_type_quals (arg);
> > > > + int parm_cv_quals = cp_type_quals (parm);
> > > > +
> > > > + /* If PARM does not contain any address spaces constraints it can
> > > > + fully match the address space of ARG. However, if PARM contains an
> > > > + address space constraints, it becomes the upper bound. That is,
> > > > + AS_ARG may be promoted to AS_PARM but not the converse. If we
> > > > + ended up here, it means that `check_cv_quals_for_unify' succeeded
> > > > + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> > > > + AS_PARM. */
> > > > + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> > > > + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> > > > + addr_space_t as_common = as_parm ? 0 : as_arg;
> > >
> > > Hmm, I'd think we also want as_common = as_arg when it's a subset of
> > > as_parm.
> >
> > Let's assume that "PARM" is "__as1 T", and since the call to
> > check_cv_quals_for_unify succeeded we know that "as_common" is
> > "__as1". That is ARG is of the form "__as2 U" with "__as2" a
> > subset of "__as1", hence we are trying to unify
> > __as1 T = __as1 U
> > which does not give any constraints over PARM since it alreay contains
> > the common address space, hence there is no more constraints on T and
> > as_common = 0.
>
> Agreed.
>
> > However, if PARM's address space is 0, we are trying to unify
> > T = __as1 U
> > and we need to add __addr_space1 to the constraints of T.
>
> Agreed.
>
> > If as_parm is not the generic address space (ie, as_parm != 0)
>
> Looks like this comment got cut off? This is the case I was talking about.
> When we are trying to unify
>
> __as1 T = __as2 U
>
> and __as2 is a subset of __as1, I think we want T to be deduced to __as2 U,
> and then substitution will need to handle substituting __as2 U for T into
> __as1 T to get __as2 U.
>
I more or less agree, but I think that the substitution will need to
handle substituting __as2 U for T into __as1 T to get __as1 U.
(Leading to __as1 U and not __as2 U, since __as1 is the biggest
address space and the templated function expect __as1 T).
Nevertheless, this means that when fully deduced __as1 T becomes __as1
__as2 U and then the substitution mechanism would make it into __as1
U. Could you please tell me where the substitution mechanism takes
place so that I can account for this case and that if we end up with
two compatible address spaces the biggest is selected?
> > > > /* Consider the case where ARG is `const volatile int' and
> > > > PARM is `const T'. Then, T should be `volatile int'. */
> > > > arg = cp_build_qualified_type
> > > > (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
> > > > + int unified_cv =
> > > > + (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> > > > + | ENCODE_QUAL_ADDR_SPACE (as_common));
> > > > + arg = cp_build_qualified_type (arg, unified_cv, tf_none);
> > > > if (arg == error_mark_node)
> > > > return unify_invalid (explain_p);
> > > > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> > > > index 45348c58bb6..1f330ca93ed 100644
> > > > --- a/gcc/cp/tree.cc
> > > > +++ b/gcc/cp/tree.cc
> > > > @@ -6072,15 +6072,6 @@ cp_free_lang_data (tree t)
> > > > DECL_CHAIN (t) = NULL_TREE;
> > > > }
> > > > -/* Stub for c-common. Please keep in sync with c-decl.cc.
> > > > - FIXME: If address space support is target specific, then this
> > > > - should be a C target hook. But currently this is not possible,
> > > > - because this function is called via REGISTER_TARGET_PRAGMAS. */
> > > > -void
> > > > -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> > > > -{
> > > > -}
> > > > -
> > > > /* Return the number of operands in T that we care about for things like
> > > > mangling. */
> > > > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> > > > index da0e1427b97..93cfdc70e2d 100644
> > > > --- a/gcc/cp/typeck.cc
> > > > +++ b/gcc/cp/typeck.cc
> > > > @@ -803,10 +803,28 @@ composite_pointer_type (const op_location_t &location,
> > > > else
> > > > return error_mark_node;
> > > > }
> > > > + /* If possible merge the address space into the superset of the address
> > > > + spaces of t1 and t2, or raise an error. */
> > > > + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
> > > > + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
> > > > + addr_space_t as_common;
> > > > +
> > > > + /* If the two named address spaces are different, determine the common
> > > > + superset address space. If there isn't one, raise an error. */
> > > > + if (!addr_space_superset (as_t1, as_t2, &as_common))
> > > > + {
> > > > + as_common = as_t1;
> > > > + error_at (location,
> > > > + "%qT and %qT are in disjoint named address spaces",
> > > > + t1, t2);
> > >
> > > Why not return error_mark_node here?
> > >
> > That's a mistake. Thanks.
> > > > + }
> > > > + int quals_t1 = cp_type_quals (TREE_TYPE (t1));
> > > > + int quals_t2 = cp_type_quals (TREE_TYPE (t2));
> > > > result_type
> > > > = cp_build_qualified_type (void_type_node,
> > > > - (cp_type_quals (TREE_TYPE (t1))
> > > > - | cp_type_quals (TREE_TYPE (t2))));
> > > > + (CLEAR_QUAL_ADDR_SPACE (quals_t1)
> > > > + | CLEAR_QUAL_ADDR_SPACE (quals_t2)
> > > > + | ENCODE_QUAL_ADDR_SPACE (as_common)));
> > > > result_type = build_pointer_type (result_type);
> > > > /* Merge the attributes. */
> > > > attributes = (*targetm.merge_type_attributes) (t1, t2);
> > > > @@ -1731,7 +1749,9 @@ comptypes (tree t1, tree t2, int strict)
> > > > }
> > > > /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
> > > > - top-level qualifiers. */
> > > > + top-level qualifiers, except for named address spaces. If the pointers point
> > > > + to different named addresses spaces, then we must determine if one address
> > > > + space is a subset of the other. */
> > > > bool
> > > > same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > > > @@ -1741,6 +1761,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
> > > > if (type1 == type2)
> > > > return true;
> > > > + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
> > > > + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
> > > > + addr_space_t as_common;
> > > > +
> > > > + /* Fail if pointers point to incompatible address spaces. */
> > > > + if (!addr_space_superset (as_type1, as_type2, &as_common))
> > > > + return false;
> > >
> > > Why do you need this change? I'd expect this function to ignore top level
> > > address space qualifiers like the other qualifiers.
> >
> > I am mirroring the C front-end here, which does the same thing in
> > "comp_target_types" (gcc/c/c-typeck.cc), which ignores qualifiers but
> > not address spaces when checking if two pointer types are equivalent.
>
> This function serves a very different function from comp_target_types, which
> deals with the types that pointers point to; this function is ignoring
> top-level qualifiers that should not affect the type.
>
> ...except now I see that cp_build_binary_op is wierdly using this function
> for pointer subtraction. I'd think it should use composite_pointer_type
> instead, like EQ_EXPR does.
I think this is because of https://eel.is/c++draft/expr.add#2.2 and I
am not sure that composite_pointer_type can replace it here since it
does try to merge the two list of qualifiers.
>
> > > > type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
> > > > type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
> > > > return same_type_p (type1, type2);
> > > > @@ -6672,10 +6700,32 @@ static tree
> > > > pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
> > > > tsubst_flags_t complain, tree *instrument_expr)
> > > > {
> > > > - tree result, inttype;
> > > > tree restype = ptrdiff_type_node;
> > > > + tree result, inttype;
> > > > +
> > > > + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
> > > > + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
> > > > tree target_type = TREE_TYPE (ptrtype);
> > > > + /* If the operands point into different address spaces, we need to
> > > > + explicitly convert them to pointers into the common address space
> > > > + before we can subtract the numerical address values. */
> > > > + if (as0 != as1)
> > > > + {
> > > > + addr_space_t as_common;
> > > > + tree common_type;
> > > > +
> > > > + /* Determine the common superset address space. This is guaranteed
> > > > + to exist because the caller verified that comp_target_types
> > > > + returned non-zero. */
> > > > + if (!addr_space_superset (as0, as1, &as_common))
> > > > + gcc_unreachable ();
> > > > +
> > > > + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
> > > > + op0 = convert (common_type, op0);
> > > > + op1 = convert (common_type, op1);
> > > > + }
> > >
> > > I think you shouldn't need to change pointer_diff if composite_pointer_type
> > > returns error_mark_node above.
> >
> > I'll have a look, the idea here is to prevent "a - b" with "a" and "b"
> > from different address spaces.
>
> As above, I think this should have been handled in cp_build_binary_op.
I don't really understand why you don't want the address space
conversion (which might be needed for subtraction) to happen at the same
time as the conversion to the "common_pointer_type".
>
> > > > if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
> > > > return error_mark_node;
> > > > @@ -11286,6 +11336,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> > > > to_more_cv_qualified = true;
> > > > }
> > > > + /* Warn about conversions between pointers to disjoint
> > > > + address spaces. */
> > > > + if (TREE_CODE (from) == POINTER_TYPE
> > > > + && TREE_CODE (to) == POINTER_TYPE)
> > > > + {
> > > > + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> > > > + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> > > > + addr_space_t as_common;
> > > > +
> > > > + if (!addr_space_superset (as_to, as_from, &as_common))
> > > > + return false;
> > >
> > > I think you also want to check that as_common == as_to here?
> > >
> > Yes.
> > > > + }
> > > > +
> > > > if (constp > 0)
> > > > constp &= TYPE_READONLY (to);
> > > > }
> > > > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > > > index cfbe32afce9..ef75f6b83a2 100644
> > > > --- a/gcc/doc/extend.texi
> > > > +++ b/gcc/doc/extend.texi
> > > > @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
> > > > @section Named Address Spaces
> > > > @cindex Named Address Spaces
> > > > -As an extension, GNU C supports named address spaces as
> > > > +As an extension, GNU C and GNU C++ support named address spaces as
> > > > defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
> > > > address spaces in GCC will evolve as the draft technical report
> > > > changes. Calling conventions for any target might also change. At
> > > > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > > > new file mode 100644
> > > > index 00000000000..c01f8d6054a
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
> > > > @@ -0,0 +1,10 @@
> > > > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > >
> > > This can be dg-do compile, I don't think you get anything from running an
> > > empty main.
> > >
> > Yes.
> > > > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> > >
> > > And then you don't need -save-temps. What are the other options for?
> > >
> > I forgot to remove -Wabi and -fabi-version, this was from my first
> > attempt when I used AS<number> to mangle which changed the ABI. I'll
> > remove them.
> > > > +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
> > > > +
> > > > +int f (int volatile __seg_fs *a)
> > > > +{
> > > > + return *a;
> > > > +}
> > > > +
> > > > +int main () {}
> > > > diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > > > new file mode 100644
> > > > index 00000000000..862bbbdcdf2
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
> > > > @@ -0,0 +1,9 @@
> > > > +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
> > > > +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
> > >
> > > Also not clear that running is important for this test.
> > >
> > Noted.
> > > > +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
> > > > +
> > > > +template <class T>
> > > > +int f (T *p) { return *p; }
> > > > +int g (__seg_fs int *p) { return *p; }
> > > > +__seg_fs int *a;
> > > > +int main() { f(a); }
> > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > > > new file mode 100644
> > > > index 00000000000..c04d2f497da
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
> > > > @@ -0,0 +1,5 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +__seg_fs char a, b, c;
> > > > +__seg_fs const int *p;
> > > > +static /* give internal linkage to the following anonymous struct */
> > >
> > > Hmm, this 'static' gives internal linkage to the variable q, not the type.
> > > What do you want it for?
> > >
> > Yes, the idea is to give internal linkage to q, otherwise g++
> > complains in -std=c++98 mode because q is externally visible but it
> > can't be reffered from anywhere else since there is no tag for this
> > structure.
>
> Then let's change the comment to /* give internal linkage to q */
Agreed.
>
> > > > +__seg_fs struct { int a; char b; } * __seg_gs q;
> > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > > > new file mode 100644
> > > > index 00000000000..86c02d1e7f5
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
> > > > @@ -0,0 +1,20 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +int __seg_fs * fs1;
> > > > +int __seg_fs * fs2;
> > > > +float __seg_gs * gs1;
> > > > +float __seg_gs * gs2;
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
> > > > + fs1 - fs2;
> > > > + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
> > > > + fs1 == fs2;
> > > > + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > > > + fs1 = fs2;
> > > > + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
> > > > + fs1 > fs2;
> > > > + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > > > new file mode 100644
> > > > index 00000000000..12d7975e560
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
> > > > @@ -0,0 +1,31 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +// { dg-prune-output "does not allow .register. storage class specifier" }
> > > > +int __seg_fs * outer_b;
> > > > +
> > > > +struct s {
> > > > + __seg_fs int * ok;
> > > > + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
> > > > +};
> > > > +
> > > > +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
> > > > +
> > > > +namespace ns_a
> > > > +{
> > > > + int __seg_fs * inner_b;
> > > > +
> > > > + template<typename T>
> > > > + int f (T &a) { return a; }
> > > > + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
> > > > + int h (__seg_fs int *a) { return *a; }
> > > > +}
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
> > > > + static __seg_gs int static_gs;
> > > > + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
> > > > + __seg_fs int *pa = outer_b;
> > > > + __seg_fs int& ra = *ns_a::inner_b;
> > > > + return ns_a::f(ra) + ns_a::f(*pa);
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
> > > > new file mode 100644
> > > > index 00000000000..ebb6316054a
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
> > > > @@ -0,0 +1,9 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +
> > > > +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > > > new file mode 100644
> > > > index 00000000000..2e8ee32a885
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
> > > > @@ -0,0 +1,10 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +// { dg-options "-std=gnu++98" }
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + struct foo {int a; char b[2];} structure;
> > > > + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > > > new file mode 100644
> > > > index 00000000000..5b2c0f28078
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
> > > > @@ -0,0 +1,9 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +
> > > > +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
> > > > new file mode 100644
> > > > index 00000000000..70dfcce53fa
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
> > > > @@ -0,0 +1,17 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +
> > > > +int __seg_fs * fs1;
> > > > +int __seg_gs * gs1;
> > > > +
> > > > +template<typename T, typename U>
> > > > +__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
> > > > +template<typename T, typename U>
> > > > +__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + f (fs1, gs1);
> > > > + f (gs1, fs1);
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
> > > > new file mode 100644
> > > > index 00000000000..5df115db939
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
> > > > @@ -0,0 +1,17 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +// { dg-skip-if "" { *-*-* } { "-std=c++98" "-std=c++03" "-std=gnu++98" "-std=gnu++03" } { "" } }
> > >
> > > This can be { dg-require-effective-target c++11 }
>
> Or put the x86 requirement in dg-require-effective-target, and put c++11 in
> the dg-do target spec, either way.
Agreed.
>
> > > > +// decltype is ony available since c++11
> > >
> > > "only"
> > >
> > > > +
> > > > +int __seg_fs * fs1;
> > > > +int __seg_gs * gs1;
> > > > +
> > > > +template<typename T> struct strip;
> > > > +template<typename T> struct strip<__seg_fs T *> { typedef T type; };
> > > > +template<typename T> struct strip<__seg_gs T *> { typedef T type; };
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
> > > > + return 0;
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
> > > > new file mode 100644
> > > > index 00000000000..526bbaa56b7
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
> > > > @@ -0,0 +1,16 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +
> > > > +int __seg_fs * fs1;
> > > > +int __seg_gs * gs1;
> > > > +
> > > > +template<typename T, typename U>
> > > > +bool f (T __seg_fs * a, U __seg_gs * b)
> > > > +{
> > > > + return *(T *) a == *(U *) b;
> > > > +}
> > > > +
> > > > +int
> > > > +main ()
> > > > +{
> > > > + return f (fs1, gs1);
> > > > +}
> > > > diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > > > new file mode 100644
> > > > index 00000000000..ae9f4de0e1f
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
> > > > @@ -0,0 +1,8 @@
> > > > +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
> > > > +
> > > > +template <class T>
> > > > +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> > > > + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> > > > +__seg_fs int *a;
> > > > +int main() { f(a); } // { dg-error "no matching" }
> > > > +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> > > > diff --git a/gcc/tree.h b/gcc/tree.h
> > > > index 9af971cf401..4aebfef854b 100644
> > > > --- a/gcc/tree.h
> > > > +++ b/gcc/tree.h
> > > > @@ -2292,7 +2292,7 @@ extern tree vector_element_bits_tree (const_tree);
> > > > /* Encode/decode the named memory support as part of the qualifier. If more
> > > > than 8 qualifiers are added, these macros need to be adjusted. */
> > > > -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
> > > > +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
> > > > #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
> > > > /* Return all qualifiers except for the address space qualifiers. */
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-10-26 7:18 ` Paul Iannetta
@ 2022-10-26 16:28 ` Jason Merrill
2022-11-10 15:42 ` [PATCH v4] " Paul Iannetta
2022-11-03 13:38 ` [PATCH v3] " Georg-Johann Lay
1 sibling, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2022-10-26 16:28 UTC (permalink / raw)
To: Paul Iannetta; +Cc: gcc-patches
On 10/26/22 03:18, Paul Iannetta wrote:
> On Wed, Oct 19, 2022 at 02:55:21PM -0400, Jason Merrill wrote:
>> On 10/18/22 13:01, Paul Iannetta wrote:
>>> Thank you very much for the detailed review.
>>>
>>> On Tue, Oct 18, 2022 at 10:24:23AM -0400, Jason Merrill wrote:
>>>> On 10/18/22 03:37, Paul Iannetta wrote:
>>>>> On Fri, Oct 14, 2022 at 11:19:50AM -0400, Jason Merrill wrote:
>>>>>> On 10/13/22 17:57, Paul Iannetta wrote:
>>>>>>> On Thu, Oct 13, 2022 at 03:41:16PM -0400, Jason Merrill wrote:
>>>>>>>> On 10/13/22 12:02, Paul Iannetta wrote:
>>>>>>>>> On Thu, Oct 13, 2022 at 11:47:42AM -0400, Jason Merrill wrote:
>>>>>>>>>> On 10/13/22 11:23, Paul Iannetta wrote:
>>>>>>>>>>> On Thu, Oct 13, 2022 at 11:02:24AM -0400, Jason Merrill wrote:
>>>>>>>>>>>> On 10/12/22 20:52, Paul Iannetta wrote:
>>>>>>>>>>>>> There are quite a few things I would like to clarify concerning some
>>>>>>>>>>>>> implementation details.
>>>>>>>>>>>>> - A variable with automatic storage (which is neither a pointer nor
>>>>>>>>>>>>> a reference) cannot be qualified with an address space. I detect
>>>>>>>>>>>>> this by the combination of `sc_none' and `! toplevel_bindings_p ()',
>>>>>>>>>>>>> but I've also seen the use of `at_function_scope' at other places.
>>>>>>>>>>>>> And I'm unsure which one is appropriate here.
>>>>>>>>>>>>> This detection happens at the very end of grokdeclarator because I
>>>>>>>>>>>>> need to know that the type is a pointer, which is not know until
>>>>>>>>>>>>> very late in the function.
>>>>>>>>>>>>
>>>>>>>>>>>> At that point you have the decl, and you can ask directly what its storage
>>>>>>>>>>>> duration is, perhaps using decl_storage_duration.
>>>>>>>>>>>>
>>>>>>>>>>>> But why do you need to know whether the type is a pointer? The attribute
>>>>>>>>>>>> applies to the target type of the pointer, not the pointer type. I think
>>>>>>>>>>>> the problem is that you're looking at declspecs when you ought to be looking
>>>>>>>>>>>> at type_quals.
>>>>>>>>>>>
>>>>>>>>>>> I need to know that the base type is a pointer to reject invalid
>>>>>>>>>>> declarations such as:
>>>>>>>>>>>
>>>>>>>>>>> int f (__seg_fs int a) { } or int f () { __seg_fs int a; }
>>>>>>>>>>>
>>>>>>>>>>> because parameters and auto variables can have an address space
>>>>>>>>>>> qualifier only if they are pointer or reference type, which I can't
>>>>>>>>>>> tell only from type_quals.
>>>>>>>>>>
>>>>>>>>>> But "int *__seg_fs a" is just as invalid as the above; the difference is not
>>>>>>>>>> whether a is a pointer, but whether the address-space-qualified is the type
>>>>>>>>>> of a itself or some sub-type.
>>>>>>>>>
>>>>>>>>> I agree that "int * __seg_fs a" is invalid but it is accepted by the C
>>>>>>>>> front-end, and by clang (both C and C++), the behavior is that the
>>>>>>>>> address-name is silently ignored.
>>>>>>>>
>>>>>>>> Hmm, that sounds like a bug; in that case, presumably the user meant to
>>>>>>>> qualify the pointed-to type, and silently ignoring seems unlikely to give
>>>>>>>> the effect they want.
>>>>>>>>
>>>>>>>
>>>>>>> Well, actually, I'm re-reading the draft and "int * __seg_fs a" is
>>>>>>> valid. It means "pointer in address space __seg_fs pointing to an
>>>>>>> object in the generic address space", whereas "__seg_fs int * a" means
>>>>>>> "pointer in the generic address space pointing to an object in the
>>>>>>> __seg_fs address-space".
>>>>>>>
>>>>>>> Oddities such as, "__seg_fs int * __seg_gs a" are also perfectly
>>>>>>> valid.
>>>>>>
>>>>>> If a has static storage duration, sure; I was still thinking about
>>>>>> declarations with automatic storage duration such as in your example above.
>>>>>>
>>>>>
>>>>> Thanks, I only use type_quals now. I also took into account the style
>>>>> recommendations from Jakub, and included the other template tests.
>>>>> I rebased over trunk, bootstrapped the compiler and run the "make
>>>>> check-gcc" with no regressions on x86.
>>>>>
>>>>> Paul
>>>>>
>>>>> # ------------------------ >8 ------------------------
>>>>> Add support for custom address spaces in C++
>>>>>
>>>>> gcc/
>>>>> * tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
>>>>>
>>>>> gcc/c/
>>>>> * c-decl.cc: Remove c_register_addr_space.
>>>>>
>>>>> gcc/c-family/
>>>>> * c-common.cc (c_register_addr_space): Imported from c-decl.cc
>>>>> (addr_space_superset): Imported from gcc/c/c-typecheck.cc
>>>>> * c-common.h: Remove the FIXME.
>>>>> (addr_space_superset): New declaration.
>>>>>
>>>>> gcc/cp/
>>>>> * cp-tree.h (enum cp_decl_spec): Add addr_space support.
>>>>> (struct cp_decl_specifier_seq): Likewise.
>>>>> * decl.cc (get_type_quals): Likewise.
>>>>> (check_tag_decl): Likewise.
>>>>> (grokdeclarator): Likewise.
>>>>> * parser.cc (cp_parser_type_specifier): Likewise.
>>>>> (cp_parser_cv_qualifier_seq_opt): Likewise.
>>>>> (cp_parser_postfix_expression): Likewise.
>>>>> (cp_parser_type_specifier): Likewise.
>>>>> (set_and_check_decl_spec_loc): Likewise.
>>>>> * typeck.cc (composite_pointer_type): Likewise
>>>>> (comp_ptr_ttypes_real): Likewise.
>>>>> (same_type_ignoring_top_level_qualifiers_p): Likewise.
>>>>> * pt.cc (check_cv_quals_for_unify): Likewise.
>>>>> (unify): Likewise.
>>>>> * tree.cc: Remove c_register_addr_space stub.
>>>>> * mangle.cc (write_CV_qualifiers_for_type): Mangle address spaces
>>>>> using the extended qualifier notation.
>>>>>
>>>>> gcc/doc
>>>>> * extend.texi (Named Address Spaces): add a mention about C++
>>>>> support.
>>>>>
>>>>> gcc/testsuite/
>>>>> * g++.dg/abi/mangle-addr-space1.C: New test.
>>>>> * g++.dg/abi/mangle-addr-space2.C: New test.
>>>>> * g++.dg/parse/addr-space.C: New test.
>>>>> * g++.dg/parse/addr-space1.C: New test.
>>>>> * g++.dg/parse/addr-space2.C: New test.
>>>>> * g++.dg/parse/template/spec-addr-space.C: New test.
>>>>> * g++.dg/ext/addr-space-decl.C: New test.
>>>>> * g++.dg/ext/addr-space-ref.C: New test.
>>>>> * g++.dg/ext/addr-space-ops.C: New test.
>>>>> * g++.dg/template/addr-space-overload.C: New test.
>>>>> * g++.dg/template/addr-space-strip1.C: New test.
>>>>> * g++.dg/template/addr-space-strip2.C: New test.
>>>>>
>>>>> # ------------------------ >8 ------------------------
>>>>> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
>>>>> index 9ec9100cc90..3b79dc47515 100644
>>>>> --- a/gcc/c-family/c-common.cc
>>>>> +++ b/gcc/c-family/c-common.cc
>>>>> @@ -588,6 +588,33 @@ c_addr_space_name (addr_space_t as)
>>>>> return IDENTIFIER_POINTER (ridpointers [rid]);
>>>>> }
>>>>> +/* Return true if between two named address spaces, whether there is a superset
>>>>> + named address space that encompasses both address spaces. If there is a
>>>>> + superset, return which address space is the superset. */
>>>>> +
>>>>> +bool
>>>>> +addr_space_superset (addr_space_t as1, addr_space_t as2,
>>>>> + addr_space_t * common)
>>>>> +{
>>>>> + if (as1 == as2)
>>>>> + {
>>>>> + *common = as1;
>>>>> + return true;
>>>>> + }
>>>>> + else if (targetm.addr_space.subset_p (as1, as2))
>>>>> + {
>>>>> + *common = as2;
>>>>> + return true;
>>>>> + }
>>>>> + else if (targetm.addr_space.subset_p (as2, as1))
>>>>> + {
>>>>> + *common = as1;
>>>>> + return true;
>>>>> + }
>>>>> + else
>>>>> + return false;
>>>>> +}
>>>>> +
>>>>> /* Push current bindings for the function name VAR_DECLS. */
>>>>> void
>>>>> @@ -2785,6 +2812,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
>>>>> return build_nonstandard_integer_type (width, unsignedp);
>>>>> }
>>>>> +/* Register reserved keyword WORD as qualifier for address space AS. */
>>>>> +
>>>>> +void
>>>>> +c_register_addr_space (const char *word, addr_space_t as)
>>>>> +{
>>>>> + int rid = RID_FIRST_ADDR_SPACE + as;
>>>>> + tree id;
>>>>> +
>>>>> + /* Address space qualifiers are only supported
>>>>> + in C with GNU extensions enabled. */
>>>>> + if (c_dialect_objc () || flag_no_asm)
>>>>> + return;
>>>>> +
>>>>> + id = get_identifier (word);
>>>>> + C_SET_RID_CODE (id, rid);
>>>>> + TREE_LANG_FLAG_0 (id) = 1;
>>>>> + ridpointers[rid] = id;
>>>>> +}
>>>>> +
>>>>> /* The C version of the register_builtin_type langhook. */
>>>>> void
>>>>> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
>>>>> index 62ab4ba437b..a3864d874aa 100644
>>>>> --- a/gcc/c-family/c-common.h
>>>>> +++ b/gcc/c-family/c-common.h
>>>>> @@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
>>>>> extern tree (*make_fname_decl) (location_t, tree, int);
>>>>> -/* In c-decl.cc and cp/tree.cc. FIXME. */
>>>>> -extern void c_register_addr_space (const char *str, addr_space_t as);
>>>>> -
>>>>> /* In c-common.cc. */
>>>>> extern bool in_late_binary_op;
>>>>> extern const char *c_addr_space_name (addr_space_t as);
>>>>> +extern const char *c_addr_space_name (addr_space_t as);
>>>>> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
>>>>> extern tree identifier_global_value (tree);
>>>>> extern tree identifier_global_tag (tree);
>>>>> extern bool names_builtin_p (const char *);
>>>>> @@ -951,6 +950,7 @@ extern bool c_common_init (void);
>>>>> extern void c_common_finish (void);
>>>>> extern void c_common_parse_file (void);
>>>>> extern alias_set_type c_common_get_alias_set (tree);
>>>>> +extern void c_register_addr_space (const char *, addr_space_t);
>>>>> extern void c_register_builtin_type (tree, const char*);
>>>>> extern bool c_promoting_integer_type_p (const_tree);
>>>>> extern bool self_promoting_args_p (const_tree);
>>>>> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
>>>>> index a7571cc7542..b1f69997ff7 100644
>>>>> --- a/gcc/c/c-decl.cc
>>>>> +++ b/gcc/c/c-decl.cc
>>>>> @@ -12531,25 +12531,6 @@ c_parse_final_cleanups (void)
>>>>> ext_block = NULL;
>>>>> }
>>>>> -/* Register reserved keyword WORD as qualifier for address space AS. */
>>>>> -
>>>>> -void
>>>>> -c_register_addr_space (const char *word, addr_space_t as)
>>>>> -{
>>>>> - int rid = RID_FIRST_ADDR_SPACE + as;
>>>>> - tree id;
>>>>> -
>>>>> - /* Address space qualifiers are only supported
>>>>> - in C with GNU extensions enabled. */
>>>>> - if (c_dialect_objc () || flag_no_asm)
>>>>> - return;
>>>>> -
>>>>> - id = get_identifier (word);
>>>>> - C_SET_RID_CODE (id, rid);
>>>>> - C_IS_RESERVED_WORD (id) = 1;
>>>>> - ridpointers [rid] = id;
>>>>> -}
>>>>> -
>>>>> /* Return identifier to look up for omp declare reduction. */
>>>>> tree
>>>>> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
>>>>> index fdb96c28c51..2a700bbaff3 100644
>>>>> --- a/gcc/c/c-typeck.cc
>>>>> +++ b/gcc/c/c-typeck.cc
>>>>> @@ -303,32 +303,6 @@ c_type_promotes_to (tree type)
>>>>> return type;
>>>>> }
>>>>> -/* Return true if between two named address spaces, whether there is a superset
>>>>> - named address space that encompasses both address spaces. If there is a
>>>>> - superset, return which address space is the superset. */
>>>>> -
>>>>> -static bool
>>>>> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
>>>>> -{
>>>>> - if (as1 == as2)
>>>>> - {
>>>>> - *common = as1;
>>>>> - return true;
>>>>> - }
>>>>> - else if (targetm.addr_space.subset_p (as1, as2))
>>>>> - {
>>>>> - *common = as2;
>>>>> - return true;
>>>>> - }
>>>>> - else if (targetm.addr_space.subset_p (as2, as1))
>>>>> - {
>>>>> - *common = as1;
>>>>> - return true;
>>>>> - }
>>>>> - else
>>>>> - return false;
>>>>> -}
>>>>> -
>>>>> /* Return a variant of TYPE which has all the type qualifiers of LIKE
>>>>> as well as those of TYPE. */
>>>>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>>>>> index e2607f09c19..0248569a95b 100644
>>>>> --- a/gcc/cp/cp-tree.h
>>>>> +++ b/gcc/cp/cp-tree.h
>>>>> @@ -6235,6 +6235,7 @@ enum cp_decl_spec {
>>>>> ds_const,
>>>>> ds_volatile,
>>>>> ds_restrict,
>>>>> + ds_addr_space,
>>>>> ds_inline,
>>>>> ds_virtual,
>>>>> ds_explicit,
>>>>> @@ -6281,6 +6282,8 @@ struct cp_decl_specifier_seq {
>>>>> cp_storage_class storage_class;
>>>>> /* For the __intN declspec, this stores the index into the int_n_* arrays. */
>>>>> int int_n_idx;
>>>>> + /* The address space that the declaration belongs to. */
>>>>> + addr_space_t address_space;
>>>>> /* True iff TYPE_SPEC defines a class or enum. */
>>>>> BOOL_BITFIELD type_definition_p : 1;
>>>>> /* True iff multiple types were (erroneously) specified for this
>>>>> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
>>>>> index 85b892cddf0..a87fed04529 100644
>>>>> --- a/gcc/cp/decl.cc
>>>>> +++ b/gcc/cp/decl.cc
>>>>> @@ -5290,6 +5290,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
>>>>> type_quals |= TYPE_QUAL_VOLATILE;
>>>>> if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
>>>>> type_quals |= TYPE_QUAL_RESTRICT;
>>>>> + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>>>> + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
>>>>> return type_quals;
>>>>> }
>>>>> @@ -5412,6 +5414,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
>>>>> error_at (declspecs->locations[ds_restrict],
>>>>> "%<__restrict%> can only be specified for objects and "
>>>>> "functions");
>>>>> + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
>>>>> + error_at (declspecs->locations[ds_addr_space],
>>>>> + "address space can only be specified for objects and "
>>>>> + "functions");
>>>>> else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
>>>>> error_at (declspecs->locations[ds_thread],
>>>>> "%<__thread%> can only be specified for objects "
>>>>> @@ -14608,6 +14614,59 @@ grokdeclarator (const cp_declarator *declarator,
>>>>> if (!processing_template_decl)
>>>>> cp_apply_type_quals_to_decl (type_quals, decl);
>>>>> + /* Warn about address space used for things other than static memory or
>>>>> + pointers. */
>>>>> + addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
>>>>> + if (!ADDR_SPACE_GENERIC_P (address_space))
>>>>> + {
>>>>> + if (decl_context == NORMAL)
>>>>> + {
>>>>> + switch (storage_class)
>>>>
>>>> I would still suggest checking decl_storage_duration at this point rather
>>>> than the storage_class specifier.
>>>
>>> Unless I misunderstand something, I can't weed out register variables
>>> if I rely on decl_storage_duration.
>>
>> Yes, but register variables are automatic, so they'll get that error; I
>> don't think they need their own specific error.
>>
> Noted.
>>>>> + {
>>>>> + case sc_auto:
>>>>> + error ("%qs combined with C++98 %<auto%> qualifier for %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + break;
>>>>> + case sc_register:
>>>>> + error ("%qs combined with %<register%> qualifier for %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + break;
>>>>> + case sc_none:
>>>>> + if (! toplevel_bindings_p ())
>>>>> + error ("%qs specified for auto variable %qs",
>>>>
>>>> And let's refer to automatic storage duration rather than shorten to 'auto'.
>>>>
>>> Right.
>>>>> + c_addr_space_name (address_space), name);
>>>>> + break;
>>>>> + case sc_mutable:
>>>>> + error ("%qs combined with %<mutable%> qualifier for %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + break;
>>>>> + case sc_static:
>>>>> + case sc_extern:
>>>>> + break;
>>>>> + default:
>>>>> + gcc_unreachable ();
>>>>> + }
>>>>> + }
>>>>> + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
>>>>> + {
>>>>> + if (name)
>>>>> + error ("%qs specified for parameter %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + else
>>>>> + error ("%qs specified for unnamed parameter",
>>>>> + c_addr_space_name (address_space));
>>>>> + }
>>>>> + else if (decl_context == FIELD)
>>>>> + {
>>>>> + if (name)
>>>>> + error ("%qs specified for structure field %qs",
>>>>> + c_addr_space_name (address_space), name);
>>>>> + else
>>>>> + error ("%qs specified for structure field",
>>>>> + c_addr_space_name (address_space));
>>>>> + }
>>>>> + }
>>>>> +
>>>>> return decl;
>>>>> }
>>>>> }
>>>>> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
>>>>> index 1215463089b..aafff98f05a 100644
>>>>> --- a/gcc/cp/mangle.cc
>>>>> +++ b/gcc/cp/mangle.cc
>>>>> @@ -2520,6 +2520,14 @@ write_CV_qualifiers_for_type (const tree type)
>>>>> array. */
>>>>> cp_cv_quals quals = TYPE_QUALS (type);
>>>>> + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
>>>>> + {
>>>>> + const char *as_name = c_addr_space_name (as);
>>>>> + write_char ('U');
>>>>> + write_unsigned_number (strlen (as_name));
>>>>> + write_string (as_name);
>>>>> + ++num_qualifiers;
>>>>> + }
>>>>> if (quals & TYPE_QUAL_RESTRICT)
>>>>> {
>>>>> write_char ('r');
>>>>> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
>>>>> index 9ddfb027ff9..c82059d1efd 100644
>>>>> --- a/gcc/cp/parser.cc
>>>>> +++ b/gcc/cp/parser.cc
>>>>> @@ -7703,6 +7703,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
>>>>> postfix_expression = error_mark_node;
>>>>> break;
>>>>> }
>>>>> + if (type != error_mark_node
>>>>> + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
>>>>> + && current_function_decl)
>>>>> + {
>>>>> + error
>>>>> + ("compound literal qualified by address-space "
>>>>> + "qualifier");
>>>>> + type = error_mark_node;
>>>>> + }
>>>>> /* Form the representation of the compound-literal. */
>>>>> postfix_expression
>>>>> = finish_compound_literal (type, initializer,
>>>>> @@ -19445,6 +19454,15 @@ cp_parser_type_specifier (cp_parser* parser,
>>>>> break;
>>>>> }
>>>>> +
>>>>> + if (RID_FIRST_ADDR_SPACE <= keyword && keyword <= RID_LAST_ADDR_SPACE)
>>>>> + {
>>>>> + ds = ds_addr_space;
>>>>> + if (is_cv_qualifier)
>>>>> + *is_cv_qualifier = true;
>>>>> + }
>>>>> +
>>>>> +
>>>>
>>>> I don't think we need two blank lines before and after this block, one each
>>>> should be enough.
>>>>
>>> Indeed.
>>>>> /* Handle simple keywords. */
>>>>> if (ds != ds_last)
>>>>> {
>>>>> @@ -23837,6 +23855,7 @@ cp_parser_ptr_operator (cp_parser* parser,
>>>>> GNU Extension:
>>>>> cv-qualifier:
>>>>> + address-space-qualifier
>>>>> __restrict__
>>>>> Returns a bitmask representing the cv-qualifiers. */
>>>>> @@ -23873,6 +23892,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
>>>>> break;
>>>>> }
>>>>> + if (RID_FIRST_ADDR_SPACE <= token->keyword
>>>>> + && token->keyword <= RID_LAST_ADDR_SPACE)
>>>>> + cv_qualifier
>>>>> + = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>>>>> +
>>>>> if (!cv_qualifier)
>>>>> break;
>>>>> @@ -32893,6 +32917,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>>>> decl_specs->locations[ds] = location;
>>>>> if (ds == ds_thread)
>>>>> decl_specs->gnu_thread_keyword_p = token_is__thread (token);
>>>>> + else if (ds == ds_addr_space)
>>>>> + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
>>>>> }
>>>>> else
>>>>> {
>>>>> @@ -32925,6 +32951,25 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
>>>>> error_at (&richloc, "duplicate %qD", token->u.value);
>>>>> }
>>>>> }
>>>>> + else if (ds == ds_addr_space)
>>>>> + {
>>>>> + addr_space_t as1 = decl_specs->address_space;
>>>>> + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
>>>>> +
>>>>> + gcc_rich_location richloc (location);
>>>>> + richloc.add_fixit_remove ();
>>>>> + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
>>>>> + && as1 != as2)
>>>>> + error_at (&richloc,
>>>>> + "conflicting named address spaces (%s vs %s)",
>>>>> + c_addr_space_name (as1), c_addr_space_name (as2));
>>>>> + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
>>>>> + error_at (&richloc,
>>>>> + "duplicate named address space %s",
>>>>> + c_addr_space_name (as1));
>>>>> +
>>>>> + decl_specs->address_space = as2;
>>>>> + }
>>>>> else
>>>>> {
>>>>> static const char *const decl_spec_names[] = {
>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>> index e4dca9d4f9d..7b73a57091e 100644
>>>>> --- a/gcc/cp/pt.cc
>>>>> +++ b/gcc/cp/pt.cc
>>>>> @@ -23778,8 +23778,19 @@ template_decl_level (tree decl)
>>>>> static int
>>>>> check_cv_quals_for_unify (int strict, tree arg, tree parm)
>>>>> {
>>>>> - int arg_quals = cp_type_quals (arg);
>>>>> - int parm_quals = cp_type_quals (parm);
>>>>> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>>>> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>>>> +
>>>>> + /* Try to unify ARG's address space into PARM's address space.
>>>>> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
>>>>> + there are no constraints on address spaces for this type. */
>>>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
>>>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
>>>>> + addr_space_t as_common;
>>>>> + addr_space_superset (as_arg, as_parm, &as_common);
>>>>> +
>>>>> + if (!(as_parm == as_common || as_parm == 0))
>>>>> + return 0;
>>>>
>>>> I'd expect address space qualifiers to follow the 'strict' parameter like
>>>> the other qualifiers; the above test seems to assume
>>>> UNIFY_ALLOW_{OUTER_,}LESS_CV_QUAL.
>>>>
>>> The reason I ignored strict was to enforce that the deduced address
>>> space is always at most "as_parm" unless "as_parm" is the generic address
>>> space, and prevent unifying if the two address spaces are disjoint
>>> unless "parm" does not have any address space constraints; and avoid the
>>> addition/deletion of an address space to "arg" during the unifying
>>> process.
>>>
>>> Since I don't really understand the whole picture behind strict, and when
>>> check_cv_quals_for_unify gets called with which variant of restrict it
>>> might be me who tried to be overcareful when unifying the address
>>> spaces.
>>
>> How we need to handle differing qualifiers varies between different template
>> argument deduction contexts.
>>
>> The code you wrote above is correct for the function call context, since
>> https://eel.is/c++draft/temp.deduct.call#4.2 says the deduced type can be
>> convertable by qualification conversion, i.e. parm more qualified than arg
>> (and my "LESS" above was backwards). This is a bit different for address
>> space qualifiers given that the qualification conversion would be removing
>> the address space qualifier or changing it to a more general one, but the
>> principle is the same.
>>
>> But the allowance for qualifier changes doesn't apply to all deduction
>> contexts: for instance,
>>
>> template <class T> void f(T * const *);
>> struct A {
>> template <class T> operator T**();
>> };
>> int main()
>> {
>> f((void**)0); // void** -> void*const* is a valid qualification conv
>> (void *const*)A(); // same conversion
>> void (*p)(void **) = f; // error, type mismatch
>> }
>>
>> so similarly,
>>
>> template <class T> void f(T **);
>> struct A {
>> template <class T> operator T*__seg_fs*();
>> };
>> int main()
>> {
>> f((void* __seg_fs *)0); // void*__seg_fs* -> void** should be OK
>> void (*p)(void * __seg_fs *) = f; // error
>> }
>>
>>
> I do not completely agree here. Currently, my implementation rejects
> all deductions which would change or remove an address space no matter
> the context, which is very conservative.
>
> I tried using "strict" as the other qualifiers do, and as I expected,
> it keeps rejecting
> f((void* __seg_fs *)0); // void*__seg_fs* -> void** should be OK
> which is to be expected, since a pointer can't jump from an address
> space to another unless there is a common superset and here __seg_fs
> is disjoint from the generic address space.
Aha, I was thinking that the generic address space was a superset.
> I don't really understand what is done in
> (void **)A(); // same conversion
> but it is similarly rejected (implicit conversion from A to (void**))
>
> The third one is strangely accepted, and clang accept is as well (only
> the address space variant, the one with const is duly rejected).
> I will investigate what clang does here, as I think it would be better
> if the behavior of clang and gcc concerning this feature matches as
> much as possible from a user standpoint, since the C++ side of this
> feature is, to my knowledge, completely undocumented.
I assume this is a bug in clang as well.
>>>>> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
>>>>> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
>>>>> @@ -24415,10 +24426,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
>>>>> arg, parm))
>>>>> return unify_cv_qual_mismatch (explain_p, parm, arg);
>>>>> + int arg_cv_quals = cp_type_quals (arg);
>>>>> + int parm_cv_quals = cp_type_quals (parm);
>>>>> +
>>>>> + /* If PARM does not contain any address spaces constraints it can
>>>>> + fully match the address space of ARG. However, if PARM contains an
>>>>> + address space constraints, it becomes the upper bound. That is,
>>>>> + AS_ARG may be promoted to AS_PARM but not the converse. If we
>>>>> + ended up here, it means that `check_cv_quals_for_unify' succeeded
>>>>> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
>>>>> + AS_PARM. */
>>>>> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
>>>>> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
>>>>> + addr_space_t as_common = as_parm ? 0 : as_arg;
>>>>
>>>> Hmm, I'd think we also want as_common = as_arg when it's a subset of
>>>> as_parm.
>>>
>>> Let's assume that "PARM" is "__as1 T", and since the call to
>>> check_cv_quals_for_unify succeeded we know that "as_common" is
>>> "__as1". That is ARG is of the form "__as2 U" with "__as2" a
>>> subset of "__as1", hence we are trying to unify
>>> __as1 T = __as1 U
>>> which does not give any constraints over PARM since it alreay contains
>>> the common address space, hence there is no more constraints on T and
>>> as_common = 0.
>>
>> Agreed.
>>
>>> However, if PARM's address space is 0, we are trying to unify
>>> T = __as1 U
>>> and we need to add __addr_space1 to the constraints of T.
>>
>> Agreed.
>>
>>> If as_parm is not the generic address space (ie, as_parm != 0)
>>
>> Looks like this comment got cut off? This is the case I was talking about.
>> When we are trying to unify
>>
>> __as1 T = __as2 U
>>
>> and __as2 is a subset of __as1, I think we want T to be deduced to __as2 U,
>> and then substitution will need to handle substituting __as2 U for T into
>> __as1 T to get __as2 U.
>>
>
> I more or less agree, but I think that the substitution will need to
> handle substituting __as2 U for T into __as1 T to get __as1 U.
> (Leading to __as1 U and not __as2 U, since __as1 is the biggest
> address space and the templated function expect __as1 T).
After substitution, I would think we want to end up with the smaller
address space; in general, we want the more specialized form.
> Nevertheless, this means that when fully deduced __as1 T becomes __as1
> __as2 U and then the substitution mechanism would make it into __as1
> U. Could you please tell me where the substitution mechanism takes
> place so that I can account for this case and that if we end up with
> two compatible address spaces the biggest is selected?
I think it would be best to handle it in cp_build_qualified_type.
>>>>> /* Consider the case where ARG is `const volatile int' and
>>>>> PARM is `const T'. Then, T should be `volatile int'. */
>>>>> arg = cp_build_qualified_type
>>>>> (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
>>>>> + int unified_cv =
>>>>> + (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
>>>>> + | ENCODE_QUAL_ADDR_SPACE (as_common));
>>>>> + arg = cp_build_qualified_type (arg, unified_cv, tf_none);
>>>>> if (arg == error_mark_node)
>>>>> return unify_invalid (explain_p);
>>>>> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
>>>>> index 45348c58bb6..1f330ca93ed 100644
>>>>> --- a/gcc/cp/tree.cc
>>>>> +++ b/gcc/cp/tree.cc
>>>>> @@ -6072,15 +6072,6 @@ cp_free_lang_data (tree t)
>>>>> DECL_CHAIN (t) = NULL_TREE;
>>>>> }
>>>>> -/* Stub for c-common. Please keep in sync with c-decl.cc.
>>>>> - FIXME: If address space support is target specific, then this
>>>>> - should be a C target hook. But currently this is not possible,
>>>>> - because this function is called via REGISTER_TARGET_PRAGMAS. */
>>>>> -void
>>>>> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
>>>>> -{
>>>>> -}
>>>>> -
>>>>> /* Return the number of operands in T that we care about for things like
>>>>> mangling. */
>>>>> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
>>>>> index da0e1427b97..93cfdc70e2d 100644
>>>>> --- a/gcc/cp/typeck.cc
>>>>> +++ b/gcc/cp/typeck.cc
>>>>> @@ -803,10 +803,28 @@ composite_pointer_type (const op_location_t &location,
>>>>> else
>>>>> return error_mark_node;
>>>>> }
>>>>> + /* If possible merge the address space into the superset of the address
>>>>> + spaces of t1 and t2, or raise an error. */
>>>>> + addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
>>>>> + addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
>>>>> + addr_space_t as_common;
>>>>> +
>>>>> + /* If the two named address spaces are different, determine the common
>>>>> + superset address space. If there isn't one, raise an error. */
>>>>> + if (!addr_space_superset (as_t1, as_t2, &as_common))
>>>>> + {
>>>>> + as_common = as_t1;
>>>>> + error_at (location,
>>>>> + "%qT and %qT are in disjoint named address spaces",
>>>>> + t1, t2);
>>>>
>>>> Why not return error_mark_node here?
>>>>
>>> That's a mistake. Thanks.
>>>>> + }
>>>>> + int quals_t1 = cp_type_quals (TREE_TYPE (t1));
>>>>> + int quals_t2 = cp_type_quals (TREE_TYPE (t2));
>>>>> result_type
>>>>> = cp_build_qualified_type (void_type_node,
>>>>> - (cp_type_quals (TREE_TYPE (t1))
>>>>> - | cp_type_quals (TREE_TYPE (t2))));
>>>>> + (CLEAR_QUAL_ADDR_SPACE (quals_t1)
>>>>> + | CLEAR_QUAL_ADDR_SPACE (quals_t2)
>>>>> + | ENCODE_QUAL_ADDR_SPACE (as_common)));
>>>>> result_type = build_pointer_type (result_type);
>>>>> /* Merge the attributes. */
>>>>> attributes = (*targetm.merge_type_attributes) (t1, t2);
>>>>> @@ -1731,7 +1749,9 @@ comptypes (tree t1, tree t2, int strict)
>>>>> }
>>>>> /* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
>>>>> - top-level qualifiers. */
>>>>> + top-level qualifiers, except for named address spaces. If the pointers point
>>>>> + to different named addresses spaces, then we must determine if one address
>>>>> + space is a subset of the other. */
>>>>> bool
>>>>> same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>>>> @@ -1741,6 +1761,14 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
>>>>> if (type1 == type2)
>>>>> return true;
>>>>> + addr_space_t as_type1 = TYPE_ADDR_SPACE (type1);
>>>>> + addr_space_t as_type2 = TYPE_ADDR_SPACE (type2);
>>>>> + addr_space_t as_common;
>>>>> +
>>>>> + /* Fail if pointers point to incompatible address spaces. */
>>>>> + if (!addr_space_superset (as_type1, as_type2, &as_common))
>>>>> + return false;
>>>>
>>>> Why do you need this change? I'd expect this function to ignore top level
>>>> address space qualifiers like the other qualifiers.
>>>
>>> I am mirroring the C front-end here, which does the same thing in
>>> "comp_target_types" (gcc/c/c-typeck.cc), which ignores qualifiers but
>>> not address spaces when checking if two pointer types are equivalent.
>>
>> This function serves a very different function from comp_target_types, which
>> deals with the types that pointers point to; this function is ignoring
>> top-level qualifiers that should not affect the type.
>>
>> ...except now I see that cp_build_binary_op is wierdly using this function
>> for pointer subtraction. I'd think it should use composite_pointer_type
>> instead, like EQ_EXPR does.
>
> I think this is because of https://eel.is/c++draft/expr.add#2.2 and I
> am not sure that composite_pointer_type can replace it here since it
> does try to merge the two list of qualifiers.
Ah, good point. So I guess it does make sense for it to use
same_type_ignoring_top_level_qualifiers_p there, but the other callers
of that function don't want this address space check; it should be
enough to do it only in pointer_diff.
>>>>> type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
>>>>> type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
>>>>> return same_type_p (type1, type2);
>>>>> @@ -6672,10 +6700,32 @@ static tree
>>>>> pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
>>>>> tsubst_flags_t complain, tree *instrument_expr)
>>>>> {
>>>>> - tree result, inttype;
>>>>> tree restype = ptrdiff_type_node;
>>>>> + tree result, inttype;
>>>>> +
>>>>> + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
>>>>> + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
>>>>> tree target_type = TREE_TYPE (ptrtype);
>>>>> + /* If the operands point into different address spaces, we need to
>>>>> + explicitly convert them to pointers into the common address space
>>>>> + before we can subtract the numerical address values. */
>>>>> + if (as0 != as1)
>>>>> + {
>>>>> + addr_space_t as_common;
>>>>> + tree common_type;
>>>>> +
>>>>> + /* Determine the common superset address space. This is guaranteed
>>>>> + to exist because the caller verified that comp_target_types
>>>>> + returned non-zero. */
>>>>> + if (!addr_space_superset (as0, as1, &as_common))
>>>>> + gcc_unreachable ();
>>>>> +
>>>>> + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
>>>>> + op0 = convert (common_type, op0);
>>>>> + op1 = convert (common_type, op1);
>>>>> + }
>>>>
>>>> I think you shouldn't need to change pointer_diff if composite_pointer_type
>>>> returns error_mark_node above.
>>>
>>> I'll have a look, the idea here is to prevent "a - b" with "a" and "b"
>>> from different address spaces.
>>
>> As above, I think this should have been handled in cp_build_binary_op.
>
> I don't really understand why you don't want the address space
> conversion (which might be needed for subtraction) to happen at the same
> time as the conversion to the "common_pointer_type".
Agreed, except you'll want a diagnostic here now.
>>>>> if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
>>>>> return error_mark_node;
>>>>> @@ -11286,6 +11336,19 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>>>>> to_more_cv_qualified = true;
>>>>> }
>>>>> + /* Warn about conversions between pointers to disjoint
>>>>> + address spaces. */
>>>>> + if (TREE_CODE (from) == POINTER_TYPE
>>>>> + && TREE_CODE (to) == POINTER_TYPE)
>>>>> + {
>>>>> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
>>>>> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
>>>>> + addr_space_t as_common;
>>>>> +
>>>>> + if (!addr_space_superset (as_to, as_from, &as_common))
>>>>> + return false;
>>>>
>>>> I think you also want to check that as_common == as_to here?
>>>>
>>> Yes.
>>>>> + }
>>>>> +
>>>>> if (constp > 0)
>>>>> constp &= TYPE_READONLY (to);
>>>>> }
>>>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>>>> index cfbe32afce9..ef75f6b83a2 100644
>>>>> --- a/gcc/doc/extend.texi
>>>>> +++ b/gcc/doc/extend.texi
>>>>> @@ -1448,7 +1448,7 @@ Fixed-point types are supported by the DWARF debug information format.
>>>>> @section Named Address Spaces
>>>>> @cindex Named Address Spaces
>>>>> -As an extension, GNU C supports named address spaces as
>>>>> +As an extension, GNU C and GNU C++ support named address spaces as
>>>>> defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
>>>>> address spaces in GCC will evolve as the draft technical report
>>>>> changes. Calling conventions for any target might also change. At
>>>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>>>> new file mode 100644
>>>>> index 00000000000..c01f8d6054a
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
>>>>> @@ -0,0 +1,10 @@
>>>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>>>
>>>> This can be dg-do compile, I don't think you get anything from running an
>>>> empty main.
>>>>
>>> Yes.
>>>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>>>
>>>> And then you don't need -save-temps. What are the other options for?
>>>>
>>> I forgot to remove -Wabi and -fabi-version, this was from my first
>>> attempt when I used AS<number> to mangle which changed the ABI. I'll
>>> remove them.
>>>>> +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
>>>>> +
>>>>> +int f (int volatile __seg_fs *a)
>>>>> +{
>>>>> + return *a;
>>>>> +}
>>>>> +
>>>>> +int main () {}
>>>>> diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>>>> new file mode 100644
>>>>> index 00000000000..862bbbdcdf2
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
>>>>> @@ -0,0 +1,9 @@
>>>>> +// { dg-do run { target { i?86-*-* x86_64-*-* } } }
>>>>> +// { dg-options "-fabi-version=8 -Wabi -save-temps" }
>>>>
>>>> Also not clear that running is important for this test.
>>>>
>>> Noted.
>>>>> +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
>>>>> +
>>>>> +template <class T>
>>>>> +int f (T *p) { return *p; }
>>>>> +int g (__seg_fs int *p) { return *p; }
>>>>> +__seg_fs int *a;
>>>>> +int main() { f(a); }
>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>>>> new file mode 100644
>>>>> index 00000000000..c04d2f497da
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
>>>>> @@ -0,0 +1,5 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +__seg_fs char a, b, c;
>>>>> +__seg_fs const int *p;
>>>>> +static /* give internal linkage to the following anonymous struct */
>>>>
>>>> Hmm, this 'static' gives internal linkage to the variable q, not the type.
>>>> What do you want it for?
>>>>
>>> Yes, the idea is to give internal linkage to q, otherwise g++
>>> complains in -std=c++98 mode because q is externally visible but it
>>> can't be reffered from anywhere else since there is no tag for this
>>> structure.
>>
>> Then let's change the comment to /* give internal linkage to q */
> Agreed.
>>
>>>>> +__seg_fs struct { int a; char b; } * __seg_gs q;
>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>>>> new file mode 100644
>>>>> index 00000000000..86c02d1e7f5
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
>>>>> @@ -0,0 +1,20 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +int __seg_fs * fs1;
>>>>> +int __seg_fs * fs2;
>>>>> +float __seg_gs * gs1;
>>>>> +float __seg_gs * gs2;
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
>>>>> + fs1 - fs2;
>>>>> + fs1 - gs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_gs float.. to binary .operator.." }
>>>>> + fs1 == fs2;
>>>>> + fs1 != gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>>>> + fs1 = fs2;
>>>>> + fs1 = gs2; // { dg-error "cannot convert .__seg_gs float.. to .__seg_fs int.. in assignment" }
>>>>> + fs1 > fs2;
>>>>> + fs1 < gs2; // { dg-error "comparison between distinct pointer types .__seg_fs int.. and .__seg_gs float.. lacks a cast" }
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>>>> new file mode 100644
>>>>> index 00000000000..12d7975e560
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
>>>>> @@ -0,0 +1,31 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +// { dg-prune-output "does not allow .register. storage class specifier" }
>>>>> +int __seg_fs * outer_b;
>>>>> +
>>>>> +struct s {
>>>>> + __seg_fs int * ok;
>>>>> + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
>>>>> +};
>>>>> +
>>>>> +int register __seg_fs reg_fs; // { dg-error ".__seg_fs. combined with .register. qualifier for .reg_fs." }
>>>>> +
>>>>> +namespace ns_a
>>>>> +{
>>>>> + int __seg_fs * inner_b;
>>>>> +
>>>>> + template<typename T>
>>>>> + int f (T &a) { return a; }
>>>>> + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
>>>>> + int h (__seg_fs int *a) { return *a; }
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. combined with .register. qualifier for .reg_gs." }
>>>>> + static __seg_gs int static_gs;
>>>>> + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for auto variable .auto_fs." }
>>>>> + __seg_fs int *pa = outer_b;
>>>>> + __seg_fs int& ra = *ns_a::inner_b;
>>>>> + return ns_a::f(ra) + ns_a::f(*pa);
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
>>>>> new file mode 100644
>>>>> index 00000000000..ebb6316054a
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space.C
>>>>> @@ -0,0 +1,9 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +
>>>>> +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>>>> new file mode 100644
>>>>> index 00000000000..2e8ee32a885
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
>>>>> @@ -0,0 +1,10 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +// { dg-options "-std=gnu++98" }
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + struct foo {int a; char b[2];} structure;
>>>>> + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>>>> new file mode 100644
>>>>> index 00000000000..5b2c0f28078
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
>>>>> @@ -0,0 +1,9 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +
>>>>> +__seg_fs __seg_gs int *a; // { dg-error "conflicting named address spaces .__seg_fs vs __seg_gs." }
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
>>>>> new file mode 100644
>>>>> index 00000000000..70dfcce53fa
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
>>>>> @@ -0,0 +1,17 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +
>>>>> +int __seg_fs * fs1;
>>>>> +int __seg_gs * gs1;
>>>>> +
>>>>> +template<typename T, typename U>
>>>>> +__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
>>>>> +template<typename T, typename U>
>>>>> +__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + f (fs1, gs1);
>>>>> + f (gs1, fs1);
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
>>>>> new file mode 100644
>>>>> index 00000000000..5df115db939
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
>>>>> @@ -0,0 +1,17 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +// { dg-skip-if "" { *-*-* } { "-std=c++98" "-std=c++03" "-std=gnu++98" "-std=gnu++03" } { "" } }
>>>>
>>>> This can be { dg-require-effective-target c++11 }
>>
>> Or put the x86 requirement in dg-require-effective-target, and put c++11 in
>> the dg-do target spec, either way.
> Agreed.
>>
>>>>> +// decltype is ony available since c++11
>>>>
>>>> "only"
>>>>
>>>>> +
>>>>> +int __seg_fs * fs1;
>>>>> +int __seg_gs * gs1;
>>>>> +
>>>>> +template<typename T> struct strip;
>>>>> +template<typename T> struct strip<__seg_fs T *> { typedef T type; };
>>>>> +template<typename T> struct strip<__seg_gs T *> { typedef T type; };
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
>>>>> + return 0;
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
>>>>> new file mode 100644
>>>>> index 00000000000..526bbaa56b7
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
>>>>> @@ -0,0 +1,16 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +
>>>>> +int __seg_fs * fs1;
>>>>> +int __seg_gs * gs1;
>>>>> +
>>>>> +template<typename T, typename U>
>>>>> +bool f (T __seg_fs * a, U __seg_gs * b)
>>>>> +{
>>>>> + return *(T *) a == *(U *) b;
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +main ()
>>>>> +{
>>>>> + return f (fs1, gs1);
>>>>> +}
>>>>> diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>>>> new file mode 100644
>>>>> index 00000000000..ae9f4de0e1f
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
>>>>> @@ -0,0 +1,8 @@
>>>>> +// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>>>>> +
>>>>> +template <class T>
>>>>> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
>>>>> + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
>>>>> +__seg_fs int *a;
>>>>> +int main() { f(a); } // { dg-error "no matching" }
>>>>> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
>>>>> diff --git a/gcc/tree.h b/gcc/tree.h
>>>>> index 9af971cf401..4aebfef854b 100644
>>>>> --- a/gcc/tree.h
>>>>> +++ b/gcc/tree.h
>>>>> @@ -2292,7 +2292,7 @@ extern tree vector_element_bits_tree (const_tree);
>>>>> /* Encode/decode the named memory support as part of the qualifier. If more
>>>>> than 8 qualifiers are added, these macros need to be adjusted. */
>>>>> -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
>>>>> +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
>>>>> #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
>>>>> /* Return all qualifiers except for the address space qualifiers. */
>
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-10-26 7:18 ` Paul Iannetta
2022-10-26 16:28 ` Jason Merrill
@ 2022-11-03 13:38 ` Georg-Johann Lay
2022-11-10 14:08 ` Paul Iannetta
1 sibling, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2022-11-03 13:38 UTC (permalink / raw)
To: Paul Iannetta, Jason Merrill; +Cc: gcc-patches
> [PATCH v3] c++: parser - Support for target address spaces in C++
First of all, it is great news that GCC is going to implement named
address spaces for C++.
I have some questions:
1. How is name-mangling going to work?
======================================
Clang supports address spaces in C++, and for address-space 1 it does
generate code like the following:
#define __flash __attribute__((__address_space__(1)))
char get_p (const __flash char *p)
{
return *p;
}
_Z5get_pPU3AS1Kc:
...
I.e. address-space 1 is mangled as "AS1".
(Notice that Clang's attribute actually works like a qualifier here, one
could not get this to work with GCC attributes.)
2. Will it work with compound literals?
=======================================
Currently, the following C code works for target avr:
const __flash char *pHallo = (const __flash char[]) { "Hallo" };
This is a pointer in RAM (AS0) that holds the address of a string in
flash (AS1) and is initialized with that address. Unfortunately, this
does not work locally:
const __flash char* get_hallo (void)
{
[static] const __flash char *p2 = (const __flash char[]) { "Hallo2" };
return p2;
}
foo.c: In function 'get_hallo':
foo.c: error: compound literal qualified by address-space qualifier
Is there any way to make this work now? Would be great!
3. Will TARGET_ADDR_SPACE_DIAGNOSE_USAGE still work?
====================================================
Currently there is target hook TARGET_ADDR_SPACE_DIAGNOSE_USAGE.
I did not see it in your patches, so maybe I just missed it? See
https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gccint/Named-Address-Spaces.html#index-TARGET_005fADDR_005fSPACE_005fDIAGNOSE_005fUSAGE
4. Will it be possible to put C++ virtual tables in ASs, and how?
=================================================================
One big complaint about avr-g++ is that there is no way to put vtables
in flash (address-space 1) and to access them accordingly. How can this
be achieved with C++ address spaces?
Background: The AVR architecture has non-linear address space, and you
cannot tell from the numeric value of an address whether it's in RAM or
flash. You will have to use different instructions depending on the
location.
This means that .rodata must be located in RAM, because otherwise one
would not know whether const char* pointed to RAM or flash, but to
de-reference you's need different instructions.
One way out is named address spaces, so we could finally fix
https://gcc.gnu.org/PR43745
Regards,
Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-11-03 13:38 ` [PATCH v3] " Georg-Johann Lay
@ 2022-11-10 14:08 ` Paul Iannetta
2022-11-10 16:40 ` Georg-Johann Lay
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-11-10 14:08 UTC (permalink / raw)
To: Georg-Johann Lay; +Cc: Jason Merrill, gcc-patches
On Thu, Nov 03, 2022 at 02:38:39PM +0100, Georg-Johann Lay wrote:
> > [PATCH v3] c++: parser - Support for target address spaces in C++
>
> First of all, it is great news that GCC is going to implement named address
> spaces for C++.
>
> I have some questions:
>
> 1. How is name-mangling going to work?
> ======================================
>
> Clang supports address spaces in C++, and for address-space 1 it does
> generate code like the following:
>
> #define __flash __attribute__((__address_space__(1)))
>
> char get_p (const __flash char *p)
> {
> return *p;
> }
>
>
> _Z5get_pPU3AS1Kc:
> ...
>
> I.e. address-space 1 is mangled as "AS1".
>
> (Notice that Clang's attribute actually works like a qualifier here, one
> could not get this to work with GCC attributes.)
>
Currently, the __address_space__ attribute does not exist in GCC, and
the definition of address-spaces as well as how they are laid-out can
only modified in the back-end.
I agree that this is a convenient attribute to define address-spaces
on the fly, but this lacks the flexibility of specifying which
address-spaces are subsets of others and how they should be promoted.
The mangling will be done with the extended qualifiers extension, for
example, your example would be mangled into "_Z5get_pPU7__flashKc".
>
> 2. Will it work with compound literals?
> =======================================
>
> Currently, the following C code works for target avr:
>
> const __flash char *pHallo = (const __flash char[]) { "Hallo" };
>
> This is a pointer in RAM (AS0) that holds the address of a string in flash
> (AS1) and is initialized with that address. Unfortunately, this does not
> work locally:
>
> const __flash char* get_hallo (void)
> {
> [static] const __flash char *p2 = (const __flash char[]) { "Hallo2" };
> return p2;
> }
>
> foo.c: In function 'get_hallo':
> foo.c: error: compound literal qualified by address-space qualifier
>
> Is there any way to make this work now? Would be great!
>
Currently, I implement the same restrictions as the C front-end, but I
think that this restriction could be lifted.
>
> 3. Will TARGET_ADDR_SPACE_DIAGNOSE_USAGE still work?
> ====================================================
>
> Currently there is target hook TARGET_ADDR_SPACE_DIAGNOSE_USAGE.
> I did not see it in your patches, so maybe I just missed it? See
> https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gccint/Named-Address-Spaces.html#index-TARGET_005fADDR_005fSPACE_005fDIAGNOSE_005fUSAGE
>
That was a point I overlooked in my previous patch. This will be in
my new revision where I also add implicit conversion between
address spaces and also expose TARGET_ADDR_SPACE_CONVERT.
>
> 4. Will it be possible to put C++ virtual tables in ASs, and how?
> =================================================================
>
Currently, I do not allow the declaration of instances of classes in
an address space, mainly to not have to cope with the handling of the
this pointer. That is,
__flash Myclass *t;
does not work. Nevertheless, I admit that this is would be nice to
have.
> One big complaint about avr-g++ is that there is no way to put vtables in
> flash (address-space 1) and to access them accordingly. How can this be
> achieved with C++ address spaces?
Do you want only the vtables in the flash address space or do you want
to be able to have the whole class content.
1. If you only want the vtables, I think that a target hook called
at vtable creation would do the trick.
2. If you want to be able to have pointer to classes in __flash, I
will need to further the support I have currently implemented to
support the pointer this qualified with an address space.
Retrospectively, I think this have to be implemented.
Paul
>
> Background: The AVR architecture has non-linear address space, and you
> cannot tell from the numeric value of an address whether it's in RAM or
> flash. You will have to use different instructions depending on the
> location.
>
> This means that .rodata must be located in RAM, because otherwise one would
> not know whether const char* pointed to RAM or flash, but to de-reference
> you's need different instructions.
>
> One way out is named address spaces, so we could finally fix
>
> https://gcc.gnu.org/PR43745
>
>
> Regards,
>
> Johann
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v4] c++: parser - Support for target address spaces in C++
2022-10-26 16:28 ` Jason Merrill
@ 2022-11-10 15:42 ` Paul Iannetta
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
0 siblings, 1 reply; 86+ messages in thread
From: Paul Iannetta @ 2022-11-10 15:42 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches
Hi,
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, and warnings; and expose the same hooks as
the C front-end does. The C++ front-end produces as much warnings
that the C front-end, however they are sometimes a bit less
informative (especially so, when passing arguments to functions).
I also address the following points:
1. The handling of the register storage class is grouped with the
other variables with automatic storage. This incurs a slight
dis-alignment since you cannot have global register variables do not
trigger an error, only a warning.
2. In template unification, I maintain that we don't want any
changes to address spaces whatsoever during the unification process,
hence ignoring the strict flag. Nevertheless, we allow implicit
conversion, and I have verified that, indeed,
template <class T> void f(T **);
struct A {
template <class T> operator T*__seg_fs*();
};
int main()
{
f((void* __seg_fs *)0); // (1): void*__seg_fs* -> void** should be OK
void (*p)(void * __seg_fs *) = f; // (2): error
}
works as intended. That is, (1) works if we set __seg_fs as a subspace
of the generic address space, and (2) is always an error.
3. In template unification, when unifying __as1 T = __as2 U we want
to unify to the __as1 at most, never to __as2 at most, because the
function requiring __as1 T may want to mix address space from the
bigger address space, therefore I think that we want to have the
smaller address-space to be unified into the bigger of the two.
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.
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.
6. Currently, I do not handle classes. I see what I can do in a
further revision and maybe add a target hook to hand down to targets
the choice of the address space of the vtable.
7. This can't be added as a test case, but I checked that:
// In this test case, __seg_gs is a subset of the generic address
// space.
int f (int *);
int main ()
{
int __seg_fs *pa;
int __seg_gs *pb;
int *pc;
pa = (__seg_fs int *) pb; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint ‘__seg_gs’ address space pointer
pa = (int __seg_fs *) pc; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint generic address space pointer
pa = pb; return *pa; // error: invalid conversion from ‘__seg_gs int*’ to ‘__seg_fs int*’
pc = pb; return *pc; // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
pb = pc; return *pb; // error: invalid conversion from ‘int*’ to ‘__seg_gs int*’
pc = pa; return *pb; // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
return f (pb); // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
// return f (pa); // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
// note: initializing argument 1 of ‘int f(int*)’
}
Thanks,
Paul
Rebased on current trunk, bootstrapped and regtested.
# ------------------------ >8 ------------------------
gcc/cp/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* 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.
* cp-tree.h (enum cp_decl_spec): Add a new decl spec to handle
address spaces.
(struct cp_decl_specifier_seq): Likewise.
* decl.cc (get_type_quals): Add address space support.
(check_tag_decl): Likewise.
(grokdeclarator): Likewise.
* class.cc (fixed_type_or_null): Add a case for
ADDR_SPACE_CONVERT_EXPR.
* constexpr.cc (cxx_eval_constant_expression): Likewise.
(potential_constant_expression_1): Likewise.
* cp-gimplify.cc (cp_fold): Likewise.
* error.cc (dump_expr): Likewise.
* expr.cc (mark_use): Likewise.
* tree.cc (cp_stabilize_reference): Likewise.
(strip_typedefs_expr): Likewise.
(cp_tree_equal): Likewise.
(mark_exp_read): Likewise.
* mangle.cc (write_CV_qualifiers_for_type): Mangle address
spaces using the extended type qualifier scheme.
* parser.cc (cp_lexer_get_preprocessor_token): Add a call to
targetm.addr_space.diagnose_usage when lexing an address space
token.
(cp_parser_postfix_expression): Prevent the use of address
spaces with compound literals.
(cp_parser_dot_deref_incomplete): Add a case for
ADDR_SPACE_CONVERT_EXPR.
(cp_parser_type_specifier): Add address space support.
(cp_parser_cv_qualifier_seq_opt): Likewise.
(set_and_check_decl_spec_loc): Likewise.
* pt.cc (invalid_tparm_referent_p): Add a case for
ADDR_SPACE_CONVERT_EXPR.
(tsubst_copy): Likewise.
(tsubst_omp_clauses): Likewise.
(tsubst_omp_for_iterator): Likewise.
(check_cv_quals_for_unify): Add address space support.
(unify): Likewise.
* typeck.cc (composite_pointer_type_r): Likewise.
(pointer_diff): Likewise.
(cp_build_addr_expr_1): Add a case for ADDR_SPACE_CONVERT_EXPR.
(warn_about_cast_crossing_as_boundaries): New function.
(build_static_cast): Add address space support.
(build_reinterpret_cast): Add address space support.
(build_const_cast): Add address space support.
(cp_build_c_cast): Add address space support.
(comp_ptr_ttypes_real): Add address space support.
gcc/c/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* c-decl.cc (r_register_addr_space): Remove.
* c-typeck.cc: (addr_space_superset): Move to gcc/c/c-common.h
gcc/c-family/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* c-common.cc (addr_space_superset): Imported from c-decl.cc
(c_register_addr_space): Imported from gcc/c/c-typeck.cc
* c-common.h: Remove the FIXME.
(addr_space_superset): New declaration.
gcc/testsuite/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* g++.dg/abi/mangle-addr-space1.C: New test.
* g++.dg/abi/mangle-addr-space2.C: New test.
* g++.dg/ext/addr-space-decl.C: New test.
* g++.dg/ext/addr-space-ops.C: New test.
* g++.dg/ext/addr-space-ref.C: New test.
* g++.dg/parse/addr-space.C: New test.
* g++.dg/parse/addr-space1.C: New test.
* g++.dg/parse/addr-space2.C: New test.
* g++.dg/template/addr-space-overload.C: New test.
* g++.dg/template/addr-space-strip1.C: New test.
* g++.dg/template/addr-space-strip2.C: New test.
* g++.dg/template/spec-addr-space.C: New test.
gcc/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* doc/.../named-address-spaces.rst: Add C++ support.
* targhooks.cc (default_addr_space_subset_p):
* tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
# Date: Sun Oct 9 16:02:22 2022 +0200
#
# ------------------------ >8 ------------------------
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 5890c18bdc3..88351441c0c 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -588,6 +588,33 @@ c_addr_space_name (addr_space_t as)
return IDENTIFIER_POINTER (ridpointers [rid]);
}
+/* Return true if between two named address spaces, whether there is a superset
+ named address space that encompasses both address spaces. If there is a
+ superset, return which address space is the superset. */
+
+bool
+addr_space_superset (addr_space_t as1, addr_space_t as2,
+ addr_space_t * common)
+{
+ if (as1 == as2)
+ {
+ *common = as1;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as1, as2))
+ {
+ *common = as2;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as2, as1))
+ {
+ *common = as1;
+ return true;
+ }
+ else
+ return false;
+}
+
/* Push current bindings for the function name VAR_DECLS. */
void
@@ -2789,6 +2816,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ TREE_LANG_FLAG_0 (id) = 1;
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index f9d0d2945a5..4ffaabd9f50 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -829,12 +829,11 @@ extern const struct attribute_spec c_common_format_attribute_table[];
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
+extern const char *c_addr_space_name (addr_space_t as);
+extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
extern bool names_builtin_p (const char *);
@@ -951,6 +950,7 @@ extern bool c_common_init (void);
extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index a99b7456055..cf4076849f7 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -12869,25 +12869,6 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
-}
-
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 636098444b4..98cfc2132fc 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -303,32 +303,6 @@ c_type_promotes_to (tree type)
return type;
}
-/* Return true if between two named address spaces, whether there is a superset
- named address space that encompasses both address spaces. If there is a
- superset, return which address space is the superset. */
-
-static bool
-addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
-{
- if (as1 == as2)
- {
- *common = as1;
- return true;
- }
- else if (targetm.addr_space.subset_p (as1, as2))
- {
- *common = as2;
- return true;
- }
- else if (targetm.addr_space.subset_p (as2, as1))
- {
- *common = as1;
- return true;
- }
- else
- return false;
-}
-
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 492db9b59ad..25ceef04abb 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -8147,7 +8147,32 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
complain);
else if (t->kind == ck_identity)
break;
- }
+ else if (t->kind == ck_ptr
+ && INDIRECT_TYPE_P (TREE_TYPE (expr))
+ && INDIRECT_TYPE_P (totype))
+ {
+ tree from = TREE_TYPE (TREE_TYPE (expr));
+ tree to = TREE_TYPE (totype);
+
+ int from_full_quals = cp_type_quals (from);
+ int to_full_quals = cp_type_quals (to);
+ int from_quals = CLEAR_QUAL_ADDR_SPACE (from_full_quals);
+ int to_quals = CLEAR_QUAL_ADDR_SPACE (to_full_quals);
+ addr_space_t as_from = DECODE_QUAL_ADDR_SPACE (from_full_quals);
+ addr_space_t as_to = DECODE_QUAL_ADDR_SPACE (to_full_quals);
+ if (same_type_ignoring_top_level_qualifiers_p (from, to)
+ && from_quals == to_quals && as_from != as_to)
+ {
+ addr_space_t as_common;
+
+ /* If AS_FROM can be converted to AS_TO fold convert to force the
+ selection of ADDR_SPACE_CONVERT_EXPR instead of NOP_EXPR. */
+ if (addr_space_superset (as_from, as_to, &as_common)
+ && as_common == as_to)
+ return cp_fold_convert (totype, expr);
+ }
+ }
+ }
if (!complained && expr != error_mark_node)
{
range_label_for_type_mismatch label (TREE_TYPE (expr), totype);
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index aebcb53739e..5addff58214 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -7902,6 +7902,7 @@ fixed_type_or_null (tree instance, int *nonnull, int *cdtorp)
return NULL_TREE;
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
return RECUR (TREE_OPERAND (instance, 0));
case ADDR_EXPR:
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 15b4f2c4a08..56124960086 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -7535,6 +7535,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
/* FALLTHROUGH. */
case CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
case UNARY_PLUS_EXPR:
{
tree oldop = TREE_OPERAND (t, 0);
@@ -9076,6 +9077,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
/* FALLTHRU */
case CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
/* -- a reinterpret_cast. FIXME not implemented, and this rule
may change to something more specific to type-punning (DR 1312). */
{
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index cc8bfada5af..5a95902e762 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -2470,6 +2470,7 @@ cp_fold (tree x)
/* FALLTHRU */
case NON_LVALUE_EXPR:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
if (VOID_TYPE_P (TREE_TYPE (x)))
{
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index bbc8be21900..6cf2406ce98 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6237,6 +6237,7 @@ enum cp_decl_spec {
ds_complex,
ds_constinit,
ds_consteval,
+ ds_addr_space,
ds_thread,
ds_type_spec,
ds_redefined_builtin_type_spec,
@@ -6273,6 +6274,8 @@ struct cp_decl_specifier_seq {
cp_storage_class storage_class;
/* For the __intN declspec, this stores the index into the int_n_* arrays. */
int int_n_idx;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
/* True iff TYPE_SPEC defines a class or enum. */
BOOL_BITFIELD type_definition_p : 1;
/* True iff multiple types were (erroneously) specified for this
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 890cfcabd35..e2d97409b05 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -5353,6 +5353,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
return type_quals;
}
@@ -5475,6 +5477,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
error_at (declspecs->locations[ds_restrict],
"%<__restrict%> can only be specified for objects and "
"functions");
+ else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ error_at (declspecs->locations[ds_addr_space],
+ "address space can only be specified for objects and "
+ "functions");
else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
error_at (declspecs->locations[ds_thread],
"%<__thread%> can only be specified for objects "
@@ -14674,6 +14680,49 @@ grokdeclarator (const cp_declarator *declarator,
if (!processing_template_decl)
cp_apply_type_quals_to_decl (type_quals, decl);
+ /* Warn about address space used for things other than static memory or
+ pointers. */
+ addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
+ if (!ADDR_SPACE_GENERIC_P (address_space))
+ {
+ if (VAR_P (decl) && decl_context == NORMAL)
+ {
+ duration_kind dk = decl_storage_duration (decl);
+ switch (dk)
+ {
+ case dk_auto:
+ if (! toplevel_bindings_p ())
+ error ("%qs specified for variable %qs with automatic storage",
+ c_addr_space_name (address_space), name);
+ break;
+ case dk_static:
+ case dk_thread:
+ case dk_dynamic:
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
+ {
+ if (name)
+ error ("%qs specified for parameter %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for unnamed parameter",
+ c_addr_space_name (address_space));
+ }
+ else if (decl_context == FIELD)
+ {
+ if (name)
+ error ("%qs specified for structure field %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for structure field",
+ c_addr_space_name (address_space));
+ }
+ }
+
return decl;
}
}
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index da8c95c9b43..c02ea67b0e7 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2633,6 +2633,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
CASE_CONVERT:
case IMPLICIT_CONV_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
{
tree op = TREE_OPERAND (t, 0);
diff --git a/gcc/cp/expr.cc b/gcc/cp/expr.cc
index f3e155b7ba3..457c2cf5cb4 100644
--- a/gcc/cp/expr.cc
+++ b/gcc/cp/expr.cc
@@ -214,6 +214,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
}
gcc_fallthrough();
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
recurse_op[0] = true;
break;
@@ -373,6 +374,7 @@ mark_exp_read (tree exp)
case FLOAT_EXPR:
case NON_DEPENDENT_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
mark_exp_read (TREE_OPERAND (exp, 0));
break;
case COMPOUND_EXPR:
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index e97428e8f30..31cd35522a5 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2594,6 +2594,14 @@ write_CV_qualifiers_for_type (const tree type)
array. */
cp_cv_quals quals = TYPE_QUALS (type);
+ if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
+ {
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index e4021835ed5..d9ae1139235 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
#define INCLUDE_MEMORY
#include "system.h"
#include "coretypes.h"
+#include "target.h"
#include "cp-tree.h"
#include "c-family/c-common.h"
#include "timevar.h"
@@ -962,6 +963,14 @@ cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token)
token->type = CPP_KEYWORD;
/* Record which keyword. */
token->keyword = C_RID_CODE (token->u.value);
+
+ if (token->keyword >= RID_FIRST_ADDR_SPACE
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ {
+ addr_space_t as
+ = (addr_space_t) (token->keyword - RID_FIRST_ADDR_SPACE);
+ targetm.addr_space.diagnose_usage (as, token->location);
+ }
}
else
{
@@ -7707,6 +7716,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
postfix_expression = error_mark_node;
break;
}
+ if (type != error_mark_node
+ && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
+ && current_function_decl)
+ {
+ error
+ ("compound literal qualified by address-space "
+ "qualifier");
+ type = error_mark_node;
+ }
/* Form the representation of the compound-literal. */
postfix_expression
= finish_compound_literal (type, initializer,
@@ -8200,6 +8218,7 @@ cp_parser_dot_deref_incomplete (tree *scope, cp_expr *postfix_expression,
case STATIC_CAST_EXPR:
case DYNAMIC_CAST_EXPR:
case IMPLICIT_CONV_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
case NON_LVALUE_EXPR:
kind = DK_ERROR;
@@ -19449,6 +19468,14 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+ if (RID_FIRST_ADDR_SPACE <= keyword
+ && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ }
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -23841,6 +23868,7 @@ cp_parser_ptr_operator (cp_parser* parser,
GNU Extension:
cv-qualifier:
+ address-space-qualifier
__restrict__
Returns a bitmask representing the cv-qualifiers. */
@@ -23877,6 +23905,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
+ if (RID_FIRST_ADDR_SPACE <= token->keyword
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ cv_qualifier
+ = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
+
if (!cv_qualifier)
break;
@@ -32898,6 +32931,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
decl_specs->locations[ds] = location;
if (ds == ds_thread)
decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ else if (ds == ds_addr_space)
+ decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
}
else
{
@@ -32930,6 +32965,24 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
error_at (&richloc, "duplicate %qD", token->u.value);
}
}
+ else if (ds == ds_addr_space)
+ {
+ addr_space_t as1 = decl_specs->address_space;
+ addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
+
+ gcc_rich_location richloc (location);
+ richloc.add_fixit_remove ();
+ if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
+ && as1 != as2)
+ error_at (&richloc,
+ "incompatible address space qualifiers %qs and %qs",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
+ error_at (&richloc, "duplicate named address space %qs",
+ c_addr_space_name (as1));
+
+ decl_specs->address_space = as2;
+ }
else
{
static const char *const decl_spec_names[] = {
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 57917de321f..bd901b6e0eb 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -7044,6 +7044,7 @@ invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
switch (TREE_CODE (expr))
{
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
return invalid_tparm_referent_p (type, TREE_OPERAND (expr, 0),
complain);
@@ -17253,6 +17254,7 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
case DYNAMIC_CAST_EXPR:
case IMPLICIT_CONV_EXPR:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
{
tsubst_flags_t tcomplain = complain;
if (code == CAST_EXPR)
@@ -17986,6 +17988,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
case MEM_REF:
case INDIRECT_REF:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case POINTER_PLUS_EXPR:
v = TREE_OPERAND (v, 0);
continue;
@@ -18170,6 +18173,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree &orig_declv,
case MEM_REF:
case INDIRECT_REF:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case POINTER_PLUS_EXPR:
v = TREE_OPERAND (v, 0);
continue;
@@ -23706,12 +23710,21 @@ template_decl_level (tree decl)
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
- int arg_quals = cp_type_quals (arg);
- int parm_quals = cp_type_quals (parm);
+ int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
+
+ /* Try to unify ARG's address space into PARM's address space.
+ If PARM does not have any address space qualifiers (ie., as_parm is 0),
+ there are no constraints on address spaces for this type. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
+ addr_space_t as_common;
+ addr_space_superset (as_arg, as_parm, &as_common);
if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
{
+
/* Although a CVR qualifier is ignored when being applied to a
substituted template parameter ([8.3.2]/1 for example), that
does not allow us to unify "const T" with "int&" because both
@@ -23728,6 +23741,9 @@ check_cv_quals_for_unify (int strict, tree arg, tree parm)
return 0;
}
+ if (!(as_parm == as_common || as_parm == 0))
+ return 0;
+
if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
&& (arg_quals & parm_quals) != parm_quals)
return 0;
@@ -24343,10 +24359,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
arg, parm))
return unify_cv_qual_mismatch (explain_p, parm, arg);
+ int arg_cv_quals = cp_type_quals (arg);
+ int parm_cv_quals = cp_type_quals (parm);
+
+ /* If PARM does not contain any address spaces constraints it can
+ fully match the address space of ARG. However, if PARM contains an
+ address space constraints, it becomes the upper bound. That is,
+ AS_ARG may be promoted to AS_PARM but not the converse. If we
+ ended up here, it means that `check_cv_quals_for_unify' succeeded
+ and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
+ AS_PARM. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
+ addr_space_t as_common = as_parm ? 0 : as_arg;
+
/* Consider the case where ARG is `const volatile int' and
PARM is `const T'. Then, T should be `volatile int'. */
arg = cp_build_qualified_type
(arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
+ int unified_cv =
+ (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
+ | ENCODE_QUAL_ADDR_SPACE (as_common));
+ arg = cp_build_qualified_type (arg, unified_cv, tf_none);
if (arg == error_mark_node)
return unify_invalid (explain_p);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 954c6de2fcd..fc603eb5568 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -421,6 +421,7 @@ cp_stabilize_reference (tree ref)
case PARM_DECL:
case RESULT_DECL:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
case INDIRECT_REF:
@@ -2040,6 +2041,7 @@ strip_typedefs_expr (tree t, bool *remove_attributes, unsigned int flags)
switch (code)
{
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case IMPLICIT_CONV_EXPR:
case DYNAMIC_CAST_EXPR:
case STATIC_CAST_EXPR:
@@ -4179,6 +4181,7 @@ cp_tree_equal (tree t1, tree t2)
case NEW_EXPR:
case BIT_CAST_EXPR:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
return false;
/* Now compare operands as usual. */
@@ -6094,15 +6097,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 5f5fb2a212b..653b015f673 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -693,9 +693,28 @@ composite_pointer_type_r (const op_location_t &location,
return error_mark_node;
result_type = void_type_node;
}
+
+
+ /* If possible merge the address space into the superset of the address
+ spaces of t1 and t2, or raise an error. */
const int q1 = cp_type_quals (pointee1);
const int q2 = cp_type_quals (pointee2);
- const int quals = q1 | q2;
+ addr_space_t as_t1 = DECODE_QUAL_ADDR_SPACE (q1);
+ addr_space_t as_t2 = DECODE_QUAL_ADDR_SPACE (q2);
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as_t1, as_t2, &as_common))
+ {
+ as_common = as_t1;
+ error_at (location, "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ return error_mark_node;
+ }
+
+ const int quals = CLEAR_QUAL_ADDR_SPACE (q1 | q2)
+ | ENCODE_QUAL_ADDR_SPACE (as_common);
result_type = cp_build_qualified_type (result_type,
(quals | (*add_const
? TYPE_QUAL_CONST
@@ -803,10 +822,27 @@ composite_pointer_type (const op_location_t &location,
else
return error_mark_node;
}
+
+ addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
+ addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as_t1, as_t2, &as_common))
+ {
+ as_common = as_t1;
+ error_at (location,
+ "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ }
+ int quals_t1 = cp_type_quals (TREE_TYPE (t1));
+ int quals_t2 = cp_type_quals (TREE_TYPE (t2));
result_type
= cp_build_qualified_type (void_type_node,
- (cp_type_quals (TREE_TYPE (t1))
- | cp_type_quals (TREE_TYPE (t2))));
+ (CLEAR_QUAL_ADDR_SPACE (quals_t1)
+ | CLEAR_QUAL_ADDR_SPACE (quals_t2)
+ | ENCODE_QUAL_ADDR_SPACE (as_common)));
result_type = build_pointer_type (result_type);
/* Merge the attributes. */
attributes = (*targetm.merge_type_attributes) (t1, t2);
@@ -1731,7 +1767,9 @@ comptypes (tree t1, tree t2, int strict)
}
/* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
- top-level qualifiers. */
+ top-level qualifiers, except for named address spaces. If the pointers point
+ to different named addresses spaces, then we must determine if one address
+ space is a subset of the other. */
bool
same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
@@ -6676,10 +6714,34 @@ static tree
pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
tsubst_flags_t complain, tree *instrument_expr)
{
- tree result, inttype;
tree restype = ptrdiff_type_node;
+ tree result, inttype;
+
+ addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
+ addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
tree target_type = TREE_TYPE (ptrtype);
+ /* If the operands point into different address spaces, we need to
+ explicitly convert them to pointers into the common address space
+ before we can subtract the numerical address values. */
+ if (as0 != as1)
+ {
+ addr_space_t as_common;
+ tree common_type;
+
+ if (!addr_space_superset (as0, as1, &as_common))
+ {
+ error_at (loc, "pointers to disjoint address spaces "
+ "%qT and %qT in substraction",
+ TREE_TYPE (op0), TREE_TYPE (op1));
+ return error_mark_node;
+ }
+
+ common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
+ op0 = convert (common_type, op0);
+ op1 = convert (common_type, op1);
+ }
+
if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
return error_mark_node;
@@ -7139,6 +7201,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
switch (TREE_CODE (arg))
{
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
/* We should have handled this above in the lvalue_kind check. */
@@ -8133,6 +8196,41 @@ maybe_warn_about_cast_ignoring_quals (location_t loc, tree type,
"type qualifiers ignored on cast result type");
}
+/* Warns if the cast cross address-space boundaries. */
+static void
+warn_about_cast_crossing_as_boundaries (location_t loc, tree type,
+ tree expr, tsubst_flags_t complain)
+{
+ if (TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE
+ && !NULLPTR_TYPE_P (expr)
+ && (complain & tf_warning))
+ {
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type));
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (expr)));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common))
+ {
+ if (ADDR_SPACE_GENERIC_P (as_from))
+ warning_at (loc, 0, "cast to %qs address space pointer "
+ "from disjoint generic address space pointer",
+ c_addr_space_name (as_to));
+
+ else if (ADDR_SPACE_GENERIC_P (as_to))
+ warning_at (loc, 0, "cast to generic address space pointer "
+ "from disjoint %qs address space pointer",
+ c_addr_space_name (as_from));
+
+ else
+ warning_at (loc, 0, "cast to %qs address space pointer "
+ "from disjoint %qs address space pointer",
+ c_addr_space_name (as_to),
+ c_addr_space_name (as_from));
+ }
+ }
+}
+
/* Convert EXPR (an expression with pointer-to-member type) to TYPE
(another pointer-to-member type in the same hierarchy) and return
the converted expression. If ALLOW_INVERSE_P is permitted, a
@@ -8564,6 +8662,7 @@ build_static_cast (location_t loc, tree type, tree oexpr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
}
if (processing_template_decl)
goto tmpl;
@@ -8886,6 +8985,7 @@ build_reinterpret_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
}
protected_set_expr_location (r, loc);
return r;
@@ -9072,6 +9172,7 @@ build_const_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
}
protected_set_expr_location (r, loc);
return r;
@@ -9181,6 +9282,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
}
return result;
}
@@ -9203,6 +9305,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
/* Non-class rvalues always have cv-unqualified type. */
if (!CLASS_TYPE_P (type))
@@ -11308,6 +11411,30 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
to_more_cv_qualified = true;
}
+ /* Warn about conversions between pointers to disjoint
+ address spaces. */
+ if (TREE_CODE (from) == POINTER_TYPE
+ && TREE_CODE (to) == POINTER_TYPE)
+ {
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common)
+ || as_common != as_to)
+ return false;
+ }
+ else
+ {
+ addr_space_t as_from = TYPE_ADDR_SPACE ((from));
+ addr_space_t as_to = TYPE_ADDR_SPACE ((to));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common)
+ || as_common != as_to)
+ return false;
+ }
+
if (constp > 0)
constp &= TYPE_READONLY (to);
}
diff --git a/gcc/doc/gcc/extensions-to-the-c-language-family/named-address-spaces.rst b/gcc/doc/gcc/extensions-to-the-c-language-family/named-address-spaces.rst
index 44e4cccc3c8..93fca7cbe3c 100644
--- a/gcc/doc/gcc/extensions-to-the-c-language-family/named-address-spaces.rst
+++ b/gcc/doc/gcc/extensions-to-the-c-language-family/named-address-spaces.rst
@@ -10,7 +10,7 @@
Named Address Spaces
********************
-As an extension, GNU C supports named address spaces as
+As an extension, GNU C and GNU C++ support named address spaces as
defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
address spaces in GCC will evolve as the draft technical report
changes. Calling conventions for any target might also change. At
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index 12a58456b39..7227739e3f8 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -1687,7 +1687,7 @@ default_addr_space_legitimize_address (rtx x, rtx oldx, machine_mode mode,
bool
default_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
{
- return (subset == superset);
+ return (subset == 2 && superset == 0) || (subset == superset);
}
/* The default hook for determining if 0 within a named address
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
new file mode 100644
index 00000000000..35701c60637
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
+
+int f (int volatile __seg_fs *a)
+{
+ return *a;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
new file mode 100644
index 00000000000..1daf15a73c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
+
+template <class T>
+int f (T *p) { return *p; }
+int g (__seg_fs int *p) { return *p; }
+__seg_fs int *a;
+int main() { f(a); }
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
new file mode 100644
index 00000000000..4199bf375b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+__seg_fs char a, b, c;
+__seg_fs const int *p;
+static /* gives internal linkage to variable q */
+__seg_fs struct { int a; char b; } * __seg_gs q;
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
new file mode 100644
index 00000000000..783e36aabab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+int __seg_fs * fs1;
+int __seg_fs * fs2;
+int __seg_gs * gs1;
+int __seg_gs * gs2;
+
+int
+main ()
+{
+ fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
+ fs1 - fs2;
+ fs1 - gs2; // { dg-error "pointers to disjoint address spaces .__seg_fs int.. and .__seg_gs int.. in substraction" }
+ // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" "" { target *-*-* } .-1 }
+ fs1 == fs2;
+ fs1 != gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" }
+ fs1 = fs2;
+ fs1 = gs2; // { dg-error "invalid conversion from .__seg_gs int.. to .__seg_fs int.." }
+ fs1 > fs2;
+ fs1 < gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
new file mode 100644
index 00000000000..e87649abb5d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
@@ -0,0 +1,29 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-prune-output "does not allow .register. storage class specifier" }
+int __seg_fs * outer_b;
+
+struct s {
+ __seg_fs int * ok;
+ __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
+};
+
+namespace ns_a
+{
+ int __seg_fs * inner_b;
+
+ template<typename T>
+ int f (T &a) { return a; }
+ int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
+ int h (__seg_fs int *a) { return *a; }
+}
+
+int
+main ()
+{
+ int register __seg_gs reg_gs; // { dg-error ".__seg_gs. specified for variable .reg_gs. with automatic storage" }
+ static __seg_gs int static_gs;
+ __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for variable .auto_fs. with automatic storage" }
+ __seg_fs int *pa = outer_b;
+ __seg_fs int& ra = *ns_a::inner_b;
+ return ns_a::f(ra) + ns_a::f(*pa);
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
new file mode 100644
index 00000000000..ebb6316054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
new file mode 100644
index 00000000000..2e8ee32a885
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-std=gnu++98" }
+
+int
+main ()
+{
+ struct foo {int a; char b[2];} structure;
+ structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
new file mode 100644
index 00000000000..5f64d52885e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs __seg_gs int *a; // { dg-error "incompatible address space qualifiers" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
new file mode 100644
index 00000000000..70dfcce53fa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
+template<typename T, typename U>
+__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
+
+int
+main ()
+{
+ f (fs1, gs1);
+ f (gs1, fs1);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
new file mode 100644
index 00000000000..36c402f5cd2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
@@ -0,0 +1,18 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-require-effective-target c++11 }
+// decltype is only available since c++11
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T> struct strip;
+template<typename T> struct strip<__seg_fs T *> { typedef T type; };
+template<typename T> struct strip<__seg_gs T *> { typedef T type; };
+
+int
+main ()
+{
+ *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
+ // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
new file mode 100644
index 00000000000..a2ffb532087
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+bool f (T __seg_fs * a, U __seg_gs * b)
+{
+ return *(T *) a == *(U *) b;
+ // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 }
+}
+
+int
+main ()
+{
+ return f (fs1, gs1);
+}
diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
new file mode 100644
index 00000000000..ae9f4de0e1f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+template <class T>
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
+ // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
+__seg_fs int *a;
+int main() { f(a); } // { dg-error "no matching" }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
diff --git a/gcc/tree.h b/gcc/tree.h
index a863d2e50e5..b9992e3fca8 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2298,7 +2298,7 @@ extern tree vector_element_bits_tree (const_tree);
/* Encode/decode the named memory support as part of the qualifier. If more
than 8 qualifiers are added, these macros need to be adjusted. */
-#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
+#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
#define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
/* Return all qualifiers except for the address space qualifiers. */
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-11-10 14:08 ` Paul Iannetta
@ 2022-11-10 16:40 ` Georg-Johann Lay
2022-11-14 17:55 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2022-11-10 16:40 UTC (permalink / raw)
To: Paul Iannetta; +Cc: Jason Merrill, gcc-patches
Am 10.11.22 um 15:08 schrieb Paul Iannetta:
> On Thu, Nov 03, 2022 at 02:38:39PM +0100, Georg-Johann Lay wrote:
>>> [PATCH v3] c++: parser - Support for target address spaces in C++
>> 2. Will it work with compound literals?
>> =======================================
>>
>> Currently, the following C code works for target avr:
>>
>> const __flash char *pHallo = (const __flash char[]) { "Hallo" };
>>
>> This is a pointer in RAM (AS0) that holds the address of a string in flash
>> (AS1) and is initialized with that address. Unfortunately, this does not
>> work locally:
>>
>> const __flash char* get_hallo (void)
>> {
>> [static] const __flash char *p2 = (const __flash char[]) { "Hallo2" };
>> return p2;
>> }
>>
>> foo.c: In function 'get_hallo':
>> foo.c: error: compound literal qualified by address-space qualifier
>>
>> Is there any way to make this work now? Would be great!
>>
>
> Currently, I implement the same restrictions as the C front-end, but I
> think that this restriction could be lifted.
Hi Paul,
this would be great. FYI, due to AVR quirks, .rodata is located in RAM.
Reason behind this is that in functions like
char get_letter (const char *c)
{
return *c;
}
there is no means to determine whether get_letter was called with a
const char* or a char*. Accessing flash vs. RAM would require different
instructions, thus .rodata is part of RAM, so that RAM accesses will
work in either case.
The obvious problem is that this wastes RAM. One way out is to define
address space in flash and to pass const __flash char*, where respective
objects are located in flash (.progmem.data in case of avr).
This is fine for objects which the application creates, but there are
also artificial objects like vtables or cswtch tables.
>> 3. Will TARGET_ADDR_SPACE_DIAGNOSE_USAGE still work?
>> ====================================================
>>
>> Currently there is target hook TARGET_ADDR_SPACE_DIAGNOSE_USAGE.
>> I did not see it in your patches, so maybe I just missed it? See
>> https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gccint/Named-Address-Spaces.html#index-TARGET_005fADDR_005fSPACE_005fDIAGNOSE_005fUSAGE
>
> That was a point I overlooked in my previous patch. This will be in
> my new revision where I also add implicit conversion between
> address spaces and also expose TARGET_ADDR_SPACE_CONVERT.
>
>> 4. Will it be possible to put C++ virtual tables in ASs, and how?
>> =================================================================
>
> Currently, I do not allow the declaration of instances of classes in
> an address space, mainly to not have to cope with the handling of the
> this pointer. That is,
>
> __flash Myclass *t;
>
> does not work. Nevertheless, I admit that this is would be nice to
> have.
>
>> One big complaint about avr-g++ is that there is no way to put vtables in
>> flash (address-space 1) and to access them accordingly. How can this be
>> achieved with C++ address spaces?
>
> Do you want only the vtables in the flash address space or do you want
> to be able to have the whole class content.
My question is about vtables, not the bits that represent some object.
vtables are stored independently of objects, usually in .rodata +
comdat. Notice that vtables are read-only and in static storage, even
if objects are neither.
The problem with vtables is that the user has no handle to specify where
to locate them -- and even if, due to AVR quirks, the right instruction
must be used. Thus just putting vtables in flash by means of some
section attribute won't work, only address-spaces can do the trick.
> 1. If you only want the vtables, I think that a target hook called
> at vtable creation would do the trick.
Yes, that would be enough, see https://gcc.gnu.org/PR43745
Johann
> 2. If you want to be able to have pointer to classes in __flash, I
> will need to further the support I have currently implemented to
> support the pointer this qualified with an address space.
> Retrospectively, I think this have to be implemented.
>
> Paul
Would be great if this would work, but I think this can be really
tricky, because it's already tricky for non-class objects.
A user has to specify __flash explicitly, which is quite different to
plain objects. For example, a const int can live in .rodata, but in
cases like
extern int func();
extern const int ival;
const int ival = func();
ival would live in .bss and be initialized at runtime by a static
constructor. Consequently,
const __flash int ival = func();
is invalid code that has to be diagnosed [1], because in the avr case,
__flash means non-volatile memory, which contradicts initialization at
runtime.
So only objects that are TREE_READONLY can go into AS __flash,
TREE_CONST is not enough.
How is this problem addressed? Does this require a new target hook to
diagnose such cases, and does the compiler know at that stage that an
object will be TREE_READONLY?
[1] Notice that in C it's enough to check that __flash is always
accompanied by const, but in C++ this is not more enough as shown in the
example above.
Johann
p.s: I just checked clang v16, it doesn't complain and accepts
nonsensical code like:
extern int func();
extern const __flash int cval;
const __flash int cval = func();
int get_cval()
{
return cval;
}
(clang puts cval into .bss and initializes at startup, but get_cval will
read from flash due to __flash.)
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-11-10 16:40 ` Georg-Johann Lay
@ 2022-11-14 17:55 ` Jason Merrill
2022-11-15 12:15 ` Georg-Johann Lay
2026-06-19 12:15 ` Georg-Johann Lay
0 siblings, 2 replies; 86+ messages in thread
From: Jason Merrill @ 2022-11-14 17:55 UTC (permalink / raw)
To: Georg-Johann Lay, Paul Iannetta; +Cc: gcc-patches
On 11/10/22 06:40, Georg-Johann Lay wrote:
>
>
> Am 10.11.22 um 15:08 schrieb Paul Iannetta:
>> On Thu, Nov 03, 2022 at 02:38:39PM +0100, Georg-Johann Lay wrote:
>>>> [PATCH v3] c++: parser - Support for target address spaces in C++
>>> 2. Will it work with compound literals?
>>> =======================================
>>>
>>> Currently, the following C code works for target avr:
>>>
>>> const __flash char *pHallo = (const __flash char[]) { "Hallo" };
>>>
>>> This is a pointer in RAM (AS0) that holds the address of a string in
>>> flash
>>> (AS1) and is initialized with that address. Unfortunately, this does not
>>> work locally:
>>>
>>> const __flash char* get_hallo (void)
>>> {
>>> [static] const __flash char *p2 = (const __flash char[]) {
>>> "Hallo2" };
>>> return p2;
>>> }
>>>
>>> foo.c: In function 'get_hallo':
>>> foo.c: error: compound literal qualified by address-space qualifier
>>>
>>> Is there any way to make this work now? Would be great!
I don't object to allowing this, but what's the advantage of this
pattern over
static __flash const char p2[] = "Hallo2";
?
>> Currently, I implement the same restrictions as the C front-end, but I
>> think that this restriction could be lifted.
>
> Hi Paul,
>
> this would be great. FYI, due to AVR quirks, .rodata is located in RAM.
> Reason behind this is that in functions like
>
> char get_letter (const char *c)
> {
> return *c;
> }
>
> there is no means to determine whether get_letter was called with a
> const char* or a char*. Accessing flash vs. RAM would require different
> instructions, thus .rodata is part of RAM, so that RAM accesses will
> work in either case.
>
> The obvious problem is that this wastes RAM. One way out is to define
> address space in flash and to pass const __flash char*, where respective
> objects are located in flash (.progmem.data in case of avr).
>
> This is fine for objects which the application creates, but there are
> also artificial objects like vtables or cswtch tables.
>
>>> 3. Will TARGET_ADDR_SPACE_DIAGNOSE_USAGE still work?
>>> ====================================================
>>>
>>> Currently there is target hook TARGET_ADDR_SPACE_DIAGNOSE_USAGE.
>>> I did not see it in your patches, so maybe I just missed it? See
>>> https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gccint/Named-Address-Spaces.html#index-TARGET_005fADDR_005fSPACE_005fDIAGNOSE_005fUSAGE
>>
>> That was a point I overlooked in my previous patch. This will be in
>> my new revision where I also add implicit conversion between
>> address spaces and also expose TARGET_ADDR_SPACE_CONVERT.
>>
>>> 4. Will it be possible to put C++ virtual tables in ASs, and how?
>>> =================================================================
>>
>> Currently, I do not allow the declaration of instances of classes in
>> an address space, mainly to not have to cope with the handling of the
>> this pointer. That is,
>>
>> __flash Myclass *t;
>>
>> does not work. Nevertheless, I admit that this is would be nice to
>> have.
>>
>>> One big complaint about avr-g++ is that there is no way to put vtables in
>>> flash (address-space 1) and to access them accordingly. How can this be
>>> achieved with C++ address spaces?
>>
>> Do you want only the vtables in the flash address space or do you want
>> to be able to have the whole class content.
>
> My question is about vtables, not the bits that represent some object.
> vtables are stored independently of objects, usually in .rodata +
> comdat. Notice that vtables are read-only and in static storage, even
> if objects are neither.
>
> The problem with vtables is that the user has no handle to specify where
> to locate them -- and even if, due to AVR quirks, the right instruction
> must be used. Thus just putting vtables in flash by means of some
> section attribute won't work, only address-spaces can do the trick.
>
>> 1. If you only want the vtables, I think that a target hook called
>> at vtable creation would do the trick.
>
> Yes, that would be enough, see https://gcc.gnu.org/PR43745
As you say there, this would be an ABI change, so there would need to be
a transition strategy. I don't know to what extent AVR users try to use
older compiled code vs. always rebuilding everything.
> Johann
>
>> 2. If you want to be able to have pointer to classes in __flash, I
>> will need to further the support I have currently implemented to
>> support the pointer this qualified with an address space.
>> Retrospectively, I think this have to be implemented.
>>
>> Paul
>
> Would be great if this would work, but I think this can be really
> tricky, because it's already tricky for non-class objects.
Indeed, especially if objects of the same type can live either in flash
or RAM: you'd need 2 or more of each method for the different accesses.
Perhaps via cloning.
Simpler might be to declare that objects of a particular class can only
live in flash.
> A user has to specify __flash explicitly, which is quite different to
> plain objects. For example, a const int can live in .rodata, but in
> cases like
>
> extern int func();
> extern const int ival;
> const int ival = func();
>
> ival would live in .bss and be initialized at runtime by a static
> constructor. Consequently,
>
> const __flash int ival = func();
>
> is invalid code that has to be diagnosed [1], because in the avr case,
> __flash means non-volatile memory, which contradicts initialization at
> runtime.
>
> So only objects that are TREE_READONLY can go into AS __flash,
> TREE_CONST is not enough.
>
> How is this problem addressed? Does this require a new target hook to
> diagnose such cases, and does the compiler know at that stage that an
> object will be TREE_READONLY?
That does sound like a job for a new target hook, if there isn't a
suitable one already.
> [1] Notice that in C it's enough to check that __flash is always
> accompanied by const, but in C++ this is not more enough as shown in the
> example above.
>
> Johann
>
>
> p.s: I just checked clang v16, it doesn't complain and accepts
> nonsensical code like:
>
> extern int func();
>
> extern const __flash int cval;
> const __flash int cval = func();
>
> int get_cval()
> {
> return cval;
> }
>
> (clang puts cval into .bss and initializes at startup, but get_cval will
> read from flash due to __flash.)
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-11-14 17:55 ` Jason Merrill
@ 2022-11-15 12:15 ` Georg-Johann Lay
2026-06-19 12:15 ` Georg-Johann Lay
1 sibling, 0 replies; 86+ messages in thread
From: Georg-Johann Lay @ 2022-11-15 12:15 UTC (permalink / raw)
To: Jason Merrill, Paul Iannetta; +Cc: gcc-patches
Am 14.11.22 um 18:55 schrieb Jason Merrill:
> On 11/10/22 06:40, Georg-Johann Lay wrote:
>>
>>
>> Am 10.11.22 um 15:08 schrieb Paul Iannetta:
>>> On Thu, Nov 03, 2022 at 02:38:39PM +0100, Georg-Johann Lay wrote:
>>>>> [PATCH v3] c++: parser - Support for target address spaces in C++
>>>> 2. Will it work with compound literals?
>>>> =======================================
>>>>
>>>> Currently, the following C code works for target avr:
>>>>
>>>> const __flash char *pHallo = (const __flash char[]) { "Hallo" };
>>>>
>>>> This is a pointer in RAM (AS0) that holds the address of a string in
>>>> flash
>>>> (AS1) and is initialized with that address. Unfortunately, this does
>>>> not
>>>> work locally:
>>>>
>>>> const __flash char* get_hallo (void)
>>>> {
>>>> [static] const __flash char *p2 = (const __flash char[]) {
>>>> "Hallo2" };
>>>> return p2;
>>>> }
>>>>
>>>> foo.c: In function 'get_hallo':
>>>> foo.c: error: compound literal qualified by address-space qualifier
>>>>
>>>> Is there any way to make this work now? Would be great!
>
> I don't object to allowing this, but what's the advantage of this
> pattern over
>
> static __flash const char p2[] = "Hallo2";
>
> ?
Hi Jason.
Take an example that's a bit more complicated, like:
#define FSTR(X) (const __flash char[]) { X }
const __flash char* strings[] =
{
FSTR("cat"),
FSTR("dog"),
FSTR("petrophaga lorioti")
};
This won't work in a function just because GCC rejects it, no matter
whether strings[] itself is in __flash or not. One work around would be to
const __flash char str_cat[] = "cat";
const __flash char str_dog[] = "dog";
const __flash char str_petrophaga_lorioti[] = "petrophaga_lorioti";
const __flash char* strings[] =
{
str_cat,
str_dog,
str_petrophaga_lorioti
};
but anyone would prefer the first alternative.
In a more broader context, code without ASes like
const char* strings[] =
{
"cat",
"dog",
"petrophaga lorioti"
};
that makes perfect sense in C/C++ should also be possible for
address-spaces, no matter whether the pointers and/or strings[] is in
non-generic AS.
Unfortunately, ISO/IEC TR 18037 seems to never have considered such
code, and they mostly are concerned with how code is being accessed, but
not how code is being located.
Johann
>>> Currently, I implement the same restrictions as the C front-end, but I
>>> think that this restriction could be lifted.
>>>> 3. Will TARGET_ADDR_SPACE_DIAGNOSE_USAGE still work?
>>>> ====================================================
>>>>
>>>> Currently there is target hook TARGET_ADDR_SPACE_DIAGNOSE_USAGE.
>>>> I did not see it in your patches, so maybe I just missed it? See
>>>> https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gccint/Named-Address-Spaces.html#index-TARGET_005fADDR_005fSPACE_005fDIAGNOSE_005fUSAGE
>>>
>>> That was a point I overlooked in my previous patch. This will be in
>>> my new revision where I also add implicit conversion between
>>> address spaces and also expose TARGET_ADDR_SPACE_CONVERT.
>>>
>>>> 4. Will it be possible to put C++ virtual tables in ASs, and how?
>>>> =================================================================
>>>
>>> Currently, I do not allow the declaration of instances of classes in
>>> an address space, mainly to not have to cope with the handling of the
>>> this pointer. That is,
>>>
>>> __flash Myclass *t;
>>>
>>> does not work. Nevertheless, I admit that this is would be nice to
>>> have.
>>>
>>>> One big complaint about avr-g++ is that there is no way to put
>>>> vtables in
>>>> flash (address-space 1) and to access them accordingly. How can
>>>> this be
>>>> achieved with C++ address spaces?
>>>
>>> Do you want only the vtables in the flash address space or do you want
>>> to be able to have the whole class content.
>>
>> My question is about vtables, not the bits that represent some object.
>> vtables are stored independently of objects, usually in .rodata +
>> comdat. Notice that vtables are read-only and in static storage, even
>> if objects are neither.
>>
>> The problem with vtables is that the user has no handle to specify
>> where to locate them -- and even if, due to AVR quirks, the right
>> instruction must be used. Thus just putting vtables in flash by means
>> of some section attribute won't work, only address-spaces can do the
>> trick.
>>
>>> 1. If you only want the vtables, I think that a target hook called
>>> at vtable creation would do the trick.
>>
>> Yes, that would be enough, see https://gcc.gnu.org/PR43745
>
> As you say there, this would be an ABI change, so there would need to be
> a transition strategy. I don't know to what extent AVR users try to use
> older compiled code vs. always rebuilding everything.
>
>> Johann
>>
>>> 2. If you want to be able to have pointer to classes in __flash, I
>>> will need to further the support I have currently implemented to
>>> support the pointer this qualified with an address space.
>>> Retrospectively, I think this have to be implemented.
>>>
>>> Paul
>>
>> Would be great if this would work, but I think this can be really
>> tricky, because it's already tricky for non-class objects.
>
> Indeed, especially if objects of the same type can live either in flash
> or RAM: you'd need 2 or more of each method for the different accesses.
> Perhaps via cloning.
>
> Simpler might be to declare that objects of a particular class can only
> live in flash.
>
>> A user has to specify __flash explicitly, which is quite different to
>> plain objects. For example, a const int can live in .rodata, but in
>> cases like
>>
>> extern int func();
>> extern const int ival;
>> const int ival = func();
>>
>> ival would live in .bss and be initialized at runtime by a static
>> constructor. Consequently,
>>
>> const __flash int ival = func();
>>
>> is invalid code that has to be diagnosed [1], because in the avr case,
>> __flash means non-volatile memory, which contradicts initialization at
>> runtime.
>>
>> So only objects that are TREE_READONLY can go into AS __flash,
>> TREE_CONST is not enough.
>>
>> How is this problem addressed? Does this require a new target hook to
>> diagnose such cases, and does the compiler know at that stage that an
>> object will be TREE_READONLY?
>
> That does sound like a job for a new target hook, if there isn't a
> suitable one already.
>
>> [1] Notice that in C it's enough to check that __flash is always
>> accompanied by const, but in C++ this is not more enough as shown in
>> the example above.
>>
>> Johann
>>
>>
>> p.s: I just checked clang v16, it doesn't complain and accepts
>> nonsensical code like:
>>
>> extern int func();
>>
>> extern const __flash int cval;
>> const __flash int cval = func();
>>
>> int get_cval()
>> {
>> return cval;
>> }
>>
>> (clang puts cval into .bss and initializes at startup, but get_cval
>> will read from flash due to __flash.)
>>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2022-11-10 15:42 ` [PATCH v4] " Paul Iannetta
@ 2026-05-15 12:17 ` Thomas Schwinge
2026-05-15 13:11 ` Paul IANNETTA
` (6 more replies)
0 siblings, 7 replies; 86+ messages in thread
From: Thomas Schwinge @ 2026-05-15 12:17 UTC (permalink / raw)
To: Jason Merrill, Patrick Palka, gcc-patches; +Cc: Paul Iannetta
[-- Attachment #1: Type: text/plain, Size: 6551 bytes --]
[Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
other email address: <paul.iannetta@ens-lyon.org>.]
Hi!
I'd like to resume this patch submission here, which is adding support
for named address spaces to GNU C++, as is implemented for GNU C. As far
as I can tell, there wasn't any specific technical reason that this patch
review stalled, back then, in 2022-11? (Jason?)
I've now rebased this onto recent GCC trunk, see attached
'0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
just a few merge conflicts that I had to fix up (nothing serious), and
I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
The 'gcc/targhooks.cc:default_addr_space_subset_p' change that appeared
in this v4:
bool
default_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
{
- return (subset == superset);
+ return (subset == 2 && superset == 0) || (subset == superset);
}
... is entirely unmotivated, as far as I can tell: probably a local hack
for testing, that sneaked into the patch posted? This change regresses
'gcc.target/i386/addr-space-typeck-1.c' (notice: C test case), which
again PASSes with that change undone.
The new test case 'g++.dg/template/spec-addr-space.C' needs minor
adjusting per changed GCC diagnostics (not related to the new named
addess space support):
@@ -1,8 +1,9 @@
// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
template <class T>
-int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate 1: 'template<class T> int f.__seg_gs T\*." }
// { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
__seg_fs int *a;
int main() { f(a); } // { dg-error "no matching" }
-// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
+// { dg-note "there is 1 candidate" "" { target *-*-* } .-1 }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-2 }
With that, all new test cases PASS, and no regressions.
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), and I'll
work on test cases some more. I'll also examine if, since then, any new
code has appeared where 'ADDR_SPACE_CONVERT_EXPR' needs to be handled.
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.
Full-quote of Paul's email from back then:
On 2022-11-10T16:42:22+0100, Paul Iannetta <piannetta@kalrayinc.com> wrote:
> Hi,
>
> 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, and warnings; and expose the same hooks as
> the C front-end does. The C++ front-end produces as much warnings
> that the C front-end, however they are sometimes a bit less
> informative (especially so, when passing arguments to functions).
>
> I also address the following points:
>
> 1. The handling of the register storage class is grouped with the
> other variables with automatic storage. This incurs a slight
> dis-alignment since you cannot have global register variables do not
> trigger an error, only a warning.
>
> 2. In template unification, I maintain that we don't want any
> changes to address spaces whatsoever during the unification process,
> hence ignoring the strict flag. Nevertheless, we allow implicit
> conversion, and I have verified that, indeed,
>
>
> template <class T> void f(T **);
> struct A {
> template <class T> operator T*__seg_fs*();
> };
> int main()
> {
> f((void* __seg_fs *)0); // (1): void*__seg_fs* -> void** should be OK
> void (*p)(void * __seg_fs *) = f; // (2): error
> }
>
> works as intended. That is, (1) works if we set __seg_fs as a subspace
> of the generic address space, and (2) is always an error.
>
> 3. In template unification, when unifying __as1 T = __as2 U we want
> to unify to the __as1 at most, never to __as2 at most, because the
> function requiring __as1 T may want to mix address space from the
> bigger address space, therefore I think that we want to have the
> smaller address-space to be unified into the bigger of the two.
>
> 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.
>
> 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.
>
> 6. Currently, I do not handle classes. I see what I can do in a
> further revision and maybe add a target hook to hand down to targets
> the choice of the address space of the vtable.
>
> 7. This can't be added as a test case, but I checked that:
>
> // In this test case, __seg_gs is a subset of the generic address
> // space.
>
> int f (int *);
> int main ()
> {
> int __seg_fs *pa;
> int __seg_gs *pb;
> int *pc;
> pa = (__seg_fs int *) pb; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint ‘__seg_gs’ address space pointer
> pa = (int __seg_fs *) pc; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint generic address space pointer
> pa = pb; return *pa; // error: invalid conversion from ‘__seg_gs int*’ to ‘__seg_fs int*’
> pc = pb; return *pc; // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
> pb = pc; return *pb; // error: invalid conversion from ‘int*’ to ‘__seg_gs int*’
> pc = pa; return *pb; // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
> return f (pb); // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
> // return f (pa); // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
> // note: initializing argument 1 of ‘int f(int*)’
> }
>
> Thanks,
> Paul
>
> Rebased on current trunk, bootstrapped and regtested.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-c-parser-Support-for-target-address-spaces-in-C.patch --]
[-- Type: text/x-diff, Size: 45944 bytes --]
From be22aa0b156c2da4bcdb270b38c7c4e31e451da9 Mon Sep 17 00:00:00 2001
From: Paul Iannetta <piannetta@kalrayinc.com>
Date: Thu, 10 Nov 2022 16:42:22 +0100
Subject: [PATCH] c++: parser - Support for target address spaces in C++
gcc/cp/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* 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.
* cp-tree.h (enum cp_decl_spec): Add a new decl spec to handle
address spaces.
(struct cp_decl_specifier_seq): Likewise.
* decl.cc (get_type_quals): Add address space support.
(check_tag_decl): Likewise.
(grokdeclarator): Likewise.
* class.cc (fixed_type_or_null): Add a case for
ADDR_SPACE_CONVERT_EXPR.
* constexpr.cc (cxx_eval_constant_expression): Likewise.
(potential_constant_expression_1): Likewise.
* cp-gimplify.cc (cp_fold): Likewise.
* error.cc (dump_expr): Likewise.
* expr.cc (mark_use): Likewise.
* tree.cc (cp_stabilize_reference): Likewise.
(strip_typedefs_expr): Likewise.
(cp_tree_equal): Likewise.
(mark_exp_read): Likewise.
* mangle.cc (write_CV_qualifiers_for_type): Mangle address
spaces using the extended type qualifier scheme.
* parser.cc (cp_lexer_get_preprocessor_token): Add a call to
targetm.addr_space.diagnose_usage when lexing an address space
token.
(cp_parser_postfix_expression): Prevent the use of address
spaces with compound literals.
(cp_parser_dot_deref_incomplete): Add a case for
ADDR_SPACE_CONVERT_EXPR.
(cp_parser_type_specifier): Add address space support.
(cp_parser_cv_qualifier_seq_opt): Likewise.
(set_and_check_decl_spec_loc): Likewise.
* pt.cc (invalid_tparm_referent_p): Add a case for
ADDR_SPACE_CONVERT_EXPR.
(tsubst_copy): Likewise.
(tsubst_omp_clauses): Likewise.
(tsubst_omp_for_iterator): Likewise.
(check_cv_quals_for_unify): Add address space support.
(unify): Likewise.
* typeck.cc (composite_pointer_type_r): Likewise.
(pointer_diff): Likewise.
(cp_build_addr_expr_1): Add a case for ADDR_SPACE_CONVERT_EXPR.
(warn_about_cast_crossing_as_boundaries): New function.
(build_static_cast): Add address space support.
(build_reinterpret_cast): Add address space support.
(build_const_cast): Add address space support.
(cp_build_c_cast): Add address space support.
(comp_ptr_ttypes_real): Add address space support.
gcc/c/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* c-decl.cc (r_register_addr_space): Remove.
* c-typeck.cc: (addr_space_superset): Move to gcc/c/c-common.h
gcc/c-family/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* c-common.cc (addr_space_superset): Imported from c-decl.cc
(c_register_addr_space): Imported from gcc/c/c-typeck.cc
* c-common.h: Remove the FIXME.
(addr_space_superset): New declaration.
gcc/testsuite/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* g++.dg/abi/mangle-addr-space1.C: New test.
* g++.dg/abi/mangle-addr-space2.C: New test.
* g++.dg/ext/addr-space-decl.C: New test.
* g++.dg/ext/addr-space-ops.C: New test.
* g++.dg/ext/addr-space-ref.C: New test.
* g++.dg/parse/addr-space.C: New test.
* g++.dg/parse/addr-space1.C: New test.
* g++.dg/parse/addr-space2.C: New test.
* g++.dg/template/addr-space-overload.C: New test.
* g++.dg/template/addr-space-strip1.C: New test.
* g++.dg/template/addr-space-strip2.C: New test.
* g++.dg/template/spec-addr-space.C: New test.
gcc/ChangeLog:
2022-11-09 Paul Iannetta <piannetta@kalray.eu>
* doc/.../named-address-spaces.rst: Add C++ support.
* targhooks.cc (default_addr_space_subset_p):
* tree.h (ENCODE_QUAL_ADDR_SPACE): Missing parentheses.
---
gcc/c-family/c-common.cc | 46 ++++++
gcc/c-family/c-common.h | 6 +-
gcc/c/c-decl.cc | 19 ---
gcc/c/c-typeck.cc | 26 ----
gcc/cp/call.cc | 25 ++++
gcc/cp/class.cc | 1 +
gcc/cp/constexpr.cc | 2 +
gcc/cp/cp-gimplify.cc | 1 +
gcc/cp/cp-tree.h | 3 +
gcc/cp/decl.cc | 49 +++++++
gcc/cp/error.cc | 1 +
gcc/cp/expr.cc | 2 +
gcc/cp/mangle.cc | 8 +
gcc/cp/parser.cc | 53 +++++++
gcc/cp/pt.cc | 37 ++++-
gcc/cp/tree.cc | 12 +-
gcc/cp/typeck.cc | 137 +++++++++++++++++-
gcc/doc/extend.texi | 2 +-
gcc/targhooks.cc | 2 +-
gcc/testsuite/g++.dg/abi/mangle-addr-space1.C | 9 ++
gcc/testsuite/g++.dg/abi/mangle-addr-space2.C | 9 ++
gcc/testsuite/g++.dg/ext/addr-space-decl.C | 5 +
gcc/testsuite/g++.dg/ext/addr-space-ops.C | 21 +++
gcc/testsuite/g++.dg/ext/addr-space-ref.C | 29 ++++
gcc/testsuite/g++.dg/parse/addr-space.C | 9 ++
gcc/testsuite/g++.dg/parse/addr-space1.C | 10 ++
gcc/testsuite/g++.dg/parse/addr-space2.C | 9 ++
.../g++.dg/template/addr-space-overload.C | 17 +++
.../g++.dg/template/addr-space-strip1.C | 18 +++
.../g++.dg/template/addr-space-strip2.C | 17 +++
.../g++.dg/template/spec-addr-space.C | 8 +
gcc/tree.h | 2 +-
32 files changed, 528 insertions(+), 67 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-decl.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ops.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ref.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space1.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space2.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-overload.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip1.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip2.C
create mode 100644 gcc/testsuite/g++.dg/template/spec-addr-space.C
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 5acb221d31f..7940d5af720 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -658,6 +658,33 @@ c_addr_space_name (addr_space_t as)
return IDENTIFIER_POINTER (ridpointers [rid]);
}
+/* Return true if between two named address spaces, whether there is a superset
+ named address space that encompasses both address spaces. If there is a
+ superset, return which address space is the superset. */
+
+bool
+addr_space_superset (addr_space_t as1, addr_space_t as2,
+ addr_space_t * common)
+{
+ if (as1 == as2)
+ {
+ *common = as1;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as1, as2))
+ {
+ *common = as2;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as2, as1))
+ {
+ *common = as1;
+ return true;
+ }
+ else
+ return false;
+}
+
/* Push current bindings for the function name VAR_DECLS. */
void
@@ -2894,6 +2921,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ TREE_LANG_FLAG_0 (id) = 1;
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 89517e2a80c..68ee08f60c7 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -840,12 +840,11 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
+extern const char *c_addr_space_name (addr_space_t as);
+extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
extern int names_builtin_p (const char *);
@@ -996,6 +995,7 @@ extern bool c_common_init (void);
extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 8a894de3a1b..763aab6291b 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -13856,25 +13856,6 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
-}
-
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index aa368a6e95a..c7f19a4efe1 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -314,32 +314,6 @@ c_type_promotes_to (tree type)
return type;
}
-/* Return true if between two named address spaces, whether there is a superset
- named address space that encompasses both address spaces. If there is a
- superset, return which address space is the superset. */
-
-static bool
-addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
-{
- if (as1 == as2)
- {
- *common = as1;
- return true;
- }
- else if (targetm.addr_space.subset_p (as1, as2))
- {
- *common = as2;
- return true;
- }
- else if (targetm.addr_space.subset_p (as2, as1))
- {
- *common = as1;
- return true;
- }
- else
- return false;
-}
-
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 92c3a23ab8d..09a44be0477 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -8822,6 +8822,31 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
complain);
else if (t->kind == ck_identity)
break;
+ else if (t->kind == ck_ptr
+ && INDIRECT_TYPE_P (TREE_TYPE (expr))
+ && INDIRECT_TYPE_P (totype))
+ {
+ tree from = TREE_TYPE (TREE_TYPE (expr));
+ tree to = TREE_TYPE (totype);
+
+ int from_full_quals = cp_type_quals (from);
+ int to_full_quals = cp_type_quals (to);
+ int from_quals = CLEAR_QUAL_ADDR_SPACE (from_full_quals);
+ int to_quals = CLEAR_QUAL_ADDR_SPACE (to_full_quals);
+ addr_space_t as_from = DECODE_QUAL_ADDR_SPACE (from_full_quals);
+ addr_space_t as_to = DECODE_QUAL_ADDR_SPACE (to_full_quals);
+ if (same_type_ignoring_top_level_qualifiers_p (from, to)
+ && from_quals == to_quals && as_from != as_to)
+ {
+ addr_space_t as_common;
+
+ /* If AS_FROM can be converted to AS_TO fold convert to force the
+ selection of ADDR_SPACE_CONVERT_EXPR instead of NOP_EXPR. */
+ if (addr_space_superset (as_from, as_to, &as_common)
+ && as_common == as_to)
+ return cp_fold_convert (totype, expr);
+ }
+ }
}
if (!complained && stub_object_p)
{
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 8beeb797a08..58344aaff4d 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -8437,6 +8437,7 @@ fixed_type_or_null (tree instance, int *nonnull, int *cdtorp)
return NULL_TREE;
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
return RECUR (TREE_OPERAND (instance, 0));
case ADDR_EXPR:
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index a593b66dd41..427b5430101 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -10101,6 +10101,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
/* FALLTHROUGH. */
case CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
case UNARY_PLUS_EXPR:
{
tree oldop = TREE_OPERAND (t, 0);
@@ -12208,6 +12209,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
/* FALLTHRU */
case CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
/* -- a reinterpret_cast. FIXME not implemented, and this rule
may change to something more specific to type-punning (DR 1312). */
{
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 5bcf5168970..db8c8c1457c 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -3392,6 +3392,7 @@ cp_fold (tree x, fold_flags_t flags)
/* FALLTHRU */
case NON_LVALUE_EXPR:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
if (VOID_TYPE_P (TREE_TYPE (x)))
{
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ff8fcca72fe..2040a9851a7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6877,6 +6877,7 @@ enum cp_decl_spec {
ds_constinit,
ds_consteval,
ds_this, /* Index of last element of decl_spec_names. */
+ ds_addr_space,
ds_thread,
ds_type_spec,
ds_redefined_builtin_type_spec,
@@ -6913,6 +6914,8 @@ struct cp_decl_specifier_seq {
cp_storage_class storage_class;
/* For the __intN declspec, this stores the index into the int_n_* arrays. */
int int_n_idx;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
/* True iff TYPE_SPEC defines a class or enum. */
BOOL_BITFIELD type_definition_p : 1;
/* True iff multiple types were (erroneously) specified for this
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 679a8007ca6..e3b824a4e50 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -6270,6 +6270,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
return type_quals;
}
@@ -6392,6 +6394,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
error_at (declspecs->locations[ds_restrict],
"%<__restrict%> can only be specified for objects and "
"functions");
+ else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ error_at (declspecs->locations[ds_addr_space],
+ "address space can only be specified for objects and "
+ "functions");
else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
error_at (declspecs->locations[ds_thread],
"%<__thread%> can only be specified for objects "
@@ -17139,6 +17145,49 @@ grokdeclarator (const cp_declarator *declarator,
&& (!VAR_P (decl) || !DECL_TEMPLATE_INSTANTIATED (decl)))
cp_apply_type_quals_to_decl (type_quals, decl);
+ /* Warn about address space used for things other than static memory or
+ pointers. */
+ addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
+ if (!ADDR_SPACE_GENERIC_P (address_space))
+ {
+ if (VAR_P (decl) && decl_context == NORMAL)
+ {
+ duration_kind dk = decl_storage_duration (decl);
+ switch (dk)
+ {
+ case dk_auto:
+ if (! toplevel_bindings_p ())
+ error ("%qs specified for variable %qs with automatic storage",
+ c_addr_space_name (address_space), name);
+ break;
+ case dk_static:
+ case dk_thread:
+ case dk_dynamic:
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
+ {
+ if (name)
+ error ("%qs specified for parameter %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for unnamed parameter",
+ c_addr_space_name (address_space));
+ }
+ else if (decl_context == FIELD)
+ {
+ if (name)
+ error ("%qs specified for structure field %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for structure field",
+ c_addr_space_name (address_space));
+ }
+ }
+
return decl;
}
}
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index e7121dc1017..cd24d74a883 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2843,6 +2843,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
CASE_CONVERT:
case IMPLICIT_CONV_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
case EXCESS_PRECISION_EXPR:
{
tree op = TREE_OPERAND (t, 0);
diff --git a/gcc/cp/expr.cc b/gcc/cp/expr.cc
index 4d017d530ef..39585b3476f 100644
--- a/gcc/cp/expr.cc
+++ b/gcc/cp/expr.cc
@@ -225,6 +225,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
}
gcc_fallthrough ();
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
recurse_op[0] = true;
break;
@@ -386,6 +387,7 @@ mark_exp_read (tree exp)
case INDIRECT_REF:
case FLOAT_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
case PREINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTINCREMENT_EXPR:
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 6e6b16d389d..f338fad386f 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2893,6 +2893,14 @@ write_CV_qualifiers_for_type (const tree type)
array. */
cp_cv_quals quals = TYPE_QUALS (type);
+ if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
+ {
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 449ed491dd8..d72ea1daa2e 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
#define INCLUDE_STRING
#include "system.h"
#include "coretypes.h"
+#include "target.h"
#include "cp-tree.h"
#include "c-family/c-common.h"
#include "timevar.h"
@@ -1151,6 +1152,14 @@ cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token)
token->type = CPP_KEYWORD;
/* Record which keyword. */
token->keyword = C_RID_CODE (token->u.value);
+
+ if (token->keyword >= RID_FIRST_ADDR_SPACE
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ {
+ addr_space_t as
+ = (addr_space_t) (token->keyword - RID_FIRST_ADDR_SPACE);
+ targetm.addr_space.diagnose_usage (as, token->location);
+ }
}
else
{
@@ -8879,6 +8888,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
postfix_expression = error_mark_node;
break;
}
+ if (type != error_mark_node
+ && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
+ && current_function_decl)
+ {
+ error
+ ("compound literal qualified by address-space "
+ "qualifier");
+ type = error_mark_node;
+ }
/* Form the representation of the compound-literal. */
postfix_expression
= finish_compound_literal (type, initializer,
@@ -9470,6 +9488,7 @@ cp_parser_dot_deref_incomplete (tree *scope, cp_expr *postfix_expression,
case STATIC_CAST_EXPR:
case DYNAMIC_CAST_EXPR:
case IMPLICIT_CONV_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
case NON_LVALUE_EXPR:
kind = diagnostics::kind::error;
@@ -22467,6 +22486,14 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+ if (RID_FIRST_ADDR_SPACE <= keyword
+ && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ }
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -27402,6 +27429,7 @@ cp_parser_ptr_operator (cp_parser* parser,
GNU Extension:
cv-qualifier:
+ address-space-qualifier
__restrict__
Returns a bitmask representing the cv-qualifiers. */
@@ -27438,6 +27466,11 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
+ if (RID_FIRST_ADDR_SPACE <= token->keyword
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ cv_qualifier
+ = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
+
if (!cv_qualifier)
break;
@@ -37583,6 +37616,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
decl_specs->locations[ds] = location;
if (ds == ds_thread)
decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ else if (ds == ds_addr_space)
+ decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
}
else
{
@@ -37615,6 +37650,24 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
error_at (&richloc, "duplicate %qD", token->u.value);
}
}
+ else if (ds == ds_addr_space)
+ {
+ addr_space_t as1 = decl_specs->address_space;
+ addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
+
+ gcc_rich_location richloc (location);
+ richloc.add_fixit_remove ();
+ if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
+ && as1 != as2)
+ error_at (&richloc,
+ "incompatible address space qualifiers %qs and %qs",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
+ error_at (&richloc, "duplicate named address space %qs",
+ c_addr_space_name (as1));
+
+ decl_specs->address_space = as2;
+ }
else
{
/* These correspond to cp-tree.h:cp_decl_spec,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 5d97ca7997f..a0eab25923a 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -7380,6 +7380,7 @@ invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
switch (TREE_CODE (expr))
{
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
return invalid_tparm_referent_p (type, TREE_OPERAND (expr, 0),
complain);
@@ -18889,6 +18890,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
case MEM_REF:
case INDIRECT_REF:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case POINTER_PLUS_EXPR:
v = TREE_OPERAND (v, 0);
continue;
@@ -19169,6 +19171,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree &orig_declv,
case MEM_REF:
case INDIRECT_REF:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case POINTER_PLUS_EXPR:
v = TREE_OPERAND (v, 0);
continue;
@@ -25688,12 +25691,21 @@ template_decl_level (tree decl)
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
- int arg_quals = cp_type_quals (arg);
- int parm_quals = cp_type_quals (parm);
+ int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
+
+ /* Try to unify ARG's address space into PARM's address space.
+ If PARM does not have any address space qualifiers (ie., as_parm is 0),
+ there are no constraints on address spaces for this type. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
+ addr_space_t as_common;
+ addr_space_superset (as_arg, as_parm, &as_common);
if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
{
+
/* Although a CVR qualifier is ignored when being applied to a
substituted template parameter ([8.3.2]/1 for example), that
does not allow us to unify "const T" with "int&" because both
@@ -25710,6 +25722,9 @@ check_cv_quals_for_unify (int strict, tree arg, tree parm)
return 0;
}
+ if (!(as_parm == as_common || as_parm == 0))
+ return 0;
+
if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
&& (arg_quals & parm_quals) != parm_quals)
return 0;
@@ -26334,10 +26349,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
arg, parm))
return unify_cv_qual_mismatch (explain_p, parm, arg);
+ int arg_cv_quals = cp_type_quals (arg);
+ int parm_cv_quals = cp_type_quals (parm);
+
+ /* If PARM does not contain any address spaces constraints it can
+ fully match the address space of ARG. However, if PARM contains an
+ address space constraints, it becomes the upper bound. That is,
+ AS_ARG may be promoted to AS_PARM but not the converse. If we
+ ended up here, it means that `check_cv_quals_for_unify' succeeded
+ and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
+ AS_PARM. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
+ addr_space_t as_common = as_parm ? 0 : as_arg;
+
/* Consider the case where ARG is `const volatile int' and
PARM is `const T'. Then, T should be `volatile int'. */
arg = cp_build_qualified_type
(arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
+ int unified_cv =
+ (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
+ | ENCODE_QUAL_ADDR_SPACE (as_common));
+ arg = cp_build_qualified_type (arg, unified_cv, tf_none);
if (arg == error_mark_node)
return unify_invalid (explain_p);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 26e12f848c6..67a4a85942c 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -519,6 +519,7 @@ cp_stabilize_reference (tree ref)
case PARM_DECL:
case RESULT_DECL:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
case INDIRECT_REF:
@@ -2173,6 +2174,7 @@ strip_typedefs_expr (tree t, bool *remove_attributes, unsigned int flags)
switch (code)
{
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case IMPLICIT_CONV_EXPR:
case DYNAMIC_CAST_EXPR:
case STATIC_CAST_EXPR:
@@ -4465,6 +4467,7 @@ cp_tree_equal (tree t1, tree t2)
case NEW_EXPR:
case BIT_CAST_EXPR:
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
return false;
/* Now compare operands as usual. */
@@ -6828,15 +6831,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index dcc35ae28a2..017390b8757 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -719,9 +719,28 @@ composite_pointer_type_r (const op_location_t &location,
return error_mark_node;
result_type = void_type_node;
}
+
+
+ /* If possible merge the address space into the superset of the address
+ spaces of t1 and t2, or raise an error. */
const int q1 = cp_type_quals (pointee1);
const int q2 = cp_type_quals (pointee2);
- const int quals = q1 | q2;
+ addr_space_t as_t1 = DECODE_QUAL_ADDR_SPACE (q1);
+ addr_space_t as_t2 = DECODE_QUAL_ADDR_SPACE (q2);
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as_t1, as_t2, &as_common))
+ {
+ as_common = as_t1;
+ error_at (location, "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ return error_mark_node;
+ }
+
+ const int quals = CLEAR_QUAL_ADDR_SPACE (q1 | q2)
+ | ENCODE_QUAL_ADDR_SPACE (as_common);
result_type = cp_build_qualified_type (result_type,
(quals | (*add_const
? TYPE_QUAL_CONST
@@ -829,10 +848,27 @@ composite_pointer_type (const op_location_t &location,
else
return error_mark_node;
}
+
+ addr_space_t as_t1 = TYPE_ADDR_SPACE (t1);
+ addr_space_t as_t2 = TYPE_ADDR_SPACE (t2);
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as_t1, as_t2, &as_common))
+ {
+ as_common = as_t1;
+ error_at (location,
+ "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ }
+ int quals_t1 = cp_type_quals (TREE_TYPE (t1));
+ int quals_t2 = cp_type_quals (TREE_TYPE (t2));
result_type
= cp_build_qualified_type (void_type_node,
- (cp_type_quals (TREE_TYPE (t1))
- | cp_type_quals (TREE_TYPE (t2))));
+ (CLEAR_QUAL_ADDR_SPACE (quals_t1)
+ | CLEAR_QUAL_ADDR_SPACE (quals_t2)
+ | ENCODE_QUAL_ADDR_SPACE (as_common)));
result_type = build_pointer_type (result_type);
/* Merge the attributes. */
attributes = (*targetm.merge_type_attributes) (t1, t2);
@@ -1781,7 +1817,9 @@ comptypes (tree t1, tree t2, int strict)
}
/* Returns nonzero iff TYPE1 and TYPE2 are the same type, ignoring
- top-level qualifiers. */
+ top-level qualifiers, except for named address spaces. If the pointers point
+ to different named addresses spaces, then we must determine if one address
+ space is a subset of the other. */
bool
same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
@@ -7101,10 +7139,34 @@ static tree
pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
tsubst_flags_t complain, tree *instrument_expr)
{
- tree result, inttype;
tree restype = ptrdiff_type_node;
+ tree result, inttype;
+
+ addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
+ addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
tree target_type = TREE_TYPE (ptrtype);
+ /* If the operands point into different address spaces, we need to
+ explicitly convert them to pointers into the common address space
+ before we can subtract the numerical address values. */
+ if (as0 != as1)
+ {
+ addr_space_t as_common;
+ tree common_type;
+
+ if (!addr_space_superset (as0, as1, &as_common))
+ {
+ error_at (loc, "pointers to disjoint address spaces "
+ "%qT and %qT in substraction",
+ TREE_TYPE (op0), TREE_TYPE (op1));
+ return error_mark_node;
+ }
+
+ common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
+ op0 = convert (common_type, op0);
+ op1 = convert (common_type, op1);
+ }
+
if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
return error_mark_node;
@@ -7582,6 +7644,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
switch (TREE_CODE (arg))
{
CASE_CONVERT:
+ case ADDR_SPACE_CONVERT_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
/* We should have handled this above in the lvalue_kind check. */
@@ -8606,6 +8669,41 @@ maybe_warn_about_cast_ignoring_quals (location_t loc, tree type,
"type qualifiers ignored on cast result type");
}
+/* Warns if the cast cross address-space boundaries. */
+static void
+warn_about_cast_crossing_as_boundaries (location_t loc, tree type,
+ tree expr, tsubst_flags_t complain)
+{
+ if (TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE
+ && !NULLPTR_TYPE_P (expr)
+ && (complain & tf_warning))
+ {
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type));
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (expr)));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common))
+ {
+ if (ADDR_SPACE_GENERIC_P (as_from))
+ warning_at (loc, 0, "cast to %qs address space pointer "
+ "from disjoint generic address space pointer",
+ c_addr_space_name (as_to));
+
+ else if (ADDR_SPACE_GENERIC_P (as_to))
+ warning_at (loc, 0, "cast to generic address space pointer "
+ "from disjoint %qs address space pointer",
+ c_addr_space_name (as_from));
+
+ else
+ warning_at (loc, 0, "cast to %qs address space pointer "
+ "from disjoint %qs address space pointer",
+ c_addr_space_name (as_to),
+ c_addr_space_name (as_from));
+ }
+ }
+}
+
/* Convert EXPR (an expression with pointer-to-member type) to TYPE
(another pointer-to-member type in the same hierarchy) and return
the converted expression. If ALLOW_INVERSE_P is permitted, a
@@ -9042,6 +9140,7 @@ build_static_cast (location_t loc, tree type, tree oexpr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
}
if (processing_template_decl)
goto tmpl;
@@ -9365,6 +9464,7 @@ build_reinterpret_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
}
protected_set_expr_location (r, loc);
return r;
@@ -9551,6 +9651,7 @@ build_const_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
}
protected_set_expr_location (r, loc);
return r;
@@ -9660,6 +9761,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
}
else if (complain & tf_error)
build_const_cast_1 (loc, type, value, tf_error, &valid_p);
@@ -9684,6 +9786,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ warn_about_cast_crossing_as_boundaries (loc, type, expr, complain);
/* Non-class rvalues always have cv-unqualified type. */
if (!CLASS_TYPE_P (type))
@@ -11861,6 +11964,30 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
to_more_cv_qualified = true;
}
+ /* Warn about conversions between pointers to disjoint
+ address spaces. */
+ if (TREE_CODE (from) == POINTER_TYPE
+ && TREE_CODE (to) == POINTER_TYPE)
+ {
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common)
+ || as_common != as_to)
+ return false;
+ }
+ else
+ {
+ addr_space_t as_from = TYPE_ADDR_SPACE ((from));
+ addr_space_t as_to = TYPE_ADDR_SPACE ((to));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common)
+ || as_common != as_to)
+ return false;
+ }
+
if (constp > 0)
constp &= TYPE_READONLY (to);
}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 47e0865c676..6a9d285eb41 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1375,7 +1375,7 @@ initializer expressions and issues a warning.
@section Named Address Spaces
@cindex Named Address Spaces
-As an extension, GNU C supports named address spaces as
+As an extension, GNU C and GNU C++ support named address spaces as
defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
address spaces in GCC will evolve as the draft technical report
changes. Calling conventions for any target might also change. At
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index 5f1eac75122..0ac01f551f1 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -1768,7 +1768,7 @@ default_addr_space_legitimize_address (rtx x, rtx oldx, machine_mode mode,
bool
default_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
{
- return (subset == superset);
+ return (subset == 2 && superset == 0) || (subset == superset);
}
/* The default hook for determining if 0 within a named address
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
new file mode 100644
index 00000000000..35701c60637
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
+
+int f (int volatile __seg_fs *a)
+{
+ return *a;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
new file mode 100644
index 00000000000..1daf15a73c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
+
+template <class T>
+int f (T *p) { return *p; }
+int g (__seg_fs int *p) { return *p; }
+__seg_fs int *a;
+int main() { f(a); }
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
new file mode 100644
index 00000000000..4199bf375b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+__seg_fs char a, b, c;
+__seg_fs const int *p;
+static /* gives internal linkage to variable q */
+__seg_fs struct { int a; char b; } * __seg_gs q;
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
new file mode 100644
index 00000000000..783e36aabab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+int __seg_fs * fs1;
+int __seg_fs * fs2;
+int __seg_gs * gs1;
+int __seg_gs * gs2;
+
+int
+main ()
+{
+ fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
+ fs1 - fs2;
+ fs1 - gs2; // { dg-error "pointers to disjoint address spaces .__seg_fs int.. and .__seg_gs int.. in substraction" }
+ // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" "" { target *-*-* } .-1 }
+ fs1 == fs2;
+ fs1 != gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" }
+ fs1 = fs2;
+ fs1 = gs2; // { dg-error "invalid conversion from .__seg_gs int.. to .__seg_fs int.." }
+ fs1 > fs2;
+ fs1 < gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
new file mode 100644
index 00000000000..e87649abb5d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
@@ -0,0 +1,29 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-prune-output "does not allow .register. storage class specifier" }
+int __seg_fs * outer_b;
+
+struct s {
+ __seg_fs int * ok;
+ __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
+};
+
+namespace ns_a
+{
+ int __seg_fs * inner_b;
+
+ template<typename T>
+ int f (T &a) { return a; }
+ int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
+ int h (__seg_fs int *a) { return *a; }
+}
+
+int
+main ()
+{
+ int register __seg_gs reg_gs; // { dg-error ".__seg_gs. specified for variable .reg_gs. with automatic storage" }
+ static __seg_gs int static_gs;
+ __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for variable .auto_fs. with automatic storage" }
+ __seg_fs int *pa = outer_b;
+ __seg_fs int& ra = *ns_a::inner_b;
+ return ns_a::f(ra) + ns_a::f(*pa);
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
new file mode 100644
index 00000000000..ebb6316054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
new file mode 100644
index 00000000000..2e8ee32a885
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-std=gnu++98" }
+
+int
+main ()
+{
+ struct foo {int a; char b[2];} structure;
+ structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
new file mode 100644
index 00000000000..5f64d52885e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs __seg_gs int *a; // { dg-error "incompatible address space qualifiers" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
new file mode 100644
index 00000000000..70dfcce53fa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
+template<typename T, typename U>
+__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
+
+int
+main ()
+{
+ f (fs1, gs1);
+ f (gs1, fs1);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
new file mode 100644
index 00000000000..36c402f5cd2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
@@ -0,0 +1,18 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-require-effective-target c++11 }
+// decltype is only available since c++11
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T> struct strip;
+template<typename T> struct strip<__seg_fs T *> { typedef T type; };
+template<typename T> struct strip<__seg_gs T *> { typedef T type; };
+
+int
+main ()
+{
+ *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
+ // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
new file mode 100644
index 00000000000..a2ffb532087
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+bool f (T __seg_fs * a, U __seg_gs * b)
+{
+ return *(T *) a == *(U *) b;
+ // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 }
+}
+
+int
+main ()
+{
+ return f (fs1, gs1);
+}
diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
new file mode 100644
index 00000000000..ae9f4de0e1f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+template <class T>
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
+ // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
+__seg_fs int *a;
+int main() { f(a); } // { dg-error "no matching" }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
diff --git a/gcc/tree.h b/gcc/tree.h
index f8b2d718b7e..ad947614816 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2530,7 +2530,7 @@ extern tree vector_element_bits_tree (const_tree);
/* Encode/decode the named memory support as part of the qualifier. If more
than 8 qualifiers are added, these macros need to be adjusted. */
-#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
+#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
#define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
/* Return all qualifiers except for the address space qualifiers. */
--
2.34.1
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v2] c++: parser - Support for target address spaces in C++
2022-10-11 22:03 ` [PATCH v2] " Paul Iannetta
2022-10-12 1:49 ` Jason Merrill
@ 2026-05-15 12:18 ` Thomas Schwinge
1 sibling, 0 replies; 86+ messages in thread
From: Thomas Schwinge @ 2026-05-15 12:18 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Paul Iannetta, Patrick Palka
[Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
other email address: <paul.iannetta@ens-lyon.org>.]
Hi!
On 2022-10-12T00:03:04+0200, Paul Iannetta <piannetta@kalrayinc.com> wrote:
> On Mon, Oct 10, 2022 at 03:20:13PM -0400, Jason Merrill wrote:
>> On 10/9/22 12:12, Paul Iannetta wrote:
>> > That's a nice feature! LLVM is using AS<number> to mangle the
>> > address-name qualified types but that would have required an update of
>> > libiberty's demangler in the binutils as well.
>>
>> And they haven't proposed this mangling to
>>
>> https://github.com/itanium-cxx-abi/cxx-abi/
>>
>> yet, either.
Hmm, maybe I'm confused (certainly possible; I didn't look into the
"named address spaces vs. mangling" topic in any detail yet), but isn't
the LLVM mangling what had gotten discuseed already in 2017
<https://github.com/itanium-cxx-abi/cxx-abi/issues/28>
"Name mangling for possible new Clang type #28", and gotten resolved via
<https://github.com/itanium-cxx-abi/cxx-abi/pull/29>
"Record the non-standard mangling of the GNU address_space attribute"?
On the other hand, do we need to describe the GNU C++ mangling, once that
patch is accepted?
Grüße
Thomas
> When looking at clang/lib/AST/{Microsoft,Itamium}Mangle.cpp, the
> comments may suggest that they wanted to implement them as extended
> qualifiers prefixed by an `U' but that's not what they ended up doing.
>
>> You certainly want some template tests, say
>>
>> template <class T>
>> int f (T *p) { return *p; }
>> __seg_fs int *a;
>> int main() { f(a); }
>> // test for mangling of f<__seg_fs int>
>>
>> -----
>>
>> template <class T>
>> int f (T __seg_gs *p) { return *p; }
>> __seg_fs int *a;
>> int main() { f(a); } // error, conflicting address spaces
>
> Indeed, I was getting the first one right by a stroke of luck but not
> the second. I've consequently adapted the part which checks and
> computes the unification of cv-qualifiers in the presence of address
> spaces. The idea being that a type parameter T can match any address
> spaces but an address-space qualified parameter can't accept more
> general address spaces than itself.
>
>> > +/* Return true if between two named address spaces, whether there is a superset
>> > + named address space that encompasses both address spaces. If there is a
>> > + superset, return which address space is the superset. */
>> > +
>> > +bool
>> > +addr_space_superset (addr_space_t as1, addr_space_t as2,
>> > + addr_space_t * common)
>> > +{
>> > + if (as1 == as2)
>> > + {
>> > + *common = as1;
>> > + return true;
>> > + }
>> > + else if (targetm.addr_space.subset_p (as1, as2))
>> > + {
>> > + *common = as2;
>> > + return true;
>> > + }
>> > + else if (targetm.addr_space.subset_p (as2, as1))
>> > + {
>> > + *common = as1;
>> > + return true;
>> > + }
>> > + else
>> > + return false;
>>
>> So it's not possible for the two spaces to both be subsets of a third?
>>
>
> According to the [N1275, page 38]:
> Address spaces may overlap in a nested fashion. For any two address
> spaces, either the address spaces must be disjoint, they must be
> equivalent, or one must be a subset of the other. [...] There is no
> requirement that named address spaces (intrinsic or otherwise) be
> subsets of the generic address space.
> [N1275]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1275.pdf
>
> Hence, two disjoint address spaces can't be subsets of a third, per
> the draft.
>
>>
>> New non-bit-fields should be added before the bit-fields.
>>
>
> Done.
>
>> > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
>> > index eb53e0ebeb4..06625ad9a4b 100644
>> > --- a/gcc/cp/mangle.cc
>> > +++ b/gcc/cp/mangle.cc
>> > @@ -2509,6 +2509,15 @@ write_CV_qualifiers_for_type (const tree type)
>> > array. */
>> > cp_cv_quals quals = TYPE_QUALS (type);
>> > + if (DECODE_QUAL_ADDR_SPACE (quals))
>> > + {
>> > + addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals);
>>
>> This can be
>>
>> if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
>>
>> so you don't need to repeat it.
>
> I thought this was c++17 only, but in fact c++17 only broadened the
> idiom. Nice! (It would be even nicer to have this feature in C as
> well)
>> > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
>> > index 763df6f479b..110ceafc98b 100644
>> > --- a/gcc/cp/parser.cc
>> > +++ b/gcc/cp/parser.cc
>> > [...]
>> > @@ -23812,6 +23830,13 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
>> > break;
>> > }
>> > + if (RID_FIRST_ADDR_SPACE <= token->keyword &&
>> > + token->keyword <= RID_LAST_ADDR_SPACE)
>> > + {
>> > + cv_qualifier =
>> > + ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
>> > + }
>>
>> We usually omit braces around a single statement.
>>
> Done.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
@ 2026-05-15 13:11 ` Paul IANNETTA
2026-06-18 9:35 ` Georg-Johann Lay
2026-05-15 15:34 ` Jason Merrill
` (5 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Paul IANNETTA @ 2026-05-15 13:11 UTC (permalink / raw)
To: Jason Merrill, Patrick Palka, gcc-patches, Thomas Schwinge
[-- Attachment #1: Type: text/plain, Size: 6726 bytes --]
On Friday, May 15, 2026 at 09:17:14 PM GMT+9, Thomas Schwinge <tschwinge@baylibre.com> wrote:
[Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
other email address: <paul.iannetta@ens-lyon.org>.]
Hi!
I'd like to resume this patch submission here, which is adding support
for named address spaces to GNU C++, as is implemented for GNU C. As far
as I can tell, there wasn't any specific technical reason that this patch
review stalled, back then, in 2022-11? (Jason?)
I've now rebased this onto recent GCC trunk, see attached
'0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
just a few merge conflicts that I had to fix up (nothing serious), and
I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
The 'gcc/targhooks.cc:default_addr_space_subset_p' change that appeared
in this v4:
bool
default_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
{
- return (subset == superset);
+ return (subset == 2 && superset == 0) || (subset == superset);
}
... is entirely unmotivated, as far as I can tell: probably a local hack
for testing, that sneaked into the patch posted? This change regresses
'gcc.target/i386/addr-space-typeck-1.c' (notice: C test case), which
again PASSes with that change undone.
The new test case 'g++.dg/template/spec-addr-space.C' needs minor
adjusting per changed GCC diagnostics (not related to the new named
addess space support):
@@ -1,8 +1,9 @@
// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
template <class T>
-int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate 1: 'template<class T> int f.__seg_gs T\*." }
// { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
__seg_fs int *a;
int main() { f(a); } // { dg-error "no matching" }
-// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
+// { dg-note "there is 1 candidate" "" { target *-*-* } .-1 }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-2 }
With that, all new test cases PASS, and no regressions.
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), and I'll
work on test cases some more. I'll also examine if, since then, any new
code has appeared where 'ADDR_SPACE_CONVERT_EXPR' needs to be handled.
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.
Full-quote of Paul's email from back then:
On 2022-11-10T16:42:22+0100, Paul Iannetta <piannetta@kalrayinc.com> wrote:
> Hi,
>
> 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, and warnings; and expose the same hooks as
> the C front-end does. The C++ front-end produces as much warnings
> that the C front-end, however they are sometimes a bit less
> informative (especially so, when passing arguments to functions).
>
> I also address the following points:
>
> 1. The handling of the register storage class is grouped with the
> other variables with automatic storage. This incurs a slight
> dis-alignment since you cannot have global register variables do not
> trigger an error, only a warning.
>
> 2. In template unification, I maintain that we don't want any
> changes to address spaces whatsoever during the unification process,
> hence ignoring the strict flag. Nevertheless, we allow implicit
> conversion, and I have verified that, indeed,
>
>
> template <class T> void f(T **);
> struct A {
> template <class T> operator T*__seg_fs*();
> };
> int main()
> {
> f((void* __seg_fs *)0); // (1): void*__seg_fs* -> void** should be OK
> void (*p)(void * __seg_fs *) = f; // (2): error
> }
>
> works as intended. That is, (1) works if we set __seg_fs as a subspace
> of the generic address space, and (2) is always an error.
>
> 3. In template unification, when unifying __as1 T = __as2 U we want
> to unify to the __as1 at most, never to __as2 at most, because the
> function requiring __as1 T may want to mix address space from the
> bigger address space, therefore I think that we want to have the
> smaller address-space to be unified into the bigger of the two.
>
> 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.
>
> 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.
>
> 6. Currently, I do not handle classes. I see what I can do in a
> further revision and maybe add a target hook to hand down to targets
> the choice of the address space of the vtable.
>
> 7. This can't be added as a test case, but I checked that:
>
> // In this test case, __seg_gs is a subset of the generic address
> // space.
>
> int f (int *);
> int main ()
> {
> int __seg_fs *pa;
> int __seg_gs *pb;
> int *pc;
> pa = (__seg_fs int *) pb; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint ‘__seg_gs’ address space pointer
> pa = (int __seg_fs *) pc; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint generic address space pointer
> pa = pb; return *pa; // error: invalid conversion from ‘__seg_gs int*’ to ‘__seg_fs int*’
> pc = pb; return *pc; // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
> pb = pc; return *pb; // error: invalid conversion from ‘int*’ to ‘__seg_gs int*’
> pc = pa; return *pb; // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
> return f (pb); // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
> // return f (pa); // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
> // note: initializing argument 1 of ‘int f(int*)’
> }
>
> Thanks,
> Paul
>
> Rebased on current trunk, bootstrapped and regtested.
Hi,
Thank you for taking the time to rebase this patch on trunk. I've compared your rebased patch to my local version and I did not spot
any significant differences.
Concerning, 'gcc/targhooks.cc:default_addr_space_subset_p', I confirm.
This is a spurious change, and it does not appear in my local tree.
Paul
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
2026-05-15 13:11 ` Paul IANNETTA
@ 2026-05-15 15:34 ` Jason Merrill
2026-05-16 7:28 ` Paul IANNETTA
2026-05-21 12:18 ` Georg-Johann Lay
` (4 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2026-05-15 15:34 UTC (permalink / raw)
To: Thomas Schwinge, Patrick Palka, gcc-patches; +Cc: Paul Iannetta
On 5/15/26 8:17 AM, Thomas Schwinge wrote:
> I'd like to resume this patch submission here, which is adding support
> for named address spaces to GNU C++, as is implemented for GNU C. As far
> as I can tell, there wasn't any specific technical reason that this patch
> review stalled, back then, in 2022-11? (Jason?)
Looking back, it seems to have been because there was no follow-up after
my comments in
https://gcc.gnu.org/pipermail/gcc-patches/2022-November/606149.html
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 15:34 ` Jason Merrill
@ 2026-05-16 7:28 ` Paul IANNETTA
2026-05-16 10:58 ` Thomas Schwinge
2026-05-16 13:30 ` Georg-Johann Lay
0 siblings, 2 replies; 86+ messages in thread
From: Paul IANNETTA @ 2026-05-16 7:28 UTC (permalink / raw)
To: Thomas Schwinge, Patrick Palka, gcc-patches, Jason Merrill,
Georg-Johann Lay
[-- Attachment #1: Type: text/plain, Size: 2192 bytes --]
On Saturday, May 16, 2026 at 12:40:21 AM GMT+9, Jason Merrill <jason@redhat.com> wrote:
> On 5/15/26 8:17 AM, Thomas Schwinge wrote:
>> I'd like to resume this patch submission here, which is adding support
>> for named address spaces to GNU C++, as is implemented for GNU C. As far
>> as I can tell, there wasn't any specific technical reason that this patch
>> review stalled, back then, in 2022-11? (Jason?)
>
>Looking back, it seems to have been because there was no follow-up after
>my comments in
>
> https://gcc.gnu.org/pipermail/gcc-patches/2022-November/606149.html
>
> Jason
Yes, I did not find time to work on the comments that were raised there.
Just a few points I'd like to add:
- Concerning mangling, the patch uses the universal vendor extension, not the AS<number>.
One of the main reasons was that GCC does not support the definition of custom
address spaces on the fly with "__attribute__((address_space (number)))", and always
have a proper name that can be used after the "U" prefix.
- Concerning pointers to class in a specific addresses, the patch does not enforce anything
and happily compiles things like "myclass __addr_space* var;". However, I did not dig more
into the consequences.
- You can easily test the current patch and see what happens at the gimple level by selecting the
KVX port on Goldbolt, you can use "__bypass" and "__speculate" as "address spaces".
(cf. https://godbolt.org/z/EcqPETEcx )
- Address spaces where used a lot in Kalray's code base, however our C++ code did not exert a lot
of pressure on the C++-only features; that is no heavy use of templates, neither heavy use of address-space
qualified pointers.
- The part dealing with vtables is yet to address, I'll plan to look into it if this patch gets merged.
- The target hook "targetm.addr_space.diagnose_usage" is called in "cp_lexer_get_preprocessor_token"
The out-tree patch is available here [1] (the content is almost the same as the rebased patch by Thomas),
with some more tests here [2].
Paul
[1]: https://github.com/kalray/gcc/commit/56fcdd97eeca5a4429869062abdd341bae77ca0d
[2]: https://github.com/kalray/gcc/commit/cba87c9b2b799923d501863c27a95d04471b8825
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-16 7:28 ` Paul IANNETTA
@ 2026-05-16 10:58 ` Thomas Schwinge
2026-05-16 13:30 ` Georg-Johann Lay
1 sibling, 0 replies; 86+ messages in thread
From: Thomas Schwinge @ 2026-05-16 10:58 UTC (permalink / raw)
To: paul.iannetta, Jason Merrill; +Cc: Patrick Palka, gcc-patches, Georg-Johann Lay
Hi!
On 2026-05-16T07:28:37+0000, Paul IANNETTA <paul.iannetta@ens-lyon.org> wrote:
> On Saturday, May 16, 2026 at 12:40:21 AM GMT+9, Jason Merrill <jason@redhat.com> wrote:
>> On 5/15/26 8:17 AM, Thomas Schwinge wrote:
>>> I'd like to resume this patch submission here, which is adding support
>>> for named address spaces to GNU C++, as is implemented for GNU C. As far
>>> as I can tell, there wasn't any specific technical reason that this patch
>>> review stalled, back then, in 2022-11? (Jason?)
>>
>>Looking back
Thanks for having a look!
>> it seems to have been because there was no follow-up after
>> my comments in
>>
>> https://gcc.gnu.org/pipermail/gcc-patches/2022-November/606149.html
I was assuming that the items discussed in that subthread (started by
Georg-Johann Lay) were for "future work" rather than "baseline support",
and that all current review items for "baseline support" had gotten
addressed in Paul's "v4".
In other words: which of these items do we need to address now, before
we're able to move forward with the "baseline support" (Paul's "v4",
rebased, and to-be slightly polished)?
Grüße
Thomas
> Yes, I did not find time to work on the comments that were raised there.
>
> Just a few points I'd like to add:
> - Concerning mangling, the patch uses the universal vendor extension, not the AS<number>.
> One of the main reasons was that GCC does not support the definition of custom
> address spaces on the fly with "__attribute__((address_space (number)))", and always
> have a proper name that can be used after the "U" prefix.
>
> - Concerning pointers to class in a specific addresses, the patch does not enforce anything
> and happily compiles things like "myclass __addr_space* var;". However, I did not dig more
> into the consequences.
>
> - You can easily test the current patch and see what happens at the gimple level by selecting the
> KVX port on Goldbolt, you can use "__bypass" and "__speculate" as "address spaces".
> (cf. https://godbolt.org/z/EcqPETEcx )
>
> - Address spaces where used a lot in Kalray's code base, however our C++ code did not exert a lot
> of pressure on the C++-only features; that is no heavy use of templates, neither heavy use of address-space
> qualified pointers.
>
> - The part dealing with vtables is yet to address, I'll plan to look into it if this patch gets merged.
> - The target hook "targetm.addr_space.diagnose_usage" is called in "cp_lexer_get_preprocessor_token"
>
> The out-tree patch is available here [1] (the content is almost the same as the rebased patch by Thomas),
> with some more tests here [2].
>
> Paul
>
> [1]: https://github.com/kalray/gcc/commit/56fcdd97eeca5a4429869062abdd341bae77ca0d
> [2]: https://github.com/kalray/gcc/commit/cba87c9b2b799923d501863c27a95d04471b8825
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-16 7:28 ` Paul IANNETTA
2026-05-16 10:58 ` Thomas Schwinge
@ 2026-05-16 13:30 ` Georg-Johann Lay
2026-05-18 14:19 ` Thomas Schwinge
1 sibling, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-05-16 13:30 UTC (permalink / raw)
To: paul.iannetta, Thomas Schwinge, Patrick Palka, gcc-patches,
Jason Merrill
Am 16.05.26 um 09:28 schrieb Paul IANNETTA:
> On Saturday, May 16, 2026 at 12:40:21 AM GMT+9, Jason Merrill
> <jason@redhat.com> wrote:
> > On 5/15/26 8:17 AM, Thomas Schwinge wrote:
> >> I'd like to resume this patch submission here, which is adding support
> >> for named address spaces to GNU C++, as is implemented for GNU C. As far
> >> as I can tell, there wasn't any specific technical reason that this
> patch
> >> review stalled, back then, in 2022-11? (Jason?)
> >
> >Looking back, it seems to have been because there was no follow-up after
> >my comments in
> >
> > https://gcc.gnu.org/pipermail/gcc-patches/2022-November/606149.html
> >
> > Jason
>
> Yes, I did not find time to work on the comments that were raised there.
>
> Just a few points I'd like to add:
> - Concerning mangling, the patch uses the universal vendor extension,
> not the AS<number>.
> One of the main reasons was that GCC does not support the definition of
> custom
> address spaces on the fly with "__attribute__((address_space
> (number)))", and always
> have a proper name that can be used after the "U" prefix.
Maybe its a good idea to use similar mangling like clang for avr does:
#define AS __attribute__((__address_space__(1)))
void fun (const AS char*)
{
}
compiles to:
_Z3funPU3AS1Kc:
ret
Johann
> - Concerning pointers to class in a specific addresses, the patch does
> not enforce anything
> and happily compiles things like "myclass __addr_space* var;". However,
> I did not dig more
> into the consequences.
>
> - You can easily test the current patch and see what happens at the
> gimple level by selecting the
> KVX port on Goldbolt, you can use "__bypass" and "__speculate" as
> "address spaces".
> (cf. https://godbolt.org/z/EcqPETEcx )
>
> - Address spaces where used a lot in Kalray's code base, however our C++
> code did not exert a lot
> of pressure on the C++-only features; that is no heavy use of templates,
> neither heavy use of address-space
> qualified pointers.
>
> - The part dealing with vtables is yet to address, I'll plan to look
> into it if this patch gets merged.
> - The target hook "targetm.addr_space.diagnose_usage" is called in
> "cp_lexer_get_preprocessor_token"
>
> The out-tree patch is available here [1] (the content is almost the same
> as the rebased patch by Thomas),
> with some more tests here [2].
>
> Paul
>
> [1]: https://github.com/kalray/gcc/
> commit/56fcdd97eeca5a4429869062abdd341bae77ca0d
> [2]: https://github.com/kalray/gcc/commit/
> cba87c9b2b799923d501863c27a95d04471b8825
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-16 13:30 ` Georg-Johann Lay
@ 2026-05-18 14:19 ` Thomas Schwinge
2026-05-18 15:05 ` Georg-Johann Lay
0 siblings, 1 reply; 86+ messages in thread
From: Thomas Schwinge @ 2026-05-18 14:19 UTC (permalink / raw)
To: Georg-Johann Lay; +Cc: paul.iannetta, Patrick Palka, gcc-patches, Jason Merrill
Hi Johann!
On 2026-05-16T15:30:17+0200, Georg-Johann Lay <avr@gjlay.de> wrote:
> Am 16.05.26 um 09:28 schrieb Paul IANNETTA:
>> On Saturday, May 16, 2026 at 12:40:21 AM GMT+9, Jason Merrill
>> <jason@redhat.com> wrote:
>> > On 5/15/26 8:17 AM, Thomas Schwinge wrote:
>> >> I'd like to resume this patch submission here, which is adding support
>> >> for named address spaces to GNU C++, as is implemented for GNU C. As far
>> >> as I can tell, there wasn't any specific technical reason that this patch
>> >> review stalled, back then, in 2022-11? (Jason?)
>> >
>> >Looking back, it seems to have been because there was no follow-up after
>> >my comments in
>> >
>> > https://gcc.gnu.org/pipermail/gcc-patches/2022-November/606149.html
>> >
>> > Jason
>>
>> Yes, I did not find time to work on the comments that were raised there.
>>
>> Just a few points I'd like to add:
>> - Concerning mangling, the patch uses the universal vendor extension,
>> not the AS<number>.
>> One of the main reasons was that GCC does not support the definition of
>> custom
>> address spaces on the fly with "__attribute__((address_space
>> (number)))", and always
>> have a proper name that can be used after the "U" prefix.
>
> Maybe its a good idea to use similar mangling like clang for avr does:
> #define AS __attribute__((__address_space__(1)))
>
> void fun (const AS char*)
> {
> }
>
> compiles to:
>
> _Z3funPU3AS1Kc:
> ret
I'm confused, because:
a.C:3:4: warning: ‘address_space’ attribute directive ignored [-Wattributes]
3 | void fun (const AS char*)
| ^~~~
..., so discussing options for mangling for an 'address_space' attribute
is indeed future work for GCC?
(..., and yes, support for that one, and also your suggestion to align
its mangling with pre-existing practice, totally makes sense to me -- but
that's another patch to be written?)
Or, is it that you're suggesting to apply a different mangling scheme for
use of *named address spaces* (which is what Paul's patch adds support
for in GCC), so that GCC produces mangled names as if the first named
address space that gets defined would correspond to
'__attribute__((__address_space__(0)))', and so on?
Grüße
Thomas
>> - Concerning pointers to class in a specific addresses, the patch does
>> not enforce anything
>> and happily compiles things like "myclass __addr_space* var;". However,
>> I did not dig more
>> into the consequences.
>>
>> - You can easily test the current patch and see what happens at the
>> gimple level by selecting the
>> KVX port on Goldbolt, you can use "__bypass" and "__speculate" as
>> "address spaces".
>> (cf. https://godbolt.org/z/EcqPETEcx )
>>
>> - Address spaces where used a lot in Kalray's code base, however our C++
>> code did not exert a lot
>> of pressure on the C++-only features; that is no heavy use of templates,
>> neither heavy use of address-space
>> qualified pointers.
>>
>> - The part dealing with vtables is yet to address, I'll plan to look
>> into it if this patch gets merged.
>> - The target hook "targetm.addr_space.diagnose_usage" is called in
>> "cp_lexer_get_preprocessor_token"
>>
>> The out-tree patch is available here [1] (the content is almost the same
>> as the rebased patch by Thomas),
>> with some more tests here [2].
>>
>> Paul
>>
>> [1]: https://github.com/kalray/gcc/
>> commit/56fcdd97eeca5a4429869062abdd341bae77ca0d
>> [2]: https://github.com/kalray/gcc/commit/
>> cba87c9b2b799923d501863c27a95d04471b8825
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-18 14:19 ` Thomas Schwinge
@ 2026-05-18 15:05 ` Georg-Johann Lay
2026-05-18 18:49 ` Thomas Schwinge
0 siblings, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-05-18 15:05 UTC (permalink / raw)
To: Thomas Schwinge; +Cc: paul.iannetta, Patrick Palka, gcc-patches, Jason Merrill
Am 18.05.26 um 16:19 schrieb Thomas Schwinge:
> Hi Johann!
>
> On 2026-05-16T15:30:17+0200, Georg-Johann Lay <avr@gjlay.de> wrote:
>> Am 16.05.26 um 09:28 schrieb Paul IANNETTA:
>>> On Saturday, May 16, 2026 at 12:40:21 AM GMT+9, Jason Merrill
>>> <jason@redhat.com> wrote:
>>> > On 5/15/26 8:17 AM, Thomas Schwinge wrote:
>>> >> I'd like to resume this patch submission here, which is adding support
>>> >> for named address spaces to GNU C++, as is implemented for GNU C. As far
>>> >> as I can tell, there wasn't any specific technical reason that this patch
>>> >> review stalled, back then, in 2022-11? (Jason?)
>>> >
>>> >Looking back, it seems to have been because there was no follow-up after
>>> >my comments in
>>> >
>>> > https://gcc.gnu.org/pipermail/gcc-patches/2022-November/606149.html
>>> >
>>> > Jason
>>>
>>> Yes, I did not find time to work on the comments that were raised there.
>>>
>>> Just a few points I'd like to add:
>>> - Concerning mangling, the patch uses the universal vendor extension,
>>> not the AS<number>.
>>> One of the main reasons was that GCC does not support the definition of
>>> custom
>>> address spaces on the fly with "__attribute__((address_space
>>> (number)))", and always
>>> have a proper name that can be used after the "U" prefix.
>>
>> Maybe its a good idea to use similar mangling like clang for avr does:
>> #define AS __attribute__((__address_space__(1)))
>>
>> void fun (const AS char*)
>> {
>> }
>>
>> compiles to:
>>
>> _Z3funPU3AS1Kc:
>> ret
>
> I'm confused, because:
>
> a.C:3:4: warning: ‘address_space’ attribute directive ignored [-Wattributes]
> 3 | void fun (const AS char*)
> | ^~~~
>
> ..., so discussing options for mangling for an 'address_space' attribute
> is indeed future work for GCC?
Hallo Thomas,
The example above is for clang that uses
__attribute__((__address_space__(1))), which is equivalent to avr-gcc's
__flash qualifier. This is possible since that attribute (and similar
ones) behave like a qualifier, whereas GCC's attributes don't have the
power of qualifiers, hence GCC cannot use attributes for that.
AFAIK, we have clang's AS(n) behave like avr-gcc's qualifiers:
AS(1) <=> __flash (16-bit flash in 0x0..0xffff)
AS(2) <=> __flash1 (16-bit flash in 0x10000..0x1ffff)
AS(3) <=> __flash2 (16-bit flash in 0x20000..0x2ffff)
etc.
avr-gcc also knows __flashx (24-bit flash address) and __memx (24-bit
linearized flash or RAM address), dunno if clang has equivalents for these.
Johann
> (..., and yes, support for that one, and also your suggestion to align
> its mangling with pre-existing practice, totally makes sense to me -- but
> that's another patch to be written?)
>
> Or, is it that you're suggesting to apply a different mangling scheme for
> use of *named address spaces* (which is what Paul's patch adds support
> for in GCC), so that GCC produces mangled names as if the first named
> address space that gets defined would correspond to
> '__attribute__((__address_space__(0)))', and so on?
>
> Grüße
> Thomas
>
>>> - Concerning pointers to class in a specific addresses, the patch does
>>> not enforce anything
>>> and happily compiles things like "myclass __addr_space* var;". However,
>>> I did not dig more
>>> into the consequences.
>>>
>>> - You can easily test the current patch and see what happens at the
>>> gimple level by selecting the
>>> KVX port on Goldbolt, you can use "__bypass" and "__speculate" as
>>> "address spaces".
>>> (cf. https://godbolt.org/z/EcqPETEcx )
>>>
>>> - Address spaces where used a lot in Kalray's code base, however our C++
>>> code did not exert a lot
>>> of pressure on the C++-only features; that is no heavy use of templates,
>>> neither heavy use of address-space
>>> qualified pointers.
>>>
>>> - The part dealing with vtables is yet to address, I'll plan to look
>>> into it if this patch gets merged.
>>> - The target hook "targetm.addr_space.diagnose_usage" is called in
>>> "cp_lexer_get_preprocessor_token"
>>>
>>> The out-tree patch is available here [1] (the content is almost the same
>>> as the rebased patch by Thomas),
>>> with some more tests here [2].
>>>
>>> Paul
>>>
>>> [1]: https://github.com/kalray/gcc/
>>> commit/56fcdd97eeca5a4429869062abdd341bae77ca0d
>>> [2]: https://github.com/kalray/gcc/commit/
>>> cba87c9b2b799923d501863c27a95d04471b8825
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-18 15:05 ` Georg-Johann Lay
@ 2026-05-18 18:49 ` Thomas Schwinge
2026-05-19 8:14 ` Georg-Johann Lay
0 siblings, 1 reply; 86+ messages in thread
From: Thomas Schwinge @ 2026-05-18 18:49 UTC (permalink / raw)
To: Georg-Johann Lay, Jason Merrill; +Cc: paul.iannetta, Patrick Palka, gcc-patches
Hi Johann, Jason!
On 2026-05-18T17:05:08+0200, Georg-Johann Lay <avr@gjlay.de> wrote:
> Am 18.05.26 um 16:19 schrieb Thomas Schwinge:
>> On 2026-05-16T15:30:17+0200, Georg-Johann Lay <avr@gjlay.de> wrote:
>>> Am 16.05.26 um 09:28 schrieb Paul IANNETTA:
>>>> On Saturday, May 16, 2026 at 12:40:21 AM GMT+9, Jason Merrill
>>>> <jason@redhat.com> wrote:
>>>> > On 5/15/26 8:17 AM, Thomas Schwinge wrote:
>>>> >> I'd like to resume this patch submission here, which is adding support
>>>> >> for named address spaces to GNU C++, as is implemented for GNU C. As far
>>>> >> as I can tell, there wasn't any specific technical reason that this patch
>>>> >> review stalled, back then, in 2022-11? (Jason?)
>>>> >
>>>> >Looking back, it seems to have been because there was no follow-up after
>>>> >my comments in
>>>> >
>>>> > https://gcc.gnu.org/pipermail/gcc-patches/2022-November/606149.html
>>>> >
>>>> > Jason
>>>>
>>>> Yes, I did not find time to work on the comments that were raised there.
>>>>
>>>> Just a few points I'd like to add:
>>>> - Concerning mangling, the patch uses the universal vendor extension,
>>>> not the AS<number>.
>>>> One of the main reasons was that GCC does not support the definition of
>>>> custom
>>>> address spaces on the fly with "__attribute__((address_space
>>>> (number)))", and always
>>>> have a proper name that can be used after the "U" prefix.
>>>
>>> Maybe its a good idea to use similar mangling like clang for avr does:
>>> #define AS __attribute__((__address_space__(1)))
>>>
>>> void fun (const AS char*)
>>> {
>>> }
>>>
>>> compiles to:
>>>
>>> _Z3funPU3AS1Kc:
>>> ret
>>
>> I'm confused, because:
>>
>> a.C:3:4: warning: ‘address_space’ attribute directive ignored [-Wattributes]
>> 3 | void fun (const AS char*)
>> | ^~~~
>>
>> ..., so discussing options for mangling for an 'address_space' attribute
>> is indeed future work for GCC?
>
> The example above is for clang that uses
> __attribute__((__address_space__(1)))
Sure, I got that -- I was just pointing out that this code isn't
currently applicable to GCC, and I didn't understand what point you're
making with it.
> which is equivalent to avr-gcc's
> __flash qualifier. This is possible since that attribute (and similar
> ones) behave like a qualifier, whereas GCC's attributes don't have the
> power of qualifiers, hence GCC cannot use attributes for that.
>
> AFAIK, we have clang's AS(n) behave like avr-gcc's qualifiers:
>
> AS(1) <=> __flash (16-bit flash in 0x0..0xffff)
> AS(2) <=> __flash1 (16-bit flash in 0x10000..0x1ffff)
> AS(3) <=> __flash2 (16-bit flash in 0x20000..0x2ffff)
> etc.
OK, so you're actually suggesting that GCC should mangle '__flash' in the
same way that LLVM/Clang mangles '__attribute__((__address_space__(1)))',
and so on, right?
This could, I guess, be a GCC target ABI decision (possibly configurable
via a command-line flag?), implemented via a target hook in
'gcc/cp/mangle.cc:write_CV_qualifiers_for_type', to either use Paul's
proposed "actual name" mangling vs. 'AS[N]'? ..., if having the mangling
configurable is desirable for GCC, conceptually? (Jason?)
Which other items do we need to address before Paul's patch can be
considered?
Grüße
Thomas
> avr-gcc also knows __flashx (24-bit flash address) and __memx (24-bit
> linearized flash or RAM address), dunno if clang has equivalents for these.
>
> Johann
>
>> (..., and yes, support for that one, and also your suggestion to align
>> its mangling with pre-existing practice, totally makes sense to me -- but
>> that's another patch to be written?)
>>
>> Or, is it that you're suggesting to apply a different mangling scheme for
>> use of *named address spaces* (which is what Paul's patch adds support
>> for in GCC), so that GCC produces mangled names as if the first named
>> address space that gets defined would correspond to
>> '__attribute__((__address_space__(0)))', and so on?
>>
>> Grüße
>> Thomas
>>
>>>> - Concerning pointers to class in a specific addresses, the patch does
>>>> not enforce anything
>>>> and happily compiles things like "myclass __addr_space* var;". However,
>>>> I did not dig more
>>>> into the consequences.
>>>>
>>>> - You can easily test the current patch and see what happens at the
>>>> gimple level by selecting the
>>>> KVX port on Goldbolt, you can use "__bypass" and "__speculate" as
>>>> "address spaces".
>>>> (cf. https://godbolt.org/z/EcqPETEcx )
>>>>
>>>> - Address spaces where used a lot in Kalray's code base, however our C++
>>>> code did not exert a lot
>>>> of pressure on the C++-only features; that is no heavy use of templates,
>>>> neither heavy use of address-space
>>>> qualified pointers.
>>>>
>>>> - The part dealing with vtables is yet to address, I'll plan to look
>>>> into it if this patch gets merged.
>>>> - The target hook "targetm.addr_space.diagnose_usage" is called in
>>>> "cp_lexer_get_preprocessor_token"
>>>>
>>>> The out-tree patch is available here [1] (the content is almost the same
>>>> as the rebased patch by Thomas),
>>>> with some more tests here [2].
>>>>
>>>> Paul
>>>>
>>>> [1]: https://github.com/kalray/gcc/
>>>> commit/56fcdd97eeca5a4429869062abdd341bae77ca0d
>>>> [2]: https://github.com/kalray/gcc/commit/
>>>> cba87c9b2b799923d501863c27a95d04471b8825
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-18 18:49 ` Thomas Schwinge
@ 2026-05-19 8:14 ` Georg-Johann Lay
2026-05-21 14:19 ` Paul IANNETTA
0 siblings, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-05-19 8:14 UTC (permalink / raw)
To: Thomas Schwinge, Jason Merrill; +Cc: paul.iannetta, Patrick Palka, gcc-patches
Am 18.05.26 um 20:49 schrieb Thomas Schwinge:
> Hi Johann, Jason!
>
> On 2026-05-18T17:05:08+0200, Georg-Johann Lay <avr@gjlay.de> wrote:
>> Am 18.05.26 um 16:19 schrieb Thomas Schwinge:
>>> On 2026-05-16T15:30:17+0200, Georg-Johann Lay <avr@gjlay.de> wrote:
>>>> Am 16.05.26 um 09:28 schrieb Paul IANNETTA:
>>>>> On Saturday, May 16, 2026 at 12:40:21 AM GMT+9, Jason Merrill
>>>>> <jason@redhat.com> wrote:
>>>>> > On 5/15/26 8:17 AM, Thomas Schwinge wrote:
>>>>> >> I'd like to resume this patch submission here, which is adding support
>>>>> >> for named address spaces to GNU C++, as is implemented for GNU C. As far
>>>>> >> as I can tell, there wasn't any specific technical reason that this patch
>>>>> >> review stalled, back then, in 2022-11? (Jason?)
>>>>> >
>>>>> >Looking back, it seems to have been because there was no follow-up after
>>>>> >my comments in
>>>>> >
>>>>> > https://gcc.gnu.org/pipermail/gcc-patches/2022-November/606149.html
>>>>> >
>>>>> > Jason
>>>>>
>>>>> Yes, I did not find time to work on the comments that were raised there.
>>>>>
>>>>> Just a few points I'd like to add:
>>>>> - Concerning mangling, the patch uses the universal vendor extension,
>>>>> not the AS<number>.
>>>>> One of the main reasons was that GCC does not support the definition of
>>>>> custom
>>>>> address spaces on the fly with "__attribute__((address_space
>>>>> (number)))", and always
>>>>> have a proper name that can be used after the "U" prefix.
>>>>
>>>> Maybe its a good idea to use similar mangling like clang for avr does:
>>>> #define AS __attribute__((__address_space__(1)))
>>>>
>>>> void fun (const AS char*)
>>>> {
>>>> }
>>>>
>>>> compiles to:
>>>>
>>>> _Z3funPU3AS1Kc:
>>>> ret
>>>
>>> I'm confused, because:
>>>
>>> a.C:3:4: warning: ‘address_space’ attribute directive ignored [-Wattributes]
>>> 3 | void fun (const AS char*)
>>> | ^~~~
>>>
>>> ..., so discussing options for mangling for an 'address_space' attribute
>>> is indeed future work for GCC?
>>
>> The example above is for clang that uses
>> __attribute__((__address_space__(1)))
>
> Sure, I got that -- I was just pointing out that this code isn't
> currently applicable to GCC, and I didn't understand what point you're
> making with it.
It was just a piece of code to demonstrate avr-clang's AS mangling.
>> which is equivalent to avr-gcc's
>> __flash qualifier. This is possible since that attribute (and similar
>> ones) behave like a qualifier, whereas GCC's attributes don't have the
>> power of qualifiers, hence GCC cannot use attributes for that.
>>
>> AFAIK, we have clang's AS(n) behave like avr-gcc's qualifiers:
>>
>> AS(1) <=> __flash (16-bit flash in 0x0..0xffff)
>> AS(2) <=> __flash1 (16-bit flash in 0x10000..0x1ffff)
>> AS(3) <=> __flash2 (16-bit flash in 0x20000..0x2ffff)
>> etc.
>
> OK, so you're actually suggesting that GCC should mangle '__flash' in the
> same way that LLVM/Clang mangles '__attribute__((__address_space__(1)))',
> and so on, right?
Hi Thomas,
yes. IMO it is the same feature, though achieved by different means
in clang vs. gcc. avr-gcc's __flash could be achieved in clang with
#define __flash __attribute__((__address_space__(1)))
etc.
> This could, I guess, be a GCC target ABI decision (possibly configurable
> via a command-line flag?), implemented via a target hook in
> 'gcc/cp/mangle.cc:write_CV_qualifiers_for_type', to either use Paul's
> proposed "actual name" mangling vs. 'AS[N]'? ..., if having the mangling
> configurable is desirable for GCC, conceptually? (Jason?)
There are already 3 target hooks for mangling.
Perhaps TARGET_MANGLE_TYPE can already do it.
A command line option for that only makes sense when it is a multilib
option. avr-gcc has already 57 multilibs, and I don't see a benefit
in boosting them by a factor of 2.
> Which other items do we need to address before Paul's patch can be
> considered?
IMO most important is that it doesn't break C++ code that doesn't
use ASes. Issues with the AS feature (like name mangling) can be
adjusted once the basic patch is upstream. For example, there
should be a means to reject code that writes to an avr AS after
load time, like
int compute_x ();
const __flash int x = compute_x ();
Such code isn't possible in C (when compute_x () is not
computable at load time), but C++ allows it.
Johann
>
> Grüße
> Thomas
>
>
>> avr-gcc also knows __flashx (24-bit flash address) and __memx (24-bit
>> linearized flash or RAM address), dunno if clang has equivalents for these.
>>
>> Johann
>>
>>> (..., and yes, support for that one, and also your suggestion to align
>>> its mangling with pre-existing practice, totally makes sense to me -- but
>>> that's another patch to be written?)
>>>
>>> Or, is it that you're suggesting to apply a different mangling scheme for
>>> use of *named address spaces* (which is what Paul's patch adds support
>>> for in GCC), so that GCC produces mangled names as if the first named
>>> address space that gets defined would correspond to
>>> '__attribute__((__address_space__(0)))', and so on?
>>>
>>> Grüße
>>> Thomas
>>>
>>>>> - Concerning pointers to class in a specific addresses, the patch does
>>>>> not enforce anything
>>>>> and happily compiles things like "myclass __addr_space* var;". However,
>>>>> I did not dig more
>>>>> into the consequences.
>>>>>
>>>>> - You can easily test the current patch and see what happens at the
>>>>> gimple level by selecting the
>>>>> KVX port on Goldbolt, you can use "__bypass" and "__speculate" as
>>>>> "address spaces".
>>>>> (cf. https://godbolt.org/z/EcqPETEcx )
>>>>>
>>>>> - Address spaces where used a lot in Kalray's code base, however our C++
>>>>> code did not exert a lot
>>>>> of pressure on the C++-only features; that is no heavy use of templates,
>>>>> neither heavy use of address-space
>>>>> qualified pointers.
>>>>>
>>>>> - The part dealing with vtables is yet to address, I'll plan to look
>>>>> into it if this patch gets merged.
>>>>> - The target hook "targetm.addr_space.diagnose_usage" is called in
>>>>> "cp_lexer_get_preprocessor_token"
>>>>>
>>>>> The out-tree patch is available here [1] (the content is almost the same
>>>>> as the rebased patch by Thomas),
>>>>> with some more tests here [2].
>>>>>
>>>>> Paul
>>>>>
>>>>> [1]: https://github.com/kalray/gcc/
>>>>> commit/56fcdd97eeca5a4429869062abdd341bae77ca0d
>>>>> [2]: https://github.com/kalray/gcc/commit/
>>>>> cba87c9b2b799923d501863c27a95d04471b8825
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
2026-05-15 13:11 ` Paul IANNETTA
2026-05-15 15:34 ` Jason Merrill
@ 2026-05-21 12:18 ` Georg-Johann Lay
2026-05-21 14:26 ` Paul IANNETTA
2026-05-22 1:51 ` Jason Merrill
` (3 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-05-21 12:18 UTC (permalink / raw)
To: Thomas Schwinge, Jason Merrill, Patrick Palka, gcc-patches; +Cc: Paul Iannetta
Am 15.05.26 um 14:17 schrieb Thomas Schwinge:
> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
> other email address: <paul.iannetta@ens-lyon.org>.]
>
>
> Hi!
>
> I'd like to resume this patch submission here, which is adding support
> for named address spaces to GNU C++, as is implemented for GNU C. As far
> as I can tell, there wasn't any specific technical reason that this patch
> review stalled, back then, in 2022-11? (Jason?)
>
> I've now rebased this onto recent GCC trunk, see attached
> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
> just a few merge conflicts that I had to fix up (nothing serious), and
> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
Hi Thomas,
as far as I can see, there is not a single use of
TARGET_ADDR_SPACE_FOR_ARTIFICIAL_RODATA resp.
targetm.addr_space.for_artificial_rodata in the patch.
There should be a new purpose enum like ARTIFICIAL_RODATA_VTABLE
when vtables are built. The new enum would be defined in coretypes.h.
The comment in target.def states it is "for optimization purposes only",
which is no more true for vtables (for vtables it's an ABI thing).
It is great so see that finally, named address spaces will be in g++.
Johann
> The 'gcc/targhooks.cc:default_addr_space_subset_p' change that appeared
> in this v4:
>
> bool
> default_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
> {
> - return (subset == superset);
> + return (subset == 2 && superset == 0) || (subset == superset);
> }
>
> ... is entirely unmotivated, as far as I can tell: probably a local hack
> for testing, that sneaked into the patch posted? This change regresses
> 'gcc.target/i386/addr-space-typeck-1.c' (notice: C test case), which
> again PASSes with that change undone.
>
> The new test case 'g++.dg/template/spec-addr-space.C' needs minor
> adjusting per changed GCC diagnostics (not related to the new named
> addess space support):
>
> @@ -1,8 +1,9 @@
> // { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>
> template <class T>
> -int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate 1: 'template<class T> int f.__seg_gs T\*." }
> // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> __seg_fs int *a;
> int main() { f(a); } // { dg-error "no matching" }
> -// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> +// { dg-note "there is 1 candidate" "" { target *-*-* } .-1 }
> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-2 }
>
> With that, all new test cases PASS, and no regressions.
>
> 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), and I'll
> work on test cases some more. I'll also examine if, since then, any new
> code has appeared where 'ADDR_SPACE_CONVERT_EXPR' needs to be handled.
>
> 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.
>
> Full-quote of Paul's email from back then:
>
> On 2022-11-10T16:42:22+0100, Paul Iannetta <piannetta@kalrayinc.com> wrote:
>> Hi,
>>
>> 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, and warnings; and expose the same hooks as
>> the C front-end does. The C++ front-end produces as much warnings
>> that the C front-end, however they are sometimes a bit less
>> informative (especially so, when passing arguments to functions).
>>
>> I also address the following points:
>>
>> 1. The handling of the register storage class is grouped with the
>> other variables with automatic storage. This incurs a slight
>> dis-alignment since you cannot have global register variables do not
>> trigger an error, only a warning.
>>
>> 2. In template unification, I maintain that we don't want any
>> changes to address spaces whatsoever during the unification process,
>> hence ignoring the strict flag. Nevertheless, we allow implicit
>> conversion, and I have verified that, indeed,
>>
>>
>> template <class T> void f(T **);
>> struct A {
>> template <class T> operator T*__seg_fs*();
>> };
>> int main()
>> {
>> f((void* __seg_fs *)0); // (1): void*__seg_fs* -> void** should be OK
>> void (*p)(void * __seg_fs *) = f; // (2): error
>> }
>>
>> works as intended. That is, (1) works if we set __seg_fs as a subspace
>> of the generic address space, and (2) is always an error.
>>
>> 3. In template unification, when unifying __as1 T = __as2 U we want
>> to unify to the __as1 at most, never to __as2 at most, because the
>> function requiring __as1 T may want to mix address space from the
>> bigger address space, therefore I think that we want to have the
>> smaller address-space to be unified into the bigger of the two.
>>
>> 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.
>>
>> 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.
>>
>> 6. Currently, I do not handle classes. I see what I can do in a
>> further revision and maybe add a target hook to hand down to targets
>> the choice of the address space of the vtable.
>>
>> 7. This can't be added as a test case, but I checked that:
>>
>> // In this test case, __seg_gs is a subset of the generic address
>> // space.
>>
>> int f (int *);
>> int main ()
>> {
>> int __seg_fs *pa;
>> int __seg_gs *pb;
>> int *pc;
>> pa = (__seg_fs int *) pb; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint ‘__seg_gs’ address space pointer
>> pa = (int __seg_fs *) pc; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint generic address space pointer
>> pa = pb; return *pa; // error: invalid conversion from ‘__seg_gs int*’ to ‘__seg_fs int*’
>> pc = pb; return *pc; // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
>> pb = pc; return *pb; // error: invalid conversion from ‘int*’ to ‘__seg_gs int*’
>> pc = pa; return *pb; // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
>> return f (pb); // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
>> // return f (pa); // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
>> // note: initializing argument 1 of ‘int f(int*)’
>> }
>>
>> Thanks,
>> Paul
>>
>> Rebased on current trunk, bootstrapped and regtested.
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-19 8:14 ` Georg-Johann Lay
@ 2026-05-21 14:19 ` Paul IANNETTA
2026-05-21 19:27 ` Georg-Johann Lay
0 siblings, 1 reply; 86+ messages in thread
From: Paul IANNETTA @ 2026-05-21 14:19 UTC (permalink / raw)
To: Thomas Schwinge, Jason Merrill, Georg-Johann Lay
Cc: Patrick Palka, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 1149 bytes --]
Hi,
> Georg-Johann Lay wrote:
> IMO most important is that it doesn't break C++ code that doesn't
> use ASes. Issues with the AS feature (like name mangling) can be
> adjusted once the basic patch is upstream. For example, there
> should be a means to reject code that writes to an avr AS after
> load time, like
>
> int compute_x ();
> const __flash int x = compute_x ();
>
> Such code isn't possible in C (when compute_x () is not
> computable at load time), but C++ allows it.
Using the same mangling scheme as llvm does is clearly not a problem, and pretty easy to
change. However, it would imply that the address spaces numbers are the same on both
GCC and LLVM, so those numbers should probably be documented somewhere.
I am not exactly sure what you mean here. Sure in C++, there are some initializations which take
place before main, and the same can be done in C with __attribute__(constructor) for example.
But what do you mean, write to an address space after load time? How do you do that in C, where
there is no concept of load-time/pre-main initialization. Would you happen to know how avr on
llvm address such problems?
Paul
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-21 12:18 ` Georg-Johann Lay
@ 2026-05-21 14:26 ` Paul IANNETTA
2026-05-21 19:32 ` Georg-Johann Lay
0 siblings, 1 reply; 86+ messages in thread
From: Paul IANNETTA @ 2026-05-21 14:26 UTC (permalink / raw)
To: Thomas Schwinge, Jason Merrill, Patrick Palka, gcc-patches,
Georg-Johann Lay
[-- Attachment #1: Type: text/plain, Size: 1694 bytes --]
Hi,
On Thursday, May 21, 2026 at 09:19:00 PM GMT+9, Georg-Johann Lay <avr@gjlay.de> wrote:
> Am 15.05.26 um 14:17 schrieb Thomas Schwinge:
> > [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
> > other email address: <paul.iannetta@ens-lyon.org>.]
> >
> >
> > Hi!
> >
> > I'd like to resume this patch submission here, which is adding support
> > for named address spaces to GNU C++, as is implemented for GNU C. As far
> > as I can tell, there wasn't any specific technical reason that this patch
> > review stalled, back then, in 2022-11? (Jason?)
> >
> > I've now rebased this onto recent GCC trunk, see attached
> > '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
> > just a few merge conflicts that I had to fix up (nothing serious), and
> > I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>
> Hi Thomas,
>
> as far as I can see, there is not a single use of
> TARGET_ADDR_SPACE_FOR_ARTIFICIAL_RODATA resp.
> targetm.addr_space.for_artificial_rodata in the patch.
> There should be a new purpose enum like ARTIFICIAL_RODATA_VTABLE
> when vtables are built. The new enum would be defined in coretypes.h.
>
> The comment in target.def states it is "for optimization purposes only",
> which is no more true for vtables (for vtables it's an ABI thing).
Yes, currently customizing the address space of vtables is not supported.
However, I've not forgotten that part. Adding the support for vtable would
probably need a few round of reviews, so if possible I would like to add support
for them in a subsequent patch. Would you agree?
Paul
> It is great so see that finally, named address spaces will be in g++.
> Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-21 14:19 ` Paul IANNETTA
@ 2026-05-21 19:27 ` Georg-Johann Lay
0 siblings, 0 replies; 86+ messages in thread
From: Georg-Johann Lay @ 2026-05-21 19:27 UTC (permalink / raw)
To: paul.iannetta, Thomas Schwinge, Jason Merrill; +Cc: Patrick Palka, gcc-patches
Am 21.05.26 um 16:19 schrieb Paul IANNETTA:
> Hi,
>
> > Georg-Johann Lay wrote:
> > IMO most important is that it doesn't break C++ code that doesn't
> > use ASes. Issues with the AS feature (like name mangling) can be
> > adjusted once the basic patch is upstream. For example, there
> > should be a means to reject code that writes to an avr AS after
> > load time, like
> >
> > int compute_x ();
> > const __flash int x = compute_x ();
> >
> > Such code isn't possible in C (when compute_x () is not
> > computable at load time), but C++ allows it.
>
> I am not exactly sure what you mean here. Sure in C++, there are some
> initializations which take
> place before main, and the same can be done in C with
> __attribute__(constructor) for example.
Not really, you cannot write to a const variable, neither in C nor
in C++. But what you can do in C++ but not in C is code like:
int volatile v;
const int var = v;
What the C++ compiler does is
1) Pop a static constructor that initializes var AFTER load time.
The constructor writes to var even though it is const.
In C++, this const means "cannot be changed by the user" rather
than read-only like in non-volatile memory.
2) Put var in some .data section instead of .rodata.
Now when var is __flash qualified, this also implies that var
is read-only in the non-volatile memory sense. The compiler must
not change the assigned section, which in avr-gcc is .progmem.data
(hence neither .data nor .rodata). Moreover, due to __flash the
compiler is obliged to use the requested address space; otherwise
you will run into all sorts of wrong-code bugs, for example when
you access __flash objects in inline asm.
> But what do you mean, write to an address space after load time?
> How do you do that in C, where there is no concept of
> load-time/pre-main initialization.
You cannot do it in C; the compiler will throw an error for code
like above, something like "v isn't computable at load time".
But it's valid in C++.
> Would you happen to know how avr on llvm address such problems?
It doesn't and generates garbage, instead of diagnosing. Example:
#define AS1 __attribute__((__address_space__(1)))
int volatile v;
const AS1 int var = v;
int read_as1 (const AS1 int *p) {
return *p;
}
int main () {
v = read_as1 (&var);
}
$ clang --target=avr -xc++ y.c -S -mmcu=atmega8 -Os -Wall
It generates no diagnostic, and code that
1) Initializes var after load time (in a static constructor).
2) Locates var in RAM (.local .comm), readable qua LDD / LDS,
and hence violates the AS1 request.
3) read_as1() correctly reads from flash using LPM instructions.
This code is broken. What clang (and avr-g++) should do is to error
on "const AS1 int var = v" since v is not computable at load time.
Similar to how avr-gcc would diagnose it (for any AS).
IMHO, an initial AS support in g++ need not address this, doing
"garbage in => garbage out". But eventually avr-g++ should do a proper
job and diagnose broken code. This will require a new target hook.
> Using the same mangling scheme as llvm does is clearly not a problem,
> and pretty easy to change.
> However, it would imply that the address spaces numbers are the
> same on both GCC and LLVM,
> so those numbers should probably be documented somewhere.
Clang accesses the n-th 64KiB flash segment as address-space n, e.g.
0x0000..0xffff = address-space 1. In avr-gcc, the address-space
numbers are only internals for 1=__flash, 2=__flash1, 3=__flash2, ...
While clang puts no limits on n and generates happily code for AS(100),
avr-gcc goes only up to 6=__flash5. 7=__flashx and 8=__memx are 24-bit
spaces that are not supported by clang.
Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-21 14:26 ` Paul IANNETTA
@ 2026-05-21 19:32 ` Georg-Johann Lay
0 siblings, 0 replies; 86+ messages in thread
From: Georg-Johann Lay @ 2026-05-21 19:32 UTC (permalink / raw)
To: paul.iannetta, Thomas Schwinge, Jason Merrill, Patrick Palka,
gcc-patches
Am 21.05.26 um 16:26 schrieb Paul IANNETTA:
> Hi,
>
> On Thursday, May 21, 2026 at 09:19:00 PM GMT+9, Georg-Johann Lay
> <avr@gjlay.de> wrote:
> > Am 15.05.26 um 14:17 schrieb Thomas Schwinge:
> > > [Note that emails to <piannetta@kalrayinc.com> bounce; please use
> Paul's
> > > other email address: <paul.iannetta@ens-lyon.org>.]
> > >
> > > Hi!
> > >
> > > I'd like to resume this patch submission here, which is adding support
> > > for named address spaces to GNU C++, as is implemented for GNU C.
> As far
> > > as I can tell, there wasn't any specific technical reason that this
> patch
> > > review stalled, back then, in 2022-11? (Jason?)
> > >
> > > I've now rebased this onto recent GCC trunk, see attached
> > > '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There
> were
> > > just a few merge conflicts that I had to fix up (nothing serious), and
> > > I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
> >
> > Hi Thomas,
> >
> > as far as I can see, there is not a single use of
> > TARGET_ADDR_SPACE_FOR_ARTIFICIAL_RODATA resp.
> > targetm.addr_space.for_artificial_rodata in the patch.
> > There should be a new purpose enum like ARTIFICIAL_RODATA_VTABLE
> > when vtables are built. The new enum would be defined in coretypes.h.
> >
> > The comment in target.def states it is "for optimization purposes only",
> > which is no more true for vtables (for vtables it's an ABI thing).
>
> Yes, currently customizing the address space of vtables is not supported.
> However, I've not forgotten that part.
Great.
> Adding the support for vtable would probably need a few round of reviews,
> so if possible I would like to add support for them in a subsequent patch.
> Would you agree?
>
> Paul
I am fine with that. (And I am not in the position to review code btw).
Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
` (2 preceding siblings ...)
2026-05-21 12:18 ` Georg-Johann Lay
@ 2026-05-22 1:51 ` Jason Merrill
2026-06-16 18:28 ` Thomas Schwinge
2026-06-18 19:57 ` [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++ Thomas Schwinge
` (2 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2026-05-22 1:51 UTC (permalink / raw)
To: Thomas Schwinge, Patrick Palka, gcc-patches; +Cc: Paul Iannetta
On 5/15/26 8:17 AM, Thomas Schwinge wrote:
> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
> other email address: <paul.iannetta@ens-lyon.org>.]
>
>
> Hi!
>
> I'd like to resume this patch submission here, which is adding support
> for named address spaces to GNU C++, as is implemented for GNU C. As far
> as I can tell, there wasn't any specific technical reason that this patch
> review stalled, back then, in 2022-11? (Jason?)
>
> I've now rebased this onto recent GCC trunk, see attached
> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
> just a few merge conflicts that I had to fix up (nothing serious), and
> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>
> The 'gcc/targhooks.cc:default_addr_space_subset_p' change that appeared
> in this v4:
>
> bool
> default_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
> {
> - return (subset == superset);
> + return (subset == 2 && superset == 0) || (subset == superset);
> }
>
> ... is entirely unmotivated, as far as I can tell: probably a local hack
> for testing, that sneaked into the patch posted? This change regresses
> 'gcc.target/i386/addr-space-typeck-1.c' (notice: C test case), which
> again PASSes with that change undone.
This should definitely be dropped.
> The new test case 'g++.dg/template/spec-addr-space.C' needs minor
> adjusting per changed GCC diagnostics (not related to the new named
> addess space support):
>
> @@ -1,8 +1,9 @@
> // { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>
> template <class T>
> -int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate 1: 'template<class T> int f.__seg_gs T\*." }
> // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
> __seg_fs int *a;
> int main() { f(a); } // { dg-error "no matching" }
> -// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
> +// { dg-note "there is 1 candidate" "" { target *-*-* } .-1 }
> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-2 }
>
> With that, all new test cases PASS, and no regressions.
>
> 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), and I'll
> work on test cases some more. I'll also examine if, since then, any new
> code has appeared where 'ADDR_SPACE_CONVERT_EXPR' needs to be handled.
>
> 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.
>
> Full-quote of Paul's email from back then:
>
> On 2022-11-10T16:42:22+0100, Paul Iannetta <piannetta@kalrayinc.com> wrote:
>> Hi,
>>
>> 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, and warnings; and expose the same hooks as
>> the C front-end does. The C++ front-end produces as much warnings
>> that the C front-end, however they are sometimes a bit less
>> informative (especially so, when passing arguments to functions).
>>
>> I also address the following points:
>>
>> 1. The handling of the register storage class is grouped with the
>> other variables with automatic storage. This incurs a slight
>> dis-alignment since you cannot have global register variables do not
>> trigger an error, only a warning.
>>
>> 2. In template unification, I maintain that we don't want any
>> changes to address spaces whatsoever during the unification process,
>> hence ignoring the strict flag. Nevertheless, we allow implicit
>> conversion, and I have verified that, indeed,
>>
>>
>> template <class T> void f(T **);
>> struct A {
>> template <class T> operator T*__seg_fs*();
>> };
>> int main()
>> {
>> f((void* __seg_fs *)0); // (1): void*__seg_fs* -> void** should be OK
>> void (*p)(void * __seg_fs *) = f; // (2): error
>> }
>>
>> works as intended. That is, (1) works if we set __seg_fs as a subspace
>> of the generic address space, and (2) is always an error.
>>
>> 3. In template unification, when unifying __as1 T = __as2 U we want
>> to unify to the __as1 at most, never to __as2 at most, because the
>> function requiring __as1 T may want to mix address space from the
>> bigger address space, therefore I think that we want to have the
>> smaller address-space to be unified into the bigger of the two.
>>
>> 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.
>>
>> 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.
>>
>> 6. Currently, I do not handle classes. I see what I can do in a
>> further revision and maybe add a target hook to hand down to targets
>> the choice of the address space of the vtable.
>>
>> 7. This can't be added as a test case, but I checked that:
>>
>> // In this test case, __seg_gs is a subset of the generic address
>> // space.
>>
>> int f (int *);
>> int main ()
>> {
>> int __seg_fs *pa;
>> int __seg_gs *pb;
>> int *pc;
>> pa = (__seg_fs int *) pb; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint ‘__seg_gs’ address space pointer
>> pa = (int __seg_fs *) pc; return *pa; // warning: cast to ‘__seg_fs’ address space pointer from disjoint generic address space pointer
>> pa = pb; return *pa; // error: invalid conversion from ‘__seg_gs int*’ to ‘__seg_fs int*’
>> pc = pb; return *pc; // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
>> pb = pc; return *pb; // error: invalid conversion from ‘int*’ to ‘__seg_gs int*’
>> pc = pa; return *pb; // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
>> return f (pb); // __seg_gs int * -> int * converted through ADDR_SPACE_CONV_EXPR
>> // return f (pa); // error: invalid conversion from ‘__seg_fs int*’ to ‘int*’
>> // note: initializing argument 1 of ‘int f(int*)’
>> }
>>
>> Thanks,
>> Paul
>>
>> Rebased on current trunk, bootstrapped and regtested.
> Subject: [PATCH] c++: parser - Support for target address spaces in C++
>
> gcc/cp/ChangeLog:
Missing the PR reference and rationale.
> * 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.
This kind of explanation belongs in a code comment rather than the
ChangeLog.
This comment suggests that we want to support these conversions, 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.
I was surprised that no change to standard_conversion was needed, but I
guess the change to comp_ptr_ttypes_real covers that.
I would also expect compare_ics to have an opinion about such
conversions vs conversions within the same address space.
> @@ -840,12 +840,11 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.cc and cp/tree.cc. FIXME. */
> -extern void c_register_addr_space (const char *str, addr_space_t as);
> -
> /* In c-common.cc. */
> extern bool in_late_binary_op;
> extern const char *c_addr_space_name (addr_space_t as);
> +extern const char *c_addr_space_name (addr_space_t as);
We don't need to declare it twice.
> pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
> tsubst_flags_t complain, tree *instrument_expr)
> {
> - tree result, inttype;
> tree restype = ptrdiff_type_node;
> + tree result, inttype;
> +
> + addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
> + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
> tree target_type = TREE_TYPE (ptrtype);
>
> + /* If the operands point into different address spaces, we need to
> + explicitly convert them to pointers into the common address space
> + before we can subtract the numerical address values. */
> + if (as0 != as1)
> + {
> + addr_space_t as_common;
> + tree common_type;
> +
> + if (!addr_space_superset (as0, as1, &as_common))
> + {
> + error_at (loc, "pointers to disjoint address spaces "
> + "%qT and %qT in substraction",
Typo in "subtraction".
> @@ -17139,6 +17145,49 @@ grokdeclarator (const cp_declarator *declarator,
> && (!VAR_P (decl) || !DECL_TEMPLATE_INSTANTIATED (decl)))
> cp_apply_type_quals_to_decl (type_quals, decl);
>
> + /* Warn about address space used for things other than static memory or
> + pointers. */
> + addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
> + if (!ADDR_SPACE_GENERIC_P (address_space))
> + {
> + if (VAR_P (decl) && decl_context == NORMAL)
> + {
> + duration_kind dk = decl_storage_duration (decl);
> + switch (dk)
> + {
> + case dk_auto:
> + if (! toplevel_bindings_p ())
> + error ("%qs specified for variable %qs with automatic storage",
> + c_addr_space_name (address_space), name);
The !toplevel_bindings_p check seems redundant with automatic storage
duration.
> @@ -26334,10 +26349,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> arg, parm))
> return unify_cv_qual_mismatch (explain_p, parm, arg);
>
> + int arg_cv_quals = cp_type_quals (arg);
> + int parm_cv_quals = cp_type_quals (parm);
> +
> + /* If PARM does not contain any address spaces constraints it can
> + fully match the address space of ARG. However, if PARM contains an
> + address space constraints, it becomes the upper bound. That is,
> + AS_ARG may be promoted to AS_PARM but not the converse. If we
> + ended up here, it means that `check_cv_quals_for_unify' succeeded
> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> + AS_PARM. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> + addr_space_t as_common = as_parm ? 0 : as_arg;
It seems unclear to talk about as_common being equal to as_parm and then
define a new as_common to 0; let's clarify the comment something like
it means that `check_cv_quals_for_unify' succeeded and that either
AS_PARM was 0 (i.e. no constraints), so the deduced AS_COMMON is AS_ARG,
or AS_COMMON was AS_PARM, so the deduced AS_COMMON is 0 (i.e. no
additional constraints).
> +static void
> +warn_about_cast_crossing_as_boundaries (location_t loc, tree type,
"as" is unclear outside the context of this patch, how about
"warn_about_address_space_cast"?
> + tree expr, tsubst_flags_t complain)
> +{
> + if (TREE_CODE (type) == POINTER_TYPE
> + && TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE
> + && !NULLPTR_TYPE_P (expr)
This line seems useless: NULLPTR_TYPE_P (expr) can never be true, an
expr is not a type. And even TREE_TYPE (expr) has already been
established to be POINTER_TYPE, so it can't also be NULLPTR_TYPE.
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-22 1:51 ` Jason Merrill
@ 2026-06-16 18:28 ` Thomas Schwinge
2026-06-18 14:04 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Thomas Schwinge @ 2026-06-16 18:28 UTC (permalink / raw)
To: Jason Merrill, Arsen Arsenović
Cc: Paul Iannetta, Patrick Palka, gcc-patches
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/87zf2098ah.fsf@euler.schwinge.ddns.net>
"[REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++".
On 2026-05-21T21:51:09-0400, Jason Merrill <jason@redhat.com> 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 <piannetta@kalrayinc.com> 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
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 13:11 ` Paul IANNETTA
@ 2026-06-18 9:35 ` Georg-Johann Lay
2026-06-18 15:02 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-18 9:35 UTC (permalink / raw)
To: paul.iannetta, Jason Merrill, Patrick Palka, gcc-patches,
Thomas Schwinge
Cc: Paul Iannetta
Am 15.05.26 um 15:11 schrieb Paul IANNETTA:
> On Friday, May 15, 2026 at 09:17:14 PM GMT+9, Thomas Schwinge
> <tschwinge@baylibre.com> wrote:
>
> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
> other email address: <paul.iannetta@ens-lyon.org>.]
>
> Hi!
>
> I'd like to resume this patch submission here, which is adding support
> for named address spaces to GNU C++, as is implemented for GNU C. As far
> as I can tell, there wasn't any specific technical reason that this patch
> review stalled, back then, in 2022-11? (Jason?)
>
> I've now rebased this onto recent GCC trunk, see attached
> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
> just a few merge conflicts that I had to fix up (nothing serious), and
> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
Hi Paul,
I have one test case where the generated code for avr is not correct:
int func1 (int x)
{
static const __flash int arr[] = { 123, 456 };
return arr[x];
}
The code should read from AS1 but reads from generic space.
$ avr-g++ -S -Os as.cpp -v
Target: avr
Configured with: ../../source/gcc-master/configure --target=avr
--enable-languages=c,c++
Thread model: single
Supported LTO compression algorithms: zlib
gcc version 17.0.0 20260615 (experimental) (GCC)
Apart from that, better dumping would be nice to have:
Take for example this function for which correct code is
generated:
int func2 (const __flash int *p, int x)
{
return p[x];
}
With -fdump-tree-optimized the respective dump file reads:
;; Function func2 (_Z5func2PU7__flashKii, funcdef_no=1, decl_uid=2281,
cgraph_uid=3, symbol_order=2)
int func2 (const <address-space-1> int * p, int x)
{
unsigned int x.0_1;
unsigned int _2;
const <address-space-1> int * _3;
int _7;
<bb 2> [local count: 1073741824]:
x.0_1 = (unsigned int) x_4(D);
_2 = x.0_1 * 2;
_3 = p_5(D) + _2; <-- uses AS1
_7 = *_3; <-- uses AS1
return _7;
}
So the dump could show more of the involved non-generic ASes.
Then I have some trouble with constructors:
struct SS { int val; };
const __flash SS ss[] = { 123, 456 };
SS read_ss (int x)
{
return ss[x];
}
as.cpp: In function 'SS read_ss(int)':
as.cpp:10:16: error: no matching function for call to 'SS(const __flash
SS&)'
10 | return ss[x];
| ~~~~^
• there are 3 candidates
• candidate 1: 'constexpr SS::SS(const SS&)' (near match)
as.cpp:1:8:
1 | struct SS
| ^~
• conversion of argument 1 would be ill-formed:
• error: binding reference of type 'const SS&' to 'const __flash
SS' discards qualifiers
as.cpp:10:16:
10 | return ss[x];
| ~~~~^
• candidate 2: 'constexpr SS::SS(SS&&)' (near match)
as.cpp:1:8:
1 | struct SS
| ^~
• conversion of argument 1 would be ill-formed:
• error: cannot bind rvalue reference of type 'SS&&' to lvalue of
type 'const __flash SS'
as.cpp:10:16:
10 | return ss[x];
| ~~~~^
• candidate 3: 'constexpr SS::SS()'
as.cpp:1:8:
1 | struct SS
| ^~
• candidate expects 0 arguments, 1 provided
Shouldn't there be a default constructor that reads from AS1?
At least when the class is trivially copyable?
And adding a qualified constructor doesn't work either:
struct SS
{
SS (const __flash SS &ss) : val(ss.val) {}
SS (int i) const __flash : val(i) {}
int val;
};
as.cpp:4:22: error: constructors may not be cv-qualified
4 | SS (int i) const __flash : val(i) {}
| ^~~~~~~
Notice that a non-qualified constructor will pop a static
constructor function that writes to __flash, which is invalid
because __flash cannot be changed after load time.
Johann
> The 'gcc/targhooks.cc:default_addr_space_subset_p' change that appeared
> in this v4:
>
> bool
> default_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
> {
> - return (subset == superset);
> + return (subset == 2 && superset == 0) || (subset == superset);
> }
>
> ... is entirely unmotivated, as far as I can tell: probably a local hack
> for testing, that sneaked into the patch posted? This change regresses
> 'gcc.target/i386/addr-space-typeck-1.c' (notice: C test case), which
> again PASSes with that change undone.
>
> The new test case 'g++.dg/template/spec-addr-space.C' needs minor
> adjusting per changed GCC diagnostics (not related to the new named
> addess space support):
>
> @@ -1,8 +1,9 @@
> // { dg-do compile { target { i?86-*-* x86_64-*-* } } }
>
> template <class T>
> -int f (T __seg_gs *p) { return *p; } // { dg-note "candidate:
> 'template<class T> int f.__seg_gs T\*." }
> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate 1:
> 'template<class T> int f.__seg_gs T\*." }
> // { dg-note "template argument deduction/substitution failed:"
> "" { target *-*-* } .-1 }
> __seg_fs int *a;
> int main() { f(a); } // { dg-error "no matching" }
> -// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible
> cv-qualifiers" "" { target *-*-* } .-1 }
> +// { dg-note "there is 1 candidate" "" { target *-*-* } .-1 }
> +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible
> cv-qualifiers" "" { target *-*-* } .-2 }
>
> With that, all new test cases PASS, and no regressions.
>
> 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), and I'll
> work on test cases some more. I'll also examine if, since then, any new
> code has appeared where 'ADDR_SPACE_CONVERT_EXPR' needs to be handled.
>
> 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.
>
> Full-quote of Paul's email from back then:
>
> On 2022-11-10T16:42:22+0100, Paul Iannetta <piannetta@kalrayinc.com> wrote:
> > Hi,
> >
> > 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, and warnings; and expose the same hooks as
> > the C front-end does. The C++ front-end produces as much warnings
> > that the C front-end, however they are sometimes a bit less
> > informative (especially so, when passing arguments to functions).
> >
> > I also address the following points:
> >
> > 1. The handling of the register storage class is grouped with the
> > other variables with automatic storage. This incurs a slight
> > dis-alignment since you cannot have global register variables do not
> > trigger an error, only a warning.
> >
> > 2. In template unification, I maintain that we don't want any
> > changes to address spaces whatsoever during the unification process,
> > hence ignoring the strict flag. Nevertheless, we allow implicit
> > conversion, and I have verified that, indeed,
> >
> >
> > template <class T> void f(T **);
> > struct A {
> > template <class T> operator T*__seg_fs*();
> > };
> > int main()
> > {
> > f((void* __seg_fs *)0); // (1): void*__seg_fs* -> void** should be OK
> > void (*p)(void * __seg_fs *) = f; // (2): error
> > }
> >
> > works as intended. That is, (1) works if we set __seg_fs as a subspace
> > of the generic address space, and (2) is always an error.
> >
> > 3. In template unification, when unifying __as1 T = __as2 U we want
> > to unify to the __as1 at most, never to __as2 at most, because the
> > function requiring __as1 T may want to mix address space from the
> > bigger address space, therefore I think that we want to have the
> > smaller address-space to be unified into the bigger of the two.
> >
> > 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.
> >
> > 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.
> >
> > 6. Currently, I do not handle classes. I see what I can do in a
> > further revision and maybe add a target hook to hand down to targets
> > the choice of the address space of the vtable.
> >
> > 7. This can't be added as a test case, but I checked that:
> >
> > // In this test case, __seg_gs is a subset of the generic address
> > // space.
> >
> > int f (int *);
> > int main ()
> > {
> > int __seg_fs *pa;
> > int __seg_gs *pb;
> > int *pc;
> > pa = (__seg_fs int *) pb; return *pa; // warning: cast to ‘__seg_fs’
> address space pointer from disjoint ‘__seg_gs’ address space pointer
> > pa = (int __seg_fs *) pc; return *pa; // warning: cast to ‘__seg_fs’
> address space pointer from disjoint generic address space pointer
> > pa = pb; return *pa; // error: invalid conversion from ‘__seg_gs
> int*’ to ‘__seg_fs int*’
> > pc = pb; return *pc; // __seg_gs int * -> int * converted through
> ADDR_SPACE_CONV_EXPR
> > pb = pc; return *pb; // error: invalid conversion from ‘int*’ to
> ‘__seg_gs int*’
> > pc = pa; return *pb; // error: invalid conversion from ‘__seg_fs
> int*’ to ‘int*’
> > return f (pb); // __seg_gs int * -> int * converted through
> ADDR_SPACE_CONV_EXPR
> > // return f (pa); // error: invalid conversion from ‘__seg_fs int*’
> to ‘int*’
> > // note: initializing argument 1 of ‘int f(int*)’
> > }
> >
> > Thanks,
> > Paul
> >
> > Rebased on current trunk, bootstrapped and regtested.
>
> Hi,
>
> Thank you for taking the time to rebase this patch on trunk. I've
> compared your rebased patch to my local version and I did not spot
> any significant differences.
>
> Concerning, 'gcc/targhooks.cc:default_addr_space_subset_p', I confirm.
> This is a spurious change, and it does not appear in my local tree.
>
> Paul
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-16 18:28 ` Thomas Schwinge
@ 2026-06-18 14:04 ` Jason Merrill
2026-06-20 18:05 ` Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2026-06-18 14:04 UTC (permalink / raw)
To: Thomas Schwinge, Arsen Arsenović
Cc: Paul Iannetta, Patrick Palka, gcc-patches
On 6/16/26 2:28 PM, Thomas Schwinge wrote:
> 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/87zf2098ah.fsf@euler.schwinge.ddns.net>
> "[REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++".
>
> On 2026-05-21T21:51:09-0400, Jason Merrill <jason@redhat.com> 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 <piannetta@kalrayinc.com> 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?
That certainly seems like a missed-case bug.
> 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);
So we're wrongly setting bad_p on a conversion that isn't actually bad.
> 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.
(apparently not yet)
> (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?
ck_qual vs ck_ptr is a bit of a question, whether these conversions
should fit into the C++ conversion model as a qualification conversion
or pointer conversion. I think ck_qual is the better fit, since they
don't change the pointed-to type. So we want to get ck_qual for (valid)
implicit conversions as well as casts.
Given that, I think handling this in comp_ptr_ttypes_real still makes
sense, rather than adding an additional case in standard_conversion. It
seems that hunk just isn't right yet.
> How do we (properly) request an
> 'ADDR_SPACE_CONVERT_EXPR' to be generated, be it indeed via
> 'cp_fold_convert' or something else?
For a ck_qual, convert_like_internal ends up calling cp_convert ->
ocp_convert -> cp_convert_to_pointer. It seems this last function needs
updating to not assume it can express all conversions between pointers
to the same type with build_nop.
Jason
> 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
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-18 9:35 ` Georg-Johann Lay
@ 2026-06-18 15:02 ` Jason Merrill
2026-06-18 16:45 ` Georg-Johann Lay
2026-06-21 9:27 ` Georg-Johann Lay
0 siblings, 2 replies; 86+ messages in thread
From: Jason Merrill @ 2026-06-18 15:02 UTC (permalink / raw)
To: Georg-Johann Lay, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge
Cc: Paul Iannetta
On 6/18/26 5:35 AM, Georg-Johann Lay wrote:
> Am 15.05.26 um 15:11 schrieb Paul IANNETTA:
>> On Friday, May 15, 2026 at 09:17:14 PM GMT+9, Thomas Schwinge
>> <tschwinge@baylibre.com> wrote:
>>
>> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
>> other email address: <paul.iannetta@ens-lyon.org>.]
>>
>> Hi!
>>
>> I'd like to resume this patch submission here, which is adding support
>> for named address spaces to GNU C++, as is implemented for GNU C. As far
>> as I can tell, there wasn't any specific technical reason that this patch
>> review stalled, back then, in 2022-11? (Jason?)
>>
>> I've now rebased this onto recent GCC trunk, see attached
>> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
>> just a few merge conflicts that I had to fix up (nothing serious), and
>> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>
> Hi Paul,
>
> I have one test case where the generated code for avr is not correct:
>
> int func1 (int x)
> {
> static const __flash int arr[] = { 123, 456 };
> return arr[x];
> }
>
> The code should read from AS1 but reads from generic space.
Sounds like decay_conversion isn't propagating the address space to the
pointer type.
Is AS1 the same as __flash here?
> $ avr-g++ -S -Os as.cpp -v
>
> Target: avr
> Configured with: ../../source/gcc-master/configure --target=avr --
> enable-languages=c,c++
> Thread model: single
> Supported LTO compression algorithms: zlib
> gcc version 17.0.0 20260615 (experimental) (GCC)
>
> Apart from that, better dumping would be nice to have:
>
> Take for example this function for which correct code is
> generated:
>
> int func2 (const __flash int *p, int x)
> {
> return p[x];
> }
>
> With -fdump-tree-optimized the respective dump file reads:
>
> ;; Function func2 (_Z5func2PU7__flashKii, funcdef_no=1, decl_uid=2281,
> cgraph_uid=3, symbol_order=2)
>
> int func2 (const <address-space-1> int * p, int x)
> {
> unsigned int x.0_1;
> unsigned int _2;
> const <address-space-1> int * _3;
> int _7;
>
> <bb 2> [local count: 1073741824]:
> x.0_1 = (unsigned int) x_4(D);
> _2 = x.0_1 * 2;
> _3 = p_5(D) + _2; <-- uses AS1
> _7 = *_3; <-- uses AS1
> return _7;
> }
>
> So the dump could show more of the involved non-generic ASes.
>
> Then I have some trouble with constructors:
>
> struct SS { int val; };
>
> const __flash SS ss[] = { 123, 456 };
>
> SS read_ss (int x)
> {
> return ss[x];
> }
>
> as.cpp: In function 'SS read_ss(int)':
> as.cpp:10:16: error: no matching function for call to 'SS(const __flash
> SS&)'
> 10 | return ss[x];
> | ~~~~^
> • there are 3 candidates
> • candidate 1: 'constexpr SS::SS(const SS&)' (near match)
> as.cpp:1:8:
> 1 | struct SS
> | ^~
> • conversion of argument 1 would be ill-formed:
> • error: binding reference of type 'const SS&' to 'const __flash
> SS' discards qualifiers
> as.cpp:10:16:
> 10 | return ss[x];
> | ~~~~^
> • candidate 2: 'constexpr SS::SS(SS&&)' (near match)
> as.cpp:1:8:
> 1 | struct SS
> | ^~
> • conversion of argument 1 would be ill-formed:
> • error: cannot bind rvalue reference of type 'SS&&' to lvalue of
> type 'const __flash SS'
> as.cpp:10:16:
> 10 | return ss[x];
> | ~~~~^
> • candidate 3: 'constexpr SS::SS()'
> as.cpp:1:8:
> 1 | struct SS
> | ^~
> • candidate expects 0 arguments, 1 provided
>
> Shouldn't there be a default constructor that reads from AS1?
> At least when the class is trivially copyable?
We might want to add a hack to overload resolution to allow arbitrary
name space conversion for a trivial copy constructor argument, or skip
overload resolution entirely for copying a trivial class.
Is __flash not a subset of the generic address space?
> And adding a qualified constructor doesn't work either:
>
> struct SS
> {
> SS (const __flash SS &ss) : val(ss.val) {}
Can you = default this constructor? I suspect we will need to add
support in defaulted_late_check.
> SS (int i) const __flash : val(i) {}
> int val;
> };
>
> as.cpp:4:22: error: constructors may not be cv-qualified
> 4 | SS (int i) const __flash : val(i) {}
> | ^~~~~~~
>
>
> Notice that a non-qualified constructor will pop a static
> constructor function that writes to __flash, which is invalid
> because __flash cannot be changed after load time.
Instead of those invalid qualifiers, declaring the constructor
'constexpr' should work.
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-18 15:02 ` Jason Merrill
@ 2026-06-18 16:45 ` Georg-Johann Lay
2026-06-18 21:47 ` Jason Merrill
2026-06-21 9:27 ` Georg-Johann Lay
1 sibling, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-18 16:45 UTC (permalink / raw)
To: Jason Merrill, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge
Cc: Paul Iannetta
Am 18.06.26 um 17:02 schrieb Jason Merrill:
> On 6/18/26 5:35 AM, Georg-Johann Lay wrote:
>> Am 15.05.26 um 15:11 schrieb Paul IANNETTA:
>>> On Friday, May 15, 2026 at 09:17:14 PM GMT+9, Thomas Schwinge
>>> <tschwinge@baylibre.com> wrote:
>>>
>>> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
>>> other email address: <paul.iannetta@ens-lyon.org>.]
>>>
>>> Hi!
>>>
>>> I'd like to resume this patch submission here, which is adding support
>>> for named address spaces to GNU C++, as is implemented for GNU C. As far
>>> as I can tell, there wasn't any specific technical reason that this
>>> patch
>>> review stalled, back then, in 2022-11? (Jason?)
>>>
>>> I've now rebased this onto recent GCC trunk, see attached
>>> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
>>> just a few merge conflicts that I had to fix up (nothing serious), and
>>> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>>
>> Hi Paul,
>>
>> I have one test case where the generated code for avr is not correct:
>>
>> int func1 (int x)
>> {
>> static const __flash int arr[] = { 123, 456 };
>> return arr[x];
>> }
>>
>> The code should read from AS1 but reads from generic space.
>
> Sounds like decay_conversion isn't propagating the address space to the
> pointer type.
>
> Is AS1 the same as __flash here?
Yes.
>> $ avr-g++ -S -Os as.cpp -v
>>
>> Target: avr
>> Configured with: ../../source/gcc-master/configure --target=avr --
>> enable-languages=c,c++
>> Thread model: single
>> Supported LTO compression algorithms: zlib
>> gcc version 17.0.0 20260615 (experimental) (GCC)
>>
>> Apart from that, better dumping would be nice to have:
>>
>> Take for example this function for which correct code is
>> generated:
>>
>> int func2 (const __flash int *p, int x)
>> {
>> return p[x];
>> }
>>
>> With -fdump-tree-optimized the respective dump file reads:
>>
>> ;; Function func2 (_Z5func2PU7__flashKii, funcdef_no=1, decl_uid=2281,
>> cgraph_uid=3, symbol_order=2)
>>
>> int func2 (const <address-space-1> int * p, int x)
>> {
>> unsigned int x.0_1;
>> unsigned int _2;
>> const <address-space-1> int * _3;
>> int _7;
>>
>> <bb 2> [local count: 1073741824]:
>> x.0_1 = (unsigned int) x_4(D);
>> _2 = x.0_1 * 2;
>> _3 = p_5(D) + _2; <-- uses AS1
>> _7 = *_3; <-- uses AS1
>> return _7;
>> }
>>
>> So the dump could show more of the involved non-generic ASes.
>>
>> Then I have some trouble with constructors:
>>
>> struct SS { int val; };
>>
>> const __flash SS ss[] = { 123, 456 };
>>
>> SS read_ss (int x)
>> {
>> return ss[x];
>> }
>>
>> as.cpp: In function 'SS read_ss(int)':
>> as.cpp:10:16: error: no matching function for call to 'SS(const
>> __flash SS&)'
>> 10 | return ss[x];
>> | ~~~~^
>> • there are 3 candidates
>> • candidate 1: 'constexpr SS::SS(const SS&)' (near match)
>> as.cpp:1:8:
>> 1 | struct SS
>> | ^~
>> • conversion of argument 1 would be ill-formed:
>> • error: binding reference of type 'const SS&' to 'const
>> __flash SS' discards qualifiers
>> as.cpp:10:16:
>> 10 | return ss[x];
>> | ~~~~^
>> • candidate 2: 'constexpr SS::SS(SS&&)' (near match)
>> as.cpp:1:8:
>> 1 | struct SS
>> | ^~
>> • conversion of argument 1 would be ill-formed:
>> • error: cannot bind rvalue reference of type 'SS&&' to lvalue
>> of type 'const __flash SS'
>> as.cpp:10:16:
>> 10 | return ss[x];
>> | ~~~~^
>> • candidate 3: 'constexpr SS::SS()'
>> as.cpp:1:8:
>> 1 | struct SS
>> | ^~
>> • candidate expects 0 arguments, 1 provided
>>
>> Shouldn't there be a default constructor that reads from AS1?
>> At least when the class is trivially copyable?
>
> We might want to add a hack to overload resolution to allow arbitrary
> name space conversion for a trivial copy constructor argument, or skip
> overload resolution entirely for copying a trivial class.
>
> Is __flash not a subset of the generic address space?
The avr back implementation is such that each AS is a subset of
each other AS. The reason is to help code migration from the
old attribute((progmem)) to ASes. progmem is in the generic AS of
course, but users may want to call __flash functions etc.
If the user wants diagnostics for questionable AS casts, they can
-Waddr-space-convert (which doesn't work properly for C++ since the
C++ front doesn't invoke hooks that the C front is invoking).
From a pure hardware perspective, flash is not a subset of generic AS:
You need different instructions for accessing flash vs. RAM, which is
why all that AS stuff is needed to begin with: You cannot tell from
a 16-bit address whether it must be loaded per LPM (flash) or LDx (RAM).
>> And adding a qualified constructor doesn't work either:
>>
>> struct SS
>> {
>> SS (const __flash SS &ss) : val(ss.val) {}
>
> Can you = default this constructor? I suspect we will need to add
> support in defaulted_late_check.
No, doesn't work either:
struct SS
{
int val;
SS (int i) : val(i) {}
constexpr SS (const __flash SS&) noexcept = default;
};
const __flash SS ss[2] = { 123, 456 };
SS read_ss (int x)
{
return ss[x];
}
as-class.cpp:5:15: warning: explicitly defaulted copy constructor is
implicitly deleted because its declared type does not match the type of
an implicit copy constructor [-Wdefaulted-function-deleted]
5 | constexpr SS (const __flash SS&) noexcept = default;
| ^~
as-class.cpp:5:15: note: expected signature: 'constexpr SS::SS(const SS&)'
as-class.cpp: In function 'SS read_ss(int)':
as-class.cpp:12:16: error: use of deleted function 'constexpr
SS::SS(const __flash SS&)'
12 | return ss[x];
| ~~~~^
as-class.cpp:5:15: internal compiler error: in
maybe_explain_implicit_delete, at cp/method.cc:3343
5 | constexpr SS (const __flash SS&) noexcept = default;
| ^~
0x7f814ce8bd8f __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
0x7f814ce8be3f __libc_start_main_impl
../csu/libc-start.c:392
Johann
>> SS (int i) const __flash : val(i) {}
>> int val;
>> };
>>
>> as.cpp:4:22: error: constructors may not be cv-qualified
>> 4 | SS (int i) const __flash : val(i) {}
>> | ^~~~~~~
>>
>>
>> Notice that a non-qualified constructor will pop a static
>> constructor function that writes to __flash, which is invalid
>> because __flash cannot be changed after load time.
> Instead of those invalid qualifiers, declaring the constructor
> 'constexpr' should work.
>
> Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
` (3 preceding siblings ...)
2026-05-22 1:51 ` Jason Merrill
@ 2026-06-18 19:57 ` Thomas Schwinge
2026-06-18 21:49 ` Jason Merrill
2026-06-18 19:57 ` Thomas Schwinge
2026-06-20 7:29 ` Georg-Johann Lay
6 siblings, 1 reply; 86+ messages in thread
From: Thomas Schwinge @ 2026-06-18 19:57 UTC (permalink / raw)
To: Jason Merrill, gcc-patches
Cc: Patrick Palka, Paul Iannetta, Arsen Arsenović
[-- Attachment #1: Type: text/plain, Size: 6899 bytes --]
Hi!
On 2026-05-15T14:17:10+0200, Thomas Schwinge <tschwinge@baylibre.com> wrote:
> I've now rebased [Paul's v4] onto recent GCC trunk, [...]
Regarding 'c_register_addr_space':
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -2894,6 +2921,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> return build_nonstandard_integer_type (width, unsignedp);
> }
>
> +/* Register reserved keyword WORD as qualifier for address space AS. */
> +
> +void
> +c_register_addr_space (const char *word, addr_space_t as)
> +{
> + int rid = RID_FIRST_ADDR_SPACE + as;
> + tree id;
> +
> + /* Address space qualifiers are only supported
> + in C with GNU extensions enabled. */
> + if (c_dialect_objc () || flag_no_asm)
> + return;
> +
> + id = get_identifier (word);
> + C_SET_RID_CODE (id, rid);
> + TREE_LANG_FLAG_0 (id) = 1;
> + ridpointers[rid] = id;
> +}
> +
> /* The C version of the register_builtin_type langhook. */
>
> void
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -840,12 +840,11 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.cc and cp/tree.cc. FIXME. */
> -extern void c_register_addr_space (const char *str, addr_space_t as);
> -
> /* In c-common.cc. */
> @@ -996,6 +995,7 @@ extern bool c_common_init (void);
> extern void c_common_finish (void);
> extern void c_common_parse_file (void);
> extern alias_set_type c_common_get_alias_set (tree);
> +extern void c_register_addr_space (const char *, addr_space_t);
> extern void c_register_builtin_type (tree, const char*);
> extern bool c_promoting_integer_type_p (const_tree);
> extern bool self_promoting_args_p (const_tree);
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -13856,25 +13856,6 @@ c_parse_final_cleanups (void)
> ext_block = NULL;
> }
>
> -/* Register reserved keyword WORD as qualifier for address space AS. */
> -
> -void
> -c_register_addr_space (const char *word, addr_space_t as)
> -{
> - int rid = RID_FIRST_ADDR_SPACE + as;
> - tree id;
> -
> - /* Address space qualifiers are only supported
> - in C with GNU extensions enabled. */
> - if (c_dialect_objc () || flag_no_asm)
> - return;
> -
> - id = get_identifier (word);
> - C_SET_RID_CODE (id, rid);
> - C_IS_RESERVED_WORD (id) = 1;
> - ridpointers [rid] = id;
> -}
> -
> /* Return identifier to look up for omp declare reduction. */
>
> tree
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -6828,15 +6831,6 @@ cp_free_lang_data (tree t)
> DECL_CHAIN (t) = NULL_TREE;
> }
>
> -/* Stub for c-common. Please keep in sync with c-decl.cc.
> - FIXME: If address space support is target specific, then this
> - should be a C target hook. But currently this is not possible,
> - because this function is called via REGISTER_TARGET_PRAGMAS. */
> -void
> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> -{
> -}
> -
> /* Return the number of operands in T that we care about for things like
> mangling. */
The original C front end 'c_register_addr_space' code had:
C_IS_RESERVED_WORD (id) = 1;
Now we're using:
TREE_LANG_FLAG_0 (id) = 1;
(That's the 'gcc/c/c-tree.h' definition of 'C_IS_RESERVED_WORD' expanded.)
That happens to work for C++ front end, but only by chance; as of
Subversion r249571 (Git commit 84c0088f38ce49f390401d11a581cd1a57a008d5)
"Reorder IDENTIFIER flags", the correct incantation for the C++ front end
is:
set_identifier_kind (id, cik_keyword);
Therefore, 'c_register_addr_space' can no longer be in
'gcc/c-family/c-common.cc' (at least not without further surgery), and so
I'm restoring the original definitions in 'gcc/c/c-decl.cc' and
'gcc/cp/tree.cc', and then implement it properly in the latter.
See the attached (incremental to Paul's v4)
"c++: parser - Support for target address spaces in C++: 'c_register_addr_space'",
which I intend to merge into the v5 of this patch, unless there's any
objection, of course. With this, the changes related to
'c_register_addr_space' of trunk vs. upcoming v5 are just:
--- gcc/c-family/c-common.h
+++ gcc/c-family/c-common.h
@@ -841,12 +841,13 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
+/* In c-decl.cc and cp/tree.cc. */
extern void c_register_addr_space (const char *str, addr_space_t as);
/* In c-common.cc. */
--- gcc/cp/tree.cc
+++ gcc/cp/tree.cc
@@ -6991,13 +6999,23 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
+c_register_addr_space (const char *word, addr_space_t as)
{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C++ with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ set_identifier_kind (id, cik_keyword);
+ ridpointers [rid] = id;
}
/* Return the number of operands in T that we care about for things like
--- gcc/doc/tm.texi.in
+++ gcc/doc/tm.texi.in
@@ -7374,7 +7374,7 @@ Internally, address spaces are represented as a small integer in the
range 0 to 15 with address space 0 being reserved for the generic
address space.
-To register a named address space qualifier keyword with the C front end,
+To register a named address space qualifier keyword for GNU C and GNU C++,
the target may call the @code{c_register_addr_space} routine. For example,
the SPU port uses the following to declare @code{__ea} as the keyword for
named address space #1:
(For avoidance of doubt: 'C_SET_RID_CODE (id, rid);' remains valid for
both the C and C++ front ends.)
For simplicity of the patch, I've not changed the 'c_' prefix of
'c_register_addr_space', to make it generic ('register_named_addr_space'
maybe?), but I'm happy to do that, if that's better?
Grüße
Thomas
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-c-parser-Support-for-target-address-spaces-in-C-c_re.patch --]
[-- Type: text/x-diff, Size: 6274 bytes --]
From d7f218e5120826382919f3c4528c9809c3717971 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <tschwinge@baylibre.com>
Date: Thu, 18 Jun 2026 19:46:31 +0200
Subject: [PATCH] c++: parser - Support for target address spaces in C++:
'c_register_addr_space'
The original C front end 'c_register_addr_space' code had:
C_IS_RESERVED_WORD (id) = 1;
Now we're using:
TREE_LANG_FLAG_0 (id) = 1;
(That's the 'gcc/c/c-tree.h' definition of 'C_IS_RESERVED_WORD' expanded.)
That happens to work for C++ front end, but only by chance; as of
Subversion r249571 (Git commit 84c0088f38ce49f390401d11a581cd1a57a008d5)
"Reorder IDENTIFIER flags", the correct incantation for the C++ front end
is:
set_identifier_kind (id, cik_keyword);
Therefore, 'c_register_addr_space' can no longer be in
'gcc/c-family/c-common.cc' (at least not without further surgery), and so
I'm restoring the original definitions in 'gcc/c/c-decl.cc' and
'gcc/cp/tree.cc', and then implement it properly in the latter.
(For avoidance of doubt: 'C_SET_RID_CODE (id, rid);' remains valid for
both the C and C++ front ends.)
For simplicity of the patch, I've not changed the 'c_' prefix of
'c_register_addr_space', to make it generic ('register_named_addr_space'
maybe?), but I'm happy to do that, if that's better?
---
gcc/c-family/c-common.cc | 19 -------------------
gcc/c-family/c-common.h | 4 +++-
gcc/c/c-decl.cc | 19 +++++++++++++++++++
gcc/cp/tree.cc | 19 +++++++++++++++++++
gcc/doc/tm.texi | 2 +-
gcc/doc/tm.texi.in | 2 +-
6 files changed, 43 insertions(+), 22 deletions(-)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 5c7ab0c1fa13..441463dc8a85 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -2926,25 +2926,6 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- TREE_LANG_FLAG_0 (id) = 1;
- ridpointers[rid] = id;
-}
-
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 90f81616b5b3..234c18677cc5 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -841,6 +841,9 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
extern tree (*make_fname_decl) (location_t, tree, int);
+/* In c-decl.cc and cp/tree.cc. */
+extern void c_register_addr_space (const char *str, addr_space_t as);
+
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
@@ -995,7 +998,6 @@ extern bool c_common_init (void);
extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern alias_set_type c_common_get_alias_set (tree);
-extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 37db0a7e62e4..1b2f2b4edece 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -13867,6 +13867,25 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ C_IS_RESERVED_WORD (id) = 1;
+ ridpointers [rid] = id;
+}
+
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 2034782a373b..77f2c7161b6f 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -6999,6 +6999,25 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C++ with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ set_identifier_kind (id, cik_keyword);
+ ridpointers [rid] = id;
+}
+
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 3ea366e13993..46449d1c9a36 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11488,7 +11488,7 @@ Internally, address spaces are represented as a small integer in the
range 0 to 15 with address space 0 being reserved for the generic
address space.
-To register a named address space qualifier keyword with the C front end,
+To register a named address space qualifier keyword for GNU C and GNU C++,
the target may call the @code{c_register_addr_space} routine. For example,
the SPU port uses the following to declare @code{__ea} as the keyword for
named address space #1:
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 12700e4ad8a4..97d62e111ad4 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -7374,7 +7374,7 @@ Internally, address spaces are represented as a small integer in the
range 0 to 15 with address space 0 being reserved for the generic
address space.
-To register a named address space qualifier keyword with the C front end,
+To register a named address space qualifier keyword for GNU C and GNU C++,
the target may call the @code{c_register_addr_space} routine. For example,
the SPU port uses the following to declare @code{__ea} as the keyword for
named address space #1:
--
2.34.1
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
` (4 preceding siblings ...)
2026-06-18 19:57 ` [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++ Thomas Schwinge
@ 2026-06-18 19:57 ` Thomas Schwinge
2026-06-18 21:53 ` Jason Merrill
2026-06-20 7:29 ` Georg-Johann Lay
6 siblings, 1 reply; 86+ messages in thread
From: Thomas Schwinge @ 2026-06-18 19:57 UTC (permalink / raw)
To: Jason Merrill, gcc-patches
Cc: Patrick Palka, Paul Iannetta, Arsen Arsenović
[-- Attachment #1: Type: text/plain, Size: 3061 bytes --]
Hi!
On 2026-05-15T14:17:10+0200, Thomas Schwinge <tschwinge@baylibre.com> wrote:
> I've now rebased [Paul's v4] onto recent GCC trunk, [...]
Arsen is currently working on addressing the items identified in the
other sub-thread, related to implicit/explicit conversions between named
address spaces (thanks, Jason, for your review and comments!), so I'm not
sure if the following then is still valid, but anyway, in the current v4,
regarding 'comp_ptr_ttypes_real':
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
| static bool
| comp_ptr_ttypes_real (tree to, tree from, int constp)
| {
| bool to_more_cv_qualified = false;
| bool is_opaque_pointer = false;
|
| for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
| {
> @@ -11861,6 +11964,30 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> to_more_cv_qualified = true;
> }
>
> + /* Warn about conversions between pointers to disjoint
> + address spaces. */
> + if (TREE_CODE (from) == POINTER_TYPE
> + && TREE_CODE (to) == POINTER_TYPE)
> + {
> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
> + addr_space_t as_common;
> +
> + if (!addr_space_superset (as_to, as_from, &as_common)
> + || as_common != as_to)
> + return false;
> + }
> + else
> + {
> + addr_space_t as_from = TYPE_ADDR_SPACE ((from));
> + addr_space_t as_to = TYPE_ADDR_SPACE ((to));
> + addr_space_t as_common;
> +
> + if (!addr_space_superset (as_to, as_from, &as_common)
> + || as_common != as_to)
> + return false;
> + }
> +
> if (constp > 0)
> constp &= TYPE_READONLY (to);
> }
The 'POINTER_TYPE'/'TREE_TYPE's will be handled in a second outer loop
iteration:
for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
..., so we may simplify this code (..., and fix source code indentation)
as per the attached (incremental to Paul's v4)
"c++: parser - Support for target address spaces in C++: simplify 'comp_ptr_ttypes_real'",
which I intend to merge into the v5 of this patch, unless there's any
objection, of course. It's certainly possible that I'm misunderstanding
the fine print here? If not, with this, the changes related to
'comp_ptr_ttypes_real' of trunk vs. upcoming v5 are just:
--- gcc/cp/typeck.cc
+++ gcc/cp/typeck.cc
@@ -11965,6 +12067,15 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
to_more_cv_qualified = true;
}
+ /* Warn about conversions between pointers to disjoint
+ address spaces. */
+ addr_space_t as_from = TYPE_ADDR_SPACE (from);
+ addr_space_t as_to = TYPE_ADDR_SPACE (to);
+ addr_space_t as_common;
+ if (!addr_space_superset (as_to, as_from, &as_common)
+ || as_common != as_to)
+ return false;
+
if (constp > 0)
constp &= TYPE_READONLY (to);
}
Grüße
Thomas
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-c-parser-Support-for-target-address-spaces-in-C-simp.patch --]
[-- Type: text/x-diff, Size: 1760 bytes --]
From cc312ee7bbd3fa44b007eecec876995f293b5094 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <tschwinge@baylibre.com>
Date: Thu, 18 Jun 2026 20:09:58 +0200
Subject: [PATCH] c++: parser - Support for target address spaces in C++:
simplify 'comp_ptr_ttypes_real'
..., and fix source code indentation.
The 'POINTER_TYPE'/'TREE_TYPE's will be handled in a second outer loop iteration:
for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
---
gcc/cp/typeck.cc | 23 ++++-------------------
1 file changed, 4 insertions(+), 19 deletions(-)
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index ed80427b0f15..f6fffc35f100 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -12067,29 +12067,14 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
to_more_cv_qualified = true;
}
- /* Warn about conversions between pointers to disjoint
- address spaces. */
- if (TREE_CODE (from) == POINTER_TYPE
- && TREE_CODE (to) == POINTER_TYPE)
- {
- addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
- addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
+ /* Warn about conversions between pointers to disjoint
+ address spaces. */
+ addr_space_t as_from = TYPE_ADDR_SPACE (from);
+ addr_space_t as_to = TYPE_ADDR_SPACE (to);
addr_space_t as_common;
-
if (!addr_space_superset (as_to, as_from, &as_common)
|| as_common != as_to)
return false;
- }
- else
- {
- addr_space_t as_from = TYPE_ADDR_SPACE ((from));
- addr_space_t as_to = TYPE_ADDR_SPACE ((to));
- addr_space_t as_common;
-
- if (!addr_space_superset (as_to, as_from, &as_common)
- || as_common != as_to)
- return false;
- }
if (constp > 0)
constp &= TYPE_READONLY (to);
--
2.34.1
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-18 16:45 ` Georg-Johann Lay
@ 2026-06-18 21:47 ` Jason Merrill
2026-06-19 8:50 ` Georg-Johann Lay
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2026-06-18 21:47 UTC (permalink / raw)
To: Georg-Johann Lay, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge
Cc: Paul Iannetta
On 6/18/26 12:45 PM, Georg-Johann Lay wrote:
> Am 18.06.26 um 17:02 schrieb Jason Merrill:
>> On 6/18/26 5:35 AM, Georg-Johann Lay wrote:
>>> Am 15.05.26 um 15:11 schrieb Paul IANNETTA:
>>>> On Friday, May 15, 2026 at 09:17:14 PM GMT+9, Thomas Schwinge
>>>> <tschwinge@baylibre.com> wrote:
>>>>
>>>> [Note that emails to <piannetta@kalrayinc.com> bounce; please use
>>>> Paul's
>>>> other email address: <paul.iannetta@ens-lyon.org>.]
>>>>
>>>> Hi!
>>>>
>>>> I'd like to resume this patch submission here, which is adding support
>>>> for named address spaces to GNU C++, as is implemented for GNU C. As
>>>> far
>>>> as I can tell, there wasn't any specific technical reason that this
>>>> patch
>>>> review stalled, back then, in 2022-11? (Jason?)
>>>>
>>>> I've now rebased this onto recent GCC trunk, see attached
>>>> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There
>>>> were
>>>> just a few merge conflicts that I had to fix up (nothing serious), and
>>>> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>>>
>>> Hi Paul,
>>>
>>> I have one test case where the generated code for avr is not correct:
>>>
>>> int func1 (int x)
>>> {
>>> static const __flash int arr[] = { 123, 456 };
>>> return arr[x];
>>> }
>>>
>>> The code should read from AS1 but reads from generic space.
>>
>> Sounds like decay_conversion isn't propagating the address space to
>> the pointer type.
>>
>> Is AS1 the same as __flash here?
>
> Yes.
>
>>> $ avr-g++ -S -Os as.cpp -v
>>>
>>> Target: avr
>>> Configured with: ../../source/gcc-master/configure --target=avr --
>>> enable-languages=c,c++
>>> Thread model: single
>>> Supported LTO compression algorithms: zlib
>>> gcc version 17.0.0 20260615 (experimental) (GCC)
>>>
>>> Apart from that, better dumping would be nice to have:
>>>
>>> Take for example this function for which correct code is
>>> generated:
>>>
>>> int func2 (const __flash int *p, int x)
>>> {
>>> return p[x];
>>> }
>>>
>>> With -fdump-tree-optimized the respective dump file reads:
>>>
>>> ;; Function func2 (_Z5func2PU7__flashKii, funcdef_no=1,
>>> decl_uid=2281, cgraph_uid=3, symbol_order=2)
>>>
>>> int func2 (const <address-space-1> int * p, int x)
>>> {
>>> unsigned int x.0_1;
>>> unsigned int _2;
>>> const <address-space-1> int * _3;
>>> int _7;
>>>
>>> <bb 2> [local count: 1073741824]:
>>> x.0_1 = (unsigned int) x_4(D);
>>> _2 = x.0_1 * 2;
>>> _3 = p_5(D) + _2; <-- uses AS1
>>> _7 = *_3; <-- uses AS1
>>> return _7;
>>> }
>>>
>>> So the dump could show more of the involved non-generic ASes.
>>>
>>> Then I have some trouble with constructors:
>>>
>>> struct SS { int val; };
>>>
>>> const __flash SS ss[] = { 123, 456 };
>>>
>>> SS read_ss (int x)
>>> {
>>> return ss[x];
>>> }
>>>
>>> as.cpp: In function 'SS read_ss(int)':
>>> as.cpp:10:16: error: no matching function for call to 'SS(const
>>> __flash SS&)'
>>> 10 | return ss[x];
>>> | ~~~~^
>>> • there are 3 candidates
>>> • candidate 1: 'constexpr SS::SS(const SS&)' (near match)
>>> as.cpp:1:8:
>>> 1 | struct SS
>>> | ^~
>>> • conversion of argument 1 would be ill-formed:
>>> • error: binding reference of type 'const SS&' to 'const
>>> __flash SS' discards qualifiers
>>> as.cpp:10:16:
>>> 10 | return ss[x];
>>> | ~~~~^
>>> • candidate 2: 'constexpr SS::SS(SS&&)' (near match)
>>> as.cpp:1:8:
>>> 1 | struct SS
>>> | ^~
>>> • conversion of argument 1 would be ill-formed:
>>> • error: cannot bind rvalue reference of type 'SS&&' to lvalue
>>> of type 'const __flash SS'
>>> as.cpp:10:16:
>>> 10 | return ss[x];
>>> | ~~~~^
>>> • candidate 3: 'constexpr SS::SS()'
>>> as.cpp:1:8:
>>> 1 | struct SS
>>> | ^~
>>> • candidate expects 0 arguments, 1 provided
>>>
>>> Shouldn't there be a default constructor that reads from AS1?
>>> At least when the class is trivially copyable?
>>
>> We might want to add a hack to overload resolution to allow arbitrary
>> name space conversion for a trivial copy constructor argument, or skip
>> overload resolution entirely for copying a trivial class.
>>
>> Is __flash not a subset of the generic address space?
>
> The avr back implementation is such that each AS is a subset of
> each other AS. The reason is to help code migration from the
> old attribute((progmem)) to ASes. progmem is in the generic AS of
> course, but users may want to call __flash functions etc.
So if they're all subsets of each other, then
const __flash SS s;
const SS *p = &s;
should work? That would also make the above error go away.
I see that the patch has no tests for conversion to/from generic address
space pointers.
> If the user wants diagnostics for questionable AS casts, they can
> -Waddr-space-convert (which doesn't work properly for C++ since the
> C++ front doesn't invoke hooks that the C front is invoking).
>
> From a pure hardware perspective, flash is not a subset of generic AS:
> You need different instructions for accessing flash vs. RAM, which is
> why all that AS stuff is needed to begin with: You cannot tell from
> a 16-bit address whether it must be loaded per LPM (flash) or LDx (RAM).
>
>>> And adding a qualified constructor doesn't work either:
>>>
>>> struct SS
>>> {
>>> SS (const __flash SS &ss) : val(ss.val) {}
>>
>> Can you = default this constructor? I suspect we will need to add
>> support in defaulted_late_check.
>
> No, doesn't work either:
>
> struct SS
> {
> int val;
> SS (int i) : val(i) {}
> constexpr SS (const __flash SS&) noexcept = default;
> };
>
> const __flash SS ss[2] = { 123, 456 };
>
> SS read_ss (int x)
> {
> return ss[x];
> }
>
> as-class.cpp:5:15: warning: explicitly defaulted copy constructor is
> implicitly deleted because its declared type does not match the type of
> an implicit copy constructor [-Wdefaulted-function-deleted]
Yes, that's what I was thinking of when I said we'd need to adjust
defaulted_late_check.
>>> int val;
>>> };
>>>
>>> as.cpp:4:22: error: constructors may not be cv-qualified
>>> 4 | SS (int i) const __flash : val(i) {}
>>> | ^~~~~~~
>>>
>>>
>>> Notice that a non-qualified constructor will pop a static
>>> constructor function that writes to __flash, which is invalid
>>> because __flash cannot be changed after load time.
>> Instead of those invalid qualifiers, declaring the constructor
>> 'constexpr' should work.
I was suggesting constexpr on the (int) constructor.
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-18 19:57 ` [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++ Thomas Schwinge
@ 2026-06-18 21:49 ` Jason Merrill
0 siblings, 0 replies; 86+ messages in thread
From: Jason Merrill @ 2026-06-18 21:49 UTC (permalink / raw)
To: Thomas Schwinge, gcc-patches
Cc: Patrick Palka, Paul Iannetta, Arsen Arsenović
On 6/18/26 3:57 PM, Thomas Schwinge wrote:
> Hi!
>
> On 2026-05-15T14:17:10+0200, Thomas Schwinge <tschwinge@baylibre.com> wrote:
>> I've now rebased [Paul's v4] onto recent GCC trunk, [...]
>
> Regarding 'c_register_addr_space':
>
>> --- a/gcc/c-family/c-common.cc
>> +++ b/gcc/c-family/c-common.cc
>
>> @@ -2894,6 +2921,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
>> return build_nonstandard_integer_type (width, unsignedp);
>> }
>>
>> +/* Register reserved keyword WORD as qualifier for address space AS. */
>> +
>> +void
>> +c_register_addr_space (const char *word, addr_space_t as)
>> +{
>> + int rid = RID_FIRST_ADDR_SPACE + as;
>> + tree id;
>> +
>> + /* Address space qualifiers are only supported
>> + in C with GNU extensions enabled. */
>> + if (c_dialect_objc () || flag_no_asm)
>> + return;
>> +
>> + id = get_identifier (word);
>> + C_SET_RID_CODE (id, rid);
>> + TREE_LANG_FLAG_0 (id) = 1;
>> + ridpointers[rid] = id;
>> +}
>> +
>> /* The C version of the register_builtin_type langhook. */
>>
>> void
>
>> --- a/gcc/c-family/c-common.h
>> +++ b/gcc/c-family/c-common.h
>> @@ -840,12 +840,11 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
>>
>> extern tree (*make_fname_decl) (location_t, tree, int);
>>
>> -/* In c-decl.cc and cp/tree.cc. FIXME. */
>> -extern void c_register_addr_space (const char *str, addr_space_t as);
>> -
>> /* In c-common.cc. */
>> @@ -996,6 +995,7 @@ extern bool c_common_init (void);
>> extern void c_common_finish (void);
>> extern void c_common_parse_file (void);
>> extern alias_set_type c_common_get_alias_set (tree);
>> +extern void c_register_addr_space (const char *, addr_space_t);
>> extern void c_register_builtin_type (tree, const char*);
>> extern bool c_promoting_integer_type_p (const_tree);
>> extern bool self_promoting_args_p (const_tree);
>
>> --- a/gcc/c/c-decl.cc
>> +++ b/gcc/c/c-decl.cc
>> @@ -13856,25 +13856,6 @@ c_parse_final_cleanups (void)
>> ext_block = NULL;
>> }
>>
>> -/* Register reserved keyword WORD as qualifier for address space AS. */
>> -
>> -void
>> -c_register_addr_space (const char *word, addr_space_t as)
>> -{
>> - int rid = RID_FIRST_ADDR_SPACE + as;
>> - tree id;
>> -
>> - /* Address space qualifiers are only supported
>> - in C with GNU extensions enabled. */
>> - if (c_dialect_objc () || flag_no_asm)
>> - return;
>> -
>> - id = get_identifier (word);
>> - C_SET_RID_CODE (id, rid);
>> - C_IS_RESERVED_WORD (id) = 1;
>> - ridpointers [rid] = id;
>> -}
>> -
>> /* Return identifier to look up for omp declare reduction. */
>>
>> tree
>
>> --- a/gcc/cp/tree.cc
>> +++ b/gcc/cp/tree.cc
>
>> @@ -6828,15 +6831,6 @@ cp_free_lang_data (tree t)
>> DECL_CHAIN (t) = NULL_TREE;
>> }
>>
>> -/* Stub for c-common. Please keep in sync with c-decl.cc.
>> - FIXME: If address space support is target specific, then this
>> - should be a C target hook. But currently this is not possible,
>> - because this function is called via REGISTER_TARGET_PRAGMAS. */
>> -void
>> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
>> -{
>> -}
>> -
>> /* Return the number of operands in T that we care about for things like
>> mangling. */
>
> The original C front end 'c_register_addr_space' code had:
>
> C_IS_RESERVED_WORD (id) = 1;
>
> Now we're using:
>
> TREE_LANG_FLAG_0 (id) = 1;
>
> (That's the 'gcc/c/c-tree.h' definition of 'C_IS_RESERVED_WORD' expanded.)
>
> That happens to work for C++ front end, but only by chance; as of
> Subversion r249571 (Git commit 84c0088f38ce49f390401d11a581cd1a57a008d5)
> "Reorder IDENTIFIER flags", the correct incantation for the C++ front end
> is:
>
> set_identifier_kind (id, cik_keyword);
>
> Therefore, 'c_register_addr_space' can no longer be in
> 'gcc/c-family/c-common.cc' (at least not without further surgery), and so
> I'm restoring the original definitions in 'gcc/c/c-decl.cc' and
> 'gcc/cp/tree.cc', and then implement it properly in the latter.
>
> See the attached (incremental to Paul's v4)
> "c++: parser - Support for target address spaces in C++: 'c_register_addr_space'",
> which I intend to merge into the v5 of this patch, unless there's any
> objection, of course. With this, the changes related to
> 'c_register_addr_space' of trunk vs. upcoming v5 are just:
>
> --- gcc/c-family/c-common.h
> +++ gcc/c-family/c-common.h
> @@ -841,12 +841,13 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.cc and cp/tree.cc. FIXME. */
> +/* In c-decl.cc and cp/tree.cc. */
> extern void c_register_addr_space (const char *str, addr_space_t as);
>
> /* In c-common.cc. */
>
> --- gcc/cp/tree.cc
> +++ gcc/cp/tree.cc
>
> @@ -6991,13 +6999,23 @@ cp_free_lang_data (tree t)
> DECL_CHAIN (t) = NULL_TREE;
> }
>
> -/* Stub for c-common. Please keep in sync with c-decl.cc.
> - FIXME: If address space support is target specific, then this
> - should be a C target hook. But currently this is not possible,
> - because this function is called via REGISTER_TARGET_PRAGMAS. */
> +/* Register reserved keyword WORD as qualifier for address space AS. */
> +
> void
> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> +c_register_addr_space (const char *word, addr_space_t as)
> {
> + int rid = RID_FIRST_ADDR_SPACE + as;
> + tree id;
> +
> + /* Address space qualifiers are only supported
> + in C++ with GNU extensions enabled. */
> + if (c_dialect_objc () || flag_no_asm)
> + return;
> +
> + id = get_identifier (word);
> + C_SET_RID_CODE (id, rid);
> + set_identifier_kind (id, cik_keyword);
> + ridpointers [rid] = id;
> }
>
> /* Return the number of operands in T that we care about for things like
>
> --- gcc/doc/tm.texi.in
> +++ gcc/doc/tm.texi.in
> @@ -7374,7 +7374,7 @@ Internally, address spaces are represented as a small integer in the
> range 0 to 15 with address space 0 being reserved for the generic
> address space.
>
> -To register a named address space qualifier keyword with the C front end,
> +To register a named address space qualifier keyword for GNU C and GNU C++,
> the target may call the @code{c_register_addr_space} routine. For example,
> the SPU port uses the following to declare @code{__ea} as the keyword for
> named address space #1:
>
> (For avoidance of doubt: 'C_SET_RID_CODE (id, rid);' remains valid for
> both the C and C++ front ends.)
>
> For simplicity of the patch, I've not changed the 'c_' prefix of
> 'c_register_addr_space', to make it generic ('register_named_addr_space'
> maybe?), but I'm happy to do that, if that's better?
No need, lots of functions shared across the c-family have a c_ prefix.
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-18 19:57 ` Thomas Schwinge
@ 2026-06-18 21:53 ` Jason Merrill
0 siblings, 0 replies; 86+ messages in thread
From: Jason Merrill @ 2026-06-18 21:53 UTC (permalink / raw)
To: Thomas Schwinge, gcc-patches
Cc: Patrick Palka, Paul Iannetta, Arsen Arsenović
On 6/18/26 3:57 PM, Thomas Schwinge wrote:
> Hi!
>
> On 2026-05-15T14:17:10+0200, Thomas Schwinge <tschwinge@baylibre.com> wrote:
>> I've now rebased [Paul's v4] onto recent GCC trunk, [...]
>
> Arsen is currently working on addressing the items identified in the
> other sub-thread, related to implicit/explicit conversions between named
> address spaces (thanks, Jason, for your review and comments!), so I'm not
> sure if the following then is still valid, but anyway, in the current v4,
> regarding 'comp_ptr_ttypes_real':
>
>> --- a/gcc/cp/typeck.cc
>> +++ b/gcc/cp/typeck.cc
>
> | static bool
> | comp_ptr_ttypes_real (tree to, tree from, int constp)
> | {
> | bool to_more_cv_qualified = false;
> | bool is_opaque_pointer = false;
> |
> | for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
> | {
>
>> @@ -11861,6 +11964,30 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>> to_more_cv_qualified = true;
>> }
>>
>> + /* Warn about conversions between pointers to disjoint
>> + address spaces. */
>> + if (TREE_CODE (from) == POINTER_TYPE
>> + && TREE_CODE (to) == POINTER_TYPE)
>> + {
>> + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (from));
>> + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (to));
>> + addr_space_t as_common;
>> +
>> + if (!addr_space_superset (as_to, as_from, &as_common)
>> + || as_common != as_to)
>> + return false;
>> + }
>> + else
>> + {
>> + addr_space_t as_from = TYPE_ADDR_SPACE ((from));
>> + addr_space_t as_to = TYPE_ADDR_SPACE ((to));
>> + addr_space_t as_common;
>> +
>> + if (!addr_space_superset (as_to, as_from, &as_common)
>> + || as_common != as_to)
>> + return false;
>> + }
>> +
>> if (constp > 0)
>> constp &= TYPE_READONLY (to);
>> }
>
> The 'POINTER_TYPE'/'TREE_TYPE's will be handled in a second outer loop
> iteration:
>
> for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
>
> ..., so we may simplify this code (..., and fix source code indentation)
> as per the attached (incremental to Paul's v4)
> "c++: parser - Support for target address spaces in C++: simplify 'comp_ptr_ttypes_real'",
> which I intend to merge into the v5 of this patch, unless there's any
> objection, of course. It's certainly possible that I'm misunderstanding
> the fine print here? If not, with this, the changes related to
> 'comp_ptr_ttypes_real' of trunk vs. upcoming v5 are just:
>
> --- gcc/cp/typeck.cc
> +++ gcc/cp/typeck.cc
>
> @@ -11965,6 +12067,15 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> to_more_cv_qualified = true;
> }
>
> + /* Warn about conversions between pointers to disjoint
> + address spaces. */
> + addr_space_t as_from = TYPE_ADDR_SPACE (from);
> + addr_space_t as_to = TYPE_ADDR_SPACE (to);
> + addr_space_t as_common;
> + if (!addr_space_superset (as_to, as_from, &as_common)
> + || as_common != as_to)
> + return false;
> +
> if (constp > 0)
> constp &= TYPE_READONLY (to);
> }
I suspect that at_least_as_qualified_p is going to need adjustment,
perhaps just to use TYPE_QUALS_NO_ADDR_SPACE.
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-18 21:47 ` Jason Merrill
@ 2026-06-19 8:50 ` Georg-Johann Lay
0 siblings, 0 replies; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-19 8:50 UTC (permalink / raw)
To: Jason Merrill, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge
Cc: Paul Iannetta
Am 18.06.26 um 23:47 schrieb Jason Merrill:
> On 6/18/26 12:45 PM, Georg-Johann Lay wrote:
>> Am 18.06.26 um 17:02 schrieb Jason Merrill:
>>> On 6/18/26 5:35 AM, Georg-Johann Lay wrote:
>>>> Am 15.05.26 um 15:11 schrieb Paul IANNETTA:
>>>>> On Friday, May 15, 2026 at 09:17:14 PM GMT+9, Thomas Schwinge
>>>>> <tschwinge@baylibre.com> wrote:
>>>>>
>>>>> [Note that emails to <piannetta@kalrayinc.com> bounce; please use
>>>>> Paul's
>>>>> other email address: <paul.iannetta@ens-lyon.org>.]
>>>>>
>>>>> Hi!
>>>>>
>>>>> I'd like to resume this patch submission here, which is adding support
>>>>> for named address spaces to GNU C++, as is implemented for GNU C.
>>>>> As far
>>>>> as I can tell, there wasn't any specific technical reason that this
>>>>> patch
>>>>> review stalled, back then, in 2022-11? (Jason?)
>>>>>
>>>>> I've now rebased this onto recent GCC trunk, see attached
>>>>> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There
>>>>> were
>>>>> just a few merge conflicts that I had to fix up (nothing serious), and
>>>>> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>>>>
>>>> Hi Paul,
>>>>
>>>> I have one test case where the generated code for avr is not correct:
>>>>
>>>> int func1 (int x)
>>>> {
>>>> static const __flash int arr[] = { 123, 456 };
>>>> return arr[x];
>>>> }
>>>>
>>>> The code should read from AS1 but reads from generic space.
>>>
>>> Sounds like decay_conversion isn't propagating the address space to
>>> the pointer type.
>>>
>>> Is AS1 the same as __flash here?
>>
>> Yes.
>>
>>>> $ avr-g++ -S -Os as.cpp -v
>>>>
>>>> Target: avr
>>>> Configured with: ../../source/gcc-master/configure --target=avr --
>>>> enable-languages=c,c++
>>>> Thread model: single
>>>> Supported LTO compression algorithms: zlib
>>>> gcc version 17.0.0 20260615 (experimental) (GCC)
>>>>
>>>> Apart from that, better dumping would be nice to have:
>>>>
>>>> Take for example this function for which correct code is
>>>> generated:
>>>>
>>>> int func2 (const __flash int *p, int x)
>>>> {
>>>> return p[x];
>>>> }
>>>>
>>>> With -fdump-tree-optimized the respective dump file reads:
>>>>
>>>> ;; Function func2 (_Z5func2PU7__flashKii, funcdef_no=1,
>>>> decl_uid=2281, cgraph_uid=3, symbol_order=2)
>>>>
>>>> int func2 (const <address-space-1> int * p, int x)
>>>> {
>>>> unsigned int x.0_1;
>>>> unsigned int _2;
>>>> const <address-space-1> int * _3;
>>>> int _7;
>>>>
>>>> <bb 2> [local count: 1073741824]:
>>>> x.0_1 = (unsigned int) x_4(D);
>>>> _2 = x.0_1 * 2;
>>>> _3 = p_5(D) + _2; <-- uses AS1
>>>> _7 = *_3; <-- uses AS1
>>>> return _7;
>>>> }
>>>>
>>>> So the dump could show more of the involved non-generic ASes.
>>>>
>>>> Then I have some trouble with constructors:
>>>>
>>>> struct SS { int val; };
>>>>
>>>> const __flash SS ss[] = { 123, 456 };
>>>>
>>>> SS read_ss (int x)
>>>> {
>>>> return ss[x];
>>>> }
>>>>
>>>> as.cpp: In function 'SS read_ss(int)':
>>>> as.cpp:10:16: error: no matching function for call to 'SS(const
>>>> __flash SS&)'
>>>> 10 | return ss[x];
>>>> | ~~~~^
>>>> • there are 3 candidates
>>>> • candidate 1: 'constexpr SS::SS(const SS&)' (near match)
>>>> as.cpp:1:8:
>>>> 1 | struct SS
>>>> | ^~
>>>> • conversion of argument 1 would be ill-formed:
>>>> • error: binding reference of type 'const SS&' to 'const
>>>> __flash SS' discards qualifiers
>>>> as.cpp:10:16:
>>>> 10 | return ss[x];
>>>> | ~~~~^
>>>> • candidate 2: 'constexpr SS::SS(SS&&)' (near match)
>>>> as.cpp:1:8:
>>>> 1 | struct SS
>>>> | ^~
>>>> • conversion of argument 1 would be ill-formed:
>>>> • error: cannot bind rvalue reference of type 'SS&&' to
>>>> lvalue of type 'const __flash SS'
>>>> as.cpp:10:16:
>>>> 10 | return ss[x];
>>>> | ~~~~^
>>>> • candidate 3: 'constexpr SS::SS()'
>>>> as.cpp:1:8:
>>>> 1 | struct SS
>>>> | ^~
>>>> • candidate expects 0 arguments, 1 provided
>>>>
>>>> Shouldn't there be a default constructor that reads from AS1?
>>>> At least when the class is trivially copyable?
>>>
>>> We might want to add a hack to overload resolution to allow arbitrary
>>> name space conversion for a trivial copy constructor argument, or
>>> skip overload resolution entirely for copying a trivial class.
>>>
>>> Is __flash not a subset of the generic address space?
>>
>> The avr back implementation is such that each AS is a subset of
>> each other AS. The reason is to help code migration from the
>> old attribute((progmem)) to ASes. progmem is in the generic AS of
>> course, but users may want to call __flash functions etc.
>
> So if they're all subsets of each other, then
>
> const __flash SS s;
> const SS *p = &s;
>
> should work? That would also make the above error go away.
>
> I see that the patch has no tests for conversion to/from generic address
> space pointers.
The old way of reading from flash is qua inline asm (macros provided bv
libc's avr/pgmspace.h for convenience. So an old code may read:
const int vals[] = { 1, 2 } __attribute((progmem));
... p = &vals[idx];
int flash_read_int (const int *p /*points to flash*/)
{
return pgm_read_int (p); // #include <avr/pgmspace.h>
}
Using a plain *p would use the wrong instructions and read from RAM.
Without changing the interface of flash_read_int(), a code using AS is:
int flash_read_int (const int *p /*points to flash*/)
{
return * (const __flash int*) p;
}
That's why casting between address-spaces is allowed. Though in your
example, wrong code would be generated (except SS *p uses pgm_read_xxx
for access), and -Waddr-space-convert should complain as the cast
is implicit.
Zo find questionable uses, users can -Waddr-space-convert, so that avr's
TARGET_CONVERT_TO_TYPE diagnoses them. The idea is that explicit casts
are okay (wanted by the user) while implicit casts should be diagnosed.
Unfortunately, the c++ front doesn't call TARGET_CONVERT_TO_TYPE whereas
the c front does. (TARGET_ADDR_SPACE_CONVERT is for emitting actual RTL
for such conversions, be they explicit or implicit. It runs late after
LTO streaming, and therefore it is not well suited for diagnostics).
Johann
>> If the user wants diagnostics for questionable AS casts, they can
>> -Waddr-space-convert (which doesn't work properly for C++ since the
>> C++ front doesn't invoke hooks that the C front is invoking).
>>
>> From a pure hardware perspective, flash is not a subset of generic AS:
>> You need different instructions for accessing flash vs. RAM, which is
>> why all that AS stuff is needed to begin with: You cannot tell from
>> a 16-bit address whether it must be loaded per LPM (flash) or LDx (RAM).
>>
>>>> And adding a qualified constructor doesn't work either:
>>>>
>>>> struct SS
>>>> {
>>>> SS (const __flash SS &ss) : val(ss.val) {}
>>>
>>> Can you = default this constructor? I suspect we will need to add
>>> support in defaulted_late_check.
>>
>> No, doesn't work either:
>>
>> struct SS
>> {
>> int val;
>> SS (int i) : val(i) {}
>> constexpr SS (const __flash SS&) noexcept = default;
>> };
>>
>> const __flash SS ss[2] = { 123, 456 };
>>
>> SS read_ss (int x)
>> {
>> return ss[x];
>> }
>>
>> as-class.cpp:5:15: warning: explicitly defaulted copy constructor is
>> implicitly deleted because its declared type does not match the type
>> of an implicit copy constructor [-Wdefaulted-function-deleted]
>
> Yes, that's what I was thinking of when I said we'd need to adjust
> defaulted_late_check.
>
> Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v3] c++: parser - Support for target address spaces in C++
2022-11-14 17:55 ` Jason Merrill
2022-11-15 12:15 ` Georg-Johann Lay
@ 2026-06-19 12:15 ` Georg-Johann Lay
1 sibling, 0 replies; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-19 12:15 UTC (permalink / raw)
To: Jason Merrill, Paul Iannetta; +Cc: gcc-patches
Am 14.11.22 um 18:55 schrieb Jason Merrill:
> On 11/10/22 06:40, Georg-Johann Lay wrote:
>> Am 10.11.22 um 15:08 schrieb Paul Iannetta:
>>> On Thu, Nov 03, 2022 at 02:38:39PM +0100, Georg-Johann Lay wrote:
>>>>> [PATCH v3] c++: parser - Support for target address spaces in C++
>>>> 2. Will it work with compound literals?
>>>> =======================================
>>>>
>>>> Currently, the following C code works for target avr:
>>>>
>>>> const __flash char *pHallo = (const __flash char[]) { "Hallo" };
>>>>
>>>> This is a pointer in RAM (AS0) that holds the address of a string in
>>>> flash
>>>> (AS1) and is initialized with that address. Unfortunately, this does
>>>> not
>>>> work locally:
>>>>
>>>> const __flash char* get_hallo (void)
>>>> {
>>>> [static] const __flash char *p2 = (const __flash char[])
>>>> { "Hallo2" };
>>>> return p2;
>>>> }
>>>>
>>>> foo.c: In function 'get_hallo':
>>>> foo.c: error: compound literal qualified by address-space qualifier
>>>>
>>>> Is there any way to make this work now? Would be great!
>
> I don't object to allowing this, but what's the advantage of this
> pattern over
>
> static __flash const char p2[] = "Hallo2";
>
> ?
It's just the AS version of C/C++ code like:
const char pet1[][13] = { "dog", "cat", "bat", "rat", "hippopotamus" };
const char *pet2[] = { "dog", "cat", "bat", "rat", "hippopotamus" };
In pet1, each element must be as wide as the widest entry, i.e. 13,
wasting space, and there is the annoyance of having to count the
number of chars.
In pet2, each entry costs the length of the string + 2 bytes for the
address, i.e. is more space efficient for the example.
Now TR18037 requires that the default AS is generic, even in
const __flash char *pet2[] = { "dog", "cat", "bat", "rat", "hippopotamus" };
where the only way to reach a string is qua const __flash char*.
The fix is to treat the string literals as compound literal to
const __flash char[], like:
#define FLIT(str) ((const __flash char[]) { str })
const __flash char *const __flash pets3[] = { FLIT("dog"), FLIT("cat"),
FLIT("bat"), FLIT("rat"), FLIT("hippopotamus") };
A bit ugly, but now both the literals and pet3[] are in flash.
>>> Currently, I implement the same restrictions as the C front-end, but I
>>> think that this restriction could be lifted.
>>
>> Hi Paul,
>>
>> this would be great. FYI, due to AVR quirks, .rodata is located in RAM.
>> Reason behind this is that in functions like
>>
>> char get_letter (const char *c)
>> {
>> return *c;
>> }
>>
>> there is no means to determine whether get_letter was called with a
>> const char* or a char*. Accessing flash vs. RAM would require
>> different instructions, thus .rodata is part of RAM, so that RAM
>> accesses will work in either case.
>>
>> The obvious problem is that this wastes RAM. One way out is to define
>> address space in flash and to pass const __flash char*, where
>> respective objects are located in flash (.progmem.data in case of avr).
>>
>> This is fine for objects which the application creates, but there are
>> also artificial objects like vtables or cswtch tables.
>>
>>>> 3. Will TARGET_ADDR_SPACE_DIAGNOSE_USAGE still work?
>>>> ====================================================
>>>>
>>>> Currently there is target hook TARGET_ADDR_SPACE_DIAGNOSE_USAGE.
>>>> I did not see it in your patches, so maybe I just missed it? See
>>>> https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gccint/Named-Address-
>>>> Spaces.html#index-TARGET_005fADDR_005fSPACE_005fDIAGNOSE_005fUSAGE
>>>
>>> That was a point I overlooked in my previous patch. This will be in
>>> my new revision where I also add implicit conversion between
>>> address spaces and also expose TARGET_ADDR_SPACE_CONVERT.
>>>
>>>> 4. Will it be possible to put C++ virtual tables in ASs, and how?
>>>> =================================================================
>>>
>>> Currently, I do not allow the declaration of instances of classes in
>>> an address space, mainly to not have to cope with the handling of the
>>> this pointer. That is,
>>>
>>> __flash Myclass *t;
>>>
>>> does not work. Nevertheless, I admit that this is would be nice to
>>> have.
>>>
>>>> One big complaint about avr-g++ is that there is no way to put
>>>> vtables in flash (address-space 1) and to access them accordingly.
>>>> How can this be achieved with C++ address spaces?
>>>
>>> Do you want only the vtables in the flash address space or do you want
>>> to be able to have the whole class content.
>>
>> My question is about vtables, not the bits that represent some object.
>> vtables are stored independently of objects, usually in .rodata +
>> comdat. Notice that vtables are read-only and in static storage, even
>> if objects are neither.
>>
>> The problem with vtables is that the user has no handle to specify
>> where to locate them -- and even if, due to AVR quirks, the right
>> instruction must be used. Thus just putting vtables in flash by means
>> of some section attribute won't work, only address-spaces can do the
>> trick.
>>
>>> 1. If you only want the vtables, I think that a target hook called
>>> at vtable creation would do the trick.
>>
>> Yes, that would be enough, see https://gcc.gnu.org/PR43745
>
> As you say there, this would be an ABI change, so there would need to be
> a transition strategy. I don't know to what extent AVR users try to use
> older compiled code vs. always rebuilding everything.
Usually, they re-build everything.
My plan is to have a new GNU attribute:
https://sourceware.org/pipermail/binutils/2026-June/149625.html
Unfortunately, that won't help with old compilers that don't set
Tag_GNU_AVR_VTABLE_AS. AFAIU, there is currently no way to tell
if a module is accessing vtables. Emitting vtables could be
recognized when there are specific _Z... symbol.
>>> 2. If you want to be able to have pointer to classes in __flash, I
>>> will need to further the support I have currently implemented to
>>> support the pointer this qualified with an address space.
>>> Retrospectively, I think this have to be implemented.
>>>
>>> Paul
>>
>> Would be great if this would work, but I think this can be really
>> tricky, because it's already tricky for non-class objects.
>
> Indeed, especially if objects of the same type can live either in flash
> or RAM: you'd need 2 or more of each method for the different accesses.
> Perhaps via cloning.
IMHO cloning would be really reasonable as a default mechanism. When
a user wants special code for a specific AS they can still AS-qualify
a method. Though currently, it gives an ICE for virtual tables:
struct AA
{
int val;
virtual int dox () const __flash = 0;
virtual int dox () const = 0;
};
struct BB : AA
{
int dox () const __flash override { return val - 2; }
int dox () const override { return val - 2; }
};
BB bb;
error: Two symbols with same comdat_group are not linked by the
same_comdat_group list.
15 | BB bb;
| ^
_ZNK2BB3doxEv/2 (virtual int BB::dox() const)
Type: function definition analyzed
Visibility: semantic_interposition public weak comdat
comdat_group:_ZNK2BB3doxEv one_only virtual
previous sharing asm name: 1
Address is taken.
References:
Referring: _ZTV2BB/10 (addr)
Function flags: body optimize_size
Called by:
Calls:
_ZNK2BB3doxEv/1 (virtual int BB::dox() const __flash)
Type: function definition analyzed
Visibility: semantic_interposition public weak comdat
comdat_group:_ZNK2BB3doxEv one_only virtual
next sharing asm name: 2
Address is taken.
References:
Referring: _ZTV2BB/10 (addr)
Function flags: body optimize_size
Called by:
Calls:
as-vtab.cpp:15:6: internal compiler error: symtab_node::verify failed
> Simpler might be to declare that objects of a particular class can only
> live in flash.
What would the syntax be?
>> A user has to specify __flash explicitly, which is quite different to
>> plain objects. For example, a const int can live in .rodata, but in
>> cases like
>>
>> extern int func();
>> extern const int ival;
>> const int ival = func();
>>
>> ival would live in .bss and be initialized at runtime by a static
>> constructor. Consequently,
>>
>> const __flash int ival = func();
>>
>> is invalid code that has to be diagnosed [1], because in the avr case,
>> __flash means non-volatile memory, which contradicts initialization at
>> runtime.
>>
>> So only objects that are TREE_READONLY can go into AS __flash,
>> TREE_CONST is not enough.
>>
>> How is this problem addressed? Does this require a new target hook to
>> diagnose such cases, and does the compiler know at that stage that an
>> object will be TREE_READONLY?
>
> That does sound like a job for a new target hook, if there isn't a
> suitable one already.
The current avr BE is using TARGET_INSERT_ATTRIBUTES for the DECLs
and TARGET_CONVERT_TO_TYPE for casts.
Unfortunately, the c++ front has different call behavior and bypasses
many of the checks, e.g. it doesn't call TARGET_CONVERT_TO_TYPE for
the implicit (pointers) casts, and TARGET_INSERT_ATTRIBUTES is only
called for ordinary functions, not for functions that come from
template instantiations. Plus, there is not way to check template
args for sanity.
Diagnosing flash objects that are constructed after load time is
caught in TARGET_ENCODE_SECTION_INFO, though that one runs late
(after LTO streaming).
Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
` (5 preceding siblings ...)
2026-06-18 19:57 ` Thomas Schwinge
@ 2026-06-20 7:29 ` Georg-Johann Lay
2026-06-20 11:10 ` Arsen Arsenović
6 siblings, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-20 7:29 UTC (permalink / raw)
To: Thomas Schwinge, Jason Merrill, Patrick Palka, gcc-patches; +Cc: Paul Iannetta
Am 15.05.26 um 14:17 schrieb Thomas Schwinge:
> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
> other email address: <paul.iannetta@ens-lyon.org>.]
>
> Hi!
>
> I'd like to resume this patch submission here, which is adding support
> for named address spaces to GNU C++, as is implemented for GNU C. As far
> as I can tell, there wasn't any specific technical reason that this patch
> review stalled, back then, in 2022-11? (Jason?)
>
> I've now rebased this onto recent GCC trunk, see attached
> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
> just a few merge conflicts that I had to fix up (nothing serious), and
> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
Hi, I found one more problem on avr:
#define AS1 const __flash
#define AS8 const __memx
typedef int (* AS1 fun_t)();
int foo (AS8 fun_t *f)
{
return (*f) ();
}
Compiling for C, I am getting a parm_decl like this:
<parm_decl 0x7fb7668d3000 f>
<pointer_type 0x7fb7668a77e0>
<pointer_type 0x7fb7668a7690 fun_t address-space-8> read-only
<function_type 0x7fb766782690>
<integer_type 0x7fb76676f5e8 int> return
<void_type 0x7fb7667771f8 void>
and an error message:
x.c:6:21: error: conflicting named address spaces (__memx vs __flash)
6 | int foo (AS8 fun_t *f)
With C++ there is AS9 even though the source says AS8:
<parm_decl 0x7ff87a763088 f>
<pointer_type 0x7ff87a74cbd0>
<pointer_type 0x7ff87a74ce70 fun_t address-space-9> read-only
<function_type 0x7ff87a619930>
<integer_type 0x7ff87a6055e8 int> return
<void_type 0x7ff87a60e1f8 void>
and then ICEs as there is no name for AS9:
x.c:6:5: internal compiler error: in c_addr_space_name, at
c-family/c-common.cc:659
6 | int foo (AS8 fun_t *f)
| ^~~
Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-20 7:29 ` Georg-Johann Lay
@ 2026-06-20 11:10 ` Arsen Arsenović
2026-06-20 12:37 ` Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-06-20 11:10 UTC (permalink / raw)
To: Georg-Johann Lay
Cc: Thomas Schwinge, Jason Merrill, Patrick Palka, gcc-patches,
Paul Iannetta
[-- Attachment #1: Type: text/plain, Size: 2478 bytes --]
Georg-Johann Lay <avr@gjlay.de> writes:
> Am 15.05.26 um 14:17 schrieb Thomas Schwinge:
>> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
>> other email address: <paul.iannetta@ens-lyon.org>.]
>> Hi!
>> I'd like to resume this patch submission here, which is adding support
>> for named address spaces to GNU C++, as is implemented for GNU C. As far
>> as I can tell, there wasn't any specific technical reason that this patch
>> review stalled, back then, in 2022-11? (Jason?)
>> I've now rebased this onto recent GCC trunk, see attached
>> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
>> just a few merge conflicts that I had to fix up (nothing serious), and
>> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>
> Hi, I found one more problem on avr:
>
> #define AS1 const __flash
> #define AS8 const __memx
>
> typedef int (* AS1 fun_t)();
>
> int foo (AS8 fun_t *f)
> {
> return (*f) ();
> }
>
> Compiling for C, I am getting a parm_decl like this:
>
> <parm_decl 0x7fb7668d3000 f>
> <pointer_type 0x7fb7668a77e0>
> <pointer_type 0x7fb7668a7690 fun_t address-space-8> read-only
> <function_type 0x7fb766782690>
> <integer_type 0x7fb76676f5e8 int> return
> <void_type 0x7fb7667771f8 void>
>
> and an error message:
>
> x.c:6:21: error: conflicting named address spaces (__memx vs __flash)
> 6 | int foo (AS8 fun_t *f)
>
> With C++ there is AS9 even though the source says AS8:
>
> <parm_decl 0x7ff87a763088 f>
> <pointer_type 0x7ff87a74cbd0>
> <pointer_type 0x7ff87a74ce70 fun_t address-space-9> read-only
> <function_type 0x7ff87a619930>
> <integer_type 0x7ff87a6055e8 int> return
> <void_type 0x7ff87a60e1f8 void>
>
> and then ICEs as there is no name for AS9:
>
> x.c:6:5: internal compiler error: in c_addr_space_name, at
> c-family/c-common.cc:659
> 6 | int foo (AS8 fun_t *f)
> | ^~~
>
> Johann
Thanks, noted. I can reproduce the lack of diagnostic on GCN in my
version of the patch (still not quite done..); will fix before sending.
The AS9 is obviously a result of doing 'quals | ENCODE_QUAL_ADDR_SPACE
(...)' somewhere.
I wish we didn't store qualifiers as just a packed integer value, so
that errors like this could be caught by the type system.
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-20 11:10 ` Arsen Arsenović
@ 2026-06-20 12:37 ` Arsen Arsenović
0 siblings, 0 replies; 86+ messages in thread
From: Arsen Arsenović @ 2026-06-20 12:37 UTC (permalink / raw)
To: Georg-Johann Lay
Cc: Thomas Schwinge, Jason Merrill, Patrick Palka, gcc-patches,
Paul Iannetta
[-- Attachment #1: Type: text/plain, Size: 4565 bytes --]
Arsen Arsenović <aarsenovic@baylibre.com> writes:
> [[PGP Signed Part:Good signature from 52C294301EA2C493 Arsen Arsenović (Gentoo Developer UID) <arsen@gentoo.org> (trust ultimate) created at 2026-06-20T13:10:08+0200 using EDDSA]]
> Georg-Johann Lay <avr@gjlay.de> writes:
>
>> Am 15.05.26 um 14:17 schrieb Thomas Schwinge:
>>> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
>>> other email address: <paul.iannetta@ens-lyon.org>.]
>>> Hi!
>>> I'd like to resume this patch submission here, which is adding support
>>> for named address spaces to GNU C++, as is implemented for GNU C. As far
>>> as I can tell, there wasn't any specific technical reason that this patch
>>> review stalled, back then, in 2022-11? (Jason?)
>>> I've now rebased this onto recent GCC trunk, see attached
>>> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
>>> just a few merge conflicts that I had to fix up (nothing serious), and
>>> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>>
>> Hi, I found one more problem on avr:
>>
>> #define AS1 const __flash
>> #define AS8 const __memx
>>
>> typedef int (* AS1 fun_t)();
>>
>> int foo (AS8 fun_t *f)
>> {
>> return (*f) ();
>> }
>>
>> Compiling for C, I am getting a parm_decl like this:
>>
>> <parm_decl 0x7fb7668d3000 f>
>> <pointer_type 0x7fb7668a77e0>
>> <pointer_type 0x7fb7668a7690 fun_t address-space-8> read-only
>> <function_type 0x7fb766782690>
>> <integer_type 0x7fb76676f5e8 int> return
>> <void_type 0x7fb7667771f8 void>
>>
>> and an error message:
>>
>> x.c:6:21: error: conflicting named address spaces (__memx vs __flash)
>> 6 | int foo (AS8 fun_t *f)
>>
>> With C++ there is AS9 even though the source says AS8:
>>
>> <parm_decl 0x7ff87a763088 f>
>> <pointer_type 0x7ff87a74cbd0>
>> <pointer_type 0x7ff87a74ce70 fun_t address-space-9> read-only
>> <function_type 0x7ff87a619930>
>> <integer_type 0x7ff87a6055e8 int> return
>> <void_type 0x7ff87a60e1f8 void>
>>
>> and then ICEs as there is no name for AS9:
>>
>> x.c:6:5: internal compiler error: in c_addr_space_name, at
>> c-family/c-common.cc:659
>> 6 | int foo (AS8 fun_t *f)
>> | ^~~
>>
>> Johann
>
> Thanks, noted. I can reproduce the lack of diagnostic on GCN in my
> version of the patch (still not quite done..); will fix before
> sending.
Thanks again. Done:
(gdb) ! cat conflict-johann.cc
#define AS1 const __flat
#define AS2 const __scalar_flat
typedef int (* AS1 fun_t)();
int foo (AS2 fun_t *f)
{
return (*f) ();
}
(gdb) r conflict-johann.cc
Starting program: /srv/data/aarsenovic/amdgcn-amdhsa/obj/gcc-mainline-0-amdgcn-amdhsa-x86_64-linux-gnu/gcc/cc1plus conflict-johann.cc
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
conflict-johann.cc:2:19: error: conflicting named address spaces (__flat vs __scalar_flat)
2 | #define AS2 const __scalar_flat
| ^~~~~~~~~~~~~
conflict-johann.cc:6:10: note: in expansion of macro 'AS2'
6 | int foo (AS2 fun_t *f)
| ^~~
(note that I had to port the testcase to GCN, but it is otherwise
identical)
> The AS9 is obviously a result of doing 'quals | ENCODE_QUAL_ADDR_SPACE
> (...)' somewhere.
>
> I wish we didn't store qualifiers as just a packed integer value, so
> that errors like this could be caught by the type system.
Indeed, it was this one, in decl.cc:grokdeclarator:
type_quals |= cp_type_quals (type);
type = cp_build_qualified_type
(type, type_quals, ((((typedef_decl && !DECL_ARTIFICIAL (typedef_decl))
|| declspecs->decltype_p)
? tf_ignore_bad_quals : 0) | tf_warning_or_error));
/* We might have ignored or rejected some of the qualifiers. */
type_quals = cp_type_quals (type);
:/ 'int' is a convenient shorthand for a set, but it starts being a big
issue when the set ceases to be a simple set, as is the case with the
introduction of address spaces. And it is non-trivial to analyze all
uses of this set-like semantic, as the 'int' type carries very little
semantic weight and is ergo used everywhere.
We should perhaps replace it with some trivially-copyable struct. That
should be just as easy to carry while preventing such errors.
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-18 14:04 ` Jason Merrill
@ 2026-06-20 18:05 ` Arsen Arsenović
2026-06-22 18:14 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-06-20 18:05 UTC (permalink / raw)
To: Jason Merrill; +Cc: Thomas Schwinge, Paul Iannetta, Patrick Palka, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 57780 bytes --]
Jason Merrill <jason@redhat.com> writes:
>>> I was surprised that no change to standard_conversion was needed, but I
>>> guess the change to comp_ptr_ttypes_real covers that.
>
> (apparently not yet)
I've now fixed this in my version of the patch.
> ck_qual vs ck_ptr is a bit of a question, whether these conversions
> should fit into the C++ conversion model as a qualification conversion
> or pointer conversion. I think ck_qual is the better fit, since they
> don't change the pointed-to type. So we want to get ck_qual for
> (valid) implicit conversions as well as casts.
>
> Given that, I think handling this in comp_ptr_ttypes_real still makes
> sense, rather than adding an additional case in standard_conversion.
> It seems that hunk just isn't right yet.
Yes, after experimenting with both approaches, I think this is right in
the end.
>> How do we (properly) request an
>> 'ADDR_SPACE_CONVERT_EXPR' to be generated, be it indeed via
>> 'cp_fold_convert' or something else?
>
> For a ck_qual, convert_like_internal ends up calling cp_convert ->
> ocp_convert -> cp_convert_to_pointer. It seems this last function
> needs updating to not assume it can express all conversions between
> pointers to the same type with build_nop.
We actually seem to need to make a far greater change than just a change
to this one function. For instance, reinterpret_casts do not go through
this mechanism for pointers to void or object types, instead just
building a NOP_EXPR directly (due to the TYPE_PTROBV_P (type) &&
TYPE_PTROBV_P (intype) branch).
The FE relies on NOP_EXPR also to represent reinterpret casts at least
in templates, AFAICT. This means that any code building pointer casts
would also be complicated by processing_template_decl.
It seems to be that a cleaner solution is to extend NOP_EXPR to be able
to handle address space conversions in the C++ frontend, by lowering
such NOP_EXPRs into an ADDR_SPACE_CONVERT_EXPR of a NOP_EXPR during
genericization. If you think this is a bad idea, I can go back and fix
it up. But, it did allow me to continue prototyping (and will unblock
Thomas in getting around to the actual work he needs this feature for),
so it's a decent stopgap.
Adjusted patch below, for reference. It is definitely not complete, not
in the least because of the TODOs left therein.
I haven't tested it fully, but it passes the Clang testcase
test/SemaCXX/address-space-conversion.cpp (which I am yet to port into
dg syntax). It is not a very extensive testcase, as it lacks coverage
of any case where there exist a pair of address spaces A and B such that
A ⊃ B but not B ⊃ A. I've tried to mitigate this issue with a
self-test.
I'm also not yet sure whether the template deduction logic here is
correct. Nor whether compare_ics behaves as it should.
Co-authored-by: Paul IANNETTA <paul.iannetta@ens-lyon.org>
Co-authored-by: Thomas Schwinge <tschwinge@baylibre.com>
---
gcc/c-family/c-common.cc | 46 +++
gcc/c-family/c-common.h | 6 +-
gcc/c/c-decl.cc | 19 -
gcc/c/c-typeck.cc | 26 --
gcc/cp/call.cc | 3 +-
gcc/cp/cp-gimplify.cc | 36 ++
gcc/cp/cp-lang.cc | 1 +
gcc/cp/cp-tree.h | 9 +-
gcc/cp/decl.cc | 68 +++-
gcc/cp/error.cc | 1 +
gcc/cp/mangle.cc | 8 +
gcc/cp/parser.cc | 60 ++-
gcc/cp/pt.cc | 33 +-
gcc/cp/tree.cc | 9 -
gcc/cp/typeck.cc | 384 +++++++++++++++++-
gcc/doc/extend.texi | 2 +-
gcc/testsuite/g++.dg/abi/mangle-addr-space1.C | 9 +
gcc/testsuite/g++.dg/abi/mangle-addr-space2.C | 9 +
gcc/testsuite/g++.dg/ext/addr-space-decl.C | 5 +
gcc/testsuite/g++.dg/ext/addr-space-ops.C | 21 +
gcc/testsuite/g++.dg/ext/addr-space-ref.C | 29 ++
gcc/testsuite/g++.dg/parse/addr-space.C | 9 +
gcc/testsuite/g++.dg/parse/addr-space1.C | 10 +
gcc/testsuite/g++.dg/parse/addr-space2.C | 9 +
.../g++.dg/template/addr-space-overload.C | 17 +
.../g++.dg/template/addr-space-strip1.C | 18 +
.../g++.dg/template/addr-space-strip2.C | 17 +
.../g++.dg/template/spec-addr-space.C | 8 +
gcc/tree.h | 6 +-
29 files changed, 799 insertions(+), 79 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-decl.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ops.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ref.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space1.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space2.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-overload.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip1.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip2.C
create mode 100644 gcc/testsuite/g++.dg/template/spec-addr-space.C
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index a16288f4441..1f131688e94 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -660,6 +660,33 @@ c_addr_space_name (addr_space_t as)
return IDENTIFIER_POINTER (ridpointers [rid]);
}
+/* Return true if between two named address spaces, whether there is a superset
+ named address space that encompasses both address spaces. If there is a
+ superset, return which address space is the superset. */
+
+bool
+addr_space_superset (addr_space_t as1, addr_space_t as2,
+ addr_space_t * common)
+{
+ if (as1 == as2)
+ {
+ *common = as1;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as1, as2))
+ {
+ *common = as2;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as2, as1))
+ {
+ *common = as1;
+ return true;
+ }
+ else
+ return false;
+}
+
/* Push current bindings for the function name VAR_DECLS. */
void
@@ -2899,6 +2926,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ TREE_LANG_FLAG_0 (id) = 1;
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 5711e174049..75ed38f8407 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -843,12 +843,11 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
+extern const char *c_addr_space_name (addr_space_t as);
+extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
extern int names_builtin_p (const char *);
@@ -999,6 +998,7 @@ extern bool c_common_init (void);
extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 1b2f2b4edec..37db0a7e62e 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -13867,25 +13867,6 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
-
-void
-c_register_addr_space (const char *word, addr_space_t as)
-{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
-}
-
/* Return identifier to look up for omp declare reduction. */
tree
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 6bce8c42754..8918dd6feb8 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -316,32 +316,6 @@ c_type_promotes_to (tree type)
return type;
}
-/* Return true if between two named address spaces, whether there is a superset
- named address space that encompasses both address spaces. If there is a
- superset, return which address space is the superset. */
-
-static bool
-addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
-{
- if (as1 == as2)
- {
- *common = as1;
- return true;
- }
- else if (targetm.addr_space.subset_p (as1, as2))
- {
- *common = as2;
- return true;
- }
- else if (targetm.addr_space.subset_p (as2, as1))
- {
- *common = as1;
- return true;
- }
- else
- return false;
-}
-
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index a071596085a..d81c0559f86 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -1497,7 +1497,8 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
if (same_type_p (from, to))
/* OK */;
- else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_either))
+ else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_either,
+ /*strict_addr_space=*/false))
/* In a C-style cast, we ignore CV-qualification because we
are allowed to perform a static_cast followed by a
const_cast. */
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 23a1b7ea2cc..b7d321f4341 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -2424,6 +2424,42 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
&& sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT)
&& TYPE_REF_P (TREE_TYPE (stmt)))
ubsan_maybe_instrument_reference (stmt_p);
+
+ /* Appropriately lower conversions between address spaces. We do this so
+ late, as the frontend uses NOP_EXPRs to represent all sorts of
+ casts. */
+ if (tree out_type = TREE_TYPE (stmt),
+ in_type = TREE_TYPE (TREE_OPERAND (stmt, 0));
+ INDIRECT_TYPE_P (out_type) && INDIRECT_TYPE_P (in_type))
+ {
+ addr_space_t in_as = TYPE_ADDR_SPACE (TREE_TYPE (in_type));
+ addr_space_t out_as = TYPE_ADDR_SPACE (TREE_TYPE (out_type));
+ if (in_as != out_as)
+ {
+ auto build_indirect_type
+ = (TYPE_PTR_P (out_type) ? build_pointer_type :
+ TYPE_REF_P (out_type) ? build_reference_type :
+ /* No other indirect types exist, for now. */
+ (gcc_unreachable (),
+ /* Placeholder, for typing. */
+ build_reference_type));
+
+ tree pointee = TREE_TYPE (out_type);
+ /* Remove the address space difference. */
+ pointee = (build_qualified_type
+ (pointee,
+ TYPE_QUALS_NO_ADDR_SPACE (pointee)
+ | ENCODE_QUAL_ADDR_SPACE (in_as)));
+ auto loc = EXPR_LOCATION (stmt);
+ /* Replace the original conversion. */
+ stmt = fold_build1_loc (loc, NOP_EXPR,
+ build_indirect_type (pointee),
+ TREE_OPERAND (stmt, 0));
+ /* Then, re-add an address space conversion. */
+ *stmt_p = fold_build1_loc (loc, ADDR_SPACE_CONVERT_EXPR,
+ out_type, stmt);
+ }
+ }
break;
case CALL_EXPR:
diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
index c9d02cc7302..450fe5f43e9 100644
--- a/gcc/cp/cp-lang.cc
+++ b/gcc/cp/cp-lang.cc
@@ -299,6 +299,7 @@ run_cp_tests (void)
/* Additional C++-specific tests. */
cp_pt_cc_tests ();
cp_tree_cc_tests ();
+ cp_typeck_cc_tests ();
}
} // namespace selftest
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e65c5326b13..9e7fec3086d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6964,6 +6964,7 @@ enum cp_decl_spec {
ds_constinit,
ds_consteval,
ds_this, /* Index of last element of decl_spec_names. */
+ ds_addr_space,
ds_thread,
ds_type_spec,
ds_redefined_builtin_type_spec,
@@ -7000,6 +7001,8 @@ struct cp_decl_specifier_seq {
cp_storage_class storage_class;
/* For the __intN declspec, this stores the index into the int_n_* arrays. */
int int_n_idx;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
/* True iff TYPE_SPEC defines a class or enum. */
BOOL_BITFIELD type_definition_p : 1;
/* True iff multiple types were (erroneously) specified for this
@@ -9023,7 +9026,8 @@ extern tree convert_for_initialization (tree, tree, tree, int,
impl_conv_rhs, tree, int,
tsubst_flags_t);
extern int comp_ptr_ttypes (tree, tree);
-extern bool comp_ptr_ttypes_const (tree, tree, compare_bounds_t);
+extern bool comp_ptr_ttypes_const (tree, tree, compare_bounds_t,
+ bool addr_space_strict = true);
extern bool error_type_p (const_tree);
extern bool ptr_reasonably_similar (const_tree, const_tree);
extern tree build_ptrmemfunc (tree, tree, int, bool,
@@ -9766,6 +9770,9 @@ namespace selftest {
/* Declarations for specific families of tests within cp,
by source file, in alphabetical order. */
extern void cp_pt_cc_tests ();
+ extern void cp_cvt_cc_tests (void);
+ extern void cp_call_cc_tests (void);
+ extern void cp_typeck_cc_tests (void);
extern void cp_tree_cc_tests (void);
} // namespace selftest
#endif /* #if CHECKING_P */
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 23f23b39544..2441c8bfd35 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -6277,6 +6277,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space);
return type_quals;
}
@@ -6399,6 +6401,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
error_at (declspecs->locations[ds_restrict],
"%<__restrict%> can only be specified for objects and "
"functions");
+ else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ error_at (declspecs->locations[ds_addr_space],
+ "address space can only be specified for objects and "
+ "functions");
else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
error_at (declspecs->locations[ds_thread],
"%<__thread%> can only be specified for objects "
@@ -14692,7 +14698,24 @@ grokdeclarator (const cp_declarator *declarator,
&& (declarator || type_quals))
type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
- type_quals |= cp_type_quals (type);
+ auto old_quals = cp_type_quals (type);
+
+ if (addr_space_t as_old = DECODE_QUAL_ADDR_SPACE (old_quals),
+ as_new = DECODE_QUAL_ADDR_SPACE (type_quals);
+ (!ADDR_SPACE_GENERIC_P (as_old) && !ADDR_SPACE_GENERIC_P (as_new)
+ && as_old != as_new))
+ {
+ /* Must exist, as type_quals has an address space. */
+ location_t loc = declspecs->locations[ds_addr_space];
+ error_at (loc, "conflicting named address spaces (%s vs %s)",
+ c_addr_space_name (as_old), c_addr_space_name (as_new));
+ /* We'll stick with the new address space. Probably, the user intends
+ to use the new one, and didn't realize that they have another one
+ specified. */
+ old_quals = CLEAR_QUAL_ADDR_SPACE (old_quals);
+ }
+
+ type_quals |= old_quals;
type = cp_build_qualified_type
(type, type_quals, ((((typedef_decl && !DECL_ARTIFICIAL (typedef_decl))
|| declspecs->decltype_p)
@@ -17141,6 +17164,49 @@ grokdeclarator (const cp_declarator *declarator,
&& (!VAR_P (decl) || !DECL_TEMPLATE_INSTANTIATED (decl)))
cp_apply_type_quals_to_decl (type_quals, decl);
+ /* Warn about address space used for things other than static memory or
+ pointers. */
+ addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
+ if (!ADDR_SPACE_GENERIC_P (address_space))
+ {
+ if (VAR_P (decl) && decl_context == NORMAL)
+ {
+ duration_kind dk = decl_storage_duration (decl);
+ switch (dk)
+ {
+ case dk_auto:
+ if (! toplevel_bindings_p ())
+ error ("%qs specified for variable %qs with automatic storage",
+ c_addr_space_name (address_space), name);
+ break;
+ case dk_static:
+ case dk_thread:
+ case dk_dynamic:
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
+ {
+ if (name)
+ error ("%qs specified for parameter %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for unnamed parameter",
+ c_addr_space_name (address_space));
+ }
+ else if (decl_context == FIELD)
+ {
+ if (name)
+ error ("%qs specified for structure field %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for structure field",
+ c_addr_space_name (address_space));
+ }
+ }
+
return decl;
}
}
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index c2fb6027c52..b6769d0a502 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2843,6 +2843,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
CASE_CONVERT:
case IMPLICIT_CONV_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
case EXCESS_PRECISION_EXPR:
{
tree op = TREE_OPERAND (t, 0);
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index a287cded8ae..48c64c0d776 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2894,6 +2894,14 @@ write_CV_qualifiers_for_type (const tree type)
array. */
cp_cv_quals quals = TYPE_QUALS (type);
+ if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals))
+ {
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 35f7653a4f1..4aa00eb2c42 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
#define INCLUDE_STRING
#include "system.h"
#include "coretypes.h"
+#include "target.h"
#include "cp-tree.h"
#include "c-family/c-common.h"
#include "timevar.h"
@@ -1151,6 +1152,14 @@ cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token)
token->type = CPP_KEYWORD;
/* Record which keyword. */
token->keyword = C_RID_CODE (token->u.value);
+
+ if (token->keyword >= RID_FIRST_ADDR_SPACE
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ {
+ addr_space_t as
+ = (addr_space_t) (token->keyword - RID_FIRST_ADDR_SPACE);
+ targetm.addr_space.diagnose_usage (as, token->location);
+ }
}
else
{
@@ -8902,6 +8911,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
postfix_expression = error_mark_node;
break;
}
+ if (type != error_mark_node
+ && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
+ && current_function_decl)
+ {
+ error
+ ("compound literal qualified by address-space "
+ "qualifier");
+ type = error_mark_node;
+ }
/* Form the representation of the compound-literal. */
postfix_expression
= finish_compound_literal (type, initializer,
@@ -22493,6 +22511,14 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+ if (RID_FIRST_ADDR_SPACE <= keyword
+ && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ }
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -27430,6 +27456,7 @@ cp_parser_ptr_operator (cp_parser* parser,
GNU Extension:
cv-qualifier:
+ address-space-qualifier
__restrict__
Returns a bitmask representing the cv-qualifiers. */
@@ -27466,10 +27493,21 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
+ if (RID_FIRST_ADDR_SPACE <= token->keyword
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ cv_qualifier
+ = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE);
+
if (!cv_qualifier)
break;
- if (cv_quals & cv_qualifier)
+ if (CLEAR_QUAL_ADDR_SPACE (cv_quals & cv_qualifier)
+ /* We need to special-case address spaces, as they are not simple
+ bits; specifying duplicate qualifiers for address spaces that do
+ not share any bits (e.g. for address space 1 and 2) would result
+ in bit-and above producing zero. */
+ || (DECODE_QUAL_ADDR_SPACE (cv_quals)
+ && DECODE_QUAL_ADDR_SPACE (cv_quals)))
{
gcc_rich_location richloc (token->location);
richloc.add_fixit_remove ();
@@ -37650,6 +37688,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
decl_specs->locations[ds] = location;
if (ds == ds_thread)
decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ else if (ds == ds_addr_space)
+ decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
}
else
{
@@ -37682,6 +37722,24 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
error_at (&richloc, "duplicate %qD", token->u.value);
}
}
+ else if (ds == ds_addr_space)
+ {
+ addr_space_t as1 = decl_specs->address_space;
+ addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
+
+ gcc_rich_location richloc (location);
+ richloc.add_fixit_remove ();
+ if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
+ && as1 != as2)
+ error_at (&richloc,
+ "incompatible address space qualifiers %qs and %qs",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
+ error_at (&richloc, "duplicate named address space %qs",
+ c_addr_space_name (as1));
+
+ decl_specs->address_space = as2;
+ }
else
{
/* These correspond to cp-tree.h:cp_decl_spec,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 0c0b3a2cac5..63df3652375 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -25831,8 +25831,16 @@ template_decl_level (tree decl)
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
- int arg_quals = cp_type_quals (arg);
- int parm_quals = cp_type_quals (parm);
+ int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
+
+ /* Try to unify ARG's address space into PARM's address space.
+ If PARM does not have any address space qualifiers (ie., as_parm is 0),
+ there are no constraints on address spaces for this type. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
+ addr_space_t as_common;
+ addr_space_superset (as_arg, as_parm, &as_common);
if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
@@ -25853,6 +25861,9 @@ check_cv_quals_for_unify (int strict, tree arg, tree parm)
return 0;
}
+ if (!(as_parm == as_common || as_parm == 0))
+ return 0;
+
if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
&& (arg_quals & parm_quals) != parm_quals)
return 0;
@@ -26477,10 +26488,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
arg, parm))
return unify_cv_qual_mismatch (explain_p, parm, arg);
+ int arg_cv_quals = cp_type_quals (arg);
+ int parm_cv_quals = cp_type_quals (parm);
+
+ /* If PARM does not contain any address spaces constraints it can
+ fully match the address space of ARG. However, if PARM contains an
+ address space constraints, it becomes the upper bound. That is,
+ AS_ARG may be promoted to AS_PARM but not the converse. If we
+ ended up here, it means that `check_cv_quals_for_unify' succeeded
+ and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
+ AS_PARM. */
+ addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
+ addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
+ addr_space_t as_common = as_parm ? 0 : as_arg;
+
/* Consider the case where ARG is `const volatile int' and
PARM is `const T'. Then, T should be `volatile int'. */
arg = cp_build_qualified_type
(arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
+ int unified_cv =
+ (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
+ | ENCODE_QUAL_ADDR_SPACE (as_common));
+ arg = cp_build_qualified_type (arg, unified_cv, tf_none);
if (arg == error_mark_node)
return unify_invalid (explain_p);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index c9b29900746..c8da1ad845d 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -6996,15 +6996,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 683457ccaf8..6ce65895c36 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
#include "attribs.h"
#include "asan.h"
#include "gimplify.h"
+#include "selftest.h"
static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
static tree cp_build_function_call (tree, tree, tsubst_flags_t);
@@ -719,8 +720,28 @@ composite_pointer_type_r (const op_location_t &location,
return error_mark_node;
result_type = void_type_node;
}
- const int q1 = cp_type_quals (pointee1);
- const int q2 = cp_type_quals (pointee2);
+
+ /* If we reach this point, and have different address spaces, that means that
+ the difference between address spaces is too "deep" to cross (e.g. AS1 T**
+ vs. AS2 T** - note the double star). */
+ auto q1 = cp_type_quals (pointee1);
+ auto q2 = cp_type_quals (pointee2);
+ addr_space_t as_t1 = DECODE_QUAL_ADDR_SPACE (q1);
+ addr_space_t as_t2 = DECODE_QUAL_ADDR_SPACE (q2);
+
+ if (as_t1 != as_t2)
+ {
+ if (complain & tf_error)
+ composite_pointer_error (location, diagnostics::kind::permerror,
+ t1, t2, operation);
+ else
+ return error_mark_node;
+
+ /* Pick an arbitrary AS and stick to it. */
+ q1 = SET_QUAL_ADDR_SPACE (q1, as_t1);
+ q2 = SET_QUAL_ADDR_SPACE (q2, as_t1);
+ }
+
const int quals = q1 | q2;
result_type = cp_build_qualified_type (result_type,
(quals | (*add_const
@@ -781,6 +802,45 @@ composite_pointer_type (const op_location_t &location,
if (null_ptr_cst_p (arg2))
return t1;
+ /* First of, as an extension, merge address-spaces of the top pointees.
+
+ We do this first such that QUALIFIERS(T1) | QUALIFIERS(T2) produces a
+ valid set of qualifiers without a munged address space. */
+ if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2))
+ {
+ addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (t1));
+ addr_space_t as2 = TYPE_ADDR_SPACE (TREE_TYPE (t2));
+
+ if (as1 != as2)
+ {
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as1, as2, &as_common))
+ {
+ as_common = as1;
+ if (complain & tf_error)
+ error_at (location,
+ "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ else
+ return error_mark_node;
+ }
+
+ auto commonize = [&] (tree &t)
+ {
+ auto t_pointee = TREE_TYPE (t);
+ t_pointee = (build_qualified_type
+ (t_pointee,
+ SET_QUAL_ADDR_SPACE (cp_type_quals (t), as_common)));
+ t = build_pointer_type (t_pointee);
+ };
+ commonize (t1);
+ commonize (t2);
+ }
+ }
+
/* We have:
[expr.type]
@@ -2079,7 +2139,11 @@ layout_compatible_type_p (tree type1, tree type2, bool explain/*=false*/)
return true;
}
-/* Returns 1 if TYPE1 is at least as qualified as TYPE2. */
+/* Returns 1 if TYPE1 is at least as qualified as TYPE2, and if its address
+ space is a superset of that of TYPE2.
+
+ Semantically, this is true if an object with qualifiers of TYPE2 can be
+ used as an object with qualifiers of TYPE1. */
bool
at_least_as_qualified_p (const_tree type1, const_tree type2)
@@ -2087,22 +2151,43 @@ at_least_as_qualified_p (const_tree type1, const_tree type2)
int q1 = cp_type_quals (type1);
int q2 = cp_type_quals (type2);
- /* All qualifiers for TYPE2 must also appear in TYPE1. */
- return (q1 & q2) == q2;
+ addr_space_t as1 = DECODE_QUAL_ADDR_SPACE (q1);
+ q1 = CLEAR_QUAL_ADDR_SPACE (q1);
+ addr_space_t as2 = DECODE_QUAL_ADDR_SPACE (q2);
+ q2 = CLEAR_QUAL_ADDR_SPACE (q2);
+
+ return (q1 & q2) == q2 && targetm.addr_space.subset_p (as2, as1);
}
-/* Returns 1 if TYPE1 is more cv-qualified than TYPE2, -1 if TYPE2 is
- more cv-qualified that TYPE1, and 0 otherwise. */
+/* Returns 1 if TYPE1 is more cv-qualified than TYPE2 (i.e. if a TYPE2 object
+ can be used safely if qualified by qualifiers of TYPE1), -1 if TYPE2 is more
+ cv-qualified that TYPE1, and 0 otherwise. */
int
comp_cv_qualification (int q1, int q2)
{
- if (q1 == q2)
+ addr_space_t as1 = DECODE_QUAL_ADDR_SPACE (q1);
+ q1 = CLEAR_QUAL_ADDR_SPACE (q1);
+ addr_space_t as2 = DECODE_QUAL_ADDR_SPACE (q2);
+ q2 = CLEAR_QUAL_ADDR_SPACE (q2);
+
+ /* Address spaces can either fully overlap (whether distinct or not), in
+ which case, the two are interchangeable, or be strict super/subsets, or
+ be disjoint. AS_EQ holds if they are fully overlapped. */
+ bool as_eq = (targetm.addr_space.subset_p (as2, as1)
+ && targetm.addr_space.subset_p (as1, as2));
+
+ if (q1 == q2 && as_eq)
return 0;
- if ((q1 & q2) == q2)
+ /* Here, the address spaces necessarily are either disjoint or in a
+ super/subset relation. A qualifier set Q1 with associated address space
+ AS1 is more qualified than Q2 with AS2 iff Q1 contains all the qualifiers
+ of Q2, and AS1 is a superset of AS2. */
+
+ if ((q1 & q2) == q2 && targetm.addr_space.subset_p (as2, as1))
return 1;
- else if ((q1 & q2) == q1)
+ else if ((q1 & q2) == q1 && targetm.addr_space.subset_p (as1, as2))
return -1;
return 0;
@@ -7211,6 +7296,29 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
tree restype = ptrdiff_type_node;
tree target_type = TREE_TYPE (ptrtype);
+ /* If the operands point into different address spaces, we need to
+ explicitly convert them to pointers into the common address space
+ before we can subtract the numerical address values. */
+ if (addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0))),
+ as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
+ as0 != as1)
+ {
+ addr_space_t as_common;
+ tree common_type;
+
+ if (!addr_space_superset (as0, as1, &as_common))
+ {
+ error_at (loc, "pointers to disjoint address spaces "
+ "%qT and %qT in subtraction",
+ TREE_TYPE (op0), TREE_TYPE (op1));
+ return error_mark_node;
+ }
+
+ common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
+ op0 = convert (common_type, op0);
+ op1 = convert (common_type, op1);
+ }
+
if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
return error_mark_node;
@@ -8712,6 +8820,41 @@ maybe_warn_about_cast_ignoring_quals (location_t loc, tree type,
"type qualifiers ignored on cast result type");
}
+/* Warns if the cast cross address-space boundaries. */
+static void
+maybe_warn_about_addr_spaces (location_t loc, tree type,
+ tree expr, tsubst_flags_t complain)
+{
+ if (TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE
+ && !NULLPTR_TYPE_P (expr)
+ && (complain & tf_warning))
+ {
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type));
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (expr)));
+ addr_space_t as_common;
+
+ if (!addr_space_superset (as_to, as_from, &as_common))
+ {
+ if (ADDR_SPACE_GENERIC_P (as_from))
+ warning_at (loc, 0, "cast to %qs address space pointer "
+ "from disjoint generic address space pointer",
+ c_addr_space_name (as_to));
+
+ else if (ADDR_SPACE_GENERIC_P (as_to))
+ warning_at (loc, 0, "cast to generic address space pointer "
+ "from disjoint %qs address space pointer",
+ c_addr_space_name (as_from));
+
+ else
+ warning_at (loc, 0, "cast to %qs address space pointer "
+ "from disjoint %qs address space pointer",
+ c_addr_space_name (as_to),
+ c_addr_space_name (as_from));
+ }
+ }
+}
+
/* Convert EXPR (an expression with pointer-to-member type) to TYPE
(another pointer-to-member type in the same hierarchy) and return
the converted expression. If ALLOW_INVERSE_P is permitted, a
@@ -9148,6 +9291,7 @@ build_static_cast (location_t loc, tree type, tree oexpr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_spaces (loc, type, expr, complain);
}
if (processing_template_decl)
goto tmpl;
@@ -9471,6 +9615,7 @@ build_reinterpret_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_spaces (loc, type, expr, complain);
}
protected_set_expr_location (r, loc);
return r;
@@ -9657,6 +9802,7 @@ build_const_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_spaces (loc, type, expr, complain);
}
protected_set_expr_location (r, loc);
return r;
@@ -9766,6 +9912,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_spaces (loc, type, expr, complain);
}
else if (complain & tf_error)
build_const_cast_1 (loc, type, value, tf_error, &valid_p);
@@ -9790,6 +9937,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_spaces (loc, type, expr, complain);
/* Non-class rvalues always have cv-unqualified type. */
if (!CLASS_TYPE_P (type))
@@ -11941,8 +12089,9 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
{
bool to_more_cv_qualified = false;
bool is_opaque_pointer = false;
+ bool is_toplevel = true;
- for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
+ for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from), is_toplevel = false)
{
if (TREE_CODE (to) != TREE_CODE (from))
return false;
@@ -11952,6 +12101,20 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
TYPE_OFFSET_BASETYPE (to)))
return false;
+ /* We want to permit a toplevel address space change, but only into a
+ subset address space. TODO(arsen): this change has an effect on
+ compare_ics through comp_cv_qual_signature, but I have no idea what
+ that effect is yet. */
+ if (auto as_to = TYPE_ADDR_SPACE (to), as_from = TYPE_ADDR_SPACE (from);
+ as_to != as_from)
+ {
+ if (!is_toplevel)
+ return false;
+
+ if (!targetm.addr_space.subset_p (as_from, as_to))
+ return false;
+ }
+
/* Const and volatile mean something different for function and
array types, so the usual checks are not appropriate. We'll
check the array type elements in further iterations. */
@@ -11963,6 +12126,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
if (!at_least_as_qualified_p (from, to))
{
if (constp == 0)
+ /* I'm not sure what this check is meant to be covering. */
return false;
to_more_cv_qualified = true;
}
@@ -12087,18 +12251,29 @@ ptr_reasonably_similar (const_tree to, const_tree from)
/* Return true if TO and FROM (both of which are POINTER_TYPEs or
pointer-to-member types) are the same, ignoring cv-qualification at
- all levels. CB says how we should behave when comparing array bounds. */
+ all levels. CB says how we should behave when comparing array bounds.
+
+ This function permits the address-space of a pointer to vary (i.e. if given
+ T AS1* and T AS2*, it considers the difference unimportant), unless
+ STRICT_ADDR_SPACE. */
bool
-comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb)
+comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb,
+ bool strict_addr_space)
{
bool is_opaque_pointer = false;
+ int depth = 0;
- for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
+ for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from), depth++)
{
if (TREE_CODE (to) != TREE_CODE (from))
return false;
+ /* We want to permit a toplevel address space change. */
+ if (auto as_to = TYPE_ADDR_SPACE (to), as_from = TYPE_ADDR_SPACE (from);
+ (strict_addr_space || depth > 1) && as_to != as_from)
+ return false;
+
if (TREE_CODE (from) == OFFSET_TYPE
&& same_type_p (TYPE_OFFSET_BASETYPE (from),
TYPE_OFFSET_BASETYPE (to)))
@@ -12529,3 +12704,184 @@ c_decl_implicit (const_tree)
{
return false;
}
+
+#if CHECKING_P
+#include <iterator>
+
+namespace selftest {
+
+static void
+test_addr_spaces ()
+{
+ /* For testing, we need an address space that is a subset of another. We'll
+ set up a function where 0 is the superset of all, 1 and 2 are distinct,
+ and 2 and 3 are overlapping. TODO(arsen): take this out of here, and put
+ it in selftest.h to be used elsewhere. Also set up other hooks. */
+ temp_override subset_p_override
+ {
+ targetm.addr_space.subset_p,
+ [] (addr_space_t sub, addr_space_t super)
+ {
+ /* Each set is its own (biggest) subset. */
+ if (super == sub)
+ return true;
+ /* 0 is the superset of all other address spaces. */
+ if (super == 0)
+ return true;
+ /* 2 and 3 overlap. */
+ if ((super == 3 && sub == 2)
+ || (super == 2 && sub == 3))
+ return true;
+ return false;
+ }
+ };
+
+ tree int_as[4];
+ tree intp_as[std::size(int_as)];
+ tree param_intp_as[std::size(int_as)];
+ tree intpp_as[std::size(int_as)];
+ for (addr_space_t i = 0; i < std::size (intp_as); i++)
+ {
+ int_as[i] = build_qualified_type (integer_type_node,
+ ENCODE_QUAL_ADDR_SPACE (i));
+ intp_as[i] = build_pointer_type (int_as[i]);
+ param_intp_as[i] = (build_decl
+ (UNKNOWN_LOCATION, PARM_DECL,
+ get_identifier ("some_parm"),
+ intp_as[i]));
+ intpp_as[i] = build_pointer_type (intp_as[i]);
+ }
+
+ /* 0 is a superset of all, ergo, is as qualified as all. */
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[0]));
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[1]));
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[2]));
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[3]));
+
+ /* Conversly, nothing is as qualified as zero (except zero). */
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[1], int_as[0]));
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[2], int_as[0]));
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[3], int_as[0]));
+
+ /* 1 and 2 are distinct, ergo, neither is as qualified as the other. */
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[1], int_as[2]));
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[2], int_as[1]));
+
+ /* 3 and 2 overlap, ergo, both are as qualified the other. */
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[3], int_as[2]));
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[2], int_as[3]));
+
+ /* The same, with comp_cv_qualification. */
+ /* Zero is as qualified as itself. */
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[0]), 0);
+ /* But so are 2 and 3, as they overlap fully. */
+ ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[3]), 0);
+
+ /* AS0 is over-qualified in relation to 1-3. */
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[1]), 1);
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[2]), 1);
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[3]), 1);
+
+ ASSERT_EQ (comp_cv_qualification (int_as[1], int_as[0]), -1);
+ ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[0]), -1);
+ ASSERT_EQ (comp_cv_qualification (int_as[3], int_as[0]), -1);
+
+ /* 1 and 2 are distinct, ergo, neither is as qualified as the other. */
+ ASSERT_EQ (comp_cv_qualification (int_as[1], int_as[2]), 0);
+ ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[1]), 0);
+
+ /* Now to try comp_ptr_ttypes_const. */
+ /* We can assign pointers in any address space to a pointer in zero. */
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[1], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[2], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[3], bounds_none, false));
+
+ /* But not if they're a double-pointer, of course! */
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[1], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[2], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[3], bounds_none, false));
+
+ /* ... or strict. */
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[1], bounds_none));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[2], bounds_none));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[3], bounds_none));
+
+ /* This function permits swapping the top-level address space. */
+ ASSERT_TRUE (comp_ptr_ttypes_const (int_as[2], int_as[3], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (int_as[3], int_as[2], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (int_as[1], int_as[2], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (int_as[2], int_as[1], bounds_none, false));
+
+
+ /* Now to try comp_ptr_ttypes_real. The tests are exactly identical, but
+ they lose a layer of pointers (see comment above comp_ptr_ttypes). */
+ /* We can assign pointers in any address space to a pointer in zero. */
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[0]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[1]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[2]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[3]));
+
+ /* But not if they're a double-pointer, of course! */
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[0]));
+ ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[1]));
+ ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[2]));
+ ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[3]));
+
+ /* This function permits going from a sub-space into a superspace. */
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[2], int_as[3]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[3], int_as[2]));
+ ASSERT_FALSE (comp_ptr_ttypes (int_as[1], int_as[2]));
+ ASSERT_FALSE (comp_ptr_ttypes (int_as[2], int_as[1]));
+
+ /* Testing composite_pointer_type. */
+ op_location_t oploc (UNKNOWN_LOCATION);
+
+ /* A composite of pointers in two address spaces such that one is a superset
+ of another is a pointer in the superset address space. */
+ auto as0_as1_composite =
+ composite_pointer_type (oploc, TREE_TYPE (param_intp_as[0]),
+ TREE_TYPE (param_intp_as[1]),
+ param_intp_as[0], param_intp_as[1],
+ CPO_CONDITIONAL_EXPR,
+ /* We'll allow emitting a diagnostic for the sake
+ of the developer. */
+ tf_warning_or_error);
+ ASSERT_FALSE (error_type_p (as0_as1_composite));
+ ASSERT_TRUE (same_type_p (intp_as[0], as0_as1_composite));
+
+ /* But if they're unrelated, you get an error. */
+ auto as2_as1_composite =
+ composite_pointer_type (oploc, TREE_TYPE (param_intp_as[2]),
+ TREE_TYPE (param_intp_as[1]),
+ param_intp_as[2], param_intp_as[1],
+ CPO_CONDITIONAL_EXPR,
+ /* We expect a diagnostic here, so suppress it to
+ get the corresponding error node. */
+ tf_none);
+ ASSERT_TRUE (error_type_p (as2_as1_composite));
+
+ /* If they overlap, it doesn't really matter which one we land on. */
+ auto as2_as3_composite =
+ composite_pointer_type (oploc, TREE_TYPE (param_intp_as[2]),
+ TREE_TYPE (param_intp_as[3]),
+ param_intp_as[2], param_intp_as[3],
+ CPO_CONDITIONAL_EXPR,
+ /* This should pass. */
+ tf_warning_or_error);
+ ASSERT_FALSE (error_type_p (as2_as3_composite));
+ /* It doesn't really matter which one we get. Or, it shouldn't matter. */
+ ASSERT_TRUE (same_type_p (intp_as[2], as2_as3_composite)
+ || same_type_p (intp_as[3], as2_as3_composite));
+}
+
+void
+cp_typeck_cc_tests ()
+{
+ test_addr_spaces ();
+}
+
+}
+#endif
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9f821f04853..cebab19e874 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1375,7 +1375,7 @@ initializer expressions and issues a warning.
@section Named Address Spaces
@cindex Named Address Spaces
-As an extension, GNU C supports named address spaces as
+As an extension, GNU C and GNU C++ support named address spaces as
defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
address spaces in GCC will evolve as the draft technical report
changes. Calling conventions for any target might also change. At
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
new file mode 100644
index 00000000000..35701c60637
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
+
+int f (int volatile __seg_fs *a)
+{
+ return *a;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
new file mode 100644
index 00000000000..1daf15a73c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
+
+template <class T>
+int f (T *p) { return *p; }
+int g (__seg_fs int *p) { return *p; }
+__seg_fs int *a;
+int main() { f(a); }
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
new file mode 100644
index 00000000000..4199bf375b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+__seg_fs char a, b, c;
+__seg_fs const int *p;
+static /* gives internal linkage to variable q */
+__seg_fs struct { int a; char b; } * __seg_gs q;
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
new file mode 100644
index 00000000000..783e36aabab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+int __seg_fs * fs1;
+int __seg_fs * fs2;
+int __seg_gs * gs1;
+int __seg_gs * gs2;
+
+int
+main ()
+{
+ fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
+ fs1 - fs2;
+ fs1 - gs2; // { dg-error "pointers to disjoint address spaces .__seg_fs int.. and .__seg_gs int.. in substraction" }
+ // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" "" { target *-*-* } .-1 }
+ fs1 == fs2;
+ fs1 != gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" }
+ fs1 = fs2;
+ fs1 = gs2; // { dg-error "invalid conversion from .__seg_gs int.. to .__seg_fs int.." }
+ fs1 > fs2;
+ fs1 < gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
new file mode 100644
index 00000000000..e87649abb5d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
@@ -0,0 +1,29 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-prune-output "does not allow .register. storage class specifier" }
+int __seg_fs * outer_b;
+
+struct s {
+ __seg_fs int * ok;
+ __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
+};
+
+namespace ns_a
+{
+ int __seg_fs * inner_b;
+
+ template<typename T>
+ int f (T &a) { return a; }
+ int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
+ int h (__seg_fs int *a) { return *a; }
+}
+
+int
+main ()
+{
+ int register __seg_gs reg_gs; // { dg-error ".__seg_gs. specified for variable .reg_gs. with automatic storage" }
+ static __seg_gs int static_gs;
+ __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for variable .auto_fs. with automatic storage" }
+ __seg_fs int *pa = outer_b;
+ __seg_fs int& ra = *ns_a::inner_b;
+ return ns_a::f(ra) + ns_a::f(*pa);
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
new file mode 100644
index 00000000000..ebb6316054a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
new file mode 100644
index 00000000000..2e8ee32a885
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-std=gnu++98" }
+
+int
+main ()
+{
+ struct foo {int a; char b[2];} structure;
+ structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
new file mode 100644
index 00000000000..5f64d52885e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs __seg_gs int *a; // { dg-error "incompatible address space qualifiers" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
new file mode 100644
index 00000000000..70dfcce53fa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
+template<typename T, typename U>
+__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
+
+int
+main ()
+{
+ f (fs1, gs1);
+ f (gs1, fs1);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
new file mode 100644
index 00000000000..36c402f5cd2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
@@ -0,0 +1,18 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-require-effective-target c++11 }
+// decltype is only available since c++11
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T> struct strip;
+template<typename T> struct strip<__seg_fs T *> { typedef T type; };
+template<typename T> struct strip<__seg_gs T *> { typedef T type; };
+
+int
+main ()
+{
+ *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
+ // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
new file mode 100644
index 00000000000..a2ffb532087
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+bool f (T __seg_fs * a, U __seg_gs * b)
+{
+ return *(T *) a == *(U *) b;
+ // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 }
+}
+
+int
+main ()
+{
+ return f (fs1, gs1);
+}
diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
new file mode 100644
index 00000000000..ae9f4de0e1f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+template <class T>
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." }
+ // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
+__seg_fs int *a;
+int main() { f(a); } // { dg-error "no matching" }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
diff --git a/gcc/tree.h b/gcc/tree.h
index 0774d6bd5a0..98fec64c53b 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2542,9 +2542,13 @@ extern tree vector_element_bits_tree (const_tree);
/* Encode/decode the named memory support as part of the qualifier. If more
than 8 qualifiers are added, these macros need to be adjusted. */
-#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8)
+#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8)
#define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
+/* Replace the address space of QUALS with AS, evaluating to the new quals. */
+#define SET_QUAL_ADDR_SPACE(QUALS, AS) \
+ ((CLEAR_QUAL_ADDR_SPACE (QUALS)) | (ENCODE_QUAL_ADDR_SPACE (AS)))
+
/* Return all qualifiers except for the address space qualifiers. */
#define CLEAR_QUAL_ADDR_SPACE(X) ((X) & ~0xFF00)
--
2.39.5
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-18 15:02 ` Jason Merrill
2026-06-18 16:45 ` Georg-Johann Lay
@ 2026-06-21 9:27 ` Georg-Johann Lay
2026-06-21 10:54 ` Georg-Johann Lay
1 sibling, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-21 9:27 UTC (permalink / raw)
To: Jason Merrill, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge
Cc: Paul Iannetta, Arsen Arsenović
Am 18.06.26 um 17:02 schrieb Jason Merrill:
> On 6/18/26 5:35 AM, Georg-Johann Lay wrote:
>> Am 15.05.26 um 15:11 schrieb Paul IANNETTA:
>>> On Friday, May 15, 2026 at 09:17:14 PM GMT+9, Thomas Schwinge
>>> <tschwinge@baylibre.com> wrote:
>>>
>>> [Note that emails to <piannetta@kalrayinc.com> bounce; please use Paul's
>>> other email address: <paul.iannetta@ens-lyon.org>.]
>>>
>>> Hi!
>>>
>>> I'd like to resume this patch submission here, which is adding support
>>> for named address spaces to GNU C++, as is implemented for GNU C. As far
>>> as I can tell, there wasn't any specific technical reason that this
>>> patch
>>> review stalled, back then, in 2022-11? (Jason?)
>>>
>>> I've now rebased this onto recent GCC trunk, see attached
>>> '0001-c-parser-Support-for-target-address-spaces-in-C.patch'. There were
>>> just a few merge conflicts that I had to fix up (nothing serious), and
>>> I've bootstrap-tested on x86_64-pc-linux-gnu (only, so far).
>>
>> Hi Paul,
>>
>> I have one test case where the generated code for avr is not correct:
>>
>> int func1 (int x)
>> {
>> static const __flash int arr[] = { 123, 456 };
>> return arr[x];
>> }
>>
>> The code should read from AS1 but reads from generic space.
>
> Sounds like decay_conversion isn't propagating the address space to the
> pointer type.
>
> Jason
Here is a related one:
#define AS1 const __flash
extern AS1 int array[] = { 1 };
The object is located in AS0 (.rodata) but should be in the section
for AS1 (.progmem.data). In avr_insert_attributes the node looks fine:
<var_decl 0x7fc286cb8c78 array>
<array_type 0x7fc286caebd0>
<integer_type 0x7fc286cae0a8 int address-space-1> read-only
Simple values like
extern AS1 int value = 1;
are located correctly.
Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-21 9:27 ` Georg-Johann Lay
@ 2026-06-21 10:54 ` Georg-Johann Lay
2026-06-22 15:24 ` Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-21 10:54 UTC (permalink / raw)
To: Jason Merrill, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge
Cc: Paul Iannetta, Arsen Arsenović
Am 21.06.26 um 11:27 schrieb Georg-Johann Lay:
>
> Here is a related one:
>
> #define AS1 const __flash
> extern AS1 int array[] = { 1 };
>
> The object is located in AS0 (.rodata) but should be in the section
> for AS1 (.progmem.data). In avr_insert_attributes the node looks fine:
>
> <var_decl 0x7fc286cb8c78 array>
> <array_type 0x7fc286caebd0>
> <integer_type 0x7fc286cae0a8 int address-space-1> read-only
>
> Simple values like
>
> extern AS1 int value = 1;
>
> are located correctly.
>
> Johann
Strange things are happening. With the correct
extern AS1 int value = 1;
the TARGET_ASM_NAMED_SECTION hook is called with
flags = 0x8100000 which is 1<<20 for SECTION_NAMED and
0x8000000 for SECTION_MACH_DEP which encodes the AS.
In
extern AS1 int array[] = { 1 };
however, the section flags are 0x140000 which is
SECTION_NAMED plus SECTION_NOTYPE where they should
be SECTION NAMED | SECTION_MACH_DEP, too.
Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-21 10:54 ` Georg-Johann Lay
@ 2026-06-22 15:24 ` Arsen Arsenović
2026-06-22 16:42 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-06-22 15:24 UTC (permalink / raw)
To: Georg-Johann Lay
Cc: Jason Merrill, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge, Paul Iannetta
[-- Attachment #1: Type: text/plain, Size: 1614 bytes --]
Georg-Johann Lay <avr@gjlay.de> writes:
> Am 21.06.26 um 11:27 schrieb Georg-Johann Lay:
>> Here is a related one:
>> #define AS1 const __flash
>> extern AS1 int array[] = { 1 };
>> The object is located in AS0 (.rodata) but should be in the section
>> for AS1 (.progmem.data). In avr_insert_attributes the node looks fine:
>> <var_decl 0x7fc286cb8c78 array>
>> <array_type 0x7fc286caebd0>
>> <integer_type 0x7fc286cae0a8 int address-space-1> read-only
>> Simple values like
>> extern AS1 int value = 1;
>> are located correctly.
>> Johann
>
> Strange things are happening. With the correct
>
> extern AS1 int value = 1;
>
> the TARGET_ASM_NAMED_SECTION hook is called with
> flags = 0x8100000 which is 1<<20 for SECTION_NAMED and
> 0x8000000 for SECTION_MACH_DEP which encodes the AS.
>
> In
>
> extern AS1 int array[] = { 1 };
>
> however, the section flags are 0x140000 which is
> SECTION_NAMED plus SECTION_NOTYPE where they should
> be SECTION NAMED | SECTION_MACH_DEP, too.
ISTM that this is just a result of the C++ FE not propagating the
TYPE_ADDR_SPACE from element type to array type. Can you try the
following on AVR (it worked for GCN):
modified gcc/cp/tree.cc
@@ -1174,6 +1174,7 @@ build_min_array_type (tree elt_type, tree index_type)
tree t = cxx_make_type (ARRAY_TYPE);
TREE_TYPE (t) = elt_type;
TYPE_DOMAIN (t) = index_type;
+ TYPE_ADDR_SPACE (t) = TYPE_ADDR_SPACE (elt_type);
return t;
}
This seems to be equivalent to what tree.cc:build_array_type_1 is doing.
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-22 15:24 ` Arsen Arsenović
@ 2026-06-22 16:42 ` Jason Merrill
2026-06-22 23:07 ` Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2026-06-22 16:42 UTC (permalink / raw)
To: Arsen Arsenović, Georg-Johann Lay
Cc: paul.iannetta, Patrick Palka, gcc-patches, Thomas Schwinge,
Paul Iannetta
On 6/22/26 11:24 AM, Arsen Arsenović wrote:
> Georg-Johann Lay <avr@gjlay.de> writes:
>
>> Am 21.06.26 um 11:27 schrieb Georg-Johann Lay:
>>> Here is a related one:
>>> #define AS1 const __flash
>>> extern AS1 int array[] = { 1 };
>>> The object is located in AS0 (.rodata) but should be in the section
>>> for AS1 (.progmem.data). In avr_insert_attributes the node looks fine:
>>> <var_decl 0x7fc286cb8c78 array>
>>> <array_type 0x7fc286caebd0>
>>> <integer_type 0x7fc286cae0a8 int address-space-1> read-only
>>> Simple values like
>>> extern AS1 int value = 1;
>>> are located correctly.
>>> Johann
>>
>> Strange things are happening. With the correct
>>
>> extern AS1 int value = 1;
>>
>> the TARGET_ASM_NAMED_SECTION hook is called with
>> flags = 0x8100000 which is 1<<20 for SECTION_NAMED and
>> 0x8000000 for SECTION_MACH_DEP which encodes the AS.
>>
>> In
>>
>> extern AS1 int array[] = { 1 };
>>
>> however, the section flags are 0x140000 which is
>> SECTION_NAMED plus SECTION_NOTYPE where they should
>> be SECTION NAMED | SECTION_MACH_DEP, too.
>
> ISTM that this is just a result of the C++ FE not propagating the
> TYPE_ADDR_SPACE from element type to array type. Can you try the
> following on AVR (it worked for GCN):
>
> modified gcc/cp/tree.cc
> @@ -1174,6 +1174,7 @@ build_min_array_type (tree elt_type, tree index_type)
> tree t = cxx_make_type (ARRAY_TYPE);
> TREE_TYPE (t) = elt_type;
> TYPE_DOMAIN (t) = index_type;
> + TYPE_ADDR_SPACE (t) = TYPE_ADDR_SPACE (elt_type);
> return t;
> }
>
> This seems to be equivalent to what tree.cc:build_array_type_1 is doing.
But we don't do that for const. In C++ the quals apply to the element
type rather than the array type; see build_cplus_array_type and
cp_type_quals for places that deal with that. Places that call
cp_apply_type_quals_to_decl (cp_type_quals (type), decl);
handle setting TREE_READONLY and such on the decl; is there a parallel
for address space?
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-20 18:05 ` Arsen Arsenović
@ 2026-06-22 18:14 ` Jason Merrill
2026-06-23 19:49 ` Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2026-06-22 18:14 UTC (permalink / raw)
To: Arsen Arsenović
Cc: Thomas Schwinge, Paul Iannetta, Patrick Palka, gcc-patches
On 6/20/26 2:05 PM, Arsen Arsenović wrote:
> Jason Merrill <jason@redhat.com> writes:
>
>>>> I was surprised that no change to standard_conversion was needed, but I
>>>> guess the change to comp_ptr_ttypes_real covers that.
>>
>> (apparently not yet)
>
> I've now fixed this in my version of the patch.
>
>> ck_qual vs ck_ptr is a bit of a question, whether these conversions
>> should fit into the C++ conversion model as a qualification conversion
>> or pointer conversion. I think ck_qual is the better fit, since they
>> don't change the pointed-to type. So we want to get ck_qual for
>> (valid) implicit conversions as well as casts.
>>
>> Given that, I think handling this in comp_ptr_ttypes_real still makes
>> sense, rather than adding an additional case in standard_conversion.
>> It seems that hunk just isn't right yet.
>
> Yes, after experimenting with both approaches, I think this is right in
> the end.
>
>>> How do we (properly) request an
>>> 'ADDR_SPACE_CONVERT_EXPR' to be generated, be it indeed via
>>> 'cp_fold_convert' or something else?
>>
>> For a ck_qual, convert_like_internal ends up calling cp_convert ->
>> ocp_convert -> cp_convert_to_pointer. It seems this last function
>> needs updating to not assume it can express all conversions between
>> pointers to the same type with build_nop.
>
> We actually seem to need to make a far greater change than just a change
> to this one function. For instance, reinterpret_casts do not go through
> this mechanism for pointers to void or object types, instead just
> building a NOP_EXPR directly (due to the TYPE_PTROBV_P (type) &&
> TYPE_PTROBV_P (intype) branch).
>
> The FE relies on NOP_EXPR also to represent reinterpret casts at least
> in templates, AFAICT. This means that any code building pointer casts
> would also be complicated by processing_template_decl.
>
> It seems to be that a cleaner solution is to extend NOP_EXPR to be able
> to handle address space conversions in the C++ frontend, by lowering
> such NOP_EXPRs into an ADDR_SPACE_CONVERT_EXPR of a NOP_EXPR during
> genericization. If you think this is a bad idea, I can go back and fix
> it up. But, it did allow me to continue prototyping (and will unblock
> Thomas in getting around to the actual work he needs this feature for),
> so it's a decent stopgap.
Hmm, seems reasonable.
> Adjusted patch below, for reference. It is definitely not complete, not
> in the least because of the TODOs left therein.
>
> I haven't tested it fully, but it passes the Clang testcase
> test/SemaCXX/address-space-conversion.cpp (which I am yet to port into
> dg syntax). It is not a very extensive testcase, as it lacks coverage
> of any case where there exist a pair of address spaces A and B such that
> A ⊃ B but not B ⊃ A. I've tried to mitigate this issue with a
> self-test.
>
> I'm also not yet sure whether the template deduction logic here is
> correct. Nor whether compare_ics behaves as it should.
> check_cv_quals_for_unify (int strict, tree arg, tree parm)
> {
> - int arg_quals = cp_type_quals (arg);
> - int parm_quals = cp_type_quals (parm);
> + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
> +
> + /* Try to unify ARG's address space into PARM's address space.
> + If PARM does not have any address space qualifiers (ie., as_parm is 0),
> + there are no constraints on address spaces for this type. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
> + addr_space_t as_common;
> + addr_space_superset (as_arg, as_parm, &as_common);
>
> if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
> && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> @@ -25853,6 +25861,9 @@ check_cv_quals_for_unify (int strict, tree arg, tree parm)
> return 0;
> }
>
> + if (!(as_parm == as_common || as_parm == 0))
> + return 0;
The address space logic needs to respect the 'strict' flags like the
rest of the function. So we want to allow as_parm == 0 iff when
UNIFY_ALLOW_LESS_CV_QUAL, or as_arg == 0 if UNIFY_ALLOW_MORE_CV_QUAL.
And I don't think we want to consider superset here, just compare
as_parm == as_arg. Deduction is stricter than conversion.
> @@ -719,8 +720,28 @@ composite_pointer_type_r (const op_location_t &location,
> return error_mark_node;
> result_type = void_type_node;
> }
> - const int q1 = cp_type_quals (pointee1);
> - const int q2 = cp_type_quals (pointee2);
> +
> + /* If we reach this point, and have different address spaces, that means that
> + the difference between address spaces is too "deep" to cross (e.g. AS1 T**
> + vs. AS2 T** - note the double star). */
> + auto q1 = cp_type_quals (pointee1);
> + auto q2 = cp_type_quals (pointee2);
> + addr_space_t as_t1 = DECODE_QUAL_ADDR_SPACE (q1);
> + addr_space_t as_t2 = DECODE_QUAL_ADDR_SPACE (q2);
> +
> + if (as_t1 != as_t2)
> + {
> + if (complain & tf_error)
> + composite_pointer_error (location, diagnostics::kind::permerror,
> + t1, t2, operation);
> + else
> + return error_mark_node;
We might invert this 'if' and move the error out of it...
> + /* Pick an arbitrary AS and stick to it. */
> + q1 = SET_QUAL_ADDR_SPACE (q1, as_t1);
> + q2 = SET_QUAL_ADDR_SPACE (q2, as_t1);
...since this is a continuation of the error path.
> @@ -26477,10 +26488,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
> arg, parm))
> return unify_cv_qual_mismatch (explain_p, parm, arg);
>
> + int arg_cv_quals = cp_type_quals (arg);
> + int parm_cv_quals = cp_type_quals (parm);
> +
> + /* If PARM does not contain any address spaces constraints it can
> + fully match the address space of ARG. However, if PARM contains an
> + address space constraints, it becomes the upper bound. That is,
> + AS_ARG may be promoted to AS_PARM but not the converse. If we
> + ended up here, it means that `check_cv_quals_for_unify' succeeded
> + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON ==
> + AS_PARM. */
> + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals);
> + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals);
> + addr_space_t as_common = as_parm ? 0 : as_arg;
> +
> /* Consider the case where ARG is `const volatile int' and
> PARM is `const T'. Then, T should be `volatile int'. */
> arg = cp_build_qualified_type
> (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
This line should be removed...
> + int unified_cv =
> + (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals)
> + | ENCODE_QUAL_ADDR_SPACE (as_common));
> + arg = cp_build_qualified_type (arg, unified_cv, tf_none);
...in favor of this one.
> @@ -11963,6 +12126,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> if (!at_least_as_qualified_p (from, to))
> {
> if (constp == 0)
> + /* I'm not sure what this check is meant to be covering. */
> return false;
Under https://eel.is/c++draft/conv#qual you can add cv-quals on an inner
level if the outer levels are all const, e.g. int** -> int const*
const*. If constp == 0, the outer levels aren't all const, so adding
cv-quals is not allowed.
I don't think we want to extend that rule to address spaces, so your
is_toplevel seems right.
Feel free to add a comment.
> @@ -11952,6 +12101,20 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
> TYPE_OFFSET_BASETYPE (to)))
> return false;
>
> + /* We want to permit a toplevel address space change, but only into a
> + subset address space. TODO(arsen): this change has an effect on
> + compare_ics through comp_cv_qual_signature, but I have no idea what
> + that effect is yet. */
> + if (auto as_to = TYPE_ADDR_SPACE (to), as_from = TYPE_ADDR_SPACE (from);
init-statements are a C++17 feature and we're still limited to C++14.
> + as_to != as_from)
> + {
> + if (!is_toplevel)
> + return false;
> +
> + if (!targetm.addr_space.subset_p (as_from, as_to))
This doesn't handle the constp == -1 case (which is gratuitously
obscure, the parameter should really be a bool called something like
"proper" and constp be a local variable) where we want to return false
if each is a subset of the other.
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-22 16:42 ` Jason Merrill
@ 2026-06-22 23:07 ` Arsen Arsenović
2026-06-23 10:17 ` Georg-Johann Lay
0 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-06-22 23:07 UTC (permalink / raw)
To: Jason Merrill
Cc: Georg-Johann Lay, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge, Paul Iannetta
[-- Attachment #1: Type: text/plain, Size: 2156 bytes --]
Jason Merrill <jason@redhat.com> writes:
> But we don't do that for const. In C++ the quals apply to the element type
> rather than the array type; see build_cplus_array_type and cp_type_quals for
> places that deal with that. Places that call
>
> cp_apply_type_quals_to_decl (cp_type_quals (type), decl);
>
> handle setting TREE_READONLY and such on the decl; is there a parallel for
> address space?
AFAICT no, I see nothing similar to DECL_READONLY etc but for address
spaces on decls.
On top of that, anything that wants to know the address space of
whatever it may be checks the type of the decl/operand/... for it
directly (and, manually each time, so there's not even a
EXPR_OR_DECL_ADDR_SPACE or such :S), at a glance with grep[1].
The narrow issue Johann originally reported can be fixed by teaching
varasm to strip arrays before concluding what address space some
variable is.
But the rest of the compiler would probably need similar changes, as,
AFAICT, it all assumes that TREE_ADDR_SPACE (array) is set to
TREE_ADDR_SPACE (TREE_TYPE (array)) prior to using it.
Alternatively, of course, we could make such a EXPR_OR_DECL_ADDR_SPACE
and replace all (applicable) usages of TYPE_ADDR_SPACE with it.
I think it'd be semantically sound to make GENERIC semantics match C++
when dealing with address spaces on array types (i.e. define that the
the address space of an array type is the address space of the element
type; nothing else really makes sense after all), so it may be best to
make TYPE_ADDR_SPACE strip arrays.
This would probably make it not an lvalue, but only a handful of places
use TYPE_ADDR_SPACE as an lvalue anyway, so that's not a big burden.
Though, that may be a bit awkward in relation to TYPE_QUALS, as,
suddenly, one of the qualifiers it reads is not necessarily applicable
on the type given to it.
What do you think is best? I'm thinking that the latter gets rid of the
whole ambiguity, and so, may be the best solution overall.
[1] rg -NI -o -g '*.cc' -g '!cp' -g '!c-family' -g '!c' '[A-Za-z0-9_]*_ADDR_SPACE(_[A-Za-z0-9_]*)?' | sort -u
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-22 23:07 ` Arsen Arsenović
@ 2026-06-23 10:17 ` Georg-Johann Lay
2026-06-23 11:14 ` Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-23 10:17 UTC (permalink / raw)
To: Arsen Arsenović, Jason Merrill
Cc: paul.iannetta, Patrick Palka, gcc-patches, Thomas Schwinge,
Paul Iannetta
[-- Attachment #1: Type: text/plain, Size: 3971 bytes --]
Am 23.06.26 um 01:07 schrieb Arsen Arsenović:
> Jason Merrill <jason@redhat.com> writes:
>
>> But we don't do that for const. In C++ the quals apply to the element type
>> rather than the array type; see build_cplus_array_type and cp_type_quals for
>> places that deal with that. Places that call
>>
>> cp_apply_type_quals_to_decl (cp_type_quals (type), decl);
>>
>> handle setting TREE_READONLY and such on the decl; is there a parallel for
>> address space?
>
> AFAICT no, I see nothing similar to DECL_READONLY etc but for address
> spaces on decls.
>
> On top of that, anything that wants to know the address space of
> whatever it may be checks the type of the decl/operand/... for it
> directly (and, manually each time, so there's not even a
> EXPR_OR_DECL_ADDR_SPACE or such :S), at a glance with grep[1].
>
> The narrow issue Johann originally reported can be fixed by teaching
> varasm to strip arrays before concluding what address space some
> variable is.
Thank you, that did the trick.I have tried the attached patch with
-f[no-]data-sections.
diff --git a/gcc/varasm.cc b/gcc/varasm.cc
index 0dbc35d926a..11e42bb6ade 100644
--- a/gcc/varasm.cc
+++ b/gcc/varasm.cc
@@ -1268,7 +1268,6 @@ compute_reloc_for_var (tree decl)
section *
get_variable_section (tree decl, bool prefer_noswitch_p)
{
- addr_space_t as = ADDR_SPACE_GENERIC;
int reloc;
varpool_node *vnode = varpool_node::get (decl);
if (vnode)
@@ -1277,8 +1276,10 @@ get_variable_section (tree decl, bool
prefer_noswitch_p)
decl = vnode->decl;
}
- if (TREE_TYPE (decl) != error_mark_node)
- as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
+ tree as_type = strip_array_types (TREE_TYPE (decl));
+ addr_space_t as = as_type == error_mark_node
+ ? ADDR_SPACE_GENERIC
+ : TYPE_ADDR_SPACE (as_type);
/* We need the constructor to figure out reloc flag. */
if (vnode)
@@ -1661,12 +1662,10 @@ make_decl_rtl (tree decl)
x = create_block_symbol (name, get_block_for_decl (decl), -1);
else
{
- machine_mode address_mode = Pmode;
- if (TREE_TYPE (decl) != error_mark_node)
- {
- addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
- address_mode = targetm.addr_space.address_mode (as);
- }
+ tree as_type = strip_array_types (TREE_TYPE (decl));
+ machine_mode address_mode = as_type == error_mark_node
+ ? Pmode
+ : targetm.addr_space.address_mode (TYPE_ADDR_SPACE (as_type));
x = gen_rtx_SYMBOL_REF (address_mode, name);
}
SYMBOL_REF_WEAK (x) = DECL_WEAK (decl);
> But the rest of the compiler would probably need similar changes, as,
> AFAICT, it all assumes that TREE_ADDR_SPACE (array) is set to
> TREE_ADDR_SPACE (TREE_TYPE (array)) prior to using it.
>
> Alternatively, of course, we could make such a EXPR_OR_DECL_ADDR_SPACE
> and replace all (applicable) usages of TYPE_ADDR_SPACE with it.
>
> I think it'd be semantically sound to make GENERIC semantics match C++
> when dealing with address spaces on array types (i.e. define that the
> the address space of an array type is the address space of the element
> type; nothing else really makes sense after all), so it may be best to
> make TYPE_ADDR_SPACE strip arrays.
I am not sure if that's a good idea. For example the outcome of
strip_array_types() might be error_mark_node.
Moreover, while convenient in many places, it would be confusing.
Johann
> This would probably make it not an lvalue, but only a handful of places
> use TYPE_ADDR_SPACE as an lvalue anyway, so that's not a big burden.
>
> Though, that may be a bit awkward in relation to TYPE_QUALS, as,
> suddenly, one of the qualifiers it reads is not necessarily applicable
> on the type given to it.
>
> What do you think is best? I'm thinking that the latter gets rid of the
> whole ambiguity, and so, may be the best solution overall.
>
> [1] rg -NI -o -g '*.cc' -g '!cp' -g '!c-family' -g '!c' '[A-Za-z0-9_]*_ADDR_SPACE(_[A-Za-z0-9_]*)?' | sort -u
[-- Attachment #2: varasm-as.diff --]
[-- Type: text/x-patch, Size: 1460 bytes --]
diff --git a/gcc/varasm.cc b/gcc/varasm.cc
index 0dbc35d926a..11e42bb6ade 100644
--- a/gcc/varasm.cc
+++ b/gcc/varasm.cc
@@ -1268,7 +1268,6 @@ compute_reloc_for_var (tree decl)
section *
get_variable_section (tree decl, bool prefer_noswitch_p)
{
- addr_space_t as = ADDR_SPACE_GENERIC;
int reloc;
varpool_node *vnode = varpool_node::get (decl);
if (vnode)
@@ -1277,8 +1276,10 @@ get_variable_section (tree decl, bool prefer_noswitch_p)
decl = vnode->decl;
}
- if (TREE_TYPE (decl) != error_mark_node)
- as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
+ tree as_type = strip_array_types (TREE_TYPE (decl));
+ addr_space_t as = as_type == error_mark_node
+ ? ADDR_SPACE_GENERIC
+ : TYPE_ADDR_SPACE (as_type);
/* We need the constructor to figure out reloc flag. */
if (vnode)
@@ -1661,12 +1662,10 @@ make_decl_rtl (tree decl)
x = create_block_symbol (name, get_block_for_decl (decl), -1);
else
{
- machine_mode address_mode = Pmode;
- if (TREE_TYPE (decl) != error_mark_node)
- {
- addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
- address_mode = targetm.addr_space.address_mode (as);
- }
+ tree as_type = strip_array_types (TREE_TYPE (decl));
+ machine_mode address_mode = as_type == error_mark_node
+ ? Pmode
+ : targetm.addr_space.address_mode (TYPE_ADDR_SPACE (as_type));
x = gen_rtx_SYMBOL_REF (address_mode, name);
}
SYMBOL_REF_WEAK (x) = DECL_WEAK (decl);
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-23 10:17 ` Georg-Johann Lay
@ 2026-06-23 11:14 ` Arsen Arsenović
2026-06-23 12:17 ` Georg-Johann Lay
0 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-06-23 11:14 UTC (permalink / raw)
To: Georg-Johann Lay
Cc: Jason Merrill, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge, Paul Iannetta
[-- Attachment #1: Type: text/plain, Size: 1768 bytes --]
Georg-Johann Lay <avr@gjlay.de> writes:
>> But the rest of the compiler would probably need similar changes, as,
>> AFAICT, it all assumes that TREE_ADDR_SPACE (array) is set to
>> TREE_ADDR_SPACE (TREE_TYPE (array)) prior to using it.
>> Alternatively, of course, we could make such a EXPR_OR_DECL_ADDR_SPACE
>> and replace all (applicable) usages of TYPE_ADDR_SPACE with it.
>> I think it'd be semantically sound to make GENERIC semantics match C++
>> when dealing with address spaces on array types (i.e. define that the
>> the address space of an array type is the address space of the element
>> type; nothing else really makes sense after all), so it may be best to
>> make TYPE_ADDR_SPACE strip arrays.
>
> I am not sure if that's a good idea. For example the outcome of
> strip_array_types() might be error_mark_node.
>
> Moreover, while convenient in many places, it would be confusing.
It's not just convenience, it's also semantically more sound, IMO.
Such a change would make it impossible to have the (nonsensical)
scenario where an array has its elements in some other address space
(which is the confusion that lead to the bug you found).
The outcome of strip_array_types() can be error_mark_node iff the array
passed into it is an array of error types (or, of course, if the error
mark was passed into it). But, if it is such an array, then we need to
invent an address space to place on the array type anyway (in
build_min_array_type). Ergo, it seems to me equivalent to treat that
case as if it were the generic address space anyway, in the
TYPE_ADDR_SPACE accessor.
Indeed, ISTM that at least varasm.cc already deals with the error mark
in that way, and other places probably do too.
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-23 11:14 ` Arsen Arsenović
@ 2026-06-23 12:17 ` Georg-Johann Lay
0 siblings, 0 replies; 86+ messages in thread
From: Georg-Johann Lay @ 2026-06-23 12:17 UTC (permalink / raw)
To: Arsen Arsenović
Cc: Jason Merrill, paul.iannetta, Patrick Palka, gcc-patches,
Thomas Schwinge
Am 23.06.26 um 13:14 schrieb Arsen Arsenović:
> Georg-Johann Lay <avr@gjlay.de> writes:
>>> But the rest of the compiler would probably need similar changes, as,
>>> AFAICT, it all assumes that TREE_ADDR_SPACE (array) is set to
>>> TREE_ADDR_SPACE (TREE_TYPE (array)) prior to using it.
>>> Alternatively, of course, we could make such a EXPR_OR_DECL_ADDR_SPACE
>>> and replace all (applicable) usages of TYPE_ADDR_SPACE with it.
>>> I think it'd be semantically sound to make GENERIC semantics match C++
>>> when dealing with address spaces on array types (i.e. define that the
>>> the address space of an array type is the address space of the element
>>> type; nothing else really makes sense after all), so it may be best to
>>> make TYPE_ADDR_SPACE strip arrays.
>>
>> I am not sure if that's a good idea. For example the outcome of
>> strip_array_types() might be error_mark_node.
>>
>> Moreover, while convenient in many places, it would be confusing.
>
> It's not just convenience, it's also semantically more sound, IMO.
>
> Such a change would make it impossible to have the (nonsensical)
> scenario where an array has its elements in some other address space
> (which is the confusion that lead to the bug you found).
>
> The outcome of strip_array_types() can be error_mark_node iff the array
> passed into it is an array of error types (or, of course, if the error
> mark was passed into it). But, if it is such an array, then we need to
> invent an address space to place on the array type anyway (in
> build_min_array_type). Ergo, it seems to me equivalent to treat that
> case as if it were the generic address space anyway, in the
> TYPE_ADDR_SPACE accessor.
>
> Indeed, ISTM that at least varasm.cc already deals with the error mark
> in that way, and other places probably do too.
Though to find out readonlyness, you's have to peel arrays by hand,
while to get an AS you don't need peel.
Maybe leave TYPE_ADDR_SPACE (and TYPE_QUALS, TYPE_QUALS_NO_ADDR_SPACE,
TYPE_READONLY, TYPE_VOLATILE and what not for that matter) consistently
operating on the very node, and add a new function like
decl_addr_space() to tree.h? In avr.cc, I am currently using this one:
/* Return the address space of a DECL_P or TYPE_P. This doesn't follow
pointer types and can be used to find the section for a decl. */
static addr_space_t
avr_node_addr_space (tree node)
{
if (DECL_P (node))
node = TREE_TYPE (node);
if (TYPE_P (node))
node = strip_array_types (node);
return TYPE_P (node) ? TYPE_ADDR_SPACE (node) : ADDR_SPACE_GENERIC;
}
Johann
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-22 18:14 ` Jason Merrill
@ 2026-06-23 19:49 ` Arsen Arsenović
2026-06-23 20:15 ` Jason Merrill
0 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-06-23 19:49 UTC (permalink / raw)
To: Jason Merrill; +Cc: Thomas Schwinge, Paul Iannetta, Patrick Palka, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 8261 bytes --]
Jason Merrill <jason@redhat.com> writes:
> The address space logic needs to respect the 'strict' flags like the rest of
> the function. So we want to allow as_parm == 0 iff when
> UNIFY_ALLOW_LESS_CV_QUAL, or as_arg == 0 if UNIFY_ALLOW_MORE_CV_QUAL.
Ah, OK. I've now implemented that:
static bool
check_addr_spaces_for_unify (int strict, bool outer,
addr_space_t as_arg, addr_space_t as_parm)
{
if (as_parm == as_arg)
return true;
if (!ADDR_SPACE_GENERIC_P (as_parm) && !ADDR_SPACE_GENERIC_P (as_arg))
{
/* We can't unify different non-generic address spaces at all, except on
the outer layer, if we're permitting more CV-qualification. */
if (outer
&& targetm.addr_space.subset_p (as_arg, as_parm)
&& (strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL)))
return true;
return false;
}
/* At this point, exactly one of PARM or ARG has an address space. */
if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
&& ADDR_SPACE_GENERIC_P (as_arg))
/* ARG lacks an address space, but PARM has it. This means PARM is more
qualified, but we don't allow that. */
return false;
if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))
&& ADDR_SPACE_GENERIC_P (as_parm))
/* Conversely, ARG is more qualified. */
return false;
return true;
}
[...]
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
if (!check_addr_spaces_for_unify (strict, false, as_arg, as_parm))
/* Address spaces cannot be unified. */
return 0;
> And I don't think we want to consider superset here, just compare
> as_parm == as_arg. Deduction is stricter than conversion.
We do want to permit it on the outer level, due to
<https://eel.is/c++draft/temp.deduct.call#4.2>, as in the following testcase:
template<typename T>
T foo(__super T *x);
void
bar(__sub int *y)
{ foo(y); }
... where __super and __sub are a pair of address spaces s.t. __super ⊃
__sub. There's a qualification conversion from __sub int* to __super T*
[with T = int].
I'm not sure this is the best way to go about it, but I've implemented
that behavior by teaching 'unify' to chuck out the ARG address space of
the toplevel pointer type as such (ergo the seemingly useless OUTER
parameter above), as that means that PARM can always be considered more
qualified than ARG:
case POINTER_TYPE:
{
if (!TYPE_PTR_P (arg))
return unify_type_mismatch (explain_p, parm, arg);
/* [temp.deduct.call]
A can be another pointer or pointer to member type that can
be converted to the deduced A via a qualification
conversion (_conv.qual_).
We pass down STRICT here rather than UNIFY_ALLOW_NONE.
This will allow for additional cv-qualification of the
pointed-to types if appropriate. */
if (TREE_CODE (TREE_TYPE (arg)) == RECORD_TYPE)
/* The derived-to-base conversion only persists through one
level of pointers. */
strict |= (strict_in & UNIFY_ALLOW_DERIVED);
auto arg_pointee = TREE_TYPE (arg);
if (strict_in & UNIFY_ALLOW_OUTER_LEVEL)
{
/* If we're in the outermost layer of a deduction, and address spaces
differ, this may still be acceptable as a result of a
qualification conversion existing.
However, there's no qualification conversions for address space
differences lower than one level deep, so we cannot simply
handle this case when unifying the two pointee types.
We'll handle this by stripping off the address space of ARG's
pointee here, if its address space is a subset of PARMs. This
will permit check_cv_quals_for_unify to succeed in case such a
conversion exists, or fail otherwise. */
auto parm_pointee_as = TYPE_ADDR_SPACE (TREE_TYPE (parm));
auto arg_pointee_as = TYPE_ADDR_SPACE (TREE_TYPE (arg));
auto arg_pointee_quals = cp_type_quals (TREE_TYPE (arg));
if (parm_pointee_as == arg_pointee_as)
/* All OK. */;
else if (check_addr_spaces_for_unify (strict, true,
arg_pointee_as,
parm_pointee_as))
/* We're permitted to remove the addr-space difference. Doing
this will allow deducing to T without the address-space, and
then applying a qualifying conversion to it. */
arg_pointee = (cp_build_qualified_type
(arg_pointee,
CLEAR_QUAL_ADDR_SPACE (arg_pointee_quals)));
}
return unify (tparms, targs, TREE_TYPE (parm),
arg_pointee, strict, explain_p);
}
AFAICT, rather than attempting unification quite permissively and then
throwing away a successful deduction in case [temp.deduct.call]p4 fails,
we "inline" the qualification test into the unification process itself.
>> @@ -11963,6 +12126,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>> if (!at_least_as_qualified_p (from, to))
>> {
>> if (constp == 0)
>> + /* I'm not sure what this check is meant to be covering. */
>> return false;
> Under https://eel.is/c++draft/conv#qual you can add cv-quals on an inner level
> if the outer levels are all const, e.g. int** -> int const* const*. If constp
> == 0, the outer levels aren't all const, so adding cv-quals is not allowed.
>
> I don't think we want to extend that rule to address spaces, so your
> is_toplevel seems right.
>
> Feel free to add a comment.
Ah, I see. It's not immediately clear that the only valid states for
CONSTP are 1 (all layers so far had const), 0 (some layer lacked const,
never passed in by callers), and -1 (only return 1 for proper subsets),
especially as the function comment explicitly says "If CONSTP is
positive, then all outer pointers have been const-qualified" implying
that the caller is partially responsible for making sure this condition
holds, and that any positive value rather than just 1 is permitted.
>> @@ -11952,6 +12101,20 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>> TYPE_OFFSET_BASETYPE (to)))
>> return false;
>> + /* We want to permit a toplevel address space change, but only into a
>> + subset address space. TODO(arsen): this change has an effect on
>> + compare_ics through comp_cv_qual_signature, but I have no idea what
>> + that effect is yet. */
>> + if (auto as_to = TYPE_ADDR_SPACE (to), as_from = TYPE_ADDR_SPACE (from);
>
> init-statements are a C++17 feature and we're still limited to C++14.
Ah, right. Will fix all instances.
>> + as_to != as_from)
>> + {
>> + if (!is_toplevel)
>> + return false;
>> +
>> + if (!targetm.addr_space.subset_p (as_from, as_to))
>
> This doesn't handle the constp == -1 case where we want to return
> false if each is a subset of the other.
Sure, but doesn't the pair of at_least_as_qualified_p checks later down
(and that was mentioned above)? Since...
at_least_as_qualified_p (to, from)
⇒ targetm.addr_space.subset_p (from_as, to_as)
... and both permutations are tested with that predicate, ergo both
permutations of SUBSET_P are also tested.
> (which is gratuitously obscure, the parameter should really be a bool
> called something like "proper" and constp be a local variable)
I wouldn't mind making this change also, if you want. I left a comment,
but this is still quite confusing.
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++
2026-06-23 19:49 ` Arsen Arsenović
@ 2026-06-23 20:15 ` Jason Merrill
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Jason Merrill @ 2026-06-23 20:15 UTC (permalink / raw)
To: Arsen Arsenović
Cc: Thomas Schwinge, Paul Iannetta, Patrick Palka, gcc-patches
On 6/23/26 3:49 PM, Arsen Arsenović wrote:
> Jason Merrill <jason@redhat.com> writes:
>
>> The address space logic needs to respect the 'strict' flags like the rest of
>> the function. So we want to allow as_parm == 0 iff when
>> UNIFY_ALLOW_LESS_CV_QUAL, or as_arg == 0 if UNIFY_ALLOW_MORE_CV_QUAL.
>
> Ah, OK. I've now implemented that:
>
> static bool
> check_addr_spaces_for_unify (int strict, bool outer,
> addr_space_t as_arg, addr_space_t as_parm)
> {
> if (as_parm == as_arg)
> return true;
>
> if (!ADDR_SPACE_GENERIC_P (as_parm) && !ADDR_SPACE_GENERIC_P (as_arg))
> {
> /* We can't unify different non-generic address spaces at all, except on
> the outer layer, if we're permitting more CV-qualification. */
> if (outer
> && targetm.addr_space.subset_p (as_arg, as_parm)
> && (strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL)))
> return true;
>
> return false;
> }
>
> /* At this point, exactly one of PARM or ARG has an address space. */
> if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
> && ADDR_SPACE_GENERIC_P (as_arg))
> /* ARG lacks an address space, but PARM has it. This means PARM is more
> qualified, but we don't allow that. */
> return false;
>
> if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))
> && ADDR_SPACE_GENERIC_P (as_parm))
> /* Conversely, ARG is more qualified. */
> return false;
>
> return true;
> }
>
> [...]
>
> static int
> check_cv_quals_for_unify (int strict, tree arg, tree parm)
> {
> int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg));
> int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm));
>
> addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg));
> addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm));
>
> if (!check_addr_spaces_for_unify (strict, false, as_arg, as_parm))
> /* Address spaces cannot be unified. */
> return 0;
>
>
>> And I don't think we want to consider superset here, just compare
>> as_parm == as_arg. Deduction is stricter than conversion.
>
> We do want to permit it on the outer level, due to
> <https://eel.is/c++draft/temp.deduct.call#4.2>, as in the following testcase:
>
> template<typename T>
> T foo(__super T *x);
>
> void
> bar(__sub int *y)
> { foo(y); }
>
> ... where __super and __sub are a pair of address spaces s.t. __super ⊃
> __sub. There's a qualification conversion from __sub int* to __super T*
> [with T = int].
OK, makes sense.
> I'm not sure this is the best way to go about it, but I've implemented
> that behavior by teaching 'unify' to chuck out the ARG address space of
> the toplevel pointer type as such (ergo the seemingly useless OUTER
> parameter above), as that means that PARM can always be considered more
> qualified than ARG:
>
> case POINTER_TYPE:
> {
> if (!TYPE_PTR_P (arg))
> return unify_type_mismatch (explain_p, parm, arg);
>
> /* [temp.deduct.call]
>
> A can be another pointer or pointer to member type that can
> be converted to the deduced A via a qualification
> conversion (_conv.qual_).
>
> We pass down STRICT here rather than UNIFY_ALLOW_NONE.
> This will allow for additional cv-qualification of the
> pointed-to types if appropriate. */
>
> if (TREE_CODE (TREE_TYPE (arg)) == RECORD_TYPE)
> /* The derived-to-base conversion only persists through one
> level of pointers. */
> strict |= (strict_in & UNIFY_ALLOW_DERIVED);
>
> auto arg_pointee = TREE_TYPE (arg);
> if (strict_in & UNIFY_ALLOW_OUTER_LEVEL)
> {
> /* If we're in the outermost layer of a deduction, and address spaces
> differ, this may still be acceptable as a result of a
> qualification conversion existing.
>
> However, there's no qualification conversions for address space
> differences lower than one level deep, so we cannot simply
> handle this case when unifying the two pointee types.
>
> We'll handle this by stripping off the address space of ARG's
> pointee here, if its address space is a subset of PARMs. This
> will permit check_cv_quals_for_unify to succeed in case such a
> conversion exists, or fail otherwise. */
>
> auto parm_pointee_as = TYPE_ADDR_SPACE (TREE_TYPE (parm));
> auto arg_pointee_as = TYPE_ADDR_SPACE (TREE_TYPE (arg));
> auto arg_pointee_quals = cp_type_quals (TREE_TYPE (arg));
>
> if (parm_pointee_as == arg_pointee_as)
> /* All OK. */;
> else if (check_addr_spaces_for_unify (strict, true,
> arg_pointee_as,
> parm_pointee_as))
> /* We're permitted to remove the addr-space difference. Doing
> this will allow deducing to T without the address-space, and
> then applying a qualifying conversion to it. */
> arg_pointee = (cp_build_qualified_type
> (arg_pointee,
> CLEAR_QUAL_ADDR_SPACE (arg_pointee_quals)));
This seems wrong for the case where parm is generic and we want to
deduce a specific address space?
> }
>
> return unify (tparms, targs, TREE_TYPE (parm),
> arg_pointee, strict, explain_p);
> }
>
> AFAICT, rather than attempting unification quite permissively and then
> throwing away a successful deduction in case [temp.deduct.call]p4 fails,
> we "inline" the qualification test into the unification process itself.
>
>>> @@ -11963,6 +12126,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>>> if (!at_least_as_qualified_p (from, to))
>>> {
>>> if (constp == 0)
>>> + /* I'm not sure what this check is meant to be covering. */
>>> return false;
>> Under https://eel.is/c++draft/conv#qual you can add cv-quals on an inner level
>> if the outer levels are all const, e.g. int** -> int const* const*. If constp
>> == 0, the outer levels aren't all const, so adding cv-quals is not allowed.
>>
>> I don't think we want to extend that rule to address spaces, so your
>> is_toplevel seems right.
>>
>> Feel free to add a comment.
>
> Ah, I see. It's not immediately clear that the only valid states for
> CONSTP are 1 (all layers so far had const), 0 (some layer lacked const,
> never passed in by callers), and -1 (only return 1 for proper subsets),
> especially as the function comment explicitly says "If CONSTP is
> positive, then all outer pointers have been const-qualified" implying
> that the caller is partially responsible for making sure this condition
> holds, and that any positive value rather than just 1 is permitted.
>
>>> + as_to != as_from)
>>> + {
>>> + if (!is_toplevel)
>>> + return false;
>>> +
>>> + if (!targetm.addr_space.subset_p (as_from, as_to))
>>
>> This doesn't handle the constp == -1 case where we want to return
>> false if each is a subset of the other.
>
> Sure, but doesn't the pair of at_least_as_qualified_p checks later down
> (and that was mentioned above)? Since...
>
> at_least_as_qualified_p (to, from)
> ⇒ targetm.addr_space.subset_p (from_as, to_as)
>
> ... and both permutations are tested with that predicate, ergo both
> permutations of SUBSET_P are also tested.
True, though then it seems the above subset_p call is redundant. And
the check for different AS at !is_toplevel could join the constp == 0
check.
>> (which is gratuitously obscure, the parameter should really be a bool
>> called something like "proper" and constp be a local variable)
>
> I wouldn't mind making this change also, if you want. I left a comment,
> but this is still quite confusing.
Sounds good, but let's make that a separate patch.
Jason
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH v5 0/7] Basic support for Named Address Spaces in C++
2026-06-23 20:15 ` Jason Merrill
@ 2026-07-09 16:36 ` Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 1/7] gcc/cp-tree: require that temp_override deduces on first constructor arg Arsen Arsenović
` (6 more replies)
0 siblings, 7 replies; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 16:36 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Arsen Arsenović
Okay, here's the updated patch series. I've broken the commits down
somewhat.
Of course, this version depends on typed qualifiers, see
https://inbox.sourceware.org/gcc-patches/20260705212455.3144273-1-aarsenovic@baylibre.com/
I've also set up self-testing for the various routines affected by
address-space checks, so that we have target-independent tests.
This revision does not do away with copying the address-space qualifier
to array types. I can go back and do that if desired.
Though, it seems to me valid also to adjust TYPE_ADDR_SPACE to strip
arrays, and use THIS_TYPES_ADDR_SPACE or such to access the actual
TYPE_ADDR_SPACE field; the latter for an array (and OFFSET_TYPEs, and
maybe others that I am not recalling immediately) must always be the
element (or struct) type.
Johann mentioned:
> The old way of reading from flash is qua inline asm (macros provided
> bv libc's avr/pgmspace.h for convenience. So an old code may read:
>
> const int vals[] = { 1, 2 } __attribute((progmem));
>
> ... p = &vals[idx];
>
> int flash_read_int (const int *p /*points to flash*/)
> {
> return pgm_read_int (p); // #include <avr/pgmspace.h>
> }
>
> Using a plain *p would use the wrong instructions and read from RAM.
> Without changing the interface of flash_read_int(), a code using AS
> is:
>
> int flash_read_int (const int *p /*points to flash*/)
> {
> return * (const __flash int*) p;
> }
>
> That's why casting between address-spaces is allowed. Though in your
> example, wrong code would be generated (except SS *p uses pgm_read_xxx
> for access), and -Waddr-space-convert should complain as the cast is
> implicit.
>
> Zo find questionable uses, users can -Waddr-space-convert, so that
> avr's TARGET_CONVERT_TO_TYPE diagnoses them. The idea is that
> explicit casts are okay (wanted by the user) while implicit casts
> should be diagnosed.
>
> Unfortunately, the c++ front doesn't call TARGET_CONVERT_TO_TYPE
> whereas the c front does. (TARGET_ADDR_SPACE_CONVERT is for emitting
> actual RTL for such conversions, be they explicit or implicit. It
> runs late after LTO streaming, and therefore it is not well suited for
> diagnostics).
I see that the C++ FE does call that hook in ocp_convert. Maybe the
issue was in the way a previous version of the patch handled the actual
conversion, and goes away with this revision.
Johann, please re-test. If it fails again, please let me know and I
will look into it in more detail.
This patch still doesn't address the issue that exists with
constructors, or generally member functions, but it is a start.
Tested on x86_64-linux-gnu.
On the review comments:
Jason Merrill <jason@redhat.com> writes:
>> /* We're permitted to remove the addr-space difference. Doing
>> this will allow deducing to T without the address-space, and
>> then applying a qualifying conversion to it. */
>> arg_pointee = (cp_build_qualified_type
>> (arg_pointee,
>> CLEAR_QUAL_ADDR_SPACE (arg_pointee_quals)));
>
> This seems wrong for the case where parm is generic and we want to deduce a
> specific address space?
Yes, it was. I worked out the logic incorrectly. This revision never
replaces the PARM address space if generic (i.e. it will always have it
be deduced).
> True, though then it seems the above subset_p call is redundant. And
> the check for different AS at !is_toplevel could join the constp == 0
> check.
No, because that check is not necessarily reached for two differing
address spaces that are overlapping, for instance. But, it is
sufficient to check for the address space difference alone and reject,
so the check does become simpler.
>>> (which is gratuitously obscure, the parameter should really be a bool
>>> called something like "proper" and constp be a local variable)
>> I wouldn't mind making this change also, if you want. I left a comment,
>> but this is still quite confusing.
>
> Sounds good, but let's make that a separate patch.
Done; that's patch 7.
Arsen Arsenović (6):
gcc/cp-tree: require that temp_override deduces on first constructor
arg
gcc/target.def: fix HOOK_PREFIX redefinition warning
gcc/{c,cp,c-family}: move some address space handling into c-family
gcc/c-family: implement addr_space_test, a helper for self-testing
ASes
gcc/c++: implement basic support for Named Address Spaces in C++
[PR69549]
c++: make comp_ptr_ttypes_real slightly easier to understand
Thomas Schwinge (1):
gcc/system.h: Allow for '#define INCLUDE_ITERATOR' to '#include
<iterator>'
gcc/c-family/c-common.cc | 142 +++++
gcc/c-family/c-common.h | 62 +-
gcc/c/c-decl.cc | 26 +-
gcc/c/c-typeck.cc | 26 -
gcc/cp/call.cc | 60 +-
gcc/cp/class.cc | 8 +-
gcc/cp/cp-gimplify.cc | 37 ++
gcc/cp/cp-lang.cc | 1 +
gcc/cp/cp-objcp-common.h | 5 +-
gcc/cp/cp-tree.h | 111 +++-
gcc/cp/decl.cc | 141 ++++-
gcc/cp/error.cc | 1 +
gcc/cp/init.cc | 32 +-
gcc/cp/lex.cc | 23 +
gcc/cp/mangle.cc | 11 +-
gcc/cp/method.cc | 23 +-
gcc/cp/module.cc | 45 +-
gcc/cp/parser.cc | 195 ++++--
gcc/cp/pt.cc | 432 +++++++++++--
gcc/cp/reflect.cc | 7 +-
gcc/cp/rtti.cc | 4 +-
gcc/cp/search.cc | 21 +-
gcc/cp/semantics.cc | 32 +-
gcc/cp/tree.cc | 171 +++++-
gcc/cp/typeck.cc | 573 +++++++++++++++---
gcc/cp/typeck2.cc | 13 +-
gcc/doc/extend.texi | 2 +-
gcc/doc/tm.texi | 8 +-
gcc/doc/tm.texi.in | 8 +-
gcc/system.h | 3 +
gcc/target.def | 1 +
gcc/testsuite/g++.dg/abi/mangle-addr-space1.C | 9 +
gcc/testsuite/g++.dg/abi/mangle-addr-space2.C | 9 +
gcc/testsuite/g++.dg/ext/addr-space-conv.C | 209 +++++++
gcc/testsuite/g++.dg/ext/addr-space-decl.C | 5 +
gcc/testsuite/g++.dg/ext/addr-space-ops.C | 24 +
gcc/testsuite/g++.dg/ext/addr-space-ref.C | 29 +
gcc/testsuite/g++.dg/parse/addr-space.C | 9 +
gcc/testsuite/g++.dg/parse/addr-space1.C | 10 +
gcc/testsuite/g++.dg/parse/addr-space2.C | 9 +
.../g++.dg/template/addr-space-overload.C | 17 +
.../g++.dg/template/addr-space-strip1.C | 18 +
.../g++.dg/template/addr-space-strip2.C | 17 +
.../g++.dg/template/spec-addr-space.C | 10 +
44 files changed, 2236 insertions(+), 363 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-conv.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-decl.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ops.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ref.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space1.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space2.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-overload.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip1.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip2.C
create mode 100644 gcc/testsuite/g++.dg/template/spec-addr-space.C
--
2.55.0
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH v5 1/7] gcc/cp-tree: require that temp_override deduces on first constructor arg
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
@ 2026-07-09 16:36 ` Arsen Arsenović
2026-07-09 17:18 ` Jason Merrill
2026-07-09 16:36 ` [PATCH v5 2/7] gcc/target.def: fix HOOK_PREFIX redefinition warning Arsen Arsenović
` (5 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 16:36 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Arsen Arsenović
This helps force a conversion from the second argument into whatever
type the variable truly is.
This change ended up being unnecessary, but it is probably useful for
others anyway.
gcc/cp/ChangeLog:
* cp-tree.h (temp_override): In provided-value constructor,
don't deduce based on provided value.
---
gcc/cp/cp-tree.h | 38 +++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5905236752bc..32865ba1ff3a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2256,25 +2256,6 @@ public:
}
};
-/* RAII sentinel that saves the value of a variable, optionally
- overrides it right away, and restores its value when the sentinel
- id destructed. */
-
-template <typename T>
-class temp_override
-{
- T& overridden_variable;
- T saved_value;
-public:
- temp_override(T& var) : overridden_variable (var), saved_value (var) {}
- temp_override(T& var, T overrider)
- : overridden_variable (var), saved_value (var)
- {
- overridden_variable = overrider;
- }
- ~temp_override() { overridden_variable = saved_value; }
-};
-
/* Wrapping a template parameter in type_identity_t hides it from template
argument deduction. */
#if __cpp_lib_type_identity
@@ -2286,6 +2267,25 @@ template <typename T>
using type_identity_t = typename type_identity<T>::type;
#endif
+/* RAII sentinel that saves the value of a variable, optionally
+ overrides it right away, and restores its value when the sentinel
+ id destructed. */
+
+template <typename T>
+class temp_override
+{
+ T& overridden_variable;
+ T saved_value;
+public:
+ temp_override (T& var) : overridden_variable (var), saved_value (var) {}
+ temp_override (T& var, type_identity_t<T> overrider)
+ : overridden_variable (var), saved_value (var)
+ {
+ overridden_variable = overrider;
+ }
+ ~temp_override() { overridden_variable = saved_value; }
+};
+
/* Object generator function for temp_override, so you don't need to write the
type of the object as a template argument.
--
2.55.0
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH v5 2/7] gcc/target.def: fix HOOK_PREFIX redefinition warning
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 1/7] gcc/cp-tree: require that temp_override deduces on first constructor arg Arsen Arsenović
@ 2026-07-09 16:36 ` Arsen Arsenović
2026-07-09 16:53 ` Andrea Pinski
2026-07-09 16:36 ` [PATCH v5 3/7] gcc/{c,cp,c-family}: move some address space handling into c-family Arsen Arsenović
` (4 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 16:36 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Arsen Arsenović
gcc/ChangeLog:
* target.def (HOOK_PREFIX): Undefine, before redefining.
---
gcc/target.def | 1 +
1 file changed, 1 insertion(+)
diff --git a/gcc/target.def b/gcc/target.def
index 884fe1bd57e1..a9ccc19c180a 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -29,6 +29,7 @@
HOOK_VECTOR (TARGET_INITIALIZER, gcc_target)
/* Functions that output assembler for the target. */
+#undef HOOK_PREFIX
#define HOOK_PREFIX "TARGET_ASM_"
HOOK_VECTOR (TARGET_ASM_OUT, asm_out)
--
2.55.0
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH v5 3/7] gcc/{c,cp,c-family}: move some address space handling into c-family
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 1/7] gcc/cp-tree: require that temp_override deduces on first constructor arg Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 2/7] gcc/target.def: fix HOOK_PREFIX redefinition warning Arsen Arsenović
@ 2026-07-09 16:36 ` Arsen Arsenović
2026-07-09 16:56 ` [PATCH v5 3/7] gcc/{c, cp, c-family}: " Andrea Pinski
2026-07-09 16:36 ` [PATCH v5 4/7] gcc/system.h: Allow for '#define INCLUDE_ITERATOR' to '#include <iterator>' Arsen Arsenović
` (3 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 16:36 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Arsen Arsenović
... in anticipation of a few coming patches implementing C++ Named
Address Space support.
gcc/c-family/ChangeLog:
* c-common.cc (addr_space_superset): Move here from
../c/c-typeck.cc.
(c_register_addr_space): Move here from ../c/c-decl.cc.
* c-common.h (c_register_addr_space): Move into c-common.cc
section.
(addr_space_superset): Declare.
(c_make_keyword): New. Marks ID as a keyword. Implemented
by both FEs.
(c_unmake_keyword): New. Unmarks ID as a keyword. Implemented
by both FEs.
gcc/c/ChangeLog:
* c-decl.cc (c_make_keyword): Implement.
(c_unmake_keyword): Ditto.
(c_register_addr_space): Move into ../c-family/c-common.cc.
* c-typeck.cc (addr_space_superset): Move into
../c-family/c-common.cc.
gcc/cp/ChangeLog:
* lex.cc (set_identifier_kind): Add comment referencing the two
functions mentioned below.
(c_make_keyword): Implement.
(c_unmake_keyword): Ditto.
* tree.cc (c_register_addr_space): Drop. The implementation is
now in c-family.cc, so no stub is needed.
---
gcc/c-family/c-common.cc | 45 ++++++++++++++++++++++++++++++++++++++++
gcc/c-family/c-common.h | 16 +++++++++++---
gcc/c/c-decl.cc | 26 +++++++++++------------
gcc/c/c-typeck.cc | 26 -----------------------
gcc/cp/lex.cc | 23 ++++++++++++++++++++
gcc/cp/tree.cc | 9 --------
6 files changed, 93 insertions(+), 52 deletions(-)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 3d7ef128f0d9..e05551e51192 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -660,6 +660,32 @@ c_addr_space_name (addr_space_t as)
return IDENTIFIER_POINTER (ridpointers [rid]);
}
+/* Return true if between two named address spaces, whether there is a superset
+ named address space that encompasses both address spaces. If there is a
+ superset, return which address space is the superset. */
+
+bool
+addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t* common)
+{
+ if (as1 == as2)
+ {
+ *common = as1;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as1, as2))
+ {
+ *common = as2;
+ return true;
+ }
+ else if (targetm.addr_space.subset_p (as2, as1))
+ {
+ *common = as1;
+ return true;
+ }
+ else
+ return false;
+}
+
/* Push current bindings for the function name VAR_DECLS. */
void
@@ -2899,6 +2925,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
return build_nonstandard_integer_type (width, unsignedp);
}
+/* Register reserved keyword WORD as qualifier for address space AS. */
+
+void
+c_register_addr_space (const char *word, addr_space_t as)
+{
+ int rid = RID_FIRST_ADDR_SPACE + as;
+ tree id;
+
+ /* Address space qualifiers are only supported
+ in C with GNU extensions enabled. */
+ if (c_dialect_objc () || flag_no_asm)
+ return;
+
+ id = get_identifier (word);
+ C_SET_RID_CODE (id, rid);
+ c_make_keyword (id);
+ ridpointers[rid] = id;
+}
+
/* The C version of the register_builtin_type langhook. */
void
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 40a4771a1f25..569edc064014 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -43,6 +43,10 @@ never after.
#endif
#include "diagnostic-core.h"
+#if CHECKING_P
+# include "target.h"
+#endif
+
/* Usage of TREE_LANG_FLAG_?:
0: IDENTIFIER_MARKED (used by search routines).
C_MAYBE_CONST_EXPR_INT_OPERANDS (in C_MAYBE_CONST_EXPR, for C)
@@ -843,12 +847,10 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
extern tree (*make_fname_decl) (location_t, tree, int);
-/* In c-decl.cc and cp/tree.cc. FIXME. */
-extern void c_register_addr_space (const char *str, addr_space_t as);
-
/* In c-common.cc. */
extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
+extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
extern int names_builtin_p (const char *);
@@ -1002,6 +1004,7 @@ extern bool c_common_init (void);
extern void c_common_finish (void);
extern void c_common_parse_file (void);
extern alias_set_type c_common_get_alias_set (tree);
+extern void c_register_addr_space (const char *, addr_space_t);
extern void c_register_builtin_type (tree, const char*);
extern bool c_promoting_integer_type_p (const_tree);
extern bool self_promoting_args_p (const_tree);
@@ -1133,6 +1136,13 @@ extern tree lookup_name (tree);
extern bool lvalue_p (const_tree);
extern int maybe_adjust_arg_pos_for_attribute (const_tree);
extern bool instantiation_dependent_expression_p (tree);
+/* Make ID into a keyword, in a front end specific manner. Will only be called
+ on normal identifiers. Used in c-common.cc. */
+extern void c_make_keyword (tree id);
+/* Make ID into a regular identifier, in a front end specific manner. Will
+ only be called on identifiers previously given to c_make_keyword. Used in
+ c-common.cc. */
+extern void c_unmake_keyword (tree id);
extern bool vector_targets_convertible_p (const_tree t1, const_tree t2);
extern bool vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note);
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 3c8050a6b13a..cd0601044da0 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -13870,23 +13870,21 @@ c_parse_final_cleanups (void)
ext_block = NULL;
}
-/* Register reserved keyword WORD as qualifier for address space AS. */
+/* c-common.cc uses these two when registering its own keywords. */
+void
+c_make_keyword (tree id)
+{
+ gcc_checking_assert (!C_IS_RESERVED_WORD (id));
+ C_IS_RESERVED_WORD (id) = 1;
+}
void
-c_register_addr_space (const char *word, addr_space_t as)
+c_unmake_keyword (tree id)
{
- int rid = RID_FIRST_ADDR_SPACE + as;
- tree id;
-
- /* Address space qualifiers are only supported
- in C with GNU extensions enabled. */
- if (c_dialect_objc () || flag_no_asm)
- return;
-
- id = get_identifier (word);
- C_SET_RID_CODE (id, rid);
- C_IS_RESERVED_WORD (id) = 1;
- ridpointers [rid] = id;
+ /* We only expect to be clearing identifiers previously made into keywords by
+ c_make_keyword. This is done primarily for self-tests. */
+ gcc_checking_assert (C_IS_RESERVED_WORD (id));
+ C_IS_RESERVED_WORD (id) = 0;
}
/* Return identifier to look up for omp declare reduction. */
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 81ed46f3ed0c..11bf30917ddb 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -316,32 +316,6 @@ c_type_promotes_to (tree type)
return type;
}
-/* Return true if between two named address spaces, whether there is a superset
- named address space that encompasses both address spaces. If there is a
- superset, return which address space is the superset. */
-
-static bool
-addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
-{
- if (as1 == as2)
- {
- *common = as1;
- return true;
- }
- else if (targetm.addr_space.subset_p (as1, as2))
- {
- *common = as2;
- return true;
- }
- else if (targetm.addr_space.subset_p (as2, as1))
- {
- *common = as1;
- return true;
- }
- else
- return false;
-}
-
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
diff --git a/gcc/cp/lex.cc b/gcc/cp/lex.cc
index da40be0a5e06..d6fad67cd5b6 100644
--- a/gcc/cp/lex.cc
+++ b/gcc/cp/lex.cc
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "cp-tree.h"
#include "stringpool.h"
+#include "c-family/c-common.h"
#include "c-family/c-pragma.h"
#include "c-family/c-objc.h"
#include "gcc-rich-location.h"
@@ -134,6 +135,7 @@ get_identifier_kind_name (tree id)
void
set_identifier_kind (tree id, cp_identifier_kind kind)
{
+ /* See also below. */
gcc_checking_assert (!IDENTIFIER_KIND_BIT_2 (id)
& !IDENTIFIER_KIND_BIT_1 (id)
& !IDENTIFIER_KIND_BIT_0 (id));
@@ -142,6 +144,27 @@ set_identifier_kind (tree id, cp_identifier_kind kind)
IDENTIFIER_KIND_BIT_0 (id) |= (kind >> 0) & 1;
}
+/* c-common.cc uses these two when registering its own keywords. */
+void
+c_make_keyword (tree id)
+{
+ set_identifier_kind (id, cik_keyword);
+}
+
+void
+c_unmake_keyword (tree id)
+{
+ /* We only expect to be clearing identifiers previously made into keywords by
+ c_make_keyword. This is done primarily for self-tests. */
+ static_assert (cik_keyword == 1, "this assert assumes this value");
+ gcc_checking_assert (IDENTIFIER_KIND_BIT_0 (id)
+ && !IDENTIFIER_KIND_BIT_1 (id)
+ && !IDENTIFIER_KIND_BIT_2 (id));
+ IDENTIFIER_KIND_BIT_2 (id) = 0;
+ IDENTIFIER_KIND_BIT_1 (id) = 0;
+ IDENTIFIER_KIND_BIT_0 (id) = 0;
+}
+
/* Create and tag the internal operator name for the overloaded
operator PTR describes. */
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index befdec3ae0de..86ba4fd4fd1a 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -7041,15 +7041,6 @@ cp_free_lang_data (tree t)
DECL_CHAIN (t) = NULL_TREE;
}
-/* Stub for c-common. Please keep in sync with c-decl.cc.
- FIXME: If address space support is target specific, then this
- should be a C target hook. But currently this is not possible,
- because this function is called via REGISTER_TARGET_PRAGMAS. */
-void
-c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
-{
-}
-
/* Return the number of operands in T that we care about for things like
mangling. */
--
2.55.0
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH v5 4/7] gcc/system.h: Allow for '#define INCLUDE_ITERATOR' to '#include <iterator>'
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
` (2 preceding siblings ...)
2026-07-09 16:36 ` [PATCH v5 3/7] gcc/{c,cp,c-family}: move some address space handling into c-family Arsen Arsenović
@ 2026-07-09 16:36 ` Arsen Arsenović
2026-07-09 17:07 ` Andrea Pinski
2026-07-09 16:36 ` [PATCH v5 5/7] gcc/c-family: implement addr_space_test, a helper for self-testing ASes Arsen Arsenović
` (2 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 16:36 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Thomas Schwinge
From: Thomas Schwinge <tschwinge@baylibre.com>
This avoids:
In file included from /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/bits/basic_ios.h:37:0,
from /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/ios:44,
from /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/ostream:38,
from /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/iterator:64,
from ../../source-gcc/gcc/[...]
/gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/bits/locale_facets.h:247:53: error: macro "toupper" passed 2 arguments, but takes just 1
toupper(char_type *__lo, const char_type* __hi) const
^
[...]
gcc/
* system.h [INCLUDE_ITERATOR]: '#include <iterator>'.
---
gcc/system.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/gcc/system.h b/gcc/system.h
index 5b8323c28e51..f0fa062d469b 100644
--- a/gcc/system.h
+++ b/gcc/system.h
@@ -228,6 +228,9 @@ extern int fprintf_unlocked (FILE *, const char *, ...);
#ifdef INCLUDE_SSTREAM
# include <sstream>
#endif
+#ifdef INCLUDE_ITERATOR
+# include <iterator>
+#endif
# include <memory>
# include <cstring>
# include <initializer_list>
--
2.55.0
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH v5 5/7] gcc/c-family: implement addr_space_test, a helper for self-testing ASes
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
` (3 preceding siblings ...)
2026-07-09 16:36 ` [PATCH v5 4/7] gcc/system.h: Allow for '#define INCLUDE_ITERATOR' to '#include <iterator>' Arsen Arsenović
@ 2026-07-09 16:36 ` Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 6/7] gcc/c++: implement basic support for Named Address Spaces in C++ [PR69549] Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 7/7] c++: make comp_ptr_ttypes_real slightly easier to understand Arsen Arsenović
6 siblings, 0 replies; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 16:36 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Arsen Arsenović
Address spaces are target-specific, meaning that any test for address
spaces must be target-specific, meaning that they're abnormally
susceptible to bitrot. This patch aims to help fix that, by providing a
helper that can (and, in the forthcoming C++ Named Address Spaces patch,
does) help write target-independent self-test for address-space related
logic. It contains enough address spaces with various relationships to
one-another to exercise all cases.
The helper should revert the compiler state to how it was when it
started, i.e. its effects should not contaminate other self-tests.
gcc/c-family/ChangeLog:
* c-common.h (c_register_addr_space): Add comment about the
correlation with address_space_test.
(selftest::addr_space_test): New class. RAII-style
helper for setting up target-independent address space support
state.
* c-common.cc (selftest::addr_space_test::addr_space_test):
Implement.
(selftest::addr_space_test::~addr_space_test): Ditto.
---
gcc/c-family/c-common.cc | 97 ++++++++++++++++++++++++++++++++++++++++
gcc/c-family/c-common.h | 46 +++++++++++++++++++
2 files changed, 143 insertions(+)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index e05551e51192..89d9ea75bfdc 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -20,6 +20,10 @@ along with GCC; see the file COPYING3. If not see
#define GCC_C_COMMON_C
#include "config.h"
+#if CHECKING_P
+# define INCLUDE_ALGORITHM
+# define INCLUDE_ITERATOR
+#endif
#include "system.h"
#include "coretypes.h"
#include "target.h"
@@ -2930,6 +2934,9 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
void
c_register_addr_space (const char *word, addr_space_t as)
{
+ /* Note that selftest::addr_space_test below must perform the opposite of
+ this logic, and ergo it has an inverse of this function. Keep in mind
+ when modifying. */
int rid = RID_FIRST_ADDR_SPACE + as;
tree id;
@@ -10714,4 +10721,94 @@ c_hardbool_type_attr_1 (tree type, tree *false_value, tree *true_value)
return attr;
}
+#if CHECKING_P
+selftest::addr_space_test::addr_space_test ()
+{
+ /* c_register_addr_space is a no-op if this condition is met. */
+ gcc_assert (!(c_dialect_objc () || flag_no_asm));
+
+ /* Save the old keywords. */
+ std::copy (&ridpointers[RID_FIRST_ADDR_SPACE],
+ &ridpointers[RID_LAST_ADDR_SPACE + 1],
+ this->old_ridpointers);
+
+ /* Un-register them. */
+ for (auto rid : this->old_ridpointers)
+ {
+ if (!rid)
+ continue;
+
+ C_SET_RID_CODE (rid, 0);
+ c_unmake_keyword (rid);
+ }
+
+ std::fill (&ridpointers[RID_FIRST_ADDR_SPACE],
+ &ridpointers[RID_LAST_ADDR_SPACE + 1],
+ nullptr);
+ c_register_addr_space ("__as0", 0);
+ c_register_addr_space ("__as1", 1);
+ c_register_addr_space ("__as2", 2);
+ c_register_addr_space ("__as3", 3);
+ c_register_addr_space ("__as4", 4);
+ c_register_addr_space ("__as5", 5);
+
+ /* We don't fully replace all the contents of the below, just enough for
+ tests. */
+ this->old_addr_space_hooks = targetm.addr_space;
+ targetm.addr_space.subset_p = [] (addr_space_t sub, addr_space_t super)
+ {
+ /* Each set is its own (biggest) subset. */
+ if (super == sub)
+ return true;
+
+ /* 0 is the superset of all other address spaces, except for 4 and 5. */
+ if (super == 0 && sub != 4 && sub != 5)
+ return true;
+
+ /* ... because 4 contains 5 (but not vice-versa!). */
+ if (super == 4 && sub == 5)
+ return true;
+
+ /* 2 and 3 overlap. */
+ if ((super == 3 && sub == 2)
+ || (super == 2 && sub == 3))
+ return true;
+ return false;
+ };
+ targetm.addr_space.address_mode
+ = targetm.addr_space.pointer_mode
+ = [] (addr_space_t)
+ { return Pmode; };
+}
+
+selftest::addr_space_test::~addr_space_test ()
+{
+ for (size_t i = RID_FIRST_ADDR_SPACE; i <= RID_LAST_ADDR_SPACE; i++)
+ {
+ tree id = ridpointers[i];
+ if (!id)
+ continue;
+
+ /* Reverse the effects of c_register_addr_space. */
+ C_SET_RID_CODE (id, 0);
+ c_unmake_keyword (id);
+ }
+
+ /* Restore the old keywords. */
+ std::copy (std::begin (this->old_ridpointers), std::end (this->old_ridpointers),
+ &ridpointers[RID_FIRST_ADDR_SPACE]);
+ for (size_t i = RID_FIRST_ADDR_SPACE; i <= RID_LAST_ADDR_SPACE; i++)
+ {
+ tree id = ridpointers[i];
+ if (!id)
+ continue;
+
+ C_SET_RID_CODE (id, i);
+ c_make_keyword (id);
+ }
+
+ targetm.addr_space = this->old_addr_space_hooks;
+}
+#endif
+
#include "gt-c-family-c-common.h"
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 569edc064014..e027b24d2ca2 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1763,6 +1763,52 @@ namespace selftest {
/* The entrypoint for running all of the above tests. */
extern void c_family_tests (void);
+
+ /* Declarations for utilities. */
+ /* A class that sets up various hooks for address-space testing.
+
+ Namely, it creates a situation where the generic address space is the
+ superset of 1, 2, and 3, address spaces 1 and 2 are disjoint, address
+ spaces 2 and 3 overlap fully, and address space 4 is distinct from all,
+ except for 5, which is a proper subset of it.
+
+ In the table below, if the column label is a superset of the row label,
+ the field is marked with X.
+
+ | / | AS0 | AS1 | AS2 | AS3 | AS4 | AS5 |
+ |-----+-----+-----+-----+-----|-----|-----|
+ | AS0 | X | | | | | |
+ | AS1 | X | X | | | | |
+ | AS2 | X | | X | X | | |
+ | AS3 | X | | X | X | | |
+ | AS4 | | | | | X | |
+ | AS5 | | | | | X | X |
+
+ The address spaces can be visualized also as:
+
+ |---------AS0---------| |---AS4---|
+ | |-AS1-| |-AS2-| | | |-AS5-| |
+ | | | |-AS3-| | | | | |
+ */
+
+ class addr_space_test
+ {
+ /* A copy of the addr_space related RID pointers. We'll register a few of
+ our own instead. */
+ tree old_ridpointers[RID_LAST_ADDR_SPACE - RID_FIRST_ADDR_SPACE + 1];
+ /* Previous addr_space hooks. */
+ decltype (targetm.addr_space) old_addr_space_hooks;
+ public:
+ addr_space_test ();
+ ~addr_space_test ();
+
+ /* Immobilize this object. */
+ addr_space_test (const addr_space_test&) = delete;
+ addr_space_test (addr_space_test&&) = delete;
+ addr_space_test &operator= (const addr_space_test&) = delete;
+ addr_space_test &operator= (addr_space_test&&) = delete;
+ };
+
} // namespace selftest
#endif /* #if CHECKING_P */
--
2.55.0
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH v5 6/7] gcc/c++: implement basic support for Named Address Spaces in C++ [PR69549]
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
` (4 preceding siblings ...)
2026-07-09 16:36 ` [PATCH v5 5/7] gcc/c-family: implement addr_space_test, a helper for self-testing ASes Arsen Arsenović
@ 2026-07-09 16:36 ` Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 7/7] c++: make comp_ptr_ttypes_real slightly easier to understand Arsen Arsenović
6 siblings, 0 replies; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 16:36 UTC (permalink / raw)
To: Jason Merrill
Cc: gcc-patches, Arsen Arsenović, Paul IANNETTA, Thomas Schwinge
This patch implements named address space support in the C++ frontend.
The patch integrates address space qualifiers into the language
where-ever they would normally fit, except for member function
qualification (not even for trivial copy/move constructors/operators;
though we should probably support at least the trivial case here at some
point).
Much of this patch grew naturally out of simply changing a few instances
of cp_cv_qualifier into qualifier_set.
In addition, the C++ frontend now extends the NOP_EXPR tree to be able
to handle address space conversions (i.e. if the NOP_EXPR tree original
and resulting type differ, it will adjust it to perform an address space
conversion).
gcc/cp/ChangeLog:
PR c++/69549
* cp-tree.h (cp_type_quals): Return qualifier_set instead of
cv_qualifier. From this, follow many other changes.
(CP_TYPE_CONST_P): Adjust to new return type of cp_type_quals.
(CP_TYPE_VOLATILE_P): Adjust to new return type of
cp_type_quals.
(CP_TYPE_RESTRICT_P): Adjust to new return type of
cp_type_quals.
(CP_TYPE_CONST_NON_VOLATILE_P): Adjust to new return type of
cp_type_quals.
(build_indirection_like): New. Helper function. Builds
indirection like that of ORIG{,_CODE} pointing to T.
(enum cp_decl_spec): Add ds_addr_space decl_spec.
(struct cp_decl_specifier_seq): Add address_space.
(struct cp_declarator): Use qualifier_sets for qualifiers.
(build_this_parm): Take qualifier_set instead of cp_cv_quals.
(build_memfn_type): Add overload taking qualifier_set, and
reducing it down to its cv_quals.
(build_stub_type): Take qualifier_set instead of cv_qualifier.
(inject_this_parameter): Take qualifier_set instead of
cp_cv_quals.
(cp_build_qualified_type): Take qualifier_set instead of
cv_qualifier.
(cp_try_quals_merge): New.
(cp_try_quals_join): New.
(comp_cv_qualification): Take qualifier sets instead of ints.
(comp_ptr_ttypes_const): Take new ADDR_SPACE_STRICT argument,
defaulting to true.
(cp_apply_type_quals_to_decl): Add overload taking qualifier_set
and discarding its cv_quals.
* call.cc (standard_conversion): Adjust to new return type of
cp_type_quals. Pass 'false' as strict_addr_space to
comp_ptr_ttypes_const in c_cast_p case, to permit address space
differences in C casts.
(build_conditional_expr): Update to new return type of
cp_cv_quals.
(convert_like_internal): Emit a hint about address space
differences, if a reference could not be bound due to address
space differences.
(compare_ics): Adjust to new return type of cp_type_quals.
* class.cc (build_simple_base_path): Adjust to new return type
of cp_type_quals.
(iobj_parm_corresponds_to): Adjust to new return type of
cp_type_quals.
* cp-gimplify.cc (cp_genericize_r): Decompose NOP_EXPRs that
convert between address spaces into an ADDR_SPACE_CONVERT_EXPR
that performs the address space conversion from the result of
the same NOP_EXPR, but without the address space conversion.
* cp-lang.cc (run_cp_tests): Run typeck.cc tests.
* cp-objcp-common.h (cp_type_quals_as_set): Drop. Now
redundant, as cp_type_quals returns a qualifier set.
(LANG_HOOKS_TREE_DUMP_TYPE_QUALS_FN): Use cp_type_quals instead
of the above.
* decl.cc (grokvardecl): Take qualifier_set instead of 'int' for
a set of qualifiers.
(check_special_function_return_type): Ditto, and adjust
accordingly.
(get_type_quals): Adjust to also handle address space
qualification.
(check_tag_decl): Forbid address space qualification.
(build_this_parm): Receive qualifier_set instead of cp_cv_quals.
Adjust acordingly.
(build_ptrmemfunc_type): Adjust to new return type of
cp_type_quals.
(smallest_type_quals_location): Adjust to operate on qualifier
sets.
(smallest_type_location): Adjust for new return type of
get_type_quals.
(grokdeclarator): Adjust to new return type of get_type_quals.
Handle conflicting address spaces.
* error.cc (dump_expr): Treat ADDR_SPACE_CONVERT_EXPR as a cast.
* init.cc (build_new_1): Adjust to new return type of
cp_type_quals.
(build_new): Reject attempts to 'new' in non-generic address
spaces.
(build_delete): Likewise, but for 'delete'.
* mangle.cc (write_CV_qualifiers_for_type): Handle address
spaces.
* method.cc (do_build_copy_constructor): Adjust for new return
type of cp_type_quals.
(do_build_copy_assign): Ditto.
(build_stub_type): Adjust to take qualifier_set instead of
cv_qualifier.
(walk_field_subobs): Adjust for new return type of
cp_type_quals.
* module.cc (qs_from_int): New. Helper function unpacking a
qualifier_set from an int.
(qs_to_int): New. Inverse of the above.
(trees_out::type_node): Adjust for new return type of
cp_type_quals. Use qs_to_int to encode quals.
(trees_in::tree_node): Adjust for new return type of
cp_type_quals. Use qs_from_int to decode quals.
* parser.cc (cp_lexer_get_preprocessor_token): Call address
space usage diagnosis hook.
(make_pointer_declarator): Adjust to receive qualifier_set
instead of cp_cv_quals.
(make_reference_declarator): Ditto.
(make_ptrmem_declarator): Ditto.
(cp_parser_make_indirect_declarator): Ditto.
(cp_parser_postfix_expression): Reject address space
qualification on compound literals.
(cp_parser_new_declarator_opt): Adjust to use qualifier_set
instead of cp_cv_quals.
(cp_parser_conversion_declarator_opt): Ditto.
(cp_parser_type_specifier): Handle address space qualification.
(cp_parser_declarator): Adjust to use qualifier_set
instead of cp_cv_quals.
(cp_parser_direct_declarator): When parsing function
qualification, forbit address space qualification.
(cp_parser_ptr_operator): Adjust to take qualifier_set instead
of cp_cv_quals.
(cp_parser_cv_qualifier_seq_opt): Delegate most logic...
(cp_parser_cv_qualifier_seq_opt_1): ... here, so that it can be
shared with...
(cp_parser_cv_qualifier_seq_opt_no_addr_space): ... this
function.
(inject_this_parameter): Adjust to take qualifier_set instead
cp_cv_quals.
(set_and_check_decl_spec_loc): Handle address spaces.
* pt.cc (tsubst_pack_index): Use cp_try_quals_join to merge
qualifiers.
(tsubst_splice_scope): Ditto.
(tsubst): Ditto.
(resolve_typename_type): Ditto.
(check_cv_quals_for_unify): Handle address space qualification.
(unify): Handle address space qualification. Permit address
space qualification differences in top-level of pointer types.
(more_specialized_fn): Adjust to new return type of
cp_type_quals.
(test_unify_addr_spaces): New self-tests. Check that 'unify'
finds the right solutions (or fails to find solutions) in cases
involving address spaces.
(cp_pt_cc_tests): Call the above tests.
* reflect.cc (remove_const): Adjust to new return type of
cp_type_quals.
(eval_remove_volatile): Adjust to new return type of
cp_type_quals.
(eval_make_signed): Adjust to new return type of
cp_type_quals.
* rtti.cc (qualifier_flags): Adjust to new return type of
cp_type_quals.
* search.cc (check_final_overrider): Adjust to new return type
of cp_type_quals.
* semantics.cc (finish_non_static_data_member): Adjust to new
return type of cp_type_quals.
(finish_base_specifier): Ditto.
(finish_decltype_type): Ditto.
* tree.cc (build_min_array_type): As in the C frontend, copy
address space qualification from element type to array type.
(c_build_qualified_type): No longer reject address space
qualification.
(cp_build_qualified_type): Adjust to take qualifier_sets.
(cp_try_quals_merge): New.
(cp_try_quals_join): New.
(cv_unqualified): Adjust to new cp_type_quals return type.
(cp_check_qualified_type): Adjust to take qualifier_set instead
of cv_qualifier.
(build_cp_fntype_variant): Adjust to use qualifier_set instead
of cv_qualifier.
(maybe_dummy_object): Ditto.
* typeck.cc (composite_pointer_type_r): Handle address space
qualification mismatches.
(composite_pointer_type): Allow changing address space
qualification. Adjust to new return type of cp_type_quals.
(comp_except_types): Adjust to new return type of cp_type_quals.
(at_least_as_qualified_p): Ditto. Delegate to can_qualify.
(comp_cv_qualification): Ditto.
(build_class_member_access_expr): Adjust to new return type of
cp_type_quals.
(pointer_diff): Handle address space.
(maybe_warn_about_addr_space_cast): New.
(build_static_cast): Call maybe_warn_about_addr_space_cast.
(build_reinterpret_cast): Ditto.
(build_const_cast): Ditto.
(cp_build_c_cast): Ditto.
(comp_ptr_ttypes_real): Handle address space differences. Add
some commentary.
(comp_ptr_ttypes_const): Handle address space differences.
(cp_type_quals): Adjust to return qualifier_sets. Permit
address space qualification.
(type_memfn_quals): Adjust to new return type of cp_type_quals.
(apply_memfn_quals): Also handle address space components.
(cv_qualified_p): Adjust to new return type of cp_type_quals.
(casts_away_constness_r): Use qualifier_set instead of
cv_qualifier.
(test_addr_spaces): New self-tests, for address space handling.
(cp_typeck_cc_tests): New file self-test function. Call the
above.
* typeck2.cc (build_m_component_ref): Adjust to new return type
of cp_type_quals.
gcc/ChangeLog:
PR c++/69549
* doc/extend.texi (Named Address Spaces): Document that the C++
frontend also parses named address spaces.
* doc/tm.texi.in (Named Address Spaces): Mention that thee are
available in GNU C and C++.
* doc/tm.texi: Regenerate.
gcc/testsuite/ChangeLog:
PR c++/69549
* g++.dg/abi/mangle-addr-space1.C: New test.
* g++.dg/abi/mangle-addr-space2.C: New test.
* g++.dg/ext/addr-space-conv.C: New test.
* g++.dg/ext/addr-space-decl.C: New test.
* g++.dg/ext/addr-space-ops.C: New test.
* g++.dg/ext/addr-space-ref.C: New test.
* g++.dg/parse/addr-space.C: New test.
* g++.dg/parse/addr-space1.C: New test.
* g++.dg/parse/addr-space2.C: New test.
* g++.dg/template/addr-space-overload.C: New test.
* g++.dg/template/addr-space-strip1.C: New test.
* g++.dg/template/addr-space-strip2.C: New test.
* g++.dg/template/spec-addr-space.C: New test.
Co-authored-by: Paul IANNETTA <paul.iannetta@ens-lyon.org>
Co-authored-by: Thomas Schwinge <tschwinge@baylibre.com>
---
gcc/cp/call.cc | 60 +-
gcc/cp/class.cc | 8 +-
gcc/cp/cp-gimplify.cc | 37 ++
gcc/cp/cp-lang.cc | 1 +
gcc/cp/cp-objcp-common.h | 5 +-
gcc/cp/cp-tree.h | 73 ++-
gcc/cp/decl.cc | 141 ++++-
gcc/cp/error.cc | 1 +
gcc/cp/init.cc | 32 +-
gcc/cp/mangle.cc | 11 +-
gcc/cp/method.cc | 23 +-
gcc/cp/module.cc | 45 +-
gcc/cp/parser.cc | 195 +++++--
gcc/cp/pt.cc | 432 ++++++++++++--
gcc/cp/reflect.cc | 7 +-
gcc/cp/rtti.cc | 4 +-
gcc/cp/search.cc | 21 +-
gcc/cp/semantics.cc | 32 +-
gcc/cp/tree.cc | 162 +++++-
gcc/cp/typeck.cc | 540 ++++++++++++++++--
gcc/cp/typeck2.cc | 13 +-
gcc/doc/extend.texi | 2 +-
gcc/doc/tm.texi | 8 +-
gcc/doc/tm.texi.in | 8 +-
gcc/testsuite/g++.dg/abi/mangle-addr-space1.C | 9 +
gcc/testsuite/g++.dg/abi/mangle-addr-space2.C | 9 +
gcc/testsuite/g++.dg/ext/addr-space-conv.C | 209 +++++++
gcc/testsuite/g++.dg/ext/addr-space-decl.C | 5 +
gcc/testsuite/g++.dg/ext/addr-space-ops.C | 24 +
gcc/testsuite/g++.dg/ext/addr-space-ref.C | 29 +
gcc/testsuite/g++.dg/parse/addr-space.C | 9 +
gcc/testsuite/g++.dg/parse/addr-space1.C | 10 +
gcc/testsuite/g++.dg/parse/addr-space2.C | 9 +
.../g++.dg/template/addr-space-overload.C | 17 +
.../g++.dg/template/addr-space-strip1.C | 18 +
.../g++.dg/template/addr-space-strip2.C | 17 +
.../g++.dg/template/spec-addr-space.C | 10 +
37 files changed, 1959 insertions(+), 277 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-conv.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-decl.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ops.C
create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ref.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space1.C
create mode 100644 gcc/testsuite/g++.dg/parse/addr-space2.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-overload.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip1.C
create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip2.C
create mode 100644 gcc/testsuite/g++.dg/template/spec-addr-space.C
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 17de392743f2..687d990d92af 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
/* High-level class interface. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "target.h"
@@ -47,6 +48,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-pretty-print-markup.h"
#include "contracts.h" // maybe_contract_wrap_call
+#include "util/optional.h"
+
/* The various kinds of conversion. */
enum conversion_kind {
@@ -1450,7 +1453,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
{
tree nfrom = TREE_TYPE (from);
/* Don't try to apply restrict to void. */
- auto quals = cp_type_quals (nfrom) & ~TYPE_QUAL_RESTRICT;
+ auto quals = cp_type_quals (nfrom).without (TYPE_QUAL_RESTRICT);
from_pointee = cp_build_qualified_type (void_type_node, quals);
from = build_pointer_type (from_pointee);
conv = build_conv (ck_ptr, from, conv);
@@ -1497,7 +1500,8 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
if (same_type_p (from, to))
/* OK */;
- else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_either))
+ else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_either,
+ /*strict_addr_space=*/false))
/* In a C-style cast, we ignore CV-qualification because we
are allowed to perform a static_cast followed by a
const_cast. */
@@ -6266,10 +6270,15 @@ build_conditional_expr (const op_location_t &loc,
if (converted
&& CLASS_TYPE_P (arg2_type)
&& cp_type_quals (arg2_type) != cp_type_quals (arg3_type))
- arg2_type = arg3_type =
- cp_build_qualified_type (arg2_type,
- cp_type_quals (arg2_type)
- | cp_type_quals (arg3_type));
+ {
+ auto common_quals = cp_try_quals_merge (loc, arg2_type, arg3_type,
+ complain);
+ if (!common_quals)
+ return error_mark_node;
+
+ arg2_type = arg3_type =
+ cp_build_qualified_type (arg2_type, *common_quals);
+ }
}
/* [expr.cond]
@@ -9307,6 +9316,39 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
&& TYPE_DOMAIN (TREE_TYPE (totype)) != NULL_TREE)
error_at (loc, "cannot bind reference of type %qH to %qI "
"due to different array bounds", totype, extype);
+ else if (!targetm.addr_space.subset_p
+ (TYPE_ADDR_SPACE (extype),
+ TYPE_ADDR_SPACE (TREE_TYPE (totype))))
+ /* Likewise, make explicit reference to address space, if
+ they may be the cause of the difference. */
+ {
+ auto_diagnostic_group _;
+ error_at (loc, "binding reference of type %qH to %qI "
+ "in incompatible address space", totype, extype);
+ auto ref_as = TYPE_ADDR_SPACE (TREE_TYPE (totype));
+ auto obj_as = TYPE_ADDR_SPACE (extype);
+ /* "address space XYZ is not contained by ABC" or something
+ to such effect. */
+ if (ADDR_SPACE_GENERIC_P (obj_as))
+ inform (loc,
+ "address space %qs does not contain the generic "
+ "address space", c_addr_space_name (ref_as));
+ else if (ADDR_SPACE_GENERIC_P (ref_as))
+ inform (loc,
+ "generic address space does not contain address "
+ "space %qs", c_addr_space_name (obj_as));
+ else if (!ADDR_SPACE_GENERIC_P (ref_as)
+ && !ADDR_SPACE_GENERIC_P (obj_as))
+ inform (loc,
+ "address space %qs does not contain address "
+ "space %qs",
+ c_addr_space_name (ref_as),
+ c_addr_space_name (obj_as));
+ else
+ /* They can't both be generic, as, for given address
+ space AS, AS is a subset of AS. */
+ gcc_unreachable ();
+ }
else
error_at (loc, "binding reference of type %qH to %qI "
"discards qualifiers", totype, extype);
@@ -13098,13 +13140,13 @@ compare_ics (conversion *ics1, conversion *ics2)
else if (!c1 && c2)
return 1;
- int q1 = cp_type_quals (TREE_TYPE (ref_conv1->type));
- int q2 = cp_type_quals (TREE_TYPE (ref_conv2->type));
+ auto q1 = cp_type_quals (TREE_TYPE (ref_conv1->type));
+ auto q2 = cp_type_quals (TREE_TYPE (ref_conv2->type));
if (ref_conv1->bad_p)
{
/* Prefer the one that drops fewer cv-quals. */
tree ftype = next_conversion (ref_conv1)->type;
- int fquals = cp_type_quals (ftype);
+ auto fquals = cp_type_quals (ftype).cv_quals ();
q1 ^= fquals;
q2 ^= fquals;
}
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 93fa2f6864d3..bca0345d0061 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -601,9 +601,9 @@ build_simple_base_path (tree expr, tree binfo)
/* Mark the expression const or volatile, as appropriate.
Even though we've dealt with the type above, we still have
to mark the expression itself. */
- if (type_quals & TYPE_QUAL_CONST)
+ if (type_quals.has (TYPE_QUAL_CONST))
TREE_READONLY (expr) = 1;
- if (type_quals & TYPE_QUAL_VOLATILE)
+ if (type_quals.has (TYPE_QUAL_VOLATILE))
TREE_THIS_VOLATILE (expr) = 1;
return expr;
@@ -1190,8 +1190,8 @@ iobj_parm_corresponds_to (tree iobj_fn, tree xobj_param, tree context)
/* Even if we are ignoring the reference qualifier, the xobj parameter
was still a reference so we still take the cv qualifiers into
account. */
- cp_cv_quals const xobj_cv_quals
- = cp_type_quals (TREE_TYPE (xobj_param)) & cv_bits;
+ auto xobj_cv_quals
+ = cp_type_quals (TREE_TYPE (xobj_param)).intersect (cv_bits);
/* Finally, if the qualifications don't match exactly, the object
parameters don't correspond. */
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 23a1b7ea2ccd..44d32eee0a79 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -2420,10 +2420,47 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
}
}
+ /* Appropriately lower conversions between address spaces. We do this so
+ late, as the frontend uses NOP_EXPRs to represent all sorts of
+ casts.
+
+ If we need to convert between address spaces, we'll start by producing
+ a NOP_EXPR conversion without the address-space difference to handle
+ qualifiers etc and, if that works, we'll convert the result of that to
+ the appropriate address space. */
+ {
+ tree out_type = TREE_TYPE (stmt);
+ tree in_type = TREE_TYPE (TREE_OPERAND (stmt, 0));
+ if (INDIRECT_TYPE_P (out_type) && INDIRECT_TYPE_P (in_type))
+ {
+ addr_space_t in_as = TYPE_ADDR_SPACE (TREE_TYPE (in_type));
+ addr_space_t out_as = TYPE_ADDR_SPACE (TREE_TYPE (out_type));
+ if (in_as != out_as)
+ {
+ tree pointee = TREE_TYPE (out_type);
+ /* Remove the address space difference. */
+ pointee = (build_qualified_type
+ (pointee,
+ qualifier_set (TYPE_QUALS_NO_ADDR_SPACE (pointee),
+ in_as)));
+ auto loc = EXPR_LOCATION (stmt);
+ /* Replace the original conversion. */
+ stmt = (fold_build1_loc
+ (loc, NOP_EXPR,
+ build_indirection_like (out_type, pointee),
+ TREE_OPERAND (stmt, 0)));
+ /* Then, re-add an address space conversion. */
+ stmt = *stmt_p = fold_build1_loc (loc, ADDR_SPACE_CONVERT_EXPR,
+ out_type, stmt);
+ }
+ }
+ }
+
if (!wtd->no_sanitize_p
&& sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT)
&& TYPE_REF_P (TREE_TYPE (stmt)))
ubsan_maybe_instrument_reference (stmt_p);
+
break;
case CALL_EXPR:
diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
index c9d02cc73024..450fe5f43e9b 100644
--- a/gcc/cp/cp-lang.cc
+++ b/gcc/cp/cp-lang.cc
@@ -299,6 +299,7 @@ run_cp_tests (void)
/* Additional C++-specific tests. */
cp_pt_cc_tests ();
cp_tree_cc_tests ();
+ cp_typeck_cc_tests ();
}
} // namespace selftest
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index e66b8f7329d9..ed29e65e4f3e 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -142,10 +142,7 @@ static const scoped_attribute_specs *const cp_objcp_attribute_table[] =
#undef LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN
#define LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN cp_dump_tree
#undef LANG_HOOKS_TREE_DUMP_TYPE_QUALS_FN
-inline qualifier_set
-cp_type_quals_as_set (const_tree type)
-{ return { cp_type_quals (type) }; }
-#define LANG_HOOKS_TREE_DUMP_TYPE_QUALS_FN cp_type_quals_as_set
+#define LANG_HOOKS_TREE_DUMP_TYPE_QUALS_FN cp_type_quals
#undef LANG_HOOKS_MAKE_TYPE
#define LANG_HOOKS_MAKE_TYPE cxx_make_type_hook
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 32865ba1ff3a..ca41f85ed6a9 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -26,6 +26,8 @@ along with GCC; see the file COPYING3. If not see
#include "function.h"
#include "tristate.h"
+#include "util/optional_fwd.h"
+
/* In order for the format checking to accept the C++ front end
diagnostic framework extensions, you must include this file before
diagnostic-core.h, not after. We override the definition of GCC_DIAG_STYLE
@@ -2547,22 +2549,22 @@ enum languages { lang_c, lang_cplusplus };
/* Nonzero if this type is const-qualified. */
#define CP_TYPE_CONST_P(NODE) \
- ((cp_type_quals (NODE) & TYPE_QUAL_CONST) != 0)
+ (cp_type_quals (NODE).has (TYPE_QUAL_CONST))
/* Nonzero if this type is volatile-qualified. */
#define CP_TYPE_VOLATILE_P(NODE) \
- ((cp_type_quals (NODE) & TYPE_QUAL_VOLATILE) != 0)
+ (cp_type_quals (NODE).has (TYPE_QUAL_VOLATILE))
/* Nonzero if this type is restrict-qualified. */
#define CP_TYPE_RESTRICT_P(NODE) \
- ((cp_type_quals (NODE) & TYPE_QUAL_RESTRICT) != 0)
+ (cp_type_quals (NODE).has (TYPE_QUAL_RESTRICT))
/* Nonzero if this type is const-qualified, but not
volatile-qualified. Other qualifiers are ignored. This macro is
used to test whether or not it is OK to bind an rvalue to a
reference. */
#define CP_TYPE_CONST_NON_VOLATILE_P(NODE) \
- ((cp_type_quals (NODE) & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) \
+ ((cp_type_quals (NODE).intersect (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) \
== TYPE_QUAL_CONST)
#define FUNCTION_ARG_CHAIN(NODE) \
@@ -5232,6 +5234,32 @@ get_vec_init_expr (tree t)
#define INDIRECT_TYPE_P(NODE) \
(TYPE_PTR_P (NODE) || TYPE_REF_P (NODE))
+/* Return an indirection of code ORIG_CODE around T. For instance, of
+ ORIG_CODE is POINTER_TYPE, then return T*.
+
+ Applicable for ORIG_CODE types accepted by INDIRECT_TYPE_P. */
+
+inline tree
+build_indirection_like (tree_code orig_code, tree t)
+{
+ switch (orig_code)
+ {
+ case POINTER_TYPE: return build_pointer_type (t);
+ case REFERENCE_TYPE: return build_reference_type (t);
+ default: gcc_unreachable ();
+ }
+}
+
+/* Return an indirection of same code as ORIG, but around T instead. For
+ instance, if ORIG is a pointer, then return T*;
+
+ Applicable for ORIGs accepted by INDIRECT_TYPE_P. */
+
+inline tree
+build_indirection_like (tree orig, tree indirected)
+{ return build_indirection_like (TREE_CODE (orig), indirected); }
+
+
/* Returns true if NODE is an object type:
[basic.types]
@@ -6964,6 +6992,7 @@ enum cp_decl_spec {
ds_constinit,
ds_consteval,
ds_this, /* Index of last element of decl_spec_names. */
+ ds_addr_space,
ds_thread,
ds_type_spec,
ds_redefined_builtin_type_spec,
@@ -7000,6 +7029,8 @@ struct cp_decl_specifier_seq {
cp_storage_class storage_class;
/* For the __intN declspec, this stores the index into the int_n_* arrays. */
int int_n_idx;
+ /* The address space that the declaration belongs to. */
+ addr_space_t address_space;
/* True iff TYPE_SPEC defines a class or enum. */
bool type_definition_p : 1;
/* True iff multiple types were (erroneously) specified for this
@@ -7133,7 +7164,7 @@ struct cp_declarator {
/* For cdk_pointer and cdk_ptrmem. */
struct {
/* The cv-qualifiers for the pointer. */
- cp_cv_quals qualifiers;
+ qualifier_set qualifiers;
/* For cdk_ptrmem, the class type containing the member. */
tree class_type;
} pointer;
@@ -7141,7 +7172,7 @@ struct cp_declarator {
struct {
/* The cv-qualifiers for the reference. These qualifiers are
only used to diagnose ill-formed code. */
- cp_cv_quals qualifiers;
+ qualifier_set qualifiers;
/* Whether this is an rvalue reference */
bool rvalue_ref;
} reference;
@@ -7646,7 +7677,7 @@ extern int cp_complete_array_type_or_error (tree *, tree, bool, tsubst_flags_t);
extern tree build_ptrmemfunc_type (tree);
extern tree build_ptrmem_type (tree, tree);
/* the grokdeclarator prototype is in decl.h */
-extern tree build_this_parm (tree, tree, cp_cv_quals);
+extern tree build_this_parm (tree, tree, qualifier_set);
extern tree grokparms (tree, tree *);
extern int copy_fn_p (const_tree);
extern bool move_fn_p (const_tree);
@@ -7725,6 +7756,13 @@ extern void overwrite_mangling (tree, tree);
extern void note_mangling_alias (tree, tree);
extern void generate_mangling_aliases (void);
extern tree build_memfn_type (tree, tree, cp_cv_quals, cp_ref_qualifier);
+inline tree
+build_memfn_type (tree fntype, tree ctype, qualifier_set quals,
+ cp_ref_qualifier rqual)
+{
+ /* Currently, member function types cannot be address-space qualified. */
+ return build_memfn_type (fntype, ctype, quals.cv_quals (), rqual);
+}
extern tree build_pointer_ptrmemfn_type (tree);
extern tree change_return_type (tree, tree);
extern void maybe_retrofit_in_chrg (tree);
@@ -8007,7 +8045,7 @@ extern tree get_copy_ctor (tree, tsubst_flags_t);
extern tree get_copy_assign (tree);
extern tree get_default_ctor (tree);
extern tree get_dtor (tree, tsubst_flags_t);
-extern tree build_stub_type (tree, cv_qualifier, bool);
+extern tree build_stub_type (tree, qualifier_set, bool);
extern tree build_stub_object (tree);
extern bool is_stub_object (tree);
extern tree build_invoke (tree, const_tree,
@@ -8158,7 +8196,7 @@ extern void cp_finish_omp_range_for (tree, tree);
extern bool cp_maybe_parse_omp_decl (tree, tree);
extern bool parsing_nsdmi (void);
extern bool parsing_function_declarator ();
-extern void inject_this_parameter (tree, cp_cv_quals);
+extern void inject_this_parameter (tree, qualifier_set);
extern location_t defparse_location (tree);
extern void maybe_show_extern_c_location (void);
extern bool literal_integer_zerop (const_tree);
@@ -8869,8 +8907,12 @@ extern tree make_ptrmem_cst (tree, tree);
extern tree cp_build_type_attribute_variant (tree, tree);
extern tree cp_build_reference_type (tree, bool);
extern tree move (tree);
-extern tree cp_build_qualified_type (tree, cv_qualifier,
+extern tree cp_build_qualified_type (tree, qualifier_set,
tsubst_flags_t = tf_warning_or_error);
+extern tl::optional<qualifier_set> cp_try_quals_merge (location_t, tree, tree,
+ tsubst_flags_t = tf_warning_or_error);
+extern tl::optional<qualifier_set> cp_try_quals_join (location_t, tree, qualifier_set,
+ tsubst_flags_t = tf_warning_or_error);
extern tree cp_build_function_type (tree, tree);
extern bool cv_qualified_p (const_tree);
extern tree cv_unqualified (tree);
@@ -8935,7 +8977,7 @@ extern bool next_common_initial_sequence (tree &, tree &);
extern bool layout_compatible_type_p (tree, tree, bool = false);
extern bool compparms (const_tree, const_tree);
extern int comp_cv_qualification (const_tree, const_tree);
-extern int comp_cv_qualification (int, int);
+extern int comp_cv_qualification (qualifier_set, qualifier_set);
extern int comp_cv_qual_signature (tree, tree);
extern tree cxx_sizeof_or_alignof_expr (location_t, tree,
enum tree_code, bool, bool);
@@ -9027,12 +9069,13 @@ extern tree convert_for_initialization (tree, tree, tree, int,
impl_conv_rhs, tree, int,
tsubst_flags_t);
extern int comp_ptr_ttypes (tree, tree);
-extern bool comp_ptr_ttypes_const (tree, tree, compare_bounds_t);
+extern bool comp_ptr_ttypes_const (tree, tree, compare_bounds_t,
+ bool addr_space_strict = true);
extern bool error_type_p (const_tree);
extern bool ptr_reasonably_similar (const_tree, const_tree);
extern tree build_ptrmemfunc (tree, tree, int, bool,
tsubst_flags_t);
-extern cv_qualifier cp_type_quals (const_tree);
+extern qualifier_set cp_type_quals (const_tree);
extern cv_qualifier type_memfn_quals (const_tree);
extern cp_ref_qualifier type_memfn_rqual (const_tree);
extern tree apply_memfn_quals (tree, cp_cv_quals,
@@ -9040,6 +9083,9 @@ extern tree apply_memfn_quals (tree, cp_cv_quals,
extern bool cp_has_mutable_p (const_tree);
extern bool at_least_as_qualified_p (const_tree, const_tree);
extern void cp_apply_type_quals_to_decl (cv_qualifier, tree);
+inline void
+cp_apply_type_quals_to_decl (qualifier_set quals, tree decl)
+{ cp_apply_type_quals_to_decl (quals.cv_quals (), decl); }
extern tree build_ptrmemfunc1 (tree, tree, tree);
extern void expand_ptrmemfunc_cst (tree, tree *, tree *);
extern tree type_after_usual_arithmetic_conversions (tree, tree);
@@ -9771,6 +9817,7 @@ namespace selftest {
/* Declarations for specific families of tests within cp,
by source file, in alphabetical order. */
extern void cp_pt_cc_tests ();
+ extern void cp_typeck_cc_tests (void);
extern void cp_tree_cc_tests (void);
} // namespace selftest
#endif /* #if CHECKING_P */
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 198a7111ac58..5225f4c0f51f 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see
line numbers. For example, the CONST_DECLs for enum values. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL
#include "system.h"
#include "coretypes.h"
#include "target.h"
@@ -65,6 +66,8 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-highlight-colors.h"
#include "pretty-print-markup.h"
+#include "util/expected.h"
+
/* Possible cases of bad specifiers type used by bad_specifiers. */
enum bad_spec_place {
BSP_VAR, /* variable */
@@ -80,7 +83,7 @@ static bool decl_instrument_init_bypass_p (tree);
static void require_complete_types_for_parms (tree);
static tree grok_reference_init (tree, tree, tree, int);
static tree grokvardecl (tree, tree, tree, const cp_decl_specifier_seq *,
- int, int, int, bool, int, tree, location_t);
+ int, qualifier_set, int, bool, int, tree, location_t);
static void check_static_variable_definition (tree, tree);
static void record_unknown_type (tree, const char *);
static int member_function_or_else (tree, tree, enum overload_flags);
@@ -102,7 +105,7 @@ static void end_cleanup_fn (void);
static tree cp_make_fname_decl (location_t, tree, int);
static void initialize_predefined_identifiers (void);
static tree check_special_function_return_type
- (special_function_kind, tree, tree, int, const cp_declarator**,
+ (special_function_kind, tree, tree, qualifier_set, const cp_declarator**,
const location_t*);
static tree push_cp_library_fn (enum tree_code, tree, int);
static tree build_cp_library_fn (tree, enum tree_code, tree, int);
@@ -6266,10 +6269,10 @@ warn_misplaced_attr_for_class_type (location_t location,
/* Returns the cv-qualifiers that apply to the type specified
by the DECLSPECS. */
-static cv_qualifier
+static qualifier_set
get_type_quals (const cp_decl_specifier_seq *declspecs)
{
- auto type_quals = TYPE_UNQUALIFIED;
+ qualifier_set type_quals {};
if (decl_spec_seq_has_spec_p (declspecs, ds_const))
type_quals |= TYPE_QUAL_CONST;
@@ -6277,6 +6280,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ type_quals.set_as (declspecs->address_space);
return type_quals;
}
@@ -6399,6 +6404,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
error_at (declspecs->locations[ds_restrict],
"%<__restrict%> can only be specified for objects and "
"functions");
+ else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space))
+ error_at (declspecs->locations[ds_addr_space],
+ "address space can only be specified for objects and "
+ "functions");
else if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
error_at (declspecs->locations[ds_thread],
"%<__thread%> can only be specified for objects "
@@ -12054,24 +12063,26 @@ check_class_member_definition_namespace (tree decl)
cv-qualifiers that apply to the function. */
tree
-build_this_parm (tree fn, tree type, cp_cv_quals quals)
+build_this_parm (tree fn, tree type, qualifier_set quals)
{
tree this_type;
tree qual_type;
tree parm;
- cp_cv_quals this_quals;
if (CLASS_TYPE_P (type))
{
this_type
- = cp_build_qualified_type (type, quals & ~TYPE_QUAL_RESTRICT);
+ = cp_build_qualified_type (type, quals.without (TYPE_QUAL_RESTRICT));
this_type = build_pointer_type (this_type);
}
else
this_type = type_of_this_parm (type);
/* The `this' parameter is implicitly `const'; it cannot be
assigned to. */
- this_quals = (quals & TYPE_QUAL_RESTRICT) | TYPE_QUAL_CONST;
+ auto this_quals = (quals.without (~TYPE_QUAL_RESTRICT)
+ .with (TYPE_QUAL_CONST)
+ /* Parameter type can't have an address space. */
+ .with_as (ADDR_SPACE_GENERIC));
qual_type = cp_build_qualified_type (this_type, this_quals);
parm = build_artificial_parm (fn, this_identifier, qual_type);
cp_apply_type_quals_to_decl (this_quals, parm);
@@ -12817,7 +12828,7 @@ grokvardecl (tree type,
tree orig_declarator,
const cp_decl_specifier_seq *declspecs,
int initialized,
- int type_quals,
+ qualifier_set type_quals,
int inlinep,
bool conceptp,
int template_count,
@@ -12829,8 +12840,8 @@ grokvardecl (tree type,
gcc_assert (!name || identifier_p (name));
- bool constp = (type_quals & TYPE_QUAL_CONST) != 0;
- bool volatilep = (type_quals & TYPE_QUAL_VOLATILE) != 0;
+ bool constp = type_quals.has (TYPE_QUAL_CONST);
+ bool volatilep = type_quals.has (TYPE_QUAL_VOLATILE);
/* Compute the scope in which to place the variable, but remember
whether or not that scope was explicitly specified by the user. */
@@ -12995,7 +13006,8 @@ build_ptrmemfunc_type (tree type)
/* Make sure that we always have the unqualified pointer-to-member
type first. */
- if (cp_cv_quals quals = cp_type_quals (type))
+ auto quals = cp_type_quals (type);
+ if (quals.nonempty_p ())
{
tree unqual = build_ptrmemfunc_type (TYPE_MAIN_VARIANT (type));
return cp_build_qualified_type (unqual, quals);
@@ -13560,29 +13572,32 @@ min_location (location_t loca, location_t locb)
/* Returns the smallest location != UNKNOWN_LOCATION among the
three stored in LOCATIONS[ds_const], LOCATIONS[ds_volatile],
- and LOCATIONS[ds_restrict]. */
+ LOCATIONS[ds_restrict], and LOCATIONS[ds_addr_space]. */
static location_t
-smallest_type_quals_location (int type_quals, const location_t* locations)
+smallest_type_quals_location (qualifier_set type_quals, const location_t* locations)
{
location_t loc = UNKNOWN_LOCATION;
- if (type_quals & TYPE_QUAL_CONST)
+ if (type_quals.has (TYPE_QUAL_CONST))
loc = locations[ds_const];
- if (type_quals & TYPE_QUAL_VOLATILE)
+ if (type_quals.has (TYPE_QUAL_VOLATILE))
loc = min_location (loc, locations[ds_volatile]);
- if (type_quals & TYPE_QUAL_RESTRICT)
+ if (type_quals.has (TYPE_QUAL_RESTRICT))
loc = min_location (loc, locations[ds_restrict]);
+ if (!ADDR_SPACE_GENERIC_P (type_quals.addr_space ()))
+ loc = min_location (loc, locations[ds_addr_space]);
+
return loc;
}
/* Returns the smallest among the latter and locations[ds_type_spec]. */
static location_t
-smallest_type_location (int type_quals, const location_t* locations)
+smallest_type_location (qualifier_set type_quals, const location_t* locations)
{
location_t loc = smallest_type_quals_location (type_quals, locations);
return min_location (loc, locations[ds_type_spec]);
@@ -13591,7 +13606,7 @@ smallest_type_location (int type_quals, const location_t* locations)
static location_t
smallest_type_location (const cp_decl_specifier_seq *declspecs)
{
- int type_quals = get_type_quals (declspecs);
+ auto type_quals = get_type_quals (declspecs);
return smallest_type_location (type_quals, declspecs->locations);
}
@@ -13623,7 +13638,7 @@ static tree
check_special_function_return_type (special_function_kind sfk,
tree type,
tree optype,
- int type_quals,
+ qualifier_set type_quals,
const cp_declarator** declarator,
const location_t* locations)
{
@@ -14688,10 +14703,42 @@ grokdeclarator (const cp_declarator *declarator,
if (CLASS_TYPE_P (type)
&& DECL_SELF_REFERENCE_P (TYPE_NAME (type))
&& type == TREE_TYPE (TYPE_NAME (type))
- && (declarator || type_quals))
+ && (declarator || type_quals.nonempty_p ()))
type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
- type_quals |= cp_type_quals (type);
+ /* Merge newly-provided qualification with old qualification of TYPE. We're
+ not using cp_type_quals_join because we have better location information
+ here than what cp_try_quals_join can use (it expects one location
+ referring to the whole problematic use of a type, we can point
+ specifically to the address space qualifier) though, the messages emitted
+ ought to be the same. */
+ auto maybe_type_quals = cp_type_quals (type).join (type_quals);
+ if (maybe_type_quals)
+ type_quals = *maybe_type_quals;
+ else
+ {
+ /* We couldn't join them. Emit a diagnostic, recover. */
+ auto old_quals = cp_type_quals (type);
+ addr_space_t type_quals_as;
+ cv_qualifier type_quals_cv;
+ std::tie (type_quals_cv, type_quals_as) = type_quals.split ();
+
+ switch (maybe_type_quals.error ())
+ {
+ case qualifier_set::join_error::double_addr_space:
+ {
+ location_t loc = declspecs->locations[ds_addr_space];
+ error_at (loc, "conflicting named address spaces (%s vs %s)",
+ c_addr_space_name (old_quals.addr_space ()),
+ c_addr_space_name (type_quals_as));
+ }
+ break;
+ }
+
+ /* Continue with new address space. */
+ type_quals = {type_quals_cv | old_quals.cv_quals(), type_quals_as};
+ }
+
type = cp_build_qualified_type
(type, type_quals, ((((typedef_decl && !DECL_ARTIFICIAL (typedef_decl))
|| declspecs->decltype_p)
@@ -14847,7 +14894,7 @@ grokdeclarator (const cp_declarator *declarator,
constexpr_p = 0;
}
/* [dcl.struct.bind] "A cv that includes volatile is deprecated." */
- if (type_quals & TYPE_QUAL_VOLATILE)
+ if (type_quals.has (TYPE_QUAL_VOLATILE))
warning_at (declspecs->locations[ds_volatile], OPT_Wvolatile,
"%<volatile%>-qualified structured binding is deprecated");
switch (storage_class)
@@ -15375,7 +15422,7 @@ grokdeclarator (const cp_declarator *declarator,
"qualifiers ignored on function return type");
/* [dcl.fct] "A volatile-qualified return type is
deprecated." */
- if (type_quals & TYPE_QUAL_VOLATILE)
+ if (type_quals.has (TYPE_QUAL_VOLATILE))
warning_at (typespec_loc, OPT_Wvolatile,
"%<volatile%>-qualified return type is "
"deprecated");
@@ -15781,7 +15828,7 @@ grokdeclarator (const cp_declarator *declarator,
/* Process a list of type modifier keywords (such as
const or volatile) that were given inside the `*' or `&'. */
- if (declarator->u.pointer.qualifiers)
+ if (declarator->u.pointer.qualifiers.nonempty_p ())
{
type
= cp_build_qualified_type (type,
@@ -16024,7 +16071,7 @@ grokdeclarator (const cp_declarator *declarator,
name);
storage_class = sc_none;
}
- else if (type_quals & TYPE_QUAL_CONST)
+ else if (type_quals.has (TYPE_QUAL_CONST))
{
error_at (sloc, "%<const%> %qs cannot be declared %<mutable%>",
name);
@@ -17067,7 +17114,7 @@ grokdeclarator (const cp_declarator *declarator,
{
/* It's common practice (and completely valid) to have a const
be initialized and declared extern. */
- if (!(type_quals & TYPE_QUAL_CONST))
+ if (!type_quals.has (TYPE_QUAL_CONST))
warning_at (DECL_SOURCE_LOCATION (decl), 0,
"%qs initialized and declared %<extern%>", name);
}
@@ -17140,6 +17187,46 @@ grokdeclarator (const cp_declarator *declarator,
&& (!VAR_P (decl) || !DECL_TEMPLATE_INSTANTIATED (decl)))
cp_apply_type_quals_to_decl (type_quals, decl);
+ /* Warn about address space used for things other than static memory or
+ pointers. */
+ addr_space_t address_space = type_quals.addr_space ();
+ if (!ADDR_SPACE_GENERIC_P (address_space))
+ {
+ if (VAR_P (decl) && decl_context == NORMAL)
+ {
+ duration_kind dk = decl_storage_duration (decl);
+ switch (dk)
+ {
+ case dk_auto:
+ error ("%qs specified for variable %qs with automatic storage",
+ c_addr_space_name (address_space), name);
+ break;
+ case dk_static:
+ case dk_thread:
+ case dk_dynamic:
+ break;
+ }
+ }
+ else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE)
+ {
+ if (name)
+ error ("%qs specified for parameter %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for unnamed parameter",
+ c_addr_space_name (address_space));
+ }
+ else if (decl_context == FIELD)
+ {
+ if (name)
+ error ("%qs specified for structure field %qs",
+ c_addr_space_name (address_space), name);
+ else
+ error ("%qs specified for structure field",
+ c_addr_space_name (address_space));
+ }
+ }
+
return decl;
}
}
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index a322ba54855e..62eb769f00c4 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2843,6 +2843,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
CASE_CONVERT:
case IMPLICIT_CONV_EXPR:
case VIEW_CONVERT_EXPR:
+ case ADDR_SPACE_CONVERT_EXPR:
case EXCESS_PRECISION_EXPR:
{
tree op = TREE_OPERAND (t, 0);
diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc
index 502c1ee37a94..27c09202a7c7 100644
--- a/gcc/cp/init.cc
+++ b/gcc/cp/init.cc
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see
#include "varasm.h"
#include "gimplify.h"
#include "c-family/c-ubsan.h"
+#include "c-family/c-common.h"
#include "intl.h"
#include "stringpool.h"
#include "attribs.h"
@@ -3673,7 +3674,9 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
/* But we want to operate on a non-const version to start with,
since we'll be modifying the elements. */
non_const_pointer_type = build_pointer_type
- (cp_build_qualified_type (type, cp_type_quals (type) & ~TYPE_QUAL_CONST));
+ (cp_build_qualified_type (type,
+ cp_type_quals (type)
+ .without (TYPE_QUAL_CONST)));
data_addr = fold_convert (non_const_pointer_type, data_addr);
/* Any further uses of alloc_node will want this type, too. */
@@ -4133,6 +4136,20 @@ build_new (location_t loc, vec<tree, va_gc> **placement, tree type,
return error_mark_node;
}
+ /* Reject attempts to allocate in non-generic AS-es. This is not possible,
+ as 'new' calls an allocation function for which "the return type shall be
+ 'pointer to void'". Hence, it cannot (even for a placement-new!) return
+ a pointer to any address space. */
+ auto alloc_as = cp_type_quals (type).addr_space ();
+ if (!ADDR_SPACE_GENERIC_P (alloc_as))
+ {
+ if (complain & tf_error)
+ error_at (loc, "%<new%> cannot allocate in address space %qs",
+ c_addr_space_name (alloc_as));
+ return error_mark_node;
+ }
+
+
/* P1009: Array size deduction in new-expressions. */
const bool array_p = TREE_CODE (type) == ARRAY_TYPE;
if (*init
@@ -5448,7 +5465,18 @@ build_delete (location_t loc, tree otype, tree addr,
return error_mark_node;
if (TYPE_PTR_P (type))
- type = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+ type = TREE_TYPE (type);
+
+ auto dealloc_as = cp_type_quals (type).addr_space ();
+ if (!ADDR_SPACE_GENERIC_P (dealloc_as))
+ {
+ if (complain & tf_error)
+ error_at (loc, "%<delete%> cannot delete objects in address space %qs",
+ c_addr_space_name (dealloc_as));
+ return error_mark_node;
+ }
+
+ type = TYPE_MAIN_VARIANT (type);
if (TREE_CODE (type) == ARRAY_TYPE)
{
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index e88e61f85184..0d27f60f670c 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2896,9 +2896,14 @@ write_CV_qualifiers_for_type (const tree type)
addr_space_t as;
std::tie (quals, as) = TYPE_QUALS (type).split ();
- /* No address space support yet. */
- gcc_checking_assert (ADDR_SPACE_GENERIC_P (as));
-
+ if (as)
+ {
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 1ef9a4ebf260..f756f995ea50 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -770,8 +770,15 @@ do_build_copy_constructor (tree fndecl)
auto quals = cvquals;
if (DECL_MUTABLE_P (field))
- quals &= ~TYPE_QUAL_CONST;
- quals |= cp_type_quals (expr_type);
+ quals.remove (TYPE_QUAL_CONST);
+ cv_qualifier expr_cv;
+ addr_space_t expr_as;
+ std::tie (expr_cv, expr_as)
+ = cp_type_quals (expr_type).split ();
+ /* EXPR_TYPE is of a field, and fields can't have address
+ spaces. */
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (expr_as));
+ quals |= expr_cv;
expr_type = cp_build_qualified_type (expr_type, quals);
}
@@ -856,7 +863,7 @@ do_build_copy_assign (tree fndecl)
tree init = parm;
tree field = fields;
tree expr_type;
- cv_qualifier quals;
+ qualifier_set quals;
if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
continue;
@@ -894,7 +901,7 @@ do_build_copy_assign (tree fndecl)
/* Compute the type of init->field */
quals = cvquals;
if (DECL_MUTABLE_P (field))
- quals &= ~TYPE_QUAL_CONST;
+ quals = quals.without (TYPE_QUAL_CONST);
expr_type = cp_build_qualified_type (expr_type, quals);
init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
@@ -1896,11 +1903,11 @@ maybe_synthesize_method (tree fndecl)
return synthesize_method (fndecl);
}
-/* Build a reference to type TYPE with cv-quals QUALS, which is an
- rvalue if RVALUE is true. */
+/* Build a reference to type TYPE with qualifiers QUALS, which is an rvalue if
+ RVALUE is true. */
tree
-build_stub_type (tree type, cv_qualifier quals, bool rvalue)
+build_stub_type (tree type, qualifier_set quals, bool rvalue)
{
tree argtype
= cp_build_qualified_type (type, quals,
@@ -2847,7 +2854,7 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
{
auto mem_quals = cp_type_quals (mem_type) | quals;
if (DECL_MUTABLE_P (field))
- mem_quals &= ~TYPE_QUAL_CONST;
+ mem_quals = mem_quals.without (TYPE_QUAL_CONST);
argtype = build_stub_type (mem_type, mem_quals, SFK_MOVE_P (sfk));
}
else
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 9bde899e5594..81dabfeb1bd5 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -562,6 +562,17 @@ public:
struct bits_in;
bits_in stream_bits ();
};
+
+/* Decode qualifier_set from DATA, as encoded by qs_to_int. */
+qualifier_set
+qs_from_int (int data)
+{
+ /* Reminder from bytes_out::qs: the storage is 0xAAC, where AA is the
+ address space and C the cv-qualifier set. */
+ auto cv = cv_qualifier (data & 0xf);
+ addr_space_t as = data >> 4;
+ return {cv, as};
+}
} // anon namespace
/* Verify the buffer's CRC is correct. */
@@ -669,6 +680,28 @@ protected:
static unsigned spans[4];
static unsigned lengths[4];
};
+
+/* Encode a qualifier-set as a positive integer. Decoded by qs_from_int. */
+int
+qs_to_int (qualifier_set qs)
+{
+ addr_space_t as;
+ cv_qualifier cv;
+ std::tie (cv, as) = qs.split ();
+
+ /* Pack AS into higher bits of CV. The lowest bits of CV represent a
+ bitmap of cv_qualifiers. Address space qualifiers are uncommon. We'll
+ have (hex) 0xAAC where AA is the address space, most often zero, and C
+ are qualifiers, of which there is only four currently. Hence, in the
+ most frequent case, where there's no address spaces, we'll be writing
+ out a number <= 16. If there is an address space, address spaces up to
+ and including 7 won't push up the size of the written int. */
+ static_assert (TYPE_QUAL_ALL <= 0xf,
+ "The module serialization currently assumes that qualifiers"
+ " fit into four bits.");
+ gcc_assert (cv <= 0xf);
+ return as << 4 | cv;
+}
} // anon namespace
/* Finish bit packet. Rewind the bytes not used. */
@@ -9664,11 +9697,12 @@ trees_out::type_node (tree type)
if (streaming_p ())
{
/* Qualifiers. */
- int rquals = cp_type_quals (root);
- int quals = cp_type_quals (type);
+ auto rquals = cp_type_quals (root);
+ auto quals = cp_type_quals (type);
if (quals == rquals)
- quals = -1;
- i (quals);
+ i (-1);
+ else
+ i (qs_to_int (quals));
}
if (ref_node (type) != WK_none)
@@ -10793,8 +10827,7 @@ trees_in::tree_node (bool is_use)
int quals = i ();
if (quals >= 0 && !get_overrun ())
- res = cp_build_qualified_type (res,
- static_cast<cv_qualifier> (quals));
+ res = cp_build_qualified_type (res, qs_from_int (quals));
int tag = i ();
if (!tag)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index b57acc6cba59..106af86acedc 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
#define INCLUDE_STRING
#include "system.h"
#include "coretypes.h"
+#include "target.h"
#include "cp-tree.h"
#include "c-family/c-common.h"
#include "timevar.h"
@@ -1152,6 +1153,14 @@ cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token)
token->type = CPP_KEYWORD;
/* Record which keyword. */
token->keyword = C_RID_CODE (token->u.value);
+
+ if (token->keyword >= RID_FIRST_ADDR_SPACE
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ {
+ addr_space_t as
+ = (addr_space_t) (token->keyword - RID_FIRST_ADDR_SPACE);
+ targetm.addr_space.diagnose_usage (as, token->location);
+ }
}
else
{
@@ -1866,11 +1875,11 @@ static cp_declarator *make_call_declarator
static cp_declarator *make_array_declarator
(cp_declarator *, tree, tree);
static cp_declarator *make_pointer_declarator
- (cp_cv_quals, cp_declarator *, tree);
+ (qualifier_set, cp_declarator *, tree);
static cp_declarator *make_reference_declarator
- (cp_cv_quals, cp_declarator *, bool, tree);
+ (qualifier_set, cp_declarator *, bool, tree);
static cp_declarator *make_ptrmem_declarator
- (cp_cv_quals, tree, cp_declarator *, tree);
+ (qualifier_set, tree, cp_declarator *, tree);
/* An erroneous declarator. */
static cp_declarator *cp_error_declarator;
@@ -1952,7 +1961,7 @@ make_id_declarator (tree qualifying_scope, tree unqualified_name,
appertain to the pointer or reference. */
cp_declarator *
-make_pointer_declarator (cp_cv_quals cv_qualifiers, cp_declarator *target,
+make_pointer_declarator (qualifier_set cv_qualifiers, cp_declarator *target,
tree attributes)
{
cp_declarator *declarator;
@@ -1980,7 +1989,7 @@ make_pointer_declarator (cp_cv_quals cv_qualifiers, cp_declarator *target,
reference. */
cp_declarator *
-make_reference_declarator (cp_cv_quals cv_qualifiers, cp_declarator *target,
+make_reference_declarator (qualifier_set cv_qualifiers, cp_declarator *target,
bool rvalue_ref, tree attributes)
{
cp_declarator *declarator;
@@ -2008,7 +2017,7 @@ make_reference_declarator (cp_cv_quals cv_qualifiers, cp_declarator *target,
appertain to the pointer or reference. */
cp_declarator *
-make_ptrmem_declarator (cp_cv_quals cv_qualifiers, tree class_type,
+make_ptrmem_declarator (qualifier_set cv_qualifiers, tree class_type,
cp_declarator *pointee,
tree attributes)
{
@@ -2759,9 +2768,11 @@ static cp_declarator *cp_parser_direct_declarator
(cp_parser *, cp_parser_declarator_kind, cp_parser_flags, int *, bool, bool,
bool);
static enum tree_code cp_parser_ptr_operator
- (cp_parser *, tree *, cp_cv_quals *, tree *);
-static cp_cv_quals cp_parser_cv_qualifier_seq_opt
+ (cp_parser *, tree *, qualifier_set *, tree *);
+static qualifier_set cp_parser_cv_qualifier_seq_opt
(cp_parser *);
+static cv_qualifier cp_parser_cv_qualifier_seq_opt_no_addr_space
+ (cp_parser* parser);
static cp_virt_specifiers cp_parser_virt_specifier_seq_opt
(cp_parser *);
static cp_ref_qualifier cp_parser_ref_qualifier_opt
@@ -3219,7 +3230,7 @@ static bool cp_parser_is_keyword
static tree cp_parser_make_typename_type
(cp_parser *, tree, location_t location);
static cp_declarator * cp_parser_make_indirect_declarator
- (enum tree_code, tree, cp_cv_quals, cp_declarator *, tree);
+ (enum tree_code, tree, qualifier_set, cp_declarator *, tree);
static bool cp_parser_compound_literal_p
(cp_parser *);
static bool cp_parser_array_designator_p
@@ -4681,7 +4692,7 @@ cp_parser_make_typename_type (cp_parser *parser, tree id,
static cp_declarator *
cp_parser_make_indirect_declarator (enum tree_code code, tree class_type,
- cp_cv_quals cv_qualifiers,
+ qualifier_set cv_qualifiers,
cp_declarator *target,
tree attributes)
{
@@ -8927,6 +8938,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
postfix_expression = error_mark_node;
break;
}
+ if (type != error_mark_node
+ && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type))
+ && current_function_decl)
+ {
+ error
+ ("compound literal qualified by address-space "
+ "qualifier");
+ type = error_mark_node;
+ }
/* Form the representation of the compound-literal. */
postfix_expression
= finish_compound_literal (type, initializer,
@@ -11077,7 +11097,7 @@ cp_parser_new_declarator_opt (cp_parser* parser)
{
enum tree_code code;
tree type, std_attributes = NULL_TREE;
- cp_cv_quals cv_quals;
+ qualifier_set cv_quals;
/* We don't know if there's a ptr-operator next, or not. */
cp_parser_parse_tentatively (parser);
@@ -19783,7 +19803,7 @@ cp_parser_conversion_declarator_opt (cp_parser* parser)
{
enum tree_code code;
tree class_type, std_attributes = NULL_TREE;
- cp_cv_quals cv_quals;
+ qualifier_set cv_quals;
/* We don't know if there's a ptr-operator next, or not. */
cp_parser_parse_tentatively (parser);
@@ -22521,6 +22541,14 @@ cp_parser_type_specifier (cp_parser* parser,
break;
}
+ if (RID_FIRST_ADDR_SPACE <= keyword
+ && keyword <= RID_LAST_ADDR_SPACE)
+ {
+ ds = ds_addr_space;
+ if (is_cv_qualifier)
+ *is_cv_qualifier = true;
+ }
+
/* Handle simple keywords. */
if (ds != ds_last)
{
@@ -26574,7 +26602,7 @@ cp_parser_declarator (cp_parser* parser,
{
cp_declarator *declarator;
enum tree_code code;
- cp_cv_quals cv_quals;
+ qualifier_set cv_quals;
tree class_type;
tree gnu_attributes = NULL_TREE, std_attributes = NULL_TREE;
@@ -26815,8 +26843,12 @@ cp_parser_direct_declarator (cp_parser* parser,
*ctor_dtor_or_conv_p = *ctor_dtor_or_conv_p < 0;
first = false;
- /* Parse the cv-qualifier-seq. */
- cv_quals = cp_parser_cv_qualifier_seq_opt (parser);
+ /* Parse the cv-qualifier-seq, though do not permit an
+ address space qualifier in this one. We may want to
+ change that in the future, but we do not permit them for
+ now. */
+ cv_quals
+ = cp_parser_cv_qualifier_seq_opt_no_addr_space (parser);
/* Parse the ref-qualifier. */
ref_qual = cp_parser_ref_qualifier_opt (parser);
/* Parse the tx-qualifier. */
@@ -27353,7 +27385,7 @@ cp_parser_direct_declarator (cp_parser* parser,
static enum tree_code
cp_parser_ptr_operator (cp_parser* parser,
tree* type,
- cp_cv_quals *cv_quals,
+ qualifier_set *cv_quals,
tree *attributes)
{
enum tree_code code = ERROR_MARK;
@@ -27446,31 +27478,17 @@ cp_parser_ptr_operator (cp_parser* parser,
return code;
}
-/* Parse an (optional) cv-qualifier-seq.
-
- cv-qualifier-seq:
- cv-qualifier cv-qualifier-seq [opt]
-
- cv-qualifier:
- const
- volatile
-
- GNU Extension:
-
- cv-qualifier:
- __restrict__
-
- Returns a bitmask representing the cv-qualifiers. */
-
-static cp_cv_quals
-cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
+/* Actually parse an optional cv-qualifier-seq, with or without permitting
+ address spaces. */
+static qualifier_set
+cp_parser_cv_qualifier_seq_opt_1 (cp_parser *parser, bool permit_as)
{
- cp_cv_quals cv_quals = TYPE_UNQUALIFIED;
+ qualifier_set cv_quals {};
while (true)
{
cp_token *token;
- cp_cv_quals cv_qualifier;
+ cv_qualifier cv_qualifier {};
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
@@ -27494,10 +27512,38 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
break;
}
- if (!cv_qualifier)
+ if (RID_FIRST_ADDR_SPACE <= token->keyword
+ && token->keyword <= RID_LAST_ADDR_SPACE)
+ {
+ if (!permit_as)
+ {
+ gcc_rich_location richloc (token->location);
+ richloc.add_fixit_remove ();
+ error_at (&richloc, "address space qualifier not permitted here");
+ cp_lexer_purge_token (parser->lexer);
+ /* Don't apply it. */
+ continue;
+ }
+
+ auto as = static_cast<addr_space_t> (token->keyword - RID_FIRST_ADDR_SPACE);
+ if (!ADDR_SPACE_GENERIC_P (cv_quals.addr_space ()))
+ {
+ gcc_rich_location richloc (token->location);
+ richloc.add_fixit_remove ();
+ error_at (&richloc, "duplicate address space qualifier");
+ cp_lexer_purge_token (parser->lexer);
+ }
+ else
+ cp_lexer_consume_token (parser->lexer);
+
+ cv_quals.set_as (as);
+ continue;
+ }
+
+ if (cv_qualifier == TYPE_UNQUALIFIED)
break;
- if (cv_quals & cv_qualifier)
+ if (cv_quals.has (cv_qualifier))
{
gcc_rich_location richloc (token->location);
richloc.add_fixit_remove ();
@@ -27514,6 +27560,57 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
return cv_quals;
}
+/* Parse an (optional) cv-qualifier-seq.
+
+ cv-qualifier-seq:
+ cv-qualifier cv-qualifier-seq [opt]
+
+ cv-qualifier:
+ const
+ volatile
+
+ GNU Extension:
+
+ cv-qualifier:
+ address-space-qualifier
+ __restrict__
+
+ Returns a qualifier set representing the cv-qualifiers. */
+
+static qualifier_set
+cp_parser_cv_qualifier_seq_opt (cp_parser* parser)
+{ return cp_parser_cv_qualifier_seq_opt_1 (parser, true); }
+
+/* Parse an (optional) cv-qualifier-seq, but without permitting address space
+ qualification. This is useful for the cv-qualifier-seq on a function
+ declarator.
+
+ cv-qualifier-seq:
+ cv-qualifier cv-qualifier-seq [opt]
+
+ cv-qualifier:
+ const
+ volatile
+
+ GNU Extension:
+
+ cv-qualifier:
+ address-space-qualifier
+ __restrict__
+
+ Returns a qualifier set representing the cv-qualifiers. */
+
+static cv_qualifier
+cp_parser_cv_qualifier_seq_opt_no_addr_space (cp_parser* parser)
+{
+ addr_space_t as;
+ cv_qualifier cv;
+ std::tie (cv, as)
+ = cp_parser_cv_qualifier_seq_opt_1 (parser, false).split ();
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (as));
+ return cv;
+}
+
/* Parse an (optional) ref-qualifier
ref-qualifier:
@@ -27666,7 +27763,7 @@ cp_parser_virt_specifier_seq_opt (cp_parser* parser)
is in scope even though it isn't real. */
void
-inject_this_parameter (tree ctype, cp_cv_quals quals)
+inject_this_parameter (tree ctype, qualifier_set quals)
{
tree this_parm;
@@ -37678,6 +37775,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
decl_specs->locations[ds] = location;
if (ds == ds_thread)
decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ else if (ds == ds_addr_space)
+ decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE;
}
else
{
@@ -37710,6 +37809,24 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
error_at (&richloc, "duplicate %qD", token->u.value);
}
}
+ else if (ds == ds_addr_space)
+ {
+ addr_space_t as1 = decl_specs->address_space;
+ addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE;
+
+ gcc_rich_location richloc (location);
+ richloc.add_fixit_remove ();
+ if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2)
+ && as1 != as2)
+ error_at (&richloc,
+ "incompatible address space qualifiers %qs and %qs",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1))
+ error_at (&richloc, "duplicate named address space %qs",
+ c_addr_space_name (as1));
+
+ decl_specs->address_space = as2;
+ }
else
{
/* These correspond to cp-tree.h:cp_decl_spec,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 48c2326b1c76..b46034557a9c 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see
#include "config.h"
#define INCLUDE_ALGORITHM // for std::equal
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
@@ -51,6 +52,12 @@ along with GCC; see the file COPYING3. If not see
#include "pretty-print-markup.h"
#include "contracts.h"
+#include "util/optional.h"
+
+#if CHECKING_P
+# include "selftest.h"
+#endif
+
/* The type of functions taking a tree, and some additional data, and
returning an int. */
typedef int (*tree_fn_t) (tree, void*);
@@ -14618,8 +14625,14 @@ tsubst_pack_index (tree t, tree args, tsubst_flags_t complain, tree in_decl)
else
r = make_pack_index (pack, index);
if (TREE_CODE (t) == PACK_INDEX_TYPE)
- r = cp_build_qualified_type (r, cp_type_quals (t) | cp_type_quals (r),
- complain | tf_ignore_bad_quals);
+ {
+ auto merged_quals = cp_try_quals_join (input_location, r,
+ cp_type_quals (t), complain);
+ if (!merged_quals)
+ return error_mark_node;
+ r = cp_build_qualified_type (r, *merged_quals,
+ complain | tf_ignore_bad_quals);
+ }
return r;
}
@@ -17018,8 +17031,15 @@ tsubst_splice_scope (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
if (type_p)
- r = cp_build_qualified_type (r, cp_type_quals (t) | cp_type_quals (r),
- complain | tf_ignore_bad_quals);
+ {
+ auto merged_quals = cp_try_quals_join (input_location, r,
+ cp_type_quals (t), complain);
+ if (!merged_quals)
+ return error_mark_node;
+
+ r = cp_build_qualified_type (r, *merged_quals,
+ complain | tf_ignore_bad_quals);
+ }
return r;
}
@@ -17228,9 +17248,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (r)
{
r = TREE_TYPE (r);
- r = cp_build_qualified_type
- (r, cp_type_quals (t) | cp_type_quals (r),
- complain | tf_ignore_bad_quals);
+ auto merged_quals = cp_try_quals_join (input_location, r,
+ cp_type_quals (t), complain);
+ if (!merged_quals)
+ return error_mark_node;
+ r = cp_build_qualified_type (r, *merged_quals,
+ complain | tf_ignore_bad_quals);
return r;
}
else
@@ -17420,10 +17443,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
{
gcc_assert (TYPE_P (arg));
- auto quals = cp_type_quals (arg) | cp_type_quals (t);
+ auto merged_quals = cp_try_quals_join (input_location, t,
+ cp_type_quals (arg),
+ complain);
+ if (!merged_quals)
+ return error_mark_node;
- return cp_build_qualified_type
- (arg, quals, complain | tf_ignore_bad_quals);
+ return cp_build_qualified_type (arg, *merged_quals,
+ complain | tf_ignore_bad_quals);
}
else if (code == BOUND_TEMPLATE_TEMPLATE_PARM)
{
@@ -17486,8 +17513,13 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
argvec, in_decl,
DECL_CONTEXT (arg),
complain);
- return cp_build_qualified_type
- (r, cp_type_quals (t) | cp_type_quals (r), complain);
+ auto merged_quals = cp_try_quals_join (input_location, r,
+ cp_type_quals (t),
+ complain);
+ if (!merged_quals)
+ return error_mark_node;
+
+ return cp_build_qualified_type (r, *merged_quals, complain);
}
else if (code == TEMPLATE_TEMPLATE_PARM)
return arg;
@@ -17513,13 +17545,13 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
/* If we get here, we must have been looking at a parm for a
more deeply nested template. Make a new version of this
template parameter, but with a lower level. */
- cv_qualifier quals;
+ qualifier_set quals;
switch (code)
{
case TEMPLATE_TYPE_PARM:
case TEMPLATE_TEMPLATE_PARM:
quals = cp_type_quals (t);
- if (quals)
+ if (quals != qualifier_set {})
{
gcc_checking_assert (code == TEMPLATE_TYPE_PARM);
t = TYPE_MAIN_VARIANT (t);
@@ -17556,7 +17588,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
TYPE_CANONICAL (r) = canonical_type_parameter (r);
}
- if (quals)
+ if (quals != qualifier_set {})
r = cp_build_qualified_type (r, quals,
complain | tf_ignore_bad_quals);
break;
@@ -17893,8 +17925,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
f = TREE_TYPE (decl);
}
}
- return cp_build_qualified_type
- (f, cp_type_quals (f) | cp_type_quals (t), complain);
+ auto merged_quals = cp_try_quals_join (input_location, t,
+ cp_type_quals (f), complain);
+ if (!merged_quals)
+ return error_mark_node;
+ return cp_build_qualified_type (f, *merged_quals, complain);
}
if (!MAYBE_CLASS_TYPE_P (ctx))
@@ -17958,8 +17993,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
}
- return cp_build_qualified_type
- (f, cp_type_quals (f) | cp_type_quals (t), complain);
+ auto merged_quals = cp_try_quals_join (input_location, t,
+ cp_type_quals (f), complain);
+ if (!merged_quals)
+ return error_mark_node;
+
+ return cp_build_qualified_type (f, *merged_quals, complain);
}
case UNBOUND_CLASS_TEMPLATE:
@@ -17998,10 +18037,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
--c_inhibit_evaluation_warnings;
type = finish_typeof (type);
- return cp_build_qualified_type (type,
- cp_type_quals (t)
- | cp_type_quals (type),
- complain);
+ auto merged_quals = cp_try_quals_join (input_location, type,
+ cp_type_quals (t), complain);
+ if (!merged_quals)
+ return error_mark_node;
+ return cp_build_qualified_type (type, *merged_quals, complain);
}
case DECLTYPE_TYPE:
@@ -18035,9 +18075,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
id = false;
type = finish_decltype_type (type, id, complain);
}
- return cp_build_qualified_type (type,
- cp_type_quals (t)
- | cp_type_quals (type),
+ auto merged_quals = cp_try_quals_join (input_location, type,
+ cp_type_quals (t), complain);
+ if (!merged_quals)
+ return error_mark_node;
+
+ return cp_build_qualified_type (type, *merged_quals,
complain | tf_ignore_bad_quals);
}
@@ -18050,8 +18093,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
type1 = tsubst_expr (type1, args, complain, in_decl);
tree type2 = tsubst (TRAIT_TYPE_TYPE2 (t), args, complain, in_decl);
type = finish_trait_type (TRAIT_TYPE_KIND (t), type1, type2, complain);
- return cp_build_qualified_type (type,
- cp_type_quals (t) | cp_type_quals (type),
+ auto merged_quals = cp_try_quals_join (input_location, type,
+ cp_type_quals (t), complain);
+ if (!merged_quals)
+ return error_mark_node;
+ return cp_build_qualified_type (type, *merged_quals,
complain | tf_ignore_bad_quals);
}
@@ -25883,8 +25929,35 @@ template_decl_level (tree decl)
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
- int arg_quals = cp_type_quals (arg);
- int parm_quals = cp_type_quals (parm);
+ auto arg_quals = cp_type_quals (arg);
+ auto parm_quals = cp_type_quals (parm);
+
+ addr_space_t arg_as, parm_as;
+ std::tie (std::ignore, arg_as) = arg_quals.split ();
+ std::tie (std::ignore, parm_as) = parm_quals.split ();
+ if (arg_as == parm_as)
+ /* Always OK. */;
+ else if (!ADDR_SPACE_GENERIC_P (arg_as) && !ADDR_SPACE_GENERIC_P (parm_as))
+ /* While it is possible to unify different non-generic address spaces in a
+ case where there's a qualification conversion, we implement that case in
+ 'unify' in POINTER_TYPE handling. At this point, any difference that a
+ qualification conversion can cross has been removed. */
+ return false;
+ else if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
+ && ADDR_SPACE_GENERIC_P (arg_as))
+ /* ARG lacks an address space, but PARM has it. This means PARM is more
+ qualified, but we don't allow that. */
+ return false;
+ else if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))
+ && ADDR_SPACE_GENERIC_P (parm_as))
+ /* Conversely, ARG is more qualified. */
+ return false;
+
+ /* Below, we use 'can_qualify' to determine the set relation between two
+ qualifier sets, but that does not treat the generic address space in the
+ special way we want to treat it here. So, strip ASes off. */
+ arg_quals.set_as (ADDR_SPACE_GENERIC);
+ parm_quals.set_as (ADDR_SPACE_GENERIC);
if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
@@ -25897,20 +25970,20 @@ check_cv_quals_for_unify (int strict, tree arg, tree parm)
at the outer level [14.8.2.1]/3,1st bullet. */
if ((TYPE_REF_P (arg)
|| FUNC_OR_METHOD_TYPE_P (arg))
- && (parm_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)))
+ && parm_quals.has ((TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)))
return 0;
if ((!INDIRECT_TYPE_P (arg) && TREE_CODE (arg) != TEMPLATE_TYPE_PARM)
- && (parm_quals & TYPE_QUAL_RESTRICT))
+ && parm_quals.has (TYPE_QUAL_RESTRICT))
return 0;
}
if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
- && (arg_quals & parm_quals) != parm_quals)
+ && !arg_quals.can_qualify (parm_quals))
return 0;
if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))
- && (parm_quals & arg_quals) != arg_quals)
+ && !parm_quals.can_qualify (arg_quals))
return 0;
return 1;
@@ -26530,9 +26603,21 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
return unify_cv_qual_mismatch (explain_p, parm, arg);
/* Consider the case where ARG is `const volatile int' and
- PARM is `const T'. Then, T should be `volatile int'. */
- arg = cp_build_qualified_type
- (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
+ PARM is `const T'. Then, T should be `volatile int'.
+
+ Similarly, for address spaces, due to check_cv_quals_for_unify,
+ either only one of PARM or ARG has an address space, or they are
+ equal. Ergo, if PARM has it, then T should lack it. */
+ auto arg_quals_p = cp_type_quals (arg).split ();
+ auto parm_quals_p = cp_type_quals (parm).split ();
+
+ cv_qualifier t_cvs = arg_quals_p.first & ~parm_quals_p.first;
+ addr_space_t t_as = (parm_quals_p.second
+ ? ADDR_SPACE_GENERIC
+ : arg_quals_p.second);
+
+ arg = cp_build_qualified_type (arg, qualifier_set {t_cvs, t_as},
+ tf_none);
if (arg == error_mark_node)
return unify_invalid (explain_p);
@@ -26738,8 +26823,63 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
level of pointers. */
strict |= (strict_in & UNIFY_ALLOW_DERIVED);
- return unify (tparms, targs, TREE_TYPE (parm),
- TREE_TYPE (arg), strict, explain_p);
+ auto arg_pointee = TREE_TYPE (arg);
+ auto parm_pointee = TREE_TYPE (parm);
+ if (strict_in & UNIFY_ALLOW_OUTER_LEVEL)
+ {
+ /* If we're in the outermost layer of a deduction, and address
+ spaces differ, this may still be acceptable as a result of a
+ qualification conversion existing. ([temp.deduct.call]p4.2 "The
+ transformed A can be another pointer or pointer-to-member type
+ that can be converted to the deduced A via a function pointer
+ conversion and/or qualification conversion",
+ [temp.deduct.conv]p5.3 "In general, the deduction process
+ attempts to find template argument values that will make the
+ deduced A identical to A. However, certain attributes of A may
+ be ignored: [...] Any cv-qualifiers in A that can be restored by
+ a qualification conversion.")
+
+ So, check whether the caller wants us to permit more/less
+ qualification, and, if so, remove the difference between
+ address spaces. We leave the other qualifiers untouched, as
+ the rest of the logic already knows how to deal with them. */
+
+ auto parm_pointee_quals = cp_type_quals (TREE_TYPE (parm));
+ auto arg_pointee_quals = cp_type_quals (TREE_TYPE (arg));
+ addr_space_t parm_pointee_as, arg_pointee_as;
+ std::tie (std::ignore, parm_pointee_as)
+ = parm_pointee_quals.split ();
+ std::tie (std::ignore, arg_pointee_as)
+ = arg_pointee_quals.split ();
+
+ bool allow_more = strict_in & (UNIFY_ALLOW_MORE_CV_QUAL
+ | UNIFY_ALLOW_OUTER_MORE_CV_QUAL);
+ bool allow_less = strict_in & (UNIFY_ALLOW_LESS_CV_QUAL
+ | UNIFY_ALLOW_OUTER_LESS_CV_QUAL);
+
+ if (ADDR_SPACE_GENERIC_P (parm_pointee_as)
+ || arg_pointee_as == parm_pointee_as)
+ /* No conversion needed: either there's no address space
+ qualification on PARM, or it is the same as that of ARG. */;
+ else if (allow_more
+ /* If ARG's AS is subset of PARM's AS, then ARG can be
+ converted into PARM by a qualification conversion. */
+ && targetm.addr_space.subset_p (arg_pointee_as, parm_pointee_as))
+ /* Ergo, we'll keep unifying as if they had the same address
+ space. */
+ arg_pointee = (cp_build_qualified_type
+ (arg_pointee,
+ arg_pointee_quals.with_as (parm_pointee_as)));
+ else if (allow_less
+ /* Likewise, but the other way around. */
+ && targetm.addr_space.subset_p (parm_pointee_as, arg_pointee_as))
+ parm_pointee = (cp_build_qualified_type
+ (parm_pointee,
+ parm_pointee_quals.with_as (arg_pointee_as)));
+ }
+
+ return unify (tparms, targs, parm_pointee, arg_pointee, strict,
+ explain_p);
}
case REFERENCE_TYPE:
@@ -27373,8 +27513,8 @@ more_specialized_fn (tree pat1, tree pat2, int len)
tree arg1 = TREE_VALUE (args1);
tree arg2 = TREE_VALUE (args2);
int deduce1, deduce2;
- int quals1 = -1;
- int quals2 = -1;
+ tl::optional<qualifier_set> quals1 = tl::nullopt;
+ tl::optional<qualifier_set> quals2 = tl::nullopt;
int ref1 = 0;
int ref2 = 0;
@@ -27497,11 +27637,11 @@ more_specialized_fn (tree pat1, tree pat2, int len)
else
lose2 = true;
}
- else if (quals1 != quals2 && quals1 >= 0 && quals2 >= 0)
+ else if (quals1 != quals2 && quals1 && quals2)
{
- if ((quals1 & quals2) == quals2)
+ if (quals1->can_qualify (*quals2))
lose2 = true;
- if ((quals1 & quals2) == quals1)
+ if (quals2->can_qualify (*quals1))
lose1 = true;
}
}
@@ -31220,9 +31360,12 @@ resolve_typename_type (tree type, bool only_current_p)
}
/* Qualify the resulting type. */
- auto quals = cp_type_quals (type);
- if (quals)
- result = cp_build_qualified_type (result, cp_type_quals (result) | quals);
+ if (cp_type_quals (type) != qualifier_set {})
+ {
+ auto merged_quals = cp_try_quals_join (input_location, result,
+ cp_type_quals (type));
+ result = cp_build_qualified_type (result, *merged_quals);
+ }
return result;
}
@@ -34097,12 +34240,207 @@ test_type_dependent_expression_p ()
--processing_template_decl;
}
+/* Verify that unification handles address spaces correctly. */
+static void
+test_unify_addr_spaces ()
+{
+ addr_space_test _;
+
+ /* Imitate part of what cp_parser_template_parameter_list does. This is to
+ construct a parameter list a la 'template<typename T>'. If this
+ self-test starts failing somewhere here (i.e. before the asserts), it's
+ probably not your fault; just the "setup" needs to be fixed. */
+ begin_template_parm_list ();
+ tree parameter_list = nullptr;
+ tree parameter = finish_template_type_parm (class_type_node,
+ get_identifier ("T"));
+ parameter = build_tree_list (nullptr, parameter);
+ parameter_list = process_template_parm (parameter_list, UNKNOWN_LOCATION,
+ parameter,
+ /*is_non_type=*/false,
+ /*is_parameter_pack=*/false);
+ parameter_list = end_template_parm_list (parameter_list);
+ /* Pull out that first parameter. */
+ parameter = TREE_VEC_ELT (parameter_list, 0);
+ end_template_decl ();
+
+ /* The actual T of 'template<typename T>'. */
+ auto t = template_parm_to_arg (parameter);
+
+ /* Attempt to unify TMPL with ARG, and check that the result is EXPECTED
+ (i.e. that the template would be [with T = EXPECTED]).
+
+ STRICT will be extended with UNIFY_ALLOW_OUTER_LEVEL and the
+ corresponding UNIFY_ALLOW_OUTER_..._CV_QUAL flags. */
+ auto assert_unifies_as =
+ [&] (::selftest::location loc,
+ tree tmpl, tree arg, int strict, tree expected)
+ {
+ tree targs = make_tree_vec (list_length (parameter_list));
+ auto idx = TEMPLATE_TYPE_IDX (t);
+
+ /* For convenience, add the UNIFY_ALLOW_OUTER_LEVEL flag. */
+ strict |= UNIFY_ALLOW_OUTER_LEVEL;
+
+ /* Perform unification. */
+ if (unify (parameter_list, targs, tmpl, arg, strict, true))
+ fail (loc, "unification failed");
+ else
+ pass (loc, "unification test");
+
+ if (!same_type_p (TREE_VEC_ELT (targs, idx), expected))
+ {
+ error_at (UNKNOWN_LOCATION,
+ "wrong unification result: expected %qT; got %qT",
+ expected, TREE_VEC_ELT (targs, idx));
+ fail (loc, "wrong unification results");
+ }
+ else
+ pass (loc, "unification result assertion");
+ };
+
+ /* Check that TMPL fails to unify with ARG.
+
+ STRICT will be extended with UNIFY_ALLOW_OUTER_LEVEL and the
+ corresponding UNIFY_ALLOW_OUTER_..._CV_QUAL flags. */
+ auto assert_no_unification =
+ [&] (::selftest::location loc, tree tmpl, tree arg, int strict)
+ {
+ tree targs = make_tree_vec (list_length (parameter_list));
+ auto idx = TEMPLATE_TYPE_IDX (t);
+
+ /* For convenience, add the ..._OUTER_... unification flags. */
+ strict |= UNIFY_ALLOW_OUTER_LEVEL;
+ if (strict & UNIFY_ALLOW_MORE_CV_QUAL)
+ strict |= UNIFY_ALLOW_OUTER_MORE_CV_QUAL;
+ if (strict & UNIFY_ALLOW_LESS_CV_QUAL)
+ strict |= UNIFY_ALLOW_OUTER_LESS_CV_QUAL;
+
+ /* Perform unification. */
+ if (!unify (parameter_list, targs, tmpl, arg, strict, false))
+ {
+ error_at (UNKNOWN_LOCATION,
+ "wrong unification result: expected failure; got %qT",
+ TREE_VEC_ELT (targs, idx));
+ fail (loc, "unification succeeded inadvertently");
+ }
+ else
+ pass (loc, "unification failure assertion");
+ };
+
+ /* Prepare 'int' and 'T' with various address spaces and with various levels
+ of indirection. */
+ tree int_as[6];
+ tree intp_as[ARRAY_SIZE (int_as)];
+ tree intpp_as[ARRAY_SIZE (int_as)];
+ tree t_as[ARRAY_SIZE (int_as)];
+ tree tp_as[ARRAY_SIZE (int_as)];
+ tree tpp_as[ARRAY_SIZE (int_as)];
+ for (addr_space_t i = 0; i < ARRAY_SIZE (intp_as); i++)
+ {
+ int_as[i] = build_qualified_type (integer_type_node,
+ qualifier_set {TYPE_UNQUALIFIED, i});
+ intp_as[i] = build_pointer_type (int_as[i]);
+ intpp_as[i] = build_pointer_type (intp_as[i]);
+ t_as[i] = build_qualified_type (t,
+ qualifier_set {TYPE_UNQUALIFIED, i});
+ tp_as[i] = build_pointer_type (t_as[i]);
+ tpp_as[i] = build_pointer_type (tp_as[i]);
+ }
+
+ static_assert (ADDR_SPACE_GENERIC == 0,
+ "this test is currently written with this assumption;"
+ " the constant is inlined a bunch and we're assuming a"
+ " continuous address space number range");
+ assert_unifies_as (SELFTEST_LOCATION, t_as[0], int_as[4], UNIFY_ALLOW_NONE,
+ int_as[4]);
+ assert_unifies_as (SELFTEST_LOCATION, t_as[4], int_as[4], UNIFY_ALLOW_NONE,
+ int_as[0]);
+ /* It's unclear to me what should happen at the outermost layer when
+ unifying '__as3 T' with '__as2 int'. In theory, outermost qualification
+ is unimportant, and indeed I can't think of any cases where outer-level
+ unification may be applied to values qualified with an address space
+ qualifier, but for now we fail such qualification as having incompatible
+ qualifiers. */
+ assert_no_unification (SELFTEST_LOCATION, t_as[3], int_as[2], UNIFY_ALLOW_NONE);
+ assert_unifies_as (SELFTEST_LOCATION, t_as[1], int_as[1], UNIFY_ALLOW_NONE,
+ int_as[0]);
+
+ /* Now, test the actually useful cases: unifying pointers. */
+
+ /* Basic case: T* vs. __asX int*. When T lacks AS-qualification (i.e. it is
+ generic-qualified), it will always take the qualification of the int. */
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[0], intp_as[4],
+ UNIFY_ALLOW_MORE_CV_QUAL, int_as[4]);
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[0], intp_as[1],
+ UNIFY_ALLOW_MORE_CV_QUAL, int_as[1]);
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[0], intp_as[2],
+ UNIFY_ALLOW_MORE_CV_QUAL, int_as[2]);
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[0], intp_as[3],
+ UNIFY_ALLOW_MORE_CV_QUAL, int_as[3]);
+
+ /* Try going between overlapping address spaces, so __as2 T* vs. __as3 T*.
+ For these cases, T=int ought to work as there's a qualification
+ conversion. */
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[2], intp_as[3],
+ UNIFY_ALLOW_MORE_CV_QUAL, int_as[0]);
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[3], intp_as[2],
+ UNIFY_ALLOW_MORE_CV_QUAL, int_as[0]);
+
+ /* template<typename T> void foo (__as5 T *); __as4 int *x = ...; foo (x);
+ This should work as we can [conv.qual] from __as4 to __as5, with
+ T=int. */
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[4], intp_as[5],
+ UNIFY_ALLOW_MORE_CV_QUAL, int_as[0]);
+ /* This case is for something like
+
+ struct S { template<typename T> operator __as5 T * (); }
+ __as4 int *x = S{};
+
+ The above is valid with T=int as __as5 int* is convertible by [conv.qual]
+ to __as4 int*, and [temp.deduct.conv]p5.3 states that "... certain
+ attributes in A may be ignored: ... Any cv-qualifiers in A that can be
+ restored by a qualification conversion".
+
+ Indeed, a testcase with __flat instead of __as4 and __lds instead of __as5
+ in to the above fails on GCN for this reason, but ought to be valid based
+ on the above. Nonetheless, this is a unit test, so we can exercise that
+ case anyway. */
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[1], intp_as[0],
+ UNIFY_ALLOW_LESS_CV_QUAL, int_as[0]);
+
+ /* Those should fail if we aren't allowing differences in qualification,
+ except, of course, if qualification is totally lacking on T. */
+ for (unsigned as_for_t = 1; as_for_t < ARRAY_SIZE (int_as); as_for_t++)
+ for (unsigned as_for_arg = 0; as_for_arg < ARRAY_SIZE (int_as); as_for_arg++)
+ if (as_for_t != as_for_arg)
+ assert_no_unification (SELFTEST_LOCATION, tp_as[as_for_t],
+ intp_as[as_for_arg], UNIFY_ALLOW_NONE);
+ for (unsigned as_for_arg = 0; as_for_arg < ARRAY_SIZE (int_as); as_for_arg++)
+ assert_unifies_as (SELFTEST_LOCATION, tp_as[0], intp_as[as_for_arg],
+ UNIFY_ALLOW_NONE, int_as[as_for_arg]);
+
+ /* Same goes for deeper levels of pointers, but even if we permit
+ differences! */
+ for (unsigned as_for_t = 1; as_for_t < ARRAY_SIZE (int_as); as_for_t++)
+ for (unsigned as_for_arg = 0; as_for_arg < ARRAY_SIZE (int_as); as_for_arg++)
+ if (as_for_t != as_for_arg)
+ assert_no_unification (SELFTEST_LOCATION, tpp_as[as_for_t],
+ intpp_as[as_for_arg],
+ UNIFY_ALLOW_MORE_CV_QUAL
+ | UNIFY_ALLOW_LESS_CV_QUAL);
+ for (unsigned as_for_arg = 0; as_for_arg < ARRAY_SIZE (int_as); as_for_arg++)
+ assert_unifies_as (SELFTEST_LOCATION, tpp_as[0], intpp_as[as_for_arg],
+ UNIFY_ALLOW_NONE, int_as[as_for_arg]);
+}
+
/* Run all of the selftests within this file. */
void
cp_pt_cc_tests ()
{
test_type_dependent_expression_p ();
+ test_unify_addr_spaces ();
}
} // namespace selftest
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 5800cf270bfe..3dcdaf56fdfb 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -3917,7 +3917,8 @@ static tree
remove_const (tree type)
{
return cp_build_qualified_type (type,
- cp_type_quals (type) & ~TYPE_QUAL_CONST);
+ cp_type_quals (type)
+ .without (TYPE_QUAL_CONST));
}
/* Process std::meta::annotations_of and annotations_of_with_type.
@@ -5368,7 +5369,7 @@ eval_remove_volatile (location_t loc, tree type)
{
type = strip_typedefs (type);
auto quals = cp_type_quals (type);
- quals &= ~TYPE_QUAL_VOLATILE;
+ quals = quals.without (TYPE_QUAL_VOLATILE);
type = cp_build_qualified_type (type, quals);
return get_reflection_raw (loc, type);
}
@@ -5525,7 +5526,7 @@ eval_make_signed (location_t loc, const constexpr_ctx *ctx, tree type,
if (ret != type)
{
auto quals = cp_type_quals (type);
- quals &= (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
+ quals.remove (~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE));
ret = cp_build_qualified_type (ret, quals);
}
else
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 48e4dfde458c..e201ade7273a 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -885,7 +885,9 @@ static int
qualifier_flags (tree type)
{
int flags = 0;
- int quals = cp_type_quals (type);
+ cv_qualifier quals;
+
+ std::tie (quals, std::ignore) = cp_type_quals (type).split ();
if (quals & TYPE_QUAL_CONST)
flags |= 1;
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index a51fd1cd6a75..2a0c73aac44a 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -2043,9 +2043,6 @@ check_final_overrider (tree overrider, tree basefn)
|| (TREE_CODE (base_return) == TREE_CODE (over_return)
&& INDIRECT_TYPE_P (base_return)))
{
- /* Potentially covariant. */
- unsigned base_quals, over_quals;
-
fail = !INDIRECT_TYPE_P (base_return);
if (!fail)
{
@@ -2060,10 +2057,22 @@ check_final_overrider (tree overrider, tree basefn)
base_return = TREE_TYPE (base_return);
over_return = TREE_TYPE (over_return);
}
- base_quals = cp_type_quals (base_return);
- over_quals = cp_type_quals (over_return);
- if ((base_quals & over_quals) != over_quals)
+ auto base_quals = cp_type_quals (base_return);
+ auto over_quals = cp_type_quals (over_return);
+
+ /* The standard permits CV-qualification to change such that "both
+ pointers or references have the same cv-qualification and the class
+ type in the return type of D::f has the same cv-qualification as or
+ less cv-qualification than the class type in the return type of B::f",
+ where D::f overrides B::f. I suspect this was written as such
+ because the conversion from fewer to more qualifiers is a no-op
+ conversion *usually*, and so, such a mismatch shouldn't present an
+ issue in generated code. However, this is not the case with address
+ space qualification; more address space qualification still requires
+ an address space conversion. Ergo, check for cv-qualification only,
+ do not permit addr-space mismatches. */
+ if (!base_quals.can_qualify (over_quals, /*nop_only=*/true))
fail = 1;
if (CLASS_TYPE_P (base_return) && CLASS_TYPE_P (over_return))
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index ff24b5556369..5735e557d365 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -2811,9 +2811,16 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
auto quals = cp_type_quals (TREE_TYPE (object));
if (DECL_MUTABLE_P (decl))
- quals &= ~TYPE_QUAL_CONST;
+ quals = quals.without (TYPE_QUAL_CONST);
- quals |= cp_type_quals (TREE_TYPE (decl));
+ auto memb_quals = cp_type_quals (TREE_TYPE (decl));
+ addr_space_t memb_as;
+ cv_qualifier memb_cv;
+ std::tie (memb_cv, memb_as) = memb_quals.split ();
+ /* It should not be possible to declare an address space on a
+ member. */
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (memb_as));
+ quals |= memb_cv;
type = cp_build_qualified_type (type, quals);
}
@@ -4486,7 +4493,7 @@ finish_base_specifier (tree base, tree access, bool virtual_p,
}
else
{
- if (cp_type_quals (base) != 0)
+ if (cp_type_quals (base) != qualifier_set {})
{
/* DR 484: Can a base-specifier name a cv-qualified
class type? */
@@ -13402,8 +13409,8 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
{
/* decltype of an NTTP object is the type of the template
parameter, which is the object type modulo cv-quals. */
- int quals = cp_type_quals (type);
- gcc_checking_assert (quals & TYPE_QUAL_CONST);
+ auto quals = cp_type_quals (type);
+ gcc_checking_assert (quals.has (TYPE_QUAL_CONST));
type = cv_unqualified (type);
}
break;
@@ -13472,7 +13479,7 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
if (type && !TYPE_REF_P (type))
{
- cv_qualifier quals;
+ qualifier_set quals;
if (current_function_decl
&& LAMBDA_FUNCTION_P (current_function_decl)
&& DECL_XOBJ_MEMBER_FUNCTION_P (current_function_decl))
@@ -13486,14 +13493,21 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
return TREE_TYPE (t);
return t;
};
- quals = (cp_type_quals (type)
- | cp_type_quals (direct_type (obtype)));
+ auto obtype_quals = cp_type_quals (direct_type (obtype));
+ addr_space_t obtype_as;
+ cv_qualifier obtype_cv;
+ std::tie (obtype_cv, obtype_as) = obtype_quals.split ();
+ /* Currently, there shouldn't be any way to get a lambda such
+ that it is qualified with an address space. */
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (obtype_as));
+ quals = (cp_type_quals (type) | obtype_cv);
}
else
/* We are in the parameter clause, trailing return type, or
the requires clause and have no relevant c_f_decl yet. */
quals = (LAMBDA_EXPR_CONST_QUAL_P (lam)
- ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
+ ? TYPE_QUAL_CONST
+ : TYPE_UNQUALIFIED);
type = cp_build_qualified_type (type, quals);
type = build_reference_type (type);
}
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 86ba4fd4fd1a..e6ba6636fbf6 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "tree.h"
@@ -37,6 +38,10 @@ along with GCC; see the file COPYING3. If not see
#include "flags.h"
#include "selftest.h"
+/* For {,cp_}try_quals_merge. */
+#include "util/expected.h"
+#include "util/optional.h"
+
static tree bot_manip (tree *, int *, void *);
static tree bot_replace (tree *, int *, void *);
static hashval_t list_hash_pieces (tree, tree, tree);
@@ -1174,6 +1179,12 @@ build_min_array_type (tree elt_type, tree index_type)
tree t = cxx_make_type (ARRAY_TYPE);
TREE_TYPE (t) = elt_type;
TYPE_DOMAIN (t) = index_type;
+ /* XXX(arsen): This is technically wrong for C++: array types should not be
+ qualified. However, the rest of GCC expects to find the address space
+ qualifier of an array type on the array type itself, and does not bother
+ stripping array layers when looking for address space qualification. It
+ may be desirable to change that instead. */
+ TYPE_ADDR_SPACE (t) = TYPE_ADDR_SPACE (elt_type);
return t;
}
@@ -1473,12 +1484,7 @@ c_build_qualified_type (tree type, qualifier_set type_quals,
tree /* orig_qual_type */,
size_t /* orig_qual_indirect */)
{
- cv_qualifier cv_quals;
- addr_space_t as;
- std::tie (cv_quals, as) = type_quals.split ();
- /* No address space support yet. */
- gcc_assert (ADDR_SPACE_GENERIC_P (as));
- return cp_build_qualified_type (type, cv_quals);
+ return cp_build_qualified_type (type, type_quals);
}
\f
@@ -1503,7 +1509,7 @@ c_build_qualified_type (tree type, qualifier_set type_quals,
in a similar manner for restricting non-pointer types. */
tree
-cp_build_qualified_type (tree type, cv_qualifier type_quals,
+cp_build_qualified_type (tree type, qualifier_set type_quals,
tsubst_flags_t complain /* = tf_warning_or_error */)
{
tree result;
@@ -1585,29 +1591,33 @@ cp_build_qualified_type (tree type, cv_qualifier type_quals,
([dcl.typedef], [temp.param]) or decltype-specifier
([dcl.type.decltype]),in which case the cv-qualifiers are
ignored. */
- if (type_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)
+ if (type_quals.has ((TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE))
&& (TYPE_REF_P (type)
|| FUNC_OR_METHOD_TYPE_P (type)))
{
if (TYPE_REF_P (type)
&& (!typedef_variant_p (type) || FUNC_OR_METHOD_TYPE_P (type)))
- bad_quals |= type_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
- type_quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
+ bad_quals |= type_quals.intersect (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
+ type_quals = type_quals.without (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
}
/* But preserve any function-cv-quals on a FUNCTION_TYPE. */
if (TREE_CODE (type) == FUNCTION_TYPE)
- type_quals |= type_memfn_quals (type);
+ {
+ /* Those cannot have address spaces. */
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (type_quals.addr_space ()));
+ type_quals |= type_memfn_quals (type);
+ }
/* A restrict-qualified type must be a pointer (or reference)
to object or incomplete type. */
- if ((type_quals & TYPE_QUAL_RESTRICT)
+ if (type_quals.has (TYPE_QUAL_RESTRICT)
&& TREE_CODE (type) != TEMPLATE_TYPE_PARM
&& TREE_CODE (type) != TYPENAME_TYPE
&& !INDIRECT_TYPE_P (type))
{
bad_quals |= TYPE_QUAL_RESTRICT;
- type_quals &= ~TYPE_QUAL_RESTRICT;
+ type_quals = type_quals.without (TYPE_QUAL_RESTRICT);
}
if (bad_quals == TYPE_UNQUALIFIED
@@ -1628,6 +1638,117 @@ cp_build_qualified_type (tree type, cv_qualifier type_quals,
return result;
}
+/* Attempt to merge qualifier sets of types T1 and T2, reporting an error on
+ failure, if allowed. This operation is used when it is necessary to find
+ fitting common qualifiers for T1 and T2.
+
+ If allowed to complain, does error recovery (i.e. never returns an empty
+ optional). */
+
+tl::optional<qualifier_set>
+cp_try_quals_merge (location_t loc, tree t1, tree t2,
+ tsubst_flags_t complain)
+{
+ if (t1 == error_mark_node || t2 == error_mark_node)
+ return tl::nullopt;
+ gcc_assert (TYPE_P (t1) && TYPE_P (t2));
+ auto q1 = cp_type_quals (t1), q2 = cp_type_quals (t2);
+ auto merged = q1.merge (q2);
+
+ if (!merged)
+ {
+ if (!(complain & tf_error))
+ /* We aren't reporting errors, don't bother with error recovery. */
+ return tl::nullopt;
+
+ auto_diagnostic_group _;
+ error_at (loc, "conflicting qualifiers for %qT and %qT",
+ t1, t2);
+
+ switch (merged.error ())
+ {
+ case qualifier_set::merge_error::disjoint_address_spaces:
+ {
+ auto as1 = q1.addr_space ();
+ auto as2 = q2.addr_space ();
+ gcc_assert (as1 != as2);
+
+ if (ADDR_SPACE_GENERIC_P (as2))
+ /* Ensure generic address space is AS1, if any address space
+ is generic. */
+ std::swap (as1, as2);
+
+ if (ADDR_SPACE_GENERIC_P (as1))
+ inform (loc,
+ "address space %qs does not overlap with generic "
+ "address space",
+ c_addr_space_name (as2));
+ else
+ inform (loc, "address spaces %qs and %qs are disjoint",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ }
+ break;
+ case qualifier_set::merge_error::atomic_mismatch:
+ inform (loc, "atomic qualification differs");
+ break;
+ }
+
+ /* As error recovery, continue with AS and atomic qualification of
+ Q1. */
+ return q1 | q2.without (TYPE_QUAL_ATOMIC).cv_quals ();
+ }
+
+ return *merged;
+}
+
+/* Attempt to add (by joining) the qualifier set QS to the qualifier set of
+ type T, reporting an error on failure, if allowed. This function is useful
+ if adding new qualifiers to an existing type, as in the following example:
+
+ using T = ...;
+ using T2 = [QS] T;
+
+ Returns the joined set, or an empty optional if not possible.
+
+ If allowed to complain, does error recovery (i.e. never returns an empty
+ optional). */
+
+tl::optional<qualifier_set>
+cp_try_quals_join (location_t loc, tree t, qualifier_set qs,
+ tsubst_flags_t complain)
+{
+ if (t == error_mark_node)
+ return tl::nullopt;
+ gcc_assert (TYPE_P (t));
+ auto qt = cp_type_quals (t);
+ auto joined = qt.join (qs);
+
+ if (!joined)
+ {
+ if (!(complain & tf_error))
+ /* We aren't reporting errors, don't bother with error recovery. */
+ return tl::nullopt;
+
+ auto_diagnostic_group _;
+
+ switch (joined.error ())
+ {
+ case qualifier_set::join_error::double_addr_space:
+ error_at (loc, "conflicting named address spaces (%s vs %s)",
+ c_addr_space_name (qt.addr_space ()),
+ c_addr_space_name (qs.addr_space ()));
+ break;
+ }
+
+
+ /* As error recovery, continue with new AS. */
+ return qs | qt.cv_quals ();
+ }
+
+ return *joined;
+}
+
+
/* Return a FUNCTION_TYPE for a function returning VALUE_TYPE
with ARG_TYPES arguments. Wrapper around build_function_type
which ensures TYPE_NO_NAMED_ARGS_STDARG_P is set if ARG_TYPES
@@ -1650,7 +1771,7 @@ cv_unqualified (tree type)
return type;
auto quals = cp_type_quals (type);
- quals &= ~(TYPE_QUAL_CONST|TYPE_QUAL_VOLATILE);
+ quals.remove (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
return cp_build_qualified_type (type, quals);
}
@@ -2453,7 +2574,7 @@ build_qualified_name (tree type, tree scope, tree name, bool template_p)
static bool
cp_check_qualified_type (const_tree cand, const_tree base,
- cv_qualifier type_quals, cp_ref_qualifier rqual,
+ qualifier_set type_quals, cp_ref_qualifier rqual,
tree raises, bool late)
{
return (TYPE_QUALS (cand) == type_quals
@@ -2942,14 +3063,7 @@ tree
build_cp_fntype_variant (tree type, cp_ref_qualifier rqual,
tree raises, bool late)
{
- cv_qualifier type_quals;
- addr_space_t as;
- std::tie (type_quals, as) = TYPE_QUALS (type).split ();
- /* AS here is the address space of the method or function. For the latter,
- it won't ever make sense. For the former, it may, one day, if we support
- address space qualification on non-static member functions. But not
- today. */
- gcc_assert (ADDR_SPACE_GENERIC_P (as));
+ auto type_quals = TYPE_QUALS (type);
if (cp_check_qualified_type (type, type, type_quals, rqual, raises, late))
return type;
@@ -4671,7 +4785,7 @@ maybe_dummy_object (tree type, tree* binfop)
non-lambda) 'this' if available. */
if (ctype)
{
- auto quals = TYPE_UNQUALIFIED;
+ qualifier_set quals {};
if (tree lambda = CLASSTYPE_LAMBDA_EXPR (ctype))
{
if (tree cap = lambda_expr_this_capture (lambda, false))
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 61a8111b2ad7..170b90828868 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
checks, and some optimization. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "target.h"
@@ -40,6 +41,10 @@ along with GCC; see the file COPYING3. If not see
#include "attribs.h"
#include "asan.h"
#include "gimplify.h"
+#include "selftest.h"
+
+#include "util/optional.h"
+#include "util/expected.h"
static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
static tree cp_build_function_call (tree, tree, tsubst_flags_t);
@@ -719,17 +724,49 @@ composite_pointer_type_r (const op_location_t &location,
return error_mark_node;
result_type = void_type_node;
}
- const auto q1 = cp_type_quals (pointee1);
- const auto q2 = cp_type_quals (pointee2);
- const auto quals = q1 | q2;
+
+ qualifier_set q1 = cp_type_quals (pointee1);
+ qualifier_set q2 = cp_type_quals (pointee2);
+
+ /* Merge qualifiers of pointee types such that the result has all the
+ CV-qualifiers of both, and the common address space of both. The address
+ spaces differing at this point is an error even if one address space is
+ subset of another, as we're "too deep" if we reach this case, so we can't
+ use cp_try_quals_merge (the latter does not expose the STRICT_ADDR_SPACE
+ parameter, and diagnoses as if the types given to it were the types whose
+ qualifiers are being merged). */
+ auto mquals = q1.merge (q2, /*strict_addr_space=*/true);
+ if (!mquals)
+ {
+ if (!(complain & tf_error))
+ return error_mark_node;
+
+ auto_diagnostic_group _;
+ composite_pointer_error (location, diagnostics::kind::error,
+ t1, t2, operation);
+ switch (mquals.error ())
+ {
+ case qualifier_set::merge_error::disjoint_address_spaces:
+ inform (location, "address spaces %qs and %qs differ",
+ c_addr_space_name (q2.addr_space ()),
+ c_addr_space_name (q2.addr_space ()));
+ break;
+ case qualifier_set::merge_error::atomic_mismatch:
+ inform (location, "atomic qualification differs");
+ break;
+ }
+
+ mquals = q1.with (q2.cv_quals ());
+ }
+
result_type = cp_build_qualified_type (result_type,
- (quals | (*add_const
- ? TYPE_QUAL_CONST
- : TYPE_UNQUALIFIED)));
+ (*mquals | (*add_const
+ ? TYPE_QUAL_CONST
+ : TYPE_UNQUALIFIED)));
/* The cv-combined type can add "const" as per [conv.qual]/3.3 (except for
the TLQ). The reason is that both T1 and T2 can then be converted to the
cv-combined type of T1 and T2. */
- if (quals != q1 || quals != q2)
+ if (*mquals != q1 || *mquals != q2)
*add_const = true;
/* If the original types were pointers to members, so is the
result. */
@@ -781,6 +818,43 @@ composite_pointer_type (const op_location_t &location,
if (null_ptr_cst_p (arg2))
return t1;
+ /* First of, as an extension, merge address-spaces of the top pointee
+ types. */
+ if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2))
+ {
+ addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (t1));
+ addr_space_t as2 = TYPE_ADDR_SPACE (TREE_TYPE (t2));
+
+ if (as1 != as2)
+ {
+ addr_space_t as_common;
+
+ /* If the two named address spaces are different, determine the common
+ superset address space. If there isn't one, raise an error. */
+ if (!addr_space_superset (as1, as2, &as_common))
+ {
+ as_common = as1;
+ if (complain & tf_error)
+ error_at (location,
+ "%qT and %qT are in disjoint named address spaces",
+ t1, t2);
+ else
+ return error_mark_node;
+ }
+
+ auto commonize = [&] (tree &t)
+ {
+ auto t_pointee = TREE_TYPE (t);
+ t_pointee = (build_qualified_type
+ (t_pointee,
+ cp_type_quals (t).with_as (as_common)));
+ t = build_indirection_like (t, t_pointee);
+ };
+ commonize (t1);
+ commonize (t2);
+ }
+ }
+
/* We have:
[expr.type]
@@ -828,11 +902,15 @@ composite_pointer_type (const op_location_t &location,
}
else
return error_mark_node;
- }
+ }
+
+ auto maybe_quals = cp_try_quals_merge (location, TREE_TYPE (t1),
+ TREE_TYPE (t2), complain);
+ if (!maybe_quals)
+ return error_mark_node;
+
result_type
- = cp_build_qualified_type (void_type_node,
- (cp_type_quals (TREE_TYPE (t1))
- | cp_type_quals (TREE_TYPE (t2))));
+ = cp_build_qualified_type (void_type_node, *maybe_quals);
result_type = build_pointer_type (result_type);
/* Merge the attributes. */
attributes = (*targetm.merge_type_attributes) (t1, t2);
@@ -1183,14 +1261,15 @@ comp_except_types (tree a, tree b, bool exact)
return true;
else if (!exact)
{
- if (cp_type_quals (a) || cp_type_quals (b))
+ if (cp_type_quals (a).nonempty_p () || cp_type_quals (b).nonempty_p ())
return false;
if (TYPE_PTR_P (a) && TYPE_PTR_P (b))
{
a = TREE_TYPE (a);
b = TREE_TYPE (b);
- if (cp_type_quals (a) || cp_type_quals (b))
+ if (cp_type_quals (a).nonempty_p ()
+ || cp_type_quals (b).nonempty_p ())
return false;
}
@@ -2078,30 +2157,42 @@ layout_compatible_type_p (tree type1, tree type2, bool explain/*=false*/)
return true;
}
-/* Returns 1 if TYPE1 is at least as qualified as TYPE2. */
+/* Returns 1 if TYPE1 is at least as qualified as TYPE2, and if its address
+ space is a superset of that of TYPE2.
+
+ Semantically, this is true if an object with qualifiers of TYPE2 can be
+ used as an object with qualifiers of TYPE1. */
bool
at_least_as_qualified_p (const_tree type1, const_tree type2)
{
- int q1 = cp_type_quals (type1);
- int q2 = cp_type_quals (type2);
+ auto q1 = cp_type_quals (type1);
+ auto q2 = cp_type_quals (type2);
/* All qualifiers for TYPE2 must also appear in TYPE1. */
- return (q1 & q2) == q2;
+ return q1.can_qualify (q2);
}
-/* Returns 1 if TYPE1 is more cv-qualified than TYPE2, -1 if TYPE2 is
- more cv-qualified that TYPE1, and 0 otherwise. */
+/* Returns 1 if TYPE1 is more cv-qualified than TYPE2 (i.e. if a TYPE2 object
+ can be used safely if qualified by qualifiers of TYPE1), -1 if TYPE2 is more
+ cv-qualified that TYPE1, and 0 otherwise. */
int
-comp_cv_qualification (int q1, int q2)
+comp_cv_qualification (qualifier_set q1, qualifier_set q2)
{
- if (q1 == q2)
+ addr_space_t as1, as2;
+ cv_qualifier cv1, cv2;
+ std::tie (cv1, as1) = q1.split ();
+ std::tie (cv2, as2) = q2.split ();
+ auto as_eq = (targetm.addr_space.subset_p (as2, as1)
+ && targetm.addr_space.subset_p (as1, as2));
+
+ if (cv1 == cv2 && as_eq)
return 0;
- if ((q1 & q2) == q2)
+ if (q1.can_qualify (q2))
return 1;
- else if ((q1 & q2) == q1)
+ else if (q2.can_qualify (q1))
return -1;
return 0;
@@ -2110,8 +2201,8 @@ comp_cv_qualification (int q1, int q2)
int
comp_cv_qualification (const_tree type1, const_tree type2)
{
- int q1 = cp_type_quals (type1);
- int q2 = cp_type_quals (type2);
+ auto q1 = cp_type_quals (type1);
+ auto q2 = cp_type_quals (type2);
return comp_cv_qualification (q1, q2);
}
@@ -3046,7 +3137,6 @@ build_class_member_access_expr (cp_expr object, tree member,
{
/* A non-static data member. */
bool null_object_p;
- cv_qualifier type_quals;
tree member_type;
if (INDIRECT_REF_P (object))
@@ -3123,19 +3213,32 @@ build_class_member_access_expr (cp_expr object, tree member,
}
/* Compute the type of the field, as described in [expr.ref]. */
- type_quals = TYPE_UNQUALIFIED;
+ auto type_quals = TYPE_UNQUALIFIED;
member_type = TREE_TYPE (member);
if (!TYPE_REF_P (member_type))
{
- type_quals = (cp_type_quals (member_type)
- | cp_type_quals (object_type));
+ cv_qualifier member_cv;
+ addr_space_t member_as;
+ std::tie (member_cv, member_as) = (cp_type_quals (member_type)
+ .split ());
+ /* Member types must lack address spaces. */
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (member_as));
+ auto object_quals = cp_type_quals (object_type);
+ type_quals = (member_cv | object_quals.cv_quals ());
/* A field is const (volatile) if the enclosing object, or the
field itself, is const (volatile). But, a mutable field is
not const, even within a const object. */
if (DECL_MUTABLE_P (member))
type_quals &= ~TYPE_QUAL_CONST;
- member_type = cp_build_qualified_type (member_type, type_quals);
+
+ /* The address space of a member is the address space of the object
+ itself. */
+ auto object_as = object_quals.addr_space ();
+ member_type = cp_build_qualified_type (member_type,
+ qualifier_set
+ {type_quals,
+ object_as});
}
result = build3_loc (input_location, COMPONENT_REF, member_type,
@@ -7210,6 +7313,29 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
tree restype = ptrdiff_type_node;
tree target_type = TREE_TYPE (ptrtype);
+ /* If the operands point into different address spaces, we need to
+ explicitly convert them to pointers into the common address space
+ before we can subtract the numerical address values. */
+ auto as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0)));
+ auto as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1)));
+ if (as0 != as1)
+ {
+ addr_space_t as_common;
+ tree common_type;
+
+ if (!addr_space_superset (as0, as1, &as_common))
+ {
+ error_at (loc, "pointers to disjoint address spaces "
+ "%qT and %qT in subtraction",
+ TREE_TYPE (op0), TREE_TYPE (op1));
+ return error_mark_node;
+ }
+
+ common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1));
+ op0 = convert (common_type, op0);
+ op1 = convert (common_type, op1);
+ }
+
if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain))
return error_mark_node;
@@ -8706,11 +8832,68 @@ maybe_warn_about_cast_ignoring_quals (location_t loc, tree type,
if (warn_ignored_qualifiers
&& complain & tf_warning
&& !CLASS_TYPE_P (type)
- && (cp_type_quals (type) & (TYPE_QUAL_CONST|TYPE_QUAL_VOLATILE)))
+ && cp_type_quals (type).has (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE))
warning_at (loc, OPT_Wignored_qualifiers,
"type qualifiers ignored on cast result type");
}
+/* Warns if the cast of EXPR into TYPE converts an address space into an
+ unrelated address space. If so, the cast is necessarily UB. */
+static void
+maybe_warn_about_addr_space_cast (location_t loc, tree type,
+ tree expr, tsubst_flags_t complain)
+{
+ if (!(INDIRECT_TYPE_P (type)
+ && INDIRECT_TYPE_P (TREE_TYPE (expr))
+ && (complain & tf_warning)))
+ return;
+
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type));
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (expr)));
+ addr_space_t as_common;
+
+ if (addr_space_superset (as_to, as_from, &as_common))
+ /* At least TO or FROM is a superset of the other. */
+ return;
+
+ if (POINTER_TYPE_P (type))
+ {
+ if (ADDR_SPACE_GENERIC_P (as_from))
+ warning_at (loc, 0, "cast to %qs address space pointer "
+ "from disjoint generic address space pointer",
+ c_addr_space_name (as_to));
+
+ else if (ADDR_SPACE_GENERIC_P (as_to))
+ warning_at (loc, 0, "cast to generic address space pointer "
+ "from disjoint %qs address space pointer",
+ c_addr_space_name (as_from));
+
+ else
+ warning_at (loc, 0, "cast to %qs address space pointer "
+ "from disjoint %qs address space pointer",
+ c_addr_space_name (as_to),
+ c_addr_space_name (as_from));
+ }
+ else
+ {
+ if (ADDR_SPACE_GENERIC_P (as_from))
+ warning_at (loc, 0, "cast to %qs address space reference "
+ "from disjoint generic address space reference",
+ c_addr_space_name (as_to));
+
+ else if (ADDR_SPACE_GENERIC_P (as_to))
+ warning_at (loc, 0, "cast to generic address space reference "
+ "from disjoint %qs address space reference",
+ c_addr_space_name (as_from));
+
+ else
+ warning_at (loc, 0, "cast to %qs address space reference "
+ "from disjoint %qs address space reference",
+ c_addr_space_name (as_to),
+ c_addr_space_name (as_from));
+ }
+}
+
/* Convert EXPR (an expression with pointer-to-member type) to TYPE
(another pointer-to-member type in the same hierarchy) and return
the converted expression. If ALLOW_INVERSE_P is permitted, a
@@ -9147,6 +9330,7 @@ build_static_cast (location_t loc, tree type, tree oexpr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_space_cast (loc, type, expr, complain);
}
if (processing_template_decl)
goto tmpl;
@@ -9470,6 +9654,7 @@ build_reinterpret_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_space_cast (loc, type, expr, complain);
}
protected_set_expr_location (r, loc);
return r;
@@ -9656,6 +9841,7 @@ build_const_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, expr, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_space_cast (loc, type, expr, complain);
}
protected_set_expr_location (r, loc);
return r;
@@ -9765,6 +9951,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
{
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_space_cast (loc, type, expr, complain);
}
else if (complain & tf_error)
build_const_cast_1 (loc, type, value, tf_error, &valid_p);
@@ -9789,6 +9976,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
+ maybe_warn_about_addr_space_cast (loc, type, expr, complain);
/* Non-class rvalues always have cv-unqualified type. */
if (!CLASS_TYPE_P (type))
@@ -11940,8 +12128,9 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
{
bool to_more_cv_qualified = false;
bool is_opaque_pointer = false;
+ bool is_toplevel = true;
- for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
+ for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from), is_toplevel = false)
{
if (TREE_CODE (to) != TREE_CODE (from))
return false;
@@ -11951,18 +12140,39 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
TYPE_OFFSET_BASETYPE (to)))
return false;
+ /* We cannot permit an address space change in anything but the toplevel.
+ The case when we *can* permit an address space change but may not be
+ able to do it because of address space sub/superset relations is
+ covered below, when checking at_least_as_qualified_p. */
+ auto as_to = TYPE_ADDR_SPACE (to);
+ auto as_from = TYPE_ADDR_SPACE (from);
+ if (as_to != as_from && !is_toplevel)
+ return false;
+ if (FUNC_OR_METHOD_TYPE_P (to))
+ /* Functions shouldn't be able to have address spaces currently. We
+ check this here because, if they do for some reason, the checks
+ below won't be reached and ergo we could provide the wrong
+ comparison result. */
+ gcc_assert (ADDR_SPACE_GENERIC_P (as_to)
+ && ADDR_SPACE_GENERIC_P (as_from));
+
/* Const and volatile mean something different for function and
array types, so the usual checks are not appropriate. We'll
check the array type elements in further iterations. */
if (!FUNC_OR_METHOD_TYPE_P (to) && TREE_CODE (to) != ARRAY_TYPE)
{
if (!at_least_as_qualified_p (to, from))
+ /* TO is missing qualifiers of FROM. */
return false;
if (!at_least_as_qualified_p (from, to))
{
+ /* TO has extra qualifiers over FROM. Per [conv.qual]p3.3, this
+ is permitted only if the outer levels all had const ("const
+ is added to every ..."). */
if (constp == 0)
return false;
+
to_more_cv_qualified = true;
}
@@ -12086,18 +12296,30 @@ ptr_reasonably_similar (const_tree to, const_tree from)
/* Return true if TO and FROM (both of which are POINTER_TYPEs or
pointer-to-member types) are the same, ignoring cv-qualification at
- all levels. CB says how we should behave when comparing array bounds. */
+ all levels. CB says how we should behave when comparing array bounds.
+
+ This function permits the address-space of a pointer to vary (i.e. if given
+ T AS1* and T AS2*, it considers the difference unimportant), unless
+ STRICT_ADDR_SPACE. */
bool
-comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb)
+comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb,
+ bool strict_addr_space)
{
bool is_opaque_pointer = false;
+ int depth = 0;
- for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
+ for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from), depth++)
{
if (TREE_CODE (to) != TREE_CODE (from))
return false;
+ /* We want to permit a toplevel address space change. */
+ auto as_to = TYPE_ADDR_SPACE (to);
+ auto as_from = TYPE_ADDR_SPACE (from);
+ if ((strict_addr_space || depth > 1) && as_to != as_from)
+ return false;
+
if (TREE_CODE (from) == OFFSET_TYPE
&& same_type_p (TYPE_OFFSET_BASETYPE (from),
TYPE_OFFSET_BASETYPE (to)))
@@ -12122,27 +12344,22 @@ comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb)
/* Returns the type qualifiers for this type, including the qualifiers on the
elements for an array type. */
-cv_qualifier
+qualifier_set
cp_type_quals (const_tree type)
{
- cv_qualifier quals;
- addr_space_t as;
+ qualifier_set quals;
/* This CONST_CAST is okay because strip_array_types returns its
argument unmodified and we assign it to a const_tree. */
type = strip_array_types (const_cast<tree> (type));
if (type == error_mark_node
/* Quals on a FUNCTION_TYPE are memfn quals. */
|| TREE_CODE (type) == FUNCTION_TYPE)
- return TYPE_UNQUALIFIED;
-
- std::tie (quals, as) = TYPE_QUALS (type).split ();
- /* No address space support yet. */
- gcc_assert (ADDR_SPACE_GENERIC_P (as));
+ return qualifier_set {};
+ quals = TYPE_QUALS (type);
/* METHOD and REFERENCE_TYPEs should never have quals. */
gcc_assert ((TREE_CODE (type) != METHOD_TYPE
&& !TYPE_REF_P (type))
- || ((quals & (TYPE_QUAL_CONST|TYPE_QUAL_VOLATILE))
- == TYPE_UNQUALIFIED));
+ || !quals.has (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE));
return quals;
}
@@ -12164,22 +12381,24 @@ type_memfn_rqual (const_tree type)
/* Returns the function-cv-quals for TYPE, which must be a FUNCTION_TYPE or
METHOD_TYPE. */
-cv_qualifier
+cp_cv_quals
type_memfn_quals (const_tree type)
{
+ qualifier_set quals;
if (TREE_CODE (type) == FUNCTION_TYPE)
- {
- cv_qualifier quals;
- addr_space_t as;
- std::tie (quals, as) = TYPE_QUALS (type).split ();
- /* No address space support yet. */
- gcc_assert (ADDR_SPACE_GENERIC_P (as));
- return quals;
- }
+ quals = TYPE_QUALS (type);
else if (TREE_CODE (type) == METHOD_TYPE)
- return cp_type_quals (class_of_this_parm (type));
+ quals = cp_type_quals (class_of_this_parm (type));
else
gcc_unreachable ();
+
+ addr_space_t as;
+ cv_qualifier cv;
+ std::tie (cv, as) = quals.split ();
+ /* These should never include an address space, at least for the time
+ being. */
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (as));
+ return cv;
}
/* Returns the FUNCTION_TYPE TYPE with its function-cv-quals changed to
@@ -12190,7 +12409,9 @@ apply_memfn_quals (tree type, cp_cv_quals memfn_quals, cp_ref_qualifier rqual)
{
/* Could handle METHOD_TYPE here if necessary. */
gcc_assert (TREE_CODE (type) == FUNCTION_TYPE);
- if (TYPE_QUALS (type) == memfn_quals
+ /* But neither of those can accept an address-space qualifier. */
+ gcc_assert (ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type)));
+ if (TYPE_QUALS_NO_ADDR_SPACE (type) == memfn_quals
&& type_memfn_rqual (type) == rqual)
return type;
@@ -12205,8 +12426,8 @@ apply_memfn_quals (tree type, cp_cv_quals memfn_quals, cp_ref_qualifier rqual)
bool
cv_qualified_p (const_tree type)
{
- int quals = cp_type_quals (type);
- return (quals & (TYPE_QUAL_CONST|TYPE_QUAL_VOLATILE)) != 0;
+ auto quals = cp_type_quals (type);
+ return quals.has (TYPE_QUAL_CONST|TYPE_QUAL_VOLATILE);
}
/* Returns nonzero if the TYPE contains a mutable member. */
@@ -12267,8 +12488,8 @@ cp_apply_type_quals_to_decl (cv_qualifier type_quals, tree decl)
static void
casts_away_constness_r (tree *t1, tree *t2, tsubst_flags_t complain)
{
- cv_qualifier quals1;
- cv_qualifier quals2;
+ qualifier_set quals1;
+ qualifier_set quals2;
/* [expr.const.cast]
@@ -12539,3 +12760,202 @@ c_decl_implicit (const_tree)
{
return false;
}
+
+#if CHECKING_P
+namespace selftest {
+
+static void
+test_addr_spaces ()
+{
+ addr_space_test addr_space_setup;
+
+ tree int_as[5];
+ tree intp_as[ARRAY_SIZE (int_as)];
+ tree param_intp_as[ARRAY_SIZE (int_as)];
+ tree intpp_as[ARRAY_SIZE (int_as)];
+ for (addr_space_t i = 0; i < ARRAY_SIZE (intp_as); i++)
+ {
+ int_as[i] = build_qualified_type (integer_type_node,
+ qualifier_set {TYPE_UNQUALIFIED, i});
+ intp_as[i] = build_pointer_type (int_as[i]);
+ param_intp_as[i] = (build_decl
+ (UNKNOWN_LOCATION, PARM_DECL,
+ get_identifier ("some_parm"),
+ intp_as[i]));
+ intpp_as[i] = build_pointer_type (intp_as[i]);
+ }
+
+ /* 0 is a superset of 1, 2, 3, ergo, is as qualified as those. */
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[0]));
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[1]));
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[2]));
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[3]));
+
+ /* Conversly, nothing is as qualified as zero (except zero). */
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[1], int_as[0]));
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[2], int_as[0]));
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[3], int_as[0]));
+
+ /* 0 and 4 aren't as qualified as each other. */
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[4], int_as[0]));
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[0], int_as[4]));
+
+ /* 1 and 2 are distinct, ergo, neither is as qualified as the other. */
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[1], int_as[2]));
+ ASSERT_FALSE (at_least_as_qualified_p (int_as[2], int_as[1]));
+
+ /* 3 and 2 overlap, ergo, both are as qualified the other. */
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[3], int_as[2]));
+ ASSERT_TRUE (at_least_as_qualified_p (int_as[2], int_as[3]));
+
+ /* The same, with comp_cv_qualification. */
+ /* Zero is as qualified as itself. */
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[0]), 0);
+ /* But so are 2 and 3, as they overlap fully. */
+ ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[3]), 0);
+ /* As is 4. */
+ ASSERT_EQ (comp_cv_qualification (int_as[4], int_as[4]), 0);
+
+ /* AS0 is over-qualified in relation to 1-3, but not 4. */
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[1]), 1);
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[2]), 1);
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[3]), 1);
+
+ ASSERT_EQ (comp_cv_qualification (int_as[1], int_as[0]), -1);
+ ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[0]), -1);
+ ASSERT_EQ (comp_cv_qualification (int_as[3], int_as[0]), -1);
+
+ /* 1 and 2 are distinct, ergo, neither is as qualified as the other. Same
+ goes for 0 and 4. */
+ ASSERT_EQ (comp_cv_qualification (int_as[1], int_as[2]), 0);
+ ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[1]), 0);
+ ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[4]), 0);
+ ASSERT_EQ (comp_cv_qualification (int_as[4], int_as[0]), 0);
+
+ /* Now to try comp_ptr_ttypes_const. */
+ /* With !STRICT, we permit any top-level address space change (this case is
+ used for C-casts). */
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[1], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[2], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[3], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[4], bounds_none, false));
+
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[1], intp_as[0], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[2], intp_as[0], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[3], intp_as[0], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[4], intp_as[0], bounds_none, false));
+
+ /* But not if they're a double-pointer, of course! */
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[1], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[2], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[3], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[4], bounds_none, false));
+
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[1], intp_as[0], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[2], intp_as[0], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[3], intp_as[0], bounds_none, false));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[4], intp_as[0], bounds_none, false));
+
+ /* ... or strict. */
+ ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[1], bounds_none));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[2], bounds_none));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[3], bounds_none));
+ ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[4], bounds_none));
+
+ /* This function permits swapping the top-level address space. */
+ ASSERT_TRUE (comp_ptr_ttypes_const (int_as[2], int_as[3], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (int_as[3], int_as[2], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (int_as[1], int_as[2], bounds_none, false));
+ ASSERT_TRUE (comp_ptr_ttypes_const (int_as[2], int_as[1], bounds_none, false));
+
+
+ /* Now to try comp_ptr_ttypes_real. The tests are exactly identical, but
+ they lose a layer of pointers (see comment above comp_ptr_ttypes). */
+ /* We can assign pointers in address spaces 1, 2, and 3 to a pointer in
+ zero. */
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[0]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[1]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[2]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[3]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[3]));
+ ASSERT_FALSE (comp_ptr_ttypes (int_as[0], int_as[4]));
+
+ /* But not if they're a double-pointer, of course! */
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[0]));
+ ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[1]));
+ ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[2]));
+ ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[3]));
+ ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[4]));
+
+ /* This function permits going from a sub-space into a superspace. */
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[2], int_as[3]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[3], int_as[2]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[1]));
+ ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[2]));
+
+ ASSERT_FALSE (comp_ptr_ttypes (int_as[1], int_as[2]));
+ ASSERT_FALSE (comp_ptr_ttypes (int_as[2], int_as[1]));
+ ASSERT_FALSE (comp_ptr_ttypes (int_as[0], int_as[4]));
+ ASSERT_FALSE (comp_ptr_ttypes (int_as[4], int_as[0]));
+
+ /* Testing composite_pointer_type. */
+ op_location_t oploc (UNKNOWN_LOCATION);
+
+ /* A composite of pointers in two address spaces such that one is a superset
+ of another is a pointer in the superset address space. */
+ auto as0_as1_composite =
+ composite_pointer_type (oploc, TREE_TYPE (param_intp_as[0]),
+ TREE_TYPE (param_intp_as[1]),
+ param_intp_as[0], param_intp_as[1],
+ CPO_CONDITIONAL_EXPR,
+ /* We'll allow emitting a diagnostic for the sake
+ of the developer. */
+ tf_warning_or_error);
+ ASSERT_FALSE (error_type_p (as0_as1_composite));
+ ASSERT_TRUE (same_type_p (intp_as[0], as0_as1_composite));
+
+ /* But if they're unrelated, you get an error. */
+ auto as2_as1_composite =
+ composite_pointer_type (oploc, TREE_TYPE (param_intp_as[2]),
+ TREE_TYPE (param_intp_as[1]),
+ param_intp_as[2], param_intp_as[1],
+ CPO_CONDITIONAL_EXPR,
+ /* We expect a diagnostic here, so suppress it to
+ get the corresponding error node. */
+ tf_none);
+ ASSERT_TRUE (error_type_p (as2_as1_composite));
+ auto as0_as4_composite =
+ composite_pointer_type (oploc, TREE_TYPE (param_intp_as[0]),
+ TREE_TYPE (param_intp_as[4]),
+ param_intp_as[0], param_intp_as[4],
+ CPO_CONDITIONAL_EXPR,
+ /* We expect a diagnostic here, so suppress it to
+ get the corresponding error node. */
+ tf_none);
+ ASSERT_TRUE (error_type_p (as0_as4_composite));
+
+ /* If they overlap, it doesn't really matter which one we land on. */
+ auto as2_as3_composite =
+ composite_pointer_type (oploc, TREE_TYPE (param_intp_as[2]),
+ TREE_TYPE (param_intp_as[3]),
+ param_intp_as[2], param_intp_as[3],
+ CPO_CONDITIONAL_EXPR,
+ /* This should pass. */
+ tf_warning_or_error);
+ ASSERT_FALSE (error_type_p (as2_as3_composite));
+ /* It doesn't really matter which one we get. Or, it shouldn't matter. */
+ ASSERT_TRUE (same_type_p (intp_as[2], as2_as3_composite)
+ || same_type_p (intp_as[3], as2_as3_composite));
+}
+
+void
+cp_typeck_cc_tests ()
+{
+ test_addr_spaces ();
+}
+
+}
+#endif
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 12c34ba6b3a7..183765c740af 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see
checks, and some optimization. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
@@ -35,6 +36,8 @@ along with GCC; see the file COPYING3. If not see
#include "gcc-rich-location.h"
#include "target.h"
+#include "util/optional.h"
+
static tree
process_init_constructor (tree type, tree init, int nested, int flags,
tsubst_flags_t complain);
@@ -2485,9 +2488,13 @@ build_m_component_ref (tree datum, tree component, tsubst_flags_t complain)
There's no such thing as a mutable pointer-to-member, so
things are not as complex as they are for references to
non-static data members. */
- type = cp_build_qualified_type (type,
- (cp_type_quals (type)
- | cp_type_quals (TREE_TYPE (datum))));
+ cv_qualifier datum_cv;
+ addr_space_t datum_as;
+ std::tie (datum_cv, datum_as) = (cp_type_quals (TREE_TYPE (datum))
+ .split ());
+ /* Member offsets shouldn't have address spaces. */
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (datum_as));
+ type = cp_build_qualified_type (type, TYPE_QUALS (type).with (datum_cv));
datum = cp_build_addr_expr (datum, complain);
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index d7dccd863c53..a4c004288be4 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1375,7 +1375,7 @@ initializer expressions and issues a warning.
@section Named Address Spaces
@cindex Named Address Spaces
-As an extension, GNU C supports named address spaces as
+As an extension, GNU C and GNU C++ support named address spaces as
defined in the N1275 draft of ISO/IEC DTR 18037. Support for named
address spaces in GCC will evolve as the draft technical report
changes. Calling conventions for any target might also change. At
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 85e175256833..b65b907cb8d9 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11484,10 +11484,10 @@ Internally, address spaces are represented as a small integer in the
range 0 to 15 with address space 0 being reserved for the generic
address space.
-To register a named address space qualifier keyword with the C front end,
-the target may call the @code{c_register_addr_space} routine. For example,
-the SPU port uses the following to declare @code{__ea} as the keyword for
-named address space #1:
+To register a named address space qualifier keyword with the GNU C and
+GNU C++, the target may call the @code{c_register_addr_space} routine.
+For example, the SPU port uses the following to declare @code{__ea} as
+the keyword for named address space #1:
@smallexample
#define ADDR_SPACE_EA 1
c_register_addr_space ("__ea", ADDR_SPACE_EA);
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 1a9edd0635d0..daf7705ac1e1 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -7361,10 +7361,10 @@ Internally, address spaces are represented as a small integer in the
range 0 to 15 with address space 0 being reserved for the generic
address space.
-To register a named address space qualifier keyword with the C front end,
-the target may call the @code{c_register_addr_space} routine. For example,
-the SPU port uses the following to declare @code{__ea} as the keyword for
-named address space #1:
+To register a named address space qualifier keyword with the GNU C and
+GNU C++, the target may call the @code{c_register_addr_space} routine.
+For example, the SPU port uses the following to declare @code{__ea} as
+the keyword for named address space #1:
@smallexample
#define ADDR_SPACE_EA 1
c_register_addr_space ("__ea", ADDR_SPACE_EA);
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
new file mode 100644
index 000000000000..35701c60637c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } }
+
+int f (int volatile __seg_fs *a)
+{
+ return *a;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
new file mode 100644
index 000000000000..1daf15a73c09
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-fabi-version=8 -Wabi -save-temps" }
+// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } }
+
+template <class T>
+int f (T *p) { return *p; }
+int g (__seg_fs int *p) { return *p; }
+__seg_fs int *a;
+int main() { f(a); }
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-conv.C b/gcc/testsuite/g++.dg/ext/addr-space-conv.C
new file mode 100644
index 000000000000..ce1ef1f24560
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-conv.C
@@ -0,0 +1,209 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } } */
+/* This test is derived from Clang test
+ clang/test/SemaCXX/address-space-conversion.cpp. It works on any platform
+ where there are two disjoint address spaces, but requires specific porting
+ to each. */
+
+#if defined(__x86_64__) || defined(__i386__)
+# define AS1 __seg_fs
+# define AS2 __seg_gs
+#else
+# error "port me! by adding two defines for two disjoint addr spaces"
+#endif
+
+// This test checks for the various conversions and casting operations
+// with address-space-qualified pointers.
+
+struct A { virtual ~A() {} };
+struct B : A { };
+
+typedef void *void_ptr;
+typedef void AS1 *void_ptr_1;
+typedef void AS2 *void_ptr_2;
+
+typedef int *int_ptr;
+typedef int AS1 *int_ptr_1;
+typedef int AS2 *int_ptr_2;
+
+typedef A *A_ptr;
+typedef A AS1 *A_ptr_1;
+typedef A AS2 *A_ptr_2;
+
+typedef B *B_ptr;
+typedef B AS1 *B_ptr_1;
+typedef B AS2 *B_ptr_2;
+
+void test_const_cast(int_ptr ip, int_ptr_1 ip1, int_ptr_2 ip2,
+ A_ptr ap, A_ptr_1 ap1, A_ptr_2 ap2,
+ const int *cip,
+ const int AS1 *cip1) {
+ // Cannot use const_cast to cast between address spaces, add an
+ // address space, or remove an address space.
+ (void)const_cast<int_ptr>(ip1); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<int_ptr>(ip2); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<int_ptr_1>(ip); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<int_ptr_1>(ip2); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<int_ptr_2>(ip); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<int_ptr_2>(ip1); // { dg-error "invalid 'const_cast'" }
+
+ (void)const_cast<A_ptr>(ap1); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<A_ptr>(ap2); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<A_ptr_1>(ap); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<A_ptr_1>(ap2); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<A_ptr_2>(ap); // { dg-error "invalid 'const_cast'" }
+ (void)const_cast<A_ptr_2>(ap1); // { dg-error "invalid 'const_cast'" }
+
+ // It's acceptable to cast away constness.
+ (void)const_cast<int_ptr>(cip);
+ (void)const_cast<int_ptr_1>(cip1);
+}
+
+void test_static_cast(void_ptr vp, void_ptr_1 vp1, void_ptr_2 vp2,
+ A_ptr ap, A_ptr_1 ap1, A_ptr_2 ap2,
+ B_ptr bp, B_ptr_1 bp1, B_ptr_2 bp2) {
+ // Well-formed upcast
+ (void)static_cast<A_ptr>(bp);
+ (void)static_cast<A_ptr_1>(bp1);
+ (void)static_cast<A_ptr_2>(bp2);
+
+ // Well-formed downcast
+ (void)static_cast<B_ptr>(ap);
+ (void)static_cast<B_ptr_1>(ap1);
+ (void)static_cast<B_ptr_2>(ap2);
+
+ // Well-formed cast to/from void
+ (void)static_cast<void_ptr>(ap);
+ (void)static_cast<void_ptr_1>(ap1);
+ (void)static_cast<void_ptr_2>(ap2);
+ (void)static_cast<A_ptr>(vp);
+ (void)static_cast<A_ptr_1>(vp1);
+ (void)static_cast<A_ptr_2>(vp2);
+
+ // Ill-formed upcasts
+ (void)static_cast<A_ptr>(bp1); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<A_ptr>(bp2); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<A_ptr_1>(bp); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<A_ptr_1>(bp2); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<A_ptr_2>(bp); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<A_ptr_2>(bp1); // { dg-error "invalid 'static_cast'" }
+
+ // Ill-formed downcasts
+ (void)static_cast<B_ptr>(ap1); // { dg-error "casts away qualifiers" }
+ (void)static_cast<B_ptr>(ap2); // { dg-error "casts away qualifiers" }
+ (void)static_cast<B_ptr_1>(ap); // { dg-error "casts away qualifiers" }
+ (void)static_cast<B_ptr_1>(ap2); // { dg-error "casts away qualifiers" }
+ (void)static_cast<B_ptr_2>(ap); // { dg-error "casts away qualifiers" }
+ (void)static_cast<B_ptr_2>(ap1); // { dg-error "casts away qualifiers" }
+
+ // Ill-formed cast to/from void
+ (void)static_cast<void_ptr>(ap1); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<void_ptr>(ap2); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<void_ptr_1>(ap); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<void_ptr_1>(ap2); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<void_ptr_2>(ap); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<void_ptr_2>(ap1); // { dg-error "invalid 'static_cast'" }
+ (void)static_cast<A_ptr>(vp1); // { dg-error "casts away qualifiers" }
+ (void)static_cast<A_ptr>(vp2); // { dg-error "casts away qualifiers" }
+ (void)static_cast<A_ptr_1>(vp); // { dg-error "casts away qualifiers" }
+ (void)static_cast<A_ptr_1>(vp2); // { dg-error "casts away qualifiers" }
+ (void)static_cast<A_ptr_2>(vp); // { dg-error "casts away qualifiers" }
+ (void)static_cast<A_ptr_2>(vp1); // { dg-error "casts away qualifiers" }
+}
+
+void test_dynamic_cast(A_ptr ap, A_ptr_1 ap1, A_ptr_2 ap2,
+ B_ptr bp, B_ptr_1 bp1, B_ptr_2 bp2) {
+ // Well-formed upcast
+ (void)dynamic_cast<A_ptr>(bp);
+ (void)dynamic_cast<A_ptr_1>(bp1);
+ (void)dynamic_cast<A_ptr_2>(bp2);
+
+ // Well-formed downcast
+ (void)dynamic_cast<B_ptr>(ap);
+ (void)dynamic_cast<B_ptr_1>(ap1);
+ (void)dynamic_cast<B_ptr_2>(ap2);
+
+ // Ill-formed upcasts
+ /* XXX: "casts away constness"?! There's no const here. */
+ (void)dynamic_cast<A_ptr>(bp1); // { dg-error "casts away constness" }
+ (void)dynamic_cast<A_ptr>(bp2); // { dg-error "casts away constness" }
+ (void)dynamic_cast<A_ptr_1>(bp); // { dg-error "casts away constness" }
+ (void)dynamic_cast<A_ptr_1>(bp2); // { dg-error "casts away constness" }
+ (void)dynamic_cast<A_ptr_2>(bp); // { dg-error "casts away constness" }
+ (void)dynamic_cast<A_ptr_2>(bp1); // { dg-error "casts away constness" }
+
+ // Ill-formed downcasts
+ (void)dynamic_cast<B_ptr>(ap1); // { dg-error "casts away constness" }
+ (void)dynamic_cast<B_ptr>(ap2); // { dg-error "casts away constness" }
+ (void)dynamic_cast<B_ptr_1>(ap); // { dg-error "casts away constness" }
+ (void)dynamic_cast<B_ptr_1>(ap2); // { dg-error "casts away constness" }
+ (void)dynamic_cast<B_ptr_2>(ap); // { dg-error "casts away constness" }
+ (void)dynamic_cast<B_ptr_2>(ap1); // { dg-error "casts away constness" }
+}
+
+void test_reinterpret_cast(void_ptr vp, void_ptr_1 vp1, void_ptr_2 vp2,
+ A_ptr ap, A_ptr_1 ap1, A_ptr_2 ap2,
+ B_ptr bp, B_ptr_1 bp1, B_ptr_2 bp2,
+ const void AS1 * cvp1) {
+ // reinterpret_cast can't be used to cast to a different address space unless they are matching (i.e. overlapping).
+ (void)reinterpret_cast<A_ptr>(ap1); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr>(ap2); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr>(bp);
+ (void)reinterpret_cast<A_ptr>(bp1); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr>(bp2); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr>(vp);
+ (void)reinterpret_cast<A_ptr>(vp1); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr>(vp2); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr_1>(ap); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr_1>(ap2); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr_1>(bp); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr_1>(bp1);
+ (void)reinterpret_cast<A_ptr_1>(bp2); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr_1>(vp); // { dg-error "casts away qualifiers" }
+ (void)reinterpret_cast<A_ptr_1>(vp1);
+ (void)reinterpret_cast<A_ptr_1>(vp2); // { dg-error "casts away qualifiers" }
+
+ // ... but don't try to cast away constness!
+ (void)reinterpret_cast<A_ptr_2>(cvp1); // { dg-error "casts away qualifiers" }
+}
+
+void test_cstyle_cast(void_ptr vp, void_ptr_1 vp1, void_ptr_2 vp2,
+ A_ptr ap, A_ptr_1 ap1, A_ptr_2 ap2,
+ B_ptr bp, B_ptr_1 bp1, B_ptr_2 bp2,
+ const void AS1 *cvp1) {
+ // C-style casts are the wild west of casts.
+ (void)(A_ptr)(ap1); // { dg-warning "from disjoint" }
+ (void)(A_ptr)(ap2); // { dg-warning "from disjoint" }
+ (void)(A_ptr)(bp);
+ (void)(A_ptr)(bp1); // { dg-warning "from disjoint" }
+ (void)(A_ptr)(bp2); // { dg-warning "from disjoint" }
+ (void)(A_ptr)(vp);
+ (void)(A_ptr)(vp1); // { dg-warning "from disjoint" }
+ (void)(A_ptr)(vp2); // { dg-warning "from disjoint" }
+ (void)(A_ptr_1)(ap); // { dg-warning "from disjoint" }
+ (void)(A_ptr_1)(ap2); // { dg-warning "from disjoint" }
+ (void)(A_ptr_1)(bp); // { dg-warning "from disjoint" }
+ (void)(A_ptr_1)(bp1);
+ (void)(A_ptr_1)(bp2); // { dg-warning "from disjoint" }
+ (void)(A_ptr_1)(vp); // { dg-warning "from disjoint" }
+ (void)(A_ptr_1)(vp1);
+ (void)(A_ptr_1)(vp2); // { dg-warning "from disjoint" }
+ (void)(A_ptr_2)(cvp1); // { dg-warning "from disjoint" }
+}
+
+void test_implicit_conversion(void_ptr vp, void_ptr_1 vp1, void_ptr_2 vp2,
+ A_ptr ap, A_ptr_1 ap1, A_ptr_2 ap2,
+ B_ptr bp, B_ptr_1 bp1, B_ptr_2 bp2) {
+ // Well-formed conversions
+ void_ptr vpA = ap;
+ void_ptr_1 vp_1A = ap1;
+ void_ptr_2 vp_2A = ap2;
+ A_ptr ap_A = bp;
+ A_ptr_1 ap_A1 = bp1;
+ A_ptr_2 ap_A2 = bp2;
+
+ // Ill-formed conversions
+ void_ptr vpB = ap1; // { dg-error "invalid conversion" }
+ void_ptr_1 vp_1B = ap2; // { dg-error "invalid conversion" }
+ A_ptr ap_B = bp1; // { dg-error "invalid conversion" }
+ A_ptr_1 ap_B1 = bp2; // { dg-error "invalid conversion" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
new file mode 100644
index 000000000000..4199bf375b3d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+__seg_fs char a, b, c;
+__seg_fs const int *p;
+static /* gives internal linkage to variable q */
+__seg_fs struct { int a; char b; } * __seg_gs q;
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
new file mode 100644
index 000000000000..104f9bbcdaa9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+int __seg_fs * fs1;
+int __seg_fs * fs2;
+int __seg_gs * gs1;
+int __seg_gs * gs2;
+
+int
+main ()
+{
+ fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." }
+ fs1 - fs2;
+ fs1 - gs2; // { dg-error "pointers to disjoint address spaces .__seg_fs int.. and .__seg_gs int.. in subtraction" }
+ // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" "" { target *-*-* } .-1 }
+ fs1 == fs2;
+ fs1 != gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" }
+ fs1 = fs2;
+ fs1 = gs2; // { dg-error "invalid conversion from .__seg_gs int.. to .__seg_fs int.." }
+ fs1 > fs2;
+ fs1 < gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" }
+ new __seg_fs int; // { dg-error "'new' cannot allocate in address space '__seg_fs'" }
+ extern __seg_fs int *t;
+ delete t;// { dg-error "'delete' cannot delete objects in address space '__seg_fs'" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
new file mode 100644
index 000000000000..e87649abb5d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C
@@ -0,0 +1,29 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-prune-output "does not allow .register. storage class specifier" }
+int __seg_fs * outer_b;
+
+struct s {
+ __seg_fs int * ok;
+ __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." }
+};
+
+namespace ns_a
+{
+ int __seg_fs * inner_b;
+
+ template<typename T>
+ int f (T &a) { return a; }
+ int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." }
+ int h (__seg_fs int *a) { return *a; }
+}
+
+int
+main ()
+{
+ int register __seg_gs reg_gs; // { dg-error ".__seg_gs. specified for variable .reg_gs. with automatic storage" }
+ static __seg_gs int static_gs;
+ __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for variable .auto_fs. with automatic storage" }
+ __seg_fs int *pa = outer_b;
+ __seg_fs int& ra = *ns_a::inner_b;
+ return ns_a::f(ra) + ns_a::f(*pa);
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C
new file mode 100644
index 000000000000..ebb6316054aa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C
new file mode 100644
index 000000000000..2e8ee32a8850
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space1.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-options "-std=gnu++98" }
+
+int
+main ()
+{
+ struct foo {int a; char b[2];} structure;
+ structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C
new file mode 100644
index 000000000000..5f64d52885e6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/addr-space2.C
@@ -0,0 +1,9 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+__seg_fs __seg_gs int *a; // { dg-error "incompatible address space qualifiers" }
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C
new file mode 100644
index 000000000000..70dfcce53faf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; }
+template<typename T, typename U>
+__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; }
+
+int
+main ()
+{
+ f (fs1, gs1);
+ f (gs1, fs1);
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
new file mode 100644
index 000000000000..36c402f5cd23
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C
@@ -0,0 +1,18 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+// { dg-require-effective-target c++11 }
+// decltype is only available since c++11
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T> struct strip;
+template<typename T> struct strip<__seg_fs T *> { typedef T type; };
+template<typename T> struct strip<__seg_gs T *> { typedef T type; };
+
+int
+main ()
+{
+ *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1;
+ // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 }
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
new file mode 100644
index 000000000000..a2ffb5320872
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+int __seg_fs * fs1;
+int __seg_gs * gs1;
+
+template<typename T, typename U>
+bool f (T __seg_fs * a, U __seg_gs * b)
+{
+ return *(T *) a == *(U *) b;
+ // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 }
+}
+
+int
+main ()
+{
+ return f (fs1, gs1);
+}
diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C
new file mode 100644
index 000000000000..beb321afc30c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target { i?86-*-* x86_64-*-* } } }
+
+template <class T>
+int f (T __seg_gs *p) { return *p; } // { dg-note "candidate 1: 'template<class T> int f.__seg_gs T\*." }
+ // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 }
+
+__seg_fs int *a;
+int main() { f(a); } // { dg-error "no matching" }
+// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 }
+// { dg-note "1 candidate" "" { target *-*-* } .-2 }
--
2.55.0
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH v5 7/7] c++: make comp_ptr_ttypes_real slightly easier to understand
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
` (5 preceding siblings ...)
2026-07-09 16:36 ` [PATCH v5 6/7] gcc/c++: implement basic support for Named Address Spaces in C++ [PR69549] Arsen Arsenović
@ 2026-07-09 16:36 ` Arsen Arsenović
6 siblings, 0 replies; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 16:36 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Arsen Arsenović
Previously, the CONSTP parameter was a weird mix of local state and
external configuration. It was valid to call the function with -1 or 1,
where -1 indicates that the function actually only returns true if
FROM had a proper subset of qualification compared to TO. The value 1
was used as an initial value to confirm that all outer layers were
const, and would be made zero if any value wasn't.
This patch splits this weird amalgamate, making the function instead
take PROPERP which, if set, instructs the function to return true only
if FROM is a proper subset of TO (as the -1 case before), and instead
tracking the outer constness via a local bool.
No functional changes intended.
gcc/cp/ChangeLog:
* typeck.cc (comp_ptr_ttypes_real): Update signature to take a
bool properp rather than a constp that's either 1 or -1, which
is then used in turn as local state.
(comp_cv_qual_signature): Call the above with true instead of -1
now.
(comp_ptr_ttypes): Call the above with false instead of 1 now.
---
gcc/cp/typeck.cc | 35 +++++++++++++++++++----------------
1 file changed, 19 insertions(+), 16 deletions(-)
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 170b90828868..43a04c7cbcf5 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -56,7 +56,7 @@ static tree cp_pointer_int_sum (location_t, enum tree_code, tree, tree,
tsubst_flags_t);
static tree rationalize_conditional_expr (enum tree_code, tree,
tsubst_flags_t);
-static bool comp_ptr_ttypes_real (tree, tree, int);
+static bool comp_ptr_ttypes_real (tree, tree, bool);
static bool comp_except_types (tree, tree, bool);
static bool comp_array_types (const_tree, const_tree, compare_bounds_t, bool);
static tree pointer_diff (location_t, tree, tree, tree, tsubst_flags_t, tree *);
@@ -2213,9 +2213,9 @@ comp_cv_qualification (const_tree type1, const_tree type2)
int
comp_cv_qual_signature (tree type1, tree type2)
{
- if (comp_ptr_ttypes_real (type2, type1, -1))
+ if (comp_ptr_ttypes_real (type2, type1, true))
return 1;
- else if (comp_ptr_ttypes_real (type1, type2, -1))
+ else if (comp_ptr_ttypes_real (type1, type2, true))
return -1;
else
return 0;
@@ -12116,20 +12116,20 @@ check_return_expr (tree retval, bool *no_warning, bool *dangling)
\f
/* Returns nonzero if the pointer-type FROM can be converted to the
- pointer-type TO via a qualification conversion. If CONSTP is -1,
- then we return nonzero if the pointers are similar, and the
- cv-qualification signature of FROM is a proper subset of that of TO.
-
- If CONSTP is positive, then all outer pointers have been
- const-qualified. */
+ pointer-type TO via a qualification conversion. If PROPERP, then we return
+ nonzero if the pointers are similar, and the cv-qualification signature of
+ FROM is a proper subset of that of TO. */
static bool
-comp_ptr_ttypes_real (tree to, tree from, int constp)
+comp_ptr_ttypes_real (tree to, tree from, bool properp)
{
bool to_more_cv_qualified = false;
bool is_opaque_pointer = false;
bool is_toplevel = true;
+ /* True if all the levels so far were const-qualified. */
+ bool outer_const = true;
+
for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from), is_toplevel = false)
{
if (TREE_CODE (to) != TREE_CODE (from))
@@ -12169,15 +12169,18 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
{
/* TO has extra qualifiers over FROM. Per [conv.qual]p3.3, this
is permitted only if the outer levels all had const ("const
- is added to every ..."). */
- if (constp == 0)
+ is added to every ...").
+
+ If PROPERP, the goal is to compare the qualification sets of
+ the pointers, so don't early-exit. */
+ if (!properp && !outer_const)
return false;
to_more_cv_qualified = true;
}
- if (constp > 0)
- constp &= TYPE_READONLY (to);
+ if (!TYPE_READONLY (to))
+ outer_const = false;
}
if (VECTOR_TYPE_P (to))
@@ -12194,7 +12197,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
&& !TYPE_PTRDATAMEM_P (to)
/* CWG 330 says we need to look through arrays. */
&& TREE_CODE (to) != ARRAY_TYPE)
- return ((constp >= 0 || to_more_cv_qualified)
+ return ((!properp || to_more_cv_qualified)
&& (is_opaque_pointer
|| same_type_ignoring_top_level_qualifiers_p (to, from)));
}
@@ -12207,7 +12210,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
int
comp_ptr_ttypes (tree to, tree from)
{
- return comp_ptr_ttypes_real (to, from, 1);
+ return comp_ptr_ttypes_real (to, from, false);
}
/* Returns true iff FNTYPE is a non-class type that involves
--
2.55.0
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v5 2/7] gcc/target.def: fix HOOK_PREFIX redefinition warning
2026-07-09 16:36 ` [PATCH v5 2/7] gcc/target.def: fix HOOK_PREFIX redefinition warning Arsen Arsenović
@ 2026-07-09 16:53 ` Andrea Pinski
2026-07-09 22:02 ` Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Andrea Pinski @ 2026-07-09 16:53 UTC (permalink / raw)
To: Arsen Arsenović; +Cc: Jason Merrill, gcc-patches
On Thu, Jul 9, 2026 at 9:43 AM Arsen Arsenović <aarsenovic@baylibre.com> wrote:
>
> gcc/ChangeLog:
>
> * target.def (HOOK_PREFIX): Undefine, before redefining.
> ---
> gcc/target.def | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/gcc/target.def b/gcc/target.def
> index 884fe1bd57e1..a9ccc19c180a 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -29,6 +29,7 @@
> HOOK_VECTOR (TARGET_INITIALIZER, gcc_target)
>
> /* Functions that output assembler for the target. */
> +#undef HOOK_PREFIX
> #define HOOK_PREFIX "TARGET_ASM_"
> HOOK_VECTOR (TARGET_ASM_OUT, asm_out)
Hmm, I am trying to figure out why is this needed.
target.def is only included once in target.h.
Are you changing that?
Or is it because you include target.h after c-target.h ?
>
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v5 3/7] gcc/{c, cp, c-family}: move some address space handling into c-family
2026-07-09 16:36 ` [PATCH v5 3/7] gcc/{c,cp,c-family}: move some address space handling into c-family Arsen Arsenović
@ 2026-07-09 16:56 ` Andrea Pinski
2026-07-09 22:05 ` Arsen Arsenović
0 siblings, 1 reply; 86+ messages in thread
From: Andrea Pinski @ 2026-07-09 16:56 UTC (permalink / raw)
To: Arsen Arsenović; +Cc: Jason Merrill, gcc-patches
On Thu, Jul 9, 2026 at 9:44 AM Arsen Arsenović <aarsenovic@baylibre.com> wrote:
>
> ... in anticipation of a few coming patches implementing C++ Named
> Address Space support.
>
> gcc/c-family/ChangeLog:
>
> * c-common.cc (addr_space_superset): Move here from
> ../c/c-typeck.cc.
> (c_register_addr_space): Move here from ../c/c-decl.cc.
> * c-common.h (c_register_addr_space): Move into c-common.cc
> section.
> (addr_space_superset): Declare.
> (c_make_keyword): New. Marks ID as a keyword. Implemented
> by both FEs.
> (c_unmake_keyword): New. Unmarks ID as a keyword. Implemented
> by both FEs.
>
> gcc/c/ChangeLog:
>
> * c-decl.cc (c_make_keyword): Implement.
> (c_unmake_keyword): Ditto.
> (c_register_addr_space): Move into ../c-family/c-common.cc.
> * c-typeck.cc (addr_space_superset): Move into
> ../c-family/c-common.cc.
>
> gcc/cp/ChangeLog:
>
> * lex.cc (set_identifier_kind): Add comment referencing the two
> functions mentioned below.
> (c_make_keyword): Implement.
> (c_unmake_keyword): Ditto.
> * tree.cc (c_register_addr_space): Drop. The implementation is
> now in c-family.cc, so no stub is needed.
> ---
> gcc/c-family/c-common.cc | 45 ++++++++++++++++++++++++++++++++++++++++
> gcc/c-family/c-common.h | 16 +++++++++++---
> gcc/c/c-decl.cc | 26 +++++++++++------------
> gcc/c/c-typeck.cc | 26 -----------------------
> gcc/cp/lex.cc | 23 ++++++++++++++++++++
> gcc/cp/tree.cc | 9 --------
> 6 files changed, 93 insertions(+), 52 deletions(-)
>
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index 3d7ef128f0d9..e05551e51192 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -660,6 +660,32 @@ c_addr_space_name (addr_space_t as)
> return IDENTIFIER_POINTER (ridpointers [rid]);
> }
>
> +/* Return true if between two named address spaces, whether there is a superset
> + named address space that encompasses both address spaces. If there is a
> + superset, return which address space is the superset. */
> +
> +bool
> +addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t* common)
> +{
> + if (as1 == as2)
> + {
> + *common = as1;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as1, as2))
> + {
> + *common = as2;
> + return true;
> + }
> + else if (targetm.addr_space.subset_p (as2, as1))
> + {
> + *common = as1;
> + return true;
> + }
> + else
> + return false;
> +}
> +
> /* Push current bindings for the function name VAR_DECLS. */
>
> void
> @@ -2899,6 +2925,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp)
> return build_nonstandard_integer_type (width, unsignedp);
> }
>
> +/* Register reserved keyword WORD as qualifier for address space AS. */
> +
> +void
> +c_register_addr_space (const char *word, addr_space_t as)
> +{
> + int rid = RID_FIRST_ADDR_SPACE + as;
> + tree id;
> +
> + /* Address space qualifiers are only supported
> + in C with GNU extensions enabled. */
> + if (c_dialect_objc () || flag_no_asm)
> + return;
> +
> + id = get_identifier (word);
> + C_SET_RID_CODE (id, rid);
> + c_make_keyword (id);
> + ridpointers[rid] = id;
> +}
> +
> /* The C version of the register_builtin_type langhook. */
>
> void
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 40a4771a1f25..569edc064014 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -43,6 +43,10 @@ never after.
> #endif
> #include "diagnostic-core.h"
>
> +#if CHECKING_P
> +# include "target.h"
> +#endif
I don't think we normally have includes depend on compiling
with/without checking.
Maybe include target.h from c-target.h instead.
> +
> /* Usage of TREE_LANG_FLAG_?:
> 0: IDENTIFIER_MARKED (used by search routines).
> C_MAYBE_CONST_EXPR_INT_OPERANDS (in C_MAYBE_CONST_EXPR, for C)
> @@ -843,12 +847,10 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table;
>
> extern tree (*make_fname_decl) (location_t, tree, int);
>
> -/* In c-decl.cc and cp/tree.cc. FIXME. */
> -extern void c_register_addr_space (const char *str, addr_space_t as);
> -
> /* In c-common.cc. */
> extern bool in_late_binary_op;
> extern const char *c_addr_space_name (addr_space_t as);
> +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *);
> extern tree identifier_global_value (tree);
> extern tree identifier_global_tag (tree);
> extern int names_builtin_p (const char *);
> @@ -1002,6 +1004,7 @@ extern bool c_common_init (void);
> extern void c_common_finish (void);
> extern void c_common_parse_file (void);
> extern alias_set_type c_common_get_alias_set (tree);
> +extern void c_register_addr_space (const char *, addr_space_t);
> extern void c_register_builtin_type (tree, const char*);
> extern bool c_promoting_integer_type_p (const_tree);
> extern bool self_promoting_args_p (const_tree);
> @@ -1133,6 +1136,13 @@ extern tree lookup_name (tree);
> extern bool lvalue_p (const_tree);
> extern int maybe_adjust_arg_pos_for_attribute (const_tree);
> extern bool instantiation_dependent_expression_p (tree);
> +/* Make ID into a keyword, in a front end specific manner. Will only be called
> + on normal identifiers. Used in c-common.cc. */
> +extern void c_make_keyword (tree id);
> +/* Make ID into a regular identifier, in a front end specific manner. Will
> + only be called on identifiers previously given to c_make_keyword. Used in
> + c-common.cc. */
> +extern void c_unmake_keyword (tree id);
>
> extern bool vector_targets_convertible_p (const_tree t1, const_tree t2);
> extern bool vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note);
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index 3c8050a6b13a..cd0601044da0 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -13870,23 +13870,21 @@ c_parse_final_cleanups (void)
> ext_block = NULL;
> }
>
> -/* Register reserved keyword WORD as qualifier for address space AS. */
> +/* c-common.cc uses these two when registering its own keywords. */
> +void
> +c_make_keyword (tree id)
> +{
> + gcc_checking_assert (!C_IS_RESERVED_WORD (id));
> + C_IS_RESERVED_WORD (id) = 1;
> +}
>
> void
> -c_register_addr_space (const char *word, addr_space_t as)
> +c_unmake_keyword (tree id)
> {
> - int rid = RID_FIRST_ADDR_SPACE + as;
> - tree id;
> -
> - /* Address space qualifiers are only supported
> - in C with GNU extensions enabled. */
> - if (c_dialect_objc () || flag_no_asm)
> - return;
> -
> - id = get_identifier (word);
> - C_SET_RID_CODE (id, rid);
> - C_IS_RESERVED_WORD (id) = 1;
> - ridpointers [rid] = id;
> + /* We only expect to be clearing identifiers previously made into keywords by
> + c_make_keyword. This is done primarily for self-tests. */
> + gcc_checking_assert (C_IS_RESERVED_WORD (id));
> + C_IS_RESERVED_WORD (id) = 0;
> }
>
> /* Return identifier to look up for omp declare reduction. */
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index 81ed46f3ed0c..11bf30917ddb 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -316,32 +316,6 @@ c_type_promotes_to (tree type)
> return type;
> }
>
> -/* Return true if between two named address spaces, whether there is a superset
> - named address space that encompasses both address spaces. If there is a
> - superset, return which address space is the superset. */
> -
> -static bool
> -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common)
> -{
> - if (as1 == as2)
> - {
> - *common = as1;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as1, as2))
> - {
> - *common = as2;
> - return true;
> - }
> - else if (targetm.addr_space.subset_p (as2, as1))
> - {
> - *common = as1;
> - return true;
> - }
> - else
> - return false;
> -}
> -
> /* Return a variant of TYPE which has all the type qualifiers of LIKE
> as well as those of TYPE. */
>
> diff --git a/gcc/cp/lex.cc b/gcc/cp/lex.cc
> index da40be0a5e06..d6fad67cd5b6 100644
> --- a/gcc/cp/lex.cc
> +++ b/gcc/cp/lex.cc
> @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see
> #include "coretypes.h"
> #include "cp-tree.h"
> #include "stringpool.h"
> +#include "c-family/c-common.h"
> #include "c-family/c-pragma.h"
> #include "c-family/c-objc.h"
> #include "gcc-rich-location.h"
> @@ -134,6 +135,7 @@ get_identifier_kind_name (tree id)
> void
> set_identifier_kind (tree id, cp_identifier_kind kind)
> {
> + /* See also below. */
> gcc_checking_assert (!IDENTIFIER_KIND_BIT_2 (id)
> & !IDENTIFIER_KIND_BIT_1 (id)
> & !IDENTIFIER_KIND_BIT_0 (id));
> @@ -142,6 +144,27 @@ set_identifier_kind (tree id, cp_identifier_kind kind)
> IDENTIFIER_KIND_BIT_0 (id) |= (kind >> 0) & 1;
> }
>
> +/* c-common.cc uses these two when registering its own keywords. */
> +void
> +c_make_keyword (tree id)
> +{
> + set_identifier_kind (id, cik_keyword);
> +}
> +
> +void
> +c_unmake_keyword (tree id)
> +{
> + /* We only expect to be clearing identifiers previously made into keywords by
> + c_make_keyword. This is done primarily for self-tests. */
> + static_assert (cik_keyword == 1, "this assert assumes this value");
> + gcc_checking_assert (IDENTIFIER_KIND_BIT_0 (id)
> + && !IDENTIFIER_KIND_BIT_1 (id)
> + && !IDENTIFIER_KIND_BIT_2 (id));
> + IDENTIFIER_KIND_BIT_2 (id) = 0;
> + IDENTIFIER_KIND_BIT_1 (id) = 0;
> + IDENTIFIER_KIND_BIT_0 (id) = 0;
> +}
> +
> /* Create and tag the internal operator name for the overloaded
> operator PTR describes. */
>
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index befdec3ae0de..86ba4fd4fd1a 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -7041,15 +7041,6 @@ cp_free_lang_data (tree t)
> DECL_CHAIN (t) = NULL_TREE;
> }
>
> -/* Stub for c-common. Please keep in sync with c-decl.cc.
> - FIXME: If address space support is target specific, then this
> - should be a C target hook. But currently this is not possible,
> - because this function is called via REGISTER_TARGET_PRAGMAS. */
> -void
> -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/)
> -{
> -}
> -
> /* Return the number of operands in T that we care about for things like
> mangling. */
>
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v5 4/7] gcc/system.h: Allow for '#define INCLUDE_ITERATOR' to '#include <iterator>'
2026-07-09 16:36 ` [PATCH v5 4/7] gcc/system.h: Allow for '#define INCLUDE_ITERATOR' to '#include <iterator>' Arsen Arsenović
@ 2026-07-09 17:07 ` Andrea Pinski
0 siblings, 0 replies; 86+ messages in thread
From: Andrea Pinski @ 2026-07-09 17:07 UTC (permalink / raw)
To: Arsen Arsenović; +Cc: Jason Merrill, gcc-patches, Thomas Schwinge
On Thu, Jul 9, 2026 at 9:45 AM Arsen Arsenović <aarsenovic@baylibre.com> wrote:
>
> From: Thomas Schwinge <tschwinge@baylibre.com>
>
> This avoids:
>
> In file included from /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/bits/basic_ios.h:37:0,
> from /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/ios:44,
> from /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/ostream:38,
> from /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/iterator:64,
> from ../../source-gcc/gcc/[...]
> /gnu/store/jdibb0iw7n0y56pq3rvz9fmzb4j60acr-gcc-5.5.0/include/c++/bits/locale_facets.h:247:53: error: macro "toupper" passed 2 arguments, but takes just 1
> toupper(char_type *__lo, const char_type* __hi) const
> ^
> [...]
>
> gcc/
> * system.h [INCLUDE_ITERATOR]: '#include <iterator>'.
Ok.
> ---
> gcc/system.h | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/gcc/system.h b/gcc/system.h
> index 5b8323c28e51..f0fa062d469b 100644
> --- a/gcc/system.h
> +++ b/gcc/system.h
> @@ -228,6 +228,9 @@ extern int fprintf_unlocked (FILE *, const char *, ...);
> #ifdef INCLUDE_SSTREAM
> # include <sstream>
> #endif
> +#ifdef INCLUDE_ITERATOR
> +# include <iterator>
> +#endif
> # include <memory>
> # include <cstring>
> # include <initializer_list>
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v5 1/7] gcc/cp-tree: require that temp_override deduces on first constructor arg
2026-07-09 16:36 ` [PATCH v5 1/7] gcc/cp-tree: require that temp_override deduces on first constructor arg Arsen Arsenović
@ 2026-07-09 17:18 ` Jason Merrill
0 siblings, 0 replies; 86+ messages in thread
From: Jason Merrill @ 2026-07-09 17:18 UTC (permalink / raw)
To: Arsen Arsenović; +Cc: gcc-patches
On 7/9/26 12:36 PM, Arsen Arsenović wrote:
> This helps force a conversion from the second argument into whatever
> type the variable truly is.
>
> This change ended up being unnecessary, but it is probably useful for
> others anyway.
OK.
> gcc/cp/ChangeLog:
>
> * cp-tree.h (temp_override): In provided-value constructor,
> don't deduce based on provided value.
> ---
> gcc/cp/cp-tree.h | 38 +++++++++++++++++++-------------------
> 1 file changed, 19 insertions(+), 19 deletions(-)
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 5905236752bc..32865ba1ff3a 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -2256,25 +2256,6 @@ public:
> }
> };
>
> -/* RAII sentinel that saves the value of a variable, optionally
> - overrides it right away, and restores its value when the sentinel
> - id destructed. */
> -
> -template <typename T>
> -class temp_override
> -{
> - T& overridden_variable;
> - T saved_value;
> -public:
> - temp_override(T& var) : overridden_variable (var), saved_value (var) {}
> - temp_override(T& var, T overrider)
> - : overridden_variable (var), saved_value (var)
> - {
> - overridden_variable = overrider;
> - }
> - ~temp_override() { overridden_variable = saved_value; }
> -};
> -
> /* Wrapping a template parameter in type_identity_t hides it from template
> argument deduction. */
> #if __cpp_lib_type_identity
> @@ -2286,6 +2267,25 @@ template <typename T>
> using type_identity_t = typename type_identity<T>::type;
> #endif
>
> +/* RAII sentinel that saves the value of a variable, optionally
> + overrides it right away, and restores its value when the sentinel
> + id destructed. */
> +
> +template <typename T>
> +class temp_override
> +{
> + T& overridden_variable;
> + T saved_value;
> +public:
> + temp_override (T& var) : overridden_variable (var), saved_value (var) {}
> + temp_override (T& var, type_identity_t<T> overrider)
> + : overridden_variable (var), saved_value (var)
> + {
> + overridden_variable = overrider;
> + }
> + ~temp_override() { overridden_variable = saved_value; }
> +};
> +
> /* Object generator function for temp_override, so you don't need to write the
> type of the object as a template argument.
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v5 2/7] gcc/target.def: fix HOOK_PREFIX redefinition warning
2026-07-09 16:53 ` Andrea Pinski
@ 2026-07-09 22:02 ` Arsen Arsenović
0 siblings, 0 replies; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 22:02 UTC (permalink / raw)
To: Andrea Pinski; +Cc: Jason Merrill, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 1685 bytes --]
Andrea Pinski <andrew.pinski@oss.qualcomm.com> writes:
> Hmm, I am trying to figure out why is this needed.
> target.def is only included once in target.h.
> Are you changing that?
> Or is it because you include target.h after c-target.h ?
Indeed, that's precisely why:
g++ -std=c++14 -fno-PIE -c -DIN_GCC_FRONTEND -DIN_GCC_FRONTEND -g -DIN_GCC -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-error=narrowing -Wwrite-strings -Wcast-qual -Wmissing-format-attribute -Wconditionally-supported -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings -DHAVE_CONFIG_H -fno-PIE -I. -Ic-family -I../../gcc/gcc -I../../gcc/gcc/c-family -I../../gcc/gcc/../include -I../../gcc/gcc/../libcpp/include -I../../gcc/gcc/../libcody -I../../gcc/gcc/../libdecnumber -I../../gcc/gcc/../libdecnumber/bid -I../libdecnumber -I../../gcc/gcc/../libbacktrace -o c-family/c-format.o -MT c-family/c-format.o -MMD -MP -MF c-family/.deps/c-format.TPo ../../gcc/gcc/c-family/c-format.cc
In file included from ../../gcc/gcc/target.h:336,
from ../../gcc/gcc/c-family/c-common.h:47,
from ../../gcc/gcc/c-family/c-format.cc:25:
../../gcc/gcc/target.def:32:9: warning: ‘HOOK_PREFIX’ redefined
32 | #define HOOK_PREFIX "TARGET_ASM_"
| ^~~~~~~~~~~
In file included from ../../gcc/gcc/c-family/c-target.h:30,
from ../../gcc/gcc/c-family/c-format.cc:24:
../../gcc/gcc/c-family/c-target.def:28:9: note: this is the location of the previous definition
28 | #define HOOK_PREFIX "TARGET_"
| ^~~~~~~~~~~
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH v5 3/7] gcc/{c, cp, c-family}: move some address space handling into c-family
2026-07-09 16:56 ` [PATCH v5 3/7] gcc/{c, cp, c-family}: " Andrea Pinski
@ 2026-07-09 22:05 ` Arsen Arsenović
0 siblings, 0 replies; 86+ messages in thread
From: Arsen Arsenović @ 2026-07-09 22:05 UTC (permalink / raw)
To: Andrea Pinski; +Cc: Jason Merrill, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 734 bytes --]
Andrea Pinski <andrew.pinski@oss.qualcomm.com> writes:
> I don't think we normally have includes depend on compiling
> with/without checking.
> Maybe include target.h from c-target.h instead.
Hm, I didn't want to increase the TU size if not needed. I can also
just include it unconditionally - it isn't /that/ large. (and, indeed,
I can see the benefit; someone could accidentally depend on
CHECKING_P-included target.h when not CHECKING_P). If that seems
preferable, I'll drop the check (and analogously for INCLUDE_ITERATORS,
INCLUDE_ALGORITHMS).
I'm not sure if it's worth including target.h in c-target.h; the latter
is included in more files than just here (though also not too much).
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 430 bytes --]
^ permalink raw reply [flat|nested] 86+ messages in thread
end of thread, other threads:[~2026-07-09 22:06 UTC | newest]
Thread overview: 86+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-06 14:34 [RFC] c++: parser - Support for target address spaces in C++ Paul Iannetta
2022-10-06 17:34 ` Jason Merrill
2022-10-09 16:12 ` [PATCH] " Paul Iannetta
2022-10-10 19:20 ` Jason Merrill
2022-10-11 22:03 ` [PATCH v2] " Paul Iannetta
2022-10-12 1:49 ` Jason Merrill
2022-10-13 0:52 ` Paul Iannetta
2022-10-13 5:46 ` Jakub Jelinek
2022-10-13 15:14 ` Paul Iannetta
2022-10-13 15:02 ` Jason Merrill
2022-10-13 15:23 ` Paul Iannetta
2022-10-13 15:47 ` Jason Merrill
2022-10-13 16:02 ` Paul Iannetta
2022-10-13 19:41 ` Jason Merrill
2022-10-13 21:57 ` Paul Iannetta
2022-10-14 15:19 ` Jason Merrill
2022-10-18 7:37 ` [PATCH v3] " Paul Iannetta
2022-10-18 14:24 ` Jason Merrill
2022-10-18 17:01 ` Paul Iannetta
2022-10-19 18:55 ` Jason Merrill
2022-10-26 7:18 ` Paul Iannetta
2022-10-26 16:28 ` Jason Merrill
2022-11-10 15:42 ` [PATCH v4] " Paul Iannetta
2026-05-15 12:17 ` [REBASED] " Thomas Schwinge
2026-05-15 13:11 ` Paul IANNETTA
2026-06-18 9:35 ` Georg-Johann Lay
2026-06-18 15:02 ` Jason Merrill
2026-06-18 16:45 ` Georg-Johann Lay
2026-06-18 21:47 ` Jason Merrill
2026-06-19 8:50 ` Georg-Johann Lay
2026-06-21 9:27 ` Georg-Johann Lay
2026-06-21 10:54 ` Georg-Johann Lay
2026-06-22 15:24 ` Arsen Arsenović
2026-06-22 16:42 ` Jason Merrill
2026-06-22 23:07 ` Arsen Arsenović
2026-06-23 10:17 ` Georg-Johann Lay
2026-06-23 11:14 ` Arsen Arsenović
2026-06-23 12:17 ` Georg-Johann Lay
2026-05-15 15:34 ` Jason Merrill
2026-05-16 7:28 ` Paul IANNETTA
2026-05-16 10:58 ` Thomas Schwinge
2026-05-16 13:30 ` Georg-Johann Lay
2026-05-18 14:19 ` Thomas Schwinge
2026-05-18 15:05 ` Georg-Johann Lay
2026-05-18 18:49 ` Thomas Schwinge
2026-05-19 8:14 ` Georg-Johann Lay
2026-05-21 14:19 ` Paul IANNETTA
2026-05-21 19:27 ` Georg-Johann Lay
2026-05-21 12:18 ` Georg-Johann Lay
2026-05-21 14:26 ` Paul IANNETTA
2026-05-21 19:32 ` Georg-Johann Lay
2026-05-22 1:51 ` Jason Merrill
2026-06-16 18:28 ` Thomas Schwinge
2026-06-18 14:04 ` Jason Merrill
2026-06-20 18:05 ` Arsen Arsenović
2026-06-22 18:14 ` Jason Merrill
2026-06-23 19:49 ` Arsen Arsenović
2026-06-23 20:15 ` Jason Merrill
2026-07-09 16:36 ` [PATCH v5 0/7] Basic support for Named Address Spaces " Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 1/7] gcc/cp-tree: require that temp_override deduces on first constructor arg Arsen Arsenović
2026-07-09 17:18 ` Jason Merrill
2026-07-09 16:36 ` [PATCH v5 2/7] gcc/target.def: fix HOOK_PREFIX redefinition warning Arsen Arsenović
2026-07-09 16:53 ` Andrea Pinski
2026-07-09 22:02 ` Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 3/7] gcc/{c,cp,c-family}: move some address space handling into c-family Arsen Arsenović
2026-07-09 16:56 ` [PATCH v5 3/7] gcc/{c, cp, c-family}: " Andrea Pinski
2026-07-09 22:05 ` Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 4/7] gcc/system.h: Allow for '#define INCLUDE_ITERATOR' to '#include <iterator>' Arsen Arsenović
2026-07-09 17:07 ` Andrea Pinski
2026-07-09 16:36 ` [PATCH v5 5/7] gcc/c-family: implement addr_space_test, a helper for self-testing ASes Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 6/7] gcc/c++: implement basic support for Named Address Spaces in C++ [PR69549] Arsen Arsenović
2026-07-09 16:36 ` [PATCH v5 7/7] c++: make comp_ptr_ttypes_real slightly easier to understand Arsen Arsenović
2026-06-18 19:57 ` [REBASED] [PATCH v4] c++: parser - Support for target address spaces in C++ Thomas Schwinge
2026-06-18 21:49 ` Jason Merrill
2026-06-18 19:57 ` Thomas Schwinge
2026-06-18 21:53 ` Jason Merrill
2026-06-20 7:29 ` Georg-Johann Lay
2026-06-20 11:10 ` Arsen Arsenović
2026-06-20 12:37 ` Arsen Arsenović
2022-11-03 13:38 ` [PATCH v3] " Georg-Johann Lay
2022-11-10 14:08 ` Paul Iannetta
2022-11-10 16:40 ` Georg-Johann Lay
2022-11-14 17:55 ` Jason Merrill
2022-11-15 12:15 ` Georg-Johann Lay
2026-06-19 12:15 ` Georg-Johann Lay
2026-05-15 12:18 ` [PATCH v2] " Thomas Schwinge
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).