diff mbox series

Backports to gcc-6-branch

Message ID 20181012181752.GE11582@tucnak
State New
Headers show
Series Backports to gcc-6-branch | expand

Commit Message

Jakub Jelinek Oct. 12, 2018, 6:17 p.m. UTC
Hi!

I've backported following 8 patches from gcc-8-branch to
gcc-6-branch, bootstrapped/regtested and committed to gcc-6-branch.

	Jakub
2018-10-12  Jakub Jelinek  <jakub@redhat.com>

	Backported from mainline
	2018-07-16  Jakub Jelinek  <jakub@redhat.com>

	PR c++/3698
	PR c++/86208
	* cp-gimplify.c (cp_genericize_r): When using extern_decl_map, or
	in TREE_USED flag from stmt to h->to.

	* g++.dg/opt/pr3698.C: New test.
2018-10-12  Jakub Jelinek  <jakub@redhat.com>

	Backported from mainline
	2018-07-26  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/86660
	* omp-low.c (scan_sharing_clauses): Don't ignore map clauses for
	declare target to variables if they have always,{to,from,tofrom} map
	kinds.

	* testsuite/libgomp.c/pr86660.c: New test.

--- gcc/omp-low.c	(revision 263011)
+++ gcc/omp-low.c	(revision 263012)
@@ -1190,13 +1190,16 @@ scan_sharing_clauses (tree clauses, omp_
 	  /* Global variables with "omp declare target" attribute
 	     don't need to be copied, the receiver side will use them
 	     directly.  However, global variables with "omp declare target link"
-	     attribute need to be copied.  */
+	     attribute need to be copied.  Or when ALWAYS modifier is used.  */
 	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 	      && DECL_P (decl)
 	      && ((OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FIRSTPRIVATE_POINTER
 		   && (OMP_CLAUSE_MAP_KIND (c)
 		       != GOMP_MAP_FIRSTPRIVATE_REFERENCE))
 		  || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_TO
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_FROM
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_TOFROM
 	      && is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx))
 	      && varpool_node::get_create (decl)->offloadable
 	      && !lookup_attribute ("omp declare target link",
--- libgomp/testsuite/libgomp.c/pr86660.c	(nonexistent)
+++ libgomp/testsuite/libgomp.c/pr86660.c	(revision 263012)
@@ -0,0 +1,28 @@
+/* PR middle-end/86660 */
+
+#pragma omp declare target
+int v[20];
+
+void
+foo (void)
+{
+  if (v[7] != 2)
+    __builtin_abort ();
+  v[7] = 1;
+}
+#pragma omp end declare target
+
+int
+main ()
+{
+  v[5] = 8;
+  v[7] = 2;
+  #pragma omp target map (always, tofrom: v)
+  {
+    foo ();
+    v[5] = 3;
+  }
+  if (v[7] != 1 || v[5] != 3)
+    __builtin_abort ();
+  return 0;
+}
2018-10-12  Jakub Jelinek  <jakub@redhat.com>

	Backported from mainline
	2018-07-17  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/86539
	* gimplify.c (gimplify_omp_for): Ensure taskloop firstprivatized init
	and cond temporaries don't have reference type if iterator has
	pointer type.  For init use &for_pre_body instead of pre_p if
	for_pre_body is non-empty.

	* testsuite/libgomp.c++/pr86539.C: New test.

--- gcc/gimplify.c	(revision 263013)
+++ gcc/gimplify.c	(revision 263014)
@@ -8725,9 +8725,25 @@ gimplify_omp_for (tree *expr_p, gimple_s
 	  t = TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i);
 	  if (!is_gimple_constant (TREE_OPERAND (t, 1)))
 	    {
+	      tree type = TREE_TYPE (TREE_OPERAND (t, 0));
 	      TREE_OPERAND (t, 1)
 		= get_initialized_tmp_var (TREE_OPERAND (t, 1),
-					   pre_p, NULL);
+					   gimple_seq_empty_p (for_pre_body)
+					   ? pre_p : &for_pre_body, NULL);
+	      /* Reference to pointer conversion is considered useless,
+		 but is significant for firstprivate clause.  Force it
+		 here.  */
+	      if (TREE_CODE (type) == POINTER_TYPE
+		  && (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 1)))
+		      == REFERENCE_TYPE))
+		{
+		  tree v = create_tmp_var (TYPE_MAIN_VARIANT (type));
+		  tree m = build2 (INIT_EXPR, TREE_TYPE (v), v,
+				   TREE_OPERAND (t, 1));
+		  gimplify_and_add (m, gimple_seq_empty_p (for_pre_body)
+				       ? pre_p : &for_pre_body);
+		  TREE_OPERAND (t, 1) = v;
+		}
 	      tree c = build_omp_clause (input_location,
 					 OMP_CLAUSE_FIRSTPRIVATE);
 	      OMP_CLAUSE_DECL (c) = TREE_OPERAND (t, 1);
@@ -8739,10 +8755,25 @@ gimplify_omp_for (tree *expr_p, gimple_s
 	  t = TREE_VEC_ELT (OMP_FOR_COND (for_stmt), i);
 	  if (!is_gimple_constant (TREE_OPERAND (t, 1)))
 	    {
+	      tree type = TREE_TYPE (TREE_OPERAND (t, 0));
 	      TREE_OPERAND (t, 1)
 		= get_initialized_tmp_var (TREE_OPERAND (t, 1),
 					   gimple_seq_empty_p (for_pre_body)
 					   ? pre_p : &for_pre_body, NULL);
+	      /* Reference to pointer conversion is considered useless,
+		 but is significant for firstprivate clause.  Force it
+		 here.  */
+	      if (TREE_CODE (type) == POINTER_TYPE
+		  && (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 1)))
+		      == REFERENCE_TYPE))
+		{
+		  tree v = create_tmp_var (TYPE_MAIN_VARIANT (type));
+		  tree m = build2 (INIT_EXPR, TREE_TYPE (v), v,
+				   TREE_OPERAND (t, 1));
+		  gimplify_and_add (m, gimple_seq_empty_p (for_pre_body)
+				       ? pre_p : &for_pre_body);
+		  TREE_OPERAND (t, 1) = v;
+		}
 	      tree c = build_omp_clause (input_location,
 					 OMP_CLAUSE_FIRSTPRIVATE);
 	      OMP_CLAUSE_DECL (c) = TREE_OPERAND (t, 1);
--- libgomp/testsuite/libgomp.c++/pr86539.C	(nonexistent)
+++ libgomp/testsuite/libgomp.c++/pr86539.C	(revision 263014)
@@ -0,0 +1,28 @@
+// PR middle-end/86539
+
+int a[384];
+
+__attribute__((noinline, noclone)) void
+foo (int &b, int &c)
+{
+  #pragma omp taskloop shared (a) collapse(3)
+  for (int i = 0; i < 1; i++)
+    for (int *p = &b; p < &c; p++)
+      for (int j = 0; j < 1; j++)
+	if (p < &a[128] || p >= &a[256])
+	  __builtin_abort ();
+	else
+	  p[0]++;
+}
+
+int
+main ()
+{
+  #pragma omp parallel
+  #pragma omp single
+    foo (a[128], a[256]);
+  for (int i = 0; i < 384; i++)
+    if (a[i] != (i >= 128 && i < 256))
+      __builtin_abort ();
+  return 0;
+}
2018-10-12  Jakub Jelinek  <jakub@redhat.com>

	Backported from mainline
	2018-07-17  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/86542
	* omp-low.c (create_task_copyfn): Copy over also fields corresponding
	to _looptemp_ clauses, other than the first two.

	* testsuite/libgomp.c++/pr86542.C: New test.

