Hi,

we recently noticed that, even at -O3, the compiler doesn't figure out that 
the following loop is dumb:

#define SIZE 64

int foo (int v[])
{
  int r;

  for (i = 0; i < SIZE; i++)
    r = v[i];

  return r;
}

which was a bit of a surprise.  On second thoughts, this isn't entirely 
unexpected, as it probably matters only for (slightly) pathological cases.
The attached patch nevertheless implements a form of load sinking in loops so 
as to optimize these cases.  It's combined with invariant motion to optimize:

int foo (int v[], int a)
{
  int r, i;

  for (i = 0; i < SIZE; i++)
    r = v[i] + a;

  return r;
}

and with store sinking to optimize:

int foo (int v1[], int v2[])
{
  int r[SIZE];
  int i, j;

  for (j = 0; j < SIZE; j++)
    for (i = 0; i < SIZE; i++)
      r[j] = v1[j] + v2[i];

  return r[SIZE - 1];
}

The optimization is enabled at -O2 in the patch for measurement purposes but, 
given how rarely it triggers (e.g. exactly 10 occurrences in a GCC bootstrap, 
compiler-only, all languages except Go), it's probably best suited to -O3.
Or perhaps we don't care and it should simply be dropped...  Thoughts?

Tested on x86_64-suse-linux.


2012-10-08  Eric Botcazou  <ebotca...@adacore.com>

        * gimple.h (gsi_insert_seq_on_edge_before): Declare.
        * gimple-iterator.c (gsi_insert_seq_on_edge_before): New function.
        * tree-ssa-loop-im.c (struct mem_ref_loc): Add LHS field.
        (mem_ref_in_stmt): Remove gcc_assert.
        (copy_load_and_single_use_chain): New function.
        (execute_lm): Likewise.
        (hoist_memory_references): Hoist the loads after the stores.
        (ref_always_accessed_p): Rename into...
        (ref_always_stored_p): ...this.  Remove STORE_P and add ONCE_P.
        (can_lsm_ref_p): New function extracted from...
        (can_sm_ref_p): ...here.  Call it.
        (follow_invariant_single_use_chain): New function.
        (can_lm_ref_p): Likewise.
        (find_refs_for_sm): Rename into..
        (find_refs_for_lsm): ...this.  Find load hoisting opportunities.
        (loop_suitable_for_sm): Rename into...
        (loop_suitable_for_lsm): ...this.
        (store_motion_loop): Rename into...
        (load_store_motion_loop): ...this.  Adjust calls to above functions.
        (tree_ssa_lim): Likewise.


2012-10-08  Eric Botcazou  <ebotca...@adacore.com>

        * gcc.dg/tree-ssa/loadmotion-1.c: New test.
        * gcc.dg/tree-ssa/loadmotion-2.c: New test.
        * gcc.dg/tree-ssa/loadmotion-3.c: New test.


-- 
Eric Botcazou
Index: gimple.h
===================================================================
--- gimple.h	(revision 192137)
+++ gimple.h	(working copy)
@@ -5196,6 +5196,7 @@ void gsi_move_before (gimple_stmt_iterat
 void gsi_move_to_bb_end (gimple_stmt_iterator *, basic_block);
 void gsi_insert_on_edge (edge, gimple);
 void gsi_insert_seq_on_edge (edge, gimple_seq);
+void gsi_insert_seq_on_edge_before (edge, gimple_seq);
 basic_block gsi_insert_on_edge_immediate (edge, gimple);
 basic_block gsi_insert_seq_on_edge_immediate (edge, gimple_seq);
 void gsi_commit_one_edge_insert (edge, basic_block *);
Index: gimple-iterator.c
===================================================================
--- gimple-iterator.c	(revision 192137)
+++ gimple-iterator.c	(working copy)
@@ -677,6 +677,16 @@ gsi_insert_seq_on_edge (edge e, gimple_s
   gimple_seq_add_seq (&PENDING_STMT (e), seq);
 }
 
+/* Likewise, but append it instead of prepending it.  */
+
+void
+gsi_insert_seq_on_edge_before (edge e, gimple_seq seq)
+{
+  gimple_seq pending = NULL;
+  gimple_seq_add_seq (&pending, seq);
+  gimple_seq_add_seq (&pending, PENDING_STMT (e));
+  PENDING_STMT (e) = pending;
+}
 
 /* Insert the statement pointed-to by GSI into edge E.  Every attempt
    is made to place the statement in an existing basic block, but
Index: tree-ssa-loop-im.c
===================================================================
--- tree-ssa-loop-im.c	(revision 192137)
+++ tree-ssa-loop-im.c	(working copy)
@@ -103,6 +103,7 @@ typedef struct mem_ref_loc
 {
   tree *ref;			/* The reference itself.  */
   gimple stmt;			/* The statement in that it occurs.  */
+  tree lhs;			/* The (ultimate) LHS for a load.  */
 } *mem_ref_loc_p;
 
 DEF_VEC_P(mem_ref_loc_p);
