diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index ca8837cef67..a000c104b53 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -443,6 +443,15 @@ enum optimize_size_level
   OPTIMIZE_SIZE_MAX
 };
 
+/* part of a complex */
+
+typedef enum
+{
+  REAL_P = 0,
+  IMAG_P = 1,
+  BOTH_P = 2
+} complex_part_t;
+
 /* Support for user-provided GGC and PCH markers.  The first parameter
    is a pointer to a pointer, the second either NULL if the pointer to
    pointer points into a GC object or the actual pointer address if
diff --git a/gcc/gensupport.cc b/gcc/gensupport.cc
index 959d1d9c83c..9aa2ba69fcd 100644
--- a/gcc/gensupport.cc
+++ b/gcc/gensupport.cc
@@ -3746,9 +3746,11 @@ match_pattern (optab_pattern *p, const char *name, const char *pat)
 		    break;
 		if (*p == 0
 		    && (! force_int || mode_class[i] == MODE_INT
+			|| mode_class[i] == MODE_COMPLEX_INT
 			|| mode_class[i] == MODE_VECTOR_INT)
 		    && (! force_partial_int
 			|| mode_class[i] == MODE_INT
+			|| mode_class[i] == MODE_COMPLEX_INT
 			|| mode_class[i] == MODE_PARTIAL_INT
 			|| mode_class[i] == MODE_VECTOR_INT)
 		    && (! force_float
diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc
index 5ffa8954022..38c48e44867 100644
--- a/gcc/lto-streamer-out.cc
+++ b/gcc/lto-streamer-out.cc
@@ -985,6 +985,7 @@ DFS::DFS_write_tree_body (struct output_block *ob,
     {
       DFS_follow_tree_edge (TREE_REALPART (expr));
       DFS_follow_tree_edge (TREE_IMAGPART (expr));
+      DFS_follow_tree_edge (TREE_COMPLEX_BOTH_PARTS (expr));
     }
 
   if (CODE_CONTAINS_STRUCT (code, TS_DECL_MINIMAL))
@@ -1417,6 +1418,7 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
     {
       visit (TREE_REALPART (t));
       visit (TREE_IMAGPART (t));
+      visit (TREE_COMPLEX_BOTH_PARTS (t));
     }
 
   if (CODE_CONTAINS_STRUCT (code, TS_DECL_MINIMAL))
diff --git a/gcc/lto/lto-common.cc b/gcc/lto/lto-common.cc
index 703e665b698..f647ee62f9e 100644
--- a/gcc/lto/lto-common.cc
+++ b/gcc/lto/lto-common.cc
@@ -1408,6 +1408,8 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map)
     {
       compare_tree_edges (TREE_REALPART (t1), TREE_REALPART (t2));
       compare_tree_edges (TREE_IMAGPART (t1), TREE_IMAGPART (t2));
+      compare_tree_edges (TREE_COMPLEX_BOTH_PARTS (t1),
+			  TREE_COMPLEX_BOTH_PARTS (t2));
     }
 
   if (CODE_CONTAINS_STRUCT (code, TS_DECL_MINIMAL))
diff --git a/gcc/tree-complex.cc b/gcc/tree-complex.cc
index 688fe13989c..63753e4acf4 100644
--- a/gcc/tree-complex.cc
+++ b/gcc/tree-complex.cc
@@ -42,6 +42,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfganal.h"
 #include "gimple-fold.h"
 #include "diagnostic-core.h"
+#include "target.h"
+#include "memmodel.h"
+#include "optabs-tree.h"
+#include "internal-fn.h"
 
 
 /* For each complex ssa name, a lattice value.  We're interested in finding
@@ -74,7 +78,7 @@ static vec<complex_lattice_t> complex_lattice_values;
    the hashtable.  */
 static int_tree_htab_type *complex_variable_components;
 
-/* For each complex SSA_NAME, a pair of ssa names for the components.  */
+/* For each complex SSA_NAME, three ssa names for the components.  */
 static vec<tree> complex_ssa_name_components;
 
 /* Vector of PHI triplets (original complex PHI and corresponding real and
@@ -476,17 +480,27 @@ create_one_component_var (tree type, tree orig, const char *prefix,
 /* Retrieve a value for a complex component of VAR.  */
 
 static tree
