Hi,

this is a regression present on mainline and 4.6 branch at -O for the SPARC.  
The compiler generates an unaligned access for the memcpy call in:

  char *s

  union
  {
    int32_t i;
    char a[sizeof (int32_t)];
  }
  v;

  memcpy (v.a, s, sizeof (int32_t));

The memcpy call is folded to an assignment between a couple of MEM_REFs:

  MEM[(char * {ref-all})&v] = MEM[(char * {ref-all})s_1];

with type 'char a[4]'.  The problem is that SRA turns this into:

  v$i_27 = MEM[(char * {ref-all})s_1].i;

but s isn't aligned enough for an int32_t access.


I don't think we can scalarize in this case on strict-alignment targets.  The 
SRA pass already contains an alignment check, but it is purely internal to the 
RHS and this is clearly not sufficient anymore with MEM_REFs.

The proposed fix is to enhance this alignment check to take into account both 
the RHS and the LHS (much like the memcpy folder).  I think it subsumes the 
previous check, which could be viewed as a check involving the RHS and the 
type of the LHS.  But there is a hitch: get_object_alignment is conservative 
for MEM_REF (unlike for INDIRECT_REF) so a trick is needed in order not to 
pessimize in some cases.

Tested on SPARC/Solaris.  OK for mainline and 4.6 branch?


2011-12-06  Eric Botcazou  <ebotca...@adacore.com>

        PR tree-optimization/51315
        * tree-sra.c (tree_non_mode_aligned_mem_p): Rename to...
        (tree_non_aligned_mem_p): ...this.  Add ALIGN parameter.  Look into
        MEM_REFs and deal with simple dereferences specially.
        (build_accesses_from_assign): Adjust for above change.
        (access_precludes_ipa_sra_p): Likewise.


2011-12-06  Eric Botcazou  <ebotca...@adacore.com>

        * gcc.c-torture/execute/20111206-1.c: New test.


-- 
Eric Botcazou
/* PR tree-optimization/51315 */
/* Reported by Jurij Smakov <ju...@wooyd.org> */

typedef unsigned int size_t;

extern void *memcpy (void *__restrict __dest,
       __const void *__restrict __src, size_t __n)
     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 2)));

extern size_t strlen (__const char *__s)
     __attribute__ ((__nothrow__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));

typedef short int int16_t;
typedef int int32_t;

extern void abort (void);

int a;

static void __attribute__ ((noinline,noclone))
do_something (int item)
{
  a = item;
}

int
pack_unpack (char *s, char *p)
{
  char *send, *pend;
  char type;
  int integer_size;

  send = s + strlen (s);
  pend = p + strlen (p);

  while (p < pend)
    {
      type = *p++;

      switch (type)
 {
 case 's':
   integer_size = 2;
   goto unpack_integer;

 case 'l':
   integer_size = 4;
   goto unpack_integer;

 unpack_integer:
   switch (integer_size)
     {
     case 2:
       {
  union
  {
    int16_t i;
    char a[sizeof (int16_t)];
  }
  v;
  memcpy (v.a, s, sizeof (int16_t));
  s += sizeof (int16_t);
  do_something (v.i);
       }
       break;

     case 4:
       {
  union
  {
    int32_t i;
    char a[sizeof (int32_t)];
  }
  v;
  memcpy (v.a, s, sizeof (int32_t));
  s += sizeof (int32_t);
  do_something (v.i);
       }
       break;
     }
   break;
 }
    }
  return (int) *s;
}

int
main (void)
{
  int n = pack_unpack ("\200\001\377\376\035\300", "sl");
  if (n != 0)
    abort ();
  return 0;
}
Index: tree-sra.c
===================================================================
--- tree-sra.c	(revision 181993)
+++ tree-sra.c	(working copy)
@@ -1067,26 +1067,31 @@ disqualify_ops_if_throwing_stmt (gimple
   return false;
 }
 
-/* Return true iff type of EXP is not sufficiently aligned.  */
+/* Return true if EXP is a memory reference less aligned than ALIGN.  This is
+   invoked only on strict-alignment targets.  */
 
 static bool
-tree_non_mode_aligned_mem_p (tree exp)
+tree_non_aligned_mem_p (tree exp, unsigned int align)
 {
-  enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-  unsigned int align;
+  unsigned int exp_align;
 
   if (TREE_CODE (exp) == VIEW_CONVERT_EXPR)
     exp = TREE_OPERAND (exp, 0);
 
-  if (TREE_CODE (exp) == SSA_NAME
-      || TREE_CODE (exp) == MEM_REF
-      || mode == BLKmode
-      || is_gimple_min_invariant (exp)
-      || !STRICT_ALIGNMENT)
+  if (TREE_CODE (exp) == SSA_NAME || is_gimple_min_invariant (exp))
     return false;
 
-  align = get_object_alignment (exp);
-  if (GET_MODE_ALIGNMENT (mode) > align)
+  /* get_object_alignment will fall back to BITS_PER_UNIT if it cannot
+     compute an explicit alignment.  Pretend that dereferenced pointers
+     are always aligned on strict-alignment targets.  */
+  if (TREE_CODE (exp) == MEM_REF
+      && TREE_CODE (TREE_OPERAND (exp, 0)) == SSA_NAME
+      && integer_zerop (TREE_OPERAND (exp, 1)))
+    exp_align = TYPE_ALIGN (TREE_TYPE (exp));
+  else
+    exp_align = get_object_alignment (exp);
+
+  if (exp_align < align)
     return true;
 
   return false;
@@ -1120,7 +1125,9 @@ build_accesses_from_assign (gimple stmt)
   if (lacc)
     {
       lacc->grp_assignment_write = 1;
-      lacc->grp_unscalarizable_region |= tree_non_mode_aligned_mem_p (rhs);
+      if (STRICT_ALIGNMENT
+	  && tree_non_aligned_mem_p (rhs, get_object_alignment (lhs)))
+        lacc->grp_unscalarizable_region = 1;
     }
 
   if (racc)
@@ -1129,7 +1136,9 @@ build_accesses_from_assign (gimple stmt)
       if (should_scalarize_away_bitmap && !gimple_has_volatile_ops (stmt)
 	  && !is_gimple_reg_type (racc->type))
 	bitmap_set_bit (should_scalarize_away_bitmap, DECL_UID (racc->base));
-      racc->grp_unscalarizable_region |= tree_non_mode_aligned_mem_p (lhs);
+      if (STRICT_ALIGNMENT
+	  && tree_non_aligned_mem_p (lhs, get_object_alignment (rhs)))
+        racc->grp_unscalarizable_region = 1;
     }
 
   if (lacc && racc
@@ -3705,7 +3714,8 @@ access_precludes_ipa_sra_p (struct acces
 	  || gimple_code (access->stmt) == GIMPLE_ASM))
     return true;
 
-  if (tree_non_mode_aligned_mem_p (access->expr))
+  if (STRICT_ALIGNMENT
+      && tree_non_aligned_mem_p (access->expr, TYPE_ALIGN (access->type)))
     return true;
 
   return false;

Reply via email to