@@ -674,7 +675,6 @@ mem_ref_in_stmt (gimple stmt)
 
   if (!mem)
     return NULL;
-  gcc_assert (!store);
 
   hash = iterative_hash_expr (*mem, 0);
   ref = (mem_ref_p) htab_find_with_hash (memory_accesses.refs, *mem, hash);
@@ -2192,6 +2192,140 @@ execute_sm (struct loop *loop, VEC (edge
       execute_sm_if_changed (ex, ref->mem, tmp_var, store_flag);
 }
 
+/* Copy the load and the chain of single uses described by LOC and return the
+   sequence of new statements.  Also set NEW_LHS to the copy of LOC->LHS.  */
+
+static gimple_seq
+copy_load_and_single_use_chain (mem_ref_loc_p loc, tree *new_lhs)
+{
+  tree mem = *loc->ref;
+  tree lhs, tmp_var, ssa_name;
+  gimple_seq seq = NULL;
+  gimple stmt;
+  unsigned n = 0;
+
+  /* First copy the load and create the new LHS for it.  */
+  lhs = gimple_assign_lhs (loc->stmt);
+  tmp_var = create_tmp_reg (TREE_TYPE (lhs), get_lsm_tmp_name (mem, n++));
+  stmt = gimple_build_assign (tmp_var, unshare_expr (mem));
+  ssa_name = make_ssa_name (tmp_var, stmt);	
+  gimple_assign_set_lhs (stmt, ssa_name);
+  gimple_seq_add_stmt (&seq, stmt);
+
+  /* Then follow the single-use chain and repeat the operation.  */
+  while (lhs != loc->lhs)
+    {
+      tree op1, op2;
+      use_operand_p use;
+      gimple use_stmt;
+      bool ret = single_imm_use (lhs, &use, &use_stmt);
+      enum tree_code rhs_code = gimple_assign_rhs_code (use_stmt);
+      gcc_assert (ret);
+      
+      if (gimple_assign_rhs1 (use_stmt) == lhs)
+	{
+	  op1 = ssa_name;
+	  op2 = gimple_assign_rhs2 (use_stmt);
+	}
+      else
+	{
+	  op1 = gimple_assign_rhs1 (use_stmt);
+	  op2 = ssa_name;
+	}
+
+      lhs = gimple_assign_lhs (use_stmt);
+      tmp_var = create_tmp_reg (TREE_TYPE (lhs), get_lsm_tmp_name (mem, n++));
+      stmt = gimple_build_assign_with_ops (rhs_code, tmp_var, op1, op2);
+      ssa_name = make_ssa_name (tmp_var, stmt);
+      gimple_assign_set_lhs (stmt, ssa_name);
+      gimple_seq_add_stmt (&seq, stmt);
+    }
+
+  *new_lhs = ssa_name;
+  return seq;
+}
+
+/* Execute load motion of memory reference REF from LOOP.
+   Exits from the LOOP are stored in EXITS.  The original loads are turned
+   into null assignments and the new loads are emitted on the exit edges.  */
+
+static void
+execute_lm (struct loop *loop, VEC (edge, heap) *exits, mem_ref_p ref)
+{
+  VEC (mem_ref_loc_p, heap) *locs = NULL;
+  mem_ref_loc_p loc;
+  unsigned i;
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Executing load motion of ");
+      print_generic_expr (dump_file, ref->mem, 0);
+      fprintf (dump_file, " from loop %d\n", loop->num);
+    }
+
+  get_all_locs_in_loop (loop, ref, &locs);
+
+  /* We have two cases: either the LHS was assigned to a reference that has
+     been hoisted out of the loop or it wasn't used within the loop.  */
+  FOR_EACH_VEC_ELT (mem_ref_loc_p, locs, i, loc)
+    {
+      imm_use_iterator iter;
+      gimple use_stmt;
+      tree new_lhs;
+
+      FOR_EACH_IMM_USE_STMT (use_stmt, iter, loc->lhs)
+	if (gimple_debug_bind_p (use_stmt))
+	  {
+	    gimple_debug_bind_reset_value (use_stmt);
+	    update_stmt (use_stmt);
+	  }
+	else if (gimple_assign_single_p (use_stmt))
+	  {
+	    tree var = gimple_assign_lhs (use_stmt);
+	    unsigned j;
+	    edge ex;
+
+	    /* This must be a variable replacing a former store.  */
+	    gcc_assert (TREE_CODE (var) == VAR_DECL);
+
+	    /* Insert the load and the single-use chain on every exit edge,
+	       before the store that has been previously inserted there.  */
+	    FOR_EACH_VEC_ELT (edge, exits, j, ex)
+	      {
+		gimple_seq s = copy_load_and_single_use_chain (loc, &new_lhs);
+		gimple stmt = gimple_build_assign (var, new_lhs);
+		gimple_seq_add_stmt (&s, stmt);
+		gsi_insert_seq_on_edge_before (ex, s);
+	      }
+	  }
+	else
+	  {
+	    use_operand_p use;
+
+	    /* We are supposed to be in loop-closed SSA form.  */
+	    gcc_assert (gimple_code (use_stmt) == GIMPLE_PHI);
+
+	    /* Insert the load and the single-use chain on every relevant edge
+	       of the PHI node and set the PHI argument to the new LHS.  */
+	    FOR_EACH_IMM_USE_ON_STMT (use, iter)
+	      {
+		edge e = gimple_phi_arg_edge (use_stmt,
+					      PHI_ARG_INDEX_FROM_USE (use));
+		gimple_seq s = copy_load_and_single_use_chain (loc, &new_lhs);
+		gsi_insert_seq_on_edge (e, s);
+		SET_USE (use, new_lhs);
+	      }
+	  }
+
+      /* Finally kill the original load.  */
+      gimple_assign_set_rhs1 (loc->stmt,
+			      build_zero_cst (TREE_TYPE (ref->mem)));
+      update_stmt (loc->stmt);
+    }
+
+  VEC_free (mem_ref_loc_p, heap, locs);
+}
+
 /* Hoists memory references MEM_REFS out of LOOP.  EXITS is the list of exit
    edges of the LOOP.  */
 
