[PING,2,PR,gdb/19893] Fix handling of synthetic C++ references

Message ID 1463582858-11127-1-git-send-email-martin.galvan@tallertechnologies.com
State New, archived
Headers

Commit Message

Martin Galvan May 18, 2016, 2:47 p.m. UTC
  Odd. Just re-generated the patch against master; see if this works.

Thanks!

---
 gdb/dwarf2loc.c                             | 125 ++++++++++++++------
 gdb/testsuite/gdb.dwarf2/implref-array.c    |  27 +++++
 gdb/testsuite/gdb.dwarf2/implref-array.exp  | 168 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/implref-const.exp  | 120 +++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/implref-global.c   |  27 +++++
 gdb/testsuite/gdb.dwarf2/implref-global.exp | 124 ++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/implref-struct.c   |  43 +++++++
 gdb/testsuite/gdb.dwarf2/implref-struct.exp | 173 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/implref.exp        |  98 ----------------
 gdb/valops.c                                |  36 +++---
 gdb/valprint.c                              |  32 +++--
 11 files changed, 817 insertions(+), 156 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-array.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-array.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-const.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-global.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-global.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-struct.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/implref-struct.exp
 delete mode 100644 gdb/testsuite/gdb.dwarf2/implref.exp
  

Comments

Pedro Alves May 18, 2016, 2:54 p.m. UTC | #1
On 05/18/2016 03:47 PM, Martin Galvan wrote:
> Odd. Just re-generated the patch against master; see if this works.

Works now.  /me looks at patch.

Thanks,
Pedro Alves
  
Pedro Alves May 18, 2016, 3:48 p.m. UTC | #2
Hi Martin,

This looks generally good.  I only have minor comments below.
Once those are resolved, this should be good to go.  Please
fix them and resubmit.

Many thanks again for following through with the tests, which is
often not-that-fun to write, but is where I think of a lot of
the value is.

On 05/18/2016 03:47 PM, Martin Galvan wrote:

> 	* gdb.dwarf2/implref.exp: Rename to...
> 	* gdb.dwarf2/implref-const.exp: ...this.

Were there changes other than renaming?  Please tweak
your git config settings to detect renames / do "git diff -M".
E.g., I have:

[diff]
        renames = true

> diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c
> index bfe1173..ecd7cc4 100644
> --- a/gdb/dwarf2loc.c
> +++ b/gdb/dwarf2loc.c
> @@ -2061,6 +2061,66 @@ get_frame_address_in_block_wrapper (void *baton)
>    return get_frame_address_in_block ((struct frame_info *) baton);
>  }
>  
> +static struct value *
> +fetch_const_value_from_synthetic_pointer (sect_offset die, LONGEST byte_offset,
> +					  struct dwarf2_per_cu_data *per_cu,
> +					  struct type *type)

Missing intro comment, which should leave no doubt on why is the function
named "const value", but then returns a non-const value.

> +{
> +  struct value *result = NULL;
> +  struct obstack temp_obstack;
> +  struct cleanup *cleanup;
> +  const gdb_byte *bytes;
> +  LONGEST len;

Empty line after decls.

> +  obstack_init (&temp_obstack);
> +  cleanup = make_cleanup_obstack_free (&temp_obstack);
> +  bytes = dwarf2_fetch_constant_bytes (die, per_cu, &temp_obstack, &len);
> +
> +  if (bytes != NULL)
> +    {
> +      if (byte_offset >= 0
> +	  && byte_offset + TYPE_LENGTH (TYPE_TARGET_TYPE (type)) <= len)
> +	{
> +	  bytes += byte_offset;
> +	  result = value_from_contents (TYPE_TARGET_TYPE (type), bytes);
> +	}
> +      else
> +	invalid_synthetic_pointer ();
> +    }
> +  else
> +    result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
> +
> +  do_cleanups (cleanup);
> +
> +  return result;
> +}
> +
> +/* Fetch the value pointed at by a synthetic pointer.  */
> +static struct value *

Empty line between comment and function.

> +indirect_synthetic_pointer (sect_offset die, LONGEST byte_offset,
> +			    struct dwarf2_per_cu_data *per_cu,
> +			    struct frame_info *frame, struct type *type)
> +{
> +  struct value *result = NULL;

No need to initialize if you're supposed to always set
it below.  Let the compiler warn if something changes and
we miss initializing in some case.

> +
> +  /* Fetch the location expression of the DIE we're pointing to.  */
> +  struct dwarf2_locexpr_baton baton
> +    = dwarf2_fetch_die_loc_sect_off (die, per_cu,
> +				     get_frame_address_in_block_wrapper, frame);
> +
> +  /* If pointed-to DIE has a DW_AT_location, evaluate it and return the
> +     resulting value.  Otherwise, it may have a DW_AT_const_value instead,
> +     or it may've been optimized out.  */
> +  if (baton.data != NULL)
> +    result = dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
> +					    baton.data, baton.size,
> +					    baton.per_cu, byte_offset);
> +  else
> +    result = fetch_const_value_from_synthetic_pointer (die, byte_offset, per_cu,
> +						       type);
> +
> +    return result;

This "return" seems incorrectly indented.

But you can actually remove the "result" local and call "return" directly
in both branches.

> +}
> +
>  /* An implementation of an lval_funcs method to indirect through a
>     pointer.  This handles the synthetic pointer case when needed.  */
>  
> @@ -2115,6 +2175,7 @@ indirect_pieced_value (struct value *value)
>        break;
>      }
>  
> +  gdb_assert (piece);

Please make this:

  gdb_assert (piece != NULL);

while at it.


