diff mbox series

[RFC] c++: parser - Support for target address spaces in C++

Message ID 20221006143400.es3u6ebqt3xkw6jp@ws2202.lin.mbt.kalray.eu
State New
Headers show
Series [RFC] c++: parser - Support for target address spaces in C++ | expand

Commit Message

Paul Iannetta Oct. 6, 2022, 2:34 p.m. UTC
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>

Comments

Jason Merrill Oct. 6, 2022, 5:34 p.m. UTC | #1
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.  */
> 
> 
> 
>
Paul Iannetta Oct. 9, 2022, 4:12 p.m. UTC | #2
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.  */
Jason Merrill Oct. 10, 2022, 7:20 p.m. UTC | #3
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.  */
> 
> 
> 
>
Paul Iannetta Oct. 11, 2022, 10:03 p.m. UTC | #4
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.  */
Jason Merrill Oct. 12, 2022, 1:49 a.m. UTC | #5
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.  */
> 
> 
> 
>
Paul Iannetta Oct. 13, 2022, 12:52 a.m. UTC | #6
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.  */
Jakub Jelinek Oct. 13, 2022, 5:46 a.m. UTC | #7
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
Jason Merrill Oct. 13, 2022, 3:02 p.m. UTC | #8
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.  */
> 
> 
> 
>
Paul Iannetta Oct. 13, 2022, 3:14 p.m. UTC | #9
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
Paul Iannetta Oct. 13, 2022, 3:23 p.m. UTC | #10
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.  */
> > 
> > 
> > 
> > 
> 
> 
> 
> 
> 
> 
>
Jason Merrill Oct. 13, 2022, 3:47 p.m. UTC | #11
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.  */
>>>
>>>
>>>
>>>
>>
>>
>>
>>
>>
>>
>>
> 
> 
> 
> 
>
Paul Iannetta Oct. 13, 2022, 4:02 p.m. UTC | #12
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.  */
> > > > 
> > > > 
> > > > 
> > > > 
> > > 
> > > 
> > > 
> > > 
> > > 
> > > 
> > > 
> > 
> > 
> > 
> > 
> > 
> 
> 
> 
> 
> 
> 
>
Jason Merrill Oct. 13, 2022, 7:41 p.m. UTC | #13
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.  */
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>
>>>
>>>
>>>
>>>
>>
>>
>>
>>
>>
>>
>>
> 
> 
> 
> 
> 
>
Paul Iannetta Oct. 13, 2022, 9:57 p.m. UTC | #14
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.  */
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > 
> > > > > 
> > > > > 
> > > > > 
> > > > > 
> > > > > 
> > > > > 
> > > > 
> > > > 
> > > > 
> > > > 
> > > > 
> > > 
> > > 
> > > 
> > > 
> > > 
> > > 
> > > 
> > 
> > 
> > 
> > 
> > 
> > 
> 
> 
> 
> 
> 
> 
>
Jason Merrill Oct. 14, 2022, 3:19 p.m. UTC | #15
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.  */
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>
>>
>>
>>
>>
>>
>>
> 
> 
> 
> 
> 
>
Paul Iannetta Oct. 18, 2022, 7:37 a.m. UTC | #16
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.  */
Jason Merrill Oct. 18, 2022, 2:24 p.m. UTC | #17
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.  */
> 
> 
> 
>
Paul Iannetta Oct. 18, 2022, 5:01 p.m. UTC | #18
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.  */
Jason Merrill Oct. 19, 2022, 6:55 p.m. UTC | #19
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.  */
> 
> 
> 
> 
> 
>
Paul Iannetta Oct. 26, 2022, 7:18 a.m. UTC | #20
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.  */
Jason Merrill Oct. 26, 2022, 4:28 p.m. UTC | #21
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.  */
> 
> 
> 
>
Georg-Johann Lay Nov. 3, 2022, 1:38 p.m. UTC | #22
> [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
Paul Iannetta Nov. 10, 2022, 2:08 p.m. UTC | #23
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
>
Paul Iannetta Nov. 10, 2022, 3:42 p.m. UTC | #24
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.  */
Georg-Johann Lay Nov. 10, 2022, 4:40 p.m. UTC | #25
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.)
Jason Merrill Nov. 14, 2022, 5:55 p.m. UTC | #26
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.)
>
Georg-Johann Lay Nov. 15, 2022, 12:15 p.m. UTC | #27
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.)
>>
>
diff mbox series

Patch

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.  */