@@ -2203,67 +2337,77 @@ hoist_memory_references (struct loop *lo
   unsigned  i;
   bitmap_iterator bi;
 
+  /* First hoist the stores.  */
+  EXECUTE_IF_SET_IN_BITMAP (mem_refs, 0, i, bi)
+    {
+      ref = VEC_index (mem_ref_p, memory_accesses.refs_list, i);
+      if (bitmap_bit_p (ref->stored, loop->num))
+	execute_sm (loop, exits, ref);
+    }
+
+  /* Then hoist the loads.  */
   EXECUTE_IF_SET_IN_BITMAP (mem_refs, 0, i, bi)
     {
       ref = VEC_index (mem_ref_p, memory_accesses.refs_list, i);
-      execute_sm (loop, exits, ref);
+      if (!bitmap_bit_p (ref->stored, loop->num))
+	execute_lm (loop, exits, ref);
     }
 }
 
-/* Returns true if REF is always accessed in LOOP.  If STORED_P is true
-   make sure REF is always stored to in LOOP.  */
+/* Return true if REF is always stored to in LOOP.  If ONCE_P is true, make
+   sure REF is stored to exactly once in LOOP.  */
 
 static bool
-ref_always_accessed_p (struct loop *loop, mem_ref_p ref, bool stored_p)
+ref_always_stored_p (struct loop *loop, mem_ref_p ref, bool once_p)
 {
   VEC (mem_ref_loc_p, heap) *locs = NULL;
   unsigned i;
   mem_ref_loc_p loc;
   bool ret = false;
   struct loop *must_exec;
-  tree base;
+  tree base, lhs;
 
   base = get_base_address (ref->mem);
-  if (INDIRECT_REF_P (base)
-      || TREE_CODE (base) == MEM_REF)
+  if (INDIRECT_REF_P (base) || TREE_CODE (base) == MEM_REF)
     base = TREE_OPERAND (base, 0);
 
   get_all_locs_in_loop (loop, ref, &locs);
+  if (once_p && VEC_length (mem_ref_loc_p, locs) != 1)
+    {
+      VEC_free (mem_ref_loc_p, heap, locs);
+      return false;
+    }
+
   FOR_EACH_VEC_ELT (mem_ref_loc_p, locs, i, loc)
     {
       if (!get_lim_data (loc->stmt))
 	continue;
 
-      /* If we require an always executed store make sure the statement
-         stores to the reference.  */
-      if (stored_p)
-	{
-	  tree lhs;
-	  if (!gimple_get_lhs (loc->stmt))
-	    continue;
-	  lhs = get_base_address (gimple_get_lhs (loc->stmt));
-	  if (!lhs)
-	    continue;
-	  if (INDIRECT_REF_P (lhs)
-	      || TREE_CODE (lhs) == MEM_REF)
-	    lhs = TREE_OPERAND (lhs, 0);
-	  if (lhs != base)
-	    continue;
-	}
+      if (!gimple_get_lhs (loc->stmt))
+	continue;
+
+      lhs = get_base_address (gimple_get_lhs (loc->stmt));
+      if (!lhs)
+	continue;
+
+      if (INDIRECT_REF_P (lhs) || TREE_CODE (lhs) == MEM_REF)
+	lhs = TREE_OPERAND (lhs, 0);
+
+      if (lhs != base)
+	continue;
 
       must_exec = get_lim_data (loc->stmt)->always_executed_in;
       if (!must_exec)
 	continue;
 
-      if (must_exec == loop
-	  || flow_loop_nested_p (must_exec, loop))
+      if (must_exec == loop || flow_loop_nested_p (must_exec, loop))
 	{
 	  ret = true;
 	  break;
 	}
     }
-  VEC_free (mem_ref_loc_p, heap, locs);
 
+  VEC_free (mem_ref_loc_p, heap, locs);
   return ret;
 }
 
