Index: gcc/machmode.h
===================================================================
--- gcc/machmode.h	2017-10-25 16:50:35.628184659 +0100
+++ gcc/machmode.h	2017-10-25 16:55:00.469175980 +0100
@@ -23,9 +23,9 @@ #define HAVE_MACHINE_MODES
 typedef opt_mode<machine_mode> opt_machine_mode;
 
 extern CONST_MODE_SIZE poly_uint16_pod mode_size[NUM_MACHINE_MODES];
-extern const poly_uint16_pod mode_precision[NUM_MACHINE_MODES];
+extern CONST_MODE_PRECISION poly_uint16_pod mode_precision[NUM_MACHINE_MODES];
 extern const unsigned char mode_inner[NUM_MACHINE_MODES];
-extern const poly_uint16_pod mode_nunits[NUM_MACHINE_MODES];
+extern CONST_MODE_NUNITS poly_uint16_pod mode_nunits[NUM_MACHINE_MODES];
 extern CONST_MODE_UNIT_SIZE unsigned char mode_unit_size[NUM_MACHINE_MODES];
 extern const unsigned short mode_unit_precision[NUM_MACHINE_MODES];
 extern const unsigned char mode_wider[NUM_MACHINE_MODES];
Index: gcc/machmode.def
===================================================================
--- gcc/machmode.def	2017-10-25 16:50:35.628184659 +0100
+++ gcc/machmode.def	2017-10-25 16:55:00.468176022 +0100
@@ -169,6 +169,12 @@ along with GCC; see the file COPYING3.
 	Unlike a FORMAT argument, if you are adjusting a float format
 	you must put an & in front of the name of each format structure.
 
+     ADJUST_NUNITS (MODE, EXPR);
+	Like the above, but set the number of nunits of MODE to EXPR.
+	This changes the size and precision of the mode in proportion
+	to the change in the number of units; for example, doubling
+	the number of units doubles the size and precision as well.
+
    Note: If a mode is ever made which is more than 255 bytes wide,
    machmode.h and genmodes.c will have to be changed to allocate
    more space for the mode_size and mode_alignment arrays.  */
Index: gcc/genmodes.c
===================================================================
--- gcc/genmodes.c	2017-10-25 16:50:35.627184698 +0100
+++ gcc/genmodes.c	2017-10-25 16:55:00.468176022 +0100
@@ -72,7 +72,9 @@ struct mode_data
   unsigned int counter;		/* Rank ordering of modes */
   unsigned int ibit;		/* the number of integral bits */
   unsigned int fbit;		/* the number of fractional bits */
-  bool need_bytesize_adj;	/* true if this mode need dynamic size
+  bool need_nunits_adj;		/* true if this mode needs dynamic nunits
+				   adjustment */
+  bool need_bytesize_adj;	/* true if this mode needs dynamic size
 				   adjustment */
   unsigned int int_n;		/* If nonzero, then __int<INT_N> will be defined */
 };
@@ -85,7 +87,7 @@ static const struct mode_data blank_mode
   0, "<unknown>", MAX_MODE_CLASS,
   -1U, -1U, -1U, -1U,
   0, 0, 0, 0, 0, 0,
-  "<unknown>", 0, 0, 0, 0, false, 0
+  "<unknown>", 0, 0, 0, 0, false, false, 0
 };
 
 static htab_t modes_by_name;
@@ -103,6 +105,7 @@ struct mode_adjust
   unsigned int line;
 };
 
+static struct mode_adjust *adj_nunits;
 static struct mode_adjust *adj_bytesize;
 static struct mode_adjust *adj_alignment;
 static struct mode_adjust *adj_format;
