C, C++, OpenMP: Add 'has_device_addr' clause to 'target' construct

Message ID b245a935-a1b4-d668-2b47-11ab20f51785@codesourcery.com
State New
Headers
Series C, C++, OpenMP: Add 'has_device_addr' clause to 'target' construct |

Commit Message

Marcel Vollweiler Oct. 18, 2021, 4:17 p.m. UTC
  Hi,

This patch adds the 'has_device_addr' clause to the OpenMP 'target'
construct which was introduced in OpenMP 5.1:

"The has_device_addr clause was added to the target construct to allow
access to variables or array sections that already have a device
address" (OpenMP 5.1 Specification, p. 669)

"The has_device_addr clause indicates that its list items already have
device addresses and therefore they may be directly accessed from a
target device. If the device address of a list item is not for the
device on which the target region executes, accessing the list item
inside the region results in unspecified behavior. The list items may
include array sections." (OpenMP 5.1 Specification, p. 200)

There are some restrictions for 'has_device_addr' (p. 202f):

1. "A list item may not be specified in both an is_device_ptr clause and
a has_device_addr clause on the directive."

2. "A list item that appears in an is_device_ptr or a has_device_addr
clause must not be specified in any data-sharing attribute clause on the
same target construct."

3. "A list item that appears in a has_device_addr clause must have a
valid device address for the device data environment."

4. As discussed on the omp-lang mailing list
(https://mailman.openmp.org/mailman/private/omp-lang/2021/017982.html),
has_device_addr is a data-sharing attribute clause (that is not yet
stated explicitly but will be corrected in OpenMP 5.2) and should not be
used together with the map clause on the same construct.

Similar restrictions hold also for the 'is_device_ptr' clause, so I
updated the code and added tests for that clause, too.

I tested the patch without regressions on powerpc64le-linux-gnu with
nvptx offloading and x86_64-linux-gnu with amdgcn offloading.

This patch only considers C/C++. The changes for Fortran will be
submitted separately later.

Marcel
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
C, C++, OpenMP: Add 'has_device_addr' clause to 'target' construct.

This patch adds the 'has_device_addr' clause to the OpenMP 'target' construct
which was introduced in OpenMP 5.1.

gcc/c-family/ChangeLog:

	* c-omp.c (c_omp_split_clauses): Add OMP_CLAUSE_HAS_DEVICE_ADDR case.
	* c-pragma.h (enum pragma_kind): Add 5.1 in comment.
	(enum pragma_omp_clause): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR.

gcc/c/ChangeLog:

	* c-parser.c (c_parser_omp_clause_name): Parse 'has_device_addr' clause.
	(c_parser_omp_clause_has_device_addr): Added.
	(c_parser_omp_all_clauses): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR case.
	(c_parser_omp_target_exit_data): Add HAS_DEVICE_ADDR to OMP_CLAUSE_MASK.
	* c-typeck.c (c_finish_omp_clauses): Add check that has_device_addr and 
	is_device_ptr do not appear together with map.

gcc/cp/ChangeLog:

	* parser.c (cp_parser_omp_clause_name): Parse 'has_device_addr' clause.
	(cp_parser_omp_all_clauses): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR case.
	(cp_parser_omp_target_update): Add HAS_DEVICE_ADDR to OMP_CLAUSE_MASK.
	* pt.c (tsubst_omp_clauses): Add cases for OMP_CLAUSE_HAS_DEVICE_ADDR.
	* semantics.c (finish_omp_clauses): Add check that has_device_addr and
	is_device_ptr do not appear together with map.

gcc/ChangeLog:

	* gimplify.c (gimplify_scan_omp_clauses): Add OMP_CLAUSE_HAS_DEVICE_ADDR case.
	(gimplify_adjust_omp_clauses): Likewise.
	* omp-low.c (scan_sharing_clauses): Add lowering for has_device_addr clause.
	(lower_omp_target): Likewise.
	* tree-core.h (enum omp_clause_code): Update enum.
	* tree-nested.c (convert_nonlocal_omp_clauses): Add has_device_addr support.
	(convert_local_omp_clauses): Likewise.
	* tree-pretty-print.c (dump_omp_clause): Likewise.
	* tree.c: Update omp_clause_num_ops array.

libgomp/ChangeLog:

	* libgomp.texi: Updated entry for 'has-device-addr'.
	* testsuite/libgomp.c++/target-has-device-addr-2.C: New test.
	* testsuite/libgomp.c-c++-common/target-has-device-addr-1.c: New test.
	* testsuite/libgomp.c-c++-common/target-has-device-addr-3.c: New test.
	* testsuite/libgomp.c/target-has-device-addr-4.c: New test.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/clauses-1.c: Add has_device_addr to test cases.
	* g++.dg/gomp/attrs-1.C: Likewise.
	* g++.dg/gomp/attrs-2.C: Likewise.
	* c-c++-common/gomp/target-has-device-addr-1.c: New test.
	* c-c++-common/gomp/target-is-device-ptr.c: New test.
  

Comments

Jakub Jelinek Oct. 20, 2021, 12:38 p.m. UTC | #1
On Mon, Oct 18, 2021 at 06:17:20PM +0200, Marcel Vollweiler wrote:
> @@ -14255,6 +14257,16 @@ c_parser_omp_clause_use_device_addr (c_parser *parser, tree list)
>  				       list);
>  }
>  
> +/* OpenMP 5.1:
> +   has_device_addr ( variable-list ) */
> +
> +static tree
> +c_parser_omp_clause_has_device_addr (c_parser *parser, tree list)
> +{
> +  return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
> +				       list);
> +}
> +
>  /* OpenMP 4.5:
>     is_device_ptr ( variable-list ) */
>  
> @@ -16945,6 +16957,10 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
>  	  clauses = c_parser_omp_clause_use_device_addr (parser, clauses);
>  	  c_name = "use_device_addr";
>  	  break;
> +	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
> +	  clauses = c_parser_omp_clause_has_device_addr (parser, clauses);
> +	  c_name = "has_device_addr";
> +	  break;
>  	case PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR:
>  	  clauses = c_parser_omp_clause_is_device_ptr (parser, clauses);
>  	  c_name = "is_device_ptr";
> @@ -20926,7 +20942,8 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
>  	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)	\
>  	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)	\
>  	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
> -	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
>  
>  static bool
>  c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p)

OpenMP 5.1 in [200:6-9] says:
The has_device_addr clause indicates ... The list items may include array sections.

This means in addition to the c-parser.c and parser.c changes you've done,
at least c_parser_omp_variable_list needs to change to include
OMP_CLAUSE_HAS_DEVICE_ADDR among
            case OMP_CLAUSE_AFFINITY:
            case OMP_CLAUSE_DEPEND:
            case OMP_CLAUSE_REDUCTION:
            case OMP_CLAUSE_IN_REDUCTION:
            case OMP_CLAUSE_TASK_REDUCTION:
clauses (similarly for C++) and then {,c_}finish_omp_clauses needs to handle
it similarly to other clauses that can have array sections.
As it is a data sharing clause, I think the closest model (e.g. for
handle_omp_array_sections* purposes) is OMP_CLAUSE_*REDUCTION.
Then even the case when OMP_CLAUSE_DECL of the clause needs handling
similarly to other clauses that accept array sections.

> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> index 0aac978..d677592 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -14054,7 +14054,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
>  {
>    bitmap_head generic_head, firstprivate_head, lastprivate_head;
>    bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
> -  bitmap_head oacc_reduction_head;
> +  bitmap_head oacc_reduction_head, has_device_addr_head, is_device_ptr_head;

I'd prefer not to add new bitmaps unless necessary, can't the clause use the
same bitmap together with is_device_ptr clause?  One can't specify something
both as is_device_ptr and has_device_addr at the same time...

> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -36145,7 +36145,9 @@ cp_parser_omp_clause_name (cp_parser *parser)
>  	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
>  	  break;
>  	case 'h':
> -	  if (!strcmp ("hint", p))
> +	  if (!strcmp ("has_device_addr", p))
> +	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
> +	  else if (!strcmp ("hint", p))
>  	    result = PRAGMA_OMP_CLAUSE_HINT;
>  	  else if (!strcmp ("host", p))
>  	    result = PRAGMA_OACC_CLAUSE_HOST;
> @@ -39830,6 +39832,11 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
>  					    clauses);
>  	  c_name = "is_device_ptr";
>  	  break;
> +	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
> +	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
> +					    clauses);
> +	  c_name = "has_device_addr";
> +	  break;
>  	case PRAGMA_OMP_CLAUSE_IF:
>  	  clauses = cp_parser_omp_clause_if (parser, clauses, token->location,
>  					     true);
> @@ -44005,7 +44012,8 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
>  	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)	\
>  	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)	\
>  	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
> -	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
> +	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
>  
>  static bool
>  cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,

For C++, another thing is whether the clause should accept also non-static
data members in non-static member functions.
5.1 had in List Item Privatization
"A variable that is part of another variable (as an array or structure element) cannot be privatized
except if the data-sharing attribute clause is associated with a construct within a class non-static
member function and the variable is an accessible data member of the object for which the
non-static member function is invoked."
but I believe that hopefully that can't be applied to has_device_addr which
wasn't declared as data sharing clause (and it really is not in the sense
that it doesn't privatize anything).
But 5.2 moves that stuff to general spot where it applies to all clauses:
"Unless otherwise specified, a variable that is part of another variable (as an array element or a
structure element) cannot be a variable list item, an extended list item or locator list item
except if the list appears on a clause that is associated with a construct within a class
non-static member function and the variable is an accessible data member of the object for
which the non-static member function is invoked."
has_device_addr has the restriction that the list item already has to have
device address, so I bet the whole class object would need to appear on the
device already, but still it is unclear to me what it would mean there.
Let's ignore that for now.

So, for additional testsuite coverage, e.g.

> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
> @@ -0,0 +1,24 @@
> +/* Testing the 'has_device_addr' clause on the target construct without
> +   enclosing 'target data' construct. */
> +
> +#include <omp.h>
> +
> +int
> +main ()
> +{
> +  int *dp = (int*)omp_target_alloc(sizeof(int), 0);

Allocate 30*sizeof(int) and arrange

> +
> +  #pragma omp target is_device_ptr(dp)
> +    *dp = 42;
> +
> +  int &x = *dp;

For x to be int (&x)[30];
and then test all of has_device_addr(x), has_device_addr(x[3]),
has_device_addr(x[0:22]), has_device_addr(x[17:]) etc.
Similarly for cases with use_device_addr.

	Jakub
  
Marcel Vollweiler Nov. 15, 2021, 9:03 a.m. UTC | #2
Hi Jakub,

Am 20.10.2021 um 14:38 schrieb Jakub Jelinek:
> On Mon, Oct 18, 2021 at 06:17:20PM +0200, Marcel Vollweiler wrote:
>> @@ -14255,6 +14257,16 @@ c_parser_omp_clause_use_device_addr (c_parser *parser, tree list)
>>                                     list);
>>   }
>>
>> +/* OpenMP 5.1:
>> +   has_device_addr ( variable-list ) */
>> +
>> +static tree
>> +c_parser_omp_clause_has_device_addr (c_parser *parser, tree list)
>> +{
>> +  return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
>> +                                   list);
>> +}
>> +
>>   /* OpenMP 4.5:
>>      is_device_ptr ( variable-list ) */
>>
>> @@ -16945,6 +16957,10 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
>>        clauses = c_parser_omp_clause_use_device_addr (parser, clauses);
>>        c_name = "use_device_addr";
>>        break;
>> +    case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
>> +      clauses = c_parser_omp_clause_has_device_addr (parser, clauses);
>> +      c_name = "has_device_addr";
>> +      break;
>>      case PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR:
>>        clauses = c_parser_omp_clause_is_device_ptr (parser, clauses);
>>        c_name = "is_device_ptr";
>> @@ -20926,7 +20942,8 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
>>      | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)     \
>>      | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)   \
>>      | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION) \
>> -    | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
>>
>>   static bool
>>   c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p)
>
> OpenMP 5.1 in [200:6-9] says:
> The has_device_addr clause indicates ... The list items may include array sections.
>
> This means in addition to the c-parser.c and parser.c changes you've done,
> at least c_parser_omp_variable_list needs to change to include
> OMP_CLAUSE_HAS_DEVICE_ADDR among
>              case OMP_CLAUSE_AFFINITY:
>              case OMP_CLAUSE_DEPEND:
>              case OMP_CLAUSE_REDUCTION:
>              case OMP_CLAUSE_IN_REDUCTION:
>              case OMP_CLAUSE_TASK_REDUCTION:
> clauses (similarly for C++) and then {,c_}finish_omp_clauses needs to handle
> it similarly to other clauses that can have array sections.
> As it is a data sharing clause, I think the closest model (e.g. for
> handle_omp_array_sections* purposes) is OMP_CLAUSE_*REDUCTION.
> Then even the case when OMP_CLAUSE_DECL of the clause needs handling
> similarly to other clauses that accept array sections.
>

The handling for array sections is added now. The basic idea of the
implementation is that it seems to be sufficient to consider the base
variable. I'm not completely sure but I think access to memory which is
not specified in has_device_addr cannot be prevented at all and my
reading of the OpenMP 5.1 specification is that the behavour is
undefined for access to memory that is not specified in has_device_addr.
Thus, limitation of an array to some section does not prevent for using
parts of the array outside the specified array section.

Moreover, cases like

   #pragma omp target data map(x[2:3]) use_device_addr(x)
     #pragma omp target has_device_addr(x[2:3])

or

   #pragma omp target data map(x[2:3]) use_device_addr(x[2:3])
     #pragma omp target has_device_addr(x[2:3])

do not work yet, since the use_device_addr clause does currently not
support array sections.

>> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
>> index 0aac978..d677592 100644
>> --- a/gcc/c/c-typeck.c
>> +++ b/gcc/c/c-typeck.c
>> @@ -14054,7 +14054,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
>>   {
>>     bitmap_head generic_head, firstprivate_head, lastprivate_head;
>>     bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
>> -  bitmap_head oacc_reduction_head;
>> +  bitmap_head oacc_reduction_head, has_device_addr_head, is_device_ptr_head;
>
> I'd prefer not to add new bitmaps unless necessary, can't the clause use the
> same bitmap together with is_device_ptr clause?  One can't specify something
> both as is_device_ptr and has_device_addr at the same time...
>

Both bitmaps are now combined to one. I previously seperated the bitmaps
in order to have a clearer naming. Now I called it 'is_on_device' to be
more general than with is_device_ptr or has_device_addr. However, other
suggestions are welcome :)

>> --- a/gcc/cp/parser.c
>> +++ b/gcc/cp/parser.c
>> @@ -36145,7 +36145,9 @@ cp_parser_omp_clause_name (cp_parser *parser)
>>          result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
>>        break;
>>      case 'h':
>> -      if (!strcmp ("hint", p))
>> +      if (!strcmp ("has_device_addr", p))
>> +        result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
>> +      else if (!strcmp ("hint", p))
>>          result = PRAGMA_OMP_CLAUSE_HINT;
>>        else if (!strcmp ("host", p))
>>          result = PRAGMA_OACC_CLAUSE_HOST;
>> @@ -39830,6 +39832,11 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
>>                                          clauses);
>>        c_name = "is_device_ptr";
>>        break;
>> +    case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
>> +      clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
>> +                                        clauses);
>> +      c_name = "has_device_addr";
>> +      break;
>>      case PRAGMA_OMP_CLAUSE_IF:
>>        clauses = cp_parser_omp_clause_if (parser, clauses, token->location,
>>                                           true);
>> @@ -44005,7 +44012,8 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
>>      | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)   \
>>      | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)     \
>>      | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION) \
>> -    | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
>> +    | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
>>
>>   static bool
>>   cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
>
> For C++, another thing is whether the clause should accept also non-static
> data members in non-static member functions.
> 5.1 had in List Item Privatization
> "A variable that is part of another variable (as an array or structure element) cannot be privatized
> except if the data-sharing attribute clause is associated with a construct within a class non-static
> member function and the variable is an accessible data member of the object for which the
> non-static member function is invoked."
> but I believe that hopefully that can't be applied to has_device_addr which
> wasn't declared as data sharing clause (and it really is not in the sense
> that it doesn't privatize anything).
> But 5.2 moves that stuff to general spot where it applies to all clauses:
> "Unless otherwise specified, a variable that is part of another variable (as an array element or a
> structure element) cannot be a variable list item, an extended list item or locator list item
> except if the list appears on a clause that is associated with a construct within a class
> non-static member function and the variable is an accessible data member of the object for
> which the non-static member function is invoked."
> has_device_addr has the restriction that the list item already has to have
> device address, so I bet the whole class object would need to appear on the
> device already, but still it is unclear to me what it would mean there.
> Let's ignore that for now.
>
> So, for additional testsuite coverage, e.g.
>
>> --- /dev/null
>> +++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
>> @@ -0,0 +1,24 @@
>> +/* Testing the 'has_device_addr' clause on the target construct without
>> +   enclosing 'target data' construct. */
>> +
>> +#include <omp.h>
>> +
>> +int
>> +main ()
>> +{
>> +  int *dp = (int*)omp_target_alloc(sizeof(int), 0);
>
> Allocate 30*sizeof(int) and arrange
>
>> +
>> +  #pragma omp target is_device_ptr(dp)
>> +    *dp = 42;
>> +
>> +  int &x = *dp;
>
> For x to be int (&x)[30];
> and then test all of has_device_addr(x), has_device_addr(x[3]),
> has_device_addr(x[0:22]), has_device_addr(x[17:]) etc.
> Similarly for cases with use_device_addr.

Some test cases were added. I'm not sure if array sections should work
on reference types? From the code point of view,
'handle_omp_array_sections_1' rejects explicitly everything which is not
of type array or pointer.

>
>       Jakub
>

Thanks,

Marcel
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
C, C++, OpenMP: Add 'has_device_addr' clause to 'target' construct.

This patch adds the 'has_device_addr' clause to the OpenMP 'target' construct
which was introduced in OpenMP 5.1.

gcc/c-family/ChangeLog:

	* c-omp.c (c_omp_split_clauses): Add OMP_CLAUSE_HAS_DEVICE_ADDR case.
	* c-pragma.h (enum pragma_kind): Add 5.1 in comment.
	(enum pragma_omp_clause): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR.

gcc/c/ChangeLog:

	* c-parser.c (c_parser_omp_clause_name): Parse 'has_device_addr' clause.
	(c_parser_omp_variable_list): Handle array sections.
	(c_parser_omp_clause_has_device_addr): Added.
	(c_parser_omp_all_clauses): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR case.
	(c_parser_omp_target_exit_data): Add HAS_DEVICE_ADDR to OMP_CLAUSE_MASK.
	* c-typeck.c (handle_omp_array_sections): Handle clause restrictions.
	(c_finish_omp_clauses): Handle array sections.

gcc/cp/ChangeLog:

	* parser.c (cp_parser_omp_clause_name): Parse 'has_device_addr' clause.
	(cp_parser_omp_var_list_no_open): Handle array sections.
	(cp_parser_omp_all_clauses): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR case.
	(cp_parser_omp_target_update): Add HAS_DEVICE_ADDR to OMP_CLAUSE_MASK.
	* pt.c (tsubst_omp_clauses): Add cases for OMP_CLAUSE_HAS_DEVICE_ADDR.
	* semantics.c (handle_omp_array_sections): Handle clause restrictions.
	(finish_omp_clauses): Handle array sections.

gcc/ChangeLog:

	* gimplify.c (gimplify_scan_omp_clauses): Add OMP_CLAUSE_HAS_DEVICE_ADDR cases
	and handle array sections.
	(gimplify_adjust_omp_clauses): Add OMP_CLAUSE_HAS_DEVICE_ADDR case.
	* omp-low.c (scan_sharing_clauses): Handle OMP_CLAUSE_HAS_DEVICE_ADDR.
	(lower_omp_target): Same.
	* tree-core.h (enum omp_clause_code): Same.
	* tree-nested.c (convert_nonlocal_omp_clauses): Same.
	(convert_local_omp_clauses): Same.
	* tree-pretty-print.c (dump_omp_clause): Same.
	* tree.c: Same.

libgomp/ChangeLog:

	* libgomp.texi:
	* testsuite/libgomp.c++/target-has-device-addr-2.C: New test.
	* testsuite/libgomp.c++/target-has-device-addr-4.C: New test.
	* testsuite/libgomp.c-c++-common/target-has-device-addr-1.c: New test.
	* testsuite/libgomp.c/target-has-device-addr-3.c: New test.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/clauses-1.c: Added has_device_addr to test cases.
	* g++.dg/gomp/attrs-1.C: Added has_device_addr to test cases.
	* g++.dg/gomp/attrs-2.C: Added has_device_addr to test cases.
	* c-c++-common/gomp/target-has-device-addr-1.c: New test.
	* c-c++-common/gomp/target-is-device-ptr.c: New test.

diff --git a/gcc/c-family/c-omp.c b/gcc/c-family/c-omp.c
index fad0606..2f38b38 100644
--- a/gcc/c-family/c-omp.c
+++ b/gcc/c-family/c-omp.c
@@ -1862,6 +1862,7 @@ c_omp_split_clauses (location_t loc, enum tree_code code,
 	case OMP_CLAUSE_DEVICE:
 	case OMP_CLAUSE_MAP:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_DEFAULTMAP:
 	case OMP_CLAUSE_DEPEND:
 	  s = C_OMP_CLAUSE_SPLIT_TARGET;
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 0c5b07a..03baacd 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -89,8 +89,8 @@ enum pragma_kind {
 };
 
 
-/* All clauses defined by OpenACC 2.0, and OpenMP 2.5, 3.0, 3.1, 4.0, 4.5
-   and 5.0.  Used internally by both C and C++ parsers.  */
+/* All clauses defined by OpenACC 2.0, and OpenMP 2.5, 3.0, 3.1, 4.0, 4.5, 5.0,
+   and 5.1.  Used internally by both C and C++ parsers.  */
 enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_NONE = 0,
 
@@ -114,6 +114,7 @@ enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_FOR,
   PRAGMA_OMP_CLAUSE_FROM,
   PRAGMA_OMP_CLAUSE_GRAINSIZE,
+  PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR,
   PRAGMA_OMP_CLAUSE_HINT,
   PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OMP_CLAUSE_IN_REDUCTION,
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 80dd61d..a70302d 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -12746,7 +12746,9 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
 	  break;
 	case 'h':
-	  if (!strcmp ("hint", p))
+	  if (!strcmp ("has_device_addr", p))
+	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  else if (!strcmp ("hint", p))
 	    result = PRAGMA_OMP_CLAUSE_HINT;
 	  else if (!strcmp ("host", p))
 	    result = PRAGMA_OACC_CLAUSE_HOST;
@@ -13128,6 +13130,7 @@ c_parser_omp_variable_list (c_parser *parser,
 	    case OMP_CLAUSE_REDUCTION:
 	    case OMP_CLAUSE_IN_REDUCTION:
 	    case OMP_CLAUSE_TASK_REDUCTION:
+	    case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	      while (c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
 		{
 		  tree low_bound = NULL_TREE, length = NULL_TREE;
@@ -14255,6 +14258,16 @@ c_parser_omp_clause_use_device_addr (c_parser *parser, tree list)
 				       list);
 }
 
+/* OpenMP 5.1:
+   has_device_addr ( variable-list ) */
+
+static tree
+c_parser_omp_clause_has_device_addr (c_parser *parser, tree list)
+{
+  return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
+				       list);
+}
+
 /* OpenMP 4.5:
    is_device_ptr ( variable-list ) */
 
@@ -16945,6 +16958,10 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_omp_clause_use_device_addr (parser, clauses);
 	  c_name = "use_device_addr";
 	  break;
+	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  clauses = c_parser_omp_clause_has_device_addr (parser, clauses);
+	  c_name = "has_device_addr";
+	  break;
 	case PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR:
 	  clauses = c_parser_omp_clause_is_device_ptr (parser, clauses);
 	  c_name = "is_device_ptr";
@@ -20926,7 +20943,8 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
-	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
 
 static bool
 c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p)
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 782414f..4f238a2 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -13765,6 +13765,8 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	}
       first = c_fully_fold (first, false, NULL);
       OMP_CLAUSE_DECL (c) = first;
+      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	return false;
       if (size)
 	size = c_fully_fold (size, false, NULL);
       OMP_CLAUSE_SIZE (c) = size;
@@ -14071,7 +14073,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 {
   bitmap_head generic_head, firstprivate_head, lastprivate_head;
   bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
-  bitmap_head oacc_reduction_head;
+  bitmap_head oacc_reduction_head, is_on_device_head;
   tree c, t, type, *pc;
   tree simdlen = NULL_TREE, safelen = NULL_TREE;
   bool branch_seen = false;
@@ -14106,6 +14108,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   /* If ort == C_ORT_OMP used as nontemporal_head or use_device_xxx_head
      instead and for ort == C_ORT_OMP_TARGET used as in_reduction_head.  */
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
+  bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
   if (ort & C_ORT_ACC)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
@@ -14534,7 +14537,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qE appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+	  else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
@@ -15099,7 +15104,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		   || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -15184,6 +15190,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
 	  if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
@@ -15204,6 +15212,23 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  goto check_dup_generic;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  t = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (t) == TREE_LIST)
+	    if (handle_omp_array_sections (c, ort))
+	      remove = true;
+	    else
+	      {
+		t = OMP_CLAUSE_DECL (c);
+		while (TREE_CODE (t) == ARRAY_REF)
+		  t = TREE_OPERAND (t, 0);
+	      }
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
+	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
+	    c_mark_addressable (t);
+	  goto check_dup_generic_t;
+
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  t = OMP_CLAUSE_DECL (c);
 	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 32de97b..17da0b7 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -36159,7 +36159,9 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
 	  break;
 	case 'h':
-	  if (!strcmp ("hint", p))
+	  if (!strcmp ("has_device_addr", p))
+	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  else if (!strcmp ("hint", p))
 	    result = PRAGMA_OMP_CLAUSE_HINT;
 	  else if (!strcmp ("host", p))
 	    result = PRAGMA_OACC_CLAUSE_HOST;
@@ -36450,6 +36452,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	    case OMP_CLAUSE_REDUCTION:
 	    case OMP_CLAUSE_IN_REDUCTION:
 	    case OMP_CLAUSE_TASK_REDUCTION:
+	    case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	      while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
 		{
 		  tree low_bound = NULL_TREE, length = NULL_TREE;
@@ -39844,6 +39847,11 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
 					    clauses);
 	  c_name = "is_device_ptr";
 	  break;
+	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
+					    clauses);
+	  c_name = "has_device_addr";
+	  break;
 	case PRAGMA_OMP_CLAUSE_IF:
 	  clauses = cp_parser_omp_clause_if (parser, clauses, token->location,
 					     true);
