On Thu, 6 Feb 2014, Jakub Jelinek wrote:
> Hi!
>
> As discussed on IRC, this patch introduces two new attributes,
> so that the C library (and other headers) have a way to
> a) tell the compiler something about functions like aligned_alloc
> or memalign
> b) tell the compiler the alignment of pointers returned say by malloc
>
> Ok for trunk if bootstrap/regtest passes?
>
> 2014-02-06 Jakub Jelinek <[email protected]>
>
> 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_alloc_assume_aligned_attribute): New function.
> (evaluate_stmt): Handle calls to functions with
> assume_aligned or alloc_align attributes.
> * doc/extend.texi: Document assume_aligned and alloc_align
> attributes.
> c-family/
> * c-common.c (handle_alloc_align_attribute,
> handle_assume_aligned_attribute): New functions.
> (c_common_attribute_table): Add alloc_align and assume_aligned
> attributes.
> testsuite/
> * 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.
>
> --- gcc/c-family/c-common.c.jj 2014-02-05 10:37:58.000000000 +0100
> +++ gcc/c-family/c-common.c 2014-02-06 15:35:15.707333771 +0100
> @@ -366,6 +366,8 @@ static tree handle_warn_unused_result_at
> 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_att
> 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 }
> };
>
> @@ -8046,13 +8052,64 @@ handle_alloc_size_attribute (tree *node,
> if (TREE_CODE (position) != INTEGER_CST
> || TREE_INT_CST_HIGH (position)
> || TREE_INT_CST_LOW (position) < 1
> - || TREE_INT_CST_LOW (position) > arg_count )
> + || TREE_INT_CST_LOW (position) > arg_count)
> {
> warning (OPT_Wattributes,
> "alloc_size parameter outside range");
> *no_add_attrs = true;
> return NULL_TREE;
> }
> + }
> + return NULL_TREE;
> +}
> +
> +/* Handle a "alloc_align" attribute; arguments as in
> + struct attribute_spec.handler. */
> +
> +static tree
> +handle_alloc_align_attribute (tree *node, tree ARG_UNUSED (name), 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
> + && 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)
You make it easier for wide-int folks if you use tree_fits_uhwi_p
and tree_to_uhwi ...
> + {
> + 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 *node, tree ARG_UNUSED (name), 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;
> }
> --- gcc/tree-ssa-ccp.c.jj 2014-01-03 11:40:46.000000000 +0100
> +++ gcc/tree-ssa-ccp.c 2014-02-06 16:16:27.260996887 +0100
> @@ -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;
> }
>
> @@ -1534,6 +1539,77 @@ bit_value_assume_aligned (gimple stmt)
> return val;
> }
>
> +/* Return the propagation value for functions with assume_aligned
> + or alloc_aligned attribute. */
> +
> +static prop_value_t
> +bit_value_alloc_assume_aligned_attribute (gimple stmt, tree attr,
> + prop_value_t ptrval,
> + bool alloc_aligned)
> +{
This function is very similar to the existing bit_value_assume_aligned
which asks for some factoring? Like share the tails once you've
figured out align and misalign values?
Ok with these changes.
I wonder if we want to backport support for these attributes
to 4.8 (and 4.7?).
Will you be working on a glibc patch?
Thanks,
Richard.
> + tree lhs = gimple_call_lhs (stmt), align, misalign = NULL_TREE;
> + tree type = TREE_TYPE (lhs);
> + unsigned HOST_WIDE_INT aligni, misaligni = 0;
> + prop_value_t alignval;
> + double_int value, mask;
> + prop_value_t val;
> + if (ptrval.lattice_val == UNDEFINED)
> + return ptrval;
> + gcc_assert ((ptrval.lattice_val == CONSTANT
> + && TREE_CODE (ptrval.value) == INTEGER_CST)
> + || ptrval.mask.is_minus_one ());
> + 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);
> + }
> + if (aligni <= 1
> + || (aligni & (aligni - 1)) != 0)
> + return ptrval;
> + if (!alloc_aligned && 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 (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,
> + type, value_to_double_int (ptrval), ptrval.mask,
> + type, value_to_double_int (alignval), alignval.mask);
> + if (!mask.is_minus_one ())
> + {
> + val.lattice_val = CONSTANT;
> + val.mask = mask;
> + gcc_assert ((mask.low & (aligni - 1)) == 0);
> + gcc_assert ((value.low & (aligni - 1)) == 0);
> + value.low |= misaligni;
> + /* ??? Delay building trees here. */
> + val.value = double_int_to_tree (type, value);
> + }
> + else
> + {
> + val.lattice_val = VARYING;
> + val.value = NULL_TREE;
> + val.mask = double_int_minus_one;
> + }
> + return val;
> +}
> +
> /* Evaluate statement STMT.
> Valid only for assignments, calls, conditionals, and switches. */
>
> @@ -1714,6 +1790,23 @@ evaluate_stmt (gimple stmt)
> 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_alloc_assume_aligned_attribute (stmt, attrs,
> + val, false);
> + attrs = lookup_attribute ("alloc_align",
> + TYPE_ATTRIBUTES (fntype));
> + if (attrs)
> + val = bit_value_alloc_assume_aligned_attribute (stmt, attrs,
> + val, true);
> + }
> + }
> is_constant = (val.lattice_val == CONSTANT);
> }
>
> --- gcc/doc/extend.texi.jj 2014-02-04 01:35:58.000000000 +0100
> +++ gcc/doc/extend.texi 2014-02-06 15:20:28.728138862 +0100
> @@ -2154,8 +2154,8 @@ The keyword @code{__attribute__} allows
> 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 m
> 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.
> --- gcc/testsuite/gcc.dg/attr-alloc_align-1.c.jj 2014-02-06
> 15:41:05.866432468 +0100
> +++ gcc/testsuite/gcc.dg/attr-alloc_align-1.c 2014-02-06 15:58:20.427853133
> +0100
> @@ -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];
> + }
> +}
> --- gcc/testsuite/gcc.dg/attr-alloc_align-2.c.jj 2014-02-06
> 15:38:34.369258030 +0100
> +++ gcc/testsuite/gcc.dg/attr-alloc_align-2.c 2014-02-06 16:28:27.202071608
> +0100
> @@ -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" } */
> --- gcc/testsuite/gcc.dg/attr-alloc_align-3.c.jj 2014-02-06
> 16:20:23.550709898 +0100
> +++ gcc/testsuite/gcc.dg/attr-alloc_align-3.c 2014-02-06 16:24:05.497509170
> +0100
> @@ -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" } } */
> --- gcc/testsuite/gcc.dg/attr-assume_aligned-1.c.jj 2014-02-06
> 15:30:31.717867866 +0100
> +++ gcc/testsuite/gcc.dg/attr-assume_aligned-1.c 2014-02-06
> 15:43:29.848668818 +0100
> @@ -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];
> + }
> +}
> --- gcc/testsuite/gcc.dg/attr-assume_aligned-2.c.jj 2014-02-06
> 15:30:36.340844433 +0100
> +++ gcc/testsuite/gcc.dg/attr-assume_aligned-2.c 2014-02-06
> 16:28:56.985935446 +0100
> @@ -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" } */
> --- gcc/testsuite/gcc.dg/attr-assume_aligned-3.c.jj 2014-02-06
> 16:24:56.038240610 +0100
> +++ gcc/testsuite/gcc.dg/attr-assume_aligned-3.c 2014-02-06
> 16:26:34.432711183 +0100
> @@ -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" } } */
>
>
> Jakub
>
>
--
Richard Biener <[email protected]>
SUSE / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746
GF: Jeff Hawn, Jennifer Guild, Felix Imend"orffer