aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Sandiford <rsandifo@redhat.com>2004-11-24 18:50:26 +0000
committerRichard Sandiford <rsandifo@gcc.gnu.org>2004-11-24 18:50:26 +0000
commitbef5d8b61f6732bec53128324913bfa8a5093a07 (patch)
treeb60ced5605459fa37b62bcb5a6c1cced24e7b3fd
parent6e2993bf5b71c17c2b60e0dec787750f44925e66 (diff)
downloadgcc-bef5d8b61f6732bec53128324913bfa8a5093a07.zip
gcc-bef5d8b61f6732bec53128324913bfa8a5093a07.tar.gz
gcc-bef5d8b61f6732bec53128324913bfa8a5093a07.tar.bz2
optabs.h (force_expand_binop): Declare.
* optabs.h (force_expand_binop): Declare. * optabs.c (force_expand_binop): Export. * stmt.c (shift_return_value): Delete. (expand_return): Don't call it. * expr.h (shift_return_value): Declare. * calls.c (shift_returned_value): Delete in favor of... (shift_return_value): ...this new function. Leave the caller to check for non-BLKmode values passed in the msb of a register. Take said mode and a shift direction as argument. Operate on the hard function value, not a pseudo. (expand_call): Adjust accordingly. * function.c (expand_function_start): If a non-BLKmode return value is padded at the last significant end of the return register, use the return value's natural mode for the DECL_RESULT, not the mode of the padded register. (expand_function_end): Shift the same sort of return values left by the appropriate amount. From-SVN: r91187
-rw-r--r--gcc/ChangeLog20
-rw-r--r--gcc/calls.c70
-rw-r--r--gcc/expr.h2
-rw-r--r--gcc/function.c51
-rw-r--r--gcc/optabs.c2
-rw-r--r--gcc/optabs.h3
-rw-r--r--gcc/stmt.c30
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/20041124-1.c10
9 files changed, 110 insertions, 82 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index d2ffdac..3b2def7 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,23 @@
+2004-11-24 Richard Sandiford <rsandifo@redhat.com>
+
+ * optabs.h (force_expand_binop): Declare.
+ * optabs.c (force_expand_binop): Export.
+ * stmt.c (shift_return_value): Delete.
+ (expand_return): Don't call it.
+ * expr.h (shift_return_value): Declare.
+ * calls.c (shift_returned_value): Delete in favor of...
+ (shift_return_value): ...this new function. Leave the caller to check
+ for non-BLKmode values passed in the msb of a register. Take said mode
+ and a shift direction as argument. Operate on the hard function value,
+ not a pseudo.
+ (expand_call): Adjust accordingly.
+ * function.c (expand_function_start): If a non-BLKmode return value
+ is padded at the last significant end of the return register, use the
+ return value's natural mode for the DECL_RESULT, not the mode of the
+ padded register.
+ (expand_function_end): Shift the same sort of return values left by
+ the appropriate amount.
+
2004-11-24 Matt Austern <austern@apple.com>
* recog.c (recog_memoized_1): Remove.
diff --git a/gcc/calls.c b/gcc/calls.c
index 44806d4c..3c4d13e 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -147,7 +147,6 @@ static int check_sibcall_argument_overlap (rtx, struct arg_data *, int);
static int combine_pending_stack_adjustment_and_call (int, struct args_size *,
unsigned int);
-static bool shift_returned_value (tree, rtx *);
static tree split_complex_values (tree);
static tree split_complex_types (tree);
@@ -1715,39 +1714,27 @@ check_sibcall_argument_overlap (rtx insn, struct arg_data *arg, int mark_stored_
return insn != NULL_RTX;
}
-/* If function value *VALUE was returned at the most significant end of a
- register, shift it towards the least significant end and convert it to
- TYPE's mode. Return true and update *VALUE if some action was needed.
+/* Given that a function returns a value of mode MODE at the most
+ significant end of hard register VALUE, shift VALUE left or right
+ as specified by LEFT_P. Return true if some action was needed. */
- TYPE is the type of the function's return value, which is known not
- to have mode BLKmode. */
-
-static bool
-shift_returned_value (tree type, rtx *value)
+bool
+shift_return_value (enum machine_mode mode, bool left_p, rtx value)
{
- if (targetm.calls.return_in_msb (type))
- {
- HOST_WIDE_INT shift;
+ HOST_WIDE_INT shift;
- shift = (GET_MODE_BITSIZE (GET_MODE (*value))
- - BITS_PER_UNIT * int_size_in_bytes (type));
- if (shift > 0)
- {
- /* Shift the value into the low part of the register. */
- *value = expand_binop (GET_MODE (*value), lshr_optab, *value,
- GEN_INT (shift), 0, 1, OPTAB_WIDEN);
-
- /* Truncate it to the type's mode, or its integer equivalent.
- This is subject to TRULY_NOOP_TRUNCATION. */
- *value = convert_to_mode (int_mode_for_mode (TYPE_MODE (type)),
- *value, 0);
-
- /* Now convert it to the final form. */
- *value = gen_lowpart (TYPE_MODE (type), *value);
- return true;
- }
- }
- return false;
+ gcc_assert (REG_P (value) && HARD_REGISTER_P (value));
+ shift = GET_MODE_BITSIZE (GET_MODE (value)) - GET_MODE_BITSIZE (mode);
+ if (shift == 0)
+ return false;
+
+ /* Use ashr rather than lshr for right shifts. This is for the benefit
+ of the MIPS port, which requires SImode values to be sign-extended
+ when stored in 64-bit registers. */
+ if (!force_expand_binop (GET_MODE (value), left_p ? ashl_optab : ashr_optab,
+ value, GEN_INT (shift), value, 1, OPTAB_WIDEN))
+ gcc_unreachable ();
+ return true;
}
/* Remove all REG_EQUIV notes found in the insn chain. */
@@ -2660,6 +2647,20 @@ expand_call (tree exp, rtx target, int ignore)
next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
flags, & args_so_far);
+ /* If a non-BLKmode value is returned at the most significant end
+ of a register, shift the register right by the appropriate amount
+ and update VALREG accordingly. BLKmode values are handled by the
+ group load/store machinery below. */
+ if (!structure_value_addr
+ && !pcc_struct_value
+ && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
+ && targetm.calls.return_in_msb (TREE_TYPE (exp)))
+ {
+ if (shift_return_value (TYPE_MODE (TREE_TYPE (exp)), false, valreg))
+ sibcall_failure = 1;
+ valreg = gen_rtx_REG (TYPE_MODE (TREE_TYPE (exp)), REGNO (valreg));
+ }
+
/* If call is cse'able, make appropriate pair of reg-notes around it.
Test valreg so we don't crash; may safely ignore `const'
if return type is void. Disable for PARALLEL return values, because
@@ -2851,12 +2852,7 @@ expand_call (tree exp, rtx target, int ignore)
sibcall_failure = 1;
}
else
- {
- if (shift_returned_value (TREE_TYPE (exp), &valreg))
- sibcall_failure = 1;
-
- target = copy_to_reg (valreg);
- }
+ target = copy_to_reg (valreg);
if (targetm.calls.promote_function_return(funtype))
{
diff --git a/gcc/expr.h b/gcc/expr.h
index f6f32e8..780ad4d 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -551,6 +551,8 @@ extern rtx hard_function_value (tree, tree, int);
extern rtx prepare_call_address (rtx, rtx, rtx *, int, int);
+extern bool shift_return_value (enum machine_mode, bool, rtx);
+
extern rtx expand_call (tree, rtx, int);
extern void fixup_tail_calls (void);
diff --git a/gcc/function.c b/gcc/function.c
index da78d3f..1021fc2 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -4059,22 +4059,31 @@ expand_function_start (tree subr)
{
/* Compute the return values into a pseudo reg, which we will copy
into the true return register after the cleanups are done. */
-
- /* In order to figure out what mode to use for the pseudo, we
- figure out what the mode of the eventual return register will
- actually be, and use that. */
- rtx hard_reg
- = hard_function_value (TREE_TYPE (DECL_RESULT (subr)),
- subr, 1);
-
- /* Structures that are returned in registers are not aggregate_value_p,
- so we may see a PARALLEL or a REG. */
- if (REG_P (hard_reg))
- SET_DECL_RTL (DECL_RESULT (subr), gen_reg_rtx (GET_MODE (hard_reg)));
+ tree return_type = TREE_TYPE (DECL_RESULT (subr));
+ if (TYPE_MODE (return_type) != BLKmode
+ && targetm.calls.return_in_msb (return_type))
+ /* expand_function_end will insert the appropriate padding in
+ this case. Use the return value's natural (unpadded) mode
+ within the function proper. */
+ SET_DECL_RTL (DECL_RESULT (subr),
+ gen_reg_rtx (TYPE_MODE (return_type)));
else
{
- gcc_assert (GET_CODE (hard_reg) == PARALLEL);
- SET_DECL_RTL (DECL_RESULT (subr), gen_group_rtx (hard_reg));
+ /* In order to figure out what mode to use for the pseudo, we
+ figure out what the mode of the eventual return register will
+ actually be, and use that. */
+ rtx hard_reg = hard_function_value (return_type, subr, 1);
+
+ /* Structures that are returned in registers are not
+ aggregate_value_p, so we may see a PARALLEL or a REG. */
+ if (REG_P (hard_reg))
+ SET_DECL_RTL (DECL_RESULT (subr),
+ gen_reg_rtx (GET_MODE (hard_reg)));
+ else
+ {
+ gcc_assert (GET_CODE (hard_reg) == PARALLEL);
+ SET_DECL_RTL (DECL_RESULT (subr), gen_group_rtx (hard_reg));
+ }
}
/* Set DECL_REGISTER flag so that expand_function_end will copy the
@@ -4368,10 +4377,22 @@ expand_function_end (void)
if (GET_MODE (real_decl_rtl) == BLKmode)
PUT_MODE (real_decl_rtl, GET_MODE (decl_rtl));
+ /* If a non-BLKmode return value should be padded at the least
+ significant end of the register, shift it left by the appropriate
+ amount. BLKmode results are handled using the group load/store
+ machinery. */
+ if (TYPE_MODE (TREE_TYPE (decl_result)) != BLKmode
+ && targetm.calls.return_in_msb (TREE_TYPE (decl_result)))
+ {
+ emit_move_insn (gen_rtx_REG (GET_MODE (decl_rtl),
+ REGNO (real_decl_rtl)),
+ decl_rtl);
+ shift_return_value (GET_MODE (decl_rtl), true, real_decl_rtl);
+ }
/* If a named return value dumped decl_return to memory, then
we may need to re-do the PROMOTE_MODE signed/unsigned
extension. */
- if (GET_MODE (real_decl_rtl) != GET_MODE (decl_rtl))
+ else if (GET_MODE (real_decl_rtl) != GET_MODE (decl_rtl))
{
int unsignedp = TYPE_UNSIGNED (TREE_TYPE (decl_result));
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 1887772..14f2b72 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -427,7 +427,7 @@ simplify_expand_binop (enum machine_mode mode, optab binoptab,
/* Like simplify_expand_binop, but always put the result in TARGET.
Return true if the expansion succeeded. */
-static bool
+bool
force_expand_binop (enum machine_mode mode, optab binoptab,
rtx op0, rtx op1, rtx target, int unsignedp,
enum optab_methods methods)
diff --git a/gcc/optabs.h b/gcc/optabs.h
index 09afccb..76ec8de 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -425,6 +425,9 @@ extern rtx expand_ternary_op (enum machine_mode mode, optab ternary_optab,
extern rtx expand_binop (enum machine_mode, optab, rtx, rtx, rtx, int,
enum optab_methods);
+extern bool force_expand_binop (enum machine_mode, optab, rtx, rtx, rtx, int,
+ enum optab_methods);
+
/* Expand a binary operation with both signed and unsigned forms. */
extern rtx sign_expand_binop (enum machine_mode, optab, optab, rtx, rtx,
rtx, int, enum optab_methods);
diff --git a/gcc/stmt.c b/gcc/stmt.c
index 3066a8b..e22ae5a 100644
--- a/gcc/stmt.c
+++ b/gcc/stmt.c
@@ -110,7 +110,6 @@ static bool check_operand_nalternatives (tree, tree);
static bool check_unique_operand_names (tree, tree);
static char *resolve_operand_name_1 (char *, tree, tree);
static void expand_null_return_1 (void);
-static rtx shift_return_value (rtx);
static void expand_value_return (rtx);
static void do_jump_if_equal (rtx, rtx, rtx, int);
static int estimate_case_costs (case_node_ptr);
@@ -1500,33 +1499,6 @@ expand_naked_return (void)
emit_jump (end_label);
}
-/* 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_shift (LSHIFT_EXPR, GET_MODE (target),
- gen_lowpart (GET_MODE (target), val),
- build_int_cst (NULL_TREE, shift), target, 1);
- }
- return val;
-}
-
-
/* Generate RTL to return from the current function, with value VAL. */
static void
@@ -1737,7 +1709,7 @@ expand_return (tree retval)
val = expand_expr (retval_rhs, val, GET_MODE (val), 0);
val = force_not_mem (val);
/* Return the calculated value. */
- expand_value_return (shift_return_value (val));
+ expand_value_return (val);
}
else
{
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index f647b33..25f2f13 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2004-11-24 Richard Sandiford <rsandifo@redhat.com>
+
+ * gcc.c-torture/execute/20041124-1.c: New test.
+
2004-11-24 Mark Mitchell <mark@codesourcery.com>
* g++.dg/template/deduce3.C: New test.
diff --git a/gcc/testsuite/gcc.c-torture/execute/20041124-1.c b/gcc/testsuite/gcc.c-torture/execute/20041124-1.c
new file mode 100644
index 0000000..51ce253
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/20041124-1.c
@@ -0,0 +1,10 @@
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+int main ()
+{
+ if (foo ().x != gs.x)
+ abort ();
+ exit (0);
+}