--- gcc/omp-low.c	(revision 263014)
+++ gcc/omp-low.c	(revision 263015)
@@ -7102,6 +7102,7 @@ create_task_copyfn (gomp_task *task_stmt
   splay_tree_node n;
   struct omp_taskcopy_context tcctx;
   location_t loc = gimple_location (task_stmt);
+  size_t looptempno = 0;
 
   child_fn = gimple_omp_task_copy_fn (task_stmt);
   child_cfun = DECL_STRUCT_FUNCTION (child_fn);
@@ -7215,6 +7216,15 @@ create_task_copyfn (gomp_task *task_stmt
 	t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src);
 	append_to_statement_list (t, &list);
 	break;
+      case OMP_CLAUSE__LOOPTEMP_:
+	/* Fields for first two _looptemp_ clauses are initialized by
+	   GOMP_taskloop*, the rest are handled like firstprivate.  */
+        if (looptempno < 2)
+	  {
+	    looptempno++;
+	    break;
+	  }
+	/* FALLTHRU */
       case OMP_CLAUSE_FIRSTPRIVATE:
 	decl = OMP_CLAUSE_DECL (c);
 	if (is_variable_sized (decl))
@@ -7240,7 +7250,10 @@ create_task_copyfn (gomp_task *task_stmt
 	  src = decl;
 	dst = build_simple_mem_ref_loc (loc, arg);
 	dst = omp_build_component_ref (dst, f);
-	t = lang_hooks.decls.omp_clause_copy_ctor (c, dst, src);
+	if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE__LOOPTEMP_)
+	  t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src);
+	else
+	  t = lang_hooks.decls.omp_clause_copy_ctor (c, dst, src);
 	append_to_statement_list (t, &list);
 	break;
       case OMP_CLAUSE_PRIVATE:
--- libgomp/testsuite/libgomp.c++/pr86542.C	(nonexistent)
+++ libgomp/testsuite/libgomp.c++/pr86542.C	(revision 263015)
@@ -0,0 +1,37 @@
+// PR middle-end/86542
+
+struct S { int s; S (); ~S (); S (const S &); };
+S s;
+
+S::S ()
+{
+}
+
+S::~S ()
+{
+}
+
+S::S (const S &x)
+{
+  s = x.s;
+}
+
+__attribute__((noinline, noclone)) void
+foo (int i, int j, int k, S s)
+{
+  if (i != 0 || j != 0 || k != 0 || s.s != 12)
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  volatile int inc = 16, jnc = 16, knc = 16;
+  s.s = 12;
+  #pragma omp taskloop collapse (3) firstprivate (s)
+  for (int i = 0; i < 16; i += inc)
+    for (int j = 0; j < 16; j += jnc)
+      for (int k = 0; k < 16; k += knc)
+	foo (i, j, k, s);
+  return 0;
+}
2018-10-12  Jakub Jelinek  <jakub@redhat.com>

	Backported from mainline
	2018-07-24  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/86627
	* expmed.c (expand_divmod): Punt if d == HOST_WIDE_INT_MIN
	and size > HOST_BITS_PER_WIDE_INT.  For size > HOST_BITS_PER_WIDE_INT
	and abs_d == d, do the power of two handling if profitable.

	* gcc.target/i386/pr86627.c: New test.

--- gcc/expmed.c	(revision 263016)
+++ gcc/expmed.c	(revision 263017)
@@ -4314,6 +4314,11 @@ expand_divmod (int rem_flag, enum tree_c
 		HOST_WIDE_INT d = INTVAL (op1);
 		unsigned HOST_WIDE_INT abs_d;
 
+		/* Not prepared to handle division/remainder by
+		   0xffffffffffffffff8000000000000000 etc.  */
+		if (d == HOST_WIDE_INT_MIN && size > HOST_BITS_PER_WIDE_INT)
+		  break;
+
 		/* Since d might be INT_MIN, we have to cast to
 		   unsigned HOST_WIDE_INT before negating to avoid
 		   undefined signed overflow.  */
@@ -4357,9 +4362,7 @@ expand_divmod (int rem_flag, enum tree_c
 						compute_mode)
 				 != CODE_FOR_nothing)))
 		  ;
-		else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d)
-			 && (size <= HOST_BITS_PER_WIDE_INT
-			     || abs_d != (unsigned HOST_WIDE_INT) d))
+		else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d))
 		  {
 		    if (rem_flag)
 		      {
--- gcc/testsuite/gcc.target/i386/pr86627.c	(nonexistent)
+++ gcc/testsuite/gcc.target/i386/pr86627.c	(revision 263017)
@@ -0,0 +1,28 @@
+/* PR middle-end/86627 */
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not "call\[^\n\r]*__divti3" } } */
+
+__int128_t
+f1 (__int128_t a)
+{
+  return a / 2;
+}
+
+__int128_t
+f2 (__int128_t a)
+{
+  return a / -2;
+}
+
+__int128_t
+f3 (__int128_t a)
+{
+  return a / 0x4000000000000000LL;
+}
+
+__int128_t
+f4 (__int128_t a)
+{
+  return a / -0x4000000000000000LL;
+}
2018-10-12  Jakub Jelinek  <jakub@redhat.com>

	Backported from mainline
	2018-08-27  Jakub Jelinek  <jakub@redhat.com>

	PR rtl-optimization/87065
	* combine.c (simplify_if_then_else): Formatting fix.
	(if_then_else_cond): Guard MULT optimization with SCALAR_INT_MODE_P
	check.
	(known_cond): Don't return const_true_rtx for vector modes.  Use
	CONST0_RTX instead of const0_rtx.  Formatting fixes.

	* gcc.target/i386/pr87065.c: New test.

--- gcc/combine.c	(revision 263872)
+++ gcc/combine.c	(revision 263873)
@@ -6455,7 +6455,7 @@ simplify_if_then_else (rtx x)
 			  pc_rtx, pc_rtx, 0, 0, 0);
       if (reg_mentioned_p (from, false_rtx))
 	false_rtx = subst (known_cond (copy_rtx (false_rtx), false_code,
-				   from, false_val),
+				       from, false_val),
 			   pc_rtx, pc_rtx, 0, 0, 0);
 
       SUBST (XEXP (x, 1), swapped ? false_rtx : true_rtx);
@@ -9294,6 +9294,7 @@ if_then_else_cond (rtx x, rtx *ptrue, rt
 
 	  if (COMPARISON_P (cond0)
 	      && COMPARISON_P (cond1)
+	      && SCALAR_INT_MODE_P (mode)
 	      && ((GET_CODE (cond0) == reversed_comparison_code (cond1, NULL)
 		   && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0))
 		   && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1)))
@@ -9474,12 +9475,12 @@ known_cond (rtx x, enum rtx_code cond, r
 	  if (COMPARISON_P (x))
 	    {
 	      if (comparison_dominates_p (cond, code))
-		return const_true_rtx;
+		return VECTOR_MODE_P (GET_MODE (x)) ? x : const_true_rtx;
 
 	      code = reversed_comparison_code (x, NULL);
 	      if (code != UNKNOWN
 		  && comparison_dominates_p (cond, code))
-		return const0_rtx;
+		return CONST0_RTX (GET_MODE (x));
 	      else
 		return x;
 	    }
@@ -9522,7 +9523,7 @@ known_cond (rtx x, enum rtx_code cond, r
 	  /* We must simplify subreg here, before we lose track of the
 	     original inner_mode.  */
 	  new_rtx = simplify_subreg (GET_MODE (x), r,
-				 inner_mode, SUBREG_BYTE (x));
+				     inner_mode, SUBREG_BYTE (x));
 	  if (new_rtx)
 	    return new_rtx;
 	  else
@@ -9547,7 +9548,7 @@ known_cond (rtx x, enum rtx_code cond, r
 	  /* We must simplify the zero_extend here, before we lose
 	     track of the original inner_mode.  */
 	  new_rtx = simplify_unary_operation (ZERO_EXTEND, GET_MODE (x),
-					  r, inner_mode);
+					      r, inner_mode);
 	  if (new_rtx)
 	    return new_rtx;
 	  else
--- gcc/testsuite/gcc.target/i386/pr87065.c	(nonexistent)
+++ gcc/testsuite/gcc.target/i386/pr87065.c	(revision 263873)
@@ -0,0 +1,22 @@
+/* PR rtl-optimization/87065 */
+/* { dg-do compile } */
+/* { dg-options "-O3 -mxop -mprefer-avx128" } */
+
+int a, c, d, e;
+short *b;
+
+void
+foo (void)
+{
+  short *g = b;
+  int h = 1;
+  unsigned i;
+  for (; h <= 1; h++)
+    g = (short *) &c;
+  for (; c; c++)
+    {
+      for (; i <= 1; i++)
+	;
+      a ^= (a > 0 <= i) + ((e += d) == 0 ?: (*g = 8));
+    }
+}
2018-10-12  Jakub Jelinek  <jakub@redhat.com>

	Backported from mainline
	2018-09-12  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/87248
	* fold-const.c (fold_ternary_loc) <case COND_EXPR>: Verify also that
	BIT_AND_EXPR's second operand is a power of two.  Formatting fix.

	* c-c++-common/torture/pr87248.c: New test.

--- gcc/fold-const.c	(revision 264230)
+++ gcc/fold-const.c	(revision 264231)
@@ -11565,10 +11565,16 @@ fold_ternary_loc (location_t loc, enum t
 	  && integer_pow2p (arg1)
 	  && TREE_CODE (TREE_OPERAND (arg0, 0)) == BIT_AND_EXPR
 	  && operand_equal_p (TREE_OPERAND (TREE_OPERAND (arg0, 0), 1),
-			      arg1, OEP_ONLY_CONST))
+			      arg1, OEP_ONLY_CONST)
+	  /* operand_equal_p compares just value, not precision, so e.g.
+	     arg1 could be 8-bit -128 and be power of two, but BIT_AND_EXPR
+	     second operand 32-bit -128, which is not a power of two (or vice
+	     versa.  */
+	  && integer_pow2p (TREE_OPERAND (TREE_OPERAND (arg0, 0), 1)))
 	return pedantic_non_lvalue_loc (loc,
-				    fold_convert_loc (loc, type,
-						      TREE_OPERAND (arg0, 0)));
+					fold_convert_loc (loc, type,
+							  TREE_OPERAND (arg0,
+									0)));
 
       /* Disable the transformations below for vectors, since
 	 fold_binary_op_with_conditional_arg may undo them immediately,
--- gcc/testsuite/c-c++-common/torture/pr87248.c	(nonexistent)
+++ gcc/testsuite/c-c++-common/torture/pr87248.c	(revision 264231)
@@ -0,0 +1,36 @@
+/* PR middle-end/87248 */
+/* { dg-do run } */
+
+void
+foo (signed char *p, int q)
+{
+  *p = q & (-__SCHAR_MAX__ - 1) ? (-__SCHAR_MAX__ - 1) : 0;
+}
+
+int
+bar (long long x)
+{
+  return x & (-__INT_MAX__ - 1) ? (-__INT_MAX__ - 1) : 0;
+}
+
+int
+main ()
+{
+#if __INT_MAX__ > 4 * __SCHAR_MAX__
+  signed char a[4];
+  foo (a, __SCHAR_MAX__ + 1U);
+  foo (a + 1, 2 * (__SCHAR_MAX__ + 1U));
+  foo (a + 2, -__INT_MAX__ - 1);
+  foo (a + 3, (__SCHAR_MAX__ + 1U) / 2);
+  if (a[0] != (-__SCHAR_MAX__ - 1) || a[1] != a[0] || a[2] != a[0] || a[3] != 0)
+    __builtin_abort ();
+#endif
+#if __LONG_LONG_MAX__ > 4 * __INT_MAX__
+  if (bar (__INT_MAX__ + 1LL) != (-__INT_MAX__ - 1)
+      || bar (2 * (__INT_MAX__ + 1LL)) != (-__INT_MAX__ - 1)
+      || bar (-__LONG_LONG_MAX__ - 1) != (-__INT_MAX__ - 1)
+      || bar ((__INT_MAX__ + 1LL) / 2) != 0)
+    __builtin_abort ();
+#endif
+  return 0;
+}
2018-10-12  Jakub Jelinek  <jakub@redhat.com>

	Backported from mainline
	2018-10-10  Jakub Jelinek  <jakub@redhat.com>

	PR target/87550
	* config/i386/i386.c (bdesc_args): Move IX86_BUILTIN_RDPMC
	from here to ...
	(bdesc_special_args): ... here.

	* gcc.target/i386/pr87550.c: New test.