@@ -2375,77 +2519,234 @@ ref_indep_loop_p (struct loop *loop, mem
   return ret;
 }
 
-/* Returns true if we can perform store motion of REF from LOOP.  */
+/* Return true if we can perform load or store motion of REF from LOOP.
+   FOR_SM is true if this is for store motion or false for load motion.  */
 
 static bool
-can_sm_ref_p (struct loop *loop, mem_ref_p ref)
+can_lsm_ref_p (struct loop *loop, mem_ref_p ref, bool for_sm)
 {
-  tree base;
+  bitmap refs;
+  bool stored;
 
   /* Can't hoist unanalyzable refs.  */
   if (!MEM_ANALYZABLE (ref))
     return false;
 
-  /* Unless the reference is stored in the loop, there is nothing to do.  */
-  if (!bitmap_bit_p (ref->stored, loop->num))
+  /* Unless the reference is accessed in the loop, there is nothing to do.  */
+  refs = VEC_index (bitmap, memory_accesses.all_refs_in_loop, loop->num);
+  if (!bitmap_bit_p (refs, ref->id))
+    return false;
+
+  /* The reference must be stored to for store motion and may not be stored
+     to for load motion.  */
+  stored = bitmap_bit_p (ref->stored, loop->num);
+  if (stored != for_sm)
     return false;
 
   /* It should be movable.  */
   if (!is_gimple_reg_type (TREE_TYPE (ref->mem))
-      || TREE_THIS_VOLATILE (ref->mem)
-      || !for_each_index (&ref->mem, may_move_till, loop))
+      || TREE_THIS_VOLATILE (ref->mem))
     return false;
 
   /* If it can throw fail, we do not properly update EH info.  */
   if (tree_could_throw_p (ref->mem))
     return false;
 
-  /* If it can trap, it must be always executed in LOOP.
-     Readonly memory locations may trap when storing to them, but
+  return true;
+}
+
+/* Return true if we can perform store motion of REF from LOOP.  */
+
+static bool
+can_sm_ref_p (struct loop *loop, mem_ref_p ref)
+{
+  tree base;
+
+  if (!can_lsm_ref_p (loop, ref, true))
+    return false;
+
+  /* It must be possible to hoist the index out of the loop.  */
+  if (!for_each_index (&ref->mem, may_move_till, loop))
+    return false;
+
+  /* If it can trap, a store must be always executed in LOOP.
+     Read-only memory locations may trap when storing to them, but
      tree_could_trap_p is a predicate for rvalues, so check that
      explicitly.  */
   base = get_base_address (ref->mem);
   if ((tree_could_trap_p (ref->mem)
        || (DECL_P (base) && TREE_READONLY (base)))
-      && !ref_always_accessed_p (loop, ref, true))
+      && !ref_always_stored_p (loop, ref, false))
     return false;
 
-  /* And it must be independent on all other memory references
-     in LOOP.  */
+  /* And the store must be independent of other memory references in LOOP.  */
   if (!ref_indep_loop_p (loop, ref))
     return false;
 
   return true;
 }
 
-/* Marks the references in LOOP for that store motion should be performed
-   in REFS_TO_SM.  SM_EXECUTED is the set of references for that store
+/* Follow a chain of single uses in LOOP starting from SSA_NAME and containing
+   only invariants of LOOP.  Return the end of the chain.  */
+
+static tree
+follow_invariant_single_use_chain (struct loop *loop, tree ssa_name)
+{
+  use_operand_p use;
+  gimple use_stmt;
+ 
+  while (single_imm_use (ssa_name, &use, &use_stmt)
+	 && flow_bb_inside_loop_p (loop, gimple_bb (use_stmt)))
+    {
+      tree other_op;
+
+      if (!is_gimple_assign (use_stmt)
+	  || gimple_assign_rhs_class (use_stmt) != GIMPLE_BINARY_RHS)
+	break;
+
+      if (gimple_assign_rhs1 (use_stmt) == ssa_name)
+	other_op = gimple_assign_rhs2 (use_stmt);
+      else
+	other_op = gimple_assign_rhs1 (use_stmt);
+
+      if (outermost_invariant_loop (other_op, loop) == NULL)
+	break;
+
+      ssa_name = gimple_assign_lhs (use_stmt);
+    }
+
+  return ssa_name;
+}
+
+/* Return true if we can perform load motion of REF from LOOP.
+   REFS_TO_SM is the set of references for which store motion will be
+   performed in LOOP.  */
+
+static bool
+can_lm_ref_p (struct loop *loop, mem_ref_p ref, bitmap refs_to_sm)
+{
+  VEC (mem_ref_loc_p, heap) *locs = NULL;
+  mem_ref_loc_p loc;
+  unsigned i;
+
+  if (!can_lsm_ref_p (loop, ref, false))
+    return false;
+
+  get_all_locs_in_loop (loop, ref, &locs);
+
+  /* The LHS may not be used within the loop or the LHS must be the head of
+     an invariant single-use chain whose end has this property.  */
+  FOR_EACH_VEC_ELT (mem_ref_loc_p, locs, i, loc)
+    {
+      struct lim_aux_data *lim_data;
+      struct loop *must_exec;
+      imm_use_iterator iter;
+      gimple use_stmt;
+      bool fail = false;
+      tree lhs;
+
+      /* Don't look at loads that will be hoisted as invariant.  */
+      lim_data = get_lim_data (loc->stmt);
+      if (!lim_data || lim_data->max_loop != NULL)
+	goto failure;
+
+      /* The loads must always be executed in LOOP.  */
+      must_exec = lim_data->always_executed_in;
+      if (!must_exec
+	  || !(must_exec == loop || flow_loop_nested_p (must_exec, loop)))
+	goto failure;
+
+      lhs = gimple_assign_lhs (loc->stmt);
+
+      /* Follow an invariant single-use chain and retrieve its end.  */
+      lhs = follow_invariant_single_use_chain (loop, lhs);
+
+      FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
+        if (gimple_debug_bind_p (use_stmt))
+	  ;
+	else if (flow_bb_inside_loop_p (loop, gimple_bb (use_stmt)))
+	  {
+	    mem_ref_p use_ref;
+
+	    /* If the LHS is assigned to references that will be hoisted out
+	       of the loop, then the condition will be fulfilled after store
+	       motion.  But we need to make sure the assignments are always
+	       executed once since we cannot insert PHI nodes on edges.  */
+	    if (!gimple_has_volatile_ops (use_stmt)
+		&& gimple_assign_single_p (use_stmt)
+		&& gimple_assign_rhs1 (use_stmt) == lhs
+		&& gimple_vdef (use_stmt)
+		&& (use_ref = mem_ref_in_stmt (use_stmt)) != NULL
+		&& bitmap_bit_p (refs_to_sm, use_ref->id)
+		&& ref_always_stored_p (loop, use_ref, true))
+	      continue;
+
+	    fail = true;
+	    BREAK_FROM_IMM_USE_STMT (iter);
+	  }
+
+      if (fail)
+	goto failure;
+
+      /* Record the end of the chain.  */
+      loc->lhs = lhs;
+    }
+
+  /* And the load must be independent of other memory references in LOOP.  */
+  if (!ref_indep_loop_p (loop, ref))
+    goto failure;
+
+  VEC_free (mem_ref_loc_p, heap, locs);
+  return true;
+
+failure:
+  VEC_free (mem_ref_loc_p, heap, locs);
+  return false;
+}
+
+/* Mark references in LOOP for which load-store motion should be performed
+   in REFS_TO_LSM.  LSM_EXECUTED is the set of references for which load-store
    motion was performed in one of the outer loops.  */
 
 static void
