This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH, stage1][PR65443] Add transform_to_exit_first_loop_alt


Hi,

this patch fixes PR65443, a todo in the parloops pass for function transform_to_exit_first_loop:
...
   TODO: the common case is that latch of the loop is empty and immediately
   follows the loop exit.  In this case, it would be better not to copy the
   body of the loop, but only move the entry of the loop directly before the
   exit check and increase the number of iterations of the loop by one.
   This may need some additional preconditioning in case NIT = ~0.
...

The current implementation of transform_to_exit_first_loop transforms the loop by moving and duplicating the loop body. This patch transforms the loop into the same normal form, but without the duplication, if that's possible (determined by try_transform_to_exit_first_loop_alt).

The actual transformation, done by transform_to_exit_first_loop_alt transforms this loop:
...
     bb preheader:
     ...
     goto <bb header>

     <bb header>:
     ...
     if (ivtmp < n)
       goto <bb latch>;
     else
       goto <bb exit>;

     <bb latch>:
     ivtmp = ivtmp + inc;
     goto <bb header>
...

into this one:
...
     bb preheader:
     ...
     goto <bb newheader>

     <bb header>:
     ...
     goto <bb latch>;

     <bb newheader>:
     if (ivtmp < n + 1)
       goto <bb header>;
     else
       goto <bb exit>;

     <bb latch>:
     ivtmp = ivtmp + inc;
     goto <bb newheader>
...

Bootstrapped and reg-tested on x86_64.

OK for stage1 trunk?

Thanks,
- Tom
Add transform_to_exit_first_loop_alt

2015-03-27  Tom de Vries  <tom@codesourcery.com>

	PR tree-optimization/65443
	* tree-parloops.c (replace_imm_uses, replace_uses_in_bb_by)
	(replace_uses_in_bbs_by, transform_to_exit_first_loop_alt)
	(try_transform_to_exit_first_loop_alt): New function.
	(transform_to_exit_first_loop): Use
	try_transform_to_exit_first_loop_alt.

	* gcc.dg/parloops-exit-first-loop-alt-2.c: New test.
	* gcc.dg/parloops-exit-first-loop-alt-3.c: New test.
	* gcc.dg/parloops-exit-first-loop-alt.c: New test.

	* testsuite/libgomp.c/parloops-exit-first-loop-alt-2.c: New test.
	* testsuite/libgomp.c/parloops-exit-first-loop-alt-3.c: New test.
	* testsuite/libgomp.c/parloops-exit-first-loop-alt.c: New test.
---
 .../gcc.dg/parloops-exit-first-loop-alt-2.c        |  27 ++
 .../gcc.dg/parloops-exit-first-loop-alt-3.c        |  26 ++
 .../gcc.dg/parloops-exit-first-loop-alt.c          |  27 ++
 gcc/tree-parloops.c                                | 344 ++++++++++++++++++++-
 .../libgomp.c/parloops-exit-first-loop-alt-2.c     |  48 +++
 .../libgomp.c/parloops-exit-first-loop-alt-3.c     |  29 ++
 .../libgomp.c/parloops-exit-first-loop-alt.c       |  48 +++
 7 files changed, 538 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt-2.c
 create mode 100644 gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt-3.c
 create mode 100644 gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt.c
 create mode 100644 libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt-2.c
 create mode 100644 libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt-3.c
 create mode 100644 libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt.c

diff --git a/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt-2.c b/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt-2.c
new file mode 100644
index 0000000..a4d6cb2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt-2.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target pthread } */
+/* { dg-options "-O2 -ftree-parallelize-loops=2 -fdump-tree-parloops" } */
+
+#define N 1000
+
+unsigned int a[N];
+unsigned int b[N];
+unsigned int c[N];
+
+void __attribute__((noclone,noinline))
+f (unsigned int n)
+{
+  int i;
+
+    for (i = 0; i < N; ++i)
+      c[i] = a[i] + b[i];
+}
+
+/* Three times three array accesses:
+   - three in f._loopfn.0
+   - three in the parallel
+   - three in the low iteration count loop
+   Crucially, none for a peeled off last iteration following the parallel.  */
+/* { dg-final { scan-tree-dump-times "(?n)\\\[i" 9 "parloops" } } */
+
+/* { dg-final { cleanup-tree-dump "parloops" } } */
diff --git a/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt-3.c b/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt-3.c
new file mode 100644
index 0000000..c7ab51f88
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt-3.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target pthread } */
+/* { dg-options "-O2 -ftree-parallelize-loops=2 -fdump-tree-parloops" } */
+
+unsigned int *a;
+
+unsigned int __attribute__((noclone,noinline))
+f (unsigned int n)
+{
+  int i;
+  unsigned int sum = 1;
+
+  for (i = 0; i < n; ++i)
+    sum += a[i];
+
+  return sum;
+}
+
+/* Three array accesses:
+   - one in f._loopfn.0
+   - one in the parallel
+   - one in the low iteration count loop
+   Crucially, none for a peeled off last iteration following the parallel.  */
+/* { dg-final { scan-tree-dump-times "(?n)\\\* 4" 3 "parloops" } } */
+
+/* { dg-final { cleanup-tree-dump "parloops" } } */
diff --git a/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt.c b/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt.c
new file mode 100644
index 0000000..42ca3ac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/parloops-exit-first-loop-alt.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target pthread } */
+/* { dg-options "-O2 -ftree-parallelize-loops=2 -fdump-tree-parloops" } */
+
+#define N 1000
+
+unsigned int a[N];
+unsigned int b[N];
+unsigned int c[N];
+
+void __attribute__((noclone,noinline))
+f (unsigned int n)
+{
+  int i;
+
+  for (i = 0; i < n; ++i)
+    c[i] = a[i] + b[i];
+}
+
+/* Three times three array accesses:
+   - three in f._loopfn.0
+   - three in the parallel
+   - three in the low iteration count loop
+   Crucially, none for a peeled off last iteration following the parallel.  */
+/* { dg-final { scan-tree-dump-times "(?n)\\\[i" 9 "parloops" } } */
+
+/* { dg-final { cleanup-tree-dump "parloops" } } */
diff --git a/gcc/tree-parloops.c b/gcc/tree-parloops.c
index fbb9eeb..a60efbe 100644
--- a/gcc/tree-parloops.c
+++ b/gcc/tree-parloops.c
@@ -1495,17 +1495,327 @@ create_loop_fn (location_t loc)
   return decl;
 }
 