>    frame = get_selected_frame (_("No frame selected."));
>  
>    /* This is an offset requested by GDB, such as value subscripts.
> @@ -2132,43 +2193,35 @@ indirect_pieced_value (struct value *value)
>  					TYPE_LENGTH (type), byte_order);
>    byte_offset += piece->v.ptr.offset;
>  
> -  gdb_assert (piece);
> -  baton
> -    = dwarf2_fetch_die_loc_sect_off (piece->v.ptr.die, c->per_cu,
> -				     get_frame_address_in_block_wrapper,
> -				     frame);
> +  return indirect_synthetic_pointer (piece->v.ptr.die, byte_offset, c->per_cu,
> +				     frame, type);
> +}
>  
> -  if (baton.data != NULL)
> -    return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
> -					  baton.data, baton.size, baton.per_cu,
> -					  byte_offset);
> -
> -  {
> -    struct obstack temp_obstack;
> -    struct cleanup *cleanup;
> -    const gdb_byte *bytes;
> -    LONGEST len;
> -    struct value *result;
> -
> -    obstack_init (&temp_obstack);
> -    cleanup = make_cleanup_obstack_free (&temp_obstack);
> -
> -    bytes = dwarf2_fetch_constant_bytes (piece->v.ptr.die, c->per_cu,
> -					 &temp_obstack, &len);
> -    if (bytes == NULL)
> -      result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
> -    else
> -      {
> -	if (byte_offset < 0
> -	    || byte_offset + TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > len)
> -	  invalid_synthetic_pointer ();
> -	bytes += byte_offset;
> -	result = value_from_contents (TYPE_TARGET_TYPE (type), bytes);
> -      }
> +static struct value *coerce_pieced_ref (const struct value *value)
> +{

Line break before "coerce".  Missing intro comment.

> +  struct value *result = NULL;
> +  struct type *type = check_typedef (value_type (value));
>  
> -    do_cleanups (cleanup);
> -    return result;
> -  }
> +  if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
> +				    TARGET_CHAR_BIT * TYPE_LENGTH (type)))
> +    {
> +      const struct piece_closure *closure
> +	= (struct piece_closure *) value_computed_closure (value);
> +
> +      /* gdb represents synthetic pointers as pieced values with a single
> +       piece.  */
> +      gdb_assert (closure != NULL);
> +      gdb_assert (closure->n_pieces == 1);
> +
> +      struct frame_info *frame = get_selected_frame (_("No frame selected."));

Declarations in the middle of a scope break the C build.  Declare "frame"
next to "closure".

> +
> +      result = indirect_synthetic_pointer (closure->pieces->v.ptr.die,
> +					   closure->pieces->v.ptr.offset,
> +					   closure->per_cu, frame, type);
> +    }
> +  /* Else: not a synthetic reference; do nothing.  */
> +
> +  return result;

Likewise here, you can avoid the odd dangling else comment
by removing the "result" variable, and write

   if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
				    TARGET_CHAR_BIT * TYPE_LENGTH (type)))
    {
      ...
      return indirect_synthetic_pointer (closure->pieces->v.ptr.die,
				         closure->pieces->v.ptr.offset,
					 closure->per_cu, frame, type);
    }
   else
     {
       /* Not a synthetic reference; do nothing.  */
       return NULL;
     }
>  
>  static void *



> +# We need to know the size of integer and address types in order
> +# to write some of the debugging info we'd like to generate.
> +#
> +# For that, we ask GDB by debugging our implref-array program.
> +# Any program would do, but since we already have implref-array
> +# specifically for this testcase, might as well use that.
> +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
> +    untested ${testfile}.exp
> +    return -1
> +}
> +
> +set array_length [get_valueof "/u" "sizeof(array) / sizeof(array\[0\])" 5]

Replace the hard coded fallback "5" with -1 or 0 or some such, so that
we're not actually relying on the fallback.  Happens in more places.

> +if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
> +    return -1
> +}
> +
> +# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# This matches e.g. '(int (&)[5])'
> +set ref_type [format {\(int \(&\)\[%d\]\)} ${array_length}]
> +
> +# This matches e.g. '(int (*)[5])'
> +set ptr_type [format {\(int \(\*\)\[%d\]\)} ${array_length}]
> +
> +# Contents of the array.  Trim leading/trailing whitespace, '{' and '}'
> +# since they confuse TCL to no end.
> +set contents [get_valueof "" "array" "\{0, 1, 2, 3, 4\}"]

Ditto.

> +set contents [string trim ${contents}]
> +set contents [string trim ${contents} "{}"]
> +
> +# Doing 'print ref' should show us e.g. '(int (&)[5]) <synthetic pointer>: {0, 1, 2, 3, 4}'.
> +gdb_test "print ref" " = ${ref_type} <synthetic pointer>: \\{${contents}\\}" "print ref"
> +
> +# Doing 'print &ref' should show us e.g. '(int (*)[5]) 0xdeadbeef <array>'.
> +gdb_test "print &ref" " = ${ptr_type} \[\[:xdigit:\]x\]+ <array>" "print &ref"

No need to spell out a test message (last parameter to gdb_test above) when it's exactly
the same as the command (first) parameter.

> +
> +# gdb assumes C++ references are implemented as pointers, and print &(&ref)
> +# shows us the underlying pointer's address.  Since in this case there's no
> +# physical pointer, gdb should tell us so.
> +gdb_test "print &(&ref)" "Attempt to take address of value not located in memory." "print &(&ref)"
> +

Ditto.  Happens in more places.

> +# Test assignment through the synthetic reference.
> +set first_value 10
> +gdb_test_no_output "set (ref\[0\] = ${first_value})" "set (ref\[0\] = ${first_value})"
> +
> +# This matches e.g. '{10, 1, 2, 3, 4}'
> +set new_contents [format {\{%d[\d,\s]+\}} ${first_value}]

Why not match exactly "{10, 1, 2, 3, 4}" ?

> +
> +# Doing 'print ref' should now show us e.g.
> +# '(int (&)[5]) <synthetic pointer>: {10, 1, 2, 3, 4}'.
> +gdb_test "print ref" " = ${ref_type} <synthetic pointer>: ${new_contents}" "print ref (after assignment)"
> +gdb_test "print array" " = ${new_contents}" "print array (after assignment)"

https://sourceware.org/gdb/wiki/GDBTestcaseCookbook#Do_not_use_.22tail_parentheses.22_on_test_messages

> +
> +# Test treating the array as a pointer.
> +set second_value 20
> +set new_contents [format {\{%d, %d[\d,\s]+\}} ${first_value} ${second_value}]
> +
> +gdb_test "print *ref" " = ${first_value}" "print *ref"
> +gdb_test_no_output "set (*(ref + 1) = ${second_value})" "set (*(ref + 1) = ${second_value})"
> +gdb_test "print ref\[1\]" " = ${second_value}" "print ref\[1\]"

Remove third param.

> +gdb_test "print array" " = ${new_contents}" "print array (after second assignment)"

See above.

The tests files are all similar, so the same comments apply to all of them.

> diff --git a/gdb/testsuite/gdb.dwarf2/implref-const.exp b/gdb/testsuite/gdb.dwarf2/implref-const.exp


> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/implref-struct.c
> @@ -0,0 +1,43 @@
> +/* Copyright (C) 2016 Free Software Foundation, Inc.


> +/* Test program for synthetic C++ references to structs.  */
> +
> +struct S {
> +  int a;
> +  int b;
> +  int c;
> +};
> +
> +struct S s1 = {
> +  .a = 0,
> +  .b = 1,
> +  .c = 2

Guess this file is compiling as a C program.  I think
designated initializers are not in C++.  Please remove
them for portability, since the test does not seem
to require them.

> +};
> +
> +struct S s2 = {
> +  .a = 10,
> +  .b = 11,
> +  .c = 12
> +};
> +

> @@ -497,13 +499,29 @@ generic_val_print_ref (struct type *type, const gdb_byte *valaddr,
>  
>    if (options->addressprint)
>      {
> -      CORE_ADDR addr
> -	= extract_typed_address (valaddr + embedded_offset, type);
> +      const int is_synthetic =

= goes on the next line.

> +	value_bits_synthetic_pointer (original_value,
> +				      TARGET_CHAR_BIT * embedded_offset,
> +				      TARGET_CHAR_BIT * TYPE_LENGTH (type));
> +      if (!is_synthetic)
> +	{
> +	  CORE_ADDR addr
> +	    = extract_typed_address (valaddr + embedded_offset, type);
>  
> -      fprintf_filtered (stream, "@");
> -      fputs_filtered (paddress (gdbarch, addr), stream);
> -      if (options->deref_ref)
> -	fputs_filtered (": ", stream);
> +	  fprintf_filtered (stream, "@");
> +	  fputs_filtered (paddress (gdbarch, addr), stream);
> +	}
> +      /* Else, we have a synthetic reference.  Don't print '@address'; we'll
> +	 show '<synthetic pointer>' instead through valprint_check_validity.
> +	 Notice however that 'set print object on' will still show '@address'.

Is that a bug, or desirable?

> +	 This happens because value_bits_synthetic_pointer returns false if
> +	 original value is not 'lval_computed'.  While that's usually the case,
> +	 if options->objectprint is true, c_value_print will call value_addr
> +	 on the reference, which coerces synthetic references and returns a
> +	 'not_lval'.  */
> +
> +	if (options->deref_ref)
> +	  fputs_filtered (": ", stream);
>      }
>    /* De-reference the reference.  */
>    if (options->deref_ref)
> 

Thanks,
Pedro Alves
  
Martin Galvan May 18, 2016, 4:07 p.m. UTC | #3
On Wed, May 18, 2016 at 12:48 PM, Pedro Alves <palves@redhat.com> wrote:
> On 05/18/2016 03:47 PM, Martin Galvan wrote:
>
>>       * gdb.dwarf2/implref.exp: Rename to...
>>       * gdb.dwarf2/implref-const.exp: ...this.
>
> Were there changes other than renaming?  Please tweak
> your git config settings to detect renames / do "git diff -M".

Yeah, there were. Will add it to the changelog.

>> +static struct value *
>> +fetch_const_value_from_synthetic_pointer (sect_offset die, LONGEST byte_offset,
> Missing intro comment, which should leave no doubt on why is the function
> named "const value", but then returns a non-const value.

I meant it was a DW_AT_const_value, which (AFAIK) isn't necessarily
represented by a const struct value in gdb.

> But you can actually remove the "result" local and call "return" directly
> in both branches.

Yeah, I just like it when functions have a single return statement.
Will change it :)

>> +# This matches e.g. '{10, 1, 2, 3, 4}'
>> +set new_contents [format {\{%d[\d,\s]+\}} ${first_value}]
>
> Why not match exactly "{10, 1, 2, 3, 4}" ?

The idea was to let the test maintainer alter the array (e.g. add
more/different elements) whenever possible without having to touch the
TCL file as well. But if it's ok to assume it won't change then I can
simplify the regex a bit.

>> +      /* Else, we have a synthetic reference.  Don't print '@address'; we'll
>> +      show '<synthetic pointer>' instead through valprint_check_validity.
>> +      Notice however that 'set print object on' will still show '@address'.
>
> Is that a bug, or desirable?

I don't think it's a bug (the address that'll be shown is the
referenced variable's, not something like 0x0). If you ask me, I think
we can just leave it as a (documented) corner case.

Thanks for the feedback!
  

Patch

diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c
index bfe1173..ecd7cc4 100644
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -2061,6 +2061,66 @@  get_frame_address_in_block_wrapper (void *baton)
   return get_frame_address_in_block ((struct frame_info *) baton);
 }
 
+static struct value *
+fetch_const_value_from_synthetic_pointer (sect_offset die, LONGEST byte_offset,
+					  struct dwarf2_per_cu_data *per_cu,
+					  struct type *type)
+{
+  struct value *result = NULL;
+  struct obstack temp_obstack;
+  struct cleanup *cleanup;
+  const gdb_byte *bytes;
+  LONGEST len;
+  obstack_init (&temp_obstack);
+  cleanup = make_cleanup_obstack_free (&temp_obstack);
+  bytes = dwarf2_fetch_constant_bytes (die, per_cu, &temp_obstack, &len);
+
+  if (bytes != NULL)
+    {
+      if (byte_offset >= 0
+	  && byte_offset + TYPE_LENGTH (TYPE_TARGET_TYPE (type)) <= len)
+	{
+	  bytes += byte_offset;
+	  result = value_from_contents (TYPE_TARGET_TYPE (type), bytes);
+	}
+      else
+	invalid_synthetic_pointer ();
+    }
+  else
+    result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
+
+  do_cleanups (cleanup);
+
+  return result;
+}
+
+/* Fetch the value pointed at by a synthetic pointer.  */
+static struct value *
+indirect_synthetic_pointer (sect_offset die, LONGEST byte_offset,
+			    struct dwarf2_per_cu_data *per_cu,
+			    struct frame_info *frame, struct type *type)
+{
+  struct value *result = NULL;
+
+  /* Fetch the location expression of the DIE we're pointing to.  */
+  struct dwarf2_locexpr_baton baton
+    = dwarf2_fetch_die_loc_sect_off (die, per_cu,
+				     get_frame_address_in_block_wrapper, frame);
+
+  /* If pointed-to DIE has a DW_AT_location, evaluate it and return the
+     resulting value.  Otherwise, it may have a DW_AT_const_value instead,
+     or it may've been optimized out.  */
+  if (baton.data != NULL)
+    result = dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
+					    baton.data, baton.size,
+					    baton.per_cu, byte_offset);
+  else
+    result = fetch_const_value_from_synthetic_pointer (die, byte_offset, per_cu,
+						       type);
+
+    return result;
+}
+
 /* An implementation of an lval_funcs method to indirect through a
    pointer.  This handles the synthetic pointer case when needed.  */
 
@@ -2115,6 +2175,7 @@  indirect_pieced_value (struct value *value)
       break;
     }
 
+  gdb_assert (piece);
   frame = get_selected_frame (_("No frame selected."));
 
   /* This is an offset requested by GDB, such as value subscripts.
@@ -2132,43 +2193,35 @@  indirect_pieced_value (struct value *value)
 					TYPE_LENGTH (type), byte_order);
   byte_offset += piece->v.ptr.offset;
 
-  gdb_assert (piece);
-  baton
-    = dwarf2_fetch_die_loc_sect_off (piece->v.ptr.die, c->per_cu,
-				     get_frame_address_in_block_wrapper,
-				     frame);
+  return indirect_synthetic_pointer (piece->v.ptr.die, byte_offset, c->per_cu,
+				     frame, type);
+}
 
-  if (baton.data != NULL)
-    return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
-					  baton.data, baton.size, baton.per_cu,
-					  byte_offset);
-
-  {
-    struct obstack temp_obstack;
-    struct cleanup *cleanup;
-    const gdb_byte *bytes;
-    LONGEST len;
-    struct value *result;
-
-    obstack_init (&temp_obstack);
-    cleanup = make_cleanup_obstack_free (&temp_obstack);
-
-    bytes = dwarf2_fetch_constant_bytes (piece->v.ptr.die, c->per_cu,
-					 &temp_obstack, &len);
-    if (bytes == NULL)
-      result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
-    else
-      {
-	if (byte_offset < 0
-	    || byte_offset + TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > len)
-	  invalid_synthetic_pointer ();
-	bytes += byte_offset;
-	result = value_from_contents (TYPE_TARGET_TYPE (type), bytes);
-      }
+static struct value *coerce_pieced_ref (const struct value *value)
+{
+  struct value *result = NULL;
+  struct type *type = check_typedef (value_type (value));
 
-    do_cleanups (cleanup);
-    return result;
-  }
+  if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
+				    TARGET_CHAR_BIT * TYPE_LENGTH (type)))
+    {
+      const struct piece_closure *closure
+	= (struct piece_closure *) value_computed_closure (value);
+
+      /* gdb represents synthetic pointers as pieced values with a single
+       piece.  */
+      gdb_assert (closure != NULL);
+      gdb_assert (closure->n_pieces == 1);
+
+      struct frame_info *frame = get_selected_frame (_("No frame selected."));
+
+      result = indirect_synthetic_pointer (closure->pieces->v.ptr.die,
+					   closure->pieces->v.ptr.offset,
+					   closure->per_cu, frame, type);
+    }
+  /* Else: not a synthetic reference; do nothing.  */
+
+  return result;
 }
 
 static void *
@@ -2206,7 +2259,7 @@  static const struct lval_funcs pieced_value_funcs = {
   read_pieced_value,
   write_pieced_value,
   indirect_pieced_value,
-  NULL,	/* coerce_ref */
+  coerce_pieced_ref,
   check_pieced_synthetic_pointer,
   copy_pieced_value_closure,
   free_pieced_value_closure
diff --git a/gdb/testsuite/gdb.dwarf2/implref-array.c b/gdb/testsuite/gdb.dwarf2/implref-array.c
new file mode 100644
index 0000000..2470412
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-array.c
@@ -0,0 +1,27 @@ 
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test program for synthetic C++ references to arrays.  */
+
+int array[5] = {0, 1, 2, 3, 4};
+
+int
+main (void)
+{
+  asm ("main_label: .globl main_label");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-array.exp b/gdb/testsuite/gdb.dwarf2/implref-array.exp
new file mode 100644
index 0000000..4569de7
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-array.exp
@@ -0,0 +1,168 @@ 
+# Copyright 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global array whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+    continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-array.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-array".  srcfile2 is "implref-array.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-array program.
+# Any program would do, but since we already have implref-array
+# specifically for this testcase, might as well use that.
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    untested ${testfile}.exp
+    return -1
+}
+
+set array_length [get_valueof "/u" "sizeof(array) / sizeof(array\[0\])" 5]
+
+# Create the DWARF.  We need a regular variable which represents the array, and
+# a reference to it that'll be marked with DW_OP_GNU_implicit_pointer.
+# The variable must be global so that its name is an exported symbol that we
+# can reference from the DWARF using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+    global srcdir subdir srcfile array_length
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_language @DW_LANG_C_plus_plus}
+	} {
+	    declare_labels int_label sizetype_label array_label variable_label ref_label
+	    set int_size [get_sizeof "int" 4]
+	    set upper_bound [expr ${array_length} - 1]
+
+	    # gdb always assumes references are implemented as pointers.
+	    set addr_size [get_sizeof "void *" 4]
+
+	    int_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_name "int"}
+	    }
+
+	    sizetype_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_unsigned}
+		{DW_AT_name "sizetype"}
+	    }
+
+	    array_label: DW_TAG_array_type {
+		    {DW_AT_type :${int_label}}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type :${sizetype_label}}
+		    {DW_AT_lower_bound 0 DW_FORM_udata}
+		    {DW_AT_upper_bound ${upper_bound} DW_FORM_udata}
+		}
+	    }
+
+	    ref_label: DW_TAG_reference_type {
+		{DW_AT_byte_size ${addr_size} DW_FORM_udata}
+		{DW_AT_type :${array_label}}
+	    }
+
+	    variable_label: DW_TAG_variable {
+		{DW_AT_name "array"}
+		{DW_AT_type :${array_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "array"]} SPECIAL_expr}
+	    }
+
+	    DW_TAG_subprogram {
+		{MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+	    } {
+		DW_TAG_variable {
+		    {DW_AT_name "ref"}
+		    {DW_AT_type :${ref_label}}
+		    {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+    return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+    return -1
+}
+
+# This matches e.g. '(int (&)[5])'
+set ref_type [format {\(int \(&\)\[%d\]\)} ${array_length}]
+
+# This matches e.g. '(int (*)[5])'
+set ptr_type [format {\(int \(\*\)\[%d\]\)} ${array_length}]
+
+# Contents of the array.  Trim leading/trailing whitespace, '{' and '}'
+# since they confuse TCL to no end.
+set contents [get_valueof "" "array" "\{0, 1, 2, 3, 4\}"]
+set contents [string trim ${contents}]
+set contents [string trim ${contents} "{}"]
+
+# Doing 'print ref' should show us e.g. '(int (&)[5]) <synthetic pointer>: {0, 1, 2, 3, 4}'.
+gdb_test "print ref" " = ${ref_type} <synthetic pointer>: \\{${contents}\\}" "print ref"
+
+# Doing 'print &ref' should show us e.g. '(int (*)[5]) 0xdeadbeef <array>'.
+gdb_test "print &ref" " = ${ptr_type} \[\[:xdigit:\]x\]+ <array>" "print &ref"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address.  Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory." "print &(&ref)"
+
+# Test assignment through the synthetic reference.
+set first_value 10
+gdb_test_no_output "set (ref\[0\] = ${first_value})" "set (ref\[0\] = ${first_value})"
+
+# This matches e.g. '{10, 1, 2, 3, 4}'
+set new_contents [format {\{%d[\d,\s]+\}} ${first_value}]
+
+# Doing 'print ref' should now show us e.g.
+# '(int (&)[5]) <synthetic pointer>: {10, 1, 2, 3, 4}'.
+gdb_test "print ref" " = ${ref_type} <synthetic pointer>: ${new_contents}" "print ref (after assignment)"
+gdb_test "print array" " = ${new_contents}" "print array (after assignment)"
+
+# Test treating the array as a pointer.
+set second_value 20
+set new_contents [format {\{%d, %d[\d,\s]+\}} ${first_value} ${second_value}]
+
+gdb_test "print *ref" " = ${first_value}" "print *ref"
+gdb_test_no_output "set (*(ref + 1) = ${second_value})" "set (*(ref + 1) = ${second_value})"
+gdb_test "print ref\[1\]" " = ${second_value}" "print ref\[1\]"
+gdb_test "print array" " = ${new_contents}" "print array (after second assignment)"
diff --git a/gdb/testsuite/gdb.dwarf2/implref-const.exp b/gdb/testsuite/gdb.dwarf2/implref-const.exp
new file mode 100644
index 0000000..cfae3d6
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-const.exp
@@ -0,0 +1,120 @@ 
+# Copyright 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a DW_AT_const_value.
+
+if [skip_cplus_tests] {
+    continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-const.S.
+standard_testfile main.c .S
+
+# ${testfile} is now "implref-const".  srcfile2 is "implref-const.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-const program.
+# Any program would do, but since we already have implref-const
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] {
+    untested ${testfile}.exp
+    return -1
+}
+
+# Create the DWARF.  We need a regular variable and a reference to it that'll
+# be marked with DW_OP_GNU_implicit_pointer.
+Dwarf::assemble ${asm_file} {
+    global srcdir subdir srcfile
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_language @DW_LANG_C_plus_plus}
+	} {
+	    declare_labels int_label const_label variable_label ref_label
+	    set int_size [get_sizeof "int" 4]
+
+	    # gdb always assumes references are implemented as pointers.
+	    set addr_size [get_sizeof "void *" 4]
+	    set var_value 42
+
+	    int_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_name "int"}
+	    }
+
+	    ref_label: DW_TAG_reference_type {
+		{DW_AT_byte_size ${addr_size} DW_FORM_udata}
+		{DW_AT_type :${int_label}}
+	    }
+
+	    const_label: DW_TAG_const_type {
+		{DW_AT_type :${ref_label}}
+	    }
+
+	    DW_TAG_subprogram {
+		{MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+	    } {
+		variable_label: DW_TAG_variable {
+		    {DW_AT_name "var"}
+		    {DW_AT_type :${int_label}}
+		    {DW_AT_const_value ${var_value} DW_FORM_udata}
+		}
+
+		DW_TAG_variable {
+		    {DW_AT_name "ref"}
+		    {DW_AT_type :${const_label}}
+		    {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+    return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+    return -1
+}
+
+# Doing 'print ref' should show us e.g. '(int &) <synthetic pointer>: 42'.
+set value_output {\(int &\) <synthetic pointer>: \d+}
+gdb_test "print ref" " = ${value_output}" "print ref"
+
+# The variable isn't located in memory, thus we can't take its address.
+gdb_test "print &var" "Can't take address of \"var\" which isn't an lvalue." "print &var"
+gdb_test "print &ref" "Attempt to take address of value not located in memory." "print &ref"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address.
+# Since in this case there's no physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory." "print &(&ref)"
diff --git a/gdb/testsuite/gdb.dwarf2/implref-global.c b/gdb/testsuite/gdb.dwarf2/implref-global.c
new file mode 100644
index 0000000..7814a30
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-global.c
@@ -0,0 +1,27 @@ 
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test program for synthetic C++ references to global variables.  */
+
+int global_var = 42;
+
+int
+main (void)
+{
+  asm ("main_label: .globl main_label");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-global.exp b/gdb/testsuite/gdb.dwarf2/implref-global.exp
new file mode 100644
index 0000000..60c8b71
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-global.exp
@@ -0,0 +1,124 @@ 
+# Copyright 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global variable whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+    continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-global.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-global".  srcfile2 is "implref-global.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-global program.
+# Any program would do, but since we already have implref-global
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] {
+    untested ${testfile}.exp
+    return -1
+}
+
+# Create the DWARF.  We need a regular variable and a reference to it that'll
+# be marked with DW_OP_GNU_implicit_pointer.  The variable must be global so
+# that its name is an exported symbol that we can reference from the DWARF
+# using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+    global srcdir subdir srcfile
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_language @DW_LANG_C_plus_plus}
+	} {
+	    declare_labels int_label variable_label ref_label
+	    set int_size [get_sizeof "int" 4]
+
+	    # gdb always assumes references are implemented as pointers.
+	    set addr_size [get_sizeof "void *" 4]
+
+	    int_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_name "int"}
+	    }
+
+	    ref_label: DW_TAG_reference_type {
+		{DW_AT_byte_size ${addr_size} DW_FORM_udata}
+		{DW_AT_type :${int_label}}
+	    }
+
+	    variable_label: DW_TAG_variable {
+		{DW_AT_name "global_var"}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "global_var"]} SPECIAL_expr}
+	    }
+
+	    DW_TAG_subprogram {
+		{MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+	    } {
+		DW_TAG_variable {
+		    {DW_AT_name "ref"}
+		    {DW_AT_type :${ref_label}}
+		    {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+    return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+    return -1
+}
+
+# Doing 'print ref' should show us e.g. '(int &) <synthetic pointer>: 42'.
+set value_output {\(int &\) <synthetic pointer>: \d+}
+gdb_test "print ref" " = ${value_output}" "print ref"
+
+# Doing 'print &ref' should show us e.g. '(int *) 0xdeadbeef <global_var>'.
+set addr_output {\(int \*\) [[:xdigit:]x]+ <global_var>}
+gdb_test "print &ref" " = ${addr_output}" "print &ref"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address.  Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory." "print &(&ref)"
+
+# Test assignment through the synthetic reference.
+set new_value 10
+gdb_test_no_output "set (ref = ${new_value})" "set (ref = ${new_value})"
+gdb_test "print ref" " = \\(int &\\) <synthetic pointer>: ${new_value}" "print ref (after assignment)"
+gdb_test "print global_var" " = ${new_value}" "print global_var (after assignment)"
diff --git a/gdb/testsuite/gdb.dwarf2/implref-struct.c b/gdb/testsuite/gdb.dwarf2/implref-struct.c
new file mode 100644
index 0000000..c241264
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-struct.c
@@ -0,0 +1,43 @@ 
+/* Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Test program for synthetic C++ references to structs.  */
+
+struct S {
+  int a;
+  int b;
+  int c;
+};
+
+struct S s1 = {
+  .a = 0,
+  .b = 1,
+  .c = 2
+};
+
+struct S s2 = {
+  .a = 10,
+  .b = 11,
+  .c = 12
+};
+
+int
+main (void)
+{
+  asm ("main_label: .globl main_label");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/implref-struct.exp b/gdb/testsuite/gdb.dwarf2/implref-struct.exp
new file mode 100644
index 0000000..cb2be7b
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/implref-struct.exp
@@ -0,0 +1,173 @@ 
+# Copyright 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global struct whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+    continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-struct.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-struct".  srcfile2 is "implref-struct.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-struct program.
+# Any program would do, but since we already have implref-struct
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug c++}] {
+    untested ${testfile}.exp
+    return -1
+}
+
+# Create the DWARF.  We need a regular variable for the struct and a reference
+# to it that'll be marked with DW_OP_GNU_implicit_pointer. The variable must be
+# global so that its name is an exported symbol that can we can reference from
+# the DWARF using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+    global srcdir subdir srcfile
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_language @DW_LANG_C_plus_plus}
+	} {
+	    declare_labels int_label struct_label variable_label ref_label
+	    set int_size [get_sizeof "int" 4]
+
+	    # gdb always assumes references are implemented as pointers.
+	    set addr_size [get_sizeof "void *" 4]
+	    set S_size [get_sizeof "S" 12]
+
+	    # The compiler shouldn't introduce structure padding here.
+	    set b_offset 4
+	    set c_offset 8
+
+	    int_label: DW_TAG_base_type {
+		{DW_AT_byte_size ${int_size} DW_FORM_udata}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_name "int"}
+	    }
+
+	    struct_label: DW_TAG_structure_type {
+		{DW_AT_name "S"}
+		{DW_AT_byte_size ${S_size} DW_FORM_udata}
+	    } {
+		DW_TAG_member {
+		    {DW_AT_name "a"}
+		    {DW_AT_type :${int_label}}
+		    {DW_AT_data_member_location 0 DW_FORM_udata}
+		}
+
+		DW_TAG_member {
+		    {DW_AT_name "b"}
+		    {DW_AT_type :${int_label}}
+		    {DW_AT_data_member_location ${b_offset} DW_FORM_udata}
+		}
+		
+		DW_TAG_member {
+		    {DW_AT_name "c"}
+		    {DW_AT_type :${int_label}}
+		    {DW_AT_data_member_location ${c_offset} DW_FORM_udata}
+		}
+	    }
+
+	    ref_label: DW_TAG_reference_type {
+		{DW_AT_byte_size ${addr_size} DW_FORM_udata}
+		{DW_AT_type :${struct_label}}
+	    }
+
+	    variable_label: DW_TAG_variable {
+		{DW_AT_name "s1"}
+		{DW_AT_type :${struct_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "s1"]} SPECIAL_expr}
+	    }
+
+	    DW_TAG_variable {
+		{DW_AT_name "s2"}
+		{DW_AT_type :${struct_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "s2"]} SPECIAL_expr}
+	    }
+
+	    DW_TAG_subprogram {
+		{MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+		{DW_AT_type :${int_label}}
+		{DW_AT_external 1 DW_FORM_flag}
+	    } {
+	        DW_TAG_variable {
+		    {DW_AT_name "ref"}
+		    {DW_AT_type :${ref_label}}
+		    {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+    return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+    return -1
+}
+
+# Returns the struct members.
+proc get_members {var default} {
+	set members [get_valueof "" ${var} ${default}]
+	
+	# Trim leading/trailing whitespace, '{' and '}' since they confuse TCL
+	# to no end.
+	set members [string trim ${members}]
+	set members [string trim ${members} "{}"]
+
+	return ${members}
+}
+
+# Values of the struct members.
+set s1_members [get_members "s1" "\{a = 0, b = 1, c = 2\}"]
+set s2_members [get_members "s2" "\{a = 10, b = 11, c = 12\}"]
+
+# Doing 'print ref' should show us e.g. '(S &) <synthetic pointer>: {a = 0, b = 1, c = 2}'.
+gdb_test "print ref" " = \\(S &\\) <synthetic pointer>: \\{${s1_members}\\}" "print ref"
+
+# Doing 'print &ref' should show us e.g. '(S *) 0xdeadbeef <s1>'.
+set addr_output {\(S \*\) [[:xdigit:]x]+ <s1>}
+gdb_test "print &ref" " = ${addr_output}" "print &ref"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address.  Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory." "print &(&ref)"
+
+# Test assignment through the synthetic reference.
+gdb_test_no_output "set (ref = s2)" "set (ref = s2)"
+gdb_test "print ref" " = \\(S &\\) <synthetic pointer>: \\{${s2_members}\\}" "print ref (after assignment)"
+gdb_test "print s1" " = \\{${s2_members}\\}" "print s1 (after assignment)"
diff --git a/gdb/testsuite/gdb.dwarf2/implref.exp b/gdb/testsuite/gdb.dwarf2/implref.exp
deleted file mode 100644
index ca4766e..0000000
--- a/gdb/testsuite/gdb.dwarf2/implref.exp
+++ /dev/null
@@ -1,98 +0,0 @@ 
-# Copyright 2016 Free Software Foundation, Inc.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-# Test C++ references marked with DW_OP_GNU_implicit_pointer.
-
-# TODO: Add more test statements after fixing bug #19893:
-# https://sourceware.org/bugzilla/show_bug.cgi?id=19893.
-
-load_lib dwarf.exp
-
-# This test can only be run on targets which support DWARF-2 and use gas.
-if {![dwarf2_support]} {
-    return 0
-}
-
-# We'll place the output of Dwarf::assemble in implref.S.
-standard_testfile main.c .S
-
-# ${testfile} is now "implref".  srcfile2 is "implref.S".
-set executable ${testfile}
-set asm_file [standard_output_file ${srcfile2}]
-
-# Create the DWARF.  We need a regular variable and a reference to it that'll
-# be marked with DW_OP_GNU_implicit_pointer.
-Dwarf::assemble $asm_file {
-    global srcdir subdir srcfile
-
-    cu { version 3 addr_size 4 } {
-	DW_TAG_compile_unit {
-	    {DW_AT_producer "GNU C++ 4.8.4"}
-	    {DW_AT_language @DW_LANG_C_plus_plus}
-	} {
-	    declare_labels int_label const_label variable_label ref_label
-
-	    int_label: DW_TAG_base_type {
-		{DW_AT_byte_size 4 DW_FORM_udata}
-		{DW_AT_encoding @DW_ATE_signed}
-		{DW_AT_name "int"}
-	    }
-
-	    ref_label: DW_TAG_reference_type {
-		{DW_AT_byte_size 4 DW_FORM_udata}
-		{DW_AT_type :${int_label}}
-	    }
-
-	    const_label: DW_TAG_const_type {
-		{DW_AT_type :${ref_label}}
-	    }
-
-	    DW_TAG_subprogram {
-		{MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
-		{DW_AT_type :${int_label}}
-		{DW_AT_external 1 DW_FORM_flag}
-	    } {
-		variable_label: DW_TAG_variable {
-		    {DW_AT_name "var"}
-		    {DW_AT_type :${int_label}}
-		    {DW_AT_const_value 42 DW_FORM_udata}
-		}
-
-		DW_TAG_variable {
-		    {DW_AT_name "ref"}
-		    {DW_AT_type :${const_label}}
-		    {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
-		}
-	    }
-	}
-    }
-}
-
-if [prepare_for_testing ${testfile}.exp ${executable} "${asm_file} ${srcfile}" {}] {
-    return -1
-}
-
-# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
-if ![runto_main] {
-    return -1
-}
-
-gdb_test "print ref" " = \\(int &\\) <synthetic pointer>" "print ref"
-gdb_test "print &ref" " = \\(int \\*\\) <synthetic pointer>" "print &ref"
-
-# gdb assumes C++ references are implemented as pointers, and print &(&ref)
-# shows us the underlying pointer's address.
-# Since in this case there's no physical pointer, gdb should tell us so.
-gdb_test "print &\(&ref\)" "Attempt to take address of value not located in memory." "print &(&ref)"
diff --git a/gdb/valops.c b/gdb/valops.c
index 71fb1b3..7f9cb93 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1465,22 +1465,28 @@  value_addr (struct value *arg1)
 
   if (TYPE_CODE (type) == TYPE_CODE_REF)
     {
-      /* Copy the value, but change the type from (T&) to (T*).  We
-	 keep the same location information, which is efficient, and
-	 allows &(&X) to get the location containing the reference.
-	 Do the same to its enclosing type for consistency.  */
-      struct type *type_ptr
-        = lookup_pointer_type (TYPE_TARGET_TYPE (type));
-      struct type *enclosing_type
-        = check_typedef (value_enclosing_type (arg1));
-      struct type *enclosing_type_ptr
-        = lookup_pointer_type (TYPE_TARGET_TYPE (enclosing_type));
-
-      arg2 = value_copy (arg1);
-      deprecated_set_value_type (arg2, type_ptr);
-      set_value_enclosing_type (arg2, enclosing_type_ptr);
+      if (value_bits_synthetic_pointer (arg1, value_embedded_offset (arg1),
+	  TARGET_CHAR_BIT * TYPE_LENGTH (type)))
+	arg1 = coerce_ref (arg1);
+      else
+	{
+	  /* Copy the value, but change the type from (T&) to (T*).  We
+	     keep the same location information, which is efficient, and
+	     allows &(&X) to get the location containing the reference.
+	     Do the same to its enclosing type for consistency.  */
+	  struct type *type_ptr
+	    = lookup_pointer_type (TYPE_TARGET_TYPE (type));
+	  struct type *enclosing_type
+	    = check_typedef (value_enclosing_type (arg1));
+	  struct type *enclosing_type_ptr
+	    = lookup_pointer_type (TYPE_TARGET_TYPE (enclosing_type));
+
+	  arg2 = value_copy (arg1);
+	  deprecated_set_value_type (arg2, type_ptr);
+	  set_value_enclosing_type (arg2, enclosing_type_ptr);
 
-      return arg2;
+	  return arg2;
+	}
     }
   if (TYPE_CODE (type) == TYPE_CODE_FUNC)
     return value_coerce_function (arg1);
diff --git a/gdb/valprint.c b/gdb/valprint.c
index cea69f3..8eb18ce 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -336,7 +336,9 @@  valprint_check_validity (struct ui_file *stream,
 					TARGET_CHAR_BIT * TYPE_LENGTH (type)))
 	{
 	  fputs_filtered (_("<synthetic pointer>"), stream);
-	  return 0;
+
+	  /* C++ references should be valid even if they're synthetic.  */
+	  return TYPE_CODE (type) == TYPE_CODE_REF;
 	}
 
       if (!value_bytes_available (val, embedded_offset, TYPE_LENGTH (type)))
@@ -497,13 +499,29 @@  generic_val_print_ref (struct type *type, const gdb_byte *valaddr,
 
   if (options->addressprint)
     {
-      CORE_ADDR addr
-	= extract_typed_address (valaddr + embedded_offset, type);
+      const int is_synthetic =
+	value_bits_synthetic_pointer (original_value,
+				      TARGET_CHAR_BIT * embedded_offset,
+				      TARGET_CHAR_BIT * TYPE_LENGTH (type));
+      if (!is_synthetic)
+	{
+	  CORE_ADDR addr
+	    = extract_typed_address (valaddr + embedded_offset, type);
 
-      fprintf_filtered (stream, "@");
-      fputs_filtered (paddress (gdbarch, addr), stream);
-      if (options->deref_ref)
-	fputs_filtered (": ", stream);
+	  fprintf_filtered (stream, "@");
+	  fputs_filtered (paddress (gdbarch, addr), stream);
+	}
+      /* Else, we have a synthetic reference.  Don't print '@address'; we'll
+	 show '<synthetic pointer>' instead through valprint_check_validity.
+	 Notice however that 'set print object on' will still show '@address'.
+	 This happens because value_bits_synthetic_pointer returns false if
+	 original value is not 'lval_computed'.  While that's usually the case,
+	 if options->objectprint is true, c_value_print will call value_addr
+	 on the reference, which coerces synthetic references and returns a
+	 'not_lval'.  */
+
+	if (options->deref_ref)
+	  fputs_filtered (": ", stream);
     }
   /* De-reference the reference.  */
   if (options->deref_ref)