diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a5a83ef3dff5..2b9464a3d6df 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2014-02-08 Jakub Jelinek + + PR middle-end/60092 + * tree-ssa-ccp.c (surely_varying_stmt_p): Don't return true + if TYPE_ATTRIBUTES (gimple_call_fntype ()) contain + assume_aligned or alloc_align attributes. + (bit_value_assume_aligned): Add ATTR, PTRVAL and ALLOC_ALIGN + arguments. Handle also assume_aligned and alloc_align attributes. + (evaluate_stmt): Adjust bit_value_assume_aligned caller. + Handle calls to functions with assume_aligned or alloc_align + attributes. + * doc/extend.texi: Document assume_aligned and alloc_align + attributes. + 2014-02-08 Terry Guo * doc/invoke.texi: Document ARM -march=armv7e-m. diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 1e61bc341a7c..ba14b99da074 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,13 @@ +2014-02-08 Jakub Jelinek + + PR middle-end/60092 + * c-common.c (handle_alloc_size_attribute): Use tree_fits_uhwi_p + and tree_to_uhwi. + (handle_alloc_align_attribute, handle_assume_aligned_attribute): New + functions. + (c_common_attribute_table): Add alloc_align and assume_aligned + attributes. + 2014-02-06 Marek Polacek PR c/60087 diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 50cc848fe902..5cf285bc5827 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -366,6 +366,8 @@ static tree handle_warn_unused_result_attribute (tree *, tree, tree, int, static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *); static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *); static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *); +static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *); +static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *); static tree handle_target_attribute (tree *, tree, tree, int, bool *); static tree handle_optimize_attribute (tree *, tree, tree, int, bool *); static tree ignore_attribute (tree *, tree, tree, int, bool *); @@ -766,6 +768,10 @@ const struct attribute_spec c_common_attribute_table[] = handle_omp_declare_simd_attribute, false }, { "omp declare target", 0, 0, true, false, false, handle_omp_declare_target_attribute, false }, + { "alloc_align", 1, 1, false, true, true, + handle_alloc_align_attribute, false }, + { "assume_aligned", 1, 2, false, true, true, + handle_assume_aligned_attribute, false }, { NULL, 0, 0, false, false, false, NULL, false } }; @@ -8043,10 +8049,9 @@ handle_alloc_size_attribute (tree *node, tree ARG_UNUSED (name), tree args, && TREE_CODE (position) != FUNCTION_DECL) position = default_conversion (position); - if (TREE_CODE (position) != INTEGER_CST - || TREE_INT_CST_HIGH (position) - || TREE_INT_CST_LOW (position) < 1 - || TREE_INT_CST_LOW (position) > arg_count ) + if (!tree_fits_uhwi_p (position) + || !arg_count + || !IN_RANGE (tree_to_uhwi (position), 1, arg_count)) { warning (OPT_Wattributes, "alloc_size parameter outside range"); @@ -8057,6 +8062,55 @@ handle_alloc_size_attribute (tree *node, tree ARG_UNUSED (name), tree args, return NULL_TREE; } +/* Handle a "alloc_align" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_alloc_align_attribute (tree *node, tree, tree args, int, + bool *no_add_attrs) +{ + unsigned arg_count = type_num_arguments (*node); + tree position = TREE_VALUE (args); + if (position && TREE_CODE (position) != IDENTIFIER_NODE) + position = default_conversion (position); + + if (!tree_fits_uhwi_p (position) + || !arg_count + || !IN_RANGE (tree_to_uhwi (position), 1, arg_count)) + { + warning (OPT_Wattributes, + "alloc_align parameter outside range"); + *no_add_attrs = true; + return NULL_TREE; + } + return NULL_TREE; +} + +/* Handle a "assume_aligned" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_assume_aligned_attribute (tree *, tree, tree args, int, + bool *no_add_attrs) +{ + for (; args; args = TREE_CHAIN (args)) + { + tree position = TREE_VALUE (args); + if (position && TREE_CODE (position) != IDENTIFIER_NODE + && TREE_CODE (position) != FUNCTION_DECL) + position = default_conversion (position); + + if (TREE_CODE (position) != INTEGER_CST) + { + warning (OPT_Wattributes, + "assume_aligned parameter not integer constant"); + *no_add_attrs = true; + return NULL_TREE; + } + } + return NULL_TREE; +} + /* Handle a "fn spec" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index a42e90490c0b..a969fb4939c3 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -2154,8 +2154,8 @@ The keyword @code{__attribute__} allows you to specify special attributes when making a declaration. This keyword is followed by an attribute specification inside double parentheses. The following attributes are currently defined for functions on all targets: -@code{aligned}, @code{alloc_size}, @code{noreturn}, -@code{returns_twice}, @code{noinline}, @code{noclone}, +@code{aligned}, @code{alloc_size}, @code{alloc_align}, @code{assume_aligned}, +@code{noreturn}, @code{returns_twice}, @code{noinline}, @code{noclone}, @code{always_inline}, @code{flatten}, @code{pure}, @code{const}, @code{nothrow}, @code{sentinel}, @code{format}, @code{format_arg}, @code{no_instrument_function}, @code{no_split_stack}, @@ -2249,6 +2249,46 @@ declares that @code{my_calloc} returns memory of the size given by the product of parameter 1 and 2 and that @code{my_realloc} returns memory of the size given by parameter 2. +@item alloc_align +@cindex @code{alloc_align} attribute +The @code{alloc_align} attribute is used to tell the compiler that the +function return value points to memory, where the returned pointer minimum +alignment is given by one of the functions parameters. GCC uses this +information to improve pointer alignment analysis. + +The function parameter denoting the allocated alignment is specified by +one integer argument, whose number is the argument of the attribute. +Argument numbering starts at one. + +For instance, + +@smallexample +void* my_memalign(size_t, size_t) __attribute__((alloc_align(1))) +@end smallexample + +@noindent +declares that @code{my_memalign} returns memory with minimum alignment +given by parameter 1. + +@item assume_aligned +@cindex @code{assume_aligned} attribute +The @code{assume_aligned} attribute is used to tell the compiler that the +function return value points to memory, where the returned pointer minimum +alignment is given by the first argument. +If the attribute has two arguments, the second argument is misalignment offset. + +For instance + +@smallexample +void* my_alloc1(size_t) __attribute__((assume_aligned(16))) +void* my_alloc2(size_t) __attribute__((assume_aligned(32, 8))) +@end smallexample + +@noindent +declares that @code{my_alloc1} returns 16-byte aligned pointer and +that @code{my_alloc2} returns a pointer whose value modulo 32 is equal +to 8. + @item always_inline @cindex @code{always_inline} function attribute Generally, functions are not inlined unless optimization is specified. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index feca2e21e3a6..274081191219 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2014-02-08 Jakub Jelinek + + PR middle-end/60092 + * gcc.dg/attr-alloc_align-1.c: New test. + * gcc.dg/attr-alloc_align-2.c: New test. + * gcc.dg/attr-alloc_align-3.c: New test. + * gcc.dg/attr-assume_aligned-1.c: New test. + * gcc.dg/attr-assume_aligned-2.c: New test. + * gcc.dg/attr-assume_aligned-3.c: New test. + 2014-02-08 Jakub Jelinek PR target/60077 diff --git a/gcc/testsuite/gcc.dg/attr-alloc_align-1.c b/gcc/testsuite/gcc.dg/attr-alloc_align-1.c new file mode 100644 index 000000000000..45f5ec065e45 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_align-1.c @@ -0,0 +1,39 @@ +/* { dg-do compile } */ +/* { dg-options "-O3" } */ + +double *my_alloc1 (int len, int align) __attribute__((__alloc_align__ (2))); +double *my_alloc2 (int align, int len) __attribute__((alloc_align (1))); + +void +test1 (int len, int align) +{ + int i; + double *__restrict o1 = my_alloc1 (len, 32); + double *__restrict o2 = my_alloc1 (len, 32); + double *__restrict o3 = my_alloc1 (len, 32); + double *__restrict i1 = my_alloc1 (len, 32); + double *__restrict i2 = my_alloc1 (len, align); + for (i = 0; i < len; ++i) + { + o1[i] = i1[i] * i2[i]; + o2[i] = i1[i] + i2[i]; + o3[i] = i1[i] - i2[i]; + } +} + +void +test2 (int len, int align) +{ + int i; + double *__restrict o1 = my_alloc2 (32, len); + double *__restrict o2 = my_alloc2 (32, len); + double *__restrict o3 = my_alloc2 (32, len); + double *__restrict i1 = my_alloc2 (32, len); + double *__restrict i2 = my_alloc2 (align, len); + for (i = 0; i < len; ++i) + { + o1[i] = i1[i] * i2[i]; + o2[i] = i1[i] + i2[i]; + o3[i] = i1[i] - i2[i]; + } +} diff --git a/gcc/testsuite/gcc.dg/attr-alloc_align-2.c b/gcc/testsuite/gcc.dg/attr-alloc_align-2.c new file mode 100644 index 000000000000..3dc7a219839c --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_align-2.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ + +int i; +void *f1 (int) __attribute__((alloc_align (1))); +void *f2 (int, int, int) __attribute__((alloc_align (3))); +void *f3 (void) __attribute__((alloc_align)); /* { dg-error "wrong number of arguments specified" } */ +void *f4 (int, int) __attribute__((alloc_align (1, 2))); /* { dg-error "wrong number of arguments specified" } */ +void *f5 (void) __attribute__((alloc_align (i))); /* { dg-warning "outside range" } */ +void *f6 (int) __attribute__((alloc_align (0))); /* { dg-warning "outside range" } */ +void *f7 (int) __attribute__((alloc_align (2))); /* { dg-warning "outside range" } */ diff --git a/gcc/testsuite/gcc.dg/attr-alloc_align-3.c b/gcc/testsuite/gcc.dg/attr-alloc_align-3.c new file mode 100644 index 000000000000..2efe0c0684ab --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-alloc_align-3.c @@ -0,0 +1,56 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +char *my_alloc1 (int len, int align) __attribute__((__alloc_align__ (2))); +char *my_alloc2 (int align, int len) __attribute__((alloc_align (1))); + +int +test1 (int len) +{ + int i; + char *p = my_alloc1 (len, 32); + return ((__INTPTR_TYPE__) p) & 31; +} + +int +test2 (int len) +{ + int i; + char *p = my_alloc2 (32, len); + return ((__INTPTR_TYPE__) p) & 31; +} + +int +test3 (int len) +{ + int i; + char *p = my_alloc1 (len, 16); + return ((__INTPTR_TYPE__) p) & 15; +} + +int +test4 (int len) +{ + int i; + char *p = my_alloc2 (16, len); + return ((__INTPTR_TYPE__) p) & 15; +} + +int +test5 (int len, int align) +{ + int i; + char *p = my_alloc1 (len, align); + return ((__INTPTR_TYPE__) p) & 15; +} + +int +test6 (int len, int align) +{ + int i; + char *p = my_alloc2 (align, len); + return ((__INTPTR_TYPE__) p) & 15; +} + +/* { dg-final { scan-tree-dump-times "return 0" 4 "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/attr-assume_aligned-1.c b/gcc/testsuite/gcc.dg/attr-assume_aligned-1.c new file mode 100644 index 000000000000..9a7d5a1ffe6e --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-assume_aligned-1.c @@ -0,0 +1,39 @@ +/* { dg-do compile } */ +/* { dg-options "-O3" } */ + +double *my_alloc1 (int len) __attribute__((__assume_aligned__ (16))); +double *my_alloc2 (int len) __attribute__((__assume_aligned__ (32, 16))); + +void +test1 (int len) +{ + int i; + double *__restrict o1 = my_alloc1 (len); + double *__restrict o2 = my_alloc1 (len); + double *__restrict o3 = my_alloc1 (len); + double *__restrict i1 = my_alloc1 (len); + double *__restrict i2 = my_alloc1 (len); + for (i = 0; i < len; ++i) + { + o1[i] = i1[i] * i2[i]; + o2[i] = i1[i] + i2[i]; + o3[i] = i1[i] - i2[i]; + } +} + +void +test2 (int len) +{ + int i; + double *__restrict o1 = my_alloc2 (len); + double *__restrict o2 = my_alloc2 (len); + double *__restrict o3 = my_alloc2 (len); + double *__restrict i1 = my_alloc2 (len); + double *__restrict i2 = my_alloc2 (len); + for (i = 0; i < len; ++i) + { + o1[i] = i1[i] * i2[i]; + o2[i] = i1[i] + i2[i]; + o3[i] = i1[i] - i2[i]; + } +} diff --git a/gcc/testsuite/gcc.dg/attr-assume_aligned-2.c b/gcc/testsuite/gcc.dg/attr-assume_aligned-2.c new file mode 100644 index 000000000000..9ab558945b37 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-assume_aligned-2.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ + +int i; +void *f1 (void) __attribute__((assume_aligned (32))); +void *f2 (void) __attribute__((assume_aligned (16, 4))); +void *f3 (void) __attribute__((assume_aligned)); /* { dg-error "wrong number of arguments specified" } */ +void *f4 (void) __attribute__((assume_aligned (32, 16, 8))); /* { dg-error "wrong number of arguments specified" } */ +void *f5 (void) __attribute__((assume_aligned (i))); /* { dg-warning "integer constant" } */ diff --git a/gcc/testsuite/gcc.dg/attr-assume_aligned-3.c b/gcc/testsuite/gcc.dg/attr-assume_aligned-3.c new file mode 100644 index 000000000000..3731aac50eae --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-assume_aligned-3.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +char *my_alloc1 (int len) __attribute__((__assume_aligned__ (32))); +char *my_alloc2 (int len) __attribute__((assume_aligned (32, 4))); + +int +test1 (int len) +{ + int i; + char *p = my_alloc1 (len); + return ((__INTPTR_TYPE__) p) & 31; +} + +int +test2 (int len) +{ + int i; + char *p = my_alloc2 (len); + return (((__INTPTR_TYPE__) p) & 31) != 4; +} + +/* { dg-final { scan-tree-dump-times "return 0" 2 "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c index 493bbcb336b9..eeefeaf07240 100644 --- a/gcc/tree-ssa-ccp.c +++ b/gcc/tree-ssa-ccp.c @@ -738,13 +738,18 @@ surely_varying_stmt_p (gimple stmt) return true; /* If it is a call and does not return a value or is not a - builtin and not an indirect call, it is varying. */ + builtin and not an indirect call or a call to function with + assume_aligned/alloc_align attribute, it is varying. */ if (is_gimple_call (stmt)) { - tree fndecl; + tree fndecl, fntype = gimple_call_fntype (stmt); if (!gimple_call_lhs (stmt) || ((fndecl = gimple_call_fndecl (stmt)) != NULL_TREE - && !DECL_BUILT_IN (fndecl))) + && !DECL_BUILT_IN (fndecl) + && !lookup_attribute ("assume_aligned", + TYPE_ATTRIBUTES (fntype)) + && !lookup_attribute ("alloc_align", + TYPE_ATTRIBUTES (fntype)))) return true; } @@ -1476,40 +1481,86 @@ bit_value_binop (enum tree_code code, tree type, tree rhs1, tree rhs2) return val; } -/* Return the propagation value when applying __builtin_assume_aligned to - its arguments. */ +/* Return the propagation value for __builtin_assume_aligned + and functions with assume_aligned or alloc_aligned attribute. + For __builtin_assume_aligned, ATTR is NULL_TREE, + for assume_aligned attribute ATTR is non-NULL and ALLOC_ALIGNED + is false, for alloc_aligned attribute ATTR is non-NULL and + ALLOC_ALIGNED is true. */ static prop_value_t -bit_value_assume_aligned (gimple stmt) +bit_value_assume_aligned (gimple stmt, tree attr, prop_value_t ptrval, + bool alloc_aligned) { - tree ptr = gimple_call_arg (stmt, 0), align, misalign = NULL_TREE; - tree type = TREE_TYPE (ptr); + tree align, misalign = NULL_TREE, type; unsigned HOST_WIDE_INT aligni, misaligni = 0; - prop_value_t ptrval = get_value_for_expr (ptr, true); prop_value_t alignval; double_int value, mask; prop_value_t val; + + if (attr == NULL_TREE) + { + tree ptr = gimple_call_arg (stmt, 0); + type = TREE_TYPE (ptr); + ptrval = get_value_for_expr (ptr, true); + } + else + { + tree lhs = gimple_call_lhs (stmt); + type = TREE_TYPE (lhs); + } + if (ptrval.lattice_val == UNDEFINED) return ptrval; gcc_assert ((ptrval.lattice_val == CONSTANT && TREE_CODE (ptrval.value) == INTEGER_CST) || ptrval.mask.is_minus_one ()); - align = gimple_call_arg (stmt, 1); - if (!tree_fits_uhwi_p (align)) - return ptrval; - aligni = tree_to_uhwi (align); - if (aligni <= 1 - || (aligni & (aligni - 1)) != 0) - return ptrval; - if (gimple_call_num_args (stmt) > 2) + if (attr == NULL_TREE) { - misalign = gimple_call_arg (stmt, 2); - if (!tree_fits_uhwi_p (misalign)) - return ptrval; - misaligni = tree_to_uhwi (misalign); - if (misaligni >= aligni) + /* Get aligni and misaligni from __builtin_assume_aligned. */ + align = gimple_call_arg (stmt, 1); + if (!tree_fits_uhwi_p (align)) return ptrval; + aligni = tree_to_uhwi (align); + if (gimple_call_num_args (stmt) > 2) + { + misalign = gimple_call_arg (stmt, 2); + if (!tree_fits_uhwi_p (misalign)) + return ptrval; + misaligni = tree_to_uhwi (misalign); + } } + else + { + /* Get aligni and misaligni from assume_aligned or + alloc_align attributes. */ + if (TREE_VALUE (attr) == NULL_TREE) + return ptrval; + attr = TREE_VALUE (attr); + align = TREE_VALUE (attr); + if (!tree_fits_uhwi_p (align)) + return ptrval; + aligni = tree_to_uhwi (align); + if (alloc_aligned) + { + if (aligni == 0 || aligni > gimple_call_num_args (stmt)) + return ptrval; + align = gimple_call_arg (stmt, aligni - 1); + if (!tree_fits_uhwi_p (align)) + return ptrval; + aligni = tree_to_uhwi (align); + } + else if (TREE_CHAIN (attr) && TREE_VALUE (TREE_CHAIN (attr))) + { + misalign = TREE_VALUE (TREE_CHAIN (attr)); + if (!tree_fits_uhwi_p (misalign)) + return ptrval; + misaligni = tree_to_uhwi (misalign); + } + } + if (aligni <= 1 || (aligni & (aligni - 1)) != 0 || misaligni >= aligni) + return ptrval; + align = build_int_cst_type (type, -aligni); alignval = get_value_for_expr (align, true); bit_value_binop_1 (BIT_AND_EXPR, type, &value, &mask, @@ -1708,12 +1759,27 @@ evaluate_stmt (gimple stmt) break; case BUILT_IN_ASSUME_ALIGNED: - val = bit_value_assume_aligned (stmt); + val = bit_value_assume_aligned (stmt, NULL_TREE, val, false); break; default:; } } + if (is_gimple_call (stmt) && gimple_call_lhs (stmt)) + { + tree fntype = gimple_call_fntype (stmt); + if (fntype) + { + tree attrs = lookup_attribute ("assume_aligned", + TYPE_ATTRIBUTES (fntype)); + if (attrs) + val = bit_value_assume_aligned (stmt, attrs, val, false); + attrs = lookup_attribute ("alloc_align", + TYPE_ATTRIBUTES (fntype)); + if (attrs) + val = bit_value_assume_aligned (stmt, attrs, val, true); + } + } is_constant = (val.lattice_val == CONSTANT); }