optabs.h (force_expand_binop): Declare.
authorRichard Sandiford <rsandifo@redhat.com>
Wed, 24 Nov 2004 18:50:26 +0000 (18:50 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Wed, 24 Nov 2004 18:50:26 +0000 (18:50 +0000)
* 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

gcc/ChangeLog
gcc/calls.c
gcc/expr.h
gcc/function.c
gcc/optabs.c
gcc/optabs.h
gcc/stmt.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/20041124-1.c [new file with mode: 0644]

index d2ffdac..3b2def7 100644 (file)
@@ -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.
index 44806d4..3c4d13e 100644 (file)
@@ -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))
        {
index f6f32e8..780ad4d 100644 (file)
@@ -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);
index da78d3f..1021fc2 100644 (file)
@@ -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));
 
index 1887772..14f2b72 100644 (file)
@@ -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)
index 09afccb..76ec8de 100644 (file)
@@ -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);
index 3066a8b..e22ae5a 100644 (file)
@@ -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
     {
index f647b33..25f2f13 100644 (file)
@@ -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 (file)
index 0000000..51ce253
--- /dev/null
@@ -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);
+}