@@ -786,6 +789,7 @@ make_vector_mode (enum mode_class bclass
 #define _ADD_ADJUST(A, M, X, C1, C2) \
   new_adjust (#M, &adj_##A, #A, #X, MODE_##C1, MODE_##C2, __FILE__, __LINE__)
 
+#define ADJUST_NUNITS(M, X)    _ADD_ADJUST (nunits, M, X, RANDOM, RANDOM)
 #define ADJUST_BYTESIZE(M, X)  _ADD_ADJUST (bytesize, M, X, RANDOM, RANDOM)
 #define ADJUST_ALIGNMENT(M, X) _ADD_ADJUST (alignment, M, X, RANDOM, RANDOM)
 #define ADJUST_FLOAT_FORMAT(M, X)    _ADD_ADJUST (format, M, X, FLOAT, FLOAT)
@@ -955,9 +959,9 @@ #define tagged_printf(FMT, ARG, TAG) do
 #define print_decl(TYPE, NAME, ASIZE) \
   puts ("\nconst " TYPE " " NAME "[" ASIZE "] =\n{");
 
-#define print_maybe_const_decl(TYPE, NAME, ASIZE, CATEGORY)	\
+#define print_maybe_const_decl(TYPE, NAME, ASIZE, NEEDS_ADJ)	\
   printf ("\n" TYPE " " NAME "[" ASIZE "] = \n{\n",		\
-	  adj_##CATEGORY ? "" : "const ")
+	  NEEDS_ADJ ? "" : "const ")
 
 #define print_closer() puts ("};")
 
@@ -1015,6 +1019,11 @@ emit_mode_size_inline (void)
 	m->need_bytesize_adj = true;
     }
 
+  /* Changing the number of units by a factor of X also changes the size
+     by a factor of X.  */
+  for (mode_adjust *a = adj_nunits; a; a = a->next)
+    a->mode->need_bytesize_adj = true;
+
   printf ("\
 #ifdef __cplusplus\n\
 inline __attribute__((__always_inline__))\n\
@@ -1027,7 +1036,7 @@ mode_size_inline (machine_mode mode)\n\
   extern %spoly_uint16_pod mode_size[NUM_MACHINE_MODES];\n\
   gcc_assert (mode >= 0 && mode < NUM_MACHINE_MODES);\n\
   switch (mode)\n\
-    {\n", adj_bytesize ? "" : "const ");
+    {\n", adj_nunits || adj_bytesize ? "" : "const ");
 
   for_all_modes (c, m)
     if (!m->need_bytesize_adj)
@@ -1046,7 +1055,10 @@ emit_mode_nunits_inline (void)
   int c;
   struct mode_data *m;
 
-  puts ("\
+  for (mode_adjust *a = adj_nunits; a; a = a->next)
+    a->mode->need_nunits_adj = true;
+
+  printf ("\
 #ifdef __cplusplus\n\
 inline __attribute__((__always_inline__))\n\
 #else\n\
@@ -1055,12 +1067,13 @@ extern __inline__ __attribute__((__alway
 poly_uint16\n\
 mode_nunits_inline (machine_mode mode)\n\
 {\n\
-  extern poly_uint16_pod mode_nunits[NUM_MACHINE_MODES];\n\
+  extern %spoly_uint16_pod mode_nunits[NUM_MACHINE_MODES];\n\
   switch (mode)\n\
-    {");
+    {\n", adj_nunits ? "" : "const ");
 
   for_all_modes (c, m)
-    printf ("    case E_%smode: return %u;\n", m->name, m->ncomponents);
+    if (!m->need_nunits_adj)
+      printf ("    case E_%smode: return %u;\n", m->name, m->ncomponents);
 
   puts ("\
     default: return mode_nunits[mode];\n\
@@ -1277,7 +1290,10 @@ enum machine_mode\n{");
 };\n");
 
   /* I can't think of a better idea, can you?  */
-  printf ("#define CONST_MODE_SIZE%s\n", adj_bytesize ? "" : " const");
+  printf ("#define CONST_MODE_NUNITS%s\n", adj_nunits ? "" : " const");
+  printf ("#define CONST_MODE_PRECISION%s\n", adj_nunits ? "" : " const");
+  printf ("#define CONST_MODE_SIZE%s\n",
+	  adj_bytesize || adj_nunits ? "" : " const");
   printf ("#define CONST_MODE_UNIT_SIZE%s\n", adj_bytesize ? "" : " const");
   printf ("#define CONST_MODE_BASE_ALIGN%s\n", adj_alignment ? "" : " const");
 #if 0 /* disabled for backward compatibility, temporary */
@@ -1392,7 +1408,8 @@ emit_mode_precision (void)
   int c;
   struct mode_data *m;
 
-  print_decl ("poly_uint16_pod", "mode_precision", "NUM_MACHINE_MODES");
+  print_maybe_const_decl ("%spoly_uint16_pod", "mode_precision",
+			  "NUM_MACHINE_MODES", adj_nunits);
 
   for_all_modes (c, m)
     if (m->precision != (unsigned int)-1)
@@ -1411,7 +1428,7 @@ emit_mode_size (void)
   struct mode_data *m;
 
   print_maybe_const_decl ("%spoly_uint16_pod", "mode_size",
-			  "NUM_MACHINE_MODES", bytesize);
+			  "NUM_MACHINE_MODES", adj_nunits || adj_bytesize);
 
   for_all_modes (c, m)
     tagged_printf ("{ %u" ZERO_COEFFS " }", m->bytesize, m->name);
@@ -1425,7 +1442,8 @@ emit_mode_nunits (void)
   int c;
   struct mode_data *m;
 
-  print_decl ("poly_uint16_pod", "mode_nunits", "NUM_MACHINE_MODES");
+  print_maybe_const_decl ("%spoly_uint16_pod", "mode_nunits",
+			  "NUM_MACHINE_MODES", adj_nunits);
 
   for_all_modes (c, m)
     tagged_printf ("{ %u" ZERO_COEFFS " }", m->ncomponents, m->name);
@@ -1563,7 +1581,7 @@ emit_mode_unit_size (void)
   struct mode_data *m;
 
   print_maybe_const_decl ("%sunsigned char", "mode_unit_size",
-			  "NUM_MACHINE_MODES", bytesize);
+			  "NUM_MACHINE_MODES", adj_bytesize);
 
   for_all_modes (c, m)
     tagged_printf ("%u",
@@ -1604,7 +1622,7 @@ emit_mode_base_align (void)
 
   print_maybe_const_decl ("%sunsigned short",
 			  "mode_base_align", "NUM_MACHINE_MODES",
-			  alignment);
+			  adj_alignment);
 
   for_all_modes (c, m)
     tagged_printf ("%u", m->alignment, m->name);
@@ -1685,6 +1703,23 @@ emit_mode_adjustments (void)
 \n  poly_uint16 ps ATTRIBUTE_UNUSED;\n\
   size_t s ATTRIBUTE_UNUSED;");
 
+  for (a = adj_nunits; a; a = a->next)
+    {
+      m = a->mode;
+      printf ("\n"
+	      "  {\n"
+	      "    /* %s:%d */\n  ps = %s;\n",
+	      a->file, a->line, a->adjustment);
+      printf ("    int old_factor = vector_element_size"
+	      " (mode_precision[E_%smode], mode_nunits[E_%smode]);\n",
+	      m->name, m->name);
+      printf ("    mode_precision[E_%smode] = ps * old_factor;\n",  m->name);
+      printf ("    mode_size[E_%smode] = exact_div (mode_precision[E_%smode],"
+	      " BITS_PER_UNIT);\n", m->name, m->name);
+      printf ("    mode_nunits[E_%smode] = ps;\n", m->name);
+      printf ("  }\n");
+    }
+
   /* Size adjustments must be propagated to all containing modes.
      A size adjustment forces us to recalculate the alignment too.  */
   for (a = adj_bytesize; a; a = a->next)
@@ -1825,7 +1860,7 @@ emit_mode_ibit (void)
 
   print_maybe_const_decl ("%sunsigned char",
 			  "mode_ibit", "NUM_MACHINE_MODES",
-			  ibit);
+			  adj_ibit);
 
   for_all_modes (c, m)
     tagged_printf ("%u", m->ibit, m->name);
@@ -1843,7 +1878,7 @@ emit_mode_fbit (void)
 
   print_maybe_const_decl ("%sunsigned char",
 			  "mode_fbit", "NUM_MACHINE_MODES",
-			  fbit);
+			  adj_fbit);
 
   for_all_modes (c, m)
     tagged_printf ("%u", m->fbit, m->name);
