On Mon, Jun 18, 2012 at 4:59 PM, Michael Matz <[email protected]> wrote:
> Hi,
>
> now that we regard MEM_EXPR as a conservative approximation for MEM_SIZE
> (and MEM_OFFSET) we must ensure that this is really the case. It isn't
> currently for the string expanders, as they use the MEM_REF (whose address
> was taken) directly as the one to use for MEM_EXPR on the MEM rtx. That's
> wrong, on gimple side we take the address only and hence its size is
> arbitrary.
>
> So, we have to build a memref always and rewrite its type to one
> representing the real size. Note that TYPE_MAX_VALUE may be NULL, so we
> don't need to check for 'len' being null or not.
>
> This fixes the C testcase (don't know about fma 3d), and is in
> regstrapping on x86_64-linux. Okay if that passes?
Ok.
Note that as a followup you should be able to remove the whole
/* Allow the string and memory builtins to overflow from one
field into another, see http://gcc.gnu.org/PR23561.
Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole
memory accessed by the string or memory builtin will fit
within the field. */
if (MEM_EXPR (mem) && TREE_CODE (MEM_EXPR (mem)) == COMPONENT_REF)
{
block. Also practically (as we are expanding from GIMPLE now), off
should always be zero and TREE_CODE (exp) should never be
POINTER_PLUS_EXPR, nor should there be wrapping conversions.
The 'off' case can also be dealt with by using the offset operand of
the MEM_REF we build.
Finally MEM_EXPR itself has invalid type-based aliasing properties
(it has so even before your patch), of course that doesn't really matter,
as below we do set_mem_alias_set (mem, 0). Still with MEM_REF
you should be able to do
Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c (revision 188733)
+++ gcc/builtins.c (working copy)
@@ -1250,132 +1250,27 @@ expand_builtin_prefetch (tree exp)
static rtx
get_memory_rtx (tree exp, tree len)
{
- tree orig_exp = exp;
rtx addr, mem;
- HOST_WIDE_INT off;
- /* When EXP is not resolved SAVE_EXPR, MEM_ATTRS can be still derived
- from its expression, for expr->a.b only <variable>.a.b is recorded. */
- if (TREE_CODE (exp) == SAVE_EXPR && !SAVE_EXPR_RESOLVED_P (exp))
- exp = TREE_OPERAND (exp, 0);
-
- addr = expand_expr (orig_exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
+ addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
- /* Get an expression we can use to find the attributes to assign to MEM.
- If it is an ADDR_EXPR, use the operand. Otherwise, dereference it if
- we can. First remove any nops. */
- while (CONVERT_EXPR_P (exp)
- && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0))))
- exp = TREE_OPERAND (exp, 0);
-
- off = 0;
- if (TREE_CODE (exp) == POINTER_PLUS_EXPR
- && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
- && host_integerp (TREE_OPERAND (exp, 1), 0)
- && (off = tree_low_cst (TREE_OPERAND (exp, 1), 0)) > 0)
- exp = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
- else if (TREE_CODE (exp) == ADDR_EXPR)
- exp = TREE_OPERAND (exp, 0);
- else if (POINTER_TYPE_P (TREE_TYPE (exp)))
- exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
- else
- exp = NULL;
+ /* Build a memory reference suitable for MEM_EXPR for use by the
+ alias oracle. Make sure to give that memory reference a proper
+ access size as well as alias-set zero. */
+ exp = fold_build2 (MEM_REF,
+ build_array_type (char_type_node,
+ build_range_type (sizetype,
+ size_one_node, len)),
+ exp, build_int_cst (ptr_type_node, 0));
/* Honor attributes derived from exp, except for the alias set
(as builtin stringops may alias with anything) and the size
(as stringops may access multiple array elements). */
- if (exp)
- {
- set_mem_attributes (mem, exp, 0);
-
- if (off)
- mem = adjust_automodify_address_nv (mem, BLKmode, NULL, off);
-
- /* Allow the string and memory builtins to overflow from one
- field into another, see http://gcc.gnu.org/PR23561.
- Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole
- memory accessed by the string or memory builtin will fit
- within the field. */
- if (MEM_EXPR (mem) && TREE_CODE (MEM_EXPR (mem)) == COMPONENT_REF)
- {
- tree mem_expr = MEM_EXPR (mem);
- HOST_WIDE_INT offset = -1, length = -1;
- tree inner = exp;
-
- while (TREE_CODE (inner) == ARRAY_REF
- || CONVERT_EXPR_P (inner)
- || TREE_CODE (inner) == VIEW_CONVERT_EXPR
- || TREE_CODE (inner) == SAVE_EXPR)
- inner = TREE_OPERAND (inner, 0);
-
- gcc_assert (TREE_CODE (inner) == COMPONENT_REF);
-
- if (MEM_OFFSET_KNOWN_P (mem))
- offset = MEM_OFFSET (mem);
-
- if (offset >= 0 && len && host_integerp (len, 0))
- length = tree_low_cst (len, 0);
-
- while (TREE_CODE (inner) == COMPONENT_REF)
- {
- tree field = TREE_OPERAND (inner, 1);
- gcc_assert (TREE_CODE (mem_expr) == COMPONENT_REF);
- gcc_assert (field == TREE_OPERAND (mem_expr, 1));
-
- /* Bitfields are generally not byte-addressable. */
- gcc_assert (!DECL_BIT_FIELD (field)
- || ((tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1)
- % BITS_PER_UNIT) == 0
- && host_integerp (DECL_SIZE (field), 0)
- && (TREE_INT_CST_LOW (DECL_SIZE (field))
- % BITS_PER_UNIT) == 0));
-
- /* If we can prove that the memory starting at XEXP (mem, 0) and
- ending at XEXP (mem, 0) + LENGTH will fit into this field, we
- can keep the COMPONENT_REF in MEM_EXPR. But be careful with
- fields without DECL_SIZE_UNIT like flexible array members. */
- if (length >= 0
- && DECL_SIZE_UNIT (field)
- && host_integerp (DECL_SIZE_UNIT (field), 0))
- {
- HOST_WIDE_INT size
- = TREE_INT_CST_LOW (DECL_SIZE_UNIT (field));
- if (offset <= size
- && length <= size
- && offset + length <= size)
- break;
- }
-
- if (offset >= 0
- && host_integerp (DECL_FIELD_OFFSET (field), 0))
- offset += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field))
- + tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1)
- / BITS_PER_UNIT;
- else
- {
- offset = -1;
- length = -1;
- }
-
- mem_expr = TREE_OPERAND (mem_expr, 0);
- inner = TREE_OPERAND (inner, 0);
- }
-
- if (mem_expr == NULL)
- offset = -1;
- if (mem_expr != MEM_EXPR (mem))
- {
- set_mem_expr (mem, mem_expr);
- if (offset >= 0)
- set_mem_offset (mem, offset);
- else
- clear_mem_offset (mem);
- }
- }
- set_mem_alias_set (mem, 0);
- clear_mem_size (mem);
- }
+ set_mem_attributes (mem, exp, 0);
+ set_mem_alias_set (mem, 0);
+ /* ??? Not sure why we clear mem-size. */
+ clear_mem_size (mem);
return mem;
}
note that the NOP stripping was even questionable as it strips conversions
between different address-spaces.
The above should be done, if at all, as a followup to this bugfix of course.
Thanks,
Richard.
>
> Ciao,
> Michael.
> PR middle-end/53688
> * builtins.c (get_memory_rtx): Always build a MEM_REF and override
> its type with one of correct range.
>
> testsuite/
> * gcc.c-torture/execute/pr53688.c: New test.
> Index: builtins.c
> ===================================================================
> --- builtins.c (revision 188734)
> +++ builtins.c (working copy)
> @@ -1274,11 +1274,11 @@ get_memory_rtx (tree exp, tree len)
> && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
> && host_integerp (TREE_OPERAND (exp, 1), 0)
> && (off = tree_low_cst (TREE_OPERAND (exp, 1), 0)) > 0)
> - exp = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
> + exp = build_simple_mem_ref (TREE_OPERAND (exp, 0));
> else if (TREE_CODE (exp) == ADDR_EXPR)
> - exp = TREE_OPERAND (exp, 0);
> + exp = build_simple_mem_ref (exp);
> else if (POINTER_TYPE_P (TREE_TYPE (exp)))
> - exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
> + exp = build_simple_mem_ref (exp);
> else
> exp = NULL;
>
> @@ -1287,6 +1287,11 @@ get_memory_rtx (tree exp, tree len)
> (as stringops may access multiple array elements). */
> if (exp)
> {
> + /* Reset the type to something spanning the whole thing. EXP
> + is always a MEM_REF, hence unshared. */
> + TREE_TYPE (exp)
> + = build_array_type (char_type_node,
> + build_range_type (sizetype, size_one_node, len));
> set_mem_attributes (mem, exp, 0);
>
> if (off)
> Index: testsuite/gcc.c-torture/execute/pr53688.c
> ===================================================================
> --- testsuite/gcc.c-torture/execute/pr53688.c (revision 0)
> +++ testsuite/gcc.c-torture/execute/pr53688.c (revision 0)
> @@ -0,0 +1,32 @@
> +char headline[256];
> +struct hdr {
> + char part1[9];
> + char part2[8];
> +} p;
> +
> +void __attribute__((noinline,noclone))
> +init()
> +{
> + __builtin_memcpy (p.part1, "FOOBARFOO", sizeof (p.part1));
> + __builtin_memcpy (p.part2, "SPEC CPU", sizeof (p.part2));
> +}
> +
> +int main()
> +{
> + char *x;
> + int c;
> + init();
> + __builtin_memcpy (&headline[0], p.part1, 9);
> + c = 9;
> + x = &headline[0];
> + x = x + c;
> + __builtin_memset (x, ' ', 245);
> + __builtin_memcpy (&headline[10], p.part2, 8);
> + c = 18;
> + x = &headline[0];
> + x = x + c;
> + __builtin_memset (x, ' ', 238);
> + if (headline[10] != 'S')
> + __builtin_abort ();
> + return 0;
> +}