@@ -44019,7 +44027,8 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
-	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
 
 static bool
 cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b2916f8..50ddd93 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -17431,6 +17431,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_INCLUSIVE:
 	case OMP_CLAUSE_EXCLUSIVE:
 	  OMP_CLAUSE_DECL (nc)
@@ -17570,6 +17571,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_INCLUSIVE:
 	  case OMP_CLAUSE_EXCLUSIVE:
 	  case OMP_CLAUSE_ALLOCATE:
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 2443d03..be9303b 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5602,6 +5602,8 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	      return false;
 	    }
 	  OMP_CLAUSE_DECL (c) = first;
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    return false;
 	  OMP_CLAUSE_SIZE (c) = size;
 	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
 	      || (TREE_CODE (t) == COMPONENT_REF
@@ -6607,7 +6609,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 {
   bitmap_head generic_head, firstprivate_head, lastprivate_head;
   bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
-  bitmap_head oacc_reduction_head;
+  bitmap_head oacc_reduction_head, is_on_device_head;
   tree c, t, *pc;
   tree safelen = NULL_TREE;
   bool branch_seen = false;
@@ -6639,6 +6641,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   /* If ort == C_ORT_OMP used as nontemporal_head or use_device_xxx_head
      instead and for ort == C_ORT_OMP_TARGET used as in_reduction_head.  */
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
+  bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
   if (ort & C_ORT_ACC)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
@@ -6937,7 +6940,9 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+	  else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
@@ -8059,7 +8064,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		   || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -8313,6 +8319,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	  field_ok = (ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP;
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
 	  if (!type_dependent_expression_p (t))
 	    {
 	      tree type = TREE_TYPE (t);
@@ -8342,6 +8350,23 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  goto check_dup_generic;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  t = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (t) == TREE_LIST)
+	    if (handle_omp_array_sections (c, ort))
+	      remove = true;
+	    else
+	      {
+		t = OMP_CLAUSE_DECL (c);
+		while (TREE_CODE (t) == ARRAY_REF)
+		  t = TREE_OPERAND (t, 0);
+	      }
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
+	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
+	    cxx_mark_addressable (t);
+	  goto check_dup_generic_t;
+
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  field_ok = true;
 	  t = OMP_CLAUSE_DECL (c);
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index c2ab96e..107e272 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -10031,6 +10031,15 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  flags = GOVD_EXPLICIT;
 	  goto do_add;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  decl = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (decl) == ARRAY_REF)
+	    {
+	      flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT;
+	      while (TREE_CODE (decl) == ARRAY_REF)
+		decl = TREE_OPERAND (decl, 0);
+	      goto do_add_decl;
+	    }
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT;
 	  goto do_add;
@@ -11459,6 +11468,7 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	case OMP_CLAUSE_DETACH:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_ASYNC:
 	case OMP_CLAUSE_WAIT:
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index f58a191..ad25ed9 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -1375,7 +1375,8 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	  decl = OMP_CLAUSE_DECL (c);
 	do_private:
 	  if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
-	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR
+	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      && is_gimple_omp_offloaded (ctx->stmt))
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE)
@@ -1383,7 +1384,8 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 		  by_ref = !omp_privatize_by_reference (decl);
 		  install_var_field (decl, by_ref, 3, ctx);
 		}
-	      else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+	      else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+		       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		install_var_field (decl, true, 3, ctx);
 	      else
 		install_var_field (decl, false, 3, ctx);
@@ -1452,6 +1454,11 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	  install_var_local (decl, ctx);
 	  break;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  decl = OMP_CLAUSE_DECL (c);
+	  decl = get_base_address (decl);
+	  goto do_private;
+
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  decl = OMP_CLAUSE_DECL (c);
 	  goto do_private;
@@ -1729,12 +1736,17 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	case OMP_CLAUSE_FIRSTPRIVATE:
 	case OMP_CLAUSE_PRIVATE:
 	case OMP_CLAUSE_LINEAR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  decl = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    decl = get_base_address (decl);
+
 	  if (is_variable_sized (decl))
 	    {
 	      if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
-		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		  && is_gimple_omp_offloaded (ctx->stmt))
 		{
 		  tree decl2 = DECL_VALUE_EXPR (decl);
@@ -12813,8 +12825,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
       case OMP_CLAUSE_USE_DEVICE_PTR:
       case OMP_CLAUSE_USE_DEVICE_ADDR:
+      case OMP_CLAUSE_HAS_DEVICE_ADDR:
       case OMP_CLAUSE_IS_DEVICE_PTR:
 	var = OMP_CLAUSE_DECL (c);
+	if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	  var = get_base_address (var);
 	map_cnt++;
 	if (is_variable_sized (var))
 	  {
@@ -12829,7 +12844,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    SET_DECL_VALUE_EXPR (new_var, x);
 	    DECL_HAS_VALUE_EXPR_P (new_var) = 1;
 	  }
-	else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		  && !omp_privatize_by_reference (var)
 		  && !omp_is_allocatable_or_ptr (var)
 		  && !lang_hooks.decls.omp_array_data (var, true))
@@ -13282,17 +13298,22 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
 	    ovar = OMP_CLAUSE_DECL (c);
+	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	      ovar = get_base_address (ovar);
 	    var = lookup_decl_in_outer_ctx (ovar, ctx);
 
 	    if (lang_hooks.decls.omp_array_data (ovar, true))
 	      {
-		tkind = (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		tkind = ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+			  && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 			 ? GOMP_MAP_USE_DEVICE_PTR : GOMP_MAP_FIRSTPRIVATE_INT);
 		x = build_sender_ref ((splay_tree_key) &DECL_NAME (ovar), ctx);
 	      }
-	    else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR)
+	    else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		     && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      {
 		tkind = GOMP_MAP_USE_DEVICE_PTR;
 		x = build_sender_ref ((splay_tree_key) &DECL_UID (ovar), ctx);
@@ -13314,7 +13335,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    type = TREE_TYPE (ovar);
 	    if (lang_hooks.decls.omp_array_data (ovar, true))
 	      var = lang_hooks.decls.omp_array_data (ovar, false);
-	    else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	    else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		      && !omp_privatize_by_reference (ovar)
 		      && !omp_is_allocatable_or_ptr (ovar))
 		     || TREE_CODE (type) == ARRAY_TYPE)
@@ -13329,6 +13351,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		    if (POINTER_TYPE_P (type)
 			&& TREE_CODE (type) != ARRAY_TYPE
 			&& ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_USE_DEVICE_ADDR
+			    && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR
 			    && !omp_is_allocatable_or_ptr (ovar))
 			   || (omp_privatize_by_reference (ovar)
 			       && omp_is_allocatable_or_ptr (ovar))))
@@ -13526,6 +13549,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    break;
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
 	    tree new_var;
 	    gimple_seq assign_body;
@@ -13536,12 +13560,17 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    var = OMP_CLAUSE_DECL (c);
 	    is_array_data = lang_hooks.decls.omp_array_data (var, true) != NULL;
 
-	    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR)
+	    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      x = build_sender_ref (is_array_data
 				    ? (splay_tree_key) &DECL_NAME (var)
 				    : (splay_tree_key) &DECL_UID (var), ctx);
 	    else
-	      x = build_receiver_ref (var, false, ctx);
+	      {
+		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+		  var = get_base_address (var);
+		x = build_receiver_ref (var, false, ctx);
+	      }
 
 	    if (is_array_data)
 	      {
@@ -13588,7 +13617,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		gimple_seq_add_stmt (&assign_body,
 				     gimple_build_assign (new_var, x));
 	      }
-	    else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	    else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		      && !omp_privatize_by_reference (var)
 		      && !omp_is_allocatable_or_ptr (var))
 		     || TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE)
diff --git a/gcc/testsuite/c-c++-common/gomp/clauses-1.c b/gcc/testsuite/c-c++-common/gomp/clauses-1.c
index 742132f..9da5255 100644
--- a/gcc/testsuite/c-c++-common/gomp/clauses-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/clauses-1.c
@@ -102,7 +102,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm)
 {
   #pragma omp for simd \
@@ -138,20 +138,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   #pragma omp target parallel \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
     ;
   #pragma omp target parallel for \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) ordered schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target parallel for \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target parallel for simd \
@@ -159,18 +159,19 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0]) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
     ;
   #pragma omp target teams distribute \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) order(concurrent) \
-    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) \
+    has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ;
   #pragma omp target teams distribute parallel for \
@@ -179,7 +180,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1) dist_schedule(static, 16) \
     if (parallel: i2) num_threads (nth) proc_bind(spread) \
     lastprivate (l) schedule(static, 4) nowait depend(inout: dd[0]) order(concurrent) \
-     allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+     allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams distribute parallel for simd \
@@ -189,7 +190,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) num_threads (nth) proc_bind(spread) \
     lastprivate (l) schedule(static, 4) order(concurrent) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams distribute simd \
@@ -197,14 +198,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) \
     collapse(1) dist_schedule(static, 16) order(concurrent) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target simd \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     safelen(8) simdlen(4) lastprivate (l) linear(ll: 1) aligned(q: 32) reduction(+:r) \
     nowait depend(inout: dd[0]) nontemporal(ntm) if(simd:i3) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp taskgroup task_reduction(+:r2) allocate (r2)
@@ -430,28 +431,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     nowait depend(inout: dd[0]) lastprivate (l) bind(parallel) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target parallel loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     nowait depend(inout: dd[0]) lastprivate (l) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target teams loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0]) \
     lastprivate (l) bind(teams) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target teams loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0]) \
     lastprivate (l) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c
new file mode 100644
index 0000000..be0c8db
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+
+void
+foo ()
+{
+  int * x;
+  #pragma omp target is_device_ptr(x) has_device_addr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target has_device_addr(x) is_device_ptr(x) /* { dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  int y = 42;
+  #pragma omp target has_device_addr(y) has_device_addr(y) /* { dg-error "'y' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target private(y) has_device_addr(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+  #pragma omp target has_device_addr(y) private(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+  #pragma omp target firstprivate(y) has_device_addr(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target has_device_addr(y) map(y) /* { dg-error "'y' appears both in data and map clauses" } */
+  ;
+  #pragma omp target map(y) has_device_addr(y) /* { dg-error "'y' appears both in data and map clauses" } */
+  ;
+
+  int z[3] = { 2, 5, 7 };
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1:])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1:2])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[:2])
+    ;
+
+  int w[3][4];
+  #pragma omp target data map(w) use_device_addr(w)
+    #pragma omp target has_device_addr(w[1][2])
+    ;
+
+  #pragma omp target data map(w) use_device_addr(w)
+    #pragma omp target has_device_addr(w[:1][2:])
+    ;
+
+  int u[0];
+  #pragma omp target data map(u) use_device_addr(u)
+    #pragma omp target has_device_addr(u)
+    ;
+
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr.c b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr.c
new file mode 100644
index 0000000..ecf30ca
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+
+void
+foo ()
+{
+  int *x;
+
+  #pragma omp target is_device_ptr(x) is_device_ptr(x) /* { dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target private(x) is_device_ptr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target is_device_ptr(x) private(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target firstprivate(x) is_device_ptr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target is_device_ptr(x) map(x) /* { dg-error "'x' appears both in data and map clauses" } */
+  ;
+  #pragma omp target map(x) is_device_ptr(x) /* { dg-error "'x' appears both in data and map clauses" } */
+  ;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-1.C b/gcc/testsuite/g++.dg/gomp/attrs-1.C
index 2a5f2cf..95a8fc6a 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-1.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-1.C
@@ -121,7 +121,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
      const char *msg)
 {
@@ -185,20 +185,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   [[omp::directive (target parallel
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
     ;
   [[omp::directive (target parallel for
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) ordered schedule(static, 4) collapse(1) nowait depend(inout: dd[0])
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target parallel for
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (omp::directive (target parallel for simd
@@ -206,22 +206,23 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (target teams
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0])
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
     ;
   [[omp::sequence (directive (target
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
     ;
   [[omp::sequence (omp::directive (target teams distribute
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) order(concurrent)
-    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ;
   [[omp::directive (target teams distribute parallel for
@@ -230,7 +231,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1) dist_schedule(static, 16)
     if (parallel: i2) num_threads (nth) proc_bind(spread)
     lastprivate (l) schedule(static, 4) nowait depend(inout: dd[0]) order(concurrent)
-     allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+     allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute parallel for simd
@@ -240,7 +241,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) num_threads (nth) proc_bind(spread)
     lastprivate (l) schedule(static, 4) order(concurrent)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute simd
@@ -248,14 +249,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl)
     collapse(1) dist_schedule(static, 16) order(concurrent)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target simd
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     safelen(8) simdlen(4) lastprivate (l) linear(ll: 1) aligned(q: 32) reduction(+:r)
     nowait depend(inout: dd[0]) nontemporal(ntm) if(simd:i3) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (taskgroup task_reduction(+:r2) allocate (r2)),
@@ -515,28 +516,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     nowait depend(inout: dd[0]) lastprivate (l) bind(parallel) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target parallel loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     nowait depend(inout: dd[0]) lastprivate (l) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0])
     lastprivate (l) bind(teams) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0])
     lastprivate (l) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (critical)]] {
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-2.C b/gcc/testsuite/g++.dg/gomp/attrs-2.C
index c00be7f..36a7584 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-2.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-2.C
@@ -121,7 +121,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
      const char *msg)
 {
@@ -185,20 +185,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   [[omp::directive (target parallel,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread)
-    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
     ;
   [[omp::directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),ordered schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[using omp:directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (omp::directive (target parallel for simd,
@@ -206,22 +206,23 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),if (simd: i3),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[using omp:sequence (directive (target teams,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
-    shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait, depend(inout: dd[0]),
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
     ;
   [[using omp:sequence (directive (target,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
-    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr(hda)))]]
     ;
   [[omp::sequence (omp::directive (target teams distribute,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),order(concurrent),
-    collapse(1),dist_schedule(static, 16),nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    collapse(1),dist_schedule(static, 16),nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),
+    has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ;
   [[omp::directive (target teams distribute parallel for,
@@ -230,7 +231,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1),dist_schedule(static, 16),
     if (parallel: i2),num_threads (nth),proc_bind(spread),
     lastprivate (l),schedule(static, 4),nowait depend(inout: dd[0]),order(concurrent),
-     allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute parallel for simd,
@@ -240,7 +241,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2),num_threads (nth),proc_bind(spread),
     lastprivate (l),schedule(static, 4),order(concurrent),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),if (simd: i3),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute simd,
@@ -248,14 +249,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),
     collapse(1),dist_schedule(static, 16),order(concurrent),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target simd,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     safelen(8),simdlen(4),lastprivate (l),linear(ll: 1),aligned(q: 32),reduction(+:r),
     nowait depend(inout: dd[0]),nontemporal(ntm),if(simd:i3),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (taskgroup, task_reduction(+:r2), allocate (r2)),
@@ -515,28 +516,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     nowait depend(inout: dd[0]),lastprivate (l),bind(parallel),order(concurrent),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target parallel loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     nowait depend(inout: dd[0]),lastprivate (l),order(concurrent),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     lastprivate (l),bind(teams),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     lastprivate (l),order(concurrent),collapse(1)
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (critical)]] {
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index f0c65a2..9253ba5 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -345,6 +345,9 @@ enum omp_clause_code {
      OpenMP clause: map ({alloc:,to:,from:,tofrom:,}variable-list).  */
   OMP_CLAUSE_MAP,
 
+  /* OpenMP clause: has_device_addr (variable-list).  */
+  OMP_CLAUSE_HAS_DEVICE_ADDR,
+
   /* Internal structure to hold OpenACC cache directive's variable-list.
      #pragma acc cache (variable-list).  */
   OMP_CLAUSE__CACHE_,
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index c7f50eb..449e4be 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -1339,6 +1339,7 @@ convert_nonlocal_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
 	case OMP_CLAUSE_LINK:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_DETACH:
 	do_decl_clause:
@@ -2123,6 +2124,7 @@ convert_local_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
 	case OMP_CLAUSE_LINK:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_DETACH:
 	do_decl_clause:
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index 275dc7d..676fadd 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -493,6 +493,9 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
     case OMP_CLAUSE_USE_DEVICE_ADDR:
       name = "use_device_addr";
       goto print_remap;
+    case OMP_CLAUSE_HAS_DEVICE_ADDR:
+      name = "has_device_addr";
+      goto print_remap;
     case OMP_CLAUSE_IS_DEVICE_PTR:
       name = "is_device_ptr";
       goto print_remap;
diff --git a/gcc/tree.c b/gcc/tree.c
index 7bfd641..e7fb1be 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -306,6 +306,7 @@ unsigned const char omp_clause_num_ops[] =
   2, /* OMP_CLAUSE_FROM  */
   2, /* OMP_CLAUSE_TO  */
   2, /* OMP_CLAUSE_MAP  */
+  1, /* OMP_CLAUSE_HAS_DEVICE_ADDR  */
   2, /* OMP_CLAUSE__CACHE_  */
   2, /* OMP_CLAUSE_GANG  */
   1, /* OMP_CLAUSE_ASYNC  */
@@ -395,6 +396,7 @@ const char * const omp_clause_code_name[] =
   "from",
   "to",
   "map",
+  "has_device_addr",
   "_cache_",
   "gang",
   "async",
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index fd747b9..c77d5e9 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -293,7 +293,8 @@ The OpenMP 4.5 specification is fully supported.
 @item @code{align} clause/modifier in @code{allocate} directive/clause
       and @code{allocator} directive @tab P @tab C/C++ on clause only
 @item @code{thread_limit} clause to @code{target} construct @tab N @tab
-@item @code{has_device_addr} clause to @code{target} construct @tab N @tab
+@item @code{has_device_addr} clause to @code{target} construct @tab P
+      @tab C/C++ on clause only
 @item iterators in @code{target update} motion clauses and @code{map}
       clauses @tab N @tab
 @item indirect calls to the device version of a procedure or function in
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
new file mode 100644
index 0000000..d9a309d7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
@@ -0,0 +1,23 @@
+/* Testing 'has_device_addr' clause on the target construct with reference. */
+
+#include <omp.h>
+
+int
+main ()
+{
+  int *dp = (int*)omp_target_alloc (sizeof(int), 0);
+
+  #pragma omp target is_device_ptr(dp)
+    *dp = 42;
+
+  int &x = *dp;
+
+  #pragma omp target has_device_addr(x)
+    x = 24;
+
+  #pragma omp target has_device_addr(x)
+    if (x != 24)
+      __builtin_abort ();
+
+  omp_target_free(dp, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C
new file mode 100644
index 0000000..401a30d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C
@@ -0,0 +1,24 @@
+#include <omp.h>
+
+int
+main ()
+{
+  int *dp = (int*)omp_target_alloc (30*sizeof(int), 0);
+
+  #pragma omp target is_device_ptr(dp)
+    for (int i = 0; i < 30; i++)
+      dp[i] = i;
+
+  int (&x)[30] = *static_cast<int(*)[30]>(static_cast<void*>(dp));
+
+  #pragma omp target has_device_addr(x)
+    for (int i = 0; i < 30; i++)
+      x[i] = 2 * i;
+
+  #pragma omp target has_device_addr(x)
+    for (int i = 0; i < 30; i++)
+      if (x[i] != 2 * i)
+	__builtin_abort ();
+
+  omp_target_free (dp, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c
new file mode 100644
index 0000000..12040dc
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c
@@ -0,0 +1,65 @@
+/* Testing the 'has_device_addr' clause on the target construct with
+   enclosing 'target data' construct. */
+
+#define N 40
+
+int
+main ()
+{
+  int x = 24;
+
+  #pragma omp target data map(x) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      x = 42;
+  if (x != 42)
+    __builtin_abort ();
+
+  int y[N];
+
+  for (int i = 0; i < N; i++)
+    y[i] = 42;
+  #pragma omp target data map(y) use_device_addr(y)
+    #pragma omp target has_device_addr(y)
+      for (int i = 0; i < N; i++)
+	y[i] = i;
+  for (int i = 0; i < N; i++)
+    if (y[i] != i)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[:N])
+      for (int i = 0; i < N; i++)
+	y[i] = i + 2;
+  for (int i = 0; i < N; i++)
+    if (y[i] != i + 2)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[24])
+	y[24] = 42;
+  if (y[24] != 42)
+    __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[24:])
+      for (int i = 24; i < N; i++)
+	y[i] = i + 3;
+  for (int i = 24; i < N; i++)
+    if (y[i] != i + 3)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[12:24])
+      for (int i = 12; i < 24; i++)
+	y[i] = i + 4;
+  for (int i = 12; i < 24; i++)
+    if (y[i] != i + 4)
+      __builtin_abort ();
+
+  int u[0];
+  #pragma omp target data map(u) use_device_addr(u)
+    #pragma omp target has_device_addr(u)
+  ;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c b/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c
new file mode 100644
index 0000000..fd99a82
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c
@@ -0,0 +1,33 @@
+/* Testing 'has_device_addr' clause with variable sized array. */
+
+int
+foo (int size)
+{
+  int x[size];
+
+  #pragma omp target data map(x[:size]) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      for (int i = 0; i < size; i++)
+	x[i] = i;
+  for (int i = 0; i < size; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  #pragma omp target data map(x) use_device_addr(x)
+    #pragma omp target has_device_addr(x[2:3])
+      for (int i = 0; i < size; i++)
+	x[i] = i;
+  for (int i = 0; i < size; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  return 0;
+}
+
+int
+main ()
+{
+  foo (40);
+
+  return 0;
+}
  
Marcel Vollweiler Nov. 24, 2021, 5:08 p.m. UTC | #3
Hi Jakub,

this is again a new version of the 'has_device_addr' patch. It includes
further minor changes in the C/C++ part and in addition the Fortran
implementation.

Tested on x86_64-linux with nvptx offloading with no regressions.

Marcel
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
C, C++, Fortran, OpenMP: Add 'has_device_addr' clause to 'target' construct.

This patch adds the 'has_device_addr' clause to the OpenMP 'target' construct
which was introduced in OpenMP 5.1 (OpenMP API 5.1 specification pp. 197ff):

	has_device_addr(list)

"The has_device_addr clause indicates that its list items already have device
addresses and therefore they may be directly accessed from a target device.
If the device address of a list item is not for the device on which the target
region executes, accessing the list item inside the region results in
unspecified behavior. The list items may include array sections." (p. 200)

"A list item may not be specified in both an is_device_ptr clause and a
has_device_addr clause on the directive." (p. 202)

"A list item that appears in an is_device_ptr or a has_device_addr clause must
not be specified in any data-sharing attribute clause on the same target
construct." (p. 203)

gcc/c-family/ChangeLog:

	* c-omp.c (c_omp_split_clauses): Add OMP_CLAUSE_HAS_DEVICE_ADDR case.
	* c-pragma.h (enum pragma_kind): Add 5.1 in comment.
	(enum pragma_omp_clause): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR.

gcc/c/ChangeLog:

	* c-parser.c (c_parser_omp_clause_name): Parse 'has_device_addr' clause.
	(c_parser_omp_variable_list): Handle array sections.
	(c_parser_omp_clause_has_device_addr): Added.
	(c_parser_omp_all_clauses): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR case.
	(c_parser_omp_target_exit_data): Add HAS_DEVICE_ADDR to OMP_CLAUSE_MASK.
	* c-typeck.c (handle_omp_array_sections): Handle clause restrictions.
	(c_finish_omp_clauses): Handle array sections.

gcc/cp/ChangeLog:

	* parser.c (cp_parser_omp_clause_name): Parse 'has_device_addr' clause.
	(cp_parser_omp_var_list_no_open): Handle array sections.
	(cp_parser_omp_all_clauses): Add PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR case.
	(cp_parser_omp_target_update): Add HAS_DEVICE_ADDR to OMP_CLAUSE_MASK.
	* pt.c (tsubst_omp_clauses): Add cases for OMP_CLAUSE_HAS_DEVICE_ADDR.
	* semantics.c (handle_omp_array_sections): Handle clause restrictions.
	(finish_omp_clauses): Handle array sections.

gcc/fortran/ChangeLog:

	* dump-parse-tree.c (show_omp_clauses): Added OMP_LIST_HAS_DEVICE_ADDR
	case.
	* gfortran.h: Added OMP_LIST_HAS_DEVICE_ADDR.
	* openmp.c (enum omp_mask1): Added OMP_CLAUSE_HAS_DEVICE_ADDR.
	(gfc_match_omp_clauses): Parse HAS_DEVICE_ADDR clause.
	(resolve_omp_clauses): Same.
	* trans-openmp.c (gfc_trans_omp_variable_list): Added 
	OMP_LIST_HAS_DEVICE_ADDR case.
	(gfc_trans_omp_clauses): Firstprivatize of array descriptors.

gcc/ChangeLog:

	* gimplify.c (gimplify_scan_omp_clauses): Add OMP_CLAUSE_HAS_DEVICE_ADDR
	cases
	and handle array sections.
	(gimplify_adjust_omp_clauses): Add OMP_CLAUSE_HAS_DEVICE_ADDR case.
	* omp-low.c (scan_sharing_clauses): Handle OMP_CLAUSE_HAS_DEVICE_ADDR.
	(lower_omp_target): Same.
	* tree-core.h (enum omp_clause_code): Same.
	* tree-nested.c (convert_nonlocal_omp_clauses): Same.
	(convert_local_omp_clauses): Same.
	* tree-pretty-print.c (dump_omp_clause): Same.
	* tree.c: Same.

libgomp/ChangeLog:

	* libgomp.texi: Updated entry for HAS_DEVICE_ADDR.
	* target.c (copy_firstprivate_data): Copy only if host address is not
	NULL.
	* testsuite/libgomp.c++/target-has-device-addr-2.C: New test.
	* testsuite/libgomp.c++/target-has-device-addr-4.C: New test.
	* testsuite/libgomp.c-c++-common/target-has-device-addr-1.c: New test.
	* testsuite/libgomp.c/target-has-device-addr-3.c: New test.
	* testsuite/libgomp.fortran/target-has-device-addr-1.f90: New test.
	* testsuite/libgomp.fortran/target-has-device-addr-2.f90: New test.
	* testsuite/libgomp.fortran/target-has-device-addr-3.f90: New test.
	* testsuite/libgomp.fortran/target-has-device-addr-4.f90: New test.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/clauses-1.c: Added has_device_addr to test cases.
	* g++.dg/gomp/attrs-1.C: Added has_device_addr to test cases.
	* g++.dg/gomp/attrs-2.C: Added has_device_addr to test cases.
	* c-c++-common/gomp/target-has-device-addr-1.c: New test.
	* c-c++-common/gomp/target-has-device-addr-2.c: New test.
	* c-c++-common/gomp/target-is-device-ptr-1.c: New test.
	* c-c++-common/gomp/target-is-device-ptr-2.c: New test.
	* gfortran.dg/gomp/is_device_ptr-3.f90: New test.
	* gfortran.dg/gomp/target-has-device-addr-1.f90: New test.
	* gfortran.dg/gomp/target-has-device-addr-2.f90: New test.

diff --git a/gcc/c-family/c-omp.c b/gcc/c-family/c-omp.c
index 3f84fd1..9c24e56 100644
--- a/gcc/c-family/c-omp.c
+++ b/gcc/c-family/c-omp.c
@@ -1862,6 +1862,7 @@ c_omp_split_clauses (location_t loc, enum tree_code code,
 	case OMP_CLAUSE_DEVICE:
 	case OMP_CLAUSE_MAP:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_DEFAULTMAP:
 	case OMP_CLAUSE_DEPEND:
 	  s = C_OMP_CLAUSE_SPLIT_TARGET;
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 0c5b07a..03baacd 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -89,8 +89,8 @@ enum pragma_kind {
 };
 
 
-/* All clauses defined by OpenACC 2.0, and OpenMP 2.5, 3.0, 3.1, 4.0, 4.5
-   and 5.0.  Used internally by both C and C++ parsers.  */
+/* All clauses defined by OpenACC 2.0, and OpenMP 2.5, 3.0, 3.1, 4.0, 4.5, 5.0,
+   and 5.1.  Used internally by both C and C++ parsers.  */
 enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_NONE = 0,
 
@@ -114,6 +114,7 @@ enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_FOR,
   PRAGMA_OMP_CLAUSE_FROM,
   PRAGMA_OMP_CLAUSE_GRAINSIZE,
+  PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR,
   PRAGMA_OMP_CLAUSE_HINT,
   PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OMP_CLAUSE_IN_REDUCTION,
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index f312630..71dafa1 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -12766,7 +12766,9 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
 	  break;
 	case 'h':
-	  if (!strcmp ("hint", p))
+	  if (!strcmp ("has_device_addr", p))
+	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  else if (!strcmp ("hint", p))
 	    result = PRAGMA_OMP_CLAUSE_HINT;
 	  else if (!strcmp ("host", p))
 	    result = PRAGMA_OACC_CLAUSE_HOST;
@@ -13148,6 +13150,7 @@ c_parser_omp_variable_list (c_parser *parser,
 	    case OMP_CLAUSE_REDUCTION:
 	    case OMP_CLAUSE_IN_REDUCTION:
 	    case OMP_CLAUSE_TASK_REDUCTION:
+	    case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	      while (c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
 		{
 		  tree low_bound = NULL_TREE, length = NULL_TREE;
@@ -14275,6 +14278,16 @@ c_parser_omp_clause_use_device_addr (c_parser *parser, tree list)
 				       list);
 }
 
+/* OpenMP 5.1:
+   has_device_addr ( variable-list ) */
+
+static tree
+c_parser_omp_clause_has_device_addr (c_parser *parser, tree list)
+{
+  return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
+				       list);
+}
+
 /* OpenMP 4.5:
    is_device_ptr ( variable-list ) */
 
@@ -17002,6 +17015,10 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_omp_clause_use_device_addr (parser, clauses);
 	  c_name = "use_device_addr";
 	  break;
+	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  clauses = c_parser_omp_clause_has_device_addr (parser, clauses);
+	  c_name = "has_device_addr";
+	  break;
 	case PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR:
 	  clauses = c_parser_omp_clause_is_device_ptr (parser, clauses);
 	  c_name = "is_device_ptr";
@@ -20984,7 +21001,8 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_THREAD_LIMIT)	\
-	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
 
 static bool
 c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p)
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index b71358e..0ea51de 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -13768,6 +13768,8 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	}
       first = c_fully_fold (first, false, NULL);
       OMP_CLAUSE_DECL (c) = first;
+      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	return false;
       if (size)
 	size = c_fully_fold (size, false, NULL);
       OMP_CLAUSE_SIZE (c) = size;
@@ -14073,7 +14075,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 {
   bitmap_head generic_head, firstprivate_head, lastprivate_head;
   bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
-  bitmap_head oacc_reduction_head;
+  bitmap_head oacc_reduction_head, is_on_device_head;
   tree c, t, type, *pc;
   tree simdlen = NULL_TREE, safelen = NULL_TREE;
   bool branch_seen = false;
@@ -14108,6 +14110,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   /* If ort == C_ORT_OMP used as nontemporal_head or use_device_xxx_head
      instead and for ort == C_ORT_OMP_TARGET used as in_reduction_head.  */
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
+  bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
   if (ort & C_ORT_ACC)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
@@ -14536,7 +14539,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qE appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+	  else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
@@ -15101,7 +15106,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		   || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -15186,6 +15192,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
 	  if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
@@ -15206,6 +15214,25 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  goto check_dup_generic;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  t = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (t) == TREE_LIST)
+	    {
+	      if (handle_omp_array_sections (c, ort))
+		remove = true;
+	      else
+		{
+		  t = OMP_CLAUSE_DECL (c);
+		  while (TREE_CODE (t) == ARRAY_REF)
+		    t = TREE_OPERAND (t, 0);
+		}
+	    }
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
+	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
+	    c_mark_addressable (t);
+	  goto check_dup_generic_t;
+
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  t = OMP_CLAUSE_DECL (c);
 	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index e2b5d68..553e452 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -36154,7 +36154,9 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
 	  break;
 	case 'h':
-	  if (!strcmp ("hint", p))
+	  if (!strcmp ("has_device_addr", p))
+	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  else if (!strcmp ("hint", p))
 	    result = PRAGMA_OMP_CLAUSE_HINT;
 	  else if (!strcmp ("host", p))
 	    result = PRAGMA_OACC_CLAUSE_HOST;
@@ -36445,6 +36447,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	    case OMP_CLAUSE_REDUCTION:
 	    case OMP_CLAUSE_IN_REDUCTION:
 	    case OMP_CLAUSE_TASK_REDUCTION:
+	    case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	      while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
 		{
 		  tree low_bound = NULL_TREE, length = NULL_TREE;
@@ -39854,6 +39857,11 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
 					    clauses);
 	  c_name = "is_device_ptr";
 	  break;
+	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
+					    clauses);
+	  c_name = "has_device_addr";
+	  break;
 	case PRAGMA_OMP_CLAUSE_IF:
 	  clauses = cp_parser_omp_clause_if (parser, clauses, token->location,
 					     true);
@@ -44030,7 +44038,8 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_THREAD_LIMIT)	\
-	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
 
 static bool
 cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 288625e..8583dca 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -17450,6 +17450,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_INCLUSIVE:
 	case OMP_CLAUSE_EXCLUSIVE:
 	  OMP_CLAUSE_DECL (nc)
@@ -17595,6 +17596,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_INCLUSIVE:
 	  case OMP_CLAUSE_EXCLUSIVE:
 	  case OMP_CLAUSE_ALLOCATE:
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 79b8162..99f42c9 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5607,6 +5607,8 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	      return false;
 	    }
 	  OMP_CLAUSE_DECL (c) = first;
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    return false;
 	  OMP_CLAUSE_SIZE (c) = size;
 	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
 	      || (TREE_CODE (t) == COMPONENT_REF
@@ -6611,7 +6613,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 {
   bitmap_head generic_head, firstprivate_head, lastprivate_head;
   bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
-  bitmap_head oacc_reduction_head;
+  bitmap_head oacc_reduction_head, is_on_device_head;
   tree c, t, *pc;
   tree safelen = NULL_TREE;
   bool branch_seen = false;
@@ -6643,6 +6645,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   /* If ort == C_ORT_OMP used as nontemporal_head or use_device_xxx_head
      instead and for ort == C_ORT_OMP_TARGET used as in_reduction_head.  */
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
+  bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
   if (ort & C_ORT_ACC)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
@@ -6941,7 +6944,9 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+	  else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
@@ -8110,7 +8115,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		   || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -8364,6 +8370,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	  field_ok = (ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP;
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
 	  if (!type_dependent_expression_p (t))
 	    {
 	      tree type = TREE_TYPE (t);
@@ -8393,6 +8401,23 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  goto check_dup_generic;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  t = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (t) == TREE_LIST)
+	    if (handle_omp_array_sections (c, ort))
+	      remove = true;
+	    else
+	      {
+		t = OMP_CLAUSE_DECL (c);
+		while (TREE_CODE (t) == ARRAY_REF)
+		  t = TREE_OPERAND (t, 0);
+	      }
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
+	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
+	    cxx_mark_addressable (t);
+	  goto check_dup_generic_t;
+
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  field_ok = true;
 	  t = OMP_CLAUSE_DECL (c);
diff --git a/gcc/fortran/dump-parse-tree.c b/gcc/fortran/dump-parse-tree.c
index 04660d5..2b97eb8 100644
--- a/gcc/fortran/dump-parse-tree.c
+++ b/gcc/fortran/dump-parse-tree.c
@@ -1683,6 +1683,7 @@ show_omp_clauses (gfc_omp_clauses *omp_clauses)
 	  case OMP_LIST_CACHE: type = "CACHE"; break;
 	  case OMP_LIST_IS_DEVICE_PTR: type = "IS_DEVICE_PTR"; break;
 	  case OMP_LIST_USE_DEVICE_PTR: type = "USE_DEVICE_PTR"; break;
+	  case OMP_LIST_HAS_DEVICE_ADDR: type = "HAS_DEVICE_ADDR"; break;
 	  case OMP_LIST_USE_DEVICE_ADDR: type = "USE_DEVICE_ADDR"; break;
 	  case OMP_LIST_NONTEMPORAL: type = "NONTEMPORAL"; break;
 	  case OMP_LIST_SCAN_IN: type = "INCLUSIVE"; break;
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 1846ee4..ef9326b 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -1391,7 +1391,8 @@ enum
   OMP_LIST_USE_DEVICE_PTR,
   OMP_LIST_USE_DEVICE_ADDR,
   OMP_LIST_NONTEMPORAL,
-  OMP_LIST_NUM
+  OMP_LIST_HAS_DEVICE_ADDR,
+  OMP_LIST_NUM  /* must be the last  */
 };
 
 /* Because a symbol can belong to multiple namelists, they must be
diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
index d120be8..a4cb1d4 100644
--- a/gcc/fortran/openmp.c
+++ b/gcc/fortran/openmp.c
@@ -918,6 +918,7 @@ enum omp_mask1
   OMP_CLAUSE_MESSAGE,  /* OpenMP 5.1.  */
   OMP_CLAUSE_SEVERITY,  /* OpenMP 5.1.  */
   OMP_CLAUSE_NOWAIT,
+  OMP_CLAUSE_HAS_DEVICE_ADDR,  /* OpenMP 5.1  */
   /* This must come last.  */
   OMP_MASK1_LAST
 };
@@ -2077,6 +2078,12 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
 	    }
 	  break;
 	case 'h':
+	  if ((mask & OMP_CLAUSE_HAS_DEVICE_ADDR)
+	      && gfc_match_omp_variable_list
+		   ("has_device_addr (",
+		    &c->lists[OMP_LIST_HAS_DEVICE_ADDR], false, NULL, NULL,
+							 true) == MATCH_YES)
+	    continue;
 	  if ((mask & OMP_CLAUSE_HINT)
 	      && (m = gfc_match_dupl_check (!c->hint, "hint", true, &c->hint))
 		 != MATCH_NO)
@@ -2850,7 +2857,8 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
 	  if ((mask & OMP_CLAUSE_USE_DEVICE_ADDR)
 	      && gfc_match_omp_variable_list
 		   ("use_device_addr (",
-		    &c->lists[OMP_LIST_USE_DEVICE_ADDR], false) == MATCH_YES)
+		    &c->lists[OMP_LIST_USE_DEVICE_ADDR], false, NULL, NULL,
+							 true) == MATCH_YES)
 	    continue;
 	  break;
 	case 'v':
@@ -3564,7 +3572,7 @@ cleanup:
    | OMP_CLAUSE_DEPEND | OMP_CLAUSE_NOWAIT | OMP_CLAUSE_PRIVATE		\
    | OMP_CLAUSE_FIRSTPRIVATE | OMP_CLAUSE_DEFAULTMAP			\
    | OMP_CLAUSE_IS_DEVICE_PTR | OMP_CLAUSE_IN_REDUCTION			\
-   | OMP_CLAUSE_THREAD_LIMIT)
+   | OMP_CLAUSE_THREAD_LIMIT | OMP_CLAUSE_HAS_DEVICE_ADDR)
 #define OMP_TARGET_DATA_CLAUSES \
   (omp_mask (OMP_CLAUSE_DEVICE) | OMP_CLAUSE_MAP | OMP_CLAUSE_IF	\
    | OMP_CLAUSE_USE_DEVICE_PTR | OMP_CLAUSE_USE_DEVICE_ADDR)
@@ -6187,7 +6195,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 	"IN_REDUCTION", "TASK_REDUCTION",
 	"DEVICE_RESIDENT", "LINK", "USE_DEVICE",
 	"CACHE", "IS_DEVICE_PTR", "USE_DEVICE_PTR", "USE_DEVICE_ADDR",
-	"NONTEMPORAL" };
+	"NONTEMPORAL", "HAS_DEVICE_ADDR" };
   STATIC_ASSERT (ARRAY_SIZE (clause_names) == OMP_LIST_NUM);
 
   if (omp_clauses == NULL)
@@ -6963,6 +6971,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 			     n->sym->name, name, &n->where);
 	      }
 	    break;
+	  case OMP_LIST_HAS_DEVICE_ADDR:
 	  case OMP_LIST_USE_DEVICE_PTR:
 	  case OMP_LIST_USE_DEVICE_ADDR:
 	    /* FIXME: Handle OMP_LIST_USE_DEVICE_PTR.  */
diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c
index 5b3c310..7afb3ea 100644
--- a/gcc/fortran/trans-openmp.c
+++ b/gcc/fortran/trans-openmp.c
@@ -1910,7 +1910,17 @@ gfc_trans_omp_variable_list (enum omp_clause_code code,
 	tree t = gfc_trans_omp_variable (namelist->sym, declare_simd);
 	if (t != error_mark_node)
 	  {
-	    tree node = build_omp_clause (input_location, code);
+	    tree node;
+	    /* For HAS_DEVICE_ADDR of an array descriptor, firstprivatize the
+	       descriptor such that the bounds are available; its data component
+	       is unmodified; it is handled as device address inside target. */
+	    if (code == OMP_CLAUSE_HAS_DEVICE_ADDR
+		&& (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (t))
+		    || (POINTER_TYPE_P (TREE_TYPE (t))
+			&& GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (TREE_TYPE (t))))))
+	      node = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE);
+	    else
+	      node = build_omp_clause (input_location, code);
 	    OMP_CLAUSE_DECL (node) = t;
 	    list = gfc_trans_add_clause (node, list);
 
@@ -2601,6 +2611,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 	case OMP_LIST_IS_DEVICE_PTR:
 	  clause_code = OMP_CLAUSE_IS_DEVICE_PTR;
 	  goto add_clause;
+	case OMP_LIST_HAS_DEVICE_ADDR:
+	  clause_code = OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  goto add_clause;
 	case OMP_LIST_NONTEMPORAL:
 	  clause_code = OMP_CLAUSE_NONTEMPORAL;
 	  goto add_clause;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 816cdaf..d9ad16e 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -10024,6 +10024,15 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  flags = GOVD_EXPLICIT;
 	  goto do_add;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  decl = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (decl) == ARRAY_REF)
+	    {
+	      flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT;
+	      while (TREE_CODE (decl) == ARRAY_REF)
+		decl = TREE_OPERAND (decl, 0);
+	      goto do_add_decl;
+	    }
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT;
 	  goto do_add;
@@ -11172,6 +11181,16 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	    }
 	  break;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  decl = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (decl) == ARRAY_REF)
+	    while (TREE_CODE (decl) == ARRAY_REF)
+	      decl = TREE_OPERAND (decl, 0);
+	  n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl);
+	  remove = n == NULL || !(n->value & GOVD_SEEN);
+	  break;
+
+	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_NONTEMPORAL:
 	  decl = OMP_CLAUSE_DECL (c);
 	  n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl);
@@ -11471,7 +11490,6 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	case OMP_CLAUSE_DETACH:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
-	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_ASYNC:
 	case OMP_CLAUSE_WAIT:
 	case OMP_CLAUSE_INDEPENDENT:
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 63a47f6..aff9065 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -1375,7 +1375,8 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	  decl = OMP_CLAUSE_DECL (c);
 	do_private:
 	  if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
-	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR
+	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      && is_gimple_omp_offloaded (ctx->stmt))
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE)
@@ -1383,7 +1384,8 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 		  by_ref = !omp_privatize_by_reference (decl);
 		  install_var_field (decl, by_ref, 3, ctx);
 		}
-	      else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+	      else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+		       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		install_var_field (decl, true, 3, ctx);
 	      else
 		install_var_field (decl, false, 3, ctx);
@@ -1452,6 +1454,11 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	  install_var_local (decl, ctx);
 	  break;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  decl = OMP_CLAUSE_DECL (c);
+	  decl = get_base_address (decl);
+	  goto do_private;
+
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  decl = OMP_CLAUSE_DECL (c);
 	  goto do_private;
@@ -1729,12 +1736,17 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	case OMP_CLAUSE_FIRSTPRIVATE:
 	case OMP_CLAUSE_PRIVATE:
 	case OMP_CLAUSE_LINEAR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  decl = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    decl = get_base_address (decl);
+
 	  if (is_variable_sized (decl))
 	    {
 	      if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
-		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		  && is_gimple_omp_offloaded (ctx->stmt))
 		{
 		  tree decl2 = DECL_VALUE_EXPR (decl);
@@ -12813,8 +12825,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
       case OMP_CLAUSE_USE_DEVICE_PTR:
       case OMP_CLAUSE_USE_DEVICE_ADDR:
+      case OMP_CLAUSE_HAS_DEVICE_ADDR:
       case OMP_CLAUSE_IS_DEVICE_PTR:
 	var = OMP_CLAUSE_DECL (c);
+	if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	  var = get_base_address (var);
 	map_cnt++;
 	if (is_variable_sized (var))
 	  {
@@ -12829,7 +12844,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    SET_DECL_VALUE_EXPR (new_var, x);
 	    DECL_HAS_VALUE_EXPR_P (new_var) = 1;
 	  }
-	else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		  && !omp_privatize_by_reference (var)
 		  && !omp_is_allocatable_or_ptr (var)
 		  && !lang_hooks.decls.omp_array_data (var, true))
@@ -13295,17 +13311,22 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
 	    ovar = OMP_CLAUSE_DECL (c);
+	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	      ovar = get_base_address (ovar);
 	    var = lookup_decl_in_outer_ctx (ovar, ctx);
 
 	    if (lang_hooks.decls.omp_array_data (ovar, true))
 	      {
-		tkind = (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		tkind = ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+			  && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 			 ? GOMP_MAP_USE_DEVICE_PTR : GOMP_MAP_FIRSTPRIVATE_INT);
 		x = build_sender_ref ((splay_tree_key) &DECL_NAME (ovar), ctx);
 	      }
-	    else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR)
+	    else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		     && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      {
 		tkind = GOMP_MAP_USE_DEVICE_PTR;
 		x = build_sender_ref ((splay_tree_key) &DECL_UID (ovar), ctx);
@@ -13327,7 +13348,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    type = TREE_TYPE (ovar);
 	    if (lang_hooks.decls.omp_array_data (ovar, true))
 	      var = lang_hooks.decls.omp_array_data (ovar, false);
-	    else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	    else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		      && !omp_privatize_by_reference (ovar)
 		      && !omp_is_allocatable_or_ptr (ovar))
 		     || TREE_CODE (type) == ARRAY_TYPE)
@@ -13342,6 +13364,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		    if (POINTER_TYPE_P (type)
 			&& TREE_CODE (type) != ARRAY_TYPE
 			&& ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_USE_DEVICE_ADDR
+			    && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR
 			    && !omp_is_allocatable_or_ptr (ovar))
 			   || (omp_privatize_by_reference (ovar)
 			       && omp_is_allocatable_or_ptr (ovar))))
@@ -13539,6 +13562,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    break;
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
 	    tree new_var;
 	    gimple_seq assign_body;
@@ -13549,12 +13573,17 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    var = OMP_CLAUSE_DECL (c);
 	    is_array_data = lang_hooks.decls.omp_array_data (var, true) != NULL;
 
-	    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR)
+	    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      x = build_sender_ref (is_array_data
 				    ? (splay_tree_key) &DECL_NAME (var)
 				    : (splay_tree_key) &DECL_UID (var), ctx);
 	    else
-	      x = build_receiver_ref (var, false, ctx);
+	      {
+		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+		  var = get_base_address (var);
+		x = build_receiver_ref (var, false, ctx);
+	      }
 
 	    if (is_array_data)
 	      {
@@ -13601,7 +13630,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		gimple_seq_add_stmt (&assign_body,
 				     gimple_build_assign (new_var, x));
 	      }
-	    else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	    else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		      && !omp_privatize_by_reference (var)
 		      && !omp_is_allocatable_or_ptr (var))
 		     || TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE)
@@ -13647,7 +13677,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 				     gimple_build_assign (new_var, x));
 	      }
 	    tree present;
-	    present = (do_optional_check
+	    present = ((do_optional_check
+			&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 		       ? omp_check_optional_argument (OMP_CLAUSE_DECL (c), true)
 		       : NULL_TREE);
 	    if (present)
diff --git a/gcc/testsuite/c-c++-common/gomp/clauses-1.c b/gcc/testsuite/c-c++-common/gomp/clauses-1.c
index 3ff49e0..71ca41c 100644
--- a/gcc/testsuite/c-c++-common/gomp/clauses-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/clauses-1.c
@@ -102,7 +102,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm)
 {
   #pragma omp for simd \
@@ -138,20 +138,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   #pragma omp target parallel \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
     ;
   #pragma omp target parallel for \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) ordered schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target parallel for \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target parallel for simd \
@@ -159,18 +159,19 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte - 1:nte) thread_limit(tl) nowait depend(inout: dd[0]) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
     ;
   #pragma omp target teams distribute \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) order(concurrent) \
-    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) \
+    has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ;
   #pragma omp target teams distribute parallel for \
@@ -179,7 +180,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1) dist_schedule(static, 16) \
     if (parallel: i2) num_threads (nth) proc_bind(spread) \
     lastprivate (l) schedule(static, 4) nowait depend(inout: dd[0]) order(concurrent) \
-     allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+     allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams distribute parallel for simd \
@@ -189,7 +190,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) num_threads (nth) proc_bind(spread) \
     lastprivate (l) schedule(static, 4) order(concurrent) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams distribute simd \
@@ -197,14 +198,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s) default(shared) reduction(+:r) num_teams(nte-1:nte) thread_limit(tl) \
     collapse(1) dist_schedule(static, 16) order(concurrent) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target simd \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     safelen(8) simdlen(4) lastprivate (l) linear(ll: 1) aligned(q: 32) reduction(+:r) \
     nowait depend(inout: dd[0]) nontemporal(ntm) if(simd:i3) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp taskgroup task_reduction(+:r2) allocate (r2)
@@ -430,28 +431,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     nowait depend(inout: dd[0]) lastprivate (l) bind(parallel) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target parallel loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     nowait depend(inout: dd[0]) lastprivate (l) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target teams loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte-1:nte) thread_limit(tl) nowait depend(inout: dd[0]) \
     lastprivate (l) bind(teams) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target teams loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0]) \
     lastprivate (l) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c
new file mode 100644
index 0000000..be0c8db
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+
+void
+foo ()
+{
+  int * x;
+  #pragma omp target is_device_ptr(x) has_device_addr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target has_device_addr(x) is_device_ptr(x) /* { dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  int y = 42;
+  #pragma omp target has_device_addr(y) has_device_addr(y) /* { dg-error "'y' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target private(y) has_device_addr(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+  #pragma omp target has_device_addr(y) private(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+  #pragma omp target firstprivate(y) has_device_addr(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target has_device_addr(y) map(y) /* { dg-error "'y' appears both in data and map clauses" } */
+  ;
+  #pragma omp target map(y) has_device_addr(y) /* { dg-error "'y' appears both in data and map clauses" } */
+  ;
+
+  int z[3] = { 2, 5, 7 };
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1:])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1:2])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[:2])
+    ;
+
+  int w[3][4];
+  #pragma omp target data map(w) use_device_addr(w)
+    #pragma omp target has_device_addr(w[1][2])
+    ;
+
+  #pragma omp target data map(w) use_device_addr(w)
+    #pragma omp target has_device_addr(w[:1][2:])
+    ;
+
+  int u[0];
+  #pragma omp target data map(u) use_device_addr(u)
+    #pragma omp target has_device_addr(u)
+    ;
+
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-2.c b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-2.c
new file mode 100644
index 0000000..7378416
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -fdump-tree-gimple" } */
+
+void
+foo ()
+{
+  int x, y;
+
+  #pragma omp target data map(x, y) use_device_addr(x, y)
+    #pragma omp target has_device_addr(x, y)
+      {
+	x = 42;
+      }
+}
+
+/* { dg-final { scan-tree-dump "has_device_addr\\(x\\)"  "gimple" } } */
+/* { dg-final { scan-tree-dump-not "has_device_addr\\(y\\)"  "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-1.c b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-1.c
new file mode 100644
index 0000000..ecf30ca
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+
+void
+foo ()
+{
+  int *x;
+
+  #pragma omp target is_device_ptr(x) is_device_ptr(x) /* { dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target private(x) is_device_ptr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target is_device_ptr(x) private(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target firstprivate(x) is_device_ptr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target is_device_ptr(x) map(x) /* { dg-error "'x' appears both in data and map clauses" } */
+  ;
+  #pragma omp target map(x) is_device_ptr(x) /* { dg-error "'x' appears both in data and map clauses" } */
+  ;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-2.c b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-2.c
new file mode 100644
index 0000000..df743dd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -fdump-tree-gimple" } */
+
+void
+foo ()
+{
+  int *x, *y;
+
+  #pragma omp target data map(x, y) use_device_ptr(x, y)
+    #pragma omp target is_device_ptr(x, y)
+      {
+	*x = 42;
+      }
+}
+
+/* { dg-final { scan-tree-dump "is_device_ptr\\(x\\)"  "gimple" } } */
+/* { dg-final { scan-tree-dump-not "is_device_ptr\\(y\\)"  "gimple" } } */
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-1.C b/gcc/testsuite/g++.dg/gomp/attrs-1.C
index 319ad32..f64b078 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-1.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-1.C
@@ -121,7 +121,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
      const char *msg)
 {
@@ -185,20 +185,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   [[omp::directive (target parallel
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
     ;
   [[omp::directive (target parallel for
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) ordered schedule(static, 4) collapse(1) nowait depend(inout: dd[0])
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target parallel for
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (omp::directive (target parallel for simd
@@ -206,22 +206,23 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (target teams
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte-1:nte) thread_limit(tl) nowait depend(inout: dd[0])
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
     ;
   [[omp::sequence (directive (target
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
     ;
   [[omp::sequence (omp::directive (target teams distribute
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) order(concurrent)
-    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ;
   [[omp::directive (target teams distribute parallel for
@@ -230,7 +231,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1) dist_schedule(static, 16)
     if (parallel: i2) num_threads (nth) proc_bind(spread)
     lastprivate (l) schedule(static, 4) nowait depend(inout: dd[0]) order(concurrent)
-     allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+     allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute parallel for simd
@@ -240,7 +241,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) num_threads (nth) proc_bind(spread)
     lastprivate (l) schedule(static, 4) order(concurrent)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute simd
@@ -248,14 +249,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s) default(shared) reduction(+:r) num_teams(nte-1:nte) thread_limit(tl)
     collapse(1) dist_schedule(static, 16) order(concurrent)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target simd
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     safelen(8) simdlen(4) lastprivate (l) linear(ll: 1) aligned(q: 32) reduction(+:r)
     nowait depend(inout: dd[0]) nontemporal(ntm) if(simd:i3) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (taskgroup task_reduction(+:r2) allocate (r2)),
@@ -515,28 +516,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     nowait depend(inout: dd[0]) lastprivate (l) bind(parallel) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target parallel loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     nowait depend(inout: dd[0]) lastprivate (l) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0])
     lastprivate (l) bind(teams) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte - 1 : nte) thread_limit(tl) nowait depend(inout: dd[0])
     lastprivate (l) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (critical)]] {
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-2.C b/gcc/testsuite/g++.dg/gomp/attrs-2.C
index 955b2dd..cc91fa2 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-2.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-2.C
@@ -121,7 +121,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
      const char *msg)
 {
@@ -185,20 +185,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   [[omp::directive (target parallel,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread)
-    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
     ;
   [[omp::directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),ordered schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[using omp:directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (omp::directive (target parallel for simd,
@@ -206,22 +206,23 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),if (simd: i3),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[using omp:sequence (directive (target teams,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
-    shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait, depend(inout: dd[0]),
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
     ;
   [[using omp:sequence (directive (target,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
-    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr(hda)))]]
     ;
   [[omp::sequence (omp::directive (target teams distribute,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte-1:nte),thread_limit(tl),order(concurrent),
-    collapse(1),dist_schedule(static, 16),nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    collapse(1),dist_schedule(static, 16),nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),
+    has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ;
   [[omp::directive (target teams distribute parallel for,
@@ -230,7 +231,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1),dist_schedule(static, 16),
     if (parallel: i2),num_threads (nth),proc_bind(spread),
     lastprivate (l),schedule(static, 4),nowait depend(inout: dd[0]),order(concurrent),
-     allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute parallel for simd,
@@ -240,7 +241,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2),num_threads (nth),proc_bind(spread),
     lastprivate (l),schedule(static, 4),order(concurrent),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),if (simd: i3),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute simd,
@@ -248,14 +249,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),
     collapse(1),dist_schedule(static, 16),order(concurrent),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target simd,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     safelen(8),simdlen(4),lastprivate (l),linear(ll: 1),aligned(q: 32),reduction(+:r),
     nowait depend(inout: dd[0]),nontemporal(ntm),if(simd:i3),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (taskgroup, task_reduction(+:r2), allocate (r2)),
@@ -515,28 +516,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     nowait depend(inout: dd[0]),lastprivate (l),bind(parallel),order(concurrent),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target parallel loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     nowait depend(inout: dd[0]),lastprivate (l),order(concurrent),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte-1:nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     lastprivate (l),bind(teams),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     lastprivate (l),order(concurrent),collapse(1)
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (critical)]] {
diff --git a/gcc/testsuite/gfortran.dg/gomp/is_device_ptr-3.f90 b/gcc/testsuite/gfortran.dg/gomp/is_device_ptr-3.f90
new file mode 100644
index 0000000..c3de772
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/is_device_ptr-3.f90
@@ -0,0 +1,27 @@
+! Test to ensure that IS_DEVICE_PTR is removed for non-used variables.
+
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+program main
+  use iso_c_binding
+  implicit none
+
+  integer :: x, y
+  call foo (x, y)
+
+contains
+  subroutine foo (a, b)
+    integer, target :: a, b
+
+    !$omp target data map(a, b) use_device_ptr(a, b)
+      !$omp target is_device_ptr(a, b)
+        a = 42
+      !$omp end target
+    !$omp end target data
+  end subroutine foo
+
+end program main
+
+! { dg-final { scan-tree-dump "is_device_ptr\\(a\\)"  "gimple" } }
+! { dg-final { scan-tree-dump-not "is_device_ptr\\(b\\)"  "gimple" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-1.f90 b/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-1.f90
new file mode 100644
index 0000000..db3fa46
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-1.f90
@@ -0,0 +1,36 @@
+! { dg-do compile }
+
+implicit none
+
+integer, target :: x
+integer, pointer :: ptr
+integer :: a(5)
+
+!$omp target has_device_addr(x)
+!$omp end target
+!$omp target has_device_addr(ptr)
+!$omp end target
+!$omp target has_device_addr(a)
+!$omp end target
+!$omp target has_device_addr(a(2:3))
+!$omp end target
+!$omp target has_device_addr(a(:3))
+!$omp end target
+!$omp target has_device_addr(a(2:))
+!$omp end target
+!$omp target has_device_addr(a(2))
+!$omp end target
+
+!$omp target has_device_addr(x) has_device_addr(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+
+!$omp target private(x) has_device_addr(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+!$omp target has_device_addr(x) private(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+!$omp target firstprivate(x) has_device_addr(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+!$omp target has_device_addr(x) firstprivate(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+
+end
\ No newline at end of file
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-2.f90 b/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-2.f90
new file mode 100644
index 0000000..7fc92b3
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-2.f90
@@ -0,0 +1,27 @@
+! Test to ensure that HAS_DEVICE_ADDR is removed for non-used variables.
+
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+program main
+  use iso_c_binding
+  implicit none
+
+  integer :: x, y
+  call foo (x, y)
+
+contains
+  subroutine foo (a, b)
+    integer :: a, b
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        a = 42
+      !$omp end target
+    !$omp end target data
+  end subroutine foo
+
+end program main
+
+! { dg-final { scan-tree-dump "has_device_addr\\(a\\)"  "gimple" } }
+! { dg-final { scan-tree-dump-not "has_device_addr\\(b\\)"  "gimple" } }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 8ab119d..32506f6 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -342,6 +342,9 @@ enum omp_clause_code {
      OpenMP clause: map ({alloc:,to:,from:,tofrom:,}variable-list).  */
   OMP_CLAUSE_MAP,
 
+  /* OpenMP clause: has_device_addr (variable-list).  */
+  OMP_CLAUSE_HAS_DEVICE_ADDR,
+
   /* Internal structure to hold OpenACC cache directive's variable-list.
      #pragma acc cache (variable-list).  */
   OMP_CLAUSE__CACHE_,
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index c7f50eb..449e4be 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -1339,6 +1339,7 @@ convert_nonlocal_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
 	case OMP_CLAUSE_LINK:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_DETACH:
 	do_decl_clause:
@@ -2123,6 +2124,7 @@ convert_local_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
 	case OMP_CLAUSE_LINK:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_DETACH:
 	do_decl_clause:
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index fcc0796..942275a 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -493,6 +493,9 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
     case OMP_CLAUSE_USE_DEVICE_ADDR:
       name = "use_device_addr";
       goto print_remap;
+    case OMP_CLAUSE_HAS_DEVICE_ADDR:
+      name = "has_device_addr";
+      goto print_remap;
     case OMP_CLAUSE_IS_DEVICE_PTR:
       name = "is_device_ptr";
       goto print_remap;
diff --git a/gcc/tree.c b/gcc/tree.c
index 62d9d78..de22e91 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -306,6 +306,7 @@ unsigned const char omp_clause_num_ops[] =
   2, /* OMP_CLAUSE_FROM  */
   2, /* OMP_CLAUSE_TO  */
   2, /* OMP_CLAUSE_MAP  */
+  1, /* OMP_CLAUSE_HAS_DEVICE_ADDR  */
   2, /* OMP_CLAUSE__CACHE_  */
   2, /* OMP_CLAUSE_GANG  */
   1, /* OMP_CLAUSE_ASYNC  */
@@ -395,6 +396,7 @@ const char * const omp_clause_code_name[] =
   "from",
   "to",
   "map",
+  "has_device_addr",
   "_cache_",
   "gang",
   "async",
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 2999609..1f673ac 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -293,7 +293,7 @@ The OpenMP 4.5 specification is fully supported.
 @item @code{align} clause/modifier in @code{allocate} directive/clause
       and @code{allocator} directive @tab P @tab C/C++ on clause only
 @item @code{thread_limit} clause to @code{target} construct @tab Y @tab
-@item @code{has_device_addr} clause to @code{target} construct @tab N @tab
+@item @code{has_device_addr} clause to @code{target} construct @tab Y @tab
 @item iterators in @code{target update} motion clauses and @code{map}
       clauses @tab N @tab
 @item indirect calls to the device version of a procedure or function in
diff --git a/libgomp/target.c b/libgomp/target.c
index 5d3103a..02716ce 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2435,7 +2435,7 @@ copy_firstprivate_data (char *tgt, size_t mapnum, void **hostaddrs,
   tgt_size = 0;
   size_t i;
   for (i = 0; i < mapnum; i++)
-    if ((kinds[i] & 0xff) == GOMP_MAP_FIRSTPRIVATE)
+    if ((kinds[i] & 0xff) == GOMP_MAP_FIRSTPRIVATE && hostaddrs[i] != NULL)
       {
 	size_t align = (size_t) 1 << (kinds[i] >> 8);
 	tgt_size = (tgt_size + align - 1) & ~(align - 1);
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
new file mode 100644
index 0000000..d9a309d7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
@@ -0,0 +1,23 @@
+/* Testing 'has_device_addr' clause on the target construct with reference. */
+
+#include <omp.h>
+
+int
+main ()
+{
+  int *dp = (int*)omp_target_alloc (sizeof(int), 0);
+
+  #pragma omp target is_device_ptr(dp)
+    *dp = 42;
+
+  int &x = *dp;
+
+  #pragma omp target has_device_addr(x)
+    x = 24;
+
+  #pragma omp target has_device_addr(x)
+    if (x != 24)
+      __builtin_abort ();
+
+  omp_target_free(dp, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C
new file mode 100644
index 0000000..401a30d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C
@@ -0,0 +1,24 @@
+#include <omp.h>
+
+int
+main ()
+{
+  int *dp = (int*)omp_target_alloc (30*sizeof(int), 0);
+
+  #pragma omp target is_device_ptr(dp)
+    for (int i = 0; i < 30; i++)
+      dp[i] = i;
+
+  int (&x)[30] = *static_cast<int(*)[30]>(static_cast<void*>(dp));
+
+  #pragma omp target has_device_addr(x)
+    for (int i = 0; i < 30; i++)
+      x[i] = 2 * i;
+
+  #pragma omp target has_device_addr(x)
+    for (int i = 0; i < 30; i++)
+      if (x[i] != 2 * i)
+	__builtin_abort ();
+
+  omp_target_free (dp, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c
new file mode 100644
index 0000000..12040dc
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c
@@ -0,0 +1,65 @@
+/* Testing the 'has_device_addr' clause on the target construct with
+   enclosing 'target data' construct. */
+
+#define N 40
+
+int
+main ()
+{
+  int x = 24;
+
+  #pragma omp target data map(x) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      x = 42;
+  if (x != 42)
+    __builtin_abort ();
+
+  int y[N];
+
+  for (int i = 0; i < N; i++)
+    y[i] = 42;
+  #pragma omp target data map(y) use_device_addr(y)
+    #pragma omp target has_device_addr(y)
+      for (int i = 0; i < N; i++)
+	y[i] = i;
+  for (int i = 0; i < N; i++)
+    if (y[i] != i)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[:N])
+      for (int i = 0; i < N; i++)
+	y[i] = i + 2;
+  for (int i = 0; i < N; i++)
+    if (y[i] != i + 2)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[24])
+	y[24] = 42;
+  if (y[24] != 42)
+    __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[24:])
+      for (int i = 24; i < N; i++)
+	y[i] = i + 3;
+  for (int i = 24; i < N; i++)
+    if (y[i] != i + 3)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[12:24])
+      for (int i = 12; i < 24; i++)
+	y[i] = i + 4;
+  for (int i = 12; i < 24; i++)
+    if (y[i] != i + 4)
+      __builtin_abort ();
+
+  int u[0];
+  #pragma omp target data map(u) use_device_addr(u)
+    #pragma omp target has_device_addr(u)
+  ;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c b/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c
new file mode 100644
index 0000000..fd99a82
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c
@@ -0,0 +1,33 @@
+/* Testing 'has_device_addr' clause with variable sized array. */
+
+int
+foo (int size)
+{
+  int x[size];
+
+  #pragma omp target data map(x[:size]) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      for (int i = 0; i < size; i++)
+	x[i] = i;
+  for (int i = 0; i < size; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  #pragma omp target data map(x) use_device_addr(x)
+    #pragma omp target has_device_addr(x[2:3])
+      for (int i = 0; i < size; i++)
+	x[i] = i;
+  for (int i = 0; i < size; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  return 0;
+}
+
+int
+main ()
+{
+  foo (40);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.fortran/target-has-device-addr-1.f90 b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-1.f90
new file mode 100644
index 0000000..2945864
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-1.f90
@@ -0,0 +1,50 @@
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+
+  integer, parameter :: N = 40
+  integer :: x, i
+  integer :: y (N)
+  integer :: u (0)
+
+  x = 24
+  !$omp target data map(x) use_device_addr(x)
+    !$omp target has_device_addr(x)
+      x = 42;
+    !$omp end target
+  !$omp end target data
+  if (x /= 42) stop 1
+
+  y = 42
+  !$omp target data map(y) use_device_addr(y)
+    !$omp target has_device_addr(y)
+      y = [(i, i=1, N)]
+    !$omp end target
+  !$omp end target data
+  if (any (y /= [(i, i = 1, N)])) stop 2
+
+  !$omp target data map(y(:N)) use_device_addr(y)
+    !$omp target has_device_addr(y(:N))
+      y = [(i+2, i=1, N)]
+    !$omp end target
+  !$omp end target data
+  if (any (y /= [(i+2, i = 1, N)])) stop 3
+
+  !$omp target data map(y) use_device_addr(y)
+    !$omp target has_device_addr(y(24:))
+      do i = 24, N
+        y(i) = i + 3
+      end do
+    !$omp end target
+  !$omp end target data
+  do i = 24, N
+    if (y(i) /= i + 3) stop 5
+  end do
+
+  !$omp target data map(u) use_device_addr(u)
+    !$omp target has_device_addr(u)
+    !$omp end target
+  !$omp end target data
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/target-has-device-addr-2.f90 b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-2.f90
new file mode 100644
index 0000000..a8d78a7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-2.f90
@@ -0,0 +1,40 @@
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+
+  integer, parameter :: N = 5
+  integer :: i, x(N), y(N), z(N:2*N-1)
+  target :: z
+
+  x = 42
+  y = 43
+  z = 44
+
+  call foo (x, y, z)
+  if (any (x /= [(i, i = 1, N)])) stop 1
+  if (any (y /= [(2*i, i = 1, N)])) stop 2
+  if (any (z /= [(3*i, i = 1, N)])) stop 3
+
+  contains
+  subroutine foo(a, b, c)
+    integer :: a(:)
+    integer :: b(*)
+    integer, pointer, intent(in) :: c(:)
+
+    !$omp target data map(a,b(:N),c) use_device_addr(a,b(:N),c)
+      !$omp target has_device_addr(A,B(:N),C)
+        if (lbound(a,dim=1) /= 1 .or. ubound(a,dim=1) /= N) stop 10
+        if (lbound(b,dim=1) /= 1) stop 11
+        if (lbound(c,dim=1) /= N .or. ubound(c,dim=1) /= 2*N-1) stop 12
+        if (any (a /= 42)) stop 13
+        if (any (b(:N) /= 43)) stop 14
+        if (any (c /= 44)) stop 15
+        a = [(i, i=1, N)]
+        b(:N) = [(2*i, i = 1, N)]
+        c = [(3*i, i = 1, N)]
+      !$omp end target
+    !$omp end target data
+  end subroutine foo
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/target-has-device-addr-3.f90 b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-3.f90
new file mode 100644
index 0000000..c6293b4
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-3.f90
@@ -0,0 +1,90 @@
+! Test optional dummy arguments in HAS_DEVICE_ADDR.
+
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+
+  integer, target :: x
+  integer, pointer :: ptr
+  integer, parameter :: N=7
+  real :: y1(N), y2(N)
+  integer, target :: y3(N:2*N-1)
+  integer :: i
+
+  x = 24
+  ptr => x
+  y1 = 42.24
+  y2 = 42.24
+  y3 = 42
+
+  call optional_scalar (is_present=.false.)
+  if (x /= 24) stop 1
+
+  call optional_scalar (x, is_present=.true.)
+  if (x /= 42) stop 2
+
+  call optional_ptr (is_present=.false.)
+  if (x /= 42) stop 3
+  if (ptr /= 42) stop 4
+
+  call optional_ptr (ptr, is_present=.true.)
+  if (x /= 84) stop 5
+  if (ptr /= 84) stop 6
+
+  call optional_array (is_present=.false.)
+  if (any (y1 /= [(42.24, i=1, N)])) stop 7
+  if (any (y2 /= [(42.24, i=1, N)])) stop 8
+  if (any (y3 /= [(42, i=1, N)])) stop 9
+
+  call optional_array (y1, y2, y3, is_present=.true.)
+  if (any (y1 /= [(42.24+i, i=1, N)])) stop 10
+  if (any (y2 /= [(42.24+2*i, i=1, N)])) stop 11
+  if (any (y3 /= [(42+3*i, i=1, N)])) stop 12
+
+contains
+  subroutine optional_scalar (a, is_present)
+    integer, optional :: a
+    logical, value :: is_present
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        if (is_present) a = 42
+      !$omp end target
+    !$omp end target data
+  end subroutine optional_scalar
+
+  subroutine optional_ptr (a, is_present)
+    integer, pointer, optional :: a
+    logical, value :: is_present
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        if (is_present) a = 84
+      !$omp end target
+    !$omp end target data
+  end subroutine optional_ptr
+
+  subroutine optional_array (a, b, c, is_present)
+    real, optional :: a(:), b(*)
+    integer, optional, pointer, intent(in) :: c(:)
+    logical, value :: is_present
+    integer :: i
+
+    !$omp target data map(a, b(:N), c) use_device_addr(a, b, c)
+      !$omp target has_device_addr(a, b, c)
+        if (is_present) then
+          if (lbound(a,dim=1) /= 1 .or. ubound(a,dim=1) /= N) stop 21
+          if (lbound(b,dim=1) /= 1) stop 22
+          if (lbound(c,dim=1) /= N .or. ubound(c,dim=1) /= 2*N-1) stop 23
+          if (any (a /= [(42.24, i = 1, N)])) stop 24
+          if (any (b(:N) /= [(42.24, i = 1, N)])) stop 25
+          if (any (c /= [(42, i = 1, N)])) stop 26
+          a = [(42.24+i, i=1, N)]
+          b(:N) = [(42.24+2*i, i=1, N)]
+          c = [(42+3*i, i=1, N)]
+        end if
+      !$omp end target
+    !$omp end target data
+  end subroutine optional_array
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/target-has-device-addr-4.f90 b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-4.f90
new file mode 100644
index 0000000..59d3e3d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-4.f90
@@ -0,0 +1,71 @@
+! Test allocatables in HAS_DEVICE_ADDR.
+
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+
+  integer, parameter :: N = 5
+  integer, allocatable :: x
+  integer, allocatable :: y(:)
+  call scalar_dummy (x)
+  call array_dummy (y)
+  call array_dummy_optional (y)
+  call array_dummy_optional ()
+
+contains
+  subroutine scalar_dummy (a)
+    integer, allocatable :: a
+
+    allocate (a)
+    a = 24
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        a = 42
+      !$omp end target
+    !$omp end target data
+    if (a /= 42) stop 1
+
+    deallocate (a)
+  end subroutine scalar_dummy
+
+  subroutine array_dummy (a)
+    integer, allocatable :: a(:)
+    integer :: i
+
+    allocate (a(N))
+    a = 42
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        a = [(i, i=1, N)]
+      !$omp end target
+    !$omp end target data
+    if (any (a /= [(i, i=1, N)])) stop 2
+
+    deallocate (a)
+  end subroutine array_dummy
+
+  subroutine array_dummy_optional (a)
+    integer, optional, allocatable :: a(:)
+    integer :: i
+
+    if (present (a)) then
+      allocate (a(N))
+      a = 42
+    end if
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        if (present (a)) a = [(i, i=1, N)]
+      !$omp end target
+    !$omp end target data
+
+    if (present (a)) then
+      if (any (a /= [(i, i=1, N)])) stop 2
+      deallocate (a)
+    end if
+  end subroutine array_dummy_optional
+
+end program main
  
Jakub Jelinek Jan. 11, 2022, 11:53 a.m. UTC | #4
On Wed, Nov 24, 2021 at 06:08:02PM +0100, Marcel Vollweiler wrote:
> +	case OMP_CLAUSE_HAS_DEVICE_ADDR:
> +	  t = OMP_CLAUSE_DECL (c);
> +	  if (TREE_CODE (t) == TREE_LIST)
> +	    {
> +	      if (handle_omp_array_sections (c, ort))
> +		remove = true;
> +	      else
> +		{
> +		  t = OMP_CLAUSE_DECL (c);
> +		  while (TREE_CODE (t) == ARRAY_REF)
> +		    t = TREE_OPERAND (t, 0);
> +		}
> +	    }
> +	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
> +	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));

Why the OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR check?
There is no goto into this block nor fallthru into it, and
handle_omp_array_sections better shouldn't change OMP_CLAUSE_CODE.

>  	  goto check_dup_generic;
>  
> +	case OMP_CLAUSE_HAS_DEVICE_ADDR:
> +	  t = OMP_CLAUSE_DECL (c);
> +	  if (TREE_CODE (t) == TREE_LIST)
> +	    if (handle_omp_array_sections (c, ort))
> +	      remove = true;
> +	    else
> +	      {
> +		t = OMP_CLAUSE_DECL (c);
> +		while (TREE_CODE (t) == ARRAY_REF)
> +		  t = TREE_OPERAND (t, 0);
> +	      }
> +	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
> +	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));

Likewise.

> +	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
> +	    cxx_mark_addressable (t);
> +	  goto check_dup_generic_t;
> +
>  	case OMP_CLAUSE_USE_DEVICE_ADDR:
>  	  field_ok = true;
>  	  t = OMP_CLAUSE_DECL (c);

> --- a/gcc/fortran/gfortran.h
> +++ b/gcc/fortran/gfortran.h
> @@ -1391,7 +1391,8 @@ enum
>    OMP_LIST_USE_DEVICE_PTR,
>    OMP_LIST_USE_DEVICE_ADDR,
>    OMP_LIST_NONTEMPORAL,
> -  OMP_LIST_NUM
> +  OMP_LIST_HAS_DEVICE_ADDR,
> +  OMP_LIST_NUM  /* must be the last  */

Capital M and . at the end.

> @@ -2077,6 +2078,12 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
>  	    }
>  	  break;
>  	case 'h':
> +	  if ((mask & OMP_CLAUSE_HAS_DEVICE_ADDR)
> +	      && gfc_match_omp_variable_list
> +		   ("has_device_addr (",
> +		    &c->lists[OMP_LIST_HAS_DEVICE_ADDR], false, NULL, NULL,
> +							 true) == MATCH_YES)

Formatting, true should be IMO below &c->lists.

> +	    continue;
>  	  if ((mask & OMP_CLAUSE_HINT)
>  	      && (m = gfc_match_dupl_check (!c->hint, "hint", true, &c->hint))
>  		 != MATCH_NO)
> @@ -2850,7 +2857,8 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
>  	  if ((mask & OMP_CLAUSE_USE_DEVICE_ADDR)
>  	      && gfc_match_omp_variable_list
>  		   ("use_device_addr (",
> -		    &c->lists[OMP_LIST_USE_DEVICE_ADDR], false) == MATCH_YES)
> +		    &c->lists[OMP_LIST_USE_DEVICE_ADDR], false, NULL, NULL,
> +							 true) == MATCH_YES)

Likewise.

> --- a/gcc/fortran/trans-openmp.c
> +++ b/gcc/fortran/trans-openmp.c
> @@ -1910,7 +1910,17 @@ gfc_trans_omp_variable_list (enum omp_clause_code code,
>  	tree t = gfc_trans_omp_variable (namelist->sym, declare_simd);
>  	if (t != error_mark_node)
>  	  {
> -	    tree node = build_omp_clause (input_location, code);
> +	    tree node;
> +	    /* For HAS_DEVICE_ADDR of an array descriptor, firstprivatize the
> +	       descriptor such that the bounds are available; its data component
> +	       is unmodified; it is handled as device address inside target. */
> +	    if (code == OMP_CLAUSE_HAS_DEVICE_ADDR
> +		&& (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (t))
> +		    || (POINTER_TYPE_P (TREE_TYPE (t))
> +			&& GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (TREE_TYPE (t))))))
> +	      node = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE);

Not sure about the above,

> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -10024,6 +10024,15 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
>  	  flags = GOVD_EXPLICIT;
>  	  goto do_add;
>  
> +	case OMP_CLAUSE_HAS_DEVICE_ADDR:
> +	  decl = OMP_CLAUSE_DECL (c);
> +	  if (TREE_CODE (decl) == ARRAY_REF)
> +	    {
> +	      flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT;
> +	      while (TREE_CODE (decl) == ARRAY_REF)
> +		decl = TREE_OPERAND (decl, 0);
> +	      goto do_add_decl;

but this looks weird.
If decl after stripping the ARRAY_REFs is a var with pointer type, sure,
firstprivatizing it is the way to go.
But it can be also a variable with ARRAY_TYPE, can't it?  Something like:
  int a[64];
  #pragma omp target data map(a) use_device_addr(a)
  {
    #pragma omp target has_device_addr(a[3:16])
    a[3] = 1;
  }
and in this case firstprivatization of a looks wrong.  use_device_addr
should replace (but only at omp-low.c time I think) a used in the block
with the remapped a (i.e. *device_address_of_a).
Or perhaps it could be a non-static data member with array type
inside of a C++ method.

> +	case OMP_CLAUSE_HAS_DEVICE_ADDR:
> +	  decl = OMP_CLAUSE_DECL (c);
> +	  if (TREE_CODE (decl) == ARRAY_REF)
> +	    while (TREE_CODE (decl) == ARRAY_REF)
> +	      decl = TREE_OPERAND (decl, 0);

Isn't this equivalent to just the while loop without the if?

	Jakub
  
Tobias Burnus Jan. 11, 2022, 1:27 p.m. UTC | #5
Hi Jakub, hi all,

let me quickly comment on 'has_device_addr' with Fortran arrays
and with an array section (i.e. regarding your comment quoted
at the very bottom of this email).

Unfortunately, the OpenMP specification is rather unclear
what has_device_addr means for C/C++ array sections and in general
for Fortran, especially when arrays, allocatables/pointers, and
type parameters like nonconst string lengths are involved. Thus,
I opened a spec issue – after some discussions (lang-spec meeting,
C++/affinity (→ Fortran) meeting), it starts to converge:
https://github.com/OpenMP/spec/issues/3180

If I understood it correctly, for C/C++, using has_device_addr with
an array section implies firstprivate, while it does not without
array section.

For Fortran, the discussion converged to
* array descriptor must be the same at beginning/end of target
   region, i.e:
- if allocated before target region, allocation may not be changed
   inside the target region. If unallocated, it can be allocated but
   must be deallocated before the target region is left.
- if a pointer, pointer association must be the same at the end of
   the target region as when it was entered (unmodified or reset)
* meta data (array bounds, alloc status, len type parameters like
   char length) are accessible on the host - and are then made
   accessible to the device (firstprivate, direct access on shared
   memory or whatever means. Due to the conditions above, it is
   indistinguishable).

However, the discussion has not yet fully settled and spec updates
are still needed.

Side remark: I note that use_device_addr permits array sections,
but GCC does not support them yet. (Useful when doing a partial
map of an array + 'omp data use_device_addr()' on the partially
mapped array.)

I have not checked what Marcel's implementation does and whether
that's compatible with the incomplete OpenMP 5.{1,2} spec and
the on-going discussion of/in OpenMP lang-spec Issue 3180.

On 11.01.22 12:53, Jakub Jelinek via Fortran wrote:

>> +++ b/gcc/fortran/trans-openmp.c
>> ...
>> +        /* For HAS_DEVICE_ADDR of an array descriptor, firstprivatize the
>> +           descriptor such that the bounds are available; its data component
>> +           is unmodified; it is handled as device address inside target. */
...
> Not sure about the above,
>
>> --- a/gcc/gimplify.c
>> +++ b/gcc/gimplify.c
>> ...
> but this looks weird.
> If decl after stripping the ARRAY_REFs is a var with pointer type, sure,
> firstprivatizing it is the way to go.
> But it can be also a variable with ARRAY_TYPE, can't it?  Something like:
>    int a[64];
>    #pragma omp target data map(a) use_device_addr(a)
>    {
>      #pragma omp target has_device_addr(a[3:16])
>      a[3] = 1;
>    }
> and in this case firstprivatization of a looks wrong.  use_device_addr
> should replace (but only at omp-low.c time I think) a used in the block
> with the remapped a (i.e. *device_address_of_a).
> Or perhaps it could be a non-static data member with array type
> inside of a C++ method.

Tobias

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
  
Jakub Jelinek Jan. 11, 2022, 2:07 p.m. UTC | #6
On Tue, Jan 11, 2022 at 02:27:57PM +0100, Tobias Burnus wrote:
> Hi Jakub, hi all,
> 
> let me quickly comment on 'has_device_addr' with Fortran arrays
> and with an array section (i.e. regarding your comment quoted
> at the very bottom of this email).
> 
> Unfortunately, the OpenMP specification is rather unclear
> what has_device_addr means for C/C++ array sections and in general
> for Fortran, especially when arrays, allocatables/pointers, and
> type parameters like nonconst string lengths are involved. Thus,
> I opened a spec issue – after some discussions (lang-spec meeting,
> C++/affinity (→ Fortran) meeting), it starts to converge:
> https://github.com/OpenMP/spec/issues/3180
> 
> If I understood it correctly, for C/C++, using has_device_addr with
> an array section implies firstprivate, while it does not without
> array section.

That seems just wrong for arrays, that will just crash, see below.

Cases like:
  struct S { whatever; } s;
  #pragma omp target data map (s) use_device_addr (s)
  {
    // At this point it is invalid to use s.field etc. because
    // &s is a device address
    #pragma omp target has_device_addr (s)
    {
      access (&s);
    }
  }
and s/struct S { whatever; } s/int s[16];/
are similar, in all the cases use_device_addr will replace
s in the body with *device_addr_of_s and has_device_addr
needs to firstprivatize the artificial address of s and ensure
that even in the target body s is *some_addr_of_s.
And IMHO array sections should be treated the same, just the
target data could map only parts of the array and not the whole
array, but still device_addr_of_s will be something pointing to the
start of the array, perhaps before the actual object allocated on the
device.
So, I think the gimplifier should strip the ARRAY_REFs and if that yields
something with ARRAY_TYPE, should just treat that var as what appeared
in the has_device_addr clause.  Only if it the array section has
a base pointer that base pointer needs to be copied to the device as is
and so the artificial firstprivate on that pointer that copies the pointer
to the device code.

> Side remark: I note that use_device_addr permits array sections,
> but GCC does not support them yet. (Useful when doing a partial
> map of an array + 'omp data use_device_addr()' on the partially
> mapped array.)

Yes, we should implement that.  But even without that supported, one can
have:
  int a[32];
  #pragma omp target data map (a) use_device_addr (a)
  {
    // So, &a[0] is now a device pointer, whole a is mapped
    #pragma omp target has_device_addr (a[3:17])
    {
      ++a[3];
    }
  }
When whole a[0:32] is mapped, obviously a[3:17] is mapped too
and for has_device-addr it IMHO should act like has_device_addr (a)
under the hood, except the user doesn't guarantee that the whole
array is mapped, just that a has a device address.

Now, if we treat the has_device_addr (a[3:17]) in the above testcase
as firstprivate (a), that will mean we try to copy the whole array from
host to the device.  But &a[0] etc. aren't host addresses, they are device
addresses, so unless the host can access the device addresses, that will
segfault.

	Jakub
  
Marcel Vollweiler Feb. 2, 2022, 8:19 a.m. UTC | #7
Hi Jakub,

>> +    case OMP_CLAUSE_HAS_DEVICE_ADDR:
>> +      t = OMP_CLAUSE_DECL (c);
>> +      if (TREE_CODE (t) == TREE_LIST)
>> +        {
>> +          if (handle_omp_array_sections (c, ort))
>> +            remove = true;
>> +          else
>> +            {
>> +              t = OMP_CLAUSE_DECL (c);
>> +              while (TREE_CODE (t) == ARRAY_REF)
>> +                t = TREE_OPERAND (t, 0);
>> +            }
>> +        }
>> +      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
>> +        bitmap_set_bit (&is_on_device_head, DECL_UID (t));
>
> Why the OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR check?
> There is no goto into this block nor fallthru into it, and
> handle_omp_array_sections better shouldn't change OMP_CLAUSE_CODE.

Good point. Removed.

>
>>        goto check_dup_generic;
>>
>> +    case OMP_CLAUSE_HAS_DEVICE_ADDR:
>> +      t = OMP_CLAUSE_DECL (c);
>> +      if (TREE_CODE (t) == TREE_LIST)
>> +        if (handle_omp_array_sections (c, ort))
>> +          remove = true;
>> +        else
>> +          {
>> +            t = OMP_CLAUSE_DECL (c);
>> +            while (TREE_CODE (t) == ARRAY_REF)
>> +              t = TREE_OPERAND (t, 0);
>> +          }
>> +      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
>> +        bitmap_set_bit (&is_on_device_head, DECL_UID (t));
>
> Likewise.

Removed.

>
>> +      if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
>> +        cxx_mark_addressable (t);
>> +      goto check_dup_generic_t;
>> +
>>      case OMP_CLAUSE_USE_DEVICE_ADDR:
>>        field_ok = true;
>>        t = OMP_CLAUSE_DECL (c);
>
>> --- a/gcc/fortran/gfortran.h
>> +++ b/gcc/fortran/gfortran.h
>> @@ -1391,7 +1391,8 @@ enum
>>     OMP_LIST_USE_DEVICE_PTR,
>>     OMP_LIST_USE_DEVICE_ADDR,
>>     OMP_LIST_NONTEMPORAL,
>> -  OMP_LIST_NUM
>> +  OMP_LIST_HAS_DEVICE_ADDR,
>> +  OMP_LIST_NUM  /* must be the last  */
>
> Capital M and . at the end.

Changed.

>
>> @@ -2077,6 +2078,12 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
>>          }
>>        break;
>>      case 'h':
>> +      if ((mask & OMP_CLAUSE_HAS_DEVICE_ADDR)
>> +          && gfc_match_omp_variable_list
>> +               ("has_device_addr (",
>> +                &c->lists[OMP_LIST_HAS_DEVICE_ADDR], false, NULL, NULL,
>> +                                                     true) == MATCH_YES)
>
> Formatting, true should be IMO below &c->lists.

Corrected the formatting.

>
>> +        continue;
>>        if ((mask & OMP_CLAUSE_HINT)
>>            && (m = gfc_match_dupl_check (!c->hint, "hint", true, &c->hint))
>>               != MATCH_NO)
>> @@ -2850,7 +2857,8 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
>>        if ((mask & OMP_CLAUSE_USE_DEVICE_ADDR)
>>            && gfc_match_omp_variable_list
>>                 ("use_device_addr (",
>> -                &c->lists[OMP_LIST_USE_DEVICE_ADDR], false) == MATCH_YES)
>> +                &c->lists[OMP_LIST_USE_DEVICE_ADDR], false, NULL, NULL,
>> +                                                     true) == MATCH_YES)
>
> Likewise.

Corrected.

>
>> --- a/gcc/fortran/trans-openmp.c
>> +++ b/gcc/fortran/trans-openmp.c
>> @@ -1910,7 +1910,17 @@ gfc_trans_omp_variable_list (enum omp_clause_code code,
>>      tree t = gfc_trans_omp_variable (namelist->sym, declare_simd);
>>      if (t != error_mark_node)
>>        {
>> -        tree node = build_omp_clause (input_location, code);
>> +        tree node;
>> +        /* For HAS_DEVICE_ADDR of an array descriptor, firstprivatize the
>> +           descriptor such that the bounds are available; its data component
>> +           is unmodified; it is handled as device address inside target. */
>> +        if (code == OMP_CLAUSE_HAS_DEVICE_ADDR
>> +            && (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (t))
>> +                || (POINTER_TYPE_P (TREE_TYPE (t))
>> +                    && GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (TREE_TYPE (t))))))
>> +          node = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE);
>
> Not sure about the above,

This is needed for allocatable arrays and array pointers to ensure that
not only the (array) data is (already) present on the device but also
the array descriptor. Otherwise the test cases
target-has-device-addr-2.f90, target-has-device-addr-3.f90 (because of
variable "c") and target-has-device-addr-4.f90 (also because of variable
"c") won't work.

>
>> --- a/gcc/gimplify.c
>> +++ b/gcc/gimplify.c
>> @@ -10024,6 +10024,15 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
>>        flags = GOVD_EXPLICIT;
>>        goto do_add;
>>
>> +    case OMP_CLAUSE_HAS_DEVICE_ADDR:
>> +      decl = OMP_CLAUSE_DECL (c);
>> +      if (TREE_CODE (decl) == ARRAY_REF)
>> +        {
>> +          flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT;
>> +          while (TREE_CODE (decl) == ARRAY_REF)
>> +            decl = TREE_OPERAND (decl, 0);
>> +          goto do_add_decl;
>
> but this looks weird.
> If decl after stripping the ARRAY_REFs is a var with pointer type, sure,
> firstprivatizing it is the way to go.
> But it can be also a variable with ARRAY_TYPE, can't it?  Something like:
>    int a[64];
>    #pragma omp target data map(a) use_device_addr(a)
>    {
>      #pragma omp target has_device_addr(a[3:16])
>      a[3] = 1;
>    }
> and in this case firstprivatization of a looks wrong.  use_device_addr
> should replace (but only at omp-low.c time I think) a used in the block
> with the remapped a (i.e. *device_address_of_a).
> Or perhaps it could be a non-static data member with array type
> inside of a C++ method.
I removed GOVD_FIRSTPRIVATE from the OMP_CLAUSE_HAS_DEVICE_ADDR case and
simplified the above code to:

        case OMP_CLAUSE_HAS_DEVICE_ADDR:
          decl = OMP_CLAUSE_DECL (c);
          while (TREE_CODE (decl) == INDIRECT_REF
                 || TREE_CODE (decl) == ARRAY_REF)
            decl = TREE_OPERAND (decl, 0);
          flags = GOVD_EXPLICIT;
          goto do_add_decl;

The reason is that I don't see a case where privatizing is useful,
because whatever type a list item has, we claim with has_device_addr
that the list item is already on the device. So we shouldn't create a
duplicate with a new device address for any possible list item.

>
>> +    case OMP_CLAUSE_HAS_DEVICE_ADDR:
>> +      decl = OMP_CLAUSE_DECL (c);
>> +      if (TREE_CODE (decl) == ARRAY_REF)
>> +        while (TREE_CODE (decl) == ARRAY_REF)
>> +          decl = TREE_OPERAND (decl, 0);
>
> Isn't this equivalent to just the while loop without the if?

Yes. I removed the if statement.

Moreover, I added testing for references to array sections (without
enclosing target data region) and for references to pointers. To tackle
also those cases further small code revisions were necessary.

The patch was tested with x86-64 and powerpc configs with offoading
(nvptx and gcn) without regressions.

Marcel
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
C, C++, Fortran, OpenMP: Add 'has_device_addr' clause to 'target' construct.

This patch adds the 'has_device_addr' clause to the OpenMP 'target' construct
which was introduced in OpenMP 5.1 (OpenMP API 5.1 specification pp. 197ff):

	has_device_addr(list)

"The has_device_addr clause indicates that its list items already have device
addresses and therefore they may be directly accessed from a target device.
If the device address of a list item is not for the device on which the target
region executes, accessing the list item inside the region results in
unspecified behavior. The list items may include array sections." (p. 200)

"A list item may not be specified in both an is_device_ptr clause and a
has_device_addr clause on the directive." (p. 202)

"A list item that appears in an is_device_ptr or a has_device_addr clause must
not be specified in any data-sharing attribute clause on the same target
construct." (p. 203)

gcc/c-family/ChangeLog:

	* c-omp.cc (c_omp_split_clauses): Added OMP_CLAUSE_HAS_DEVICE_ADDR case.
	* c-pragma.h (enum pragma_kind): Added 5.1 in comment.
	(enum pragma_omp_clause): Added PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR.

gcc/c/ChangeLog:

	* c-parser.cc (c_parser_omp_clause_name): Parse 'has_device_addr'
	clause.
	(c_parser_omp_variable_list): Handle array sections.
	(c_parser_omp_clause_has_device_addr): Added.
	(c_parser_omp_all_clauses): Added PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR
	case.
	(c_parser_omp_target_exit_data): Added HAS_DEVICE_ADDR to 
	OMP_CLAUSE_MASK.
	* c-typeck.cc (handle_omp_array_sections): Handle clause restrictions.
	(c_finish_omp_clauses): Handle array sections.

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_omp_clause_name): Parse 'has_device_addr' clause.
	(cp_parser_omp_var_list_no_open): Handle array sections.
	(cp_parser_omp_all_clauses): Added PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR
	case.
	(cp_parser_omp_target_update): Added HAS_DEVICE_ADDR to OMP_CLAUSE_MASK.
	* pt.c (tsubst_omp_clauses): Added cases for OMP_CLAUSE_HAS_DEVICE_ADDR.
	* semantics.cc (handle_omp_array_sections): Handle clause restrictions.
	(finish_omp_clauses): Handle array sections.

gcc/fortran/ChangeLog:

	* dump-parse-tree.cc (show_omp_clauses): Added OMP_LIST_HAS_DEVICE_ADDR
	case.
	* gfortran.h: Added OMP_LIST_HAS_DEVICE_ADDR.
	* openmp.cc (enum omp_mask2): Added OMP_CLAUSE_HAS_DEVICE_ADDR.
	(gfc_match_omp_clauses): Parse HAS_DEVICE_ADDR clause.
	(resolve_omp_clauses): Same.
	* trans-openmp.cc (gfc_trans_omp_variable_list): Added 
	OMP_LIST_HAS_DEVICE_ADDR case.
	(gfc_trans_omp_clauses): Firstprivatize of array descriptors.

gcc/ChangeLog:

	* gimplify.cc (gimplify_scan_omp_clauses): Added cases for
	OMP_CLAUSE_HAS_DEVICE_ADDR
	and handle array sections.
	(gimplify_adjust_omp_clauses): Added OMP_CLAUSE_HAS_DEVICE_ADDR case.
	* omp-low.cc (scan_sharing_clauses): Handle OMP_CLAUSE_HAS_DEVICE_ADDR.
	(lower_omp_target): Same.
	* tree-core.h (enum omp_clause_code): Same.
	* tree-nested.cc (convert_nonlocal_omp_clauses): Same.
	(convert_local_omp_clauses): Same.
	* tree-pretty-print.cc (dump_omp_clause): Same.
	* tree.cc: Same.

libgomp/ChangeLog:

	* libgomp.texi: Updated entry for HAS_DEVICE_ADDR.
	* target.c (copy_firstprivate_data): Copy only if host address is not
	NULL.
	* testsuite/libgomp.c++/target-has-device-addr-2.C: New test.
	* testsuite/libgomp.c++/target-has-device-addr-4.C: New test.
	* testsuite/libgomp.c++/target-has-device-addr-5.C: New test.
	* testsuite/libgomp.c++/target-has-device-addr-6.C: New test.
	* testsuite/libgomp.c-c++-common/target-has-device-addr-1.c: New test.
	* testsuite/libgomp.c/target-has-device-addr-3.c: New test.
	* testsuite/libgomp.fortran/target-has-device-addr-1.f90: New test.
	* testsuite/libgomp.fortran/target-has-device-addr-2.f90: New test.
	* testsuite/libgomp.fortran/target-has-device-addr-3.f90: New test.
	* testsuite/libgomp.fortran/target-has-device-addr-4.f90: New test.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/clauses-1.c: Added has_device_addr to test cases.
	* g++.dg/gomp/attrs-1.C: Added has_device_addr to test cases.
	* g++.dg/gomp/attrs-2.C: Added has_device_addr to test cases.
	* c-c++-common/gomp/target-has-device-addr-1.c: New test.
	* c-c++-common/gomp/target-has-device-addr-2.c: New test.
	* c-c++-common/gomp/target-is-device-ptr-1.c: New test.
	* c-c++-common/gomp/target-is-device-ptr-2.c: New test.
	* gfortran.dg/gomp/is_device_ptr-3.f90: New test.
	* gfortran.dg/gomp/target-has-device-addr-1.f90: New test.
	* gfortran.dg/gomp/target-has-device-addr-2.f90: New test.

diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 0251aec..f5314d6 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -1862,6 +1862,7 @@ c_omp_split_clauses (location_t loc, enum tree_code code,
 	case OMP_CLAUSE_DEVICE:
 	case OMP_CLAUSE_MAP:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_DEFAULTMAP:
 	case OMP_CLAUSE_DEPEND:
 	  s = C_OMP_CLAUSE_SPLIT_TARGET;
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 6e6c806..54864c2 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -89,8 +89,8 @@ enum pragma_kind {
 };
 
 
-/* All clauses defined by OpenACC 2.0, and OpenMP 2.5, 3.0, 3.1, 4.0, 4.5
-   and 5.0.  Used internally by both C and C++ parsers.  */
+/* All clauses defined by OpenACC 2.0, and OpenMP 2.5, 3.0, 3.1, 4.0, 4.5, 5.0,
+   and 5.1.  Used internally by both C and C++ parsers.  */
 enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_NONE = 0,
 
@@ -114,6 +114,7 @@ enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_FOR,
   PRAGMA_OMP_CLAUSE_FROM,
   PRAGMA_OMP_CLAUSE_GRAINSIZE,
+  PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR,
   PRAGMA_OMP_CLAUSE_HINT,
   PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OMP_CLAUSE_IN_REDUCTION,
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index e9086c5..8aca415 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -12772,7 +12772,9 @@ c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
 	  break;
 	case 'h':
-	  if (!strcmp ("hint", p))
+	  if (!strcmp ("has_device_addr", p))
+	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  else if (!strcmp ("hint", p))
 	    result = PRAGMA_OMP_CLAUSE_HINT;
 	  else if (!strcmp ("host", p))
 	    result = PRAGMA_OACC_CLAUSE_HOST;
@@ -13165,6 +13167,7 @@ c_parser_omp_variable_list (c_parser *parser,
 	    case OMP_CLAUSE_REDUCTION:
 	    case OMP_CLAUSE_IN_REDUCTION:
 	    case OMP_CLAUSE_TASK_REDUCTION:
+	    case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	      array_section_p = false;
 	      dims.truncate (0);
 	      while (c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
@@ -14325,6 +14328,16 @@ c_parser_omp_clause_use_device_addr (c_parser *parser, tree list)
 				       list);
 }
 
+/* OpenMP 5.1:
+   has_device_addr ( variable-list ) */
+
+static tree
+c_parser_omp_clause_has_device_addr (c_parser *parser, tree list)
+{
+  return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
+				       list);
+}
+
 /* OpenMP 4.5:
    is_device_ptr ( variable-list ) */
 
@@ -17053,6 +17066,10 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_omp_clause_use_device_addr (parser, clauses);
 	  c_name = "use_device_addr";
 	  break;
+	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  clauses = c_parser_omp_clause_has_device_addr (parser, clauses);
+	  c_name = "has_device_addr";
+	  break;
 	case PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR:
 	  clauses = c_parser_omp_clause_is_device_ptr (parser, clauses);
 	  c_name = "is_device_ptr";
@@ -21035,7 +21052,8 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_THREAD_LIMIT)	\
-	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
 
 static bool
 c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p)
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index b06f078..61bc4f6 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -13800,6 +13800,8 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	}
       first = c_fully_fold (first, false, NULL);
       OMP_CLAUSE_DECL (c) = first;
+      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	return false;
       if (size)
 	size = c_fully_fold (size, false, NULL);
       OMP_CLAUSE_SIZE (c) = size;
@@ -14105,7 +14107,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 {
   bitmap_head generic_head, firstprivate_head, lastprivate_head;
   bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
-  bitmap_head oacc_reduction_head;
+  bitmap_head oacc_reduction_head, is_on_device_head;
   tree c, t, type, *pc;
   tree simdlen = NULL_TREE, safelen = NULL_TREE;
   bool branch_seen = false;
@@ -14141,6 +14143,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   /* If ort == C_ORT_OMP used as nontemporal_head or use_device_xxx_head
      instead and for ort == C_ORT_OMP_TARGET used as in_reduction_head.  */
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
+  bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
   if (ort & C_ORT_ACC)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
@@ -14569,7 +14572,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qE appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+	  else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
@@ -15183,7 +15188,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		   || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -15268,6 +15274,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
 	  if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
@@ -15288,6 +15296,24 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  goto check_dup_generic;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  t = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (t) == TREE_LIST)
+	    {
+	      if (handle_omp_array_sections (c, ort))
+		remove = true;
+	      else
+		{
+		  t = OMP_CLAUSE_DECL (c);
+		  while (TREE_CODE (t) == ARRAY_REF)
+		    t = TREE_OPERAND (t, 0);
+		}
+	    }
+	  bitmap_set_bit (&is_on_device_head, DECL_UID (t));
+	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
+	    c_mark_addressable (t);
+	  goto check_dup_generic_t;
+
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  t = OMP_CLAUSE_DECL (c);
 	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 00279c4..d4bdf30 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -36327,7 +36327,9 @@ cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
 	  break;
 	case 'h':
-	  if (!strcmp ("hint", p))
+	  if (!strcmp ("has_device_addr", p))
+	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  else if (!strcmp ("hint", p))
 	    result = PRAGMA_OMP_CLAUSE_HINT;
 	  else if (!strcmp ("host", p))
 	    result = PRAGMA_OACC_CLAUSE_HOST;
@@ -36630,6 +36632,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	    case OMP_CLAUSE_REDUCTION:
 	    case OMP_CLAUSE_IN_REDUCTION:
 	    case OMP_CLAUSE_TASK_REDUCTION:
+	    case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	      array_section_p = false;
 	      dims.truncate (0);
 	      while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
@@ -40071,6 +40074,11 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
 					    clauses);
 	  c_name = "is_device_ptr";
 	  break;
+	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
+					    clauses);
+	  c_name = "has_device_addr";
+	  break;
 	case PRAGMA_OMP_CLAUSE_IF:
 	  clauses = cp_parser_omp_clause_if (parser, clauses, token->location,
 					     true);
@@ -44251,7 +44259,8 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_THREAD_LIMIT)	\
-	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
 
 static bool
 cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 8e22944..066320e 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5642,6 +5642,8 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	      return false;
 	    }
 	  OMP_CLAUSE_DECL (c) = first;
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    return false;
 	  OMP_CLAUSE_SIZE (c) = size;
 	  if (TREE_CODE (t) == FIELD_DECL)
 	    t = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
@@ -6671,7 +6673,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 {
   bitmap_head generic_head, firstprivate_head, lastprivate_head;
   bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
-  bitmap_head oacc_reduction_head;
+  bitmap_head oacc_reduction_head, is_on_device_head;
   tree c, t, *pc;
   tree safelen = NULL_TREE;
   bool branch_seen = false;
@@ -6704,6 +6706,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   /* If ort == C_ORT_OMP used as nontemporal_head or use_device_xxx_head
      instead and for ort == C_ORT_OMP_TARGET used as in_reduction_head.  */
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
+  bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
   if (ort & C_ORT_ACC)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
@@ -7002,7 +7005,9 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+	  else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
@@ -8226,7 +8231,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		   || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -8485,6 +8491,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	  field_ok = (ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP;
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	    bitmap_set_bit (&is_on_device_head, DECL_UID (t));
 	  if (!type_dependent_expression_p (t))
 	    {
 	      tree type = TREE_TYPE (t);
@@ -8514,6 +8522,25 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  goto check_dup_generic;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  t = OMP_CLAUSE_DECL (c);
+	  if (TREE_CODE (t) == TREE_LIST)
+	    {
+	      if (handle_omp_array_sections (c, ort))
+		remove = true;
+	      else
+		{
+		  t = OMP_CLAUSE_DECL (c);
+		  while (TREE_CODE (t) == INDIRECT_REF
+			 || TREE_CODE (t) == ARRAY_REF)
+		    t = TREE_OPERAND (t, 0);
+		}
+	    }
+	  bitmap_set_bit (&is_on_device_head, DECL_UID (t));
+	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
+	    cxx_mark_addressable (t);
+	  goto check_dup_generic_t;
+
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  field_ok = true;
 	  t = OMP_CLAUSE_DECL (c);
diff --git a/gcc/fortran/dump-parse-tree.cc b/gcc/fortran/dump-parse-tree.cc
index a618ae2..b56ed85 100644
--- a/gcc/fortran/dump-parse-tree.cc
+++ b/gcc/fortran/dump-parse-tree.cc
@@ -1683,6 +1683,7 @@ show_omp_clauses (gfc_omp_clauses *omp_clauses)
 	  case OMP_LIST_CACHE: type = "CACHE"; break;
 	  case OMP_LIST_IS_DEVICE_PTR: type = "IS_DEVICE_PTR"; break;
 	  case OMP_LIST_USE_DEVICE_PTR: type = "USE_DEVICE_PTR"; break;
+	  case OMP_LIST_HAS_DEVICE_ADDR: type = "HAS_DEVICE_ADDR"; break;
 	  case OMP_LIST_USE_DEVICE_ADDR: type = "USE_DEVICE_ADDR"; break;
 	  case OMP_LIST_NONTEMPORAL: type = "NONTEMPORAL"; break;
 	  case OMP_LIST_ALLOCATE: type = "ALLOCATE"; break;
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 00a558a..74bb24b 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -1393,7 +1393,8 @@ enum
   OMP_LIST_USE_DEVICE_ADDR,
   OMP_LIST_NONTEMPORAL,
   OMP_LIST_ALLOCATE,
-  OMP_LIST_NUM
+  OMP_LIST_HAS_DEVICE_ADDR,
+  OMP_LIST_NUM /* Must be the last.  */
 };
 
 /* Because a symbol can belong to multiple namelists, they must be
diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index 9b73b9f..845a763 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -926,7 +926,7 @@ enum omp_mask1
   OMP_MASK1_LAST
 };
 
-/* OpenACC 2.0+ specific clauses. */
+/* More OpenMP clauses and OpenACC 2.0+ specific clauses. */
 enum omp_mask2
 {
   OMP_CLAUSE_ASYNC,
@@ -955,6 +955,7 @@ enum omp_mask2
   OMP_CLAUSE_FINALIZE,
   OMP_CLAUSE_ATTACH,
   OMP_CLAUSE_NOHOST,
+  OMP_CLAUSE_HAS_DEVICE_ADDR,  /* OpenMP 5.1  */
   /* This must come last.  */
   OMP_MASK2_LAST
 };
@@ -2151,6 +2152,11 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
 	    }
 	  break;
 	case 'h':
+	  if ((mask & OMP_CLAUSE_HAS_DEVICE_ADDR)
+	      && gfc_match_omp_variable_list
+		   ("has_device_addr (", &c->lists[OMP_LIST_HAS_DEVICE_ADDR],
+		    false, NULL, NULL, true) == MATCH_YES)
+	    continue;
 	  if ((mask & OMP_CLAUSE_HINT)
 	      && (m = gfc_match_dupl_check (!c->hint, "hint", true, &c->hint))
 		 != MATCH_NO)
@@ -2923,8 +2929,8 @@ gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
 	    continue;
 	  if ((mask & OMP_CLAUSE_USE_DEVICE_ADDR)
 	      && gfc_match_omp_variable_list
-		   ("use_device_addr (",
-		    &c->lists[OMP_LIST_USE_DEVICE_ADDR], false) == MATCH_YES)
+		   ("use_device_addr (", &c->lists[OMP_LIST_USE_DEVICE_ADDR],
+		    false, NULL, NULL, true) == MATCH_YES)
 	    continue;
 	  break;
 	case 'v':
@@ -3651,7 +3657,8 @@ cleanup:
    | OMP_CLAUSE_DEPEND | OMP_CLAUSE_NOWAIT | OMP_CLAUSE_PRIVATE		\
    | OMP_CLAUSE_FIRSTPRIVATE | OMP_CLAUSE_DEFAULTMAP			\
    | OMP_CLAUSE_IS_DEVICE_PTR | OMP_CLAUSE_IN_REDUCTION			\
-   | OMP_CLAUSE_THREAD_LIMIT | OMP_CLAUSE_ALLOCATE)
+   | OMP_CLAUSE_THREAD_LIMIT | OMP_CLAUSE_ALLOCATE			\
+   | OMP_CLAUSE_HAS_DEVICE_ADDR)
 #define OMP_TARGET_DATA_CLAUSES \
   (omp_mask (OMP_CLAUSE_DEVICE) | OMP_CLAUSE_MAP | OMP_CLAUSE_IF	\
    | OMP_CLAUSE_USE_DEVICE_PTR | OMP_CLAUSE_USE_DEVICE_ADDR)
@@ -6283,7 +6290,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 	"IN_REDUCTION", "TASK_REDUCTION",
 	"DEVICE_RESIDENT", "LINK", "USE_DEVICE",
 	"CACHE", "IS_DEVICE_PTR", "USE_DEVICE_PTR", "USE_DEVICE_ADDR",
-	"NONTEMPORAL", "ALLOCATE" };
+	"NONTEMPORAL", "ALLOCATE", "HAS_DEVICE_ADDR" };
   STATIC_ASSERT (ARRAY_SIZE (clause_names) == OMP_LIST_NUM);
 
   if (omp_clauses == NULL)
@@ -7132,6 +7139,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
 			     n->sym->name, name, &n->where);
 	      }
 	    break;
+	  case OMP_LIST_HAS_DEVICE_ADDR:
 	  case OMP_LIST_USE_DEVICE_PTR:
 	  case OMP_LIST_USE_DEVICE_ADDR:
 	    /* FIXME: Handle OMP_LIST_USE_DEVICE_PTR.  */
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index 9eabf68..3b86df0 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -1910,7 +1910,17 @@ gfc_trans_omp_variable_list (enum omp_clause_code code,
 	tree t = gfc_trans_omp_variable (namelist->sym, declare_simd);
 	if (t != error_mark_node)
 	  {
-	    tree node = build_omp_clause (input_location, code);
+	    tree node;
+	    /* For HAS_DEVICE_ADDR of an array descriptor, firstprivatize the
+	       descriptor such that the bounds are available; its data component
+	       is unmodified; it is handled as device address inside target. */
+	    if (code == OMP_CLAUSE_HAS_DEVICE_ADDR
+		&& (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (t))
+		    || (POINTER_TYPE_P (TREE_TYPE (t))
+			&& GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (TREE_TYPE (t))))))
+	      node = build_omp_clause (input_location, OMP_CLAUSE_FIRSTPRIVATE);
+	    else
+	      node = build_omp_clause (input_location, code);
 	    OMP_CLAUSE_DECL (node) = t;
 	    list = gfc_trans_add_clause (node, list);
 
@@ -2604,6 +2614,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 	case OMP_LIST_IS_DEVICE_PTR:
 	  clause_code = OMP_CLAUSE_IS_DEVICE_PTR;
 	  goto add_clause;
+	case OMP_LIST_HAS_DEVICE_ADDR:
+	  clause_code = OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  goto add_clause;
 	case OMP_LIST_NONTEMPORAL:
 	  clause_code = OMP_CLAUSE_NONTEMPORAL;
 	  goto add_clause;
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index bf2f60c..bbe8ec4 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -10275,6 +10275,14 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  flags = GOVD_EXPLICIT;
 	  goto do_add;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  decl = OMP_CLAUSE_DECL (c);
+	  while (TREE_CODE (decl) == INDIRECT_REF
+		 || TREE_CODE (decl) == ARRAY_REF)
+	    decl = TREE_OPERAND (decl, 0);
+	  flags = GOVD_EXPLICIT;
+	  goto do_add_decl;
+
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT;
 	  goto do_add;
@@ -11425,6 +11433,16 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	    }
 	  break;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  decl = OMP_CLAUSE_DECL (c);
+	  while (TREE_CODE (decl) == INDIRECT_REF
+		 || TREE_CODE (decl) == ARRAY_REF)
+	    decl = TREE_OPERAND (decl, 0);
+	  n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl);
+	  remove = n == NULL || !(n->value & GOVD_SEEN);
+	  break;
+
+	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_NONTEMPORAL:
 	  decl = OMP_CLAUSE_DECL (c);
 	  n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl);
@@ -11726,7 +11744,6 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	case OMP_CLAUSE_DETACH:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
-	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_ASYNC:
 	case OMP_CLAUSE_WAIT:
 	case OMP_CLAUSE_INDEPENDENT:
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index c33b3da..0652084 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -1375,7 +1375,8 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	  decl = OMP_CLAUSE_DECL (c);
 	do_private:
 	  if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
-	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR
+	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      && is_gimple_omp_offloaded (ctx->stmt))
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE)
@@ -1383,8 +1384,14 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 		  by_ref = !omp_privatize_by_reference (decl);
 		  install_var_field (decl, by_ref, 3, ctx);
 		}
+	      else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+		{
+		  if (TREE_CODE (decl) == INDIRECT_REF)
+		    decl = TREE_OPERAND (decl, 0);
+		  install_var_field (decl, true, 3, ctx);
+		}
 	      else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
-		install_var_field (decl, true, 3, ctx);
+		  install_var_field (decl, true, 3, ctx);
 	      else
 		install_var_field (decl, false, 3, ctx);
 	    }
@@ -1452,6 +1459,13 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	  install_var_local (decl, ctx);
 	  break;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  decl = OMP_CLAUSE_DECL (c);
+	  while (TREE_CODE (decl) == INDIRECT_REF
+		 || TREE_CODE (decl) == ARRAY_REF)
+	    decl = TREE_OPERAND (decl, 0);
+	  goto do_private;
+
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  decl = OMP_CLAUSE_DECL (c);
 	  goto do_private;
@@ -1729,12 +1743,21 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	case OMP_CLAUSE_FIRSTPRIVATE:
 	case OMP_CLAUSE_PRIVATE:
 	case OMP_CLAUSE_LINEAR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  decl = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    {
+	      while (TREE_CODE (decl) == INDIRECT_REF
+		     || TREE_CODE (decl) == ARRAY_REF)
+		decl = TREE_OPERAND (decl, 0);
+	    }
+
 	  if (is_variable_sized (decl))
 	    {
 	      if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
-		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		  && is_gimple_omp_offloaded (ctx->stmt))
 		{
 		  tree decl2 = DECL_VALUE_EXPR (decl);
@@ -12819,8 +12842,15 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
       case OMP_CLAUSE_USE_DEVICE_PTR:
       case OMP_CLAUSE_USE_DEVICE_ADDR:
+      case OMP_CLAUSE_HAS_DEVICE_ADDR:
       case OMP_CLAUSE_IS_DEVICE_PTR:
 	var = OMP_CLAUSE_DECL (c);
+	if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	  {
+	    while (TREE_CODE (var) == INDIRECT_REF
+		   || TREE_CODE (var) == ARRAY_REF)
+	      var = TREE_OPERAND (var, 0);
+	  }
 	map_cnt++;
 	if (is_variable_sized (var))
 	  {
@@ -12835,7 +12865,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    SET_DECL_VALUE_EXPR (new_var, x);
 	    DECL_HAS_VALUE_EXPR_P (new_var) = 1;
 	  }
-	else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		  && !omp_privatize_by_reference (var)
 		  && !omp_is_allocatable_or_ptr (var)
 		  && !lang_hooks.decls.omp_array_data (var, true))
@@ -13301,17 +13332,26 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
 	    ovar = OMP_CLAUSE_DECL (c);
+	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	      {
+		while (TREE_CODE (ovar) == INDIRECT_REF
+		       || TREE_CODE (ovar) == ARRAY_REF)
+		  ovar = TREE_OPERAND (ovar, 0);
+	      }
 	    var = lookup_decl_in_outer_ctx (ovar, ctx);
 
 	    if (lang_hooks.decls.omp_array_data (ovar, true))
 	      {
-		tkind = (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		tkind = ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+			  && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 			 ? GOMP_MAP_USE_DEVICE_PTR : GOMP_MAP_FIRSTPRIVATE_INT);
 		x = build_sender_ref ((splay_tree_key) &DECL_NAME (ovar), ctx);
 	      }
-	    else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR)
+	    else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		     && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      {
 		tkind = GOMP_MAP_USE_DEVICE_PTR;
 		x = build_sender_ref ((splay_tree_key) &DECL_UID (ovar), ctx);
@@ -13333,7 +13373,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    type = TREE_TYPE (ovar);
 	    if (lang_hooks.decls.omp_array_data (ovar, true))
 	      var = lang_hooks.decls.omp_array_data (ovar, false);
-	    else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	    else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		      && !omp_privatize_by_reference (ovar)
 		      && !omp_is_allocatable_or_ptr (ovar))
 		     || TREE_CODE (type) == ARRAY_TYPE)
@@ -13348,6 +13389,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		    if (POINTER_TYPE_P (type)
 			&& TREE_CODE (type) != ARRAY_TYPE
 			&& ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_USE_DEVICE_ADDR
+			    && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR
 			    && !omp_is_allocatable_or_ptr (ovar))
 			   || (omp_privatize_by_reference (ovar)
 			       && omp_is_allocatable_or_ptr (ovar))))
@@ -13545,6 +13587,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    break;
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
 	    tree new_var;
 	    gimple_seq assign_body;
@@ -13555,12 +13598,21 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    var = OMP_CLAUSE_DECL (c);
 	    is_array_data = lang_hooks.decls.omp_array_data (var, true) != NULL;
 
-	    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR)
+	    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      x = build_sender_ref (is_array_data
 				    ? (splay_tree_key) &DECL_NAME (var)
 				    : (splay_tree_key) &DECL_UID (var), ctx);
 	    else
-	      x = build_receiver_ref (var, false, ctx);
+	      {
+		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+		  {
+		    while (TREE_CODE (var) == INDIRECT_REF
+			   || TREE_CODE (var) == ARRAY_REF)
+		      var = TREE_OPERAND (var, 0);
+		  }
+		x = build_receiver_ref (var, false, ctx);
+	      }
 
 	    if (is_array_data)
 	      {
@@ -13607,7 +13659,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		gimple_seq_add_stmt (&assign_body,
 				     gimple_build_assign (new_var, x));
 	      }
-	    else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	    else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		      && !omp_privatize_by_reference (var)
 		      && !omp_is_allocatable_or_ptr (var))
 		     || TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE)
@@ -13630,7 +13683,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		    type = TREE_TYPE (type);
 		    if (POINTER_TYPE_P (type)
 			&& TREE_CODE (type) != ARRAY_TYPE
-			&& (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_USE_DEVICE_ADDR
+			&& ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_USE_DEVICE_ADDR
+			    && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 			    || (omp_privatize_by_reference (var)
 				&& omp_is_allocatable_or_ptr (var))))
 		      {
@@ -13653,7 +13707,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 				     gimple_build_assign (new_var, x));
 	      }
 	    tree present;
-	    present = (do_optional_check
+	    present = ((do_optional_check
+			&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 		       ? omp_check_optional_argument (OMP_CLAUSE_DECL (c), true)
 		       : NULL_TREE);
 	    if (present)
diff --git a/gcc/testsuite/c-c++-common/gomp/clauses-1.c b/gcc/testsuite/c-c++-common/gomp/clauses-1.c
index 3ff49e0..71ca41c 100644
--- a/gcc/testsuite/c-c++-common/gomp/clauses-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/clauses-1.c
@@ -102,7 +102,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm)
 {
   #pragma omp for simd \
@@ -138,20 +138,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   #pragma omp target parallel \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
     ;
   #pragma omp target parallel for \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) ordered schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target parallel for \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target parallel for simd \
@@ -159,18 +159,19 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte - 1:nte) thread_limit(tl) nowait depend(inout: dd[0]) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
     ;
   #pragma omp target teams distribute \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) order(concurrent) \
-    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) \
+    has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ;
   #pragma omp target teams distribute parallel for \
@@ -179,7 +180,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1) dist_schedule(static, 16) \
     if (parallel: i2) num_threads (nth) proc_bind(spread) \
     lastprivate (l) schedule(static, 4) nowait depend(inout: dd[0]) order(concurrent) \
-     allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+     allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams distribute parallel for simd \
@@ -189,7 +190,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) num_threads (nth) proc_bind(spread) \
     lastprivate (l) schedule(static, 4) order(concurrent) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams distribute simd \
@@ -197,14 +198,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s) default(shared) reduction(+:r) num_teams(nte-1:nte) thread_limit(tl) \
     collapse(1) dist_schedule(static, 16) order(concurrent) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target simd \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     safelen(8) simdlen(4) lastprivate (l) linear(ll: 1) aligned(q: 32) reduction(+:r) \
     nowait depend(inout: dd[0]) nontemporal(ntm) if(simd:i3) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp taskgroup task_reduction(+:r2) allocate (r2)
@@ -430,28 +431,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     nowait depend(inout: dd[0]) lastprivate (l) bind(parallel) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target parallel loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     nowait depend(inout: dd[0]) lastprivate (l) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target teams loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte-1:nte) thread_limit(tl) nowait depend(inout: dd[0]) \
     lastprivate (l) bind(teams) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target teams loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0]) \
     lastprivate (l) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c
