expr.c (optimize_bitfield_assignment_op): Split out from ...
* expr.c (optimize_bitfield_assignment_op): Split out from ... (expand_assignment): ... here. Use handled_component_p to gate get_inner_reference code. Simplify MEM handling. Special case CONCAT destinations. From-SVN: r91570
This commit is contained in:
parent
d7cf6dd66a
commit
8c1cfd5aa4
|
@ -1,5 +1,10 @@
|
||||||
2004-12-01 Richard Henderson <rth@redhat.com>
|
2004-12-01 Richard Henderson <rth@redhat.com>
|
||||||
|
|
||||||
|
* expr.c (optimize_bitfield_assignment_op): Split out from ...
|
||||||
|
(expand_assignment): ... here. Use handled_component_p to gate
|
||||||
|
get_inner_reference code. Simplify MEM handling. Special case
|
||||||
|
CONCAT destinations.
|
||||||
|
|
||||||
* expmed.c (store_bit_field): Use simplify_gen_subreg instead
|
* expmed.c (store_bit_field): Use simplify_gen_subreg instead
|
||||||
of gen_rtx_SUBREG directly.
|
of gen_rtx_SUBREG directly.
|
||||||
|
|
||||||
|
|
280
gcc/expr.c
280
gcc/expr.c
|
@ -3499,6 +3499,124 @@ get_subtarget (rtx x)
|
||||||
? 0 : x);
|
? 0 : x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A subroutine of expand_assignment. Optimize FIELD op= VAL, where
|
||||||
|
FIELD is a bitfield. Returns true if the optimization was successful,
|
||||||
|
and there's nothing else to do. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
optimize_bitfield_assignment_op (unsigned HOST_WIDE_INT bitsize,
|
||||||
|
unsigned HOST_WIDE_INT bitpos,
|
||||||
|
enum machine_mode mode1, rtx str_rtx,
|
||||||
|
tree to, tree src)
|
||||||
|
{
|
||||||
|
enum machine_mode str_mode = GET_MODE (str_rtx);
|
||||||
|
unsigned int str_bitsize = GET_MODE_BITSIZE (str_mode);
|
||||||
|
tree op0, op1;
|
||||||
|
rtx value, result;
|
||||||
|
optab binop;
|
||||||
|
|
||||||
|
if (mode1 != VOIDmode
|
||||||
|
|| bitsize >= BITS_PER_WORD
|
||||||
|
|| str_bitsize > BITS_PER_WORD
|
||||||
|
|| TREE_SIDE_EFFECTS (to)
|
||||||
|
|| TREE_THIS_VOLATILE (to))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
STRIP_NOPS (src);
|
||||||
|
if (!BINARY_CLASS_P (src)
|
||||||
|
|| TREE_CODE (TREE_TYPE (src)) != INTEGER_TYPE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
op0 = TREE_OPERAND (src, 0);
|
||||||
|
op1 = TREE_OPERAND (src, 1);
|
||||||
|
STRIP_NOPS (op0);
|
||||||
|
|
||||||
|
if (!operand_equal_p (to, op0, 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (MEM_P (str_rtx))
|
||||||
|
{
|
||||||
|
unsigned HOST_WIDE_INT offset1;
|
||||||
|
|
||||||
|
if (str_bitsize == 0 || str_bitsize > BITS_PER_WORD)
|
||||||
|
str_mode = word_mode;
|
||||||
|
str_mode = get_best_mode (bitsize, bitpos,
|
||||||
|
MEM_ALIGN (str_rtx), str_mode, 0);
|
||||||
|
if (str_mode == VOIDmode)
|
||||||
|
return false;
|
||||||
|
str_bitsize = GET_MODE_BITSIZE (str_mode);
|
||||||
|
|
||||||
|
offset1 = bitpos;
|
||||||
|
bitpos %= str_bitsize;
|
||||||
|
offset1 = (offset1 - bitpos) / BITS_PER_UNIT;
|
||||||
|
str_rtx = adjust_address (str_rtx, str_mode, offset1);
|
||||||
|
}
|
||||||
|
else if (!REG_P (str_rtx) && GET_CODE (str_rtx) != SUBREG)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* If the bit field covers the whole REG/MEM, store_field
|
||||||
|
will likely generate better code. */
|
||||||
|
if (bitsize >= str_bitsize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* We can't handle fields split across multiple entities. */
|
||||||
|
if (bitpos + bitsize > str_bitsize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (BYTES_BIG_ENDIAN)
|
||||||
|
bitpos = str_bitsize - bitpos - bitsize;
|
||||||
|
|
||||||
|
switch (TREE_CODE (src))
|
||||||
|
{
|
||||||
|
case PLUS_EXPR:
|
||||||
|
case MINUS_EXPR:
|
||||||
|
/* For now, just optimize the case of the topmost bitfield
|
||||||
|
where we don't need to do any masking and also
|
||||||
|
1 bit bitfields where xor can be used.
|
||||||
|
We might win by one instruction for the other bitfields
|
||||||
|
too if insv/extv instructions aren't used, so that
|
||||||
|
can be added later. */
|
||||||
|
if (bitpos + bitsize != str_bitsize
|
||||||
|
&& (bitsize != 1 || TREE_CODE (op1) != INTEGER_CST))
|
||||||
|
break;
|
||||||
|
|
||||||
|
value = expand_expr (op1, NULL_RTX, str_mode, 0);
|
||||||
|
value = convert_modes (str_mode,
|
||||||
|
TYPE_MODE (TREE_TYPE (op1)), value,
|
||||||
|
TYPE_UNSIGNED (TREE_TYPE (op1)));
|
||||||
|
|
||||||
|
/* We may be accessing data outside the field, which means
|
||||||
|
we can alias adjacent data. */
|
||||||
|
if (MEM_P (str_rtx))
|
||||||
|
{
|
||||||
|
str_rtx = shallow_copy_rtx (str_rtx);
|
||||||
|
set_mem_alias_set (str_rtx, 0);
|
||||||
|
set_mem_expr (str_rtx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
binop = TREE_CODE (src) == PLUS_EXPR ? add_optab : sub_optab;
|
||||||
|
if (bitsize == 1 && bitpos + bitsize != str_bitsize)
|
||||||
|
{
|
||||||
|
value = expand_and (str_mode, value, const1_rtx, NULL);
|
||||||
|
binop = xor_optab;
|
||||||
|
}
|
||||||
|
value = expand_shift (LSHIFT_EXPR, str_mode, value,
|
||||||
|
build_int_cst (NULL_TREE, bitpos),
|
||||||
|
NULL_RTX, 1);
|
||||||
|
result = expand_binop (str_mode, binop, str_rtx,
|
||||||
|
value, str_rtx, 1, OPTAB_WIDEN);
|
||||||
|
if (result != str_rtx)
|
||||||
|
emit_move_insn (str_rtx, result);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Expand an assignment that stores the value of FROM into TO. */
|
/* Expand an assignment that stores the value of FROM into TO. */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -3520,9 +3638,7 @@ expand_assignment (tree to, tree from)
|
||||||
Assignment of an array element at a constant index, and assignment of
|
Assignment of an array element at a constant index, and assignment of
|
||||||
an array element in an unaligned packed structure field, has the same
|
an array element in an unaligned packed structure field, has the same
|
||||||
problem. */
|
problem. */
|
||||||
|
if (handled_component_p (to)
|
||||||
if (TREE_CODE (to) == COMPONENT_REF || TREE_CODE (to) == BIT_FIELD_REF
|
|
||||||
|| TREE_CODE (to) == ARRAY_REF || TREE_CODE (to) == ARRAY_RANGE_REF
|
|
||||||
|| TREE_CODE (TREE_TYPE (to)) == ARRAY_TYPE)
|
|| TREE_CODE (TREE_TYPE (to)) == ARRAY_TYPE)
|
||||||
{
|
{
|
||||||
enum machine_mode mode1;
|
enum machine_mode mode1;
|
||||||
|
@ -3575,153 +3691,43 @@ expand_assignment (tree to, tree from)
|
||||||
offset));
|
offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MEM_P (to_rtx))
|
/* Handle expand_expr of a complex value returning a CONCAT. */
|
||||||
|
if (GET_CODE (to_rtx) == CONCAT)
|
||||||
{
|
{
|
||||||
/* If the field is at offset zero, we could have been given the
|
gcc_assert (bitpos == 0 || bitpos == GET_MODE_BITSIZE (mode1));
|
||||||
DECL_RTX of the parent struct. Don't munge it. */
|
result = store_expr (from, XEXP (to_rtx, bitpos != 0), false);
|
||||||
to_rtx = shallow_copy_rtx (to_rtx);
|
|
||||||
|
|
||||||
set_mem_attributes_minus_bitpos (to_rtx, to, 0, bitpos);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/* Deal with volatile and readonly fields. The former is only done
|
|
||||||
for MEM. Also set MEM_KEEP_ALIAS_SET_P if needed. */
|
|
||||||
if (volatilep && MEM_P (to_rtx))
|
|
||||||
{
|
{
|
||||||
if (to_rtx == orig_to_rtx)
|
if (MEM_P (to_rtx))
|
||||||
to_rtx = copy_rtx (to_rtx);
|
|
||||||
MEM_VOLATILE_P (to_rtx) = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MEM_P (to_rtx) && ! can_address_p (to))
|
|
||||||
{
|
|
||||||
if (to_rtx == orig_to_rtx)
|
|
||||||
to_rtx = copy_rtx (to_rtx);
|
|
||||||
MEM_KEEP_ALIAS_SET_P (to_rtx) = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Optimize bitfld op= val in certain cases. */
|
|
||||||
while (mode1 == VOIDmode
|
|
||||||
&& bitsize > 0 && bitsize < BITS_PER_WORD
|
|
||||||
&& GET_MODE_BITSIZE (GET_MODE (to_rtx)) <= BITS_PER_WORD
|
|
||||||
&& !TREE_SIDE_EFFECTS (to)
|
|
||||||
&& !TREE_THIS_VOLATILE (to))
|
|
||||||
{
|
|
||||||
tree src, op0, op1;
|
|
||||||
rtx value, str_rtx = to_rtx;
|
|
||||||
HOST_WIDE_INT bitpos1 = bitpos;
|
|
||||||
optab binop;
|
|
||||||
|
|
||||||
src = from;
|
|
||||||
STRIP_NOPS (src);
|
|
||||||
if (TREE_CODE (TREE_TYPE (src)) != INTEGER_TYPE
|
|
||||||
|| !BINARY_CLASS_P (src))
|
|
||||||
break;
|
|
||||||
|
|
||||||
op0 = TREE_OPERAND (src, 0);
|
|
||||||
op1 = TREE_OPERAND (src, 1);
|
|
||||||
STRIP_NOPS (op0);
|
|
||||||
|
|
||||||
if (! operand_equal_p (to, op0, 0))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (MEM_P (str_rtx))
|
|
||||||
{
|
{
|
||||||
enum machine_mode mode = GET_MODE (str_rtx);
|
/* If the field is at offset zero, we could have been given the
|
||||||
HOST_WIDE_INT offset1;
|
DECL_RTX of the parent struct. Don't munge it. */
|
||||||
|
to_rtx = shallow_copy_rtx (to_rtx);
|
||||||
|
|
||||||
if (GET_MODE_BITSIZE (mode) == 0
|
set_mem_attributes_minus_bitpos (to_rtx, to, 0, bitpos);
|
||||||
|| GET_MODE_BITSIZE (mode) > BITS_PER_WORD)
|
|
||||||
mode = word_mode;
|
|
||||||
mode = get_best_mode (bitsize, bitpos1, MEM_ALIGN (str_rtx),
|
|
||||||
mode, 0);
|
|
||||||
if (mode == VOIDmode)
|
|
||||||
break;
|
|
||||||
|
|
||||||
offset1 = bitpos1;
|
/* Deal with volatile and readonly fields. The former is only
|
||||||
bitpos1 %= GET_MODE_BITSIZE (mode);
|
done for MEM. Also set MEM_KEEP_ALIAS_SET_P if needed. */
|
||||||
offset1 = (offset1 - bitpos1) / BITS_PER_UNIT;
|
if (volatilep)
|
||||||
str_rtx = adjust_address (str_rtx, mode, offset1);
|
MEM_VOLATILE_P (to_rtx) = 1;
|
||||||
}
|
|
||||||
else if (!REG_P (str_rtx) && GET_CODE (str_rtx) != SUBREG)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* If the bit field covers the whole REG/MEM, store_field
|
if (!can_address_p (to))
|
||||||
will likely generate better code. */
|
MEM_KEEP_ALIAS_SET_P (to_rtx) = 1;
|
||||||
if (bitsize >= GET_MODE_BITSIZE (GET_MODE (str_rtx)))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* We can't handle fields split across multiple entities. */
|
|
||||||
if (bitpos1 + bitsize > GET_MODE_BITSIZE (GET_MODE (str_rtx)))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (BYTES_BIG_ENDIAN)
|
|
||||||
bitpos1 = GET_MODE_BITSIZE (GET_MODE (str_rtx)) - bitpos1
|
|
||||||
- bitsize;
|
|
||||||
|
|
||||||
/* Special case some bitfield op= exp. */
|
|
||||||
switch (TREE_CODE (src))
|
|
||||||
{
|
|
||||||
case PLUS_EXPR:
|
|
||||||
case MINUS_EXPR:
|
|
||||||
/* For now, just optimize the case of the topmost bitfield
|
|
||||||
where we don't need to do any masking and also
|
|
||||||
1 bit bitfields where xor can be used.
|
|
||||||
We might win by one instruction for the other bitfields
|
|
||||||
too if insv/extv instructions aren't used, so that
|
|
||||||
can be added later. */
|
|
||||||
if (bitpos1 + bitsize != GET_MODE_BITSIZE (GET_MODE (str_rtx))
|
|
||||||
&& (bitsize != 1 || TREE_CODE (op1) != INTEGER_CST))
|
|
||||||
break;
|
|
||||||
value = expand_expr (op1, NULL_RTX, GET_MODE (str_rtx), 0);
|
|
||||||
value = convert_modes (GET_MODE (str_rtx),
|
|
||||||
TYPE_MODE (TREE_TYPE (op1)), value,
|
|
||||||
TYPE_UNSIGNED (TREE_TYPE (op1)));
|
|
||||||
|
|
||||||
/* We may be accessing data outside the field, which means
|
|
||||||
we can alias adjacent data. */
|
|
||||||
if (MEM_P (str_rtx))
|
|
||||||
{
|
|
||||||
str_rtx = shallow_copy_rtx (str_rtx);
|
|
||||||
set_mem_alias_set (str_rtx, 0);
|
|
||||||
set_mem_expr (str_rtx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
binop = TREE_CODE (src) == PLUS_EXPR ? add_optab : sub_optab;
|
|
||||||
if (bitsize == 1
|
|
||||||
&& bitpos1 + bitsize != GET_MODE_BITSIZE (GET_MODE (str_rtx)))
|
|
||||||
{
|
|
||||||
value = expand_and (GET_MODE (str_rtx), value, const1_rtx,
|
|
||||||
NULL_RTX);
|
|
||||||
binop = xor_optab;
|
|
||||||
}
|
|
||||||
value = expand_shift (LSHIFT_EXPR, GET_MODE (str_rtx), value,
|
|
||||||
build_int_cst (NULL_TREE, bitpos1),
|
|
||||||
NULL_RTX, 1);
|
|
||||||
result = expand_binop (GET_MODE (str_rtx), binop, str_rtx,
|
|
||||||
value, str_rtx, 1, OPTAB_WIDEN);
|
|
||||||
if (result != str_rtx)
|
|
||||||
emit_move_insn (str_rtx, result);
|
|
||||||
free_temp_slots ();
|
|
||||||
pop_temp_slots ();
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
if (optimize_bitfield_assignment_op (bitsize, bitpos, mode1,
|
||||||
|
to_rtx, to, from))
|
||||||
|
result = NULL;
|
||||||
|
else
|
||||||
|
result = store_field (to_rtx, bitsize, bitpos, mode1, from,
|
||||||
|
TREE_TYPE (tem), get_alias_set (to));
|
||||||
}
|
}
|
||||||
|
|
||||||
result = store_field (to_rtx, bitsize, bitpos, mode1, from,
|
if (result)
|
||||||
TREE_TYPE (tem), get_alias_set (to));
|
preserve_temp_slots (result);
|
||||||
|
|
||||||
preserve_temp_slots (result);
|
|
||||||
free_temp_slots ();
|
free_temp_slots ();
|
||||||
pop_temp_slots ();
|
pop_temp_slots ();
|
||||||
|
|
||||||
/* If the value is meaningful, convert RESULT to the proper mode.
|
|
||||||
Otherwise, return nothing. */
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue