From 594a51febe0e6419db7bf30ca233da8a563301fa Mon Sep 17 00:00:00 2001 From: Stan Shebs Date: Sun, 14 Nov 2004 20:28:58 +0000 Subject: [PATCH] Basic ABI changes for passing structs by value in 64-bit Darwin. * config/rs6000/rs6000.c (rs6000_darwin64_abi): New flag. (rs6000_override_options): Set it for 64-bit Darwin. (rs6000_parse_abi_options): Add testing options to change it. (rs6000_return_in_memory): Test whether the type is one that can be passed in registers. (darwin64_function_arg_advance): New. (function_arg_advance): Call it, plus add recursion depth argument and test when counting off arguments. (rs6000_darwin64_function_arg): New. (function_arg): Call it. (setup_incoming_varargs): Add argument to function_arg_advance. (rs6000_darwin64_function_value): New. (rs6000_function_value): Call it. * config/rs6000/rs6000.h (FUNCTION_ARG_ADVANCE): Pass depth arg. * config/rs6000/rs6000-protos.h: Update decl of function_arg_advance. From-SVN: r90629 --- gcc/ChangeLog | 20 +++ gcc/config/rs6000/rs6000-protos.h | 2 +- gcc/config/rs6000/rs6000.c | 357 +++++++++++++++++++++++++++++++++++++- gcc/config/rs6000/rs6000.h | 2 +- 4 files changed, 375 insertions(+), 6 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d6adbcb..4eea1ed 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2004-11-14 Stan Shebs + + Basic ABI changes for passing structs by value in 64-bit Darwin. + * config/rs6000/rs6000.c (rs6000_darwin64_abi): New flag. + (rs6000_override_options): Set it for 64-bit Darwin. + (rs6000_parse_abi_options): Add testing options to change it. + (rs6000_return_in_memory): Test whether the type is one + that can be passed in registers. + (darwin64_function_arg_advance): New. + (function_arg_advance): Call it, plus add recursion depth + argument and test when counting off arguments. + (rs6000_darwin64_function_arg): New. + (function_arg): Call it. + (setup_incoming_varargs): Add argument to function_arg_advance. + (rs6000_darwin64_function_value): New. + (rs6000_function_value): Call it. + * config/rs6000/rs6000.h (FUNCTION_ARG_ADVANCE): Pass depth arg. + * config/rs6000/rs6000-protos.h: Update decl of + function_arg_advance. + 2004-11-14 Andrew Pinski PR c/17279 diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h index d3a3122..b756693 100644 --- a/gcc/config/rs6000/rs6000-protos.h +++ b/gcc/config/rs6000/rs6000-protos.h @@ -156,7 +156,7 @@ extern rtx rs6000_machopic_legitimize_pic_address (rtx orig, #ifdef TREE_CODE extern unsigned int rs6000_special_round_type_align (tree, int, int); extern void function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, - tree, int); + tree, int, int); extern int function_arg_boundary (enum machine_mode, tree); extern struct rtx_def *function_arg (CUMULATIVE_ARGS *, enum machine_mode, tree, int); diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index ada49ce..a0c607f 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -175,6 +175,9 @@ int rs6000_spe; /* Nonzero if floating point operations are done in the GPRs. */ int rs6000_float_gprs = 0; +/* Nonzero if we want Darwin's struct-by-value-in-regs ABI. */ +int rs6000_darwin64_abi; + /* String from -mfloat-gprs=. */ const char *rs6000_float_gprs_string; @@ -746,6 +749,8 @@ 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_darwin64_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 *, @@ -1292,6 +1297,12 @@ rs6000_override_options (const char *default_cpu) rs6000_altivec_vrsave = 1; } + /* Set the Darwin64 ABI as default for 64-bit Darwin. */ + if (DEFAULT_ABI == ABI_DARWIN && TARGET_64BIT) + { + rs6000_darwin64_abi = 1; + } + /* Handle -mabi= options. */ rs6000_parse_abi_options (); @@ -1625,6 +1636,19 @@ rs6000_parse_abi_options (void) error ("not configured for ABI: '%s'", rs6000_abi_string); } + /* These are here for testing during development only, do not + document in the manual please. */ + else if (! strcmp (rs6000_abi_string, "d64")) + { + rs6000_darwin64_abi = 1; + warning ("Using darwin64 ABI"); + } + else if (! strcmp (rs6000_abi_string, "d32")) + { + rs6000_darwin64_abi = 0; + warning ("Using old darwin ABI"); + } + else if (! strcmp (rs6000_abi_string, "no-spe")) rs6000_spe_abi = 0; else @@ -4603,6 +4627,15 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode) static bool rs6000_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED) { + /* In the darwin64 abi, try to use registers for larger structs + if possible. */ + if (AGGREGATE_TYPE_P (type) + && rs6000_darwin64_abi + && TREE_CODE (type) == RECORD_TYPE + && ((unsigned HOST_WIDE_INT) int_size_in_bytes (type) <= 32) + && ((unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 0)) + return false; + if (AGGREGATE_TYPE_P (type) && (TARGET_AIX_STRUCT_RET || (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8)) @@ -4783,6 +4816,48 @@ rs6000_arg_size (enum machine_mode mode, tree type) return (size + 7) >> 3; } +/* The darwin64 ABI calls for us to recurse down through structs, + applying the same rules to struct elements as if a reference to + each were being passed directly. */ + +static void +darwin64_function_arg_advance (CUMULATIVE_ARGS *cum, tree type, + int named, int depth) +{ + tree f, ftype; + int i, tot; + + switch (TREE_CODE (type)) + { + case RECORD_TYPE: + for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f)) + if (TREE_CODE (f) == FIELD_DECL) + { + ftype = TREE_TYPE (f); + function_arg_advance (cum, TYPE_MODE (ftype), ftype, + named, depth + 1); + } + break; + + case ARRAY_TYPE: + tot = int_size_in_bytes (type); + if (tot <= 0) + return; + ftype = TREE_TYPE (type); + tot /= int_size_in_bytes (ftype); + + for (i = 0; i < tot; ++i) + { + function_arg_advance (cum, TYPE_MODE (ftype), ftype, + named, depth + 1); + } + break; + + default: + abort (); + } +} + /* Update the data in CUM to advance over an argument of mode MODE and data type TYPE. (TYPE is null for libcalls where that information may not be available.) @@ -4793,9 +4868,11 @@ rs6000_arg_size (enum machine_mode mode, tree type) void function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, - tree type, int named) + tree type, int named, int depth) { - cum->nargs_prototype--; + /* Only tick off an argument if we're not recursing. */ + if (depth == 0) + cum->nargs_prototype--; if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode)) { @@ -4850,6 +4927,13 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, && !cum->stdarg && cum->sysv_gregno <= GP_ARG_MAX_REG) cum->sysv_gregno++; + + else if (rs6000_darwin64_abi + && mode == BLKmode + && (TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == ARRAY_TYPE)) + darwin64_function_arg_advance (cum, type, named, depth); + else if (DEFAULT_ABI == ABI_V4) { if (TARGET_HARD_FLOAT && TARGET_FPRS @@ -4925,7 +5009,8 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, 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); + fprintf (stderr, "named = %d, align = %d, depth = %d\n", + named, align, depth); } } } @@ -5003,6 +5088,123 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, } } +/* For the darwin64 ABI, we want to construct a PARALLEL consisting of + the register(s) to be used for each field and subfield of a struct + being passed by value, along with the offset of where the + register's value may be found in the block. */ + +static rtx +rs6000_darwin64_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, int named) +{ + tree f, ftype, offset; + rtx rvec[FIRST_PSEUDO_REGISTER], sub, suboff, roffset; + int k = 0, i, j, bytepos, subbytepos, tot; + CUMULATIVE_ARGS saved_cum = *cum; + enum machine_mode submode; + + switch (TREE_CODE (type)) + { + case RECORD_TYPE: + for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f)) + if (TREE_CODE (f) == FIELD_DECL) + { + ftype = TREE_TYPE (f); + offset = DECL_FIELD_OFFSET (f); + bytepos = int_bit_position (f) / BITS_PER_UNIT; + /* Force substructs to be handled as BLKmode even if + they're small enough to be recorded as DImode, so we + drill through to non-record fields. */ + submode = TYPE_MODE (ftype); + if (TREE_CODE (ftype) == RECORD_TYPE) + submode = BLKmode; + sub = function_arg (cum, submode, ftype, named); + if (sub == NULL_RTX) + return NULL_RTX; + if (GET_CODE (sub) == PARALLEL) + { + for (i = 0; i < XVECLEN (sub, 0); i++) + { + rtx subsub = XVECEXP (sub, 0, i); + suboff = XEXP (subsub, 1); + subbytepos = INTVAL (suboff); + subbytepos += bytepos; + roffset = gen_rtx_CONST_INT (SImode, subbytepos); + subsub = XEXP (subsub, 0); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, subsub, roffset); + } + } + else + { + roffset = gen_rtx_CONST_INT (SImode, bytepos); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset); + } + /* Now do an arg advance to get all the cumulative arg + stuff set correctly for the next subfield. Note that it + has no lasting effect, because it is being done on a + temporary copy of the cumulative arg data. */ + function_arg_advance (cum, submode, ftype, named, 1); + } + break; + + case ARRAY_TYPE: + tot = int_size_in_bytes (type); + if (tot <= 0) + return NULL_RTX; + ftype = TREE_TYPE (type); + tot /= int_size_in_bytes (ftype); + bytepos = 0; + + for (j = 0; j < tot; ++j) + { + /* Force substructs to be handled as BLKmode even if + they're small enough to be recorded as DImode, so we + drill through to non-record fields. */ + submode = TYPE_MODE (ftype); + if (TREE_CODE (ftype) == RECORD_TYPE) + submode = BLKmode; + sub = function_arg (cum, submode, ftype, named); + if (sub == NULL_RTX) + return NULL_RTX; + if (GET_CODE (sub) == PARALLEL) + { + for (i = 0; i < XVECLEN (sub, 0); i++) + { + rtx subsub = XVECEXP (sub, 0, i); + + suboff = XEXP (subsub, 1); + subbytepos = INTVAL (suboff); + subbytepos += bytepos; + roffset = gen_rtx_CONST_INT (SImode, subbytepos); + subsub = XEXP (subsub, 0); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, subsub, roffset); + } + } + else + { + roffset = gen_rtx_CONST_INT (SImode, bytepos); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset); + } + /* Now do an arg advance to get all the cumulative arg + stuff set correctly for the next subfield. Note that it + has no lasting effect, because it is being done on a + temporary copy of the cumulative arg data. */ + function_arg_advance (cum, submode, ftype, named, 1); + bytepos += int_size_in_bytes (ftype); + } + break; + + default: + abort (); + } + + *cum = saved_cum; + if (k > 0) + return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec)); + else + return NULL_RTX; +} + /* Determine where to place an argument in 64-bit mode with 32-bit ABI. */ static rtx @@ -5180,6 +5382,13 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, && (SPE_VECTOR_MODE (mode) || (TARGET_E500_DOUBLE && mode == DFmode))) return rs6000_spe_function_arg (cum, mode, type); + + else if (rs6000_darwin64_abi + && mode == BLKmode + && (TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == ARRAY_TYPE)) + return rs6000_darwin64_function_arg (cum, mode, type, named); + else if (abi == ABI_V4) { if (TARGET_HARD_FLOAT && TARGET_FPRS @@ -5454,7 +5663,7 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode, /* Skip the last named argument. */ next_cum = *cum; - function_arg_advance (&next_cum, mode, type, 1); + function_arg_advance (&next_cum, mode, type, 1, 0); if (DEFAULT_ABI == ABI_V4) { @@ -18127,6 +18336,128 @@ rs6000_complex_function_value (enum machine_mode mode) return gen_rtx_PARALLEL (mode, gen_rtvec (2, r1, r2)); } +/* Compose a PARALLEL for a darwin64 struct being returned by + value. */ + +static rtx +rs6000_darwin64_function_value (CUMULATIVE_ARGS *cum, tree valtype) +{ + tree f, ftype; + rtx rvec[FIRST_PSEUDO_REGISTER], sub, roffset, suboff; + int k = 0, bytepos, tot, elt, i, subbytepos; + enum machine_mode fmode; + + switch (TREE_CODE (valtype)) + { + case RECORD_TYPE: + for (f = TYPE_FIELDS (valtype); f ; f = TREE_CHAIN (f)) + if (TREE_CODE (f) == FIELD_DECL) + { + ftype = TREE_TYPE (f); + fmode = TYPE_MODE (ftype); + bytepos = int_bit_position (f) / BITS_PER_UNIT; + if (USE_FP_FOR_ARG_P (cum, fmode, ftype)) + { + sub = gen_rtx_REG (fmode, cum->fregno++); + cum->sysv_gregno++; + } + else if (USE_ALTIVEC_FOR_ARG_P (cum, fmode, ftype, 1)) + { + sub = gen_rtx_REG (fmode, cum->vregno++); + cum->sysv_gregno++; + } + else if (fmode == BLKmode + && (TREE_CODE (ftype) == RECORD_TYPE + || TREE_CODE (ftype) == ARRAY_TYPE)) + sub = rs6000_darwin64_function_value (cum, ftype); + else + sub = gen_rtx_REG (fmode, cum->sysv_gregno++); + if (sub == NULL_RTX) + return sub; + else if (GET_CODE (sub) == PARALLEL) + { + for (i = 0; i < XVECLEN (sub, 0); i++) + { + rtx subsub = XVECEXP (sub, 0, i); + + suboff = XEXP (subsub, 1); + subbytepos = INTVAL (suboff); + subbytepos += bytepos; + roffset = gen_rtx_CONST_INT (SImode, subbytepos); + subsub = XEXP (subsub, 0); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, subsub, roffset); + } + } + else + { + roffset = gen_rtx_CONST_INT (SImode, bytepos); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset); + } + } + if (k > 0) + return gen_rtx_PARALLEL (TYPE_MODE (valtype), gen_rtvec_v (k, rvec)); + else + return NULL_RTX; + + case ARRAY_TYPE: + /* If passing by value won't work, give up. */ + if (int_size_in_bytes (valtype) <= 0) + return NULL_RTX; + ftype = TREE_TYPE (valtype); + fmode = TYPE_MODE (ftype); + tot = int_size_in_bytes (valtype) / int_size_in_bytes (ftype); + bytepos = 0; + for (elt = 0; elt < tot; ++elt) + { + if (USE_FP_FOR_ARG_P (cum, fmode, ftype)) + { + sub = gen_rtx_REG (fmode, cum->fregno++); + cum->sysv_gregno++; + } + else if (USE_ALTIVEC_FOR_ARG_P (cum, fmode, ftype, 1)) + { + sub = gen_rtx_REG (fmode, cum->vregno++); + cum->sysv_gregno++; + } + else if (fmode == BLKmode + && (TREE_CODE (ftype) == RECORD_TYPE + || TREE_CODE (ftype) == ARRAY_TYPE)) + sub = rs6000_darwin64_function_value (cum, ftype); + else + sub = gen_rtx_REG (fmode, cum->sysv_gregno++); + if (sub == NULL_RTX) + return sub; + else if (GET_CODE (sub) == PARALLEL) + { + for (i = 0; i < XVECLEN (sub, 0); i++) + { + rtx subsub = XVECEXP (sub, 0, i); + + suboff = XEXP (subsub, 1); + subbytepos = INTVAL (suboff); + subbytepos += bytepos; + roffset = gen_rtx_CONST_INT (SImode, subbytepos); + subsub = XEXP (subsub, 0); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, subsub, roffset); + } + } + else + { + roffset = gen_rtx_CONST_INT (SImode, bytepos); + rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, sub, roffset); + } + bytepos += int_size_in_bytes (ftype); + } + if (k > 0) + return gen_rtx_PARALLEL (TYPE_MODE (valtype), gen_rtvec_v (k, rvec)); + else + return NULL_RTX; + + default: + abort (); + } +} + /* Define how to find the value returned by a function. VALTYPE is the data type of the value (as a tree). If the precise function being called is known, FUNC is its FUNCTION_DECL; @@ -18143,6 +18474,24 @@ rs6000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED) enum machine_mode mode; unsigned int regno; + /* Special handling for structs in darwin64. */ + if (rs6000_darwin64_abi + && TYPE_MODE (valtype) == BLKmode + && (TREE_CODE (valtype) == RECORD_TYPE + || TREE_CODE (valtype) == ARRAY_TYPE)) + { + CUMULATIVE_ARGS valcum; + rtx valret; + + valcum.sysv_gregno = GP_ARG_RETURN; + valcum.fregno = FP_ARG_MIN_REG; + valcum.vregno = ALTIVEC_ARG_MIN_REG; + valret = rs6000_darwin64_function_value (&valcum, valtype); + if (valret) + return valret; + /* Otherwise fall through to standard ABI rules. */ + } + if (TARGET_32BIT && TARGET_POWERPC64 && TYPE_MODE (valtype) == DImode) { /* Long long return value need be split in -mpowerpc64, 32bit ABI. */ diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h index 50b6ac1..1b74db8 100644 --- a/gcc/config/rs6000/rs6000.h +++ b/gcc/config/rs6000/rs6000.h @@ -1713,7 +1713,7 @@ typedef struct rs6000_args (TYPE is null for libcalls where that information may not be available.) */ #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ - function_arg_advance (&CUM, MODE, TYPE, NAMED) + function_arg_advance (&CUM, MODE, TYPE, NAMED, 0) /* Determine where to put an argument to a function. Value is zero to push the argument on the stack, -- 2.7.4