-get_component_var (tree var, bool imag_p)
+get_component_var (tree var, complex_part_t part)
 {
-  size_t decl_index = DECL_UID (var) * 2 + imag_p;
+  size_t decl_index = DECL_UID (var) * 3 + part;
   tree ret = cvc_lookup (decl_index);
 
   if (ret == NULL)
     {
-      ret = create_one_component_var (TREE_TYPE (TREE_TYPE (var)), var,
-				      imag_p ? "CI" : "CR",
-				      imag_p ? "$imag" : "$real",
-				      imag_p ? IMAGPART_EXPR : REALPART_EXPR);
+      switch (part)
+	{
+	case REAL_P:
+	  ret = create_one_component_var (TREE_TYPE (TREE_TYPE (var)), var,
+					  "CR", "$real", REALPART_EXPR);
+	  break;
+	case IMAG_P:
+	  ret = create_one_component_var (TREE_TYPE (TREE_TYPE (var)), var,
+					  "CI", "$imag", IMAGPART_EXPR);
+	  break;
+	case BOTH_P:
+	  ret = var;
+	  break;
+	}
       cvc_insert (decl_index, ret);
     }
 
@@ -496,13 +510,15 @@ get_component_var (tree var, bool imag_p)
 /* Retrieve a value for a complex component of SSA_NAME.  */
 
 static tree
-get_component_ssa_name (tree ssa_name, bool imag_p)
+get_component_ssa_name (tree ssa_name, complex_part_t part)
 {
   complex_lattice_t lattice = find_lattice_value (ssa_name);
   size_t ssa_name_index;
   tree ret;
 
-  if (lattice == (imag_p ? ONLY_REAL : ONLY_IMAG))
+  if (((lattice == ONLY_IMAG) && (part == REAL_P))
+      || ((lattice == ONLY_REAL) && (part == IMAG_P)))
+
     {
       tree inner_type = TREE_TYPE (TREE_TYPE (ssa_name));
       if (SCALAR_FLOAT_TYPE_P (inner_type))
@@ -511,14 +527,33 @@ get_component_ssa_name (tree ssa_name, bool imag_p)
 	return build_int_cst (inner_type, 0);
     }
 
-  ssa_name_index = SSA_NAME_VERSION (ssa_name) * 2 + imag_p;
+  if (part == BOTH_P)
+    return ssa_name;
+
+  ssa_name_index = SSA_NAME_VERSION (ssa_name) * 3 + part;
+
+  /* increase size of dynamic array if needed */
+  if (ssa_name_index >= complex_ssa_name_components.length ())
+    {
+      complex_ssa_name_components.safe_grow_cleared
+	(2 * complex_ssa_name_components.length (), true);
+      complex_lattice_values.safe_grow_cleared
+	(2 * complex_lattice_values.length (), true);
+    }
+
   ret = complex_ssa_name_components[ssa_name_index];
   if (ret == NULL)
     {
       if (SSA_NAME_VAR (ssa_name))
-	ret = get_component_var (SSA_NAME_VAR (ssa_name), imag_p);
+	ret = get_component_var (SSA_NAME_VAR (ssa_name), part);
       else
-	ret = TREE_TYPE (TREE_TYPE (ssa_name));
+	{
+	  if (part == BOTH_P)
+	    ret = TREE_TYPE (ssa_name);
+	  else
+	    ret = TREE_TYPE (TREE_TYPE (ssa_name));
+	}
+
       ret = make_ssa_name (ret);
 
       /* Copy some properties from the original.  In particular, whether it
@@ -542,7 +577,7 @@ get_component_ssa_name (tree ssa_name, bool imag_p)
    gimple_seq of stuff that needs doing.  */
 
 static gimple_seq
-set_component_ssa_name (tree ssa_name, bool imag_p, tree value)
+set_component_ssa_name (tree ssa_name, complex_part_t part, tree value)
 {
   complex_lattice_t lattice = find_lattice_value (ssa_name);
   size_t ssa_name_index;
@@ -553,14 +588,24 @@ set_component_ssa_name (tree ssa_name, bool imag_p, tree value)
   /* We know the value must be zero, else there's a bug in our lattice
      analysis.  But the value may well be a variable known to contain
      zero.  We should be safe ignoring it.  */
-  if (lattice == (imag_p ? ONLY_REAL : ONLY_IMAG))
+  if (((lattice == ONLY_IMAG) && (part == REAL_P))
+      || ((lattice == ONLY_REAL) && (part == IMAG_P)))
     return NULL;
 
   /* If we've already assigned an SSA_NAME to this component, then this
      means that our walk of the basic blocks found a use before the set.
      This is fine.  Now we should create an initialization for the value
      we created earlier.  */
-  ssa_name_index = SSA_NAME_VERSION (ssa_name) * 2 + imag_p;
+  ssa_name_index = SSA_NAME_VERSION (ssa_name) * 3 + part;
+
+  /* increase size of dynamic array if needed */
+  if (ssa_name_index >= complex_ssa_name_components.length ())
+    {
+      size_t old_size = complex_ssa_name_components.length ();
+      complex_ssa_name_components.safe_grow (2 * old_size, true);
+      complex_lattice_values.safe_grow (2 * old_size, true);
+    }
+
   comp = complex_ssa_name_components[ssa_name_index];
   if (comp)
     ;
@@ -584,7 +629,7 @@ set_component_ssa_name (tree ssa_name, bool imag_p, tree value)
 	  && (!SSA_NAME_VAR (value) || DECL_IGNORED_P (SSA_NAME_VAR (value)))
 	  && !DECL_IGNORED_P (SSA_NAME_VAR (ssa_name)))
 	{
-	  comp = get_component_var (SSA_NAME_VAR (ssa_name), imag_p);
+	  comp = get_component_var (SSA_NAME_VAR (ssa_name), part);
 	  replace_ssa_name_symbol (value, comp);
 	}
 
@@ -595,7 +640,7 @@ set_component_ssa_name (tree ssa_name, bool imag_p, tree value)
   /* Finally, we need to stabilize the result by installing the value into
      a new ssa name.  */
   else
-    comp = get_component_ssa_name (ssa_name, imag_p);
+    comp = get_component_ssa_name (ssa_name, part);
 
   /* Do all the work to assign VALUE to COMP.  */
   list = NULL;
@@ -612,13 +657,14 @@ set_component_ssa_name (tree ssa_name, bool imag_p, tree value)
    Emit any new code before gsi.  */
 
 static tree
-extract_component (gimple_stmt_iterator *gsi, tree t, bool imagpart_p,
+extract_component (gimple_stmt_iterator * gsi, tree t, complex_part_t part,
 		   bool gimple_p, bool phiarg_p = false)
 {
   switch (TREE_CODE (t))
     {
     case COMPLEX_CST:
-      return imagpart_p ? TREE_IMAGPART (t) : TREE_REALPART (t);
+      return (part == BOTH_P) ? t : (part == IMAG_P) ?
+	TREE_IMAGPART (t) : TREE_REALPART (t);
 
     case COMPLEX_EXPR:
       gcc_unreachable ();
@@ -629,7 +675,7 @@ extract_component (gimple_stmt_iterator *gsi, tree t, bool imagpart_p,
 	t = unshare_expr (t);
 	TREE_TYPE (t) = inner_type;
 	TREE_OPERAND (t, 1) = TYPE_SIZE (inner_type);
-	if (imagpart_p)
+	if (part == IMAG_P)
 	  TREE_OPERAND (t, 2) = size_binop (PLUS_EXPR, TREE_OPERAND (t, 2),
 					    TYPE_SIZE (inner_type));
 	if (gimple_p)
@@ -646,10 +692,11 @@ extract_component (gimple_stmt_iterator *gsi, tree t, bool imagpart_p,
     case VIEW_CONVERT_EXPR:
     case MEM_REF:
       {
-	tree inner_type = TREE_TYPE (TREE_TYPE (t));
-
-	t = build1 ((imagpart_p ? IMAGPART_EXPR : REALPART_EXPR),
-		    inner_type, unshare_expr (t));
+	if (part == BOTH_P)
+	  t = unshare_expr (t);
+	else
+	  t = build1 (((part == IMAG_P) ? IMAGPART_EXPR : REALPART_EXPR),
+		      (TREE_TYPE (TREE_TYPE (t))), unshare_expr (t));
 
 	if (gimple_p)
 	  t = force_gimple_operand_gsi (gsi, t, true, NULL, true,
@@ -659,10 +706,12 @@ extract_component (gimple_stmt_iterator *gsi, tree t, bool imagpart_p,
       }
 
     case SSA_NAME:
-      t = get_component_ssa_name (t, imagpart_p);
-      if (TREE_CODE (t) == SSA_NAME && SSA_NAME_DEF_STMT (t) == NULL)
-	gcc_assert (phiarg_p);
-      return t;
+      {
+	t = get_component_ssa_name (t, part);
+	if (TREE_CODE (t) == SSA_NAME && SSA_NAME_DEF_STMT (t) == NULL)
+	  gcc_assert (phiarg_p);
+	return t;
+      }
 
     default:
       gcc_unreachable ();
@@ -673,18 +722,29 @@ extract_component (gimple_stmt_iterator *gsi, tree t, bool imagpart_p,
 
 static void
 update_complex_components (gimple_stmt_iterator *gsi, gimple *stmt, tree r,
-			   tree i)
+			   tree i, tree b = NULL)
 {
   tree lhs;
   gimple_seq list;
 
+  gcc_assert (b || (r && i));
   lhs = gimple_get_lhs (stmt);
+  if (!b)
+    b = lhs;
+  if (!r)
+    r = build1 (REALPART_EXPR, TREE_TYPE (TREE_TYPE (b)), unshare_expr (b));
+  if (!i)
+    i = build1 (IMAGPART_EXPR, TREE_TYPE (TREE_TYPE (b)), unshare_expr (b));
+
+  list = set_component_ssa_name (lhs, REAL_P, r);
+  if (list)
+    gsi_insert_seq_after (gsi, list, GSI_CONTINUE_LINKING);
 
-  list = set_component_ssa_name (lhs, false, r);
+  list = set_component_ssa_name (lhs, IMAG_P, i);
   if (list)
     gsi_insert_seq_after (gsi, list, GSI_CONTINUE_LINKING);
 
-  list = set_component_ssa_name (lhs, true, i);
+  list = set_component_ssa_name (lhs, BOTH_P, b);
   if (list)
     gsi_insert_seq_after (gsi, list, GSI_CONTINUE_LINKING);
 }
@@ -694,11 +754,11 @@ update_complex_components_on_edge (edge e, tree lhs, tree r, tree i)
 {
   gimple_seq list;
 
-  list = set_component_ssa_name (lhs, false, r);
+  list = set_component_ssa_name (lhs, REAL_P, r);
   if (list)
     gsi_insert_seq_on_edge (e, list);
 
-  list = set_component_ssa_name (lhs, true, i);
+  list = set_component_ssa_name (lhs, IMAG_P, i);
   if (list)
     gsi_insert_seq_on_edge (e, list);
 }
@@ -707,19 +767,24 @@ update_complex_components_on_edge (edge e, tree lhs, tree r, tree i)
 /* Update an assignment to a complex variable in place.  */
 
 static void
-update_complex_assignment (gimple_stmt_iterator *gsi, tree r, tree i)
+update_complex_assignment (gimple_stmt_iterator * gsi, tree r, tree i,
+			   tree b = NULL)
 {
   gimple *old_stmt = gsi_stmt (*gsi);
-  gimple_assign_set_rhs_with_ops (gsi, COMPLEX_EXPR, r, i);
+  if (b == NULL)
+    gimple_assign_set_rhs_with_ops (gsi, COMPLEX_EXPR, r, i);
+  else
+    /* dummy assignment, but pr45569.C fails if removed */
+    gimple_assign_set_rhs_from_tree (gsi, b);
+
   gimple *stmt = gsi_stmt (*gsi);
   update_stmt (stmt);
   if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt))
     bitmap_set_bit (need_eh_cleanup, gimple_bb (stmt)->index);
 
-  update_complex_components (gsi, gsi_stmt (*gsi), r, i);
+  update_complex_components (gsi, gsi_stmt (*gsi), r, i, b);
 }
 
-
 /* Generate code at the entry point of the function to initialize the
    component variables for a complex parameter.  */
 
@@ -768,7 +833,8 @@ update_phi_components (basic_block bb)
 
 	  for (j = 0; j < 2; j++)
 	    {
-	      tree l = get_component_ssa_name (gimple_phi_result (phi), j > 0);
+	      tree l = get_component_ssa_name (gimple_phi_result (phi),
+					       (complex_part_t) j);
 	      if (TREE_CODE (l) == SSA_NAME)
 		p[j] = create_phi_node (l, bb);
 	    }
@@ -779,7 +845,9 @@ update_phi_components (basic_block bb)
 	      for (j = 0; j < 2; j++)
 		if (p[j])
 		  {
-		    comp = extract_component (NULL, arg, j > 0, false, true);
+		    comp =
+		      extract_component (NULL, arg, (complex_part_t) j, false,
+					 true);
 		    if (TREE_CODE (comp) == SSA_NAME
 			&& SSA_NAME_DEF_STMT (comp) == NULL)
 		      {
@@ -809,13 +877,14 @@ update_phi_components (basic_block bb)
     }
 }
 
+
 /* Expand a complex move to scalars.  */
 
 static void
 expand_complex_move (gimple_stmt_iterator *gsi, tree type)
 {
   tree inner_type = TREE_TYPE (type);
-  tree r, i, lhs, rhs;
+  tree r, i, b, lhs, rhs;
   gimple *stmt = gsi_stmt (*gsi);
 
   if (is_gimple_assign (stmt))
@@ -862,16 +931,13 @@ expand_complex_move (gimple_stmt_iterator *gsi, tree type)
       else
 	{
 	  if (gimple_assign_rhs_code (stmt) != COMPLEX_EXPR)
-	    {
-	      r = extract_component (gsi, rhs, 0, true);
-	      i = extract_component (gsi, rhs, 1, true);
-	    }
+	    update_complex_assignment (gsi, NULL, NULL,
+				       extract_component (gsi, rhs,
+							  BOTH_P, true));
 	  else
-	    {
-	      r = gimple_assign_rhs1 (stmt);
-	      i = gimple_assign_rhs2 (stmt);
-	    }
-	  update_complex_assignment (gsi, r, i);
+	    update_complex_assignment (gsi,
+				       gimple_assign_rhs1 (stmt),
+				       gimple_assign_rhs2 (stmt), NULL);
 	}
     }
   else if (rhs
@@ -883,24 +949,18 @@ expand_complex_move (gimple_stmt_iterator *gsi, tree type)
       location_t loc;
 
       loc = gimple_location (stmt);
-      r = extract_component (gsi, rhs, 0, false);
-      i = extract_component (gsi, rhs, 1, false);
-
-      x = build1 (REALPART_EXPR, inner_type, unshare_expr (lhs));
-      t = gimple_build_assign (x, r);
-      gimple_set_location (t, loc);
-      gsi_insert_before (gsi, t, GSI_SAME_STMT);
+      b = extract_component (gsi, rhs, BOTH_P, false);
 
       if (stmt == gsi_stmt (*gsi))
 	{
-	  x = build1 (IMAGPART_EXPR, inner_type, unshare_expr (lhs));
+	  x = unshare_expr (lhs);
 	  gimple_assign_set_lhs (stmt, x);
-	  gimple_assign_set_rhs1 (stmt, i);
+	  gimple_assign_set_rhs1 (stmt, b);
 	}
       else
 	{
-	  x = build1 (IMAGPART_EXPR, inner_type, unshare_expr (lhs));
-	  t = gimple_build_assign (x, i);
+	  x = unshare_expr (lhs);
+	  t = gimple_build_assign (x, b);
 	  gimple_set_location (t, loc);
 	  gsi_insert_before (gsi, t, GSI_SAME_STMT);
 
@@ -1641,26 +1701,88 @@ expand_complex_asm (gimple_stmt_iterator *gsi)
 		}
 	      /* Make sure to not ICE later, see PR105165.  */
 	      tree zero = build_zero_cst (TREE_TYPE (TREE_TYPE (op)));
-	      set_component_ssa_name (op, false, zero);
-	      set_component_ssa_name (op, true, zero);
+	      set_component_ssa_name (op, REAL_P, zero);
+	      set_component_ssa_name (op, IMAG_P, zero);
+	      set_component_ssa_name (op, BOTH_P, zero);
 	      continue;
 	    }
 	  tree type = TREE_TYPE (op);
 	  tree inner_type = TREE_TYPE (type);
 	  tree r = build1 (REALPART_EXPR, inner_type, op);
 	  tree i = build1 (IMAGPART_EXPR, inner_type, op);
-	  gimple_seq list = set_component_ssa_name (op, false, r);
+	  tree b = op;
+	  gimple_seq list = set_component_ssa_name (op, REAL_P, r);
 
 	  if (list)
 	    gsi_insert_seq_after (gsi, list, GSI_CONTINUE_LINKING);
 
-	  list = set_component_ssa_name (op, true, i);
+	  list = set_component_ssa_name (op, IMAG_P, i);
+	  if (list)
+	    gsi_insert_seq_after (gsi, list, GSI_CONTINUE_LINKING);
+
+	  list = set_component_ssa_name (op, BOTH_P, b);
 	  if (list)
 	    gsi_insert_seq_after (gsi, list, GSI_CONTINUE_LINKING);
 	}
     }
 }
 
+/* Returns true if a complex component is a constant */
+
+static bool
+complex_component_cst_p (tree cplx, complex_part_t part)
+{
+  switch (TREE_CODE (cplx))
+    {
+    case COMPLEX_CST:
+      return true;
+
+    case SSA_NAME:
+      {
+	size_t ssa_name_index = SSA_NAME_VERSION (cplx) * 3 + part;
+	tree val = complex_ssa_name_components[ssa_name_index];
+	return (val) ? CONSTANT_CLASS_P (val) : false;
+      }
+
+    default:
+      return false;
+    }
+}
+
+/* Returns true if the target support a particular complex operation natively */
+
+static bool
+target_native_complex_operation (enum tree_code code, tree type,
+				 tree inner_type, tree ac, tree bc,
+				 complex_lattice_t al, complex_lattice_t bl)
+{
+  /* Native complex instructions are currently only used when both operands are varying,
+     but a finer grain approach may be interesting */
+  if ((al != VARYING) || ((bl != VARYING) && (bl != UNINITIALIZED)))
+    return false;
+
+  /* do not use native operations when a part of the result is constant */
+  if ((bl == UNINITIALIZED)
+      && (complex_component_cst_p (ac, REAL_P)
+	  || complex_component_cst_p (ac, IMAG_P)))
+    return false;
+  else if ((bl != UNINITIALIZED)
+	   &&
+	   ((complex_component_cst_p (ac, REAL_P)
+	     && complex_component_cst_p (bc, REAL_P))
+	    || (complex_component_cst_p (ac, IMAG_P)
+		&& complex_component_cst_p (bc, IMAG_P))))
+    return false;
+
+  optab op = optab_for_tree_code (code, inner_type, optab_default);
+
+  /* no need to search if operation is not in the optab */
+  if (op == unknown_optab)
+    return false;
+
+  return optab_handler (op, TYPE_MODE (type)) != CODE_FOR_nothing;
+}
+
 /* Process one statement.  If we identify a complex operation, expand it.  */
 
 static void
@@ -1729,14 +1851,17 @@ expand_complex_operations_1 (gimple_stmt_iterator *gsi)
 		 && TREE_CODE (lhs) == SSA_NAME)
 	  {
 	    rhs = gimple_assign_rhs1 (stmt);
+	    enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
 	    rhs = extract_component (gsi, TREE_OPERAND (rhs, 0),
-		                     gimple_assign_rhs_code (stmt)
-				       == IMAGPART_EXPR,
-				     false);
+				     (rhs_code == IMAGPART_EXPR) ? IMAG_P
+				     : (rhs_code == REALPART_EXPR) ? REAL_P
+				     : BOTH_P, false);
 	    gimple_assign_set_rhs_from_tree (gsi, rhs);
 	    stmt = gsi_stmt (*gsi);
 	    update_stmt (stmt);
 	  }
+	else if (is_gimple_call (stmt))
+	  return;
       }
       return;
     }
@@ -1755,19 +1880,6 @@ expand_complex_operations_1 (gimple_stmt_iterator *gsi)
       bc = gimple_cond_rhs (stmt);
     }
 