-/* Moves the exit condition of LOOP to the beginning of its header, and
-   duplicates the part of the last iteration that gets disabled to the
-   exit of the loop.  NIT is the number of iterations of the loop
-   (used to initialize the variables in the duplicated part).
-
-   TODO: the common case is that latch of the loop is empty and immediately
-   follows the loop exit.  In this case, it would be better not to copy the
-   body of the loop, but only move the entry of the loop directly before the
-   exit check and increase the number of iterations of the loop by one.
-   This may need some additional preconditioning in case NIT = ~0.
-   REDUCTION_LIST describes the reductions in LOOP.  */
+/* Replace uses of VAL in iterator IMM_ITER.  */
+
+static void
+replace_imm_uses (tree val, imm_use_iterator *imm_iter)
+{
+  use_operand_p use_p;
+
+  FOR_EACH_IMM_USE_ON_STMT (use_p, *imm_iter)
+    SET_USE (use_p, val);
+}
+
+/* Replace uses of NAME by VAL in block BB.  */
+
+static void
+replace_uses_in_bb_by (tree name, tree val, basic_block bb)
+{
+  gimple use_stmt;
+  imm_use_iterator imm_iter;
+
+  FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, name)
+    {
+      if (gimple_bb (use_stmt) != bb)
+	continue;
+
+      replace_imm_uses (val, &imm_iter);
+    }
+}
+
+/* Replace uses of NAME by VAL in blocks BBS.  */
+
+static void
+replace_uses_in_bbs_by (tree name, tree val, bitmap bbs)
+{
+  gimple use_stmt;
+  imm_use_iterator imm_iter;
+
+  FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, name)
+    {
+      if (!bitmap_bit_p (bbs, gimple_bb (use_stmt)->index))
+	continue;
+
+      replace_imm_uses (val, &imm_iter);
+    }
+}
+
+/*
+   Do transformation from:
+
+     bb preheader:
+     ...
+     goto <bb header>
+
+     <bb header>:
+     ...
+     if (ivtmp < n)
+       goto <bb latch>;
+     else
+       goto <bb exit>;
+
+     <bb latch>:
+     ivtmp = ivtmp + inc;
+     goto <bb header>
+
+   to:
+
+     bb preheader:
+     ...
+     goto <bb newheader>
+
+     <bb header>:
+     ...
+     goto <bb latch>;
+
+     <bb newheader>:
+     if (ivtmp < n + 1)
+       goto <bb header>;
+     else
+       goto <bb exit>;
+
+     <bb latch>:
+     ivtmp = ivtmp + inc;
+     goto <bb newheader>
+
+   Moves the exit condition of LOOP to the beginning of its header.
+   REDUCTION_LIST describes the reductions in LOOP.  BOUND is the new loop
+   bound.  */
+
+static void
+transform_to_exit_first_loop_alt (struct loop *loop,
+				  reduction_info_table_type *reduction_list,
+				  tree bound)
+{
+  basic_block header = loop->header;
+  basic_block latch = loop->latch;
+  edge exit = single_dom_exit (loop);
+  basic_block exit_block = exit->dest;
+  gcond *cond_stmt = as_a <gcond *> (last_stmt (exit->src));
+  tree control = gimple_cond_lhs (cond_stmt);
+  gphi *phi, *nphi;
+  tree res, new_res, init, inc_res;
+
+  /* Gather the bbs dominated by the exit block.  */
+  bitmap exit_dominated = BITMAP_ALLOC (NULL);
+  bitmap_set_bit (exit_dominated, exit_block->index);
+  vec<basic_block> exit_dominated_vec
+    = get_dominated_by (CDI_DOMINATORS, exit_block);
+
+  int i;
+  basic_block dom_bb;
+  FOR_EACH_VEC_ELT (exit_dominated_vec, i, dom_bb)
+    bitmap_set_bit (exit_dominated, dom_bb->index);
+
+  exit_dominated_vec.release ();
+
+  /* Create the new_header block.  */
+  basic_block new_header = split_block_before_cond_jump (exit->src);
+  edge split_edge = single_pred_edge (new_header);
+
+  /* We could redirect entry to new_header, but we want to keep the phis on
+     header intact.  So we create new_entry here, and remove entry later.  */
+  edge entry = loop_preheader_edge (loop);
+  edge new_entry = make_edge (entry->src, new_header, EDGE_FALLTHRU);
+  new_entry->probability = entry->probability;
+
+  /* We could redirect post_inc_edge to new_header, but we want to keep the phis
+     on header intact.  So we create new_post_inc_edge here, and remove
+     post_inc_edge later.  */
+  edge post_inc_edge = single_succ_edge (latch);
+  edge new_post_inc_edge = make_edge (latch, new_header, EDGE_FALLTHRU);
+  new_post_inc_edge->probability = REG_BR_PROB_BASE;
+
+  /* Redirect pre_inc_edge to header.  */
+  edge pre_inc_edge = single_pred_edge (latch);
+  edge new_latch_edge = redirect_edge_and_branch (pre_inc_edge, header);
+
+  /* Redirect split_edge to latch.  */
+  redirect_edge_and_branch (split_edge, latch);
+
+  /* Set the new loop bound.  */
+  gimple_cond_set_rhs (cond_stmt, bound);
+
+  /* Repair the ssa.  */
+  for (gphi_iterator gsi = gsi_start_phis (header);
+       !gsi_end_p (gsi);
+       gsi_next (&gsi))
+    {
+      phi = gsi.phi ();
+      res = PHI_RESULT (phi);
+
+      /* Create new phi.  */
+      new_res = copy_ssa_name (res, phi);
+      nphi = create_phi_node (new_res, new_header);
+
+      /* Set the entry argument of the new phi.  */
+      init = gimple_phi_arg_def (phi, entry->dest_idx);
+      add_phi_arg (nphi, init, new_entry, UNKNOWN_LOCATION);
+
+      replace_uses_in_bb_by (res, new_res, new_header);
+
+      /* Set the back argument of the new phi.  */
+      inc_res = gimple_phi_arg_def (phi, 1 - entry->dest_idx);
+      add_phi_arg (nphi, inc_res, new_post_inc_edge, UNKNOWN_LOCATION);
+
+      /* Fixup old phi.  */
+      add_phi_arg (phi, new_res, new_latch_edge, UNKNOWN_LOCATION);
+
+      /* Loop-closed ssa does not hold for virtuals, so we cannot get away with
+	 exit_block only.  */
+      replace_uses_in_bbs_by (inc_res, new_res, exit_dominated);
+
+      struct reduction_info *red = reduction_phi (reduction_list, phi);
+      gcc_assert (virtual_operand_p (res)
+		  || res == control
+		  || red != NULL);
+
+      if (red)
+	{
+	  /* Register the new reduction phi.  */
+	  red->reduc_phi = nphi;
+	  gimple_set_uid (red->reduc_phi, red->reduc_version);
+	}
+    }
+  BITMAP_FREE (exit_dominated);
+
+  /* Register the reduction exit phis.  */
+  for (gphi_iterator gsi = gsi_start_phis (exit_block);
+       !gsi_end_p (gsi);
+       gsi_next (&gsi))
+    {
+      phi = gsi.phi ();
+      res = PHI_RESULT (phi);
+      if (virtual_operand_p (res))
+	continue;
+
+      tree val = PHI_ARG_DEF_FROM_EDGE (phi, exit);
+      gimple reduc_phi = SSA_NAME_DEF_STMT (val);
+      struct reduction_info *red = reduction_phi (reduction_list, reduc_phi);
+      if (red != NULL)
+	red->keep_res = phi;
+    }
+
+  /* We no longer need the phis on the old loop header.  Remove replaced
+     edges.  */
+  remove_edge (entry);
+  remove_edge (post_inc_edge);
+
+  /* Fix up loop structure.  TODO: Check whether this is sufficient.  */
+  loop->header = new_header;
+
+  /* Recalculate dominance info.  */
+  free_dominance_info (CDI_DOMINATORS);
+  calculate_dominance_info (CDI_DOMINATORS);
+}
+
+/* Tries to moves the exit condition of LOOP to the beginning of its header
+   without duplication of the loop body.  NIT is the number of iterations of the
+   loop.  REDUCTION_LIST describes the reductions in LOOP.  Return true if
+   transformation is successful.  */
+
+static bool
+try_transform_to_exit_first_loop_alt (struct loop *loop,
+				      reduction_info_table_type *reduction_list,
+				      tree nit)
+{
+  /* Check whether the latch contains a single statement.  */
+  if (!gimple_seq_singleton_p (bb_seq (loop->latch)))
+    return true;
+
+  /* Check whether the latch contains the loop iv increment.  */
+  edge back = single_succ_edge (loop->latch);
+  edge exit = single_dom_exit (loop);
+  gcond *cond_stmt = as_a <gcond *> (last_stmt (exit->src));
+  tree control = gimple_cond_lhs (cond_stmt);
+  gphi *phi = as_a <gphi *> (SSA_NAME_DEF_STMT (control));
+  tree inc_res = gimple_phi_arg_def (phi, back->dest_idx);
+  if (gimple_bb (SSA_NAME_DEF_STMT (inc_res)) != loop->latch)
+    return false;
+
+  /* Check whether there's no code between the loop condition and the latch.  */
+  if (!single_pred_p (loop->latch)
+      || single_pred (loop->latch) != exit->src)
+    return false;
+
+  tree alt_bound = NULL_TREE;
+  tree nit_type = TREE_TYPE (nit);
+
+  /* Figure out whether nit + 1 overflows.  */
+  if (TREE_CODE (nit) == INTEGER_CST)
+    {
+      if (!tree_int_cst_equal (nit, TYPE_MAXVAL (nit_type)))
+	{
+	  alt_bound = fold_build2_loc (UNKNOWN_LOCATION, PLUS_EXPR, nit_type,
+				       nit, build_one_cst (nit_type));
+
+	  gcc_assert (TREE_CODE (alt_bound) == INTEGER_CST);
+	}
+      else
+	{
+	  /* Todo: Figure out if we can trigger this, if it's worth to handle
+	     optimally, and if we can handle it optimally.  */
+	}
+    }
+  else
+    {
+      gcc_assert (TREE_CODE (nit) == SSA_NAME);
+
+      gimple def = SSA_NAME_DEF_STMT (nit);
+
+      if (def
+	  && is_gimple_assign (def)
+	  && gimple_assign_rhs_code (def) == PLUS_EXPR)
+	{
+	  tree op1 = gimple_assign_rhs1 (def);
+	  tree op2 = gimple_assign_rhs2 (def);
+	  if (integer_minus_onep (op1))
+	    alt_bound = op2;
+	  else if (integer_minus_onep (op2))
+	    alt_bound = op1;
+	}
+
+      /* There is a number of test-cases for which we don't get an alt_bound
+	 here: they're listed here, with the lhs of the last stmt as the nit:
+
+	 libgomp.graphite/force-parallel-1.c:
+	 _21 = (signed long) N_6(D);
+	 _19 = _21 + -1;
+	 _7 = (unsigned long) _19;
+
+	 libgomp.graphite/force-parallel-2.c:
+	 _33 = (signed long) N_9(D);
+	 _16 = _33 + -1;
+	 _37 = (unsigned long) _16;
+
+	 libgomp.graphite/force-parallel-5.c:
+	 <bb 6>:
+	 # graphite_IV.5_46 = PHI <0(5), graphite_IV.5_47(11)>
+	 <bb 7>:
+	 _33 = (unsigned long) graphite_IV.5_46;
+
+	 g++.dg/tree-ssa/pr34355.C:
+	 _2 = (unsigned int) i_9;
+	 _3 = 4 - _2;
+
+	 gcc.dg/pr53849.c:
+	 _5 = d.0_11 + -2;
+	 _18 = (unsigned int) _5;
+
+	 We will be able to handle some of these cases, if we can determine when
+	 it's safe to look past casts.  */
+    }
+
+  if (alt_bound == NULL_TREE)
+    return false;
+
+  transform_to_exit_first_loop_alt (loop, reduction_list, alt_bound);
+  return true;
+}
+
+/* Moves the exit condition of LOOP to the beginning of its header.  NIT is the
+   number of iterations of the loop.  REDUCTION_LIST describes the reductions in
+   LOOP.  */
 
 static void
 transform_to_exit_first_loop (struct loop *loop,
@@ -1522,6 +1832,18 @@ transform_to_exit_first_loop (struct loop *loop,
   gcond *cond_stmt, *cond_nit;
   tree nit_1;
 
+  /* The common case is that latch of the loop is empty (apart from the
+     increment) and immediately follows the loop exit test.  Attempt to move the
+     entry of the loop directly before the exit check and increase the number of
+     iterations of the loop by one.  */
+  if (try_transform_to_exit_first_loop_alt (loop, reduction_list, nit))
+    return;
+
+  /* Fall back on the method that handles more cases, but duplicates the loop
+     body: move the exit condition of LOOP to the beginning of its header, and
+     duplicate the part of the last iteration that gets disabled to the exit of
+     the loop.  */
+
   split_block_after_labels (loop->header);
   orig_header = single_succ (loop->header);
   hpred = single_succ_edge (loop->header);
diff --git a/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt-2.c b/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt-2.c
new file mode 100644
index 0000000..eb5e11f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt-2.c
@@ -0,0 +1,48 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -ftree-parallelize-loops=2" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define N 1000
+
+unsigned int a[N];
+unsigned int b[N];
+unsigned int c[N];
+
+void __attribute__((noclone,noinline))
+f (void)
+{
+  int i;
+
+  for (i = 0; i < N; ++i)
+    c[i] = a[i] + b[i];
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  /* Complexify loop to inhibit parloops.  */
+  for (j = 0; j < 100; ++j)
+    for (i = 0; i < 10; i++)
+      {
+	int k = i + (10 * j);
+	a[k] = k;
+	b[k] = (k * 3) % 7;
+	c[k] = k * 2;
+      }
+
+  f ();
+
+  for (i = 0; i < N; i++)
+    {
+      unsigned int actual = c[i];
+      unsigned int expected = i + ((i * 3) % 7);
+      if (actual != expected)
+	abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt-3.c b/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt-3.c
new file mode 100644
index 0000000..b426b3f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt-3.c
@@ -0,0 +1,29 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -ftree-parallelize-loops=2" } */
+
+unsigned int *a;
+
+unsigned int __attribute__((noclone,noinline))
+f (unsigned int n)
+{
+  int i;
+  unsigned int sum = 1;
+
+  for (i = 0; i < n; ++i)
+    sum += a[i];
+
+  return sum;
+}
+
+int
+main (void)
+{
+  unsigned int res;
+  unsigned int array[4000];
+  int i;
+  for (i = 0; i < 4000; ++i)
+    array[i] = i % 7;
+  a = &array[0];
+  res = f (4000);
+  return !(res == 11995);
+}
diff --git a/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt.c b/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt.c
new file mode 100644
index 0000000..d7d4003
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/parloops-exit-first-loop-alt.c
@@ -0,0 +1,48 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -ftree-parallelize-loops=2" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define N 1000
+
+unsigned int a[N];
+unsigned int b[N];
+unsigned int c[N];
+
+void __attribute__((noclone,noinline))
+f (unsigned int n)
+{
+  int i;
+
+  for (i = 0; i < n; ++i)
+    c[i] = a[i] + b[i];
+}
+
+int
+main (void)
+{
+  int i, j;
+
+  /* Complexify loop to inhibit parloops.  */
+  for (j = 0; j < 100; ++j)
+    for (i = 0; i < 10; i++)
+      {
+	int k = i + (10 * j);
+	a[k] = k;
+	b[k] = (k * 3) % 7;
+	c[k] = k * 2;
+      }
+
+  f (N);
+
+  for (i = 0; i < N; i++)
+    {
+      unsigned int actual = c[i];
+      unsigned int expected = i + ((i * 3) % 7);
+      if (actual != expected)
+	abort ();
+    }
+
+  return 0;
+}
-- 
1.9.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]