tree-ssa-loop-memset.c   [plain text]


/* APPLE LOCAL begin loops-to-memset  (ENTIRE FILE!)  */
/*  Loops to memset.
    Copyright (C) 2004 Free Software Foundation, Inc.
    Contributed by Andrew Pinski <apinski@apple.com>.
  
    This file is part of GCC.
    
    GCC is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
    Free Software Foundation; either version 2, or (at your option) any
    later version.
    
    GCC is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.
    
    You should have received a copy of the GNU General Public License
    along with GCC; see the file COPYING.  If not, write to the Free
    Software Foundation, 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "output.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-dump.h"
#include "timevar.h"
#include "cfgloop.h"
#include "domwalk.h"
#include "params.h"
#include "tree-pass.h"
#include "flags.h"
#include "tree-data-ref.h"
#include "tree-scalar-evolution.h"
#include "tree-chrec.h"
#include "tree-vectorizer.h"

static bool memset_debug_stats (struct loop *loop);
static bool memset_debug_details (struct loop *loop);

/* Function memset_analyze_data_refs.

   Find all the data references in the loop.

   Handle INDIRECT_REFs and one dimensional ARRAY_REFs 
   which base is really an array (not a pointer).

   This is different from vect_analyze_data_refs in tree-vectorizer.c as
   we handle unaligned data stores and we only handle data stores.  */

static bool
memset_analyze_data_refs (loop_vec_info loop_vinfo)
{
  struct loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
  basic_block *bbs = LOOP_VINFO_BBS (loop_vinfo);
  int nbbs = loop->num_nodes;
  block_stmt_iterator si;
  int j;
  struct data_reference *dr;
  
  if (memset_debug_details (NULL))
    fprintf (dump_file, "\n<<memset_analyze_data_refs>>\n");
  
  for (j = 0; j < nbbs; j++)
    {
      basic_block bb = bbs[j];
      for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
	{
	  tree stmt = bsi_stmt (si);
	  stmt_vec_info stmt_info = vinfo_for_stmt (stmt);
	  v_may_def_optype v_may_defs = STMT_V_MAY_DEF_OPS (stmt);
	  v_must_def_optype v_must_defs = STMT_V_MUST_DEF_OPS (stmt);
	  vuse_optype vuses = STMT_VUSE_OPS (stmt);
	  varray_type *datarefs = NULL;
	  int nvuses, nv_may_defs, nv_must_defs;
	  tree memref = NULL;
	  tree array_base;
	  tree symbl;
	  
	  /* Assumption: there exists a data-ref in stmt, if and only if 
	     it has vuses/vdefs.  */
	  
	  if (!vuses && !v_may_defs && !v_must_defs)
	    continue;
	  
	  nvuses = NUM_VUSES (vuses);
	  nv_may_defs = NUM_V_MAY_DEFS (v_may_defs);
	  nv_must_defs = NUM_V_MUST_DEFS (v_must_defs);
	  
	  if (nvuses && (nv_may_defs || nv_must_defs))
	    {
	      if (memset_debug_details (NULL))
		{
		  fprintf (dump_file, "unexpected vdefs and vuses in stmt: ");
		  print_generic_expr (dump_file, stmt, TDF_SLIM);
		}
	      return false;
	    }
	  
	  if (TREE_CODE (stmt) != MODIFY_EXPR)
	    {
	      if (memset_debug_details (NULL))
		{
		  fprintf (dump_file, "unexpected vops in stmt: ");
		  print_generic_expr (dump_file, stmt, TDF_SLIM);
		}
	      return false;
	    }
	  
	  if (vuses)
	    {
	      if (memset_debug_details (NULL))
		{
		  fprintf (dump_file, "Memory access in the loop: ");
		  print_generic_expr (dump_file, stmt, TDF_SLIM);
		}
	      return false;
	    } 
	  else /* vdefs */
	    {
	      memref = TREE_OPERAND (stmt, 0);
	      datarefs = &(LOOP_VINFO_DATAREF_WRITES (loop_vinfo));
	    }
	  
	  if (TREE_CODE (memref) == INDIRECT_REF)
            {
	      /* MERGE FIXME */
	      abort ();
	      /* dr = vect_analyze_pointer_ref_access (memref, stmt, false); */
	      if (! dr)
		return false; 
	      symbl = DR_BASE_NAME (dr);	
            }
	  else if (TREE_CODE (memref) == ARRAY_REF)
	    {
	      tree base;		
	      array_base = TREE_OPERAND (memref, 0);
	      
	      if (TREE_CODE (array_base) == ARRAY_REF)
                {
		  if (memset_debug_stats (loop) || memset_debug_details (loop))
		    {
		      fprintf (dump_file, 
			       "not vectorized: multi-dimensional array.");
		      print_generic_expr (dump_file, stmt, TDF_SLIM);
		    }
		  return false;
                }
	      
	      dr = analyze_array (stmt, memref, false);
	      
	      /* Find the relevant symbol for aliasing purposes.  */	
	      base = DR_BASE_NAME (dr);
	      switch (TREE_CODE (base))	
		{
		case VAR_DECL:
		  symbl = base;
		  break;
		default:
		  if (memset_debug_stats (loop) 
		      || memset_debug_details (loop))
		    {
		      fprintf (dump_file,
			       "not transformed: unhandled struct/class field access ");
		      print_generic_expr (dump_file, stmt, TDF_SLIM);
		    }
		  return false;
		} /* switch */
	    }
	  else
	    {
	      if (memset_debug_stats (loop) || memset_debug_details (loop))
		{
		  fprintf (dump_file, "not transformed: unhandled data ref: ");
		  print_generic_expr (dump_file, stmt, TDF_SLIM);
		}
	      return false;
	    }
		
	  /* Find and record the memtag assigned to this data-ref.  */
	  if (TREE_CODE (symbl) == VAR_DECL 
	      || (TREE_CODE (symbl) == COMPONENT_REF 
		  && TREE_CODE (TREE_OPERAND (symbl, 0)) == VAR_DECL))
	    STMT_VINFO_MEMTAG (stmt_info) = symbl;
	  else if (TREE_CODE (symbl) == SSA_NAME)
	    {
	      tree tag;
	      symbl = SSA_NAME_VAR (symbl);
	      tag = get_var_ann (symbl)->type_mem_tag;
	      if (!tag)
		{
		  tree ptr = TREE_OPERAND (memref, 0);
		  if (TREE_CODE (ptr) == SSA_NAME)
		    tag = get_var_ann (SSA_NAME_VAR (ptr))->type_mem_tag;
		}
	      if (!tag)
		{
		  if (memset_debug_stats (loop) || memset_debug_details (loop))
		    fprintf (dump_file, "not vectorized: no memtag for ref.");
		  return false;
		}
	      STMT_VINFO_MEMTAG (stmt_info) = tag;
	    }
	  else
	    {
	      if (memset_debug_stats (loop) || memset_debug_details (loop))
		{
		  fprintf (dump_file, "not vectorized: unsupported data-ref: ");
		  print_generic_expr (dump_file, memref, TDF_SLIM);
		}
	      return false;
	    }
	  
	  VARRAY_PUSH_GENERIC_PTR (*datarefs, dr);
	  STMT_VINFO_DATA_REF (stmt_info) = dr;
	}
    }
  
  return true;
}

/* Function memset_analyze_loop_with_symbolic_num_of_iters.

   In case the number of iterations that LOOP iterates in unknown at compile 
   time, an epilog loop will be generated, and the loop induction variables 
   (IVs) will be "advanced" to the value they are supposed to take just before 
   the epilog loop. Here we check that the access function of the loop IVs
   and the expression that represents the loop bound are simple enough.
   These restrictions will be relxed in the future.  */

static bool 
memset_analyze_loop_with_symbolic_num_of_iters (tree niters, 
					        struct loop *loop)
{
  basic_block bb = loop->header;
  tree phi;

  if (memset_debug_details (NULL))
    fprintf (dump_file, 
	     "\n<<memset_analyze_loop_with_symbolic_num_of_iters>>\n");
  
  if (chrec_contains_undetermined (niters))
    {
      if (memset_debug_details (NULL))
        fprintf (dump_file, "Infinite number of iterations.");
      return false;
    }

  if (!niters)
    {
      if (memset_debug_details (NULL))
        fprintf (dump_file, "niters is NULL pointer.");
      return false;
    }

  if (memset_debug_details (NULL))
    {
      fprintf (dump_file, "Symbolic number of iterations is ");
      print_generic_expr (dump_file, niters, TDF_DETAILS);
    }
   
  /* Analyze phi functions of the loop header.  */

  for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
    {
      tree access_fn = NULL;
      tree evolution_part;

      if (memset_debug_details (NULL))
	{
          fprintf (dump_file, "Analyze phi: ");
          print_generic_expr (dump_file, phi, TDF_SLIM);
	}

      /* Skip virtual phi's. The data dependences that are associated with
         virtual defs/uses (i.e., memory accesses) are analyzed elsewhere.  */

      if (!is_gimple_reg (SSA_NAME_VAR (PHI_RESULT (phi))))
	{
	  if (memset_debug_details (NULL))
	    fprintf (dump_file, "virtual phi. skip.");
	  continue;
	}

      /* Analyze the evolution function.  */

      access_fn = instantiate_parameters
	(loop, analyze_scalar_evolution (loop, PHI_RESULT (phi)));

      if (!access_fn)
	{
	  if (memset_debug_details (NULL))
	    fprintf (dump_file, "No Access function.");
	  return false;
	}

      if (memset_debug_details (NULL))
        {
	  fprintf (dump_file, "Access function of PHI: ");
	  print_generic_expr (dump_file, access_fn, TDF_SLIM);
        }

      evolution_part = evolution_part_in_loop_num (access_fn, loop->num);
      
      if (evolution_part == NULL_TREE)
	return false;
  
      /* FORNOW: We do not transform initial conditions of IVs 
	 which evolution functions are a polynomial of degree >= 2.  */

      if (tree_is_chrec (evolution_part))
	return false;  
    }

  return  true;
}


/* Function debug_loop_details.

   For memset debug dumps.  */

static bool
memset_debug_details (struct loop *loop)
{
   basic_block bb;
   block_stmt_iterator si;
   tree node = NULL_TREE;

  if (!dump_file || !(dump_flags & TDF_DETAILS))
    return false;

  if (!loop)
    {
      fprintf (dump_file, "\n");
      return true;
    }

  if (!loop->header)
    return false;

  bb = loop->header;

  for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
    {
      node = bsi_stmt (si);
      if (node && EXPR_P (node) && EXPR_LOCUS (node))
	break;
    }

  if (node && EXPR_P (node) && EXPR_LOCUS (node)
      && EXPR_FILENAME (node) && EXPR_LINENO (node))
    {
      fprintf (dump_file, "\nloop at %s:%d: ", 
               EXPR_FILENAME (node), EXPR_LINENO (node));
      return true;
    }

  return false;
}

/* Function debug_loop_stats.

   For vectorization statistics dumps.  */

static bool
memset_debug_stats (struct loop *loop)
{
  basic_block bb;
  block_stmt_iterator si;
  tree node = NULL_TREE;

  if (!dump_file || !(dump_flags & TDF_STATS))
    return false;

  if (!loop)
    {
      fprintf (dump_file, "\n");
      return true;
    }

  if (!loop->header)
    return false;

  bb = loop->header;

  for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
    {
      node = bsi_stmt (si);
      if (node && EXPR_P (node) && EXPR_LOCUS (node))
        break;
    }

  if (node && EXPR_P (node) && EXPR_LOCUS (node) 
      && EXPR_FILENAME (node) && EXPR_LINENO (node))
    {
      fprintf (dump_file, "\nloop at %s:%d: ", 
	EXPR_FILENAME (node), EXPR_LINENO (node));
      return true;
    }

  return false;
}

/* Function memset_analyze_loop_form.

   Verify the following restrictions:
   - it's an inner-most loop
   - number of BBs = 2 (which are the loop header and the latch)
   - the loop has a pre-header
   - the loop has a single entry and exit
   - the loop exit condition is simple enough, and the number of iterations
     can be analyzed (a countable loop).

   This differs from vect_analyze_loop_form by we handle non constant
   interations.  */

static loop_vec_info
memset_analyze_loop_form (struct loop *loop)
{
  loop_vec_info loop_vinfo;
  tree loop_cond;
  tree number_of_iterations = NULL_TREE;

  if (memset_debug_details (loop))
    fprintf (dump_file, "\n<<memset_analyze_loop_form>>\n");

  if (loop->level > 1		/* FORNOW: inner-most loop  */
      || loop->num_exits > 1 || loop->num_entries > 1 || loop->num_nodes != 2
      || !loop->pre_header || !loop->header || !loop->latch)
    {
      if (memset_debug_stats (loop) || memset_debug_details (loop))	
	{
	  fprintf (dump_file, "not vectorized: bad loop form.\n");
	  if (loop->level > 1)
	    fprintf (dump_file, "nested loop.\n");
	  else if (loop->num_exits > 1 || loop->num_entries > 1)
	    fprintf (dump_file, "multiple entries or exits.\n");
	  else if (loop->num_nodes != 2 || !loop->header || !loop->latch)
	    fprintf (dump_file, "too many BBs in loop.\n");
	  else if (!loop->pre_header)
	    fprintf (dump_file, "no pre-header BB for loop.\n");
	}
      
      return NULL;
    }

  /* We assume that the loop exit condition is at the end of the loop. i.e,
     that the loop is represented as a do-while (with a proper if-guard
     before the loop if needed), where the loop header contains all the
     executable statements, and the latch is empty.  */
  if (!empty_block_p (loop->latch))
    {
      if (memset_debug_stats (loop) || memset_debug_details (loop))     
        fprintf (dump_file, "not vectorized: unexpectd loop form.");
      return NULL;
    }

  if (empty_block_p (loop->header))
    {
      if (memset_debug_stats (loop) || memset_debug_details (loop))     
        fprintf (dump_file, "not transformed: empty loop.");
      return NULL;
    }

  loop_cond = vect_get_loop_niters (loop, &number_of_iterations);
  if (!loop_cond)
    {
      if (memset_debug_stats (loop) || memset_debug_details (loop))
	fprintf (dump_file, "not vectorized: complicated exit condition.\n");
      return NULL;
    }
    
  if (!number_of_iterations) 
    {
      if (memset_debug_stats (loop) || memset_debug_details (loop))
	fprintf (dump_file, 
		 "not vectorized: number of iterations cannot be computed.");
      return NULL;
    }

  loop_vinfo = new_loop_vec_info (loop);
  LOOP_VINFO_NITERS (loop_vinfo) = number_of_iterations;
  
  if (!LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo))
    {
      if (memset_debug_details (NULL))
	fprintf (dump_file, "loop bound unknown.");

      /* Unknown loop bound.  */
      if (!memset_analyze_loop_with_symbolic_num_of_iters (number_of_iterations,
							   loop))
	{
          if (memset_debug_stats (loop) || memset_debug_details (loop))
	    fprintf (dump_file, "not transformed: can't determine loop bound.\n");
	  return NULL;
	}
      else
	{
	  /* We need only one loop entry for unknown loop bound support.  */
	  if (loop->num_entries != 1 || !loop->pre_header)
	    {	      
	      if (memset_debug_stats (loop) || memset_debug_details (loop))
		fprintf (dump_file, 
			 "not transformed: more than one loop entry.");
	      return NULL;
	    }
	}
    }
  else if (LOOP_VINFO_INT_NITERS (loop_vinfo) == 0)
    {
      if (memset_debug_stats (loop) || memset_debug_details (loop))
	fprintf (dump_file, "not transformed: number of iterations = 0.\n");
      return NULL;
    }

  LOOP_VINFO_EXIT_COND (loop_vinfo) = loop_cond;

  return loop_vinfo;
}