-find_refs_for_sm (struct loop *loop, bitmap sm_executed, bitmap refs_to_sm)
+find_refs_for_lsm (struct loop *loop, bitmap lsm_executed, bitmap refs_to_lsm)
 {
+  bitmap refs_to_sm = BITMAP_ALLOC (NULL);
   bitmap refs = VEC_index (bitmap, memory_accesses.all_refs_in_loop,
 			   loop->num);
   unsigned i;
   bitmap_iterator bi;
   mem_ref_p ref;
 
-  EXECUTE_IF_AND_COMPL_IN_BITMAP (refs, sm_executed, 0, i, bi)
+  /* First mark references for which store motion should be performed, as
+     this will enable load motion for further references.  */
+  EXECUTE_IF_AND_COMPL_IN_BITMAP (refs, lsm_executed, 0, i, bi)
     {
       ref = VEC_index (mem_ref_p, memory_accesses.refs_list, i);
       if (can_sm_ref_p (loop, ref))
 	bitmap_set_bit (refs_to_sm, i);
     }
+
+  /* Then mark references for which load motion should be performed, taking
+     into account the result of the above analysis.  */
+  EXECUTE_IF_AND_COMPL_IN_BITMAP (refs, lsm_executed, 0, i, bi)
+    {
+      ref = VEC_index (mem_ref_p, memory_accesses.refs_list, i);
+      if (can_lm_ref_p (loop, ref, refs_to_sm))
+	bitmap_set_bit (refs_to_lsm, i);
+    }
+
+  bitmap_ior_into (refs_to_lsm, refs_to_sm);
+  BITMAP_FREE (refs_to_sm);
 }
 
-/* Checks whether LOOP (with exits stored in EXITS array) is suitable
-   for a store motion optimization (i.e. whether we can insert statement
+/* Check whether LOOP (with exits stored in EXITS array) is suitable for
+   a load-store motion optimization (i.e. whether we can insert statement
    on its exits).  */
 
 static bool
-loop_suitable_for_sm (struct loop *loop ATTRIBUTE_UNUSED,
-		      VEC (edge, heap) *exits)
+loop_suitable_for_lsm (struct loop *loop ATTRIBUTE_UNUSED,
+		       VEC (edge, heap) *exits)
 {
   unsigned i;
   edge ex;
@@ -2457,44 +2758,44 @@ loop_suitable_for_sm (struct loop *loop
   return true;
 }
 
-/* Try to perform store motion for all memory references modified inside
-   LOOP.  SM_EXECUTED is the bitmap of the memory references for that
+/* Try to perform load-store motion for all memory references modified inside
+   LOOP.  LSM_EXECUTED is the bitmap of the memory references for which load-
    store motion was executed in one of the outer loops.  */
 
 static void
-store_motion_loop (struct loop *loop, bitmap sm_executed)
+load_store_motion_loop (struct loop *loop, bitmap lsm_executed)
 {
   VEC (edge, heap) *exits = get_loop_exit_edges (loop);
   struct loop *subloop;
-  bitmap sm_in_loop = BITMAP_ALLOC (NULL);
+  bitmap lsm_in_loop = BITMAP_ALLOC (NULL);
 
-  if (loop_suitable_for_sm (loop, exits))
+  if (loop_suitable_for_lsm (loop, exits))
     {
-      find_refs_for_sm (loop, sm_executed, sm_in_loop);
-      hoist_memory_references (loop, sm_in_loop, exits);
+      find_refs_for_lsm (loop, lsm_executed, lsm_in_loop);
+      hoist_memory_references (loop, lsm_in_loop, exits);
     }
   VEC_free (edge, heap, exits);
 
-  bitmap_ior_into (sm_executed, sm_in_loop);
+  bitmap_ior_into (lsm_executed, lsm_in_loop);
   for (subloop = loop->inner; subloop != NULL; subloop = subloop->next)
-    store_motion_loop (subloop, sm_executed);
-  bitmap_and_compl_into (sm_executed, sm_in_loop);
-  BITMAP_FREE (sm_in_loop);
+    load_store_motion_loop (subloop, lsm_executed);
+  bitmap_and_compl_into (lsm_executed, lsm_in_loop);
+  BITMAP_FREE (lsm_in_loop);
 }
 
-/* Try to perform store motion for all memory references modified inside
+/* Try to perform load-store motion for all memory references modified inside
    loops.  */
 
 static void
-store_motion (void)
+load_store_motion (void)
 {
   struct loop *loop;
-  bitmap sm_executed = BITMAP_ALLOC (NULL);
+  bitmap lsm_executed = BITMAP_ALLOC (NULL);
 
   for (loop = current_loops->tree_root->inner; loop != NULL; loop = loop->next)
-    store_motion_loop (loop, sm_executed);
+    load_store_motion_loop (loop, lsm_executed);
 
-  BITMAP_FREE (sm_executed);
+  BITMAP_FREE (lsm_executed);
   gsi_commit_edge_inserts ();
 }
 
@@ -2652,9 +2953,9 @@ tree_ssa_lim (void)
      invariant and cost for computing the invariant.  */
   determine_invariantness ();
 
-  /* Execute store motion.  Force the necessary invariants to be moved
+  /* Execute load-store motion.  Force the necessary invariants to be moved
      out of the loops as well.  */
-  store_motion ();
+  load_store_motion ();
 
   /* Move the expressions that are expensive enough.  */
   todo = move_computations ();
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */

#define SIZE 64

int foo (int v[], int a)
{
  int r, i;

  for (i = 0; i < SIZE; i++)
    r = v[i] + a;

  return r;
}

/* { dg-final { scan-tree-dump "MEM\\\[.* \\+ 252B\\\]" "optimized"} } */
/* { dg-final { cleanup-tree-dump "optimized" } } */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */

#define SIZE 64

int foo (int v1[], int v2[])
{
  int r, i, j;

  for (j = 0; j < SIZE; j++)
    for (i = 0; i < SIZE; i++)
      r = v1[j] + v2[i];

  return r;
}

/* { dg-final { scan-tree-dump "MEM\\\[.* \\+ 252B\\\]" "optimized"} } */
/* { dg-final { cleanup-tree-dump "optimized" } } */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */

#define SIZE 64

int foo (int v1[], int v2[])
{
  int r[SIZE];
  int i, j;

  for (j = 0; j < SIZE; j++)
    for (i = 0; i < SIZE; i++)
      r[j] = v1[j] + v2[i];

  return r[SIZE - 1];
}

/* { dg-final { scan-tree-dump "MEM\\\[.* \\+ 252B\\\]" "optimized"} } */
/* { dg-final { cleanup-tree-dump "optimized" } } */

Reply via email to