2007-09-23 Jakub Jelinek PR middle-end/28755 * expr.c (expand_constructor): New function. (expand_expr_real_1) : Call it. (expand_expr_real_1) : Call it if VALUE is CONSTRUCTOR. * gcc.dg/pr28755.c: New test. --- gcc/expr.c (revision 128684) +++ gcc/expr.c (revision 128685) @@ -6435,6 +6435,89 @@ expand_expr_addr_expr (tree exp, rtx tar return result; } +/* Generate code for computing CONSTRUCTOR EXP. + An rtx for the computed value is returned. If AVOID_TEMP_MEM + is TRUE, instead of creating a temporary variable in memory + NULL is returned and the caller needs to handle it differently. */ + +static rtx +expand_constructor (tree exp, rtx target, enum expand_modifier modifier, + bool avoid_temp_mem) +{ + tree type = TREE_TYPE (exp); + enum machine_mode mode = TYPE_MODE (type); + + /* Try to avoid creating a temporary at all. This is possible + if all of the initializer is zero. + FIXME: try to handle all [0..255] initializers we can handle + with memset. */ + if (TREE_STATIC (exp) + && !TREE_ADDRESSABLE (exp) + && target != 0 && mode == BLKmode + && all_zeros_p (exp)) + { + clear_storage (target, expr_size (exp), BLOCK_OP_NORMAL); + return target; + } + + /* All elts simple constants => refer to a constant in memory. But + if this is a non-BLKmode mode, let it store a field at a time + since that should make a CONST_INT or CONST_DOUBLE when we + fold. Likewise, if we have a target we can use, it is best to + store directly into the target unless the type is large enough + that memcpy will be used. If we are making an initializer and + all operands are constant, put it in memory as well. + + FIXME: Avoid trying to fill vector constructors piece-meal. + Output them with output_constant_def below unless we're sure + they're zeros. This should go away when vector initializers + are treated like VECTOR_CST instead of arrays. */ + if ((TREE_STATIC (exp) + && ((mode == BLKmode + && ! (target != 0 && safe_from_p (target, exp, 1))) + || TREE_ADDRESSABLE (exp) + || (host_integerp (TYPE_SIZE_UNIT (type), 1) + && (! MOVE_BY_PIECES_P + (tree_low_cst (TYPE_SIZE_UNIT (type), 1), + TYPE_ALIGN (type))) + && ! mostly_zeros_p (exp)))) + || ((modifier == EXPAND_INITIALIZER || modifier == EXPAND_CONST_ADDRESS) + && TREE_CONSTANT (exp))) + { + rtx constructor; + + if (avoid_temp_mem) + return NULL_RTX; + + constructor = output_constant_def (exp, 1); + + if (modifier != EXPAND_CONST_ADDRESS + && modifier != EXPAND_INITIALIZER + && modifier != EXPAND_SUM) + constructor = validize_mem (constructor); + + return constructor; + } + + /* Handle calls that pass values in multiple non-contiguous + locations. The Irix 6 ABI has examples of this. */ + if (target == 0 || ! safe_from_p (target, exp, 1) + || GET_CODE (target) == PARALLEL || modifier == EXPAND_STACK_PARM) + { + if (avoid_temp_mem) + return NULL_RTX; + + target + = assign_temp (build_qualified_type (type, (TYPE_QUALS (type) + | (TREE_READONLY (exp) + * TYPE_QUAL_CONST))), + 0, TREE_ADDRESSABLE (exp), 1); + } + + store_constructor (exp, target, 0, int_expr_size (exp)); + return target; +} + /* expand_expr: generate code for computing expression EXP. An rtx for the computed value is returned. The value is never null. @@ -6899,71 +6982,7 @@ expand_expr_real_1 (tree exp, rtx target return const0_rtx; } - /* Try to avoid creating a temporary at all. This is possible - if all of the initializer is zero. - FIXME: try to handle all [0..255] initializers we can handle - with memset. */ - else if (TREE_STATIC (exp) - && !TREE_ADDRESSABLE (exp) - && target != 0 && mode == BLKmode - && all_zeros_p (exp)) - { - clear_storage (target, expr_size (exp), BLOCK_OP_NORMAL); - return target; - } - - /* All elts simple constants => refer to a constant in memory. But - if this is a non-BLKmode mode, let it store a field at a time - since that should make a CONST_INT or CONST_DOUBLE when we - fold. Likewise, if we have a target we can use, it is best to - store directly into the target unless the type is large enough - that memcpy will be used. If we are making an initializer and - all operands are constant, put it in memory as well. - - FIXME: Avoid trying to fill vector constructors piece-meal. - Output them with output_constant_def below unless we're sure - they're zeros. This should go away when vector initializers - are treated like VECTOR_CST instead of arrays. - */ - else if ((TREE_STATIC (exp) - && ((mode == BLKmode - && ! (target != 0 && safe_from_p (target, exp, 1))) - || TREE_ADDRESSABLE (exp) - || (host_integerp (TYPE_SIZE_UNIT (type), 1) - && (! MOVE_BY_PIECES_P - (tree_low_cst (TYPE_SIZE_UNIT (type), 1), - TYPE_ALIGN (type))) - && ! mostly_zeros_p (exp)))) - || ((modifier == EXPAND_INITIALIZER - || modifier == EXPAND_CONST_ADDRESS) - && TREE_CONSTANT (exp))) - { - rtx constructor = output_constant_def (exp, 1); - - if (modifier != EXPAND_CONST_ADDRESS - && modifier != EXPAND_INITIALIZER - && modifier != EXPAND_SUM) - constructor = validize_mem (constructor); - - return constructor; - } - else - { - /* Handle calls that pass values in multiple non-contiguous - locations. The Irix 6 ABI has examples of this. */ - if (target == 0 || ! safe_from_p (target, exp, 1) - || GET_CODE (target) == PARALLEL - || modifier == EXPAND_STACK_PARM) - target - = assign_temp (build_qualified_type (type, - (TYPE_QUALS (type) - | (TREE_READONLY (exp) - * TYPE_QUAL_CONST))), - 0, TREE_ADDRESSABLE (exp), 1); - - store_constructor (exp, target, 0, int_expr_size (exp)); - return target; - } + return expand_constructor (exp, target, modifier, false); case MISALIGNED_INDIRECT_REF: case ALIGN_INDIRECT_REF: @@ -7105,10 +7124,25 @@ expand_expr_real_1 (tree exp, rtx target field, value) if (tree_int_cst_equal (field, index)) { - if (!TREE_SIDE_EFFECTS (value)) - return expand_expr (fold (value), target, tmode, - modifier); - break; + if (TREE_SIDE_EFFECTS (value)) + break; + + if (TREE_CODE (value) == CONSTRUCTOR) + { + /* If VALUE is a CONSTRUCTOR, this + optimization is only useful if + this doesn't store the CONSTRUCTOR + into memory. If it does, it is more + efficient to just load the data from + the array directly. */ + rtx ret = expand_constructor (value, target, + modifier, true); + if (ret == NULL_RTX) + break; + } + + return expand_expr (fold (value), target, tmode, + modifier); } } else if(TREE_CODE (init) == STRING_CST) --- gcc/testsuite/gcc.dg/pr28755.c (revision 0) +++ gcc/testsuite/gcc.dg/pr28755.c (revision 128685) @@ -0,0 +1,22 @@ +/* PR middle-end/28755 */ +/* { dg-do compile } */ +/* { dg-options "-Os" } */ +/* { dg-final { scan-assembler-times "2112543726\|7deadbee" 2 } } */ + +struct S +{ + void *s1; + unsigned s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14; +}; + +const struct S array[] = { + { (void *) 0, 60, 640, 2112543726, 39682, 48, 16, 33, 10, 96, 2, 0, 0, 4 }, + { (void *) 0, 60, 2112543726, 192, 18251, 16, 33, 10, 96, 2, 0, 0, 4, 212 } +}; + +void +foo (struct S *x) +{ + x[0] = array[0]; + x[5] = array[1]; +}