new file mode 100644
index 0000000..ebf55ee
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c
@@ -0,0 +1,65 @@
+/* { dg-do compile } */
+
+void
+foo ()
+{
+  int * x;
+  #pragma omp target is_device_ptr(x) has_device_addr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target has_device_addr(x) is_device_ptr(x) /* { dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  int y = 42;
+  #pragma omp target has_device_addr(y) has_device_addr(y) /* { dg-error "'y' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target private(y) has_device_addr(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+  #pragma omp target has_device_addr(y) private(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+  #pragma omp target firstprivate(y) has_device_addr(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target has_device_addr(y) map(y) /* { dg-error "'y' appears both in data and map clauses" } */
+  ;
+  #pragma omp target map(y) has_device_addr(y) /* { dg-error "'y' appears both in data and map clauses" } */
+  ;
+
+  int z[3] = { 2, 5, 7 };
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1:])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[1:2])
+    ;
+
+  #pragma omp target data map(z[:3]) use_device_addr(z)
+    #pragma omp target has_device_addr(z[:2])
+    ;
+
+  int w[3][4];
+  #pragma omp target data map(w) use_device_addr(w)
+    #pragma omp target has_device_addr(w[1][2])
+    ;
+
+  #pragma omp target data map(w) use_device_addr(w)
+    #pragma omp target has_device_addr(w[:1][2:])
+    ;
+
+  int u[0];
+  #pragma omp target data map(u) use_device_addr(u)
+    #pragma omp target has_device_addr(u)
+    ;
+
+  struct S { int m; } s;
+  s.m = 42;
+  #pragma omp target data map (s) use_device_addr (s)
+    #pragma omp target has_device_addr (s)
+      ++s.m;
+
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-2.c b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-2.c
new file mode 100644
index 0000000..7378416
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -fdump-tree-gimple" } */
+
+void
+foo ()
+{
+  int x, y;
+
+  #pragma omp target data map(x, y) use_device_addr(x, y)
+    #pragma omp target has_device_addr(x, y)
+      {
+	x = 42;
+      }
+}
+
+/* { dg-final { scan-tree-dump "has_device_addr\\(x\\)"  "gimple" } } */
+/* { dg-final { scan-tree-dump-not "has_device_addr\\(y\\)"  "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-1.c b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-1.c
new file mode 100644
index 0000000..ecf30ca
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+
+void
+foo ()
+{
+  int *x;
+
+  #pragma omp target is_device_ptr(x) is_device_ptr(x) /* { dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target private(x) is_device_ptr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target is_device_ptr(x) private(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target firstprivate(x) is_device_ptr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target is_device_ptr(x) map(x) /* { dg-error "'x' appears both in data and map clauses" } */
+  ;
+  #pragma omp target map(x) is_device_ptr(x) /* { dg-error "'x' appears both in data and map clauses" } */
+  ;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-2.c b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-2.c
new file mode 100644
index 0000000..df743dd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp -fdump-tree-gimple" } */
+
+void
+foo ()
+{
+  int *x, *y;
+
+  #pragma omp target data map(x, y) use_device_ptr(x, y)
+    #pragma omp target is_device_ptr(x, y)
+      {
+	*x = 42;
+      }
+}
+
+/* { dg-final { scan-tree-dump "is_device_ptr\\(x\\)"  "gimple" } } */
+/* { dg-final { scan-tree-dump-not "is_device_ptr\\(y\\)"  "gimple" } } */
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-1.C b/gcc/testsuite/g++.dg/gomp/attrs-1.C
index 319ad32..f64b078 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-1.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-1.C
@@ -121,7 +121,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
      const char *msg)
 {
@@ -185,20 +185,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   [[omp::directive (target parallel
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
     ;
   [[omp::directive (target parallel for
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) ordered schedule(static, 4) collapse(1) nowait depend(inout: dd[0])
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target parallel for
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (omp::directive (target parallel for simd
@@ -206,22 +206,23 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (target teams
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte-1:nte) thread_limit(tl) nowait depend(inout: dd[0])
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
     ;
   [[omp::sequence (directive (target
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
     ;
   [[omp::sequence (omp::directive (target teams distribute
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) order(concurrent)
-    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ;
   [[omp::directive (target teams distribute parallel for
@@ -230,7 +231,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1) dist_schedule(static, 16)
     if (parallel: i2) num_threads (nth) proc_bind(spread)
     lastprivate (l) schedule(static, 4) nowait depend(inout: dd[0]) order(concurrent)
-     allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+     allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute parallel for simd
@@ -240,7 +241,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) num_threads (nth) proc_bind(spread)
     lastprivate (l) schedule(static, 4) order(concurrent)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute simd
@@ -248,14 +249,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s) default(shared) reduction(+:r) num_teams(nte-1:nte) thread_limit(tl)
     collapse(1) dist_schedule(static, 16) order(concurrent)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target simd
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     safelen(8) simdlen(4) lastprivate (l) linear(ll: 1) aligned(q: 32) reduction(+:r)
     nowait depend(inout: dd[0]) nontemporal(ntm) if(simd:i3) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (taskgroup task_reduction(+:r2) allocate (r2)),
@@ -515,28 +516,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     nowait depend(inout: dd[0]) lastprivate (l) bind(parallel) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target parallel loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     nowait depend(inout: dd[0]) lastprivate (l) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0])
     lastprivate (l) bind(teams) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte - 1 : nte) thread_limit(tl) nowait depend(inout: dd[0])
     lastprivate (l) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (critical)]] {
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-2.C b/gcc/testsuite/g++.dg/gomp/attrs-2.C
index 955b2dd..cc91fa2 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-2.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-2.C
@@ -121,7 +121,7 @@ baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
      const char *msg)
 {
@@ -185,20 +185,20 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   [[omp::directive (target parallel,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread)
-    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
     ;
   [[omp::directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),ordered schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[using omp:directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (omp::directive (target parallel for simd,
@@ -206,22 +206,23 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),if (simd: i3),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[using omp:sequence (directive (target teams,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
-    shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait, depend(inout: dd[0]),
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
     ;
   [[using omp:sequence (directive (target,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
-    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr(hda)))]]
     ;
   [[omp::sequence (omp::directive (target teams distribute,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte-1:nte),thread_limit(tl),order(concurrent),
-    collapse(1),dist_schedule(static, 16),nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    collapse(1),dist_schedule(static, 16),nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),
+    has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ;
   [[omp::directive (target teams distribute parallel for,
@@ -230,7 +231,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1),dist_schedule(static, 16),
     if (parallel: i2),num_threads (nth),proc_bind(spread),
     lastprivate (l),schedule(static, 4),nowait depend(inout: dd[0]),order(concurrent),
-     allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute parallel for simd,
@@ -240,7 +241,7 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2),num_threads (nth),proc_bind(spread),
     lastprivate (l),schedule(static, 4),order(concurrent),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),if (simd: i3),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute simd,
@@ -248,14 +249,14 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),
     collapse(1),dist_schedule(static, 16),order(concurrent),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target simd,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     safelen(8),simdlen(4),lastprivate (l),linear(ll: 1),aligned(q: 32),reduction(+:r),
     nowait depend(inout: dd[0]),nontemporal(ntm),if(simd:i3),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (taskgroup, task_reduction(+:r2), allocate (r2)),
@@ -515,28 +516,28 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     nowait depend(inout: dd[0]),lastprivate (l),bind(parallel),order(concurrent),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target parallel loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     nowait depend(inout: dd[0]),lastprivate (l),order(concurrent),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte-1:nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     lastprivate (l),bind(teams),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     lastprivate (l),order(concurrent),collapse(1)
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (critical)]] {
diff --git a/gcc/testsuite/gfortran.dg/gomp/is_device_ptr-3.f90 b/gcc/testsuite/gfortran.dg/gomp/is_device_ptr-3.f90
new file mode 100644
index 0000000..c3de772
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/is_device_ptr-3.f90
@@ -0,0 +1,27 @@
+! Test to ensure that IS_DEVICE_PTR is removed for non-used variables.
+
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+program main
+  use iso_c_binding
+  implicit none
+
+  integer :: x, y
+  call foo (x, y)
+
+contains
+  subroutine foo (a, b)
+    integer, target :: a, b
+
+    !$omp target data map(a, b) use_device_ptr(a, b)
+      !$omp target is_device_ptr(a, b)
+        a = 42
+      !$omp end target
+    !$omp end target data
+  end subroutine foo
+
+end program main
+
+! { dg-final { scan-tree-dump "is_device_ptr\\(a\\)"  "gimple" } }
+! { dg-final { scan-tree-dump-not "is_device_ptr\\(b\\)"  "gimple" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-1.f90 b/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-1.f90
new file mode 100644
index 0000000..db3fa46
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-1.f90
@@ -0,0 +1,36 @@
+! { dg-do compile }
+
+implicit none
+
+integer, target :: x
+integer, pointer :: ptr
+integer :: a(5)
+
+!$omp target has_device_addr(x)
+!$omp end target
+!$omp target has_device_addr(ptr)
+!$omp end target
+!$omp target has_device_addr(a)
+!$omp end target
+!$omp target has_device_addr(a(2:3))
+!$omp end target
+!$omp target has_device_addr(a(:3))
+!$omp end target
+!$omp target has_device_addr(a(2:))
+!$omp end target
+!$omp target has_device_addr(a(2))
+!$omp end target
+
+!$omp target has_device_addr(x) has_device_addr(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+
+!$omp target private(x) has_device_addr(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+!$omp target has_device_addr(x) private(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+!$omp target firstprivate(x) has_device_addr(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+!$omp target has_device_addr(x) firstprivate(x)  ! { dg-error "'x' present on multiple clauses" }
+!$omp end target
+
+end
\ No newline at end of file
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-2.f90 b/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-2.f90
new file mode 100644
index 0000000..7fc92b3
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-has-device-addr-2.f90
@@ -0,0 +1,27 @@
+! Test to ensure that HAS_DEVICE_ADDR is removed for non-used variables.
+
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+program main
+  use iso_c_binding
+  implicit none
+
+  integer :: x, y
+  call foo (x, y)
+
+contains
+  subroutine foo (a, b)
+    integer :: a, b
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        a = 42
+      !$omp end target
+    !$omp end target data
+  end subroutine foo
+
+end program main
+
+! { dg-final { scan-tree-dump "has_device_addr\\(a\\)"  "gimple" } }
+! { dg-final { scan-tree-dump-not "has_device_addr\\(b\\)"  "gimple" } }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index e83669f..2682d2c 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -342,6 +342,9 @@ enum omp_clause_code {
      OpenMP clause: map ({alloc:,to:,from:,tofrom:,}variable-list).  */
   OMP_CLAUSE_MAP,
 
+  /* OpenMP clause: has_device_addr (variable-list).  */
+  OMP_CLAUSE_HAS_DEVICE_ADDR,
+
   /* Internal structure to hold OpenACC cache directive's variable-list.
      #pragma acc cache (variable-list).  */
   OMP_CLAUSE__CACHE_,
diff --git a/gcc/tree-nested.cc b/gcc/tree-nested.cc
index b7e9a3b..078ceab 100644
--- a/gcc/tree-nested.cc
+++ b/gcc/tree-nested.cc
@@ -1339,6 +1339,7 @@ convert_nonlocal_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
 	case OMP_CLAUSE_LINK:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_DETACH:
 	do_decl_clause:
@@ -2123,6 +2124,7 @@ convert_local_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
 	case OMP_CLAUSE_LINK:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_DETACH:
 	do_decl_clause:
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index 6130c30..14351fa 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -493,6 +493,9 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
     case OMP_CLAUSE_USE_DEVICE_ADDR:
       name = "use_device_addr";
       goto print_remap;
+    case OMP_CLAUSE_HAS_DEVICE_ADDR:
+      name = "has_device_addr";
+      goto print_remap;
     case OMP_CLAUSE_IS_DEVICE_PTR:
       name = "is_device_ptr";
       goto print_remap;
diff --git a/gcc/tree.cc b/gcc/tree.cc
index ae159ee..307b1f6 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -306,6 +306,7 @@ unsigned const char omp_clause_num_ops[] =
   2, /* OMP_CLAUSE_FROM  */
   2, /* OMP_CLAUSE_TO  */
   2, /* OMP_CLAUSE_MAP  */
+  1, /* OMP_CLAUSE_HAS_DEVICE_ADDR  */
   2, /* OMP_CLAUSE__CACHE_  */
   2, /* OMP_CLAUSE_GANG  */
   1, /* OMP_CLAUSE_ASYNC  */
@@ -395,6 +396,7 @@ const char * const omp_clause_code_name[] =
   "from",
   "to",
   "map",
+  "has_device_addr",
   "_cache_",
   "gang",
   "async",
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 3be9de5..336f16d 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -293,7 +293,7 @@ The OpenMP 4.5 specification is fully supported.
 @item @code{align} clause/modifier in @code{allocate} directive/clause
       and @code{allocator} directive @tab P @tab C/C++ on clause only
 @item @code{thread_limit} clause to @code{target} construct @tab Y @tab
-@item @code{has_device_addr} clause to @code{target} construct @tab N @tab
+@item @code{has_device_addr} clause to @code{target} construct @tab Y @tab
 @item iterators in @code{target update} motion clauses and @code{map}
       clauses @tab N @tab
 @item indirect calls to the device version of a procedure or function in
diff --git a/libgomp/target.c b/libgomp/target.c
index 698ff14..9017458 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2510,7 +2510,7 @@ copy_firstprivate_data (char *tgt, size_t mapnum, void **hostaddrs,
   tgt_size = 0;
   size_t i;
   for (i = 0; i < mapnum; i++)
-    if ((kinds[i] & 0xff) == GOMP_MAP_FIRSTPRIVATE)
+    if ((kinds[i] & 0xff) == GOMP_MAP_FIRSTPRIVATE && hostaddrs[i] != NULL)
       {
 	size_t align = (size_t) 1 << (kinds[i] >> 8);
 	tgt_size = (tgt_size + align - 1) & ~(align - 1);
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
new file mode 100644
index 0000000..d9a309d7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
@@ -0,0 +1,23 @@
+/* Testing 'has_device_addr' clause on the target construct with reference. */
+
+#include <omp.h>
+
+int
+main ()
+{
+  int *dp = (int*)omp_target_alloc (sizeof(int), 0);
+
+  #pragma omp target is_device_ptr(dp)
+    *dp = 42;
+
+  int &x = *dp;
+
+  #pragma omp target has_device_addr(x)
+    x = 24;
+
+  #pragma omp target has_device_addr(x)
+    if (x != 24)
+      __builtin_abort ();
+
+  omp_target_free(dp, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C
new file mode 100644
index 0000000..6468c6c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-4.C
@@ -0,0 +1,33 @@
+#include <omp.h>
+
+int
+main ()
+{
+  int *dp = (int*)omp_target_alloc (30*sizeof(int), 0);
+
+  #pragma omp target is_device_ptr(dp)
+    for (int i = 0; i < 30; i++)
+      dp[i] = i;
+
+  int (&x)[30] = *static_cast<int(*)[30]>(static_cast<void*>(dp));
+
+  #pragma omp target has_device_addr(x)
+    for (int i = 0; i < 30; i++)
+      x[i] = 2 * i;
+
+  #pragma omp target has_device_addr(x)
+    for (int i = 0; i < 30; i++)
+      if (x[i] != 2 * i)
+	__builtin_abort ();
+
+  #pragma omp target has_device_addr(x[1:5])
+    for (int i = 1; i < 6; i++)
+      x[i] = 3 * i;
+
+  #pragma omp target has_device_addr(x[1:5])
+    for (int i = 1; i < 6; i++)
+      if (x[i] != 3 * i)
+	__builtin_abort ();
+
+  omp_target_free (dp, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-5.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-5.C
new file mode 100644
index 0000000..e847cdc
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-5.C
@@ -0,0 +1,33 @@
+/* Testing 'has_device_addr' clause on the target construct with reference. */
+
+#include <omp.h>
+
+int
+main ()
+{
+  int *dpx = (int*)omp_target_alloc (sizeof(int), 0);
+  int **dpy = (int**)omp_target_alloc (sizeof(int*), 0);
+
+  #pragma omp target is_device_ptr(dpx, dpy)
+    {
+      *dpx = 42;
+      int z = 77;
+      *dpy = &z;
+    }
+
+  int& x = *dpx;
+  int*& y = *dpy;
+
+  #pragma omp target has_device_addr(x, y)
+    {
+      x = 24;
+      y = &x;
+    }
+
+  #pragma omp target has_device_addr(x, y)
+    if (x != 24 || y != &x)
+      __builtin_abort ();
+
+  omp_target_free(dpx, 0);
+  omp_target_free(dpy, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-6.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-6.C
new file mode 100644
index 0000000..141edb1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-6.C
@@ -0,0 +1,32 @@
+/* Testing 'has_device_addr' clause on the target construct with reference. */
+
+#include <omp.h>
+
+int
+main ()
+{
+  int *dpx = (int*)omp_target_alloc (sizeof(int), 0);
+  double *dpy = (double*)omp_target_alloc (sizeof(double), 0);
+
+  #pragma omp target is_device_ptr(dpx, dpy)
+    {
+      *dpx = 42;
+      *dpy = 43.5;
+    }
+
+  int &x = *dpx;
+  double &y = *dpy;
+
+  #pragma omp target has_device_addr(x, y)
+    {
+      x = 24;
+      y = 25.7;
+    }
+
+  #pragma omp target has_device_addr(y, x)
+    if (x != 24 || y != 25.7)
+      __builtin_abort ();
+
+  omp_target_free(dpx, 0);
+  omp_target_free(dpy, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c
new file mode 100644
index 0000000..fcc5c9e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c
@@ -0,0 +1,73 @@
+/* Testing the 'has_device_addr' clause on the target construct with
+   enclosing 'target data' construct. */
+
+#define N 40
+
+int
+main ()
+{
+  int x = 24;
+
+  #pragma omp target data map(x) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      x = 42;
+  if (x != 42)
+    __builtin_abort ();
+
+  int y[N];
+
+  for (int i = 0; i < N; i++)
+    y[i] = 42;
+  #pragma omp target data map(y) use_device_addr(y)
+    #pragma omp target has_device_addr(y)
+      for (int i = 0; i < N; i++)
+	y[i] = i;
+  for (int i = 0; i < N; i++)
+    if (y[i] != i)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[:N])
+      for (int i = 0; i < N; i++)
+	y[i] = i + 2;
+  for (int i = 0; i < N; i++)
+    if (y[i] != i + 2)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[24])
+	y[24] = 42;
+  if (y[24] != 42)
+    __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[24:])
+      for (int i = 24; i < N; i++)
+	y[i] = i + 3;
+  for (int i = 24; i < N; i++)
+    if (y[i] != i + 3)
+      __builtin_abort ();
+
+  #pragma omp target data map(y[:N]) use_device_addr(y)
+    #pragma omp target has_device_addr(y[12:24])
+      for (int i = 12; i < 24; i++)
+	y[i] = i + 4;
+  for (int i = 12; i < 24; i++)
+    if (y[i] != i + 4)
+      __builtin_abort ();
+
+  int u[0];
+  #pragma omp target data map(u) use_device_addr(u)
+    #pragma omp target has_device_addr(u)
+  ;
+
+  struct S { int m; } s;
+  s.m = 42;
+  #pragma omp target data map (s) use_device_addr (s)
+    #pragma omp target has_device_addr (s)
+      ++s.m;
+  if (s.m != 43)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c b/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c
new file mode 100644
index 0000000..fd99a82
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/target-has-device-addr-3.c
@@ -0,0 +1,33 @@
+/* Testing 'has_device_addr' clause with variable sized array. */
+
+int
+foo (int size)
+{
+  int x[size];
+
+  #pragma omp target data map(x[:size]) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      for (int i = 0; i < size; i++)
+	x[i] = i;
+  for (int i = 0; i < size; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  #pragma omp target data map(x) use_device_addr(x)
+    #pragma omp target has_device_addr(x[2:3])
+      for (int i = 0; i < size; i++)
+	x[i] = i;
+  for (int i = 0; i < size; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  return 0;
+}
+
+int
+main ()
+{
+  foo (40);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.fortran/target-has-device-addr-1.f90 b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-1.f90
new file mode 100644
index 0000000..2945864
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-1.f90
@@ -0,0 +1,50 @@
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+
+  integer, parameter :: N = 40
+  integer :: x, i
+  integer :: y (N)
+  integer :: u (0)
+
+  x = 24
+  !$omp target data map(x) use_device_addr(x)
+    !$omp target has_device_addr(x)
+      x = 42;
+    !$omp end target
+  !$omp end target data
+  if (x /= 42) stop 1
+
+  y = 42
+  !$omp target data map(y) use_device_addr(y)
+    !$omp target has_device_addr(y)
+      y = [(i, i=1, N)]
+    !$omp end target
+  !$omp end target data
+  if (any (y /= [(i, i = 1, N)])) stop 2
+
+  !$omp target data map(y(:N)) use_device_addr(y)
+    !$omp target has_device_addr(y(:N))
+      y = [(i+2, i=1, N)]
+    !$omp end target
+  !$omp end target data
+  if (any (y /= [(i+2, i = 1, N)])) stop 3
+
+  !$omp target data map(y) use_device_addr(y)
+    !$omp target has_device_addr(y(24:))
+      do i = 24, N
+        y(i) = i + 3
+      end do
+    !$omp end target
+  !$omp end target data
+  do i = 24, N
+    if (y(i) /= i + 3) stop 5
+  end do
+
+  !$omp target data map(u) use_device_addr(u)
+    !$omp target has_device_addr(u)
+    !$omp end target
+  !$omp end target data
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/target-has-device-addr-2.f90 b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-2.f90
new file mode 100644
index 0000000..a8d78a7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-2.f90
@@ -0,0 +1,40 @@
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+
+  integer, parameter :: N = 5
+  integer :: i, x(N), y(N), z(N:2*N-1)
+  target :: z
+
+  x = 42
+  y = 43
+  z = 44
+
+  call foo (x, y, z)
+  if (any (x /= [(i, i = 1, N)])) stop 1
+  if (any (y /= [(2*i, i = 1, N)])) stop 2
+  if (any (z /= [(3*i, i = 1, N)])) stop 3
+
+  contains
+  subroutine foo(a, b, c)
+    integer :: a(:)
+    integer :: b(*)
+    integer, pointer, intent(in) :: c(:)
+
+    !$omp target data map(a,b(:N),c) use_device_addr(a,b(:N),c)
+      !$omp target has_device_addr(A,B(:N),C)
+        if (lbound(a,dim=1) /= 1 .or. ubound(a,dim=1) /= N) stop 10
+        if (lbound(b,dim=1) /= 1) stop 11
+        if (lbound(c,dim=1) /= N .or. ubound(c,dim=1) /= 2*N-1) stop 12
+        if (any (a /= 42)) stop 13
+        if (any (b(:N) /= 43)) stop 14
+        if (any (c /= 44)) stop 15
+        a = [(i, i=1, N)]
+        b(:N) = [(2*i, i = 1, N)]
+        c = [(3*i, i = 1, N)]
+      !$omp end target
+    !$omp end target data
+  end subroutine foo
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/target-has-device-addr-3.f90 b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-3.f90
new file mode 100644
index 0000000..c6293b4
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-3.f90
@@ -0,0 +1,90 @@
+! Test optional dummy arguments in HAS_DEVICE_ADDR.
+
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+
+  integer, target :: x
+  integer, pointer :: ptr
+  integer, parameter :: N=7
+  real :: y1(N), y2(N)
+  integer, target :: y3(N:2*N-1)
+  integer :: i
+
+  x = 24
+  ptr => x
+  y1 = 42.24
+  y2 = 42.24
+  y3 = 42
+
+  call optional_scalar (is_present=.false.)
+  if (x /= 24) stop 1
+
+  call optional_scalar (x, is_present=.true.)
+  if (x /= 42) stop 2
+
+  call optional_ptr (is_present=.false.)
+  if (x /= 42) stop 3
+  if (ptr /= 42) stop 4
+
+  call optional_ptr (ptr, is_present=.true.)
+  if (x /= 84) stop 5
+  if (ptr /= 84) stop 6
+
+  call optional_array (is_present=.false.)
+  if (any (y1 /= [(42.24, i=1, N)])) stop 7
+  if (any (y2 /= [(42.24, i=1, N)])) stop 8
+  if (any (y3 /= [(42, i=1, N)])) stop 9
+
+  call optional_array (y1, y2, y3, is_present=.true.)
+  if (any (y1 /= [(42.24+i, i=1, N)])) stop 10
+  if (any (y2 /= [(42.24+2*i, i=1, N)])) stop 11
+  if (any (y3 /= [(42+3*i, i=1, N)])) stop 12
+
+contains
+  subroutine optional_scalar (a, is_present)
+    integer, optional :: a
+    logical, value :: is_present
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        if (is_present) a = 42
+      !$omp end target
+    !$omp end target data
+  end subroutine optional_scalar
+
+  subroutine optional_ptr (a, is_present)
+    integer, pointer, optional :: a
+    logical, value :: is_present
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        if (is_present) a = 84
+      !$omp end target
+    !$omp end target data
+  end subroutine optional_ptr
+
+  subroutine optional_array (a, b, c, is_present)
+    real, optional :: a(:), b(*)
+    integer, optional, pointer, intent(in) :: c(:)
+    logical, value :: is_present
+    integer :: i
+
+    !$omp target data map(a, b(:N), c) use_device_addr(a, b, c)
+      !$omp target has_device_addr(a, b, c)
+        if (is_present) then
+          if (lbound(a,dim=1) /= 1 .or. ubound(a,dim=1) /= N) stop 21
+          if (lbound(b,dim=1) /= 1) stop 22
+          if (lbound(c,dim=1) /= N .or. ubound(c,dim=1) /= 2*N-1) stop 23
+          if (any (a /= [(42.24, i = 1, N)])) stop 24
+          if (any (b(:N) /= [(42.24, i = 1, N)])) stop 25
+          if (any (c /= [(42, i = 1, N)])) stop 26
+          a = [(42.24+i, i=1, N)]
+          b(:N) = [(42.24+2*i, i=1, N)]
+          c = [(42+3*i, i=1, N)]
+        end if
+      !$omp end target
+    !$omp end target data
+  end subroutine optional_array
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/target-has-device-addr-4.f90 b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-4.f90
new file mode 100644
index 0000000..59d3e3d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-has-device-addr-4.f90
@@ -0,0 +1,71 @@
+! Test allocatables in HAS_DEVICE_ADDR.
+
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+
+  integer, parameter :: N = 5
+  integer, allocatable :: x
+  integer, allocatable :: y(:)
+  call scalar_dummy (x)
+  call array_dummy (y)
+  call array_dummy_optional (y)
+  call array_dummy_optional ()
+
+contains
+  subroutine scalar_dummy (a)
+    integer, allocatable :: a
+
+    allocate (a)
+    a = 24
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        a = 42
+      !$omp end target
+    !$omp end target data
+    if (a /= 42) stop 1
+
+    deallocate (a)
+  end subroutine scalar_dummy
+
+  subroutine array_dummy (a)
+    integer, allocatable :: a(:)
+    integer :: i
+
+    allocate (a(N))
+    a = 42
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        a = [(i, i=1, N)]
+      !$omp end target
+    !$omp end target data
+    if (any (a /= [(i, i=1, N)])) stop 2
+
+    deallocate (a)
+  end subroutine array_dummy
+
+  subroutine array_dummy_optional (a)
+    integer, optional, allocatable :: a(:)
+    integer :: i
+
+    if (present (a)) then
+      allocate (a(N))
+      a = 42
+    end if
+
+    !$omp target data map(a) use_device_addr(a)
+      !$omp target has_device_addr(a)
+        if (present (a)) a = [(i, i=1, N)]
+      !$omp end target
+    !$omp end target data
+
+    if (present (a)) then
+      if (any (a /= [(i, i=1, N)])) stop 2
+      deallocate (a)
+    end if
+  end subroutine array_dummy_optional
+
+end program main
  
Jakub Jelinek Feb. 2, 2022, 2:24 p.m. UTC | #8
On Wed, Feb 02, 2022 at 09:19:03AM +0100, Marcel Vollweiler wrote:
> gcc/c-family/ChangeLog:
> 
> 	* c-omp.cc (c_omp_split_clauses): Added OMP_CLAUSE_HAS_DEVICE_ADDR case.
> 	* c-pragma.h (enum pragma_kind): Added 5.1 in comment.
> 	(enum pragma_omp_clause): Added PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR.
> 
> gcc/c/ChangeLog:
> 
> 	* c-parser.cc (c_parser_omp_clause_name): Parse 'has_device_addr'
> 	clause.
> 	(c_parser_omp_variable_list): Handle array sections.
> 	(c_parser_omp_clause_has_device_addr): Added.
> 	(c_parser_omp_all_clauses): Added PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR
> 	case.
> 	(c_parser_omp_target_exit_data): Added HAS_DEVICE_ADDR to 
> 	OMP_CLAUSE_MASK.
> 	* c-typeck.cc (handle_omp_array_sections): Handle clause restrictions.
> 	(c_finish_omp_clauses): Handle array sections.
> 
> gcc/cp/ChangeLog:
> 
> 	* parser.cc (cp_parser_omp_clause_name): Parse 'has_device_addr' clause.
> 	(cp_parser_omp_var_list_no_open): Handle array sections.
> 	(cp_parser_omp_all_clauses): Added PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR
> 	case.
> 	(cp_parser_omp_target_update): Added HAS_DEVICE_ADDR to OMP_CLAUSE_MASK.
> 	* pt.c (tsubst_omp_clauses): Added cases for OMP_CLAUSE_HAS_DEVICE_ADDR.
> 	* semantics.cc (handle_omp_array_sections): Handle clause restrictions.
> 	(finish_omp_clauses): Handle array sections.
> 
> gcc/fortran/ChangeLog:
> 
> 	* dump-parse-tree.cc (show_omp_clauses): Added OMP_LIST_HAS_DEVICE_ADDR
> 	case.
> 	* gfortran.h: Added OMP_LIST_HAS_DEVICE_ADDR.
> 	* openmp.cc (enum omp_mask2): Added OMP_CLAUSE_HAS_DEVICE_ADDR.
> 	(gfc_match_omp_clauses): Parse HAS_DEVICE_ADDR clause.
> 	(resolve_omp_clauses): Same.
> 	* trans-openmp.cc (gfc_trans_omp_variable_list): Added 
> 	OMP_LIST_HAS_DEVICE_ADDR case.
> 	(gfc_trans_omp_clauses): Firstprivatize of array descriptors.
> 
> gcc/ChangeLog:
> 
> 	* gimplify.cc (gimplify_scan_omp_clauses): Added cases for
> 	OMP_CLAUSE_HAS_DEVICE_ADDR
> 	and handle array sections.
> 	(gimplify_adjust_omp_clauses): Added OMP_CLAUSE_HAS_DEVICE_ADDR case.
> 	* omp-low.cc (scan_sharing_clauses): Handle OMP_CLAUSE_HAS_DEVICE_ADDR.
> 	(lower_omp_target): Same.
> 	* tree-core.h (enum omp_clause_code): Same.
> 	* tree-nested.cc (convert_nonlocal_omp_clauses): Same.
> 	(convert_local_omp_clauses): Same.
> 	* tree-pretty-print.cc (dump_omp_clause): Same.
> 	* tree.cc: Same.
> 
> libgomp/ChangeLog:
> 
> 	* libgomp.texi: Updated entry for HAS_DEVICE_ADDR.
> 	* target.c (copy_firstprivate_data): Copy only if host address is not
> 	NULL.
> 	* testsuite/libgomp.c++/target-has-device-addr-2.C: New test.
> 	* testsuite/libgomp.c++/target-has-device-addr-4.C: New test.
> 	* testsuite/libgomp.c++/target-has-device-addr-5.C: New test.
> 	* testsuite/libgomp.c++/target-has-device-addr-6.C: New test.
> 	* testsuite/libgomp.c-c++-common/target-has-device-addr-1.c: New test.
> 	* testsuite/libgomp.c/target-has-device-addr-3.c: New test.
> 	* testsuite/libgomp.fortran/target-has-device-addr-1.f90: New test.
> 	* testsuite/libgomp.fortran/target-has-device-addr-2.f90: New test.
> 	* testsuite/libgomp.fortran/target-has-device-addr-3.f90: New test.
> 	* testsuite/libgomp.fortran/target-has-device-addr-4.f90: New test.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* c-c++-common/gomp/clauses-1.c: Added has_device_addr to test cases.
> 	* g++.dg/gomp/attrs-1.C: Added has_device_addr to test cases.
> 	* g++.dg/gomp/attrs-2.C: Added has_device_addr to test cases.
> 	* c-c++-common/gomp/target-has-device-addr-1.c: New test.
> 	* c-c++-common/gomp/target-has-device-addr-2.c: New test.
> 	* c-c++-common/gomp/target-is-device-ptr-1.c: New test.
> 	* c-c++-common/gomp/target-is-device-ptr-2.c: New test.
> 	* gfortran.dg/gomp/is_device_ptr-3.f90: New test.
> 	* gfortran.dg/gomp/target-has-device-addr-1.f90: New test.
> 	* gfortran.dg/gomp/target-has-device-addr-2.f90: New test.

Ok, thanks.

	Jakub
  

Patch

diff --git a/gcc/c-family/c-omp.c b/gcc/c-family/c-omp.c
index b9024cb..eb4950c 100644
--- a/gcc/c-family/c-omp.c
+++ b/gcc/c-family/c-omp.c
@@ -1837,6 +1837,7 @@  c_omp_split_clauses (location_t loc, enum tree_code code,
 	case OMP_CLAUSE_DEVICE:
 	case OMP_CLAUSE_MAP:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_DEFAULTMAP:
 	case OMP_CLAUSE_DEPEND:
 	  s = C_OMP_CLAUSE_SPLIT_TARGET;
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 0c5b07a..03baacd 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -89,8 +89,8 @@  enum pragma_kind {
 };
 
 
-/* All clauses defined by OpenACC 2.0, and OpenMP 2.5, 3.0, 3.1, 4.0, 4.5
-   and 5.0.  Used internally by both C and C++ parsers.  */
+/* All clauses defined by OpenACC 2.0, and OpenMP 2.5, 3.0, 3.1, 4.0, 4.5, 5.0,
+   and 5.1.  Used internally by both C and C++ parsers.  */
 enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_NONE = 0,
 
@@ -114,6 +114,7 @@  enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_FOR,
   PRAGMA_OMP_CLAUSE_FROM,
   PRAGMA_OMP_CLAUSE_GRAINSIZE,
+  PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR,
   PRAGMA_OMP_CLAUSE_HINT,
   PRAGMA_OMP_CLAUSE_IF,
   PRAGMA_OMP_CLAUSE_IN_REDUCTION,
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 869a811..e93a37f 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -12746,7 +12746,9 @@  c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
 	  break;
 	case 'h':
-	  if (!strcmp ("hint", p))
+	  if (!strcmp ("has_device_addr", p))
+	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  else if (!strcmp ("hint", p))
 	    result = PRAGMA_OMP_CLAUSE_HINT;
 	  else if (!strcmp ("host", p))
 	    result = PRAGMA_OACC_CLAUSE_HOST;
@@ -14255,6 +14257,16 @@  c_parser_omp_clause_use_device_addr (c_parser *parser, tree list)
 				       list);
 }
 
+/* OpenMP 5.1:
+   has_device_addr ( variable-list ) */
+
+static tree
+c_parser_omp_clause_has_device_addr (c_parser *parser, tree list)
+{
+  return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
+				       list);
+}
+
 /* OpenMP 4.5:
    is_device_ptr ( variable-list ) */
 
@@ -16945,6 +16957,10 @@  c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
 	  clauses = c_parser_omp_clause_use_device_addr (parser, clauses);
 	  c_name = "use_device_addr";
 	  break;
+	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  clauses = c_parser_omp_clause_has_device_addr (parser, clauses);
+	  c_name = "has_device_addr";
+	  break;
 	case PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR:
 	  clauses = c_parser_omp_clause_is_device_ptr (parser, clauses);
 	  c_name = "is_device_ptr";
@@ -20926,7 +20942,8 @@  c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
-	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
 
 static bool
 c_parser_omp_target (c_parser *parser, enum pragma_context context, bool *if_p)
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 0aac978..d677592 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -14054,7 +14054,7 @@  c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 {
   bitmap_head generic_head, firstprivate_head, lastprivate_head;
   bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
-  bitmap_head oacc_reduction_head;
+  bitmap_head oacc_reduction_head, has_device_addr_head, is_device_ptr_head;
   tree c, t, type, *pc;
   tree simdlen = NULL_TREE, safelen = NULL_TREE;
   bool branch_seen = false;
@@ -14089,6 +14089,8 @@  c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   /* If ort == C_ORT_OMP used as nontemporal_head or use_device_xxx_head
      instead and for ort == C_ORT_OMP_TARGET used as in_reduction_head.  */
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
+  bitmap_initialize (&has_device_addr_head, &bitmap_default_obstack);
+  bitmap_initialize (&is_device_ptr_head, &bitmap_default_obstack);
 
   if (ort & C_ORT_ACC)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
@@ -14517,7 +14519,9 @@  c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qE appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+	  else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
@@ -15082,7 +15086,9 @@  c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		   || bitmap_bit_p (&has_device_addr_head, DECL_UID (t))
+		   || bitmap_bit_p (&is_device_ptr_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -15167,6 +15173,8 @@  c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	    bitmap_set_bit (&is_device_ptr_head, DECL_UID (t));
 	  if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
@@ -15187,8 +15195,11 @@  c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  goto check_dup_generic;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    bitmap_set_bit (&has_device_addr_head, DECL_UID (t));
 	  if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
 	    c_mark_addressable (t);
 	  goto check_dup_generic;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 0818d66..76582a7 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -36145,7 +36145,9 @@  cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_GRAINSIZE;
 	  break;
 	case 'h':
-	  if (!strcmp ("hint", p))
+	  if (!strcmp ("has_device_addr", p))
+	    result = PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR;
+	  else if (!strcmp ("hint", p))
 	    result = PRAGMA_OMP_CLAUSE_HINT;
 	  else if (!strcmp ("host", p))
 	    result = PRAGMA_OACC_CLAUSE_HOST;
@@ -39830,6 +39832,11 @@  cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
 					    clauses);
 	  c_name = "is_device_ptr";
 	  break;
+	case PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR:
+	  clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_HAS_DEVICE_ADDR,
+					    clauses);
+	  c_name = "has_device_addr";
+	  break;
 	case PRAGMA_OMP_CLAUSE_IF:
 	  clauses = cp_parser_omp_clause_if (parser, clauses, token->location,
 					     true);
@@ -44005,7 +44012,8 @@  cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE)	\
 	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)	\
-	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR))
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)\
+	| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR))
 
 static bool
 cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 009fe6d..e5a6f2f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -17442,6 +17442,7 @@  tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_INCLUSIVE:
 	case OMP_CLAUSE_EXCLUSIVE:
 	  OMP_CLAUSE_DECL (nc)
@@ -17581,6 +17582,7 @@  tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_INCLUSIVE:
 	  case OMP_CLAUSE_EXCLUSIVE:
 	  case OMP_CLAUSE_ALLOCATE:
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 0d8e5fd..314d180 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -6580,7 +6580,7 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 {
   bitmap_head generic_head, firstprivate_head, lastprivate_head;
   bitmap_head aligned_head, map_head, map_field_head, map_firstprivate_head;
-  bitmap_head oacc_reduction_head;
+  bitmap_head oacc_reduction_head, has_device_addr_head, is_device_ptr_head;
   tree c, t, *pc;
   tree safelen = NULL_TREE;
   bool branch_seen = false;
@@ -6612,6 +6612,8 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   /* If ort == C_ORT_OMP used as nontemporal_head or use_device_xxx_head
      instead and for ort == C_ORT_OMP_TARGET used as in_reduction_head.  */
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
+  bitmap_initialize (&has_device_addr_head, &bitmap_default_obstack);
+  bitmap_initialize (&is_device_ptr_head, &bitmap_default_obstack);
 
   if (ort & C_ORT_ACC)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
@@ -6910,7 +6912,9 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+	  else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
+		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
@@ -8032,7 +8036,9 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		   || bitmap_bit_p (&has_device_addr_head, DECL_UID (t))
+		   || bitmap_bit_p (&is_device_ptr_head, DECL_UID (t)))
 	    {
 	      if (ort == C_ORT_ACC)
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -8286,6 +8292,8 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	  field_ok = (ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP;
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	    bitmap_set_bit (&is_device_ptr_head, DECL_UID (t));
 	  if (!type_dependent_expression_p (t))
 	    {
 	      tree type = TREE_TYPE (t);
@@ -8315,9 +8323,12 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  goto check_dup_generic;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
 	  field_ok = true;
 	  t = OMP_CLAUSE_DECL (c);
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
+	    bitmap_set_bit (&has_device_addr_head, DECL_UID (t));
 	  if (!processing_template_decl
 	      && (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
 	      && !TYPE_REF_P (TREE_TYPE (t))
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index d8e4b13..3c0b262 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -10018,6 +10018,7 @@  gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  flags = GOVD_EXPLICIT;
 	  goto do_add;
 
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  flags = GOVD_FIRSTPRIVATE | GOVD_EXPLICIT;
 	  goto do_add;
@@ -11446,6 +11447,7 @@  gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	case OMP_CLAUSE_DETACH:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_ASYNC:
 	case OMP_CLAUSE_WAIT:
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 057b7ae..3c92cb2 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -1368,7 +1368,8 @@  scan_sharing_clauses (tree clauses, omp_context *ctx)
 	  decl = OMP_CLAUSE_DECL (c);
 	do_private:
 	  if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
-	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR
+	       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      && is_gimple_omp_offloaded (ctx->stmt))
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE)
@@ -1376,7 +1377,8 @@  scan_sharing_clauses (tree clauses, omp_context *ctx)
 		  by_ref = !omp_privatize_by_reference (decl);
 		  install_var_field (decl, by_ref, 3, ctx);
 		}
-	      else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+	      else if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+		       || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		install_var_field (decl, true, 3, ctx);
 	      else
 		install_var_field (decl, false, 3, ctx);
@@ -1446,6 +1448,7 @@  scan_sharing_clauses (tree clauses, omp_context *ctx)
 	  break;
 
 	case OMP_CLAUSE_IS_DEVICE_PTR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  decl = OMP_CLAUSE_DECL (c);
 	  goto do_private;
 
@@ -1722,12 +1725,14 @@  scan_sharing_clauses (tree clauses, omp_context *ctx)
 	case OMP_CLAUSE_FIRSTPRIVATE:
 	case OMP_CLAUSE_PRIVATE:
 	case OMP_CLAUSE_LINEAR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	  decl = OMP_CLAUSE_DECL (c);
 	  if (is_variable_sized (decl))
 	    {
 	      if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
-		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		  && is_gimple_omp_offloaded (ctx->stmt))
 		{
 		  tree decl2 = DECL_VALUE_EXPR (decl);
@@ -12791,6 +12796,7 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
       case OMP_CLAUSE_USE_DEVICE_PTR:
       case OMP_CLAUSE_USE_DEVICE_ADDR:
+      case OMP_CLAUSE_HAS_DEVICE_ADDR:
       case OMP_CLAUSE_IS_DEVICE_PTR:
 	var = OMP_CLAUSE_DECL (c);
 	map_cnt++;
@@ -12807,7 +12813,8 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    SET_DECL_VALUE_EXPR (new_var, x);
 	    DECL_HAS_VALUE_EXPR_P (new_var) = 1;
 	  }
-	else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		  && !omp_privatize_by_reference (var)
 		  && !omp_is_allocatable_or_ptr (var)
 		  && !lang_hooks.decls.omp_array_data (var, true))
@@ -13260,17 +13267,20 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
 	    ovar = OMP_CLAUSE_DECL (c);
 	    var = lookup_decl_in_outer_ctx (ovar, ctx);
 
 	    if (lang_hooks.decls.omp_array_data (ovar, true))
 	      {
-		tkind = (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		tkind = ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+			  && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 			 ? GOMP_MAP_USE_DEVICE_PTR : GOMP_MAP_FIRSTPRIVATE_INT);
 		x = build_sender_ref ((splay_tree_key) &DECL_NAME (ovar), ctx);
 	      }
-	    else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR)
+	    else if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		     && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      {
 		tkind = GOMP_MAP_USE_DEVICE_PTR;
 		x = build_sender_ref ((splay_tree_key) &DECL_UID (ovar), ctx);
@@ -13292,7 +13302,8 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    type = TREE_TYPE (ovar);
 	    if (lang_hooks.decls.omp_array_data (ovar, true))
 	      var = lang_hooks.decls.omp_array_data (ovar, false);
-	    else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	    else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		      && !omp_privatize_by_reference (ovar)
 		      && !omp_is_allocatable_or_ptr (ovar))
 		     || TREE_CODE (type) == ARRAY_TYPE)
@@ -13307,6 +13318,7 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		    if (POINTER_TYPE_P (type)
 			&& TREE_CODE (type) != ARRAY_TYPE
 			&& ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_USE_DEVICE_ADDR
+			    && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR
 			    && !omp_is_allocatable_or_ptr (ovar))
 			   || (omp_privatize_by_reference (ovar)
 			       && omp_is_allocatable_or_ptr (ovar))))
@@ -13504,6 +13516,7 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    break;
 	  case OMP_CLAUSE_USE_DEVICE_PTR:
 	  case OMP_CLAUSE_USE_DEVICE_ADDR:
+	  case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	  case OMP_CLAUSE_IS_DEVICE_PTR:
 	    tree new_var;
 	    gimple_seq assign_body;
@@ -13514,7 +13527,8 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    var = OMP_CLAUSE_DECL (c);
 	    is_array_data = lang_hooks.decls.omp_array_data (var, true) != NULL;
 
-	    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR)
+	    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_IS_DEVICE_PTR
+		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE_HAS_DEVICE_ADDR)
 	      x = build_sender_ref (is_array_data
 				    ? (splay_tree_key) &DECL_NAME (var)
 				    : (splay_tree_key) &DECL_UID (var), ctx);
@@ -13566,7 +13580,8 @@  lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		gimple_seq_add_stmt (&assign_body,
 				     gimple_build_assign (new_var, x));
 	      }
-	    else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+	    else if (((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_ADDR
+		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 		      && !omp_privatize_by_reference (var)
 		      && !omp_is_allocatable_or_ptr (var))
 		     || TREE_CODE (TREE_TYPE (var)) == ARRAY_TYPE)
diff --git a/gcc/testsuite/c-c++-common/gomp/clauses-1.c b/gcc/testsuite/c-c++-common/gomp/clauses-1.c
index 742132f..9da5255 100644
--- a/gcc/testsuite/c-c++-common/gomp/clauses-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/clauses-1.c
@@ -102,7 +102,7 @@  baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm)
 {
   #pragma omp for simd \
@@ -138,20 +138,20 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   #pragma omp target parallel \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
     ;
   #pragma omp target parallel for \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) ordered schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target parallel for \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target parallel for simd \
@@ -159,18 +159,19 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0]) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
     ;
   #pragma omp target teams distribute \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) order(concurrent) \
-    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) \
+    has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ;
   #pragma omp target teams distribute parallel for \
@@ -179,7 +180,7 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1) dist_schedule(static, 16) \
     if (parallel: i2) num_threads (nth) proc_bind(spread) \
     lastprivate (l) schedule(static, 4) nowait depend(inout: dd[0]) order(concurrent) \
-     allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+     allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams distribute parallel for simd \
@@ -189,7 +190,7 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) num_threads (nth) proc_bind(spread) \
     lastprivate (l) schedule(static, 4) order(concurrent) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target teams distribute simd \
@@ -197,14 +198,14 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) \
     collapse(1) dist_schedule(static, 16) order(concurrent) \
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp target simd \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     safelen(8) simdlen(4) lastprivate (l) linear(ll: 1) aligned(q: 32) reduction(+:r) \
     nowait depend(inout: dd[0]) nontemporal(ntm) if(simd:i3) order(concurrent) \
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr(hda)
   for (int i = 0; i < 64; i++)
     ll++;
   #pragma omp taskgroup task_reduction(+:r2) allocate (r2)
@@ -430,28 +431,28 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     nowait depend(inout: dd[0]) lastprivate (l) bind(parallel) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target parallel loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) \
     nowait depend(inout: dd[0]) lastprivate (l) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target teams loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0]) \
     lastprivate (l) bind(teams) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
   #pragma omp target teams loop \
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp) \
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0]) \
     lastprivate (l) order(concurrent) collapse(1) \
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2)
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr(hda)
   for (l = 0; l < 64; ++l)
     ;
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c
new file mode 100644
index 0000000..3a12e62
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-has-device-addr-1.c
@@ -0,0 +1,27 @@ 
+/* { dg-do compile } */
+
+void
+foo ()
+{
+  int * x;
+  #pragma omp target is_device_ptr(x) has_device_addr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target has_device_addr(x) is_device_ptr(x) /* { dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  int y = 42;
+  #pragma omp target has_device_addr(y) has_device_addr(y) /* { dg-error "'y' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target private(y) has_device_addr(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+  #pragma omp target has_device_addr(y) private(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+  #pragma omp target firstprivate(y) has_device_addr(y) /*{ dg-error "'y' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target has_device_addr(y) map(y) /* { dg-error "'y' appears both in data and map clauses" } */
+  ;
+  #pragma omp target map(y) has_device_addr(y) /* { dg-error "'y' appears both in data and map clauses" } */
+  ;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr.c b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr.c
new file mode 100644
index 0000000..ecf30ca
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/target-is-device-ptr.c
@@ -0,0 +1,22 @@ 
+/* { dg-do compile } */
+
+void
+foo ()
+{
+  int *x;
+
+  #pragma omp target is_device_ptr(x) is_device_ptr(x) /* { dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target private(x) is_device_ptr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target is_device_ptr(x) private(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+  #pragma omp target firstprivate(x) is_device_ptr(x) /*{ dg-error "'x' appears more than once in data clauses" } */
+  ;
+
+  #pragma omp target is_device_ptr(x) map(x) /* { dg-error "'x' appears both in data and map clauses" } */
+  ;
+  #pragma omp target map(x) is_device_ptr(x) /* { dg-error "'x' appears both in data and map clauses" } */
+  ;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-1.C b/gcc/testsuite/g++.dg/gomp/attrs-1.C
index 2a5f2cf..95a8fc6a 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-1.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-1.C
@@ -121,7 +121,7 @@  baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
      const char *msg)
 {
@@ -185,20 +185,20 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   [[omp::directive (target parallel
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
     ;
   [[omp::directive (target parallel for
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) ordered schedule(static, 4) collapse(1) nowait depend(inout: dd[0])
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target parallel for
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1) nowait depend(inout: dd[0]) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (omp::directive (target parallel for simd
@@ -206,22 +206,23 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) linear (ll:1) schedule(static, 4) collapse(1)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (target teams
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0])
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
     ;
   [[omp::sequence (directive (target
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
-    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda)))]]
     ;
   [[omp::sequence (omp::directive (target teams distribute
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) order(concurrent)
-    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    collapse(1) dist_schedule(static, 16) nowait depend(inout: dd[0]) allocate (omp_default_mem_alloc:f) in_reduction(+:r2)
+    has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ;
   [[omp::directive (target teams distribute parallel for
@@ -230,7 +231,7 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1) dist_schedule(static, 16)
     if (parallel: i2) num_threads (nth) proc_bind(spread)
     lastprivate (l) schedule(static, 4) nowait depend(inout: dd[0]) order(concurrent)
-     allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+     allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute parallel for simd
@@ -240,7 +241,7 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2) num_threads (nth) proc_bind(spread)
     lastprivate (l) schedule(static, 4) order(concurrent)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm) if (simd: i3)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute simd
@@ -248,14 +249,14 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl)
     collapse(1) dist_schedule(static, 16) order(concurrent)
     safelen(8) simdlen(4) aligned(q: 32) nowait depend(inout: dd[0]) nontemporal(ntm)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target simd
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     safelen(8) simdlen(4) lastprivate (l) linear(ll: 1) aligned(q: 32) reduction(+:r)
     nowait depend(inout: dd[0]) nontemporal(ntm) if(simd:i3) order(concurrent)
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f) in_reduction(+:r2) has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (taskgroup task_reduction(+:r2) allocate (r2)),
@@ -515,28 +516,28 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     nowait depend(inout: dd[0]) lastprivate (l) bind(parallel) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target parallel loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     if (parallel: i2) default(shared) shared(s) reduction(+:r) num_threads (nth) proc_bind(spread)
     nowait depend(inout: dd[0]) lastprivate (l) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0])
     lastprivate (l) bind(teams) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop
     device(d) map (tofrom: m) if (target: i1) private (p) firstprivate (f) defaultmap(tofrom: scalar) is_device_ptr (idp)
     shared(s) default(shared) reduction(+:r) num_teams(nte) thread_limit(tl) nowait depend(inout: dd[0])
     lastprivate (l) order(concurrent) collapse(1)
-    allocate (omp_default_mem_alloc: f) in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f) in_reduction(+:r2) has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (critical)]] {
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-2.C b/gcc/testsuite/g++.dg/gomp/attrs-2.C
index c00be7f..36a7584 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-2.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-2.C
@@ -121,7 +121,7 @@  baz (int d, int m, int i1, int i2, int p, int *idp, int s,
 }
 
 void
-bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
+bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int hda, int s,
      int nte, int tl, int nth, int g, int nta, int fi, int pp, int *q, int *dd, int ntm,
      const char *msg)
 {
@@ -185,20 +185,20 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
   [[omp::directive (target parallel,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread)
-    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
     ;
   [[omp::directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),ordered schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[using omp:directive (target parallel for,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),nowait depend(inout: dd[0]),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (omp::directive (target parallel for simd,
@@ -206,22 +206,23 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),linear (ll:1),schedule(static, 4),collapse(1),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),if (simd: i3),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[using omp:sequence (directive (target teams,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
-    shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait, depend(inout: dd[0]),
-    allocate (omp_default_mem_alloc:f) in_reduction(+:r2)))]]
+    shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda)))]]
     ;
   [[using omp:sequence (directive (target,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
-    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr(hda)))]]
     ;
   [[omp::sequence (omp::directive (target teams distribute,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),order(concurrent),
-    collapse(1),dist_schedule(static, 16),nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2)))]]
+    collapse(1),dist_schedule(static, 16),nowait depend(inout: dd[0]),allocate (omp_default_mem_alloc:f),in_reduction(+:r2),
+    has_device_addr (hda)))]]
   for (int i = 0; i < 64; i++)
     ;
   [[omp::directive (target teams distribute parallel for,
@@ -230,7 +231,7 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     collapse(1),dist_schedule(static, 16),
     if (parallel: i2),num_threads (nth),proc_bind(spread),
     lastprivate (l),schedule(static, 4),nowait depend(inout: dd[0]),order(concurrent),
-     allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute parallel for simd,
@@ -240,7 +241,7 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     if (parallel: i2),num_threads (nth),proc_bind(spread),
     lastprivate (l),schedule(static, 4),order(concurrent),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),if (simd: i3),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target teams distribute simd,
@@ -248,14 +249,14 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),
     collapse(1),dist_schedule(static, 16),order(concurrent),
     safelen(8),simdlen(4),aligned(q: 32),nowait depend(inout: dd[0]),nontemporal(ntm),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::directive (target simd,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     safelen(8),simdlen(4),lastprivate (l),linear(ll: 1),aligned(q: 32),reduction(+:r),
     nowait depend(inout: dd[0]),nontemporal(ntm),if(simd:i3),order(concurrent),
-    allocate (omp_default_mem_alloc:f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc:f),in_reduction(+:r2),has_device_addr (hda))]]
   for (int i = 0; i < 64; i++)
     ll++;
   [[omp::sequence (directive (taskgroup, task_reduction(+:r2), allocate (r2)),
@@ -515,28 +516,28 @@  bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     nowait depend(inout: dd[0]),lastprivate (l),bind(parallel),order(concurrent),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target parallel loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     if (parallel: i2),default(shared),shared(s),reduction(+:r),num_threads (nth),proc_bind(spread),
     nowait depend(inout: dd[0]),lastprivate (l),order(concurrent),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     lastprivate (l),bind(teams),collapse(1),
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (target teams loop,
     device(d),map (tofrom: m),if (target: i1),private (p),firstprivate (f),defaultmap(tofrom: scalar),is_device_ptr (idp),
     shared(s),default(shared),reduction(+:r),num_teams(nte),thread_limit(tl),nowait,depend(inout: dd[0]),
     lastprivate (l),order(concurrent),collapse(1)
-    allocate (omp_default_mem_alloc: f),in_reduction(+:r2))]]
+    allocate (omp_default_mem_alloc: f),in_reduction(+:r2),has_device_addr (hda))]]
   for (l = 0; l < 64; ++l)
     ;
   [[omp::directive (critical)]] {
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index d3d2a8d..9091b9e 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -321,6 +321,9 @@  enum omp_clause_code {
   /* OpenMP clause: is_device_ptr (variable-list).  */
   OMP_CLAUSE_IS_DEVICE_PTR,
 
+  /* OpenMP clause: has_device_addr (variable-list).  */
+  OMP_CLAUSE_HAS_DEVICE_ADDR,
+
   /* OpenMP clause: inclusive (variable-list).  */
   OMP_CLAUSE_INCLUSIVE,
 
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index c7f50eb..449e4be 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -1339,6 +1339,7 @@  convert_nonlocal_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
 	case OMP_CLAUSE_LINK:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_DETACH:
 	do_decl_clause:
@@ -2123,6 +2124,7 @@  convert_local_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
 	case OMP_CLAUSE_LINK:
 	case OMP_CLAUSE_USE_DEVICE_PTR:
 	case OMP_CLAUSE_USE_DEVICE_ADDR:
+	case OMP_CLAUSE_HAS_DEVICE_ADDR:
 	case OMP_CLAUSE_IS_DEVICE_PTR:
 	case OMP_CLAUSE_DETACH:
 	do_decl_clause:
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index 275dc7d..676fadd 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -493,6 +493,9 @@  dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
     case OMP_CLAUSE_USE_DEVICE_ADDR:
       name = "use_device_addr";
       goto print_remap;
+    case OMP_CLAUSE_HAS_DEVICE_ADDR:
+      name = "has_device_addr";
+      goto print_remap;
     case OMP_CLAUSE_IS_DEVICE_PTR:
       name = "is_device_ptr";
       goto print_remap;
diff --git a/gcc/tree.c b/gcc/tree.c
index 7bfd641..a47375e 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -301,6 +301,7 @@  unsigned const char omp_clause_num_ops[] =
   1, /* OMP_CLAUSE_USE_DEVICE_PTR  */
   1, /* OMP_CLAUSE_USE_DEVICE_ADDR  */
   1, /* OMP_CLAUSE_IS_DEVICE_PTR  */
+  1, /* OMP_CLAUSE_HAS_DEVICE_ADDR  */
   1, /* OMP_CLAUSE_INCLUSIVE  */
   1, /* OMP_CLAUSE_EXCLUSIVE  */
   2, /* OMP_CLAUSE_FROM  */
@@ -390,6 +391,7 @@  const char * const omp_clause_code_name[] =
   "use_device_ptr",
   "use_device_addr",
   "is_device_ptr",
+  "has_device_addr",
   "inclusive",
   "exclusive",
   "from",
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index bdd7e3a..44d060c 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -293,7 +293,8 @@  The OpenMP 4.5 specification is fully supported.
 @item @code{align} clause/modifier in @code{allocate} directive/clause
       and @code{allocator} directive @tab P @tab C/C++ on clause only
 @item @code{thread_limit} clause to @code{target} construct @tab N @tab
-@item @code{has_device_addr} clause to @code{target} construct @tab N @tab
+@item @code{has_device_addr} clause to @code{target} construct @tab P
+      @tab C/C++ on clause only
 @item iterators in @code{target update} motion clauses and @code{map}
       clauses @tab N @tab
 @item indirect calls to the device version of a procedure or function in
diff --git a/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
new file mode 100644
index 0000000..b93b880
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-has-device-addr-2.C
@@ -0,0 +1,24 @@ 
+/* Testing the 'has_device_addr' clause on the target construct without
+   enclosing 'target data' construct. */
+
+#include <omp.h>
+
+int
+main ()
+{
+  int *dp = (int*)omp_target_alloc(sizeof(int), 0);
+
+  #pragma omp target is_device_ptr(dp)
+    *dp = 42;
+
+  int &x = *dp;
+
+  #pragma omp target has_device_addr(x)
+    x = 24;
+
+  #pragma omp target has_device_addr(x)
+    if (x != 24)
+      __builtin_abort ();
+
+  omp_target_free(dp, 0);
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c
new file mode 100644
index 0000000..76739d4
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-1.c
@@ -0,0 +1,23 @@ 
+/* Testing the 'has_device_addr' clause on the target construct with
+   enclosing 'target data' construct. */
+
+int
+main ()
+{
+  int x = 24;
+
+  #pragma omp target data map(x) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      x = 42;
+  if (x != 42)
+    __builtin_abort ();
+
+  x = 24;
+  #pragma omp target data map(to: x) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      x = 42;
+  if (x != 24)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-3.c b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-3.c
new file mode 100644
index 0000000..d90f389
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-has-device-addr-3.c
@@ -0,0 +1,30 @@ 
+/* Testing the 'has_device_addr' clause on the target construct with
+   enclosing 'target data' construct. */
+
+#include <stdlib.h>
+
+#define N 40
+
+int
+main ()
+{
+  int x[N];
+
+  #pragma omp target data map(x[:N]) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      for (int i = 0; i < N; i++)
+	x[i] = i;
+  for (int i = 0; i < N; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  #pragma omp target data map(to: x[:N]) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      for (int i = 0; i < N; i++)
+	x[i] = i+1;
+  for (int i = 0; i < N; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/target-has-device-addr-4.c b/libgomp/testsuite/libgomp.c/target-has-device-addr-4.c
new file mode 100644
index 0000000..9d89f38
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/target-has-device-addr-4.c
@@ -0,0 +1,34 @@ 
+/* Testing the 'has_device_addr' clause on the target construct with
+   enclosing 'target data' construct. */
+
+int
+foo (int size)
+{
+  int x[size];
+
+  #pragma omp target data map(x[:size]) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      for (int i = 0; i < size; i++)
+	x[i] = i;
+  for (int i = 0; i < size; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  #pragma omp target data map(to: x[:size]) use_device_addr(x)
+    #pragma omp target has_device_addr(x)
+      for (int i = 0; i < size; i++)
+	x[i] = i+1;
+  for (int i = 0; i < size; i++)
+    if (x[i] != i)
+      __builtin_abort ();
+
+  return 0;
+}
+
+int
+main ()
+{
+  foo (40);
+
+  return 0;
+}