PR tree-optimization/92765 - wrong code for strcmp of a union member gcc/testsuite/ChangeLog: PR tree-optimization/92765 * gcc.dg/strlenopt-92.c: New test. gcc/ChangeLog: PR tree-optimization/92765 * tree-ssa-strlen.c (component_ref_via_union_p): New function. (determine_min_objsize): Call it. Use the maximum object size for PHI arguments. diff --git a/gcc/testsuite/gcc.dg/strlenopt-92.c b/gcc/testsuite/gcc.dg/strlenopt-92.c new file mode 100644 index 00000000000..44ad5b88854 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-92.c @@ -0,0 +1,94 @@ +/* PR tree-optimization/92765 - wrong code for strcmp of a union member + { dg-do run } + { dg-options "-O2 -Wall" } */ + +#include "strlenopt.h" + +extern void free (void*); +extern void* calloc (size_t, size_t); +extern void* malloc (size_t); + + +/* Test from comment #0. */ +struct S0 { + char a[2]; +}; + +union U0 { + char b[4]; + struct S0 s; +}; + +union U0 u0; +union U0 *p0 = &u0; + +int test_0 () +{ + u0.b[0] = 'a'; + u0.b[1] = 'b'; + u0.b[2] = '\0'; + + int x = memcmp (p0->s.a, "x", 2); + + if (strcmp (p0->b, "ab")) + abort (); + + return x; +} + + +/* Test from comment #6. */ +union U1 { struct S1 { char a[2]; char b[2]; char c[2]; } s; char d[6]; } u1; + +__attribute__((noipa)) void +barrier (char *p) +{ + asm volatile ("" : : "g" (p) : "memory"); +} + +__attribute__((noipa)) void +test_1 (union U1 *x) +{ + char *p = (char *) &x->s.b; + barrier (p); + if (strcmp (&x->d[2], "cde")) + abort (); +} + +/* Test from comment #7. */ + +__attribute__((noipa)) int +barrier_copy (char *x, int y) +{ + asm volatile ("" : : "g" (x), "g" (y) : "memory"); + if (y == 0) + strcpy (x, "abcd"); + return y; +} + +__attribute__((noipa)) char * +test_2 (int x) +{ + char *p; + if (x) + p = malloc (2); + else + p = calloc (16, 1); + if (barrier_copy (p, x)) + return p; + if (strcmp (p, "abcd") != 0) + abort (); + return p; +} + + +int main (void) +{ + test_0 (); + + strcpy (u1.d, "abcde"); + test_1 (&u1); + + free (test_2 (0)); + free (test_2 (1)); +} diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index ad9e98973b1..f4b6aadae47 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -4087,6 +4087,29 @@ compute_string_length (int idx) return string_leni; } +/* Returns true if REF is a reference to a member of a union type, + or a member of such a type (traversing all references along + the path). Used to avoid making assumptions about accesses + to members that could also be accessed by other members of + incompatible types. */ + +static bool +component_ref_via_union_p (tree ref) +{ + if (TREE_CODE (ref) == ADDR_EXPR) + ref = TREE_OPERAND (ref, 0); + + while (TREE_CODE (ref) == MEM_REF || handled_component_p (ref)) + { + tree type = TREE_TYPE (ref); + if (TREE_CODE (type) == UNION_TYPE) + return true; + ref = TREE_OPERAND (ref, 0); + } + + return false; +} + /* Determine the minimum size of the object referenced by DEST expression which must have a pointer type. Return the minimum size of the object if successful or HWI_M1U when @@ -4099,14 +4122,18 @@ determine_min_objsize (tree dest) init_object_sizes (); - if (compute_builtin_object_size (dest, 2, &size)) - return size; - /* Try to determine the size of the object through the RHS of the assign statement. */ if (TREE_CODE (dest) == SSA_NAME) { gimple *stmt = SSA_NAME_DEF_STMT (dest); + + /* Determine the size of the largest object when DEST refers + to two or more via a PHI, otherwise the smallest. */ + int ostype = gimple_code (stmt) == GIMPLE_PHI ? 0 : 2; + if (compute_builtin_object_size (dest, ostype, &size)) + return size; + if (!is_gimple_assign (stmt)) return HOST_WIDE_INT_M1U; @@ -4118,6 +4145,10 @@ determine_min_objsize (tree dest) return determine_min_objsize (dest); } + /* Try to determine the size of the referenced object itself. */ + if (compute_builtin_object_size (dest, 2, &size)) + return size; + /* Try to determine the size of the object from its type. */ if (TREE_CODE (dest) != ADDR_EXPR) return HOST_WIDE_INT_M1U; @@ -4129,11 +4160,13 @@ determine_min_objsize (tree dest) type = TYPE_MAIN_VARIANT (type); /* The size of a flexible array cannot be determined. Otherwise, - for arrays with more than one element, return the size of its - type. GCC itself misuses arrays of both zero and one elements - as flexible array members so they are excluded as well. */ + unless the reference involves a union, for arrays with more than + one element, return the size of its type. GCC itself misuses + arrays of both zero and one elements as flexible array members + so they are excluded as well. */ if (TREE_CODE (type) != ARRAY_TYPE - || !array_at_struct_end_p (dest)) + || (!component_ref_via_union_p (dest) + && !array_at_struct_end_p (dest))) { tree type_size = TYPE_SIZE_UNIT (type); if (type_size && TREE_CODE (type_size) == INTEGER_CST