--- gcc/config/i386/i386.c.jj	2018-06-25 19:24:51.358948658 +0200
+++ gcc/config/i386/i386.c	2018-10-12 17:15:14.851953119 +0200
@@ -33509,6 +33509,7 @@ static const struct builtin_description
 {
   { 0, CODE_FOR_nothing, "__builtin_ia32_rdtsc", IX86_BUILTIN_RDTSC, UNKNOWN, (int) UINT64_FTYPE_VOID },
   { 0, CODE_FOR_nothing, "__builtin_ia32_rdtscp", IX86_BUILTIN_RDTSCP, UNKNOWN, (int) UINT64_FTYPE_PUNSIGNED },
+  { 0, CODE_FOR_nothing, "__builtin_ia32_rdpmc", IX86_BUILTIN_RDPMC, UNKNOWN, (int) UINT64_FTYPE_INT },
   { 0, CODE_FOR_pause, "__builtin_ia32_pause", IX86_BUILTIN_PAUSE, UNKNOWN, (int) VOID_FTYPE_VOID },
 
   /* 80387 (for use internally for atomic compound assignment).  */
@@ -33806,7 +33807,6 @@ static const struct builtin_description
 {
   { 0, CODE_FOR_bsr, "__builtin_ia32_bsrsi", IX86_BUILTIN_BSRSI, UNKNOWN, (int) INT_FTYPE_INT },
   { OPTION_MASK_ISA_64BIT, CODE_FOR_bsr_rex64, "__builtin_ia32_bsrdi", IX86_BUILTIN_BSRDI, UNKNOWN, (int) INT64_FTYPE_INT64 },
-  { 0, CODE_FOR_nothing, "__builtin_ia32_rdpmc", IX86_BUILTIN_RDPMC, UNKNOWN, (int) UINT64_FTYPE_INT },
   { 0, CODE_FOR_rotlqi3, "__builtin_ia32_rolqi", IX86_BUILTIN_ROLQI, UNKNOWN, (int) UINT8_FTYPE_UINT8_INT },
   { 0, CODE_FOR_rotlhi3, "__builtin_ia32_rolhi", IX86_BUILTIN_ROLHI, UNKNOWN, (int) UINT16_FTYPE_UINT16_INT },
   { 0, CODE_FOR_rotrqi3, "__builtin_ia32_rorqi", IX86_BUILTIN_RORQI, UNKNOWN, (int) UINT8_FTYPE_UINT8_INT },
--- gcc/testsuite/gcc.target/i386/pr87550.c	(nonexistent)
+++ gcc/testsuite/gcc.target/i386/pr87550.c	(revision 265049)
@@ -0,0 +1,21 @@
+/* PR target/87550 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <x86intrin.h>
+
+int
+foo (int x)
+{
+  return __rdtsc () + __rdtsc ();
+}
+
+/* { dg-final { scan-assembler-times "\trdtsc\[\n\r]" 2 } } */
+
+int
+bar (int x)
+{
+  return __rdpmc (0) + __rdpmc (0);
+}
+
+/* { dg-final { scan-assembler-times "\trdpmc\[\n\r]" 2 } } */
diff mbox series

Patch

--- gcc/cp/cp-gimplify.c	(revision 262678)
+++ gcc/cp/cp-gimplify.c	(revision 262679)
@@ -1085,6 +1085,7 @@  cp_genericize_r (tree *stmt_p, int *walk
       if (h)
 	{
 	  *stmt_p = h->to;
+	  TREE_USED (h->to) |= TREE_USED (stmt);
 	  *walk_subtrees = 0;
 	  return NULL;
 	}
--- gcc/testsuite/g++.dg/opt/pr3698.C	(nonexistent)
+++ gcc/testsuite/g++.dg/opt/pr3698.C	(revision 262679)
@@ -0,0 +1,21 @@ 
+// PR c++/3698
+// { dg-do link }
+// { dg-options "-O0" }
+
+struct X {
+  int i;
+};
+
+inline const int&
+OHashKey (const X& x)
+{
+  return x.i;
+}
+
+int
+main ()
+{
+ extern const int& OHashKey (const X& x);
+ X x;
+ return OHashKey (x);
+}