diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 811ea31..3490874 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,14 @@
+2011-12-10  Richard Henderson  <rth@redhat.com>
+
+	* genmodes.c (struct mode_data): Remove wider_2x member.
+	(blank_mode): Adjust initializer.
+	(calc_wider_mode): Use XALLOCAVEC.
+	(emit_move_wider): Select double-width same-element vectors for
+	2xwider vectors.
+	* machmode.h (GET_MODE_2XWIDER_MODE): Update documentation.
+	* config/i386/i386.c (doublesize_vector_mode): Remove.
+	(expand_vselect_vconcat): Use GET_MODE_2XWIDER_MODE instead.
+
 2011-12-10  Joern Rennecke  <joern.rennecke@embecosm.com>
 
 	* config/epiphany/epiphany.h (USE_LOAD_POST_INCREMENT): Define.
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 15c6c37..216ab0b 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -35306,40 +35306,6 @@ ix86_builtin_vectorization_cost (enum vect_cost_for_stmt type_of_cost,
     }
 }
 
-
-/* Return a vector mode with twice as many elements as VMODE.  */
-/* ??? Consider moving this to a table generated by genmodes.c.  */
-
-static enum machine_mode
-doublesize_vector_mode (enum machine_mode vmode)
-{
-  switch (vmode)
-    {
-    case V2SFmode:	return V4SFmode;
-    case V1DImode:	return V2DImode;
-    case V2SImode:	return V4SImode;
-    case V4HImode:	return V8HImode;
-    case V8QImode:	return V16QImode;
-
-    case V2DFmode:	return V4DFmode;
-    case V4SFmode:	return V8SFmode;
-    case V2DImode:	return V4DImode;
-    case V4SImode:	return V8SImode;
-    case V8HImode:	return V16HImode;
-    case V16QImode:	return V32QImode;
-
-    case V4DFmode:	return V8DFmode;
-    case V8SFmode:	return V16SFmode;
-    case V4DImode:	return V8DImode;
-    case V8SImode:	return V16SImode;
-    case V16HImode:	return V32HImode;
-    case V32QImode:	return V64QImode;
-
-    default:
-      gcc_unreachable ();
-    }
-}
-
 /* Construct (set target (vec_select op0 (parallel perm))) and
    return true if that's a valid instruction in the active ISA.  */
 
@@ -35374,7 +35340,7 @@ expand_vselect_vconcat (rtx target, rtx op0, rtx op1,
   enum machine_mode v2mode;
   rtx x;
 
-  v2mode = doublesize_vector_mode (GET_MODE (op0));
+  v2mode = GET_MODE_2XWIDER_MODE (GET_MODE (op0));
   x = gen_rtx_VEC_CONCAT (v2mode, op0, op1);
   return expand_vselect (target, x, perm, nelt);
 }
diff --git a/gcc/genmodes.c b/gcc/genmodes.c
index dae7e38..8b6f5bc 100644
--- a/gcc/genmodes.c
+++ b/gcc/genmodes.c
@@ -63,7 +63,6 @@ struct mode_data
 
   struct mode_data *component;	/* mode of components */
   struct mode_data *wider;	/* next wider mode */
-  struct mode_data *wider_2x;	/* 2x wider mode */
 
   struct mode_data *contained;  /* Pointer to list of modes that have
 				   this mode as a component.  */
@@ -83,7 +82,7 @@ static struct mode_data *void_mode;
 static const struct mode_data blank_mode = {
   0, "<unknown>", MAX_MODE_CLASS,
   -1U, -1U, -1U, -1U,
-  0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0,
   "<unknown>", 0, 0, 0, 0
 };
 
@@ -790,7 +789,7 @@ calc_wider_mode (void)
 
   /* Allocate max_n_modes + 1 entries to leave room for the extra null
      pointer assigned after the qsort call below.  */
-  sortbuf = (struct mode_data **) alloca ((max_n_modes + 1) * sizeof (struct mode_data *));
+  sortbuf = XALLOCAVEC (struct mode_data *, max_n_modes + 1);
 
   for (c = 0; c < MAX_MODE_CLASS; c++)
     {
@@ -804,7 +803,6 @@ calc_wider_mode (void)
 	  for (prev = 0, m = modes[c]; m; m = next)
 	    {
 	      m->wider = void_mode;
-	      m->wider_2x = void_mode;
 
 	      /* this is nreverse */
 	      next = m->next;
@@ -827,7 +825,6 @@ calc_wider_mode (void)
 	  for (j = 0; j < i; j++)
 	    sortbuf[j]->next = sortbuf[j]->wider = sortbuf[j + 1];
 
-
 	  modes[c] = sortbuf[0];
 	}
     }
@@ -1062,6 +1059,21 @@ emit_mode_wider (void)
 		continue;
 	    }
 
+	  /* For vectors we want twice the number of components,
+	     with the same element type.  */
+	  if (m->cl == MODE_VECTOR_INT
+	      || m->cl == MODE_VECTOR_FLOAT
+	      || m->cl == MODE_VECTOR_FRACT
+	      || m->cl == MODE_VECTOR_UFRACT
+	      || m->cl == MODE_VECTOR_ACCUM
+	      || m->cl == MODE_VECTOR_UACCUM)
+	    {
+	      if (m2->ncomponents != 2 * m->ncomponents)
+		continue;
+	      if (m->component != m2->component)
+		continue;
+	    }
+
 	  break;
 	}
       if (m2 == void_mode)
diff --git a/gcc/machmode.h b/gcc/machmode.h
index b965d0f..4a3f6f5 100644
--- a/gcc/machmode.h
+++ b/gcc/machmode.h
@@ -221,6 +221,8 @@ extern const unsigned char mode_nunits[NUM_MACHINE_MODES];
 extern const unsigned char mode_wider[NUM_MACHINE_MODES];
 #define GET_MODE_WIDER_MODE(MODE) ((enum machine_mode) mode_wider[MODE])
 
+/* For scalars, this is a mode with twice the precision.  For vectors,
+   this is a mode with the same inner mode but with twice the elements.  */
 extern const unsigned char mode_2xwider[NUM_MACHINE_MODES];
 #define GET_MODE_2XWIDER_MODE(MODE) ((enum machine_mode) mode_2xwider[MODE])
 