-  ar = extract_component (gsi, ac, false, true);
-  ai = extract_component (gsi, ac, true, true);
-
-  if (ac == bc)
-    br = ar, bi = ai;
-  else if (bc)
-    {
-      br = extract_component (gsi, bc, 0, true);
-      bi = extract_component (gsi, bc, 1, true);
-    }
-  else
-    br = bi = NULL_TREE;
-
   al = find_lattice_value (ac);
   if (al == UNINITIALIZED)
     al = VARYING;
@@ -1783,44 +1895,142 @@ expand_complex_operations_1 (gimple_stmt_iterator *gsi)
 	bl = VARYING;
     }
 
-  switch (code)
+  if (target_native_complex_operation
+      (code, type, inner_type, ac, bc, al, bl))
     {
-    case PLUS_EXPR:
-    case MINUS_EXPR:
-      expand_complex_addition (gsi, inner_type, ar, ai, br, bi, code, al, bl);
-      break;
+      tree ab, bb, rb;
+      gimple_seq stmts = NULL;
+      location_t loc = gimple_location (gsi_stmt (*gsi));
+
+      ab = extract_component (gsi, ac, BOTH_P, true);
+      if (ac == bc)
+	bb = ab;
+      else if (bc)
+	{
+	  bb = extract_component (gsi, bc, BOTH_P, true);
+	}
+      else
+	bb = NULL_TREE;
 
-    case MULT_EXPR:
-      expand_complex_multiplication (gsi, type, ar, ai, br, bi, al, bl);
-      break;
+      switch (code)
+	{
+	case PLUS_EXPR:
+	case MINUS_EXPR:
+	case MULT_EXPR:
+	  rb = gimple_build (&stmts, loc, code, type, ab, bb);
+	  gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+	  update_complex_assignment (gsi, NULL, NULL, rb);
+	  break;
 
-    case TRUNC_DIV_EXPR:
-    case CEIL_DIV_EXPR:
-    case FLOOR_DIV_EXPR:
-    case ROUND_DIV_EXPR:
-    case RDIV_EXPR:
-      expand_complex_division (gsi, type, ar, ai, br, bi, code, al, bl);
-      break;
+	case NEGATE_EXPR:
+	case CONJ_EXPR:
+	  rb = gimple_build (&stmts, loc, code, type, ab);
+	  gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+	  update_complex_assignment (gsi, NULL, NULL, rb);
+	  break;
 
-    case NEGATE_EXPR:
-      expand_complex_negation (gsi, inner_type, ar, ai);
-      break;
+	case EQ_EXPR:
+	case NE_EXPR:
+	  /* FIXME */
+	  {
+	    gimple *stmt = gsi_stmt (*gsi);
+	    rb = gimple_build (&stmts, loc, code, type, ab, bb);
+	    switch (gimple_code (stmt))
+	      {
+	      case GIMPLE_RETURN:
+		{
+		  greturn *return_stmt = as_a < greturn * >(stmt);
+		  gimple_return_set_retval (return_stmt,
+					    fold_convert (type, rb));
+		}
+		break;
 
-    case CONJ_EXPR:
-      expand_complex_conjugate (gsi, inner_type, ar, ai);
-      break;
+	      case GIMPLE_ASSIGN:
+		update_complex_assignment (gsi, NULL, NULL, rb);
+		gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+		break;
 
-    case EQ_EXPR:
-    case NE_EXPR:
-      expand_complex_comparison (gsi, ar, ai, br, bi, code);
-      break;
+	      case GIMPLE_COND:
+		{
+		  gcond *cond_stmt = as_a < gcond * >(stmt);
+		  gimple_cond_set_code (cond_stmt, EQ_EXPR);
+		  gimple_cond_set_lhs (cond_stmt, rb);
+		  gimple_cond_set_rhs (cond_stmt, boolean_true_node);
+		}
+		break;
 
-    default:
-      gcc_unreachable ();
+	      default:
+		break;
+	      }
+	    break;
+	  }
+
+
+	  /* not supported yet */
+	case TRUNC_DIV_EXPR:
+	case CEIL_DIV_EXPR:
+	case FLOOR_DIV_EXPR:
+	case ROUND_DIV_EXPR:
+	case RDIV_EXPR:
+
+	default:
+	  gcc_unreachable ();
+	}
+      return;
     }
