From ec6376abd7f23e0993138ed92c56552eea9cd5a2 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 1 Jul 2004 03:17:31 +0000 Subject: [PATCH] rs6000.c (rs6000_mixed_function_arg): Rewrite. * config/rs6000/rs6000.c (rs6000_mixed_function_arg): Rewrite. (function_arg): Use rs6000_arg_size rather than CLASS_MAX_NREGS in calculating gpr size for altivec. Simplify and correct rs6000_mixed_function_arg calls. Call rs6000_mixed_function_arg for ABI_V4 gpr case too. Fix off-by-one error in long double reg test. Generate the correct PARALLEL to handle long double for ABI_AIX 32-bit. Use this for -m32 -mpowerpc64 fpr case too. (function_arg_partial_nregs): Align before calculating regs left. Don't return info on partial fprs when we need info on gprs. Correct long double fpr off-by-one error. From-SVN: r83951 --- gcc/ChangeLog | 13 ++ gcc/config/rs6000/rs6000.c | 303 ++++++++++++++++--------------------- 2 files changed, 140 insertions(+), 176 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c1a19dda5b4..31e9c5cc848 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2004-07-01 Alan Modra + + * config/rs6000/rs6000.c (rs6000_mixed_function_arg): Rewrite. + (function_arg): Use rs6000_arg_size rather than CLASS_MAX_NREGS in + calculating gpr size for altivec. Simplify and correct + rs6000_mixed_function_arg calls. Call rs6000_mixed_function_arg + for ABI_V4 gpr case too. Fix off-by-one error in long double + reg test. Generate the correct PARALLEL to handle long double + for ABI_AIX 32-bit. Use this for -m32 -mpowerpc64 fpr case too. + (function_arg_partial_nregs): Align before calculating regs left. + Don't return info on partial fprs when we need info on gprs. + Correct long double fpr off-by-one error. + 2004-06-30 John David Anglin * pa-protos.h (prefetch_operand): Add prototype. diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 3102e54569a..5b7cd3f1723 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -425,8 +425,7 @@ static int rs6000_get_some_local_dynamic_name_1 (rtx *, void *); static rtx rs6000_complex_function_value (enum machine_mode); static rtx rs6000_spe_function_arg (CUMULATIVE_ARGS *, enum machine_mode, tree); -static rtx rs6000_mixed_function_arg (CUMULATIVE_ARGS *, - enum machine_mode, tree, int); +static rtx rs6000_mixed_function_arg (enum machine_mode, tree, int); static void rs6000_move_block_from_reg (int regno, rtx x, int nregs); static void setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, tree, @@ -4424,124 +4423,49 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, /* Determine where to place an argument in 64-bit mode with 32-bit ABI. */ static rtx -rs6000_mixed_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, - tree type, int align_words) -{ - if (mode == DFmode) - { - /* -mpowerpc64 with 32bit ABI splits up a DFmode argument - in vararg list into zero, one or two GPRs */ - if (align_words >= GP_ARG_NUM_REG) - return gen_rtx_PARALLEL (DFmode, - gen_rtvec (2, - gen_rtx_EXPR_LIST (VOIDmode, - NULL_RTX, const0_rtx), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (mode, - cum->fregno), - const0_rtx))); - else if (align_words + rs6000_arg_size (mode, type) - > GP_ARG_NUM_REG) - /* If this is partially on the stack, then we only - include the portion actually in registers here. */ - return gen_rtx_PARALLEL (DFmode, - gen_rtvec (2, - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words), - const0_rtx), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (mode, - cum->fregno), - const0_rtx))); - - /* split a DFmode arg into two GPRs */ - return gen_rtx_PARALLEL (DFmode, - gen_rtvec (3, - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words), - const0_rtx), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words + 1), - GEN_INT (4)), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (mode, cum->fregno), - const0_rtx))); - } - /* -mpowerpc64 with 32bit ABI splits up a DImode argument into one - or two GPRs */ - else if (mode == DImode) +rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words) +{ + int n_units; + int i, k; + rtx rvec[GP_ARG_NUM_REG + 1]; + + if (align_words >= GP_ARG_NUM_REG) + return NULL_RTX; + + n_units = rs6000_arg_size (mode, type); + + /* Optimize the simple case where the arg fits in one gpr, except in + the case of BLKmode due to assign_parms assuming that registers are + BITS_PER_WORD wide. */ + if (n_units == 0 + || (n_units == 1 && mode != BLKmode)) + return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words); + + k = 0; + if (align_words + n_units > GP_ARG_NUM_REG) + /* Not all of the arg fits in gprs. Say that it goes in memory too, + using a magic NULL_RTX component. + FIXME: This is not strictly correct. Only some of the arg + belongs in memory, not all of it. However, there isn't any way + to do this currently, apart from building rtx descriptions for + the pieces of memory we want stored. Due to bugs in the generic + code we can't use the normal function_arg_partial_nregs scheme + with the PARALLEL arg description we emit here. + In any case, the code to store the whole arg to memory is often + more efficient than code to store pieces, and we know that space + is available in the right place for the whole arg. */ + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx); + + i = 0; + do { - if (align_words < GP_ARG_NUM_REG - 1) - return gen_rtx_PARALLEL (DImode, - gen_rtvec (2, - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words), - const0_rtx), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words + 1), - GEN_INT (4)))); - else if (align_words == GP_ARG_NUM_REG - 1) - return gen_rtx_PARALLEL (DImode, - gen_rtvec (2, - gen_rtx_EXPR_LIST (VOIDmode, - NULL_RTX, const0_rtx), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words), - const0_rtx))); - } - else if (ALTIVEC_VECTOR_MODE (mode) && align_words == GP_ARG_NUM_REG - 2) - { - /* Varargs vector regs must be saved in R9-R10. */ - return gen_rtx_PARALLEL (mode, - gen_rtvec (3, - gen_rtx_EXPR_LIST (VOIDmode, - NULL_RTX, const0_rtx), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words), - const0_rtx), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words + 1), - GEN_INT (4)))); - } - else if ((mode == BLKmode || ALTIVEC_VECTOR_MODE (mode)) - && align_words <= (GP_ARG_NUM_REG - 1)) - { - /* AltiVec vector regs are saved in R5-R8. */ - int k; - int size = int_size_in_bytes (type); - int no_units = ((size - 1) / 4) + 1; - int max_no_words = GP_ARG_NUM_REG - align_words; - int rtlvec_len = no_units < max_no_words ? no_units : max_no_words; - rtx *rtlvec = (rtx *) alloca (rtlvec_len * sizeof (rtx)); - - memset ((char *) rtlvec, 0, rtlvec_len * sizeof (rtx)); - - for (k=0; k < rtlvec_len; k++) - rtlvec[k] = gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (SImode, - GP_ARG_MIN_REG - + align_words + k), - k == 0 ? const0_rtx : GEN_INT (k*4)); - - return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rtlvec)); + rtx r = gen_rtx_REG (SImode, GP_ARG_MIN_REG + align_words); + rtx off = GEN_INT (i++ * 4); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off); } - return NULL_RTX; + while (++align_words < GP_ARG_NUM_REG && --n_units != 0); + + return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec)); } /* Determine where to put an argument to a function. @@ -4636,8 +4560,8 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, { /* Vector parameters to varargs functions under AIX or Darwin get passed in memory and possibly also in GPRs. */ - int align, align_words; - enum machine_mode part_mode = mode; + int align, align_words, n_words; + enum machine_mode part_mode; /* Vector parameters must be 16-byte aligned. This places them at 2 mod 4 in terms of words in 32-bit mode, since the parameter @@ -4653,20 +4577,20 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, /* Out of registers? Memory, then. */ if (align_words >= GP_ARG_NUM_REG) return NULL_RTX; - + + if (TARGET_32BIT && TARGET_POWERPC64) + return rs6000_mixed_function_arg (mode, type, align_words); + /* The vector value goes in GPRs. Only the part of the value in GPRs is reported here. */ - if (align_words + CLASS_MAX_NREGS (mode, GENERAL_REGS) - > GP_ARG_NUM_REG) + part_mode = mode; + n_words = rs6000_arg_size (mode, type); + if (align_words + n_words > GP_ARG_NUM_REG) /* Fortunately, there are only two possibilities, the value is either wholly in GPRs or half in GPRs and half not. */ part_mode = DImode; - - if (TARGET_32BIT - && (TARGET_POWERPC64 || (align_words == GP_ARG_NUM_REG - 2))) - return rs6000_mixed_function_arg (cum, part_mode, type, align_words); - else - return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words); + + return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words); } } else if (TARGET_SPE_ABI && TARGET_SPE && SPE_VECTOR_MODE (mode)) @@ -4693,10 +4617,13 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, gregno += (1 - gregno) & 1; /* Multi-reg args are not split between registers and stack. */ - if (gregno + n_words - 1 <= GP_ARG_MAX_REG) - return gen_rtx_REG (mode, gregno); - else + if (gregno + n_words - 1 > GP_ARG_MAX_REG) return NULL_RTX; + + if (TARGET_32BIT && TARGET_POWERPC64) + return rs6000_mixed_function_arg (mode, type, + gregno - GP_ARG_MIN_REG); + return gen_rtx_REG (mode, gregno); } } else @@ -4706,25 +4633,23 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, if (USE_FP_FOR_ARG_P (cum, mode, type)) { - rtx fpr[2]; - rtx *r; + rtx rvec[GP_ARG_NUM_REG + 1]; + rtx r; + int k; bool needs_psave; enum machine_mode fmode = mode; - int n; unsigned long n_fpreg = (GET_MODE_SIZE (mode) + 7) >> 3; if (cum->fregno + n_fpreg > FP_ARG_MAX_REG + 1) { - /* Long double split over regs and memory. */ - if (fmode == TFmode) - fmode = DFmode; - /* Currently, we only ever need one reg here because complex doubles are split. */ - if (cum->fregno != FP_ARG_MAX_REG - 1) + if (cum->fregno != FP_ARG_MAX_REG || fmode != TFmode) abort (); + + /* Long double split over regs and memory. */ + fmode = DFmode; } - fpr[1] = gen_rtx_REG (fmode, cum->fregno); /* Do we also need to pass this arg in the parameter save area? */ @@ -4735,46 +4660,55 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, && align_words >= GP_ARG_NUM_REG))); if (!needs_psave && mode == fmode) - return fpr[1]; - - if (TARGET_32BIT && TARGET_POWERPC64 - && mode == DFmode && cum->stdarg) - return rs6000_mixed_function_arg (cum, mode, type, align_words); - - /* Describe where this piece goes. */ - r = fpr + 1; - *r = gen_rtx_EXPR_LIST (VOIDmode, *r, const0_rtx); - n = 1; + return gen_rtx_REG (fmode, cum->fregno); + k = 0; if (needs_psave) { - /* Now describe the part that goes in gprs or the stack. + /* Describe the part that goes in gprs or the stack. This piece must come first, before the fprs. */ - rtx reg = NULL_RTX; if (align_words < GP_ARG_NUM_REG) { unsigned long n_words = rs6000_arg_size (mode, type); - enum machine_mode rmode = mode; - - if (align_words + n_words > GP_ARG_NUM_REG) - /* If this is partially on the stack, then we only - include the portion actually in registers here. - We know this can only be one register because - complex doubles are splt. */ - rmode = Pmode; - reg = gen_rtx_REG (rmode, GP_ARG_MIN_REG + align_words); + + if (align_words + n_words > GP_ARG_NUM_REG + || (TARGET_32BIT && TARGET_POWERPC64)) + { + /* If this is partially on the stack, then we only + include the portion actually in registers here. */ + enum machine_mode rmode = TARGET_32BIT ? SImode : DImode; + rtx off; + do + { + r = gen_rtx_REG (rmode, + GP_ARG_MIN_REG + align_words); + off = GEN_INT (k * GET_MODE_SIZE (rmode)); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off); + } + while (++align_words < GP_ARG_NUM_REG && --n_words != 0); + } + else + { + /* The whole arg fits in gprs. */ + r = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx); + } } - *--r = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx); - ++n; + else + /* It's entirely in memory. */ + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx); } - return gen_rtx_PARALLEL (mode, gen_rtvec_v (n, r)); + /* Describe where this piece goes in the fprs. */ + r = gen_rtx_REG (fmode, cum->fregno); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx); + + return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec)); } else if (align_words < GP_ARG_NUM_REG) { - if (TARGET_32BIT && TARGET_POWERPC64 - && (mode == DImode || mode == BLKmode)) - return rs6000_mixed_function_arg (cum, mode, type, align_words); + if (TARGET_32BIT && TARGET_POWERPC64) + return rs6000_mixed_function_arg (mode, type, align_words); return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words); } @@ -4783,15 +4717,20 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, } } -/* For an arg passed partly in registers and partly in memory, - this is the number of registers used. - For args passed entirely in registers or entirely in memory, zero. */ +/* For an arg passed partly in registers and partly in memory, this is + the number of registers used. For args passed entirely in registers + or entirely in memory, zero. When an arg is described by a PARALLEL, + perhaps using more than one register type, this function returns the + number of registers used by the first element of the PARALLEL. */ int function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, int named) { int ret = 0; + int align; + int parm_offset; + int align_words; if (DEFAULT_ABI == ABI_V4) return 0; @@ -4800,17 +4739,29 @@ function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, && cum->nargs_prototype >= 0) return 0; - if (USE_FP_FOR_ARG_P (cum, mode, type)) + align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1; + parm_offset = TARGET_32BIT ? 2 : 0; + align_words = cum->words + ((parm_offset - cum->words) & align); + + if (USE_FP_FOR_ARG_P (cum, mode, type) + /* If we are passing this arg in gprs as well, then this function + should return the number of gprs (or memory) partially passed, + *not* the number of fprs. */ + && !(type + && (cum->nargs_prototype <= 0 + || (DEFAULT_ABI == ABI_AIX + && TARGET_XL_CALL + && align_words >= GP_ARG_NUM_REG)))) { if (cum->fregno + ((GET_MODE_SIZE (mode) + 7) >> 3) > FP_ARG_MAX_REG + 1) - ret = FP_ARG_MAX_REG - cum->fregno; + ret = FP_ARG_MAX_REG + 1 - cum->fregno; else if (cum->nargs_prototype >= 0) return 0; } - if (cum->words < GP_ARG_NUM_REG - && GP_ARG_NUM_REG < cum->words + rs6000_arg_size (mode, type)) - ret = GP_ARG_NUM_REG - cum->words; + if (align_words < GP_ARG_NUM_REG + && GP_ARG_NUM_REG < align_words + rs6000_arg_size (mode, type)) + ret = GP_ARG_NUM_REG - align_words; if (ret != 0 && TARGET_DEBUG_ARG) fprintf (stderr, "function_arg_partial_nregs: %d\n", ret); -- 2.30.2