/* Mark all the variables in V_MAY_DEF or V_MUST_DEF operands for STMT for
   renaming. This becomes necessary when we modify all of a non-scalar.  */

static void
mark_all_v_defs (tree stmt)
{
  tree sym;
  ssa_op_iter iter;

  get_stmt_operands (stmt);

  FOR_EACH_SSA_TREE_OPERAND (sym, stmt, iter, SSA_OP_VIRTUAL_DEFS)
    { 
      if (TREE_CODE (sym) == SSA_NAME) 
	sym = SSA_NAME_VAR (sym);
      bitmap_set_bit (vars_to_rename, var_ann (sym)->uid);
    }
}


/* This is the main entry point for the transformation.  */
void
tree_ssa_memset (struct loops *loops)
{
  unsigned i;
  
  for (i = 1; i < loops->num; i++)
    {
      struct loop *loop = loops->parray[i];
      loop_vec_info vectorizer_info;
      varray_type writes;
      struct data_reference *drw;
      tree access_chrec;
      tree noi;
      
      if (!loop)
        continue;
      
      flow_loop_scan (loop, LOOP_ALL);
      vectorizer_info = memset_analyze_loop_form (loop);
      if (!vectorizer_info)
        continue;
      
      if (!memset_analyze_data_refs (vectorizer_info))
	{
	  if (memset_debug_details (loop))
	    fprintf (dump_file, "bad data references.");
	  destroy_loop_vec_info (vectorizer_info);
	  continue;
	}
      
      writes = LOOP_VINFO_DATAREF_WRITES (vectorizer_info);
      
      /* TODO: handle more than data write.  */
      if (VARRAY_ACTIVE_SIZE (writes) != 1)
	{
	  if (memset_debug_details (loop))
	    fprintf (dump_file, "no or more than one store.");
	  destroy_loop_vec_info (vectorizer_info);
	  continue;
	}
      
      drw = VARRAY_GENERIC_PTR (writes, 0);
      
      /* TODO: handle multi-dimension arrays.  */
      if (DR_NUM_DIMENSIONS (drw) != 1)
	{
	  if (memset_debug_details (loop))
	    fprintf (dump_file, "cannot handle multiple dimension array.");
	  destroy_loop_vec_info (vectorizer_info);
	  continue;
	}
      
      if (TREE_CODE (TREE_OPERAND (DR_STMT (drw), 1)) != INTEGER_CST)
	{
	  if (memset_debug_details (loop))
	    fprintf (dump_file, "non constant store value.");
	  destroy_loop_vec_info (vectorizer_info);
	  continue;
	}
      
      /* TODO: handle other than zero values in types
         where the unit size is greater than one. */
      if (!integer_zerop (TREE_OPERAND (DR_STMT (drw), 1))
	  && !integer_onep (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (drw)))))
	{
	  if (memset_debug_details (loop))
	    fprintf (dump_file, "cannot handle other zero value for types of other than char (for now).");
	  destroy_loop_vec_info (vectorizer_info);
	  continue;
	}
      
      access_chrec = DR_ACCESS_FN (drw, 0);
      
      noi = LOOP_VINFO_NITERS (vectorizer_info);      
      
      /* Build the memset call.  */
      {
        tree array = DR_BASE_NAME (drw);
	tree value = TREE_OPERAND (DR_STMT (drw), 1);
	tree function = implicit_built_in_decls[BUILT_IN_MEMSET];
	tree args = NULL_TREE;
	block_stmt_iterator bsi = bsi_last (loop->pre_header);
	tree array_1 = make_rename_temp (ptr_type_node, NULL);
	tree temp, stmt, var;
	tree ni_name;
	
	stmt = DR_STMT (drw);
	
	/* Remove the array access stmt. */
	{
	  block_stmt_iterator access_bsi;
	  /* Mark the MAY_DEF as needed to be renamed. */
	  mark_all_v_defs (stmt);
	  access_bsi = bsi_for_stmt (stmt);
	  bsi_remove (&access_bsi);
	}
	
	if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE)
	  {
	    tree type = TREE_TYPE (TREE_TYPE (array));
	    tree base = array;
	    
	    while (TREE_CODE (base) == REALPART_EXPR
		   || TREE_CODE (base) == IMAGPART_EXPR
		   || handled_component_p (base))
	      base = TREE_OPERAND (base, 0);
	    
	    if (DECL_P (base))
	      TREE_ADDRESSABLE (base) = 1;
	    
	    array = build4 (ARRAY_REF, type, array, size_zero_node,
	                    NULL_TREE, NULL_TREE);
	    array = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (array)),
	                    array);
	  }
	
	{
	  tree start = CHREC_LEFT (access_chrec);
	  tree size_mult;
	  tree array_var;
	  start = fold_convert (TREE_TYPE (array), start);
	  size_mult = fold_convert (TREE_TYPE (array),
			            TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (array))));
	  array = fold (build2 (PLUS_EXPR, 
				TREE_TYPE (array),
				array, 
				fold (build2 (MULT_EXPR, 
					      TREE_TYPE (array), 
					      start, 
					      size_mult))));
	  array_var = create_tmp_var (TREE_TYPE (array), "tmp");
	  add_referenced_tmp_var (array_var);
	  array = force_gimple_operand (array, &stmt, false, array_var);

	  if (stmt)
	    bsi_insert_after (&bsi, stmt, BSI_CONTINUE_LINKING);
	}
	
	var = create_tmp_var (size_type_node, "tmp");
	add_referenced_tmp_var (var);
	
	noi = fold (build2 (MULT_EXPR, TREE_TYPE (noi), noi,
	                    fold_convert (TREE_TYPE (noi),
			                  TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (array))))));

	stmt = NULL_TREE;
	ni_name = force_gimple_operand (noi, &stmt, false, var);

	if (stmt)
	  bsi_insert_after (&bsi, stmt, BSI_CONTINUE_LINKING);
      
	temp = build2 (MODIFY_EXPR, void_type_node, array_1,
		       array);
	
	bsi_insert_after (&bsi, temp, BSI_CONTINUE_LINKING);
	
	args = tree_cons (NULL, ni_name, args);
	args = tree_cons (NULL, fold_convert (integer_type_node, value), args);
	args = tree_cons (NULL, array_1, args);
	
	temp = build_function_call_expr (function, args);
	
	bsi_insert_after (&bsi, temp, BSI_CONTINUE_LINKING);
      }
      
      destroy_loop_vec_info (vectorizer_info);
      
    }
  
  rewrite_into_ssa (false);
  if (!bitmap_empty_p (vars_to_rename))
    {
      /* The rewrite of ssa names may cause violation of loop closed ssa
         form invariants.  TODO -- avoid these rewrites completely.
         Information in virtual phi nodes is sufficient for it.  */
      rewrite_into_loop_closed_ssa (); 
    }
  bitmap_clear (vars_to_rename);
}

/* APPLE LOCAL end loops-to-memset (ENTIRE FILE!)  */