expr.h (copy_blkmode_to_reg): Declare.

gcc/
	* expr.h (copy_blkmode_to_reg): Declare.
	* expr.c (copy_blkmode_to_reg): New function.
	(expand_assignment): Don't expand register RESULT_DECLs before
	the lhs.  Use copy_blkmode_to_reg to copy BLKmode values into a
	RESULT_DECL register.
	(expand_expr_real_1): Handle BLKmode decls when looking for promotion.
	* stmt.c (expand_return): Move BLKmode-to-register code into
	copy_blkmode_to_reg.

From-SVN: r179839
This commit is contained in:
Richard Sandiford 2011-10-12 07:46:58 +00:00 committed by Richard Sandiford
parent e755e54342
commit 2ba87a294f
5 changed files with 165 additions and 113 deletions

View File

@ -1,3 +1,14 @@
2011-10-12 Richard Sandiford <richard.sandiford@linaro.org>
* expr.h (copy_blkmode_to_reg): Declare.
* expr.c (copy_blkmode_to_reg): New function.
(expand_assignment): Don't expand register RESULT_DECLs before
the lhs. Use copy_blkmode_to_reg to copy BLKmode values into a
RESULT_DECL register.
(expand_expr_real_1): Handle BLKmode decls when looking for promotion.
* stmt.c (expand_return): Move BLKmode-to-register code into
copy_blkmode_to_reg.
2011-10-11 Eric Botcazou <ebotcazou@adacore.com>
PR target/49965

View File

@ -2180,6 +2180,112 @@ copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type)
return tgtblk;
}
/* Copy BLKmode value SRC into a register of mode MODE. Return the
register if it contains any data, otherwise return null.
This is used on targets that return BLKmode values in registers. */
rtx
copy_blkmode_to_reg (enum machine_mode mode, tree src)
{
int i, n_regs;
unsigned HOST_WIDE_INT bitpos, xbitpos, padding_correction = 0, bytes;
unsigned int bitsize;
rtx *dst_words, dst, x, src_word = NULL_RTX, dst_word = NULL_RTX;
enum machine_mode dst_mode;
gcc_assert (TYPE_MODE (TREE_TYPE (src)) == BLKmode);
x = expand_normal (src);
bytes = int_size_in_bytes (TREE_TYPE (src));
if (bytes == 0)
return NULL_RTX;
/* If the structure doesn't take up a whole number of words, see
whether the register value should be padded on the left or on
the right. Set PADDING_CORRECTION to the number of padding
bits needed on the left side.
In most ABIs, the structure will be returned at the least end of
the register, which translates to right padding on little-endian
targets and left padding on big-endian targets. The opposite
holds if the structure is returned at the most significant
end of the register. */
if (bytes % UNITS_PER_WORD != 0
&& (targetm.calls.return_in_msb (TREE_TYPE (src))
? !BYTES_BIG_ENDIAN
: BYTES_BIG_ENDIAN))
padding_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
* BITS_PER_UNIT));
n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
dst_words = XALLOCAVEC (rtx, n_regs);
bitsize = MIN (TYPE_ALIGN (TREE_TYPE (src)), BITS_PER_WORD);
/* Copy the structure BITSIZE bits at a time. */
for (bitpos = 0, xbitpos = padding_correction;
bitpos < bytes * BITS_PER_UNIT;
bitpos += bitsize, xbitpos += bitsize)
{
/* We need a new destination pseudo each time xbitpos is
on a word boundary and when xbitpos == padding_correction
(the first time through). */
if (xbitpos % BITS_PER_WORD == 0
|| xbitpos == padding_correction)
{
/* Generate an appropriate register. */
dst_word = gen_reg_rtx (word_mode);
dst_words[xbitpos / BITS_PER_WORD] = dst_word;
/* Clear the destination before we move anything into it. */
emit_move_insn (dst_word, CONST0_RTX (word_mode));
}
/* We need a new source operand each time bitpos is on a word
boundary. */
if (bitpos % BITS_PER_WORD == 0)
src_word = operand_subword_force (x, bitpos / BITS_PER_WORD, BLKmode);
/* Use bitpos for the source extraction (left justified) and
xbitpos for the destination store (right justified). */
store_bit_field (dst_word, bitsize, xbitpos % BITS_PER_WORD,
0, 0, word_mode,
extract_bit_field (src_word, bitsize,
bitpos % BITS_PER_WORD, 1, false,
NULL_RTX, word_mode, word_mode));
}
if (mode == BLKmode)
{
/* Find the smallest integer mode large enough to hold the
entire structure. */
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
/* Have we found a large enough mode? */
if (GET_MODE_SIZE (mode) >= bytes)
break;
/* A suitable mode should have been found. */
gcc_assert (mode != VOIDmode);
}
if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (word_mode))
dst_mode = word_mode;
else
dst_mode = mode;
dst = gen_reg_rtx (dst_mode);
for (i = 0; i < n_regs; i++)
emit_move_insn (operand_subword (dst, i, 0, dst_mode), dst_words[i]);
if (mode != dst_mode)
dst = gen_lowpart (mode, dst);
return dst;
}
/* Add a USE expression for REG to the (possibly empty) list pointed
to by CALL_FUSAGE. REG must denote a hard register. */
@ -4720,7 +4826,9 @@ expand_assignment (tree to, tree from, bool nontemporal)
if (TREE_CODE (from) == CALL_EXPR && ! aggregate_value_p (from, from)
&& COMPLETE_TYPE_P (TREE_TYPE (from))
&& TREE_CODE (TYPE_SIZE (TREE_TYPE (from))) == INTEGER_CST
&& ! (((TREE_CODE (to) == VAR_DECL || TREE_CODE (to) == PARM_DECL)
&& ! (((TREE_CODE (to) == VAR_DECL
|| TREE_CODE (to) == PARM_DECL
|| TREE_CODE (to) == RESULT_DECL)
&& REG_P (DECL_RTL (to)))
|| TREE_CODE (to) == SSA_NAME))
{
@ -4766,12 +4874,15 @@ expand_assignment (tree to, tree from, bool nontemporal)
rtx temp;
push_temp_slots ();
temp = expand_expr (from, NULL_RTX, GET_MODE (to_rtx), EXPAND_NORMAL);
if (REG_P (to_rtx) && TYPE_MODE (TREE_TYPE (from)) == BLKmode)
temp = copy_blkmode_to_reg (GET_MODE (to_rtx), from);
else
temp = expand_expr (from, NULL_RTX, GET_MODE (to_rtx), EXPAND_NORMAL);
if (GET_CODE (to_rtx) == PARALLEL)
emit_group_load (to_rtx, temp, TREE_TYPE (from),
int_size_in_bytes (TREE_TYPE (from)));
else
else if (temp)
emit_move_insn (to_rtx, temp);
preserve_temp_slots (to_rtx);
@ -8955,10 +9066,15 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
return temp;
}
/* If the mode of DECL_RTL does not match that of the decl, it
must be a promoted value. We return a SUBREG of the wanted mode,
but mark it so that we know that it was already extended. */
if (REG_P (decl_rtl) && GET_MODE (decl_rtl) != DECL_MODE (exp))
/* If the mode of DECL_RTL does not match that of the decl,
there are two cases: we are dealing with a BLKmode value
that is returned in a register, or we are dealing with
a promoted value. In the latter case, return a SUBREG
of the wanted mode, but mark it so that we know that it
was already extended. */
if (REG_P (decl_rtl)
&& DECL_MODE (exp) != BLKmode
&& GET_MODE (decl_rtl) != DECL_MODE (exp))
{
enum machine_mode pmode;

View File

@ -325,6 +325,8 @@ extern rtx copy_blkmode_from_reg (rtx, rtx, tree);
Mode is TYPE_MODE of the non-promoted parameter, or VOIDmode. */
extern void use_reg_mode (rtx *, rtx, enum machine_mode);
extern rtx copy_blkmode_to_reg (enum machine_mode, tree);
/* Mark REG as holding a parameter for the next CALL_INSN. */
static inline void
use_reg (rtx *fusage, rtx reg)

View File

@ -1685,120 +1685,21 @@ expand_return (tree retval)
expand_value_return (result_rtl);
/* If the result is an aggregate that is being returned in one (or more)
registers, load the registers here. The compiler currently can't handle
copying a BLKmode value into registers. We could put this code in a
more general area (for use by everyone instead of just function
call/return), but until this feature is generally usable it is kept here
(and in expand_call). */
registers, load the registers here. */
else if (retval_rhs != 0
&& TYPE_MODE (TREE_TYPE (retval_rhs)) == BLKmode
&& REG_P (result_rtl))
{
int i;
unsigned HOST_WIDE_INT bitpos, xbitpos;
unsigned HOST_WIDE_INT padding_correction = 0;
unsigned HOST_WIDE_INT bytes
= int_size_in_bytes (TREE_TYPE (retval_rhs));
int n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
unsigned int bitsize
= MIN (TYPE_ALIGN (TREE_TYPE (retval_rhs)), BITS_PER_WORD);
rtx *result_pseudos = XALLOCAVEC (rtx, n_regs);
rtx result_reg, src = NULL_RTX, dst = NULL_RTX;
rtx result_val = expand_normal (retval_rhs);
enum machine_mode tmpmode, result_reg_mode;
if (bytes == 0)
val = copy_blkmode_to_reg (GET_MODE (result_rtl), retval_rhs);
if (val)
{
expand_null_return ();
return;
/* Use the mode of the result value on the return register. */
PUT_MODE (result_rtl, GET_MODE (val));
expand_value_return (val);
}
/* If the structure doesn't take up a whole number of words, see
whether the register value should be padded on the left or on
the right. Set PADDING_CORRECTION to the number of padding
bits needed on the left side.
In most ABIs, the structure will be returned at the least end of
the register, which translates to right padding on little-endian
targets and left padding on big-endian targets. The opposite
holds if the structure is returned at the most significant
end of the register. */
if (bytes % UNITS_PER_WORD != 0
&& (targetm.calls.return_in_msb (TREE_TYPE (retval_rhs))
? !BYTES_BIG_ENDIAN
: BYTES_BIG_ENDIAN))
padding_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
* BITS_PER_UNIT));
/* Copy the structure BITSIZE bits at a time. */
for (bitpos = 0, xbitpos = padding_correction;
bitpos < bytes * BITS_PER_UNIT;
bitpos += bitsize, xbitpos += bitsize)
{
/* We need a new destination pseudo each time xbitpos is
on a word boundary and when xbitpos == padding_correction
(the first time through). */
if (xbitpos % BITS_PER_WORD == 0
|| xbitpos == padding_correction)
{
/* Generate an appropriate register. */
dst = gen_reg_rtx (word_mode);
result_pseudos[xbitpos / BITS_PER_WORD] = dst;
/* Clear the destination before we move anything into it. */
emit_move_insn (dst, CONST0_RTX (GET_MODE (dst)));
}
/* We need a new source operand each time bitpos is on a word
boundary. */
if (bitpos % BITS_PER_WORD == 0)
src = operand_subword_force (result_val,
bitpos / BITS_PER_WORD,
BLKmode);
/* Use bitpos for the source extraction (left justified) and
xbitpos for the destination store (right justified). */
store_bit_field (dst, bitsize, xbitpos % BITS_PER_WORD,
0, 0, word_mode,
extract_bit_field (src, bitsize,
bitpos % BITS_PER_WORD, 1, false,
NULL_RTX, word_mode, word_mode));
}
tmpmode = GET_MODE (result_rtl);
if (tmpmode == BLKmode)
{
/* Find the smallest integer mode large enough to hold the
entire structure and use that mode instead of BLKmode
on the USE insn for the return register. */
for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmpmode != VOIDmode;
tmpmode = GET_MODE_WIDER_MODE (tmpmode))
/* Have we found a large enough mode? */
if (GET_MODE_SIZE (tmpmode) >= bytes)
break;
/* A suitable mode should have been found. */
gcc_assert (tmpmode != VOIDmode);
PUT_MODE (result_rtl, tmpmode);
}
if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode))
result_reg_mode = word_mode;
else
result_reg_mode = tmpmode;
result_reg = gen_reg_rtx (result_reg_mode);
for (i = 0; i < n_regs; i++)
emit_move_insn (operand_subword (result_reg, i, 0, result_reg_mode),
result_pseudos[i]);
if (tmpmode != result_reg_mode)
result_reg = gen_lowpart (tmpmode, result_reg);
expand_value_return (result_reg);
expand_null_return ();
}
else if (retval_rhs != 0
&& !VOID_TYPE_P (TREE_TYPE (retval_rhs))

View File

@ -0,0 +1,22 @@
template<int N> struct val { char a[N]; };
class Base
{
public:
virtual val<1> get1() const = 0;
virtual val<2> get2() const = 0;
virtual val<3> get3() const = 0;
virtual val<4> get4() const = 0;
};
class Derived : public virtual Base
{
public:
virtual val<1> get1() const { return foo->get1(); }
virtual val<2> get2() const { return foo->get2(); }
virtual val<3> get3() const { return foo->get3(); }
virtual val<4> get4() const { return foo->get4(); }
Base *foo;
};
Base* make() { return new Derived; }