rs6000.h (struct rs6000_args): Add sysv_gregno.

* rs6000.h (struct rs6000_args): Add sysv_gregno.
        * rs6000.c (init_cumulative_args): Init sysv_gregno.
        (function_arg_boundary): Align DFmode.
        (function_arg_advance): Restructure for ABI_V4; use sysv_gregno
        to get fp reg and stack overflow correct.
        (function_arg): Likewise.
        (function_arg_pass_by_reference): True for TFmode for ABI_V4.
        (setup_incoming_varargs): Restructure for ABI_V4; use
        function_arg_advance to skip final named argument.
        (expand_builtin_saveregs): Properly unskip the last integer arg
        when doing varargs.  Adjust overflow location calculation.

        * ginclude/va-ppc.h (struct __va_list_tag): Make gpr and fpr
        explicitly unsigned.
        (__VA_FP_REGSAVE): Use new OFS argument instead of AP->fpr directly.
        (__VA_GP_REGSAVE): Similarly.
        (__va_longlong_p): Delete.
        (__va_arg_type_violation): New declaration.
        (va_arg): Restructure.  Flag promotion errors.  Align double.
        TFmode passed by reference.

        * rs6000.md (movdi_32+1): Use GEN_INT after arithmetic
        in the HOST_BITS_PER_WIDE_INT > 32 case.

From-SVN: r28199
This commit is contained in:
Richard Henderson 1999-07-20 17:26:00 -07:00
parent 7705e9db01
commit 4cc833b7a8
4 changed files with 317 additions and 193 deletions

View File

@ -1253,6 +1253,7 @@ init_cumulative_args (cum, fntype, libname, incoming)
cum->fregno = FP_ARG_MIN_REG; cum->fregno = FP_ARG_MIN_REG;
cum->prototype = (fntype && TYPE_ARG_TYPES (fntype)); cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
cum->call_cookie = CALL_NORMAL; cum->call_cookie = CALL_NORMAL;
cum->sysv_gregno = GP_ARG_MIN_REG;
if (incoming) if (incoming)
cum->nargs_prototype = 1000; /* don't return a PARALLEL */ cum->nargs_prototype = 1000; /* don't return a PARALLEL */
@ -1338,7 +1339,8 @@ function_arg_boundary (mode, type)
enum machine_mode mode; enum machine_mode mode;
tree type; tree type;
{ {
if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) && mode == DImode) if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)
&& (mode == DImode || mode == DFmode))
return 64; return 64;
if (DEFAULT_ABI != ABI_NT || TARGET_64BIT) if (DEFAULT_ABI != ABI_NT || TARGET_64BIT)
@ -1361,48 +1363,85 @@ function_arg_advance (cum, mode, type, named)
tree type; tree type;
int named; int named;
{ {
int align = (TARGET_32BIT && (cum->words & 1) != 0
&& function_arg_boundary (mode, type) == 64) ? 1 : 0;
cum->words += align;
cum->nargs_prototype--; cum->nargs_prototype--;
if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)
{ {
/* Long longs must not be split between registers and stack */ if (TARGET_HARD_FLOAT
if ((GET_MODE_CLASS (mode) != MODE_FLOAT || TARGET_SOFT_FLOAT) && (mode == SFmode || mode == DFmode))
&& type && !AGGREGATE_TYPE_P (type)
&& cum->words < GP_ARG_NUM_REG
&& cum->words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG)
{ {
cum->words = GP_ARG_NUM_REG; if (cum->fregno <= FP_ARG_V4_MAX_REG)
cum->fregno++;
else
{
if (mode == DFmode)
cum->words += cum->words & 1;
cum->words += RS6000_ARG_SIZE (mode, type, 1);
}
}
else
{
int n_words;
int gregno = cum->sysv_gregno;
/* Aggregates and IEEE quad get passed by reference. */
if ((type && AGGREGATE_TYPE_P (type))
|| mode == TFmode)
n_words = 1;
else
n_words = RS6000_ARG_SIZE (mode, type, 1);
/* Long long is put in odd registers. */
if (n_words == 2 && (gregno & 1) == 0)
gregno += 1;
/* Long long is not split between registers and stack. */
if (gregno + n_words - 1 > GP_ARG_MAX_REG)
{
/* Long long is aligned on the stack. */
if (n_words == 2)
cum->words += cum->words & 1;
cum->words += n_words;
}
/* Note: continuing to accumulate gregno past when we've started
spilling to the stack indicates the fact that we've started
spilling to the stack to expand_builtin_saveregs. */
cum->sysv_gregno = gregno + n_words;
} }
/* Aggregates get passed as pointers */ if (TARGET_DEBUG_ARG)
if (type && AGGREGATE_TYPE_P (type)) {
cum->words++; fprintf (stderr, "function_adv: words = %2d, fregno = %2d, ",
cum->words, cum->fregno);
/* Floats go in registers, & don't occupy space in the GP registers fprintf (stderr, "gregno = %2d, nargs = %4d, proto = %d, ",
like they do for AIX unless software floating point. */ cum->sysv_gregno, cum->nargs_prototype, cum->prototype);
else if (GET_MODE_CLASS (mode) == MODE_FLOAT fprintf (stderr, "mode = %4s, named = %d\n",
&& TARGET_HARD_FLOAT GET_MODE_NAME (mode), named);
&& cum->fregno <= FP_ARG_V4_MAX_REG) }
cum->fregno++;
else
cum->words += RS6000_ARG_SIZE (mode, type, 1);
} }
else else
if (named) {
{ int align = (TARGET_32BIT && (cum->words & 1) != 0
cum->words += RS6000_ARG_SIZE (mode, type, named); && function_arg_boundary (mode, type) == 64) ? 1 : 0;
if (GET_MODE_CLASS (mode) == MODE_FLOAT && TARGET_HARD_FLOAT) cum->words += align;
cum->fregno++;
}
if (TARGET_DEBUG_ARG) if (named)
fprintf (stderr, {
"function_adv: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d, align = %d\n", cum->words += RS6000_ARG_SIZE (mode, type, named);
cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named, align); if (GET_MODE_CLASS (mode) == MODE_FLOAT && TARGET_HARD_FLOAT)
cum->fregno++;
}
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "function_adv: words = %2d, fregno = %2d, ",
cum->words, cum->fregno);
fprintf (stderr, "nargs = %4d, proto = %d, mode = %4s, ",
cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode));
fprintf (stderr, "named = %d, align = %d\n", named, align);
}
}
} }
/* Determine where to put an argument to a function. /* Determine where to put an argument to a function.
@ -1435,22 +1474,14 @@ function_arg (cum, mode, type, named)
tree type; tree type;
int named; int named;
{ {
int align = (TARGET_32BIT && (cum->words & 1) != 0 enum rs6000_abi abi = DEFAULT_ABI;
&& function_arg_boundary (mode, type) == 64) ? 1 : 0;
int align_words = cum->words + align;
if (TARGET_DEBUG_ARG) /* Return a marker to indicate whether CR1 needs to set or clear the bit
fprintf (stderr, that V.4 uses to say fp args were passed in registers. Assume that we
"function_arg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d, align = %d\n", don't need the marker for software floating point, or compiler generated
cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named, align); library calls. */
/* Return a marker to indicate whether CR1 needs to set or clear the bit that V.4
uses to say fp args were passed in registers. Assume that we don't need the
marker for software floating point, or compiler generated library calls. */
if (mode == VOIDmode) if (mode == VOIDmode)
{ {
enum rs6000_abi abi = DEFAULT_ABI;
if ((abi == ABI_V4 || abi == ABI_SOLARIS) if ((abi == ABI_V4 || abi == ABI_SOLARIS)
&& TARGET_HARD_FLOAT && TARGET_HARD_FLOAT
&& cum->nargs_prototype < 0 && cum->nargs_prototype < 0
@ -1465,31 +1496,65 @@ function_arg (cum, mode, type, named)
return GEN_INT (cum->call_cookie); return GEN_INT (cum->call_cookie);
} }
if (!named) if (abi == ABI_V4 || abi == ABI_SOLARIS)
{ {
if (DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) if (TARGET_HARD_FLOAT
return NULL_RTX; && (mode == SFmode || mode == DFmode))
{
if (cum->fregno <= FP_ARG_V4_MAX_REG)
return gen_rtx_REG (mode, cum->fregno);
else
return NULL;
}
else
{
int n_words;
int gregno = cum->sysv_gregno;
/* Aggregates and IEEE quad get passed by reference. */
if ((type && AGGREGATE_TYPE_P (type))
|| mode == TFmode)
n_words = 1;
else
n_words = RS6000_ARG_SIZE (mode, type, 1);
/* Long long is put in odd registers. */
if (n_words == 2 && (gregno & 1) == 0)
gregno += 1;
/* Long long is not split between registers and stack. */
if (gregno + n_words - 1 <= GP_ARG_MAX_REG)
return gen_rtx_REG (mode, gregno);
else
return NULL;
}
} }
else
if (type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
return NULL_RTX;
if (USE_FP_FOR_ARG_P (*cum, mode, type))
{ {
if (DEFAULT_ABI == ABI_V4 /* V.4 never passes FP values in GP registers */ int align = (TARGET_32BIT && (cum->words & 1) != 0
|| DEFAULT_ABI == ABI_SOLARIS && function_arg_boundary (mode, type) == 64) ? 1 : 0;
|| ! type int align_words = cum->words + align;
|| ((cum->nargs_prototype > 0)
/* IBM AIX extended its linkage convention definition always to
require FP args after register save area hole on the stack. */
&& (DEFAULT_ABI != ABI_AIX
|| ! TARGET_XL_CALL
|| (align_words < GP_ARG_NUM_REG))))
return gen_rtx_REG (mode, cum->fregno);
return gen_rtx_PARALLEL (mode, if (!named)
gen_rtvec return NULL_RTX;
(2,
if (type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
return NULL_RTX;
if (USE_FP_FOR_ARG_P (*cum, mode, type))
{
if (! type
|| ((cum->nargs_prototype > 0)
/* IBM AIX extended its linkage convention definition always
to require FP args after register save area hole on the
stack. */
&& (DEFAULT_ABI != ABI_AIX
|| ! TARGET_XL_CALL
|| (align_words < GP_ARG_NUM_REG))))
return gen_rtx_REG (mode, cum->fregno);
return gen_rtx_PARALLEL (mode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_EXPR_LIST (VOIDmode,
((align_words >= GP_ARG_NUM_REG) ((align_words >= GP_ARG_NUM_REG)
? NULL_RTX ? NULL_RTX
@ -1507,21 +1572,12 @@ function_arg (cum, mode, type, named)
gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (mode, cum->fregno), gen_rtx_REG (mode, cum->fregno),
const0_rtx))); const0_rtx)));
}
else if (align_words < GP_ARG_NUM_REG)
return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
else
return NULL_RTX;
} }
/* Long longs won't be split between register and stack;
FP arguments get passed on the stack if they didn't get a register. */
else if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) &&
(align_words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG
|| (GET_MODE_CLASS (mode) == MODE_FLOAT && TARGET_HARD_FLOAT)))
{
return NULL_RTX;
}
else if (align_words < GP_ARG_NUM_REG)
return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
return NULL_RTX;
} }
/* For an arg passed partly in registers and partly in memory, /* For an arg passed partly in registers and partly in memory,
@ -1576,7 +1632,8 @@ function_arg_pass_by_reference (cum, mode, type, named)
int named ATTRIBUTE_UNUSED; int named ATTRIBUTE_UNUSED;
{ {
if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)
&& type && AGGREGATE_TYPE_P (type)) && ((type && AGGREGATE_TYPE_P (type))
|| mode == TFmode))
{ {
if (TARGET_DEBUG_ARG) if (TARGET_DEBUG_ARG)
fprintf (stderr, "function_arg_pass_by_reference: aggregate\n"); fprintf (stderr, "function_arg_pass_by_reference: aggregate\n");
@ -1611,73 +1668,87 @@ setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl)
int no_rtl; int no_rtl;
{ {
rtx save_area = virtual_incoming_args_rtx; CUMULATIVE_ARGS next_cum;
int reg_size = TARGET_32BIT ? 4 : 8; int reg_size = TARGET_32BIT ? 4 : 8;
rtx save_area;
if (TARGET_DEBUG_ARG) int first_reg_offset;
fprintf (stderr,
"setup_vararg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, no_rtl= %d\n",
cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), no_rtl);
if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)
{ {
tree fntype;
int stdarg_p;
fntype = TREE_TYPE (current_function_decl);
stdarg_p = (TYPE_ARG_TYPES (fntype) != 0
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
!= void_type_node));
/* For varargs, we do not want to skip the dummy va_dcl argument.
For stdargs, we do want to skip the last named argument. */
next_cum = *cum;
if (stdarg_p)
function_arg_advance (&next_cum, mode, type, 1);
/* Indicate to allocate space on the stack for varargs save area. */
/* ??? Does this really have to be located at a magic spot on the
stack, or can we allocate this with assign_stack_local instead. */
rs6000_sysv_varargs_p = 1; rs6000_sysv_varargs_p = 1;
if (! no_rtl) if (! no_rtl)
save_area = plus_constant (virtual_stack_vars_rtx, save_area = plus_constant (virtual_stack_vars_rtx,
- RS6000_VARARGS_SIZE); - RS6000_VARARGS_SIZE);
first_reg_offset = next_cum.sysv_gregno - GP_ARG_MIN_REG;
} }
else else
rs6000_sysv_varargs_p = 0;
if (cum->words < 8)
{ {
int first_reg_offset = cum->words; save_area = virtual_incoming_args_rtx;
rs6000_sysv_varargs_p = 0;
first_reg_offset = cum->words;
if (MUST_PASS_IN_STACK (mode, type)) if (MUST_PASS_IN_STACK (mode, type))
first_reg_offset += RS6000_ARG_SIZE (TYPE_MODE (type), type, 1); first_reg_offset += RS6000_ARG_SIZE (TYPE_MODE (type), type, 1);
}
if (first_reg_offset > GP_ARG_NUM_REG) if (!no_rtl && first_reg_offset < GP_ARG_NUM_REG)
first_reg_offset = GP_ARG_NUM_REG; {
move_block_from_reg
if (!no_rtl && first_reg_offset != GP_ARG_NUM_REG) (GP_ARG_MIN_REG + first_reg_offset,
move_block_from_reg gen_rtx_MEM (BLKmode,
(GP_ARG_MIN_REG + first_reg_offset, plus_constant (save_area, first_reg_offset * reg_size)),
gen_rtx_MEM (BLKmode, GP_ARG_NUM_REG - first_reg_offset,
plus_constant (save_area, first_reg_offset * reg_size)), (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD);
GP_ARG_NUM_REG - first_reg_offset,
(GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD);
/* ??? Does ABI_V4 need this at all? */
*pretend_size = (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD; *pretend_size = (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD;
} }
/* Save FP registers if needed. */ /* Save FP registers if needed. */
if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) && TARGET_HARD_FLOAT && !no_rtl) if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)
&& TARGET_HARD_FLOAT && !no_rtl
&& next_cum.fregno <= FP_ARG_V4_MAX_REG)
{ {
int fregno = cum->fregno; int fregno = next_cum.fregno;
int num_fp_reg = FP_ARG_V4_MAX_REG + 1 - fregno; rtx cr1 = gen_rtx_REG (CCmode, 69);
rtx lab = gen_label_rtx ();
int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) * 8);
if (num_fp_reg >= 0) emit_jump_insn (gen_rtx_SET (VOIDmode,
{
rtx cr1 = gen_rtx_REG (CCmode, 69);
rtx lab = gen_label_rtx ();
int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) * 8);
emit_jump_insn (gen_rtx_SET (VOIDmode,
pc_rtx, pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode, gen_rtx_IF_THEN_ELSE (VOIDmode,
gen_rtx_NE (VOIDmode, cr1, const0_rtx), gen_rtx_NE (VOIDmode, cr1,
const0_rtx),
gen_rtx_LABEL_REF (VOIDmode, lab), gen_rtx_LABEL_REF (VOIDmode, lab),
pc_rtx))); pc_rtx)));
while ( num_fp_reg-- >= 0) while (fregno <= FP_ARG_V4_MAX_REG)
{ {
emit_move_insn (gen_rtx_MEM (DFmode, plus_constant (save_area, off)), emit_move_insn (gen_rtx_MEM (DFmode, plus_constant (save_area, off)),
gen_rtx_REG (DFmode, fregno++)); gen_rtx_REG (DFmode, fregno));
off += 8; fregno++;
} off += 8;
emit_label (lab);
} }
emit_label (lab);
} }
} }
@ -1733,9 +1804,18 @@ expand_builtin_saveregs (args)
2 * UNITS_PER_WORD)); 2 * UNITS_PER_WORD));
/* Construct the two characters of `gpr' and `fpr' as a unit. */ /* Construct the two characters of `gpr' and `fpr' as a unit. */
words = current_function_args_info.words - !stdarg_p; words = current_function_args_info.words;
gpr = (words > 8 ? 8 : words); gpr = current_function_args_info.sysv_gregno - GP_ARG_MIN_REG;
fpr = current_function_args_info.fregno - 33; fpr = current_function_args_info.fregno - FP_ARG_MIN_REG;
/* Varargs has the va_dcl argument, but we don't count it. */
if (!stdarg_p)
{
if (gpr > GP_ARG_NUM_REG)
words -= 1;
else
gpr -= 1;
}
if (BYTES_BIG_ENDIAN) if (BYTES_BIG_ENDIAN)
{ {
@ -1754,12 +1834,9 @@ expand_builtin_saveregs (args)
emit_move_insn (mem_gpr_fpr, tmp); emit_move_insn (mem_gpr_fpr, tmp);
/* Find the overflow area. */ /* Find the overflow area. */
if (words <= 8) tmp = expand_binop (Pmode, add_optab, virtual_incoming_args_rtx,
tmp = virtual_incoming_args_rtx; GEN_INT (words * UNITS_PER_WORD),
else mem_overflow, 0, OPTAB_WIDEN);
tmp = expand_binop (Pmode, add_optab, virtual_incoming_args_rtx,
GEN_INT ((words - 8) * UNITS_PER_WORD),
mem_overflow, 0, OPTAB_WIDEN);
if (tmp != mem_overflow) if (tmp != mem_overflow)
emit_move_insn (mem_overflow, tmp); emit_move_insn (mem_overflow, tmp);
@ -1773,7 +1850,6 @@ expand_builtin_saveregs (args)
/* Return the address of the va_list constructor. */ /* Return the address of the va_list constructor. */
return XEXP (block, 0); return XEXP (block, 0);
} }
/* Generate a memory reference for expand_block_move, copying volatile, /* Generate a memory reference for expand_block_move, copying volatile,
and other bits from an original memory reference. */ and other bits from an original memory reference. */

View File

@ -1435,17 +1435,22 @@ extern int rs6000_sysv_varargs_p;
floating-point register number, and the third says how many more args we floating-point register number, and the third says how many more args we
have prototype types for. have prototype types for.
For ABI_V4, we treat these slightly differently -- `sysv_gregno' is
the next availible GP register, `fregno' is the next available FP
register, and `words' is the number of words used on the stack.
The varargs/stdarg support requires that this structure's size The varargs/stdarg support requires that this structure's size
be a multiple of sizeof(int). */ be a multiple of sizeof(int). */
typedef struct rs6000_args typedef struct rs6000_args
{ {
int words; /* # words uses for passing GP registers */ int words; /* # words used for passing GP registers */
int fregno; /* next available FP register */ int fregno; /* next available FP register */
int nargs_prototype; /* # args left in the current prototype */ int nargs_prototype; /* # args left in the current prototype */
int orig_nargs; /* Original value of nargs_prototype */ int orig_nargs; /* Original value of nargs_prototype */
int prototype; /* Whether a prototype was defined */ int prototype; /* Whether a prototype was defined */
int call_cookie; /* Do special things for this call */ int call_cookie; /* Do special things for this call */
int sysv_gregno; /* next available GP register */
} CUMULATIVE_ARGS; } CUMULATIVE_ARGS;
/* Define intermediate macro to compute the size (in registers) of an argument /* Define intermediate macro to compute the size (in registers) of an argument

View File

@ -6624,8 +6624,8 @@
#if HOST_BITS_PER_WIDE_INT == 32 #if HOST_BITS_PER_WIDE_INT == 32
operands[4] = (INTVAL (operands[1]) & 0x80000000) ? constm1_rtx : const0_rtx; operands[4] = (INTVAL (operands[1]) & 0x80000000) ? constm1_rtx : const0_rtx;
#else #else
operands[4] = (HOST_WIDE_INT) INTVAL (operands[1]) >> 32; operands[4] = GEN_INT ((HOST_WIDE_INT) INTVAL (operands[1]) >> 32);
operands[1] = INTVAL (operands[1]) & 0xffffffff; operands[1] = GEN_INT (INTVAL (operands[1]) & 0xffffffff);
#endif #endif
}") }")

View File

@ -17,10 +17,10 @@
/* Note that the names in this structure are in the user's namespace, but /* Note that the names in this structure are in the user's namespace, but
that the V.4 abi explicitly states that these names should be used. */ that the V.4 abi explicitly states that these names should be used. */
typedef struct __va_list_tag { typedef struct __va_list_tag {
char gpr; /* index into the array of 8 GPRs stored in the unsigned char gpr; /* index into the array of 8 GPRs stored in the
register save area gpr=0 corresponds to r3, register save area gpr=0 corresponds to r3,
gpr=1 to r4, etc. */ gpr=1 to r4, etc. */
char fpr; /* index into the array of 8 FPRs stored in the unsigned char fpr; /* index into the array of 8 FPRs stored in the
register save area fpr=0 corresponds to f1, register save area fpr=0 corresponds to f1,
fpr=1 to f2, etc. */ fpr=1 to f2, etc. */
char *overflow_arg_area; /* location on stack that holds the next char *overflow_arg_area; /* location on stack that holds the next
@ -51,13 +51,13 @@ typedef struct {
/* Macros to access the register save area */ /* Macros to access the register save area */
/* We cast to void * and then to TYPE * because this avoids /* We cast to void * and then to TYPE * because this avoids
a warning about increasing the alignment requirement. */ a warning about increasing the alignment requirement. */
#define __VA_FP_REGSAVE(AP,TYPE) \ #define __VA_FP_REGSAVE(AP,OFS,TYPE) \
((TYPE *) (void *) (&(((__va_regsave_t *) \ ((TYPE *) (void *) (&(((__va_regsave_t *) \
(AP)->reg_save_area)->__fp_save[(int)(AP)->fpr]))) (AP)->reg_save_area)->__fp_save[OFS])))
#define __VA_GP_REGSAVE(AP,TYPE) \ #define __VA_GP_REGSAVE(AP,OFS,TYPE) \
((TYPE *) (void *) (&(((__va_regsave_t *) \ ((TYPE *) (void *) (&(((__va_regsave_t *) \
(AP)->reg_save_area)->__gp_save[(int)(AP)->gpr]))) (AP)->reg_save_area)->__gp_save[OFS])))
/* Common code for va_start for both varargs and stdarg. We allow all /* Common code for va_start for both varargs and stdarg. We allow all
the work to be done by __builtin_saveregs. It returns a pointer to the work to be done by __builtin_saveregs. It returns a pointer to
@ -88,60 +88,103 @@ typedef struct {
#define __va_float_p(TYPE) (__builtin_classify_type(*(TYPE *)0) == 8) #define __va_float_p(TYPE) (__builtin_classify_type(*(TYPE *)0) == 8)
#endif #endif
#define __va_longlong_p(TYPE) \
((__builtin_classify_type(*(TYPE *)0) == 1) && (sizeof(TYPE) == 8))
#define __va_aggregate_p(TYPE) (__builtin_classify_type(*(TYPE *)0) >= 12) #define __va_aggregate_p(TYPE) (__builtin_classify_type(*(TYPE *)0) >= 12)
#define __va_size(TYPE) ((sizeof(TYPE) + sizeof (long) - 1) / sizeof (long)) #define __va_size(TYPE) ((sizeof(TYPE) + sizeof (long) - 1) / sizeof (long))
#define va_arg(AP,TYPE) \ /* This symbol isn't defined. It is used to flag type promotion violations
__extension__ (*({ \ at link time. We can only do this when optimizing. Use __builtin_trap
register TYPE *__ptr; \ instead of abort so that we don't require a prototype for abort. */
\
if (__va_float_p (TYPE) && (AP)->fpr < 8) \ #ifdef __OPTIMIZE__
{ \ extern void __va_arg_type_violation(void) __attribute__((__noreturn__));
__ptr = __VA_FP_REGSAVE (AP, TYPE); \ #else
(AP)->fpr++; \ #define __va_arg_type_violation() __builtin_trap()
} \ #endif
\
else if (__va_aggregate_p (TYPE) && (AP)->gpr < 8) \ #define va_arg(AP,TYPE) \
{ \ __extension__ (*({ \
__ptr = * __VA_GP_REGSAVE (AP, TYPE *); \ register TYPE *__ptr; \
(AP)->gpr++; \ \
} \ if (__va_float_p (TYPE) && sizeof (TYPE) < 16) \
\ { \
else if (!__va_float_p (TYPE) && !__va_aggregate_p (TYPE) \ unsigned char __fpr = (AP)->fpr; \
&& (AP)->gpr + __va_size(TYPE) <= 8 \ if (__fpr < 8) \
&& (!__va_longlong_p(TYPE) \ { \
|| (AP)->gpr + __va_size(TYPE) <= 8)) \ __ptr = __VA_FP_REGSAVE (AP, __fpr, TYPE); \
{ \ (AP)->fpr = __fpr + 1; \
if (__va_longlong_p(TYPE) && ((AP)->gpr & 1) != 0) \ } \
(AP)->gpr++; \ else if (sizeof (TYPE) == 8) \
\ { \
__ptr = __VA_GP_REGSAVE (AP, TYPE); \ unsigned long __addr = (unsigned long) (__va_overflow (AP)); \
(AP)->gpr += __va_size (TYPE); \ __ptr = (TYPE *)((__addr + 7) & -8); \
} \ __va_overflow (AP) = (char *)(__ptr + 1); \
\ } \
else if (!__va_float_p (TYPE) && !__va_aggregate_p (TYPE) \ else \
&& (AP)->gpr < 8) \ { \
{ \ /* float is promoted to double. */ \
(AP)->gpr = 8; \ __va_arg_type_violation (); \
__ptr = (TYPE *) (void *) (__va_overflow(AP)); \ } \
__va_overflow(AP) += __va_size (TYPE) * sizeof (long); \ } \
} \ \
\ /* Aggregates and long doubles are passed by reference. */ \
else if (__va_aggregate_p (TYPE)) \ else if (__va_aggregate_p (TYPE) || __va_float_p (TYPE)) \
{ \ { \
__ptr = * (TYPE **) (void *) (__va_overflow(AP)); \ unsigned char __gpr = (AP)->gpr; \
__va_overflow(AP) += sizeof (TYPE *); \ if (__gpr < 8) \
} \ { \
else \ __ptr = * __VA_GP_REGSAVE (AP, __gpr, TYPE *); \
{ \ (AP)->gpr = __gpr + 1; \
__ptr = (TYPE *) (void *) (__va_overflow(AP)); \ } \
__va_overflow(AP) += __va_size (TYPE) * sizeof (long); \ else \
} \ { \
\ TYPE **__pptr = (TYPE **) (__va_overflow (AP)); \
__ptr; \ __ptr = * __pptr; \
__va_overflow (AP) = (char *) (__pptr + 1); \
} \
} \
\
/* Only integrals remaining. */ \
else \
{ \
/* longlong is aligned. */ \
if (sizeof (TYPE) == 8) \
{ \
unsigned char __gpr = (AP)->gpr; \
if (__gpr < 7) \
{ \
__gpr += __gpr & 1; \
__ptr = __VA_GP_REGSAVE (AP, __gpr, TYPE); \
(AP)->gpr = __gpr + 2; \
} \
else \
{ \
unsigned long __addr = (unsigned long) (__va_overflow (AP)); \
__ptr = (TYPE *)((__addr + 7) & -8); \
(AP)->gpr = 8; \
__va_overflow (AP) = (char *)(__ptr + 1); \
} \
} \
else if (sizeof (TYPE) == 4) \
{ \
unsigned char __gpr = (AP)->gpr; \
if (__gpr < 8) \
{ \
__ptr = __VA_GP_REGSAVE (AP, __gpr, TYPE); \
(AP)->gpr = __gpr + 1; \
} \
else \
{ \
__ptr = (TYPE *) __va_overflow (AP); \
__va_overflow (AP) = (char *)(__ptr + 1); \
} \
} \
else \
{ \
/* Everything else was promoted to int. */ \
__va_arg_type_violation (); \
} \
} \
__ptr; \
})) }))
#define va_end(AP) ((void)0) #define va_end(AP) ((void)0)