summaryrefslogtreecommitdiffstats
path: root/gcc/stmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/stmt.c')
-rw-r--r--gcc/stmt.c93
1 files changed, 67 insertions, 26 deletions
diff --git a/gcc/stmt.c b/gcc/stmt.c
index 718f2558e7d..3c8e2863edd 100644
--- a/gcc/stmt.c
+++ b/gcc/stmt.c
@@ -403,6 +403,7 @@ static bool check_unique_operand_names (tree, tree);
static char *resolve_operand_name_1 (char *, tree, tree);
static void expand_null_return_1 (rtx);
static enum br_predictor return_prediction (rtx);
+static rtx shift_return_value (rtx);
static void expand_value_return (rtx);
static int tail_recursion_args (tree, tree);
static void expand_cleanups (tree, int, int);
@@ -2902,6 +2903,34 @@ return_prediction (rtx val)
return PRED_NO_PREDICTION;
}
+
+/* If the current function returns values in the most significant part
+ of a register, shift return value VAL appropriately. The mode of
+ the function's return type is known not to be BLKmode. */
+
+static rtx
+shift_return_value (rtx val)
+{
+ tree type;
+
+ type = TREE_TYPE (DECL_RESULT (current_function_decl));
+ if (targetm.calls.return_in_msb (type))
+ {
+ rtx target;
+ HOST_WIDE_INT shift;
+
+ target = DECL_RTL (DECL_RESULT (current_function_decl));
+ shift = (GET_MODE_BITSIZE (GET_MODE (target))
+ - BITS_PER_UNIT * int_size_in_bytes (type));
+ if (shift > 0)
+ val = expand_binop (GET_MODE (target), ashl_optab,
+ gen_lowpart (GET_MODE (target), val),
+ GEN_INT (shift), target, 1, OPTAB_WIDEN);
+ }
+ return val;
+}
+
+
/* Generate RTL to return from the current function, with value VAL. */
static void
@@ -3066,7 +3095,7 @@ expand_return (tree retval)
{
int i;
unsigned HOST_WIDE_INT bitpos, xbitpos;
- unsigned HOST_WIDE_INT big_endian_correction = 0;
+ 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;
@@ -3083,25 +3112,33 @@ expand_return (tree retval)
return;
}
- /* Structures whose size is not a multiple of a word are aligned
- to the least significant byte (to the right). On a BYTES_BIG_ENDIAN
- machine, this means we must skip the empty high order bytes when
- calculating the bit offset. */
- if (BYTES_BIG_ENDIAN
- && bytes % UNITS_PER_WORD)
- big_endian_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
- * BITS_PER_UNIT));
+ /* 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 = big_endian_correction;
+ 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 == big_endian_correction
+ on a word boundary and when xbitpos == padding_correction
(the first time through). */
if (xbitpos % BITS_PER_WORD == 0
- || xbitpos == big_endian_correction)
+ || xbitpos == padding_correction)
{
/* Generate an appropriate register. */
dst = gen_reg_rtx (word_mode);
@@ -3128,21 +3165,25 @@ expand_return (tree retval)
BITS_PER_WORD);
}
- /* 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;
+ 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;
- /* No suitable mode found. */
- if (tmpmode == VOIDmode)
- abort ();
+ /* No suitable mode found. */
+ if (tmpmode == VOIDmode)
+ abort ();
- PUT_MODE (result_rtl, tmpmode);
+ PUT_MODE (result_rtl, tmpmode);
+ }
if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode))
result_reg_mode = word_mode;
@@ -3175,7 +3216,7 @@ expand_return (tree retval)
val = force_not_mem (val);
emit_queue ();
/* Return the calculated value, doing cleanups first. */
- expand_value_return (val);
+ expand_value_return (shift_return_value (val));
}
else
{
OpenPOWER on IntegriCloud