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;