On Sun, Jan 25, 2015 at 3:25 PM, Mikael Pettersson <mikpeli...@gmail.com> wrote: > This backports the fixes for PR middle-end/57748, a wrong-code and ICE > regression, to the 4.8 branch. > > Tested extensively on x86_64, powerpc64, sparc64, ARMv{5,7}, and m68k. > > Ok for 4.8?
Ok - I assume you have checked that all relevant patches in this area have been backported already? Thanks, Richard. > (I don't have commit rights.) > > /Mikael > > gcc/ > > 2015-01-25 Mikael Pettersson <mikpeli...@gmail.com> > > Backport from mainline > > 2013-09-20 Bernd Edlinger <bernd.edlin...@hotmail.de> > > PR middle-end/57748 > * expr.c (expand_assignment): Remove misalignp code path. > > 2014-01-08 Bernd Edlinger <bernd.edlin...@hotmail.de> > > 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. > > gcc/testsuite/ > > 2015-01-25 Mikael Pettersson <mikpeli...@gmail.com> > > Backport from mainline > > 2013-09-20 Bernd Edlinger <bernd.edlin...@hotmail.de> > > PR middle-end/57748 > * gcc.dg/torture/pr57748-1.c: New test. > * gcc.dg/torture/pr57748-2.c: New test. > > 2014-01-08 Bernd Edlinger <bernd.edlin...@hotmail.de> > > PR middle-end/57748 > * gcc.dg/torture/pr57748-3.c: New test. > * gcc.dg/torture/pr57748-4.c: New test. > > diff -rupN gcc-4.8-20150122/gcc/cfgexpand.c > gcc-4.8-20150122.pr57748/gcc/cfgexpand.c > --- gcc-4.8-20150122/gcc/cfgexpand.c 2014-01-07 17:49:22.000000000 +0100 > +++ gcc-4.8-20150122.pr57748/gcc/cfgexpand.c 2015-01-25 15:00:35.240949368 > +0100 > @@ -2111,7 +2111,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 -rupN gcc-4.8-20150122/gcc/expr.c gcc-4.8-20150122.pr57748/gcc/expr.c > --- gcc-4.8-20150122/gcc/expr.c 2014-11-28 18:06:23.000000000 +0100 > +++ gcc-4.8-20150122.pr57748/gcc/expr.c 2015-01-25 15:00:35.240949368 +0100 > @@ -4708,8 +4708,6 @@ expand_assignment (tree to, tree from, b > int unsignedp; > int volatilep = 0; > tree tem; > - bool misalignp; > - rtx mem = NULL_RTX; > > push_temp_slots (); > tem = get_inner_reference (to, &bitsize, &bitpos, &offset, &mode1, > @@ -4728,40 +4726,7 @@ expand_assignment (tree to, tree from, b > && DECL_BIT_FIELD_TYPE (TREE_OPERAND (to, 1))) > get_bit_range (&bitregion_start, &bitregion_end, to, &bitpos, > &offset); > > - /* If we are going to use store_bit_field and extract_bit_field, > - make sure to_rtx will be safe for multiple use. */ > - mode = TYPE_MODE (TREE_TYPE (tem)); > - if (TREE_CODE (tem) == MEM_REF > - && mode != BLKmode > - && ((align = get_object_alignment (tem)) > - < GET_MODE_ALIGNMENT (mode)) > - && ((icode = optab_handler (movmisalign_optab, mode)) > - != CODE_FOR_nothing)) > - { > - struct expand_operand ops[2]; > - > - misalignp = true; > - to_rtx = gen_reg_rtx (mode); > - mem = expand_expr (tem, NULL_RTX, VOIDmode, EXPAND_WRITE); > - > - /* If the misaligned store doesn't overwrite all bits, perform > - rmw cycle on MEM. */ > - if (bitsize != GET_MODE_BITSIZE (mode)) > - { > - create_input_operand (&ops[0], to_rtx, mode); > - create_fixed_operand (&ops[1], mem); > - /* The movmisalign<mode> pattern cannot fail, else the > assignment > - would silently be omitted. */ > - expand_insn (icode, 2, ops); > - > - mem = copy_rtx (mem); > - } > - } > - else > - { > - misalignp = false; > - to_rtx = expand_expr (tem, NULL_RTX, VOIDmode, EXPAND_WRITE); > - } > + to_rtx = expand_expr (tem, NULL_RTX, VOIDmode, EXPAND_WRITE); > > /* If the bitfield is volatile, we want to access it in the > field's mode, not the computed mode. > @@ -4900,17 +4865,6 @@ expand_assignment (tree to, tree from, b > get_alias_set (to), nontemporal); > } > > - if (misalignp) > - { > - struct expand_operand ops[2]; > - > - create_fixed_operand (&ops[0], mem); > - create_input_operand (&ops[1], to_rtx, mode); > - /* The movmisalign<mode> pattern cannot fail, else the assignment > - would silently be omitted. */ > - expand_insn (icode, 2, ops); > - } > - > if (result) > preserve_temp_slots (result); > pop_temp_slots (); > @@ -5262,7 +5216,7 @@ store_expr (tree exp, rtx target, int ca > 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 > @@ -7881,11 +7835,21 @@ expand_constructor (tree exp, rtx target > 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; > > @@ -7897,7 +7861,8 @@ expand_expr_real (tree exp, rtx target, > 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; > } > > @@ -9190,7 +9155,8 @@ expand_expr_real_2 (sepops ops, rtx targ > > 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; > @@ -9336,7 +9302,7 @@ expand_expr_real_1 (tree exp, rtx target > > 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); > @@ -9557,7 +9523,8 @@ expand_expr_real_1 (tree exp, rtx target > 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)) > { > @@ -9706,6 +9673,7 @@ expand_expr_real_1 (tree exp, rtx target > MEM_VOLATILE_P (temp) = 1; > if (modifier != EXPAND_WRITE > && modifier != EXPAND_MEMORY > + && !inner_reference_p > && mode != BLKmode > && align < GET_MODE_ALIGNMENT (mode)) > { > @@ -9940,18 +9908,19 @@ expand_expr_real_1 (tree exp, rtx target > 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_INITIALIZER > - || modifier == EXPAND_CONST_ADDRESS > - || modifier == EXPAND_STACK_PARM) > - ? modifier : EXPAND_NORMAL); > + = 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_INITIALIZER > + || modifier == EXPAND_CONST_ADDRESS > + || modifier == EXPAND_STACK_PARM) > + ? modifier : EXPAND_NORMAL, > + NULL, true); > > > /* If the bitfield is volatile, we want to access it in the > @@ -10302,17 +10271,18 @@ expand_expr_real_1 (tree exp, rtx target > { > /* 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_INITIALIZER > - || modifier == EXPAND_CONST_ADDRESS > - || modifier == EXPAND_STACK_PARM) > - ? modifier : EXPAND_NORMAL); > + = 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_INITIALIZER > + || modifier == EXPAND_CONST_ADDRESS > + || modifier == EXPAND_STACK_PARM) > + ? modifier : EXPAND_NORMAL, > + NULL, true); > > if (MEM_P (orig_op0)) > { > @@ -10339,8 +10309,8 @@ expand_expr_real_1 (tree exp, rtx target > } > > 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)) > @@ -10407,50 +10377,53 @@ expand_expr_real_1 (tree exp, rtx target > 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 = new_rtx; > + 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 = adjust_address (op0, mode, 0); > @@ -10550,7 +10523,7 @@ expand_expr_real_1 (tree exp, rtx target > /* 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 -rupN gcc-4.8-20150122/gcc/expr.h gcc-4.8-20150122.pr57748/gcc/expr.h > --- gcc-4.8-20150122/gcc/expr.h 2014-04-04 16:09:23.000000000 +0200 > +++ gcc-4.8-20150122.pr57748/gcc/expr.h 2015-01-25 15:00:35.240949368 +0100 > @@ -41,7 +41,8 @@ along with GCC; see the file COPYING3. > 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}; > @@ -428,9 +429,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); > > @@ -441,13 +442,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 -rupN gcc-4.8-20150122/gcc/testsuite/gcc.dg/torture/pr57748-1.c > gcc-4.8-20150122.pr57748/gcc/testsuite/gcc.dg/torture/pr57748-1.c > --- gcc-4.8-20150122/gcc/testsuite/gcc.dg/torture/pr57748-1.c 1970-01-01 > 01:00:00.000000000 +0100 > +++ gcc-4.8-20150122.pr57748/gcc/testsuite/gcc.dg/torture/pr57748-1.c > 2015-01-25 15:00:32.660962074 +0100 > @@ -0,0 +1,49 @@ > +/* PR middle-end/57748 */ > +/* { dg-do run } */ > +/* ICE in expand_assignment: > + misalignp == true, !MEM_P (to_rtx), offset != 0, > + => gcc_assert (TREE_CODE (offset) == INTEGER_CST) */ > + > +#include <stdlib.h> > + > +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 (struct T *t) > +{ > + if (t->s.b[0][0] != 3 || t->s.b[0][1] != 4) > + abort (); > +} > + > +int __attribute__((noinline, noclone)) > +get_i (void) > +{ > + return 0; > +} > + > +void __attribute__((noinline, noclone)) > +foo (P *p) > +{ > + V a = { 3, 4 }; > + int i = get_i (); > + p->b[i] = a; > +} > + > +int > +main () > +{ > + struct T *t = (struct T *) calloc (128, 1); > + > + foo (&t->s); > + check (t); > + > + free (t); > + return 0; > +} > diff -rupN gcc-4.8-20150122/gcc/testsuite/gcc.dg/torture/pr57748-2.c > gcc-4.8-20150122.pr57748/gcc/testsuite/gcc.dg/torture/pr57748-2.c > --- gcc-4.8-20150122/gcc/testsuite/gcc.dg/torture/pr57748-2.c 1970-01-01 > 01:00:00.000000000 +0100 > +++ gcc-4.8-20150122.pr57748/gcc/testsuite/gcc.dg/torture/pr57748-2.c > 2015-01-25 15:00:32.660962074 +0100 > @@ -0,0 +1,43 @@ > +/* PR middle-end/57748 */ > +/* { dg-do run } */ > +/* wrong code in expand_assignment: > + misalignp == true, !MEM_P (to_rtx), > + offset == 0, bitpos >= GET_MODE_PRECISION, > + => result = NULL. */ > + > +#include <stdlib.h> > + > +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 (struct T *t) > +{ > + if (t->s.b[0][0] != 3 || t->s.b[0][1] != 4) > + abort (); > +} > + > +void __attribute__((noinline, noclone)) > +foo (P *p) > +{ > + V a = { 3, 4 }; > + p->b[0] = a; > +} > + > +int > +main () > +{ > + struct T *t = (struct T *) calloc (128, 1); > + > + foo (&t->s); > + check (t); > + > + free (t); > + return 0; > +} > diff -rupN gcc-4.8-20150122/gcc/testsuite/gcc.dg/torture/pr57748-3.c > gcc-4.8-20150122.pr57748/gcc/testsuite/gcc.dg/torture/pr57748-3.c > --- gcc-4.8-20150122/gcc/testsuite/gcc.dg/torture/pr57748-3.c 1970-01-01 > 01:00:00.000000000 +0100 > +++ gcc-4.8-20150122.pr57748/gcc/testsuite/gcc.dg/torture/pr57748-3.c > 2015-01-25 15:00:35.240949368 +0100 > @@ -0,0 +1,40 @@ > +/* PR middle-end/57748 */ > +/* { dg-do run } */ > +/* wrong code in expand_expr_real_1. */ > + > +#include <stdlib.h> > + > +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 -rupN gcc-4.8-20150122/gcc/testsuite/gcc.dg/torture/pr57748-4.c > gcc-4.8-20150122.pr57748/gcc/testsuite/gcc.dg/torture/pr57748-4.c > --- gcc-4.8-20150122/gcc/testsuite/gcc.dg/torture/pr57748-4.c 1970-01-01 > 01:00:00.000000000 +0100 > +++ gcc-4.8-20150122.pr57748/gcc/testsuite/gcc.dg/torture/pr57748-4.c > 2015-01-25 15:00:35.240949368 +0100 > @@ -0,0 +1,40 @@ > +/* PR middle-end/57748 */ > +/* { dg-do run } */ > +/* wrong code in expand_expr_real_1. */ > + > +#include <stdlib.h> > + > +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; > +}