From 4c437f02c24d896b08267b39cd8c8216da3bce4e Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Wed, 8 Jan 2014 17:25:38 +0000 Subject: [PATCH] re PR middle-end/57748 (ICE when expanding assignment to unaligned zero-sized array) 2014-01-08 Bernd Edlinger PR middle-end/57748 * expr.h (expand_expr_real, expand_expr_real_1): Add new parameter inner_reference_p. (expand_expr, expand_normal): Adjust. * expr.c (expand_expr_real, expand_expr_real_1): Add new parameter inner_reference_p. Use inner_reference_p to expand inner references. (store_expr): Adjust. * cfgexpand.c (expand_call_stmt): Adjust. testsuite: 2014-01-08 Bernd Edlinger PR middle-end/57748 * gcc.dg/torture/pr57748-3.c: New test. * gcc.dg/torture/pr57748-4.c: New test. From-SVN: r206437 --- gcc/ChangeLog | 11 +++ gcc/cfgexpand.c | 2 +- gcc/expr.c | 152 +++++++++++++++++-------------- gcc/expr.h | 11 ++- gcc/testsuite/ChangeLog | 6 ++ gcc/testsuite/gcc.dg/torture/pr57748-3.c | 40 ++++++++ gcc/testsuite/gcc.dg/torture/pr57748-4.c | 40 ++++++++ 7 files changed, 190 insertions(+), 72 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/torture/pr57748-3.c create mode 100644 gcc/testsuite/gcc.dg/torture/pr57748-4.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2fc426d..7c1edb5 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2014-01-08 Bernd Edlinger + + PR middle-end/57748 + * expr.h (expand_expr_real, expand_expr_real_1): Add new parameter + inner_reference_p. + (expand_expr, expand_normal): Adjust. + * expr.c (expand_expr_real, expand_expr_real_1): Add new parameter + inner_reference_p. Use inner_reference_p to expand inner references. + (store_expr): Adjust. + * cfgexpand.c (expand_call_stmt): Adjust. + 2014-01-08 Rong Xu * gcov-io.c (gcov_var): Move from gcov-io.h. diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 2ba1cf4..0008be9 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -2253,7 +2253,7 @@ expand_call_stmt (gimple stmt) if (lhs) expand_assignment (lhs, exp, false); else - expand_expr_real_1 (exp, const0_rtx, VOIDmode, EXPAND_NORMAL, NULL); + expand_expr (exp, const0_rtx, VOIDmode, EXPAND_NORMAL); mark_transaction_restart_calls (stmt); } diff --git a/gcc/expr.c b/gcc/expr.c index 916a8aa..400a152 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -5325,7 +5325,7 @@ store_expr (tree exp, rtx target, int call_param_p, bool nontemporal) temp = expand_expr_real (exp, tmp_target, GET_MODE (target), (call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL), - &alt_rtl); + &alt_rtl, false); } /* If TEMP is a VOIDmode constant and the mode of the type of EXP is not @@ -7911,11 +7911,21 @@ expand_constructor (tree exp, rtx target, enum expand_modifier modifier, address, and ALT_RTL is non-NULL, then *ALT_RTL is set to the DECL_RTL of the VAR_DECL. *ALT_RTL is also set if EXP is a COMPOUND_EXPR whose second argument is such a VAR_DECL, and so on - recursively. */ + recursively. + + If INNER_REFERENCE_P is true, we are expanding an inner reference. + In this case, we don't adjust a returned MEM rtx that wouldn't be + sufficiently aligned for its mode; instead, it's up to the caller + to deal with it afterwards. This is used to make sure that unaligned + base objects for which out-of-bounds accesses are supported, for + example record types with trailing arrays, aren't realigned behind + the back of the caller. + The normal operating mode is to pass FALSE for this parameter. */ rtx expand_expr_real (tree exp, rtx target, enum machine_mode tmode, - enum expand_modifier modifier, rtx *alt_rtl) + enum expand_modifier modifier, rtx *alt_rtl, + bool inner_reference_p) { rtx ret; @@ -7927,7 +7937,8 @@ expand_expr_real (tree exp, rtx target, enum machine_mode tmode, return ret ? ret : const0_rtx; } - ret = expand_expr_real_1 (exp, target, tmode, modifier, alt_rtl); + ret = expand_expr_real_1 (exp, target, tmode, modifier, alt_rtl, + inner_reference_p); return ret; } @@ -9232,7 +9243,8 @@ stmt_is_replaceable_p (gimple stmt) rtx expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, - enum expand_modifier modifier, rtx *alt_rtl) + enum expand_modifier modifier, rtx *alt_rtl, + bool inner_reference_p) { rtx op0, op1, temp, decl_rtl; tree type; @@ -9378,7 +9390,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, set_curr_insn_location (gimple_location (g)); r = expand_expr_real (gimple_assign_rhs_to_tree (g), target, - tmode, modifier, NULL); + tmode, modifier, NULL, inner_reference_p); set_curr_insn_location (saved_loc); if (REG_P (r) && !REG_EXPR (r)) set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (exp), r); @@ -9597,7 +9609,8 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, case SAVE_EXPR: { tree val = treeop0; - rtx ret = expand_expr_real_1 (val, target, tmode, modifier, alt_rtl); + rtx ret = expand_expr_real_1 (val, target, tmode, modifier, alt_rtl, + inner_reference_p); if (!SAVE_EXPR_RESOLVED_P (exp)) { @@ -9735,6 +9748,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, MEM_VOLATILE_P (temp) = 1; if (modifier != EXPAND_WRITE && modifier != EXPAND_MEMORY + && !inner_reference_p && mode != BLKmode && align < GET_MODE_ALIGNMENT (mode)) { @@ -9960,15 +9974,16 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, computation, since it will need a temporary and TARGET is known to have to do. This occurs in unchecked conversion in Ada. */ orig_op0 = op0 - = expand_expr (tem, - (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE - && COMPLETE_TYPE_P (TREE_TYPE (tem)) - && (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem))) - != INTEGER_CST) - && modifier != EXPAND_STACK_PARM - ? target : NULL_RTX), - VOIDmode, - modifier == EXPAND_SUM ? EXPAND_NORMAL : modifier); + = expand_expr_real (tem, + (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE + && COMPLETE_TYPE_P (TREE_TYPE (tem)) + && (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem))) + != INTEGER_CST) + && modifier != EXPAND_STACK_PARM + ? target : NULL_RTX), + VOIDmode, + modifier == EXPAND_SUM ? EXPAND_NORMAL : modifier, + NULL, true); /* If the field has a mode, we want to access it in the field's mode, not the computed mode. @@ -10325,14 +10340,15 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, { /* See the normal_inner_ref case for the rationale. */ orig_op0 - = expand_expr (tem, - (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE - && (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem))) - != INTEGER_CST) - && modifier != EXPAND_STACK_PARM - ? target : NULL_RTX), - VOIDmode, - modifier == EXPAND_SUM ? EXPAND_NORMAL : modifier); + = expand_expr_real (tem, + (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE + && (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem))) + != INTEGER_CST) + && modifier != EXPAND_STACK_PARM + ? target : NULL_RTX), + VOIDmode, + modifier == EXPAND_SUM ? EXPAND_NORMAL : modifier, + NULL, true); if (MEM_P (orig_op0)) { @@ -10359,7 +10375,8 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, } if (!op0) - op0 = expand_expr (treeop0, NULL_RTX, VOIDmode, modifier); + op0 = expand_expr_real (treeop0, NULL_RTX, VOIDmode, modifier, + NULL, inner_reference_p); /* If the input and output modes are both the same, we are done. */ if (mode == GET_MODE (op0)) @@ -10426,50 +10443,53 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, op0 = copy_rtx (op0); set_mem_align (op0, MAX (MEM_ALIGN (op0), TYPE_ALIGN (type))); } - else if (mode != BLKmode - && MEM_ALIGN (op0) < GET_MODE_ALIGNMENT (mode) - /* If the target does have special handling for unaligned - loads of mode then use them. */ - && ((icode = optab_handler (movmisalign_optab, mode)) - != CODE_FOR_nothing)) - { - rtx reg, insn; - - op0 = adjust_address (op0, mode, 0); - /* We've already validated the memory, and we're creating a - new pseudo destination. The predicates really can't - fail. */ - reg = gen_reg_rtx (mode); - - /* Nor can the insn generator. */ - insn = GEN_FCN (icode) (reg, op0); - emit_insn (insn); - return reg; - } - else if (STRICT_ALIGNMENT + else if (modifier != EXPAND_WRITE + && modifier != EXPAND_MEMORY + && !inner_reference_p && mode != BLKmode && MEM_ALIGN (op0) < GET_MODE_ALIGNMENT (mode)) { - tree inner_type = TREE_TYPE (treeop0); - HOST_WIDE_INT temp_size - = MAX (int_size_in_bytes (inner_type), - (HOST_WIDE_INT) GET_MODE_SIZE (mode)); - rtx new_rtx - = assign_stack_temp_for_type (mode, temp_size, type); - rtx new_with_op0_mode - = adjust_address (new_rtx, GET_MODE (op0), 0); - - gcc_assert (!TREE_ADDRESSABLE (exp)); - - if (GET_MODE (op0) == BLKmode) - emit_block_move (new_with_op0_mode, op0, - GEN_INT (GET_MODE_SIZE (mode)), - (modifier == EXPAND_STACK_PARM - ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL)); - else - emit_move_insn (new_with_op0_mode, op0); + /* If the target does have special handling for unaligned + loads of mode then use them. */ + if ((icode = optab_handler (movmisalign_optab, mode)) + != CODE_FOR_nothing) + { + rtx reg, insn; + + op0 = adjust_address (op0, mode, 0); + /* We've already validated the memory, and we're creating a + new pseudo destination. The predicates really can't + fail. */ + reg = gen_reg_rtx (mode); + + /* Nor can the insn generator. */ + insn = GEN_FCN (icode) (reg, op0); + emit_insn (insn); + return reg; + } + else if (STRICT_ALIGNMENT) + { + tree inner_type = TREE_TYPE (treeop0); + HOST_WIDE_INT temp_size + = MAX (int_size_in_bytes (inner_type), + (HOST_WIDE_INT) GET_MODE_SIZE (mode)); + rtx new_rtx + = assign_stack_temp_for_type (mode, temp_size, type); + rtx new_with_op0_mode + = adjust_address (new_rtx, GET_MODE (op0), 0); + + gcc_assert (!TREE_ADDRESSABLE (exp)); + + if (GET_MODE (op0) == BLKmode) + emit_block_move (new_with_op0_mode, op0, + GEN_INT (GET_MODE_SIZE (mode)), + (modifier == EXPAND_STACK_PARM + ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL)); + else + emit_move_insn (new_with_op0_mode, op0); - op0 = new_rtx; + op0 = new_rtx; + } } op0 = adjust_address (op0, mode, 0); @@ -10569,7 +10589,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, /* WITH_SIZE_EXPR expands to its first argument. The caller should have pulled out the size to use in whatever context it needed. */ return expand_expr_real (treeop0, original_target, tmode, - modifier, alt_rtl); + modifier, alt_rtl, inner_reference_p); default: return expand_expr_real_2 (&ops, target, tmode, modifier); diff --git a/gcc/expr.h b/gcc/expr.h index a39b98e..da5d4a6 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -41,7 +41,8 @@ along with GCC; see the file COPYING3. If not see is a constant that is not a legitimate address. EXPAND_WRITE means we are only going to write to the resulting rtx. EXPAND_MEMORY means we are interested in a memory result, even if - the memory is constant and we could have propagated a constant value. */ + the memory is constant and we could have propagated a constant value, + or the memory is unaligned on a STRICT_ALIGNMENT target. */ enum expand_modifier {EXPAND_NORMAL = 0, EXPAND_STACK_PARM, EXPAND_SUM, EXPAND_CONST_ADDRESS, EXPAND_INITIALIZER, EXPAND_WRITE, EXPAND_MEMORY}; @@ -437,9 +438,9 @@ extern rtx force_operand (rtx, rtx); /* Work horses for expand_expr. */ extern rtx expand_expr_real (tree, rtx, enum machine_mode, - enum expand_modifier, rtx *); + enum expand_modifier, rtx *, bool); extern rtx expand_expr_real_1 (tree, rtx, enum machine_mode, - enum expand_modifier, rtx *); + enum expand_modifier, rtx *, bool); extern rtx expand_expr_real_2 (sepops, rtx, enum machine_mode, enum expand_modifier); @@ -450,13 +451,13 @@ static inline rtx expand_expr (tree exp, rtx target, enum machine_mode mode, enum expand_modifier modifier) { - return expand_expr_real (exp, target, mode, modifier, NULL); + return expand_expr_real (exp, target, mode, modifier, NULL, false); } static inline rtx expand_normal (tree exp) { - return expand_expr_real (exp, NULL_RTX, VOIDmode, EXPAND_NORMAL, NULL); + return expand_expr_real (exp, NULL_RTX, VOIDmode, EXPAND_NORMAL, NULL, false); } /* At the start of a function, record that we have no previously-pushed diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 39e7af0..827b482 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2014-01-08 Bernd Edlinger + + PR middle-end/57748 + * gcc.dg/torture/pr57748-3.c: New test. + * gcc.dg/torture/pr57748-4.c: New test. + 2014-01-08 Marek Polacek PR middle-end/59669 diff --git a/gcc/testsuite/gcc.dg/torture/pr57748-3.c b/gcc/testsuite/gcc.dg/torture/pr57748-3.c new file mode 100644 index 0000000..5ddb609 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr57748-3.c @@ -0,0 +1,40 @@ +/* PR middle-end/57748 */ +/* { dg-do run } */ +/* wrong code in expand_expr_real_1. */ + +#include + +extern void abort (void); + +typedef long long V + __attribute__ ((vector_size (2 * sizeof (long long)), may_alias)); + +typedef struct S { V a; V b[0]; } P __attribute__((aligned (1))); + +struct __attribute__((packed)) T { char c; P s; }; + +void __attribute__((noinline, noclone)) +check (P *p) +{ + if (p->b[0][0] != 3 || p->b[0][1] != 4) + abort (); +} + +void __attribute__((noinline, noclone)) +foo (struct T *t) +{ + V a = { 3, 4 }; + t->s.b[0] = a; +} + +int +main () +{ + struct T *t = (struct T *) calloc (128, 1); + + foo (t); + check (&t->s); + + free (t); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/pr57748-4.c b/gcc/testsuite/gcc.dg/torture/pr57748-4.c new file mode 100644 index 0000000..455cb3d --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr57748-4.c @@ -0,0 +1,40 @@ +/* PR middle-end/57748 */ +/* { dg-do run } */ +/* wrong code in expand_expr_real_1. */ + +#include + +extern void abort (void); + +typedef long long V + __attribute__ ((vector_size (2 * sizeof (long long)), may_alias)); + +typedef struct S { V b[1]; } P __attribute__((aligned (1))); + +struct __attribute__((packed)) T { char c; P s; }; + +void __attribute__((noinline, noclone)) +check (P *p) +{ + if (p->b[1][0] != 3 || p->b[1][1] != 4) + abort (); +} + +void __attribute__((noinline, noclone)) +foo (struct T *t) +{ + V a = { 3, 4 }; + t->s.b[1] = a; +} + +int +main () +{ + struct T *t = (struct T *) calloc (128, 1); + + foo (t); + check (&t->s); + + free (t); + return 0; +} -- 2.7.4