c: Speed up compilation of large char array initializers when not using #embed

Message ID Zq52E7aqs+8LbUiK@tucnak
State New
Headers
Series c: Speed up compilation of large char array initializers when not using #embed |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply

Commit Message

Jakub Jelinek Aug. 3, 2024, 6:25 p.m. UTC
  Hi!

The following patch on top of the #embed patchset
https://gcc.gnu.org/pipermail/gcc-patches/2024-June/655012.html                                                                                                                       
https://gcc.gnu.org/pipermail/gcc-patches/2024-June/655013.html                                                                                                                       
https://gcc.gnu.org/pipermail/gcc-patches/2024-July/657049.html                                                                                                                       
https://gcc.gnu.org/pipermail/gcc-patches/2024-July/657053.html                                                                                                                       
https://gcc.gnu.org/pipermail/gcc-patches/2024-July/658137.html                                                                                                                       
https://gcc.gnu.org/pipermail/gcc-patches/2024-August/659331.html
attempts to speed up compilation of large char array initializers when
one doesn't use #embed in the source.

My testcase has been
unsigned char a[] = {
#embed "cc1gm2" limit (100000000)
};
and corresponding variant which has the middle line replaced with
dd if=cc1gm bs=100000000 count=1 | xxd -i
With embed 95.3MiB is really fast:
time ./cc1 -quiet -O2 -o test4a.s test4a.c 

real    0m0.700s
user    0m0.576s
sys     0m0.123s
Without embed and without this patch it needs around 11GB of RAM and
time ./cc1 -quiet -O2 -o test4b.s test4b.c 

real    2m47.230s
user    2m41.548s
sys     0m4.328s
Without embed and with this patch it needs around 3.5GB of RAM and
time ./cc1 -quiet -O2 -o test4b.s2 test4b.c 

real    0m25.004s
user    0m23.655s
sys     0m1.308s
Not perfect (but one needs to parse all the numbers, libcpp also creates
strings which are pointed by CPP_NUMBER tokens (that can take up to 4 bytes
per byte), but still almost 7x speed improvement and 3x compile time memory.

One drawback of the patch is that for the larger initializers the precise
locations for -Wconversion warnings are gone when initializing signed char
(or char when it is signed) arrays.

If that is important, perhaps c_maybe_optimize_large_byte_initializer could
tell the caller this is the case and c_parser_initval could emit the
warnings directly when it still knows the location_t and suppress warnings
on the RAW_DATA_CST.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-08-03  Jakub Jelinek  <jakub@redhat.com>

	* c-tree.h (c_maybe_optimize_large_byte_initializer): Declare.
	* c-parser.cc (c_parser_initval): Attempt to optimize large char array
	initializers into RAW_DATA_CST.
	* c-typeck.cc (c_maybe_optimize_large_byte_initializer): New function.

	* c-c++-common/init-1.c: New test.


	Jakub
  

Patch

--- gcc/c/c-tree.h.jj	2024-08-01 18:44:24.626517239 +0200
+++ gcc/c/c-tree.h	2024-08-02 13:34:31.756532582 +0200
@@ -807,6 +807,7 @@  extern struct c_expr pop_init_level (loc
 				     location_t);
 extern void set_init_index (location_t, tree, tree, struct obstack *);
 extern void set_init_label (location_t, tree, location_t, struct obstack *);
+unsigned c_maybe_optimize_large_byte_initializer (void);
 extern void process_init_element (location_t, struct c_expr, bool,
 				  struct obstack *);
 extern tree build_compound_literal (location_t, tree, tree, bool,
--- gcc/c/c-parser.cc.jj	2024-08-02 11:34:03.816025686 +0200
+++ gcc/c/c-parser.cc	2024-08-02 16:45:22.376262609 +0200
@@ -6505,7 +6505,125 @@  c_parser_initval (c_parser *parser, stru
 					    (init.value))))
 	init = convert_lvalue_to_rvalue (loc, init, true, true, true);
     }
+  tree val = init.value;
   process_init_element (loc, init, false, braced_init_obstack);
+
+  /* Attempt to optimize large char array initializers into RAW_DATA_CST
+     to save compile time and memory even when not using #embed.  */
+  static unsigned vals_to_ignore;
+  if (vals_to_ignore)
+    /* If earlier call determined there is certain number of CPP_COMMA
+       CPP_NUMBER tokens with 0-255 int values, but not enough for
+       RAW_DATA_CST to be beneficial, don't try to check it again until
+       they are all parsed.  */
+    --vals_to_ignore;
+  else if (val
+	   && TREE_CODE (val) == INTEGER_CST
+	   && TREE_TYPE (val) == integer_type_node
+	   && c_parser_next_token_is (parser, CPP_COMMA))
+    if (unsigned int len = c_maybe_optimize_large_byte_initializer ())
+      {
+	char buf1[64];
+	unsigned int i;
+	gcc_checking_assert (len >= 64);
+	location_t last_loc = UNKNOWN_LOCATION;
+	for (i = 0; i < 64; ++i)
+	  {
+	    c_token *tok = c_parser_peek_nth_token_raw (parser, 1 + 2 * i);
+	    if (tok->type != CPP_COMMA)
+	      break;
+	    tok = c_parser_peek_nth_token_raw (parser, 2 + 2 * i);
+	    if (tok->type != CPP_NUMBER
+		|| TREE_CODE (tok->value) != INTEGER_CST
+		|| TREE_TYPE (tok->value) != integer_type_node
+		|| wi::neg_p (wi::to_wide (tok->value))
+		|| wi::to_widest (tok->value) > UCHAR_MAX)
+	      break;
+	    buf1[i] = (char) tree_to_uhwi (tok->value);
+	    if (i == 0)
+	      loc = tok->location;
+	    last_loc = tok->location;
+	  }
+	if (i < 64)
+	  {
+	    vals_to_ignore = i;
+	    return;
+	  }
+	c_token *tok = c_parser_peek_nth_token_raw (parser, 1 + 2 * i);
+	/* If 64 CPP_COMMA CPP_NUMBER pairs are followed by CPP_CLOSE_BRACE,
+	   punt if len is INT_MAX as that can mean this is a flexible array
+	   member and in that case we need one CPP_NUMBER afterwards
+	   (as guaranteed for CPP_EMBED).  */
+	if (tok->type == CPP_CLOSE_BRACE && len != INT_MAX)
+	  len = i;
+	else if (tok->type != CPP_COMMA)
+	  {
+	    vals_to_ignore = i;
+	    return;
+	  }
+	/* Ensure the STRING_CST fits into 128K.  */
+	unsigned int max_len = 131072 - offsetof (struct tree_string, str) - 1;
+	unsigned int orig_len = len;
+	unsigned int off = 0, last = 0;
+	if (!wi::neg_p (wi::to_wide (val)) && wi::to_widest (val) <= UCHAR_MAX)
+	  off = 1;
+	len = MIN (len, max_len - off);
+	char *buf2 = XNEWVEC (char, len + off);
+	if (off)
+	  buf2[0] = (char) tree_to_uhwi (val);
+	memcpy (buf2 + off, buf1, i);
+	for (unsigned int j = 0; j < i; ++j)
+	  {
+	    c_parser_peek_token (parser);
+	    c_parser_consume_token (parser);
+	    c_parser_peek_token (parser);
+	    c_parser_consume_token (parser);
+	  }
+	for (; i < len; ++i)
+	  {
+	    if (!c_parser_next_token_is (parser, CPP_COMMA))
+	      break;
+	    tok = c_parser_peek_2nd_token (parser);
+	    if (tok->type != CPP_NUMBER
+		|| TREE_CODE (tok->value) != INTEGER_CST
+		|| TREE_TYPE (tok->value) != integer_type_node
+		|| wi::neg_p (wi::to_wide (tok->value))
+		|| wi::to_widest (tok->value) > UCHAR_MAX)
+	      break;
+	    c_token *tok2 = c_parser_peek_nth_token (parser, 3);
+	    if (tok2->type != CPP_COMMA && tok2->type != CPP_CLOSE_BRACE)
+	      break;
+	    buf2[i + off] = (char) tree_to_uhwi (tok->value);
+	    /* If orig_len is INT_MAX, this can be flexible array member and
+	       in that case we need to ensure another element which
+	       for CPP_EMBED is normally guaranteed after it.  Include
+	       that byte in the RAW_DATA_OWNER though, so it can be optimized
+	       later.  */
+	    if (tok2->type == CPP_CLOSE_BRACE && orig_len == INT_MAX)
+	      {
+		last = 1;
+		break;
+	      }
+	    last_loc = tok->location;
+	    c_parser_consume_token (parser);
+	    c_parser_consume_token (parser);
+	  }
+	val = make_node (RAW_DATA_CST);
+	TREE_TYPE (val) = integer_type_node;
+	RAW_DATA_LENGTH (val) = i;
+	tree owner = build_string (i + off + last, buf2);
+	XDELETEVEC (buf2);
+	TREE_TYPE (owner) = build_array_type_nelts (unsigned_char_type_node,
+						    i + off + last);
+	RAW_DATA_OWNER (val) = owner;
+	RAW_DATA_POINTER (val) = TREE_STRING_POINTER (owner) + off;
+	init.value = val;
+	set_c_expr_source_range (&init, loc, last_loc);
+	init.original_code = RAW_DATA_CST;
+	init.original_type = integer_type_node;
+	init.m_decimal = 0;
+	process_init_element (loc, init, false, braced_init_obstack);
+      }
 }
 
 /* Parse a compound statement (possibly a function body) (C90 6.6.2,
--- gcc/c/c-typeck.cc.jj	2024-08-02 11:34:03.831025494 +0200
+++ gcc/c/c-typeck.cc	2024-08-02 16:42:08.410818121 +0200
@@ -11329,6 +11329,42 @@  maybe_split_raw_data (tree value, tree *
   return value;
 }
 
+/* Return non-zero if c_parser_initval should attempt to optimize
+   large initializers into RAW_DATA_CST.  In that case return how
+   many elements to optimize at most.  */
+
+unsigned
+c_maybe_optimize_large_byte_initializer (void)
+{
+  if (!constructor_type
+      || TREE_CODE (constructor_type) != ARRAY_TYPE
+      || constructor_stack->implicit)
+    return 0;
+  tree elttype = TYPE_MAIN_VARIANT (TREE_TYPE (constructor_type));
+  if (TREE_CODE (elttype) != INTEGER_TYPE
+      && TREE_CODE (elttype) != BITINT_TYPE)
+    return 0;
+  if (TYPE_PRECISION (elttype) != CHAR_BIT
+      || constructor_stack->replacement_value.value
+      || (COMPLETE_TYPE_P (constructor_type)
+	  && !poly_int_tree_p (TYPE_SIZE (constructor_type)))
+      || constructor_range_stack)
+    return 0;
+  if (constructor_max_index == NULL_TREE)
+    return INT_MAX;
+  if (tree_int_cst_le (constructor_max_index, constructor_index)
+      || integer_all_onesp (constructor_max_index))
+    return 0;
+  widest_int w = wi::to_widest (constructor_max_index);
+  w -= wi::to_widest (constructor_index);
+  w += 1;
+  if (w < 64)
+    return 0;
+  if (w > INT_MAX)
+    return INT_MAX;
+  return w.to_uhwi ();
+}
+
 /* Add one non-braced element to the current constructor level.
    This adjusts the current position within the constructor's type.
    This may also start or terminate implicit levels
--- gcc/testsuite/c-c++-common/init-1.c.jj	2024-08-03 11:23:49.771374323 +0200
+++ gcc/testsuite/c-c++-common/init-1.c	2024-08-03 11:29:40.557759375 +0200
@@ -0,0 +1,218 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+unsigned char a[] = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23 + 1 };
+unsigned char b[] = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1 };
+unsigned char c[] = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2 };
+unsigned char d[] = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3 };
+unsigned char e[] = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3 };
+struct A { unsigned char a[64]; int b; } f = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23 + 1,
+  221 };
+struct A g = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  221 };
+struct A h = {
+{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23 },
+  221 };
+struct B { unsigned char a[65]; int b; } k = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2 };
+struct C { unsigned char a[66]; int b; } l = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3 };
+struct D { unsigned char a[67]; int b; } m = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4 };
+struct E { unsigned char a[227]; int b; } n = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4 };
+unsigned char o[] = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, (unsigned char) -3, 4 };
+unsigned char p[] = {
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 230, 15,
+  229, 16, 228, 17, 227, 18, 226, 19, 225, 20, 224, 21, 223, 22, 222, 23,
+  1, 2, 3 + 1, 5 };
+
+int
+main ()
+{
+  if (sizeof (a) != 64
+      || sizeof (b) != 65
+      || sizeof (c) != 66
+      || sizeof (d) != 67
+      || sizeof (e) != 227
+      || sizeof (o) != 228
+      || sizeof (p) != 228)
+    __builtin_abort ();
+  for (int i = 0; i < 32; ++i)
+    {
+      int j;
+      if (i < 14)
+	j = i + 1;
+      else if (i & 1)
+	j = 15 + (i - 15) / 2;
+      else
+	j = 230 - (i - 14) / 2;
+      if (a[i] != j
+	  || (i != 31 && a[i + 32] != j)
+	  || b[i] != j
+	  || b[i + 32] != j
+	  || c[i] != j
+	  || c[i + 32] != j
+	  || d[i] != j
+	  || d[i + 32] != j
+	  || e[i] != j
+	  || e[i + 32] != j
+	  || e[i + 64] != j
+	  || e[i + 96] != j
+	  || e[i + 128] != j
+	  || e[i + 160] != j
+	  || e[i + 192] != j
+	  || f.a[i] != j
+	  || (i != 31 && f.a[i + 32] != j)
+	  || g.a[i] != j
+	  || g.a[i + 32] != j
+	  || h.a[i] != j
+	  || h.a[i + 32] != j
+	  || k.a[i] != j
+	  || k.a[i + 32] != j
+	  || l.a[i] != j
+	  || l.a[i + 32] != j
+	  || m.a[i] != j
+	  || m.a[i + 32] != j
+	  || n.a[i] != j
+	  || n.a[i + 32] != j
+	  || n.a[i + 64] != j
+	  || n.a[i + 96] != j
+	  || n.a[i + 128] != j
+	  || n.a[i + 160] != j
+	  || n.a[i + 192] != j)
+	__builtin_abort ();
+    }
+  if (a[63] != 24
+      || b[64] != 1
+      || c[64] != 1
+      || c[65] != 2
+      || d[64] != 1
+      || d[65] != 2
+      || d[66] != 3
+      || e[224] != 1
+      || e[225] != 2
+      || e[226] != 3
+      || f.a[63] != 24
+      || f.b != 221
+      || g.b != 221
+      || h.b != 221
+      || k.a[64] != 1
+      || k.b != 2
+      || l.a[64] != 1
+      || l.a[65] != 2
+      || l.b != 3
+      || m.a[64] != 1
+      || m.a[65] != 2
+      || m.a[66] != 3
+      || m.b != 4
+      || n.a[224] != 1
+      || n.a[225] != 2
+      || n.a[226] != 3
+      || n.b != 4
+      || __builtin_memcmp (e, o, 226)
+      || o[226] != (unsigned char) -3
+      || o[227] != 4
+      || __builtin_memcmp (e, p, 226)
+      || p[226] != 4
+      || p[227] != 5)
+    __builtin_abort ();
+}