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 <[email protected]>
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 <[email protected]>
* gcc.c-torture/execute/20111206-1.c: New test.
--
Eric Botcazou
/* PR tree-optimization/51315 */
/* Reported by Jurij Smakov <[email protected]> */
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;