This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
RFA: infrastructure for builtin_apply handling of multi-hard-register return values (Was: Re: [patch RFC] SH: Use FRAME_GROWS_DOWNWARD)
- From: Joern RENNECKE <joern dot rennecke at st dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Kaz Kojima <kkojima at rr dot iij4u dot or dot jp>
- Date: Tue, 02 Aug 2005 19:22:57 +0100
- Subject: RFA: infrastructure for builtin_apply handling of multi-hard-register return values (Was: Re: [patch RFC] SH: Use FRAME_GROWS_DOWNWARD)
- References: <20050721.222305.13777025.kkojima@rr.iij4u.or.jp> <42E004AE.7080306@st.com> <42EA5EC8.2030503@st.com> <20050731.094254.90827867.kkojima@rr.iij4u.or.jp>
Currrently, builtin_apply handles multi-hard-register return values
wrong by default, and while there are target macros that allow to
override this behaviour, they require you to implement most of the
details of rtl generation inside your port. ASAICS no port gets this
quite right at the moment, i.e. show all the assignments that are being
made, and emit USEs of the right size. This has been brought to the fore
again when an unrelated patch made the register allocator use the leeway
that the incorrect builtin_apply RTL gave it, and caused a
gcc.dg/builin-apply-4.c
regression on sh-elf -m4 -ml.
There has also been a thread about the issue in April, titled
"apply_result_size vs FUNCTION_VALUE_REGNO_P" :
http://gcc.gnu.org/ml/gcc/2005-04/msg00299.html
I am using the approach first outlined here:
http://gcc.gnu.org/ml/gcc-patches/2005-07/msg01507.html.
The patch posted there is included unchanged, but I had to add a patch to
mode-switching.c to make this work for the SH, as I will explain later
in this post.
I.e. I added a target hook that specifies how apply_result_size get the
mode for a
function value regno. For backward compatibility, I have left the
default at the old
behaviour, but there is a function in targhooks.c that a port can use
with a simple
#undef / #define of the target hook. That should suit processors with
straightforward
multi-hard-register argument passing and no special mode switching
requirements;
this option is in fact functionally equivalent to the patch posted here:
http://gcc.gnu.org/ml/gcc-patches/2005-07/msg01422.html
The SH allows a number of modes that are wider than any return value
that may be returned in a register, and even though they can be placed
in there,
trying to move them out of there for all return registers causes reload
problems.
Therefore, the sh is better off defining its own hook function to
calculate the modes.
By using CDImode rather than TImode there, it also eliminates the need
for a TImode
expander, and thus allows us to treat this issue separately from PR23135.
A further complication for the SH (and this probably applies to the x86
too when you
want to fix it using the right USE modes) is that the mode switching
code currently
does not anticipate the wide USEs and return value copies. When
create_pre_exit
sees a SImode use at the end, it ignores DImode value copies which are
part of a
TImode or CDImode return value copy (since they contain registers that
are not
part of the return value being looked for), and eventually panics
because it couldn't
find the return value copy of a likly spilled return value.
Therefore, I changed create_pre_exit to recognize these wider USEs and
return
value copies.
I have included the SH specific code as context here so that it can be
observed how it
all works together; what I would like to be reviewed is the machine
independent code.
I have bootstrapped & regtested this on i686-pc-linux-gnu native, and
regtested
i686-pc-linux-gnu X sh-elf and i686-pc-linux-gnu X sh64-elf.
2005-07-22 J"orn Rennecke <joern.rennecke@st.com>
* target.h (struct gcc_target): Add new member calls.apply_result_mode.
* target-def.h (TARGET_APPLY_RESULT_MODE): Define.
(TARGET_CALLS): Add TARGET_APPLY_RESULT_MODE.
* targhooks.c (regs.h, hard-reg-set.h): Include.
(apply_result_mode_1reg, apply_result_mode_scanreg): New functions.
* targhooks.h (apply_result_mode_1reg): Declare.
(apply_result_mode_scanreg): Likewise.
* buitins.c (apply_result_size): Use targetm.apply_result_mode.
* doc/tm.texi (TARGET_APPLY_RESULT_MODE): Document.
2005-08-01 J"orn Rennecke <joern.rennecke@st.com>
Kaz Kojima <kkojima@gcc.gnu.org>
* mode-switching.c (create_pre_exit): Cope with USEs made by
builtin_return which are wider than the last function result USE.
2005-07-28 J"orn Rennecke <joern.rennecke@st.com>
* sh.c (sh_apply_result_mode): New function.
(TARGET_APPLY_RESULT_MODE): Redefine.
* sh.md (UNSPEC_CALL_RESULT): New constant.
(untyped_call_result): New pattern.
(untyped_call): Use it to make function result assignments visible.
Remove expander predicate.
(untyped_return): New pattern.
* sh.h (HARD_REGNO_MODE_OK): Fix handling of DCmode.
(LEGITIMIZE_RELOAD_ADDRESS): For TARGET_SHMEDIA32, reload
inside of 64 bit reg+reg address as needed.
Index: builtins.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.c,v
retrieving revision 1.468
diff -p -u -r1.468 builtins.c
--- builtins.c 12 Jul 2005 09:19:59 -0000 1.468
+++ builtins.c 1 Aug 2005 18:53:48 -0000
@@ -1119,7 +1119,7 @@ apply_result_size (void)
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (FUNCTION_VALUE_REGNO_P (regno))
{
- mode = reg_raw_mode[regno];
+ mode = targetm.calls.apply_result_mode (regno);
gcc_assert (mode != VOIDmode);
Index: mode-switching.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/mode-switching.c,v
retrieving revision 2.5
diff -p -u -r2.5 mode-switching.c
--- mode-switching.c 5 Jul 2005 16:20:07 -0000 2.5
+++ mode-switching.c 1 Aug 2005 18:53:48 -0000
@@ -233,30 +233,52 @@ create_pre_exit (int n_entities, int *en
&& GET_CODE (PATTERN (last_insn)) == USE
&& GET_CODE ((ret_reg = XEXP (PATTERN (last_insn), 0))) == REG)
{
- int ret_start = REGNO (ret_reg);
+ unsigned int ret_start = REGNO (ret_reg);
int nregs = hard_regno_nregs[ret_start][GET_MODE (ret_reg)];
- int ret_end = ret_start + nregs;
+ unsigned int ret_end = ret_start + nregs;
int short_block = 0;
int maybe_builtin_apply = 0;
int forced_late_switch = 0;
rtx before_return_copy;
+ rtx all_used_regs = ret_reg;
do
{
rtx return_copy = PREV_INSN (last_insn);
rtx return_copy_pat, copy_reg;
- int copy_start, copy_num;
+ unsigned int copy_start, copy_num;
int j;
+ int is_copy = 0;
if (INSN_P (return_copy))
{
if (GET_CODE (PATTERN (return_copy)) == USE
- && GET_CODE (XEXP (PATTERN (return_copy), 0)) == REG
+ && REG_P (XEXP (PATTERN (return_copy), 0))
&& (FUNCTION_VALUE_REGNO_P
(REGNO (XEXP (PATTERN (return_copy), 0)))))
{
+ rtx new_used = XEXP (PATTERN (return_copy), 0);
+
maybe_builtin_apply = 1;
+ all_used_regs
+ = gen_rtx_EXPR_LIST (VOIDmode, new_used,
+ all_used_regs);
last_insn = return_copy;
+ if ((REGNO (new_used) == ret_start
+ && (GET_MODE_SIZE (GET_MODE (new_used))
+ > GET_MODE_SIZE (GET_MODE (ret_reg))))
+ || (!(CLASS_LIKELY_SPILLED_P
+ (REGNO_REG_CLASS (ret_start)))
+ && (CLASS_LIKELY_SPILLED_P
+ (REGNO_REG_CLASS (REGNO (new_used))))))
+ {
+ gcc_assert (nregs == ret_end - ret_start);
+ ret_reg = new_used;
+ ret_start = REGNO (ret_reg);
+ nregs
+ = hard_regno_nregs[ret_start][GET_MODE (ret_reg)];
+ ret_end = ret_start + nregs;
+ }
continue;
}
/* If the return register is not (in its entirety)
@@ -281,6 +303,16 @@ create_pre_exit (int n_entities, int *en
break;
copy_num
= hard_regno_nregs[copy_start][GET_MODE (copy_reg)];
+ if (copy_start >= ret_start
+ && copy_start + copy_num <= ret_end)
+ is_copy = 1;
+ else if (maybe_builtin_apply
+ && refers_to_regno_p (copy_start, copy_start+1,
+ all_used_regs, NULL)
+ && refers_to_regno_p (copy_start+copy_num-1,
+ copy_start+copy_num,
+ all_used_regs, NULL))
+ is_copy = -1;
/* If the return register is not likely spilled, - as is
the case for floating point on SH4 - then it might
@@ -301,18 +333,15 @@ create_pre_exit (int n_entities, int *en
after the return value copy. That is still OK,
because a floating point return value does not
conflict with address reloads. */
- if (copy_start >= ret_start
- && copy_start + copy_num <= ret_end
+ if (is_copy
&& OBJECT_P (SET_SRC (return_copy_pat)))
forced_late_switch = 1;
break;
}
- if (copy_start >= ret_start
- && copy_start + copy_num <= ret_end)
+ if (is_copy > 0)
nregs -= copy_num;
- else if (!maybe_builtin_apply
- || !FUNCTION_VALUE_REGNO_P (copy_start))
+ else if (!is_copy)
break;
last_insn = return_copy;
}
Index: target-def.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target-def.h,v
retrieving revision 1.132
diff -p -u -r1.132 target-def.h
--- target-def.h 14 Jul 2005 07:39:54 -0000 1.132
+++ target-def.h 1 Aug 2005 18:53:48 -0000
@@ -439,6 +439,7 @@ Foundation, 51 Franklin Street, Fifth Fl
#define TARGET_ARG_PARTIAL_BYTES hook_int_CUMULATIVE_ARGS_mode_tree_bool_0
#define TARGET_FUNCTION_VALUE default_function_value
+#define TARGET_APPLY_RESULT_MODE apply_result_mode_1reg
#define TARGET_CALLS { \
TARGET_PROMOTE_FUNCTION_ARGS, \
@@ -457,7 +458,8 @@ Foundation, 51 Franklin Street, Fifth Fl
TARGET_CALLEE_COPIES, \
TARGET_ARG_PARTIAL_BYTES, \
TARGET_INVALID_ARG_FOR_UNPROTOTYPED_FN, \
- TARGET_FUNCTION_VALUE \
+ TARGET_FUNCTION_VALUE, \
+ TARGET_APPLY_RESULT_MODE \
}
#ifndef TARGET_UNWIND_TABLES_DEFAULT
Index: target.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target.h,v
retrieving revision 1.144
diff -p -u -r1.144 target.h
--- target.h 14 Jul 2005 07:39:55 -0000 1.144
+++ target.h 1 Aug 2005 18:53:49 -0000
@@ -608,6 +608,10 @@ struct gcc_target
specified by FN_DECL_OR_TYPE with a return type of RET_TYPE. */
rtx (*function_value) (tree ret_type, tree fn_decl_or_type,
bool outgoing);
+
+ /* For a function value register, return a mode wide enough to copy
+ any function value that it might hold. */
+ enum machine_mode (*apply_result_mode) (unsigned regno);
} calls;
/* Return the diagnostic message string if conversion from FROMTYPE
Index: targhooks.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/targhooks.c,v
retrieving revision 2.46
diff -p -u -r2.46 targhooks.c
--- targhooks.c 14 Jul 2005 07:39:55 -0000 2.46
+++ targhooks.c 1 Aug 2005 18:53:49 -0000
@@ -62,6 +62,8 @@ Software Foundation, 51 Franklin Street,
#include "tm_p.h"
#include "target-def.h"
#include "ggc.h"
+#include "regs.h"
+#include "hard-reg-set.h"
void
@@ -439,4 +441,39 @@ default_function_value (tree ret_type AT
#endif
}
+/* Implementation of the apply_result_mode which assumes that all
+ return values fit into a single hard register.
+ For the sake of backward compatibility, this his is currently the
+ default. */
+enum machine_mode
+apply_result_mode_1reg (unsigned regno)
+{
+ return reg_raw_mode[regno];
+}
+
+/* Implementation of the apply_result_mode which tries to compute
+ a mode for the largest number of hard registers that could
+ hold a return value.
+ At some point, this should become the default, when enough ports have
+ been changed to take advantage of the apply_result_mode hook. */
+enum machine_mode
+apply_result_mode_scanreg (unsigned regno)
+{
+ unsigned n;
+ enum machine_mode mode, wider_mode;
+
+ mode = reg_raw_mode[regno];
+ gcc_assert (mode != VOIDmode);
+
+ n = hard_regno_nregs[regno][mode];
+ while (regno + n < FIRST_PSEUDO_REGISTER && call_used_regs[regno + n])
+ {
+ n++;
+ wider_mode = choose_hard_reg_mode (regno, n, false);
+ if (wider_mode != VOIDmode)
+ mode = wider_mode;
+ }
+ return mode;
+}
+
#include "gt-targhooks.h"
Index: targhooks.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/targhooks.h,v
retrieving revision 2.33
diff -p -u -r2.33 targhooks.h
--- targhooks.h 14 Jul 2005 07:39:56 -0000 2.33
+++ targhooks.h 1 Aug 2005 18:53:49 -0000
@@ -68,4 +68,6 @@ extern const char *hook_invalid_arg_for_
(tree, tree, tree);
extern bool hook_bool_rtx_commutative_p (rtx, int);
extern rtx default_function_value (tree, tree, bool);
+extern enum machine_mode apply_result_mode_1reg (unsigned regno);
+extern enum machine_mode apply_result_mode_scanreg (unsigned regno);
Index: config/sh/sh.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/sh/sh.c,v
retrieving revision 1.339
diff -p -u -r1.339 sh.c
--- config/sh/sh.c 20 Jul 2005 05:03:21 -0000 1.339
+++ config/sh/sh.c 1 Aug 2005 18:53:50 -0000
@@ -296,6 +296,7 @@ static bool sh_callee_copies (CUMULATIVE
static int sh_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
static int sh_dwarf_calling_convention (tree);
+static enum machine_mode sh_apply_result_mode (unsigned regno);
static int hard_regs_intersect_p (HARD_REG_SET *, HARD_REG_SET *);
@@ -463,6 +464,8 @@ static int hard_regs_intersect_p (HARD_R
#define TARGET_CALLEE_COPIES sh_callee_copies
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES sh_arg_partial_bytes
+#undef TARGET_APPLY_RESULT_MODE
+#define TARGET_APPLY_RESULT_MODE sh_apply_result_mode
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST sh_build_builtin_va_list
@@ -10514,6 +10517,19 @@ shmedia_prepare_call_address (rtx fnaddr
return fnaddr;
}
+static enum machine_mode
+sh_apply_result_mode (unsigned regno)
+{
+ if (TARGET_SH5)
+ return FP_REGISTER_P (regno) ? DFmode : DImode;
+ else
+ {
+ if (FP_REGISTER_P (regno))
+ return TARGET_FPU_DOUBLE ? DCmode : SCmode;
+ return CDImode;
+ }
+}
+
enum sh_divide_strategy_e sh_div_strategy = SH_DIV_STRATEGY_DEFAULT;
/* This defines the storage for the variable part of a -mboard= option.
Index: config/sh/sh.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/sh/sh.h,v
retrieving revision 1.275
diff -p -u -r1.275 sh.h
--- config/sh/sh.h 20 Jul 2005 05:03:25 -0000 1.275
+++ config/sh/sh.h 1 Aug 2005 18:53:50 -0000
@@ -1159,10 +1159,12 @@ extern char sh_additional_register_names
: (REGNO) == FIRST_XD_REG) \
: FP_REGISTER_P (REGNO) \
? ((MODE) == SFmode || (MODE) == SImode \
- || ((TARGET_SH2E || TARGET_SHMEDIA) && (MODE) == SCmode) \
- || ((((TARGET_SH4 || TARGET_SH2A_DOUBLE) && (MODE) == DFmode) || (MODE) == DCmode \
- || (TARGET_SHMEDIA && ((MODE) == DFmode || (MODE) == DImode \
- || (MODE) == V2SFmode || (MODE) == TImode))) \
+ || (((TARGET_SH2E || TARGET_SHMEDIA) && (MODE) == SCmode)) \
+ || ((((TARGET_SH4 || TARGET_SH2A_DOUBLE) \
+ && ((MODE) == DFmode || (MODE) == DCmode)) \
+ || (TARGET_SHMEDIA \
+ && ((MODE) == DFmode || (MODE) == DCmode || (MODE) == DImode \
+ || (MODE) == V2SFmode || (MODE) == TImode))) \
&& (((REGNO) - FIRST_FP_REG) & 1) == 0) \
|| ((TARGET_SH4 || TARGET_SHMEDIA) \
&& (MODE) == TImode \
@@ -2696,6 +2698,23 @@ struct sh_args {
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, (OPNUM), (TYPE));\
goto WIN; \
} \
+ /* TARGET_SHMEDIA32 can't do indexed addressing using 32 bit \
+ base & index, hence INDEX_REG_CLASS is NO_REGS. However, some \
+ special patterns use 64 bit base & index addressing. */ \
+ else if (GET_CODE (X) == PLUS \
+ && TARGET_SHMEDIA32 && ! ALLOW_INDEXED_ADDRESS \
+ && GET_MODE (X) == DImode \
+ && REG_P (XEXP (X, 0)) \
+ && REG_P (XEXP (X, 1))) \
+ { \
+ if (!GENERAL_REGISTER_P (REGNO (XEXP ((X), 0)))) \
+ push_reload (XEXP ((X), 0), NULL_RTX, &XEXP ((X), 0), NULL, \
+ BASE_REG_CLASS, DImode, VOIDmode, 0, 0, (OPNUM), (TYPE));\
+ if (!GENERAL_REGISTER_P (REGNO (XEXP ((X), 1)))) \
+ push_reload (XEXP ((X), 1), NULL_RTX, &XEXP ((X), 1), NULL, \
+ BASE_REG_CLASS, DImode, VOIDmode, 0, 0, (OPNUM), (TYPE));\
+ goto WIN; \
+ } \
}
/* Go to LABEL if ADDR (a legitimate address expression)
Index: config/sh/sh.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/sh/sh.md,v
retrieving revision 1.198
diff -p -u -r1.198 sh.md
--- config/sh/sh.md 20 Jul 2005 22:55:50 -0000 1.198
+++ config/sh/sh.md 1 Aug 2005 18:53:50 -0000
@@ -150,6 +150,7 @@
(UNSPEC_DIV_INV20 34)
(UNSPEC_ASHIFTRT 35)
(UNSPEC_THUNK 36)
+ (UNSPEC_CALL_RESULT 37)
(UNSPEC_SP_SET 40)
(UNSPEC_SP_TEST 41)
@@ -8075,27 +8076,89 @@ label:
(const_int 0))
(match_operand 1 "" "")
(match_operand 2 "" "")])]
- "(TARGET_SH2E || TARGET_SH2A) || TARGET_SHMEDIA"
+ ""
"
{
int i;
-
- emit_call_insn (gen_call (operands[0], const0_rtx, const0_rtx));
-
+ /* result[0] is for genereal purpose registers, result[1] for floating
+ point. reg is the register that holds the result, insn copies it to
+ memory. */
+ struct { rtx reg, insn; } result[2];
+
+ emit_call_insn (gen_call_value (gen_rtx_REG (TImode, R0_REG),
+ operands[0], const0_rtx, const0_rtx));
+ result[1].reg = gen_rtx_REG (SImode, PR_REG);
+ result[1].insn = NULL_RTX;
for (i = 0; i < XVECLEN (operands[2], 0); i++)
{
rtx set = XVECEXP (operands[2], 0, i);
- emit_move_insn (SET_DEST (set), SET_SRC (set));
+ rtx src = SET_SRC (set);
+ rtx dst = SET_DEST (set);
+ int fp = 0;
+
+ gcc_assert (REG_P (src));
+ fp = REGNO (src) == DR0_REG;
+ result[fp].reg = src;
+ result[fp].insn = gen_move_insn (dst, src);
}
-
/* The optimizer does not know that the call sets the function value
- registers we stored in the result block. We avoid problems by
- claiming that all hard registers are used and clobbered at this
- point. */
- emit_insn (gen_blockage ());
+ registers we'll store in the result block. */
+ emit_insn (gen_untyped_call_result(result[0].reg, result[1].reg));
+ emit_insn (result[0].insn);
+ if (result[1].insn)
+ emit_insn (result[1].insn);
DONE;
}")
+
+;; This needs to have two alternatives because we don't have an f/l union class.
+(define_insn "untyped_call_result"
+ [(set (match_operand 0 "any_register_operand" "=r,r")
+ (unspec:CDI [(reg:TI R0_REG)] UNSPEC_CALL_RESULT))
+ (set (match_operand 1 "" "=f,l")
+ (unspec [(reg:DC R0_REG) (match_dup 1)] UNSPEC_CALL_RESULT))]
+ ""
+ ""
+ [(set_attr "length" "0")])
+
+(define_expand "untyped_return"
+ [(match_operand:BLK 0 "memory_operand" "")
+ (match_operand 1 "" "")]
+ "TARGET_SH4 || TARGET_SH2A_DOUBLE"
+ "
+{
+ int i;
+ /* result[0] is for genereal purpose registers, result[1] for floating
+ point. reg is the register that holds the result, insn copies it from
+ memory. */
+ struct { rtx reg, insn; } result[2];
+
+ result[1].reg = NULL_RTX;
+ result[1].insn = NULL_RTX;
+ for (i = 0; i < XVECLEN (operands[1], 0); i++)
+ {
+ rtx set = XVECEXP (operands[1], 0, i);
+ rtx src = SET_SRC (set);
+ rtx dst = SET_DEST (set);
+ int fp = 0;
+
+ gcc_assert (REG_P (dst));
+ fp = REGNO (dst) == DR0_REG;
+ result[fp].reg = dst;
+ result[fp].insn = gen_move_insn (dst, src);
+ }
+ /* Because of the register requirements of mode switching, we must first
+ load the floating point result registers, and then the general purpose
+ result registers. */
+ if (result[1].insn)
+ emit_insn (result[1].insn);
+ emit_insn (result[0].insn);
+ emit_insn (gen_rtx_USE (VOIDmode, result[1].reg));
+ emit_insn (gen_rtx_USE (VOIDmode, result[0].reg));
+ expand_naked_return ();
+ DONE;
+}")
+
;; ------------------------------------------------------------------------
;; Misc insns
Index: doc/tm.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/tm.texi,v
retrieving revision 1.443
diff -p -u -r1.443 tm.texi
--- doc/tm.texi 21 Jul 2005 00:55:20 -0000 1.443
+++ doc/tm.texi 1 Aug 2005 18:53:51 -0000
@@ -4405,6 +4405,28 @@ The default version of this hook invokes
normally defined in @file{libgcc2.c}.
@end deftypefn
+@deftypefn {Target Hook} enum machine_mode TARGET_APPLY_RESULT_MODE (unsigned @var{regno})
+@var{regno} is a register number for which @code{FUNCTION_VALUE_REGNO_P}
+is true.
+This hook is supposed to return a mode wide enough to hold any value that
+might be returned in this register.
+Two stock implemetations are available in targhooks.c:
+@itemize @bullet
+@item
+@code{apply_result_mode_1reg}: Use this implementation if no more than
+one hard register is needed to return any value. For backwards compatibility
+reasons, this is currently the default.
+@item
+@code{apply_result_mode_scanreg}: This returns the/a largest mode that can
+be held in the return value register. This should generally give correct
+code, but it could lead to register allocation problems if very wide modes
+are allowed in a return value register according to HARD_REGNO_MODE_OK,
+but may not actually be used at the point of function return (or not
+in conjunction with all the other return value registers in their maximum
+mode).
+@end itemize
+@end deftypefn
+
@node Varargs
@section Implementing the Varargs Macros
@cindex varargs implementation