-}
 
+    ar = extract_component (gsi, ac, REAL_P, true);
+    ai = extract_component (gsi, ac, IMAG_P, true);
+
+    if (ac == bc)
+      br = ar, bi = ai;
+    else if (bc)
+      {
+	br = extract_component (gsi, bc, REAL_P, true);
+	bi = extract_component (gsi, bc, IMAG_P, true);
+      }
+    else
+	br = bi = NULL_TREE;
+
+    switch (code)
+      {
+      case PLUS_EXPR:
+      case MINUS_EXPR:
+	expand_complex_addition (gsi, inner_type, ar, ai, br, bi, code, al,
+				 bl);
+	break;
+
+      case MULT_EXPR:
+	expand_complex_multiplication (gsi, type, ar, ai, br, bi, al, bl);
+	break;
+
+      case TRUNC_DIV_EXPR:
+      case CEIL_DIV_EXPR:
+      case FLOOR_DIV_EXPR:
+      case ROUND_DIV_EXPR:
+      case RDIV_EXPR:
+	expand_complex_division (gsi, type, ar, ai, br, bi, code, al, bl);
+	break;
+
+      case NEGATE_EXPR:
+	expand_complex_negation (gsi, inner_type, ar, ai);
+	break;
+
+      case CONJ_EXPR:
+	expand_complex_conjugate (gsi, inner_type, ar, ai);
+	break;
+
+      case EQ_EXPR:
+      case NE_EXPR:
+	expand_complex_comparison (gsi, ar, ai, br, bi, code);
+	break;
+
+      default:
+	gcc_unreachable ();
+      }
+}
 
+
 /* Entry point for complex operation lowering during optimization.  */
 
 static unsigned int
@@ -1845,8 +2055,8 @@ tree_lower_complex (void)
 
   complex_variable_components = new int_tree_htab_type (10);
 
-  complex_ssa_name_components.create (2 * num_ssa_names);
-  complex_ssa_name_components.safe_grow_cleared (2 * num_ssa_names, true);
+  complex_ssa_name_components.create (3 * num_ssa_names);
+  complex_ssa_name_components.safe_grow_cleared (3 * num_ssa_names, true);
 
   update_parameter_components ();
 
@@ -1879,7 +2089,9 @@ tree_lower_complex (void)
 		      || is_gimple_min_invariant (op))
 		    continue;
 		  tree arg = gimple_phi_arg_def (phis_to_revisit[j], l);
-		  op = extract_component (NULL, arg, k > 0, false, false);
+		  op =
+		    extract_component (NULL, arg, (complex_part_t) k, false,
+				       false);
 		  SET_PHI_ARG_DEF (phi, l, op);
 		}
 	    }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 668808a29d0..da6daf99fc1 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1486,6 +1486,7 @@ struct GTY(()) tree_complex {
   struct tree_typed typed;
   tree real;
   tree imag;
+  tree both;
 };
 
 struct GTY(()) tree_vector {
diff --git a/gcc/tree-streamer-in.cc b/gcc/tree-streamer-in.cc
index 5bead0c3c6a..a1fa2cb9eea 100644
--- a/gcc/tree-streamer-in.cc
+++ b/gcc/tree-streamer-in.cc
@@ -695,6 +695,7 @@ lto_input_ts_complex_tree_pointers (class lto_input_block *ib,
 {
   TREE_REALPART (expr) = stream_read_tree_ref (ib, data_in);
   TREE_IMAGPART (expr) = stream_read_tree_ref (ib, data_in);
+  TREE_COMPLEX_BOTH_PARTS (expr) = stream_read_tree_ref (ib, data_in);
 }
 
 
diff --git a/gcc/tree-streamer-out.cc b/gcc/tree-streamer-out.cc
index ff9694e17dd..be7314ef748 100644
--- a/gcc/tree-streamer-out.cc
+++ b/gcc/tree-streamer-out.cc
@@ -592,6 +592,7 @@ write_ts_complex_tree_pointers (struct output_block *ob, tree expr)
 {
   stream_write_tree_ref (ob, TREE_REALPART (expr));
   stream_write_tree_ref (ob, TREE_IMAGPART (expr));
+  stream_write_tree_ref (ob, TREE_COMPLEX_BOTH_PARTS (expr));
 }
 
 
diff --git a/gcc/tree.cc b/gcc/tree.cc
index 420857b110c..2bc1b0d1e3f 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -2497,6 +2497,14 @@ build_complex (tree type, tree real, tree imag)
 
   tree t = make_node (COMPLEX_CST);
 
+  /* represent both parts as a constant vector */
+  tree vector_type = build_vector_type (TREE_TYPE (real), 2);
+  tree_vector_builder v (vector_type, 1, 2);
+  v.quick_push (real);
+  v.quick_push (imag);
+  tree both = v.build ();
+
+  TREE_COMPLEX_BOTH_PARTS (t) = both;
   TREE_REALPART (t) = real;
   TREE_IMAGPART (t) = imag;
   TREE_TYPE (t) = type ? type : build_complex_type (TREE_TYPE (real));
diff --git a/gcc/tree.h b/gcc/tree.h
index fa02e2907a1..28716b53120 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -634,6 +634,12 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
 
 /* Nonzero if TYPE represents a complex floating-point type.  */
 
+#define COMPLEX_INTEGER_TYPE_P(TYPE)	\
+  (TREE_CODE (TYPE) == COMPLEX_TYPE	\
+   && TREE_CODE (TREE_TYPE (TYPE)) == INTEGER_TYPE)
+
+/* Nonzero if TYPE represents a complex floating-point type.  */
+
 #define COMPLEX_FLOAT_TYPE_P(TYPE)	\
   (TREE_CODE (TYPE) == COMPLEX_TYPE	\
    && TREE_CODE (TREE_TYPE (TYPE)) == REAL_TYPE)
@@ -1155,6 +1161,7 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
 /* In a COMPLEX_CST node.  */
 #define TREE_REALPART(NODE) (COMPLEX_CST_CHECK (NODE)->complex.real)
 #define TREE_IMAGPART(NODE) (COMPLEX_CST_CHECK (NODE)->complex.imag)
+#define TREE_COMPLEX_BOTH_PARTS(NODE) (COMPLEX_CST_CHECK (NODE)->complex.both)
 
 /* In a VECTOR_CST node.  See generic.texi for details.  */
 #define VECTOR_CST_NELTS(NODE) (TYPE_VECTOR_SUBPARTS (TREE_TYPE (NODE)))
@@ -2214,6 +2221,8 @@ class auto_suppress_location_wrappers
   (as_a <scalar_int_mode> (TYPE_CHECK (NODE)->type_common.mode))
 #define SCALAR_FLOAT_TYPE_MODE(NODE) \
   (as_a <scalar_float_mode> (TYPE_CHECK (NODE)->type_common.mode))
+#define COMPLEX_TYPE_MODE(NODE) \
+  (as_a <complex_mode> (TYPE_CHECK (NODE)->type_common.mode))
 #define SET_TYPE_MODE(NODE, MODE) \
   (TYPE_CHECK (NODE)->type_common.mode = (MODE))
 
@@ -6646,7 +6655,11 @@ extern const builtin_structptr_type builtin_structptr_types[6];
 inline bool
 type_has_mode_precision_p (const_tree t)
 {
-  return known_eq (TYPE_PRECISION (t), GET_MODE_PRECISION (TYPE_MODE (t)));
+  if (TREE_CODE (t) == COMPLEX_TYPE)
+    return known_eq (2*TYPE_PRECISION (TREE_TYPE(t)),
+		     GET_MODE_PRECISION (TYPE_MODE (t)));
+  else
+    return known_eq (TYPE_PRECISION (t), GET_MODE_PRECISION (TYPE_MODE (t)));
 }
 
 /* Helper functions for fndecl_built_in_p.  */
