diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 87997b76338..b73147aea9f 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -4605,6 +4605,12 @@ to return a nonzero value when it is required, the compiler will run out
 of spill registers and print a fatal error message.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_GEN_RTX_COMPLEX (machine_mode @var{mode}, rtx @var{real_part}, rtx @var{imag_part})
+This hook should return an rtx representing a complex of mode @var{machine_mode} built from @var{real_part} and @var{imag_part}.
+  If both arguments are @code{NULL}, create them as registers.
+ The default is @code{gen_rtx_CONCAT}.
+@end deftypefn
+
 @deftypefn {Target Hook} rtx TARGET_READ_COMPLEX_PART (rtx @var{cplx}, complex_part_t @var{part})
 This hook should return the rtx representing the specified @var{part} of the complex given by @var{cplx}.
   @var{part} can be the real part, the imaginary part, or both of them.
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index efbf972e6a7..dd39e450903 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -3390,6 +3390,8 @@ stack.
 
 @hook TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P
 
+@hook TARGET_GEN_RTX_COMPLEX
+
 @hook TARGET_READ_COMPLEX_PART
 
 @hook TARGET_WRITE_COMPLEX_PART
diff --git a/gcc/emit-rtl.cc b/gcc/emit-rtl.cc
index f6276a2d0b6..22012bfea13 100644
--- a/gcc/emit-rtl.cc
+++ b/gcc/emit-rtl.cc
@@ -1190,19 +1190,7 @@ gen_reg_rtx (machine_mode mode)
   if (generating_concat_p
       && (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
 	  || GET_MODE_CLASS (mode) == MODE_COMPLEX_INT))
-    {
-      /* For complex modes, don't make a single pseudo.
-	 Instead, make a CONCAT of two pseudos.
-	 This allows noncontiguous allocation of the real and imaginary parts,
-	 which makes much better code.  Besides, allocating DCmode
-	 pseudos overstrains reload on some machines like the 386.  */
-      rtx realpart, imagpart;
-      machine_mode partmode = GET_MODE_INNER (mode);
-
-      realpart = gen_reg_rtx (partmode);
-      imagpart = gen_reg_rtx (partmode);
-      return gen_rtx_CONCAT (mode, realpart, imagpart);
-    }
+    return targetm.gen_rtx_complex (mode, NULL, NULL);
 
   /* Do not call gen_reg_rtx with uninitialized crtl.  */
   gcc_assert (crtl->emit.regno_pointer_align_length);
@@ -6274,14 +6262,18 @@ init_emit_once (void)
 
   FOR_EACH_MODE_IN_CLASS (mode, MODE_COMPLEX_INT)
     {
-      rtx inner = const_tiny_rtx[0][(int)GET_MODE_INNER (mode)];
-      const_tiny_rtx[0][(int) mode] = gen_rtx_CONCAT (mode, inner, inner);
+      machine_mode imode = GET_MODE_INNER (mode);
+      rtx inner = const_tiny_rtx[0][(int) imode];
+      const_tiny_rtx[0][(int) mode] =
+	targetm.gen_rtx_complex (mode, inner, inner);
     }
 
   FOR_EACH_MODE_IN_CLASS (mode, MODE_COMPLEX_FLOAT)
     {
-      rtx inner = const_tiny_rtx[0][(int)GET_MODE_INNER (mode)];
-      const_tiny_rtx[0][(int) mode] = gen_rtx_CONCAT (mode, inner, inner);
+      machine_mode imode = GET_MODE_INNER (mode);
+      rtx inner = const_tiny_rtx[0][(int) imode];
+      const_tiny_rtx[0][(int) mode] =
+	targetm.gen_rtx_complex (mode, inner, inner);
     }
 
   FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_BOOL)
diff --git a/gcc/expmed.cc b/gcc/expmed.cc
index 2f787cc28f9..8a18161827b 100644
--- a/gcc/expmed.cc
+++ b/gcc/expmed.cc
@@ -400,7 +400,7 @@ flip_storage_order (machine_mode mode, rtx x)
       real = flip_storage_order (GET_MODE_INNER (mode), real);
       imag = flip_storage_order (GET_MODE_INNER (mode), imag);
 
-      return gen_rtx_CONCAT (mode, real, imag);
+      return targetm.gen_rtx_complex (mode, real, imag);
     }
 
   if (UNLIKELY (reverse_storage_order_supported < 0))
diff --git a/gcc/optabs.cc b/gcc/optabs.cc
index 4e9f58f8060..18900e8113e 100644
--- a/gcc/optabs.cc
+++ b/gcc/optabs.cc
@@ -1001,16 +1001,18 @@ expand_doubleword_mod (machine_mode mode, rtx op0, rtx op1, bool unsignedp)
 	  machine_mode cmode = TYPE_MODE (ctype);
 	  rtx op00 = operand_subword_force (op0, 0, mode);
 	  rtx op01 = operand_subword_force (op0, 1, mode);
-	  rtx cres = gen_rtx_CONCAT (cmode, gen_reg_rtx (word_mode),
-				     gen_reg_rtx (word_mode));
+	  rtx cres = targetm.gen_rtx_complex (cmode, gen_reg_rtx (word_mode),
+					      gen_reg_rtx (word_mode));
 	  tree lhs = make_tree (ctype, cres);
 	  tree arg0 = make_tree (wtype, op00);
 	  tree arg1 = make_tree (wtype, op01);
 	  expand_addsub_overflow (UNKNOWN_LOCATION, PLUS_EXPR, lhs, arg0,
 				  arg1, true, true, true, false, NULL);
-	  sum = expand_simple_binop (word_mode, PLUS, XEXP (cres, 0),
-				     XEXP (cres, 1), NULL_RTX, 1,
-				     OPTAB_DIRECT);
+	  sum =
+	    expand_simple_binop (word_mode, PLUS,
+				 read_complex_part (cres, REAL_P),
+				 read_complex_part (cres, IMAG_P), NULL_RTX,
+				 1, OPTAB_DIRECT);
 	  if (sum == NULL_RTX)
 	    return NULL_RTX;
 	}
diff --git a/gcc/target.def b/gcc/target.def
index 9798c0f58e4..ee1dfdc7565 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -3306,6 +3306,16 @@ a pointer to int.",
  bool, (ao_ref *ref),
  default_ref_may_alias_errno)
 
+/* Return the rtx representation of a complex with a specified mode.  */
+DEFHOOK
+(gen_rtx_complex,
+ "This hook should return an rtx representing a complex of mode @var{machine_mode} built from @var{real_part} and @var{imag_part}.\n\
+  If both arguments are @code{NULL}, create them as registers.\n\
+ The default is @code{gen_rtx_CONCAT}.",
+ rtx,
+ (machine_mode mode, rtx real_part, rtx imag_part),
+ default_gen_rtx_complex)
+
 /* Returns the value corresponding to the specified part of a complex.  */
 DEFHOOK
 (read_complex_part,
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index d33fcbd9a13..4ea40c643a8 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -1532,6 +1532,33 @@ default_preferred_simd_mode (scalar_mode)
   return word_mode;
 }
 
+/* By default, call gen_rtx_CONCAT.  */
+
+rtx
+default_gen_rtx_complex (machine_mode mode, rtx real_part, rtx imag_part)
+{
+  /* For complex modes, don't make a single pseudo.
+     Instead, make a CONCAT of two pseudos.
+     This allows noncontiguous allocation of the real and imaginary parts,
+     which makes much better code.  Besides, allocating DCmode
+     pseudos overstrains reload on some machines like the 386.  */
+  machine_mode imode = GET_MODE_INNER (mode);
+
+  if (real_part == NULL)
+    real_part = gen_reg_rtx (imode);
+  else
+    gcc_assert ((GET_MODE (real_part) == imode)
+		|| (GET_MODE (real_part) == E_VOIDmode));
+
+  if (imag_part == NULL)
+    imag_part = gen_reg_rtx (imode);
+  else
+    gcc_assert ((GET_MODE (imag_part) == imode)
+		|| (GET_MODE (imag_part) == E_VOIDmode));
+
+  return gen_rtx_CONCAT (mode, real_part, imag_part);
+}
+
 /* By default, extract one of the components of the complex value CPLX.  Extract the
    real part if part is REAL_P, and the imaginary part if it is IMAG_P. If part is
    BOTH_P, return cplx directly*/
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 805abd96938..811cd6165de 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -124,6 +124,8 @@ extern opt_machine_mode default_get_mask_mode (machine_mode);
 extern bool default_empty_mask_is_expensive (unsigned);
 extern vector_costs *default_vectorize_create_costs (vec_info *, bool);
 
+extern rtx default_gen_rtx_complex (machine_mode mode, rtx real_part,
+				    rtx imag_part);
 extern rtx default_read_complex_part (rtx cplx, complex_part_t part);
 extern void default_write_complex_part (rtx cplx, rtx val,
 					complex_part_t part,
