diff options
author | John David Anglin <dave@hiauly1.hia.nrc.ca> | 2002-09-17 03:30:37 +0000 |
---|---|---|
committer | John David Anglin <danglin@gcc.gnu.org> | 2002-09-17 03:30:37 +0000 |
commit | 9dff28ab5386ec651699c6245823e3e2c68d1c64 (patch) | |
tree | 9562fab54c7f71944f468dd8ea88012248c2545e /gcc/config | |
parent | 94313f351a9d53ae192d425c82b023b895827009 (diff) | |
download | gcc-9dff28ab5386ec651699c6245823e3e2c68d1c64.tar.gz |
calls.c (store_one_arg): Set default alignment for BLKmode arguments to BITS_PER_UNIT when...
* calls.c (store_one_arg): Set default alignment for BLKmode arguments
to BITS_PER_UNIT when ARGS_GROW_DOWNWARD and the padding direction is
downward.
* function.c (pad_below): Always compile.
(locate_and_pad_parm): If defined ARGS_GROW_DOWNWARD, pad argument to
alignment when it is not in a register or REG_PARM_STACK_SPACE is true.
Pad below when the argument is not in a register and the padding
direction is downward.
* pa-64.h (MUST_PASS_IN_STACK): Move define to pa.h.
(PAD_VARARGS_DOWN): Define.
* pa.c (function_arg_padding): Revise padding directions to make them
compatible with the 32 and 64-bit runtime architecture documentation.
(hppa_va_arg): Add code to handle variable and size zero arguments
passed by reference on TARGET_64BIT. Reformat.
(function_arg): Use a PARALLEL for BLKmode and aggregates args on
TARGET_64BIT. Use a DImode PARALLEL for BLKmode args 5 to 8 bytes
wide when !TARGET_64BIT. Move forward check for mode==VOIDmode.
Add comments.
* pa.h (MAX_PARM_BOUNDARY): Correct define for TARGET_64BIT.
(RETURN_IN_MEMORY): Return size zero types in memory.
(FUNCTION_VALUE): Return TFmode in general registers.
(MUST_PASS_IN_STACK): Define.
(FUNCTION_ARG_BOUNDARY): Simplify.
(FUNCTION_ARG_PASS_BY_REFERENCE): Pass variable and zero sized types
by reference.
(FUNCTION_ARG_CALLEE_COPIES): Define to FUNCTION_ARG_PASS_BY_REFERENCE.
From-SVN: r57226
Diffstat (limited to 'gcc/config')
-rw-r--r-- | gcc/config/pa/pa-64.h | 13 | ||||
-rw-r--r-- | gcc/config/pa/pa.c | 205 | ||||
-rw-r--r-- | gcc/config/pa/pa.h | 86 |
3 files changed, 200 insertions, 104 deletions
diff --git a/gcc/config/pa/pa-64.h b/gcc/config/pa/pa-64.h index 82ce3ef45f2..646f5f147d7 100644 --- a/gcc/config/pa/pa-64.h +++ b/gcc/config/pa/pa-64.h @@ -88,8 +88,11 @@ Boston, MA 02111-1307, USA. */ #undef STATIC_CHAIN_REGNUM #define STATIC_CHAIN_REGNUM 31 -/* Nonzero if we do not know how to pass TYPE solely in registers. */ -#define MUST_PASS_IN_STACK(MODE,TYPE) \ - ((TYPE) != 0 \ - && (TREE_CODE (TYPE_SIZE (TYPE)) != INTEGER_CST \ - || TREE_ADDRESSABLE (TYPE))) +/* If defined, a C expression which determines whether the default + implementation of va_arg will attempt to pad down before reading the + next argument, if that argument is smaller than its aligned space as + controlled by PARM_BOUNDARY. If this macro is not defined, all such + arguments are padded down when BYTES_BIG_ENDIAN is true. We don't + want aggregrates padded down. */ + +#define PAD_VARARGS_DOWN (!AGGREGATE_TYPE_P (type)) diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c index 2d655394a0d..e9679a70ade 100644 --- a/gcc/config/pa/pa.c +++ b/gcc/config/pa/pa.c @@ -5089,22 +5089,33 @@ function_arg_padding (mode, type) enum machine_mode mode; tree type; { - int size; - - if (mode == BLKmode) - { - if (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) - size = int_size_in_bytes (type) * BITS_PER_UNIT; + if (mode == BLKmode + || (TARGET_64BIT && type && AGGREGATE_TYPE_P (type))) + { + /* Return none if justification is not required. */ + if (type + && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST + && (int_size_in_bytes (type) * BITS_PER_UNIT) % PARM_BOUNDARY == 0) + return none; + + /* The directions set here are ignored when a BLKmode argument larger + than a word is placed in a register. Different code is used for + the stack and registers. This makes it difficult to have a + consistent data representation for both the stack and registers. + For both runtimes, the justification and padding for arguments on + the stack and in registers should be identical. */ + if (TARGET_64BIT) + /* The 64-bit runtime specifies left justification for aggregates. */ + return upward; else - return upward; /* Don't know if this is right, but */ - /* same as old definition. */ + /* The 32-bit runtime architecture specifies right justification. + When the argument is passed on the stack, the argument is padded + with garbage on the left. The HP compiler pads with zeros. */ + return downward; } - else - size = GET_MODE_BITSIZE (mode); - if (size < PARM_BOUNDARY) + + if (GET_MODE_BITSIZE (mode) < PARM_BOUNDARY) return downward; - else if (size % PARM_BOUNDARY) - return upward; else return none; } @@ -5196,15 +5207,23 @@ rtx hppa_va_arg (valist, type) tree valist, type; { - HOST_WIDE_INT align, size, ofs; + HOST_WIDE_INT size = int_size_in_bytes (type); + HOST_WIDE_INT ofs; tree t, ptr, pptr; if (TARGET_64BIT) { - /* Every argument in PA64 is passed by value (including large structs). - Arguments with size greater than 8 must be aligned 0 MOD 16. */ + /* Every argument in PA64 is supposed to be passed by value + (including large structs). However, as a GCC extension, we + pass zero and variable sized arguments by reference. Empty + structures are a GCC extension not supported by the HP + compilers. Thus, passing them by reference isn't likely + to conflict with the ABI. For variable sized arguments, + GCC doesn't have the infrastructure to allocate these to + registers. */ + + /* Arguments with a size greater than 8 must be aligned 0 MOD 16. */ - size = int_size_in_bytes (type); if (size > UNITS_PER_WORD) { t = build (PLUS_EXPR, TREE_TYPE (valist), valist, @@ -5213,57 +5232,75 @@ hppa_va_arg (valist, type) build_int_2 (-2 * UNITS_PER_WORD, -1)); t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t); TREE_SIDE_EFFECTS (t) = 1; - expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } - return std_expand_builtin_va_arg (valist, type); - } - - /* Compute the rounded size of the type. */ - align = PARM_BOUNDARY / BITS_PER_UNIT; - size = int_size_in_bytes (type); - ptr = build_pointer_type (type); + if (size > 0) + return std_expand_builtin_va_arg (valist, type); + else + { + ptr = build_pointer_type (type); - /* "Large" types are passed by reference. */ - if (size > 8) - { - t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist, - build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0)); - TREE_SIDE_EFFECTS (t) = 1; + /* Args grow upward. */ + t = build (POSTINCREMENT_EXPR, TREE_TYPE (valist), valist, + build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0)); + TREE_SIDE_EFFECTS (t) = 1; - pptr = build_pointer_type (ptr); - t = build1 (NOP_EXPR, pptr, t); - TREE_SIDE_EFFECTS (t) = 1; + pptr = build_pointer_type (ptr); + t = build1 (NOP_EXPR, pptr, t); + TREE_SIDE_EFFECTS (t) = 1; - t = build1 (INDIRECT_REF, ptr, t); - TREE_SIDE_EFFECTS (t) = 1; + t = build1 (INDIRECT_REF, ptr, t); + TREE_SIDE_EFFECTS (t) = 1; + } } - else + else /* !TARGET_64BIT */ { - t = build (PLUS_EXPR, TREE_TYPE (valist), valist, - build_int_2 (-size, -1)); + ptr = build_pointer_type (type); - /* Copied from va-pa.h, but we probably don't need to align - to word size, since we generate and preserve that invariant. */ - t = build (BIT_AND_EXPR, TREE_TYPE (valist), t, - build_int_2 ((size > 4 ? -8 : -4), -1)); + /* "Large" and variable sized types are passed by reference. */ + if (size > 8 || size <= 0) + { + /* Args grow downward. */ + t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist, + build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0)); + TREE_SIDE_EFFECTS (t) = 1; - t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t); - TREE_SIDE_EFFECTS (t) = 1; + pptr = build_pointer_type (ptr); + t = build1 (NOP_EXPR, pptr, t); + TREE_SIDE_EFFECTS (t) = 1; - ofs = (8 - size) % 4; - if (ofs) - { - t = build (PLUS_EXPR, TREE_TYPE (valist), t, build_int_2 (ofs, 0)); + t = build1 (INDIRECT_REF, ptr, t); TREE_SIDE_EFFECTS (t) = 1; } + else + { + t = build (PLUS_EXPR, TREE_TYPE (valist), valist, + build_int_2 (-size, -1)); + + /* Copied from va-pa.h, but we probably don't need to align to + word size, since we generate and preserve that invariant. */ + t = build (BIT_AND_EXPR, TREE_TYPE (valist), t, + build_int_2 ((size > 4 ? -8 : -4), -1)); + + t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t); + TREE_SIDE_EFFECTS (t) = 1; + + ofs = (8 - size) % 4; + if (ofs) + { + t = build (PLUS_EXPR, TREE_TYPE (valist), t, + build_int_2 (ofs, 0)); + TREE_SIDE_EFFECTS (t) = 1; + } - t = build1 (NOP_EXPR, ptr, t); - TREE_SIDE_EFFECTS (t) = 1; + t = build1 (NOP_EXPR, ptr, t); + TREE_SIDE_EFFECTS (t) = 1; + } } /* Calculate! */ - return expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL); + return expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL); } @@ -7446,28 +7483,32 @@ function_arg (cum, mode, type, named, incoming) int incoming; { int max_arg_words = (TARGET_64BIT ? 8 : 4); - int arg_size = FUNCTION_ARG_SIZE (mode, type); int alignment = 0; + int arg_size; int fpr_reg_base; int gpr_reg_base; rtx retval; + if (mode == VOIDmode) + return NULL_RTX; + + arg_size = FUNCTION_ARG_SIZE (mode, type); + + /* If this arg would be passed partially or totally on the stack, then + this routine should return zero. FUNCTION_ARG_PARTIAL_NREGS will + handle arguments which are split between regs and stack slots if + the ABI mandates split arguments. */ if (! TARGET_64BIT) { - /* If this arg would be passed partially or totally on the stack, then - this routine should return zero. FUNCTION_ARG_PARTIAL_NREGS will - handle arguments which are split between regs and stack slots if - the ABI mandates split arguments. */ - if (cum->words + arg_size > max_arg_words - || mode == VOIDmode) + /* The 32-bit ABI does not split arguments. */ + if (cum->words + arg_size > max_arg_words) return NULL_RTX; } else { if (arg_size > 1) alignment = cum->words & 1; - if (cum->words + alignment >= max_arg_words - || mode == VOIDmode) + if (cum->words + alignment >= max_arg_words) return NULL_RTX; } @@ -7488,8 +7529,11 @@ function_arg (cum, mode, type, named, incoming) gpr_reg_base = 26 - cum->words; fpr_reg_base = 32 + cum->words; - /* Arguments wider than one word need special treatment. */ - if (arg_size > 1) + /* Arguments wider than one word and small aggregates need special + treatment. */ + if (arg_size > 1 + || mode == BLKmode + || (type && AGGREGATE_TYPE_P (type))) { /* Double-extended precision (80-bit), quad-precision (128-bit) and aggregates including complex numbers are aligned on @@ -7497,7 +7541,10 @@ function_arg (cum, mode, type, named, incoming) are associated one-to-one, with general registers r26 through r19, and also with floating-point registers fr4 through fr11. Arguments larger than one word are always - passed in general registers. */ + passed in general registers. + + Using a PARALLEL with a word mode register results in left + justified data on a big-endian target. */ rtx loc[8]; int i, offset = 0, ub = arg_size; @@ -7517,7 +7564,7 @@ function_arg (cum, mode, type, named, incoming) return gen_rtx_PARALLEL (mode, gen_rtvec_v (ub, loc)); } - } + } else { /* If the argument is larger than a word, then we know precisely @@ -7534,6 +7581,34 @@ function_arg (cum, mode, type, named, incoming) gpr_reg_base = 25; fpr_reg_base = 34; } + + /* Structures 5 to 8 bytes in size are passed in the general + registers in the same manner as other non floating-point + objects. The data is right-justified and zero-extended + to 64 bits. + + This is magic. Normally, using a PARALLEL results in left + justified data on a big-endian target. However, using a + single double-word register provides the required right + justication for 5 to 8 byte structures. This has nothing + to do with the direction of padding specified for the argument. + It has to do with how the data is widened and shifted into + and from the register. + + Aside from adding load_multiple and store_multiple patterns, + this is the only way that I have found to obtain right + justification of BLKmode data when it has a size greater + than one word. Splitting the operation into two SImode loads + or returning a DImode REG results in left justified data. */ + if (mode == BLKmode) + { + rtx loc[1]; + + loc[0] = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (DImode, gpr_reg_base), + const0_rtx); + return gen_rtx_PARALLEL (mode, gen_rtvec_v (1, loc)); + } } else { diff --git a/gcc/config/pa/pa.h b/gcc/config/pa/pa.h index 04e417f3640..aeef60fccf2 100644 --- a/gcc/config/pa/pa.h +++ b/gcc/config/pa/pa.h @@ -407,7 +407,7 @@ extern int target_flags; /* Largest alignment required for any stack parameter, in bits. Don't define this if it is equal to PARM_BOUNDARY */ -#define MAX_PARM_BOUNDARY 64 +#define MAX_PARM_BOUNDARY (2 * PARM_BOUNDARY) /* Boundary (in *bits*) on which stack pointer is always aligned; certain optimizations in combine depend on this. @@ -506,9 +506,13 @@ extern struct rtx_def *hppa_pic_save_rtx PARAMS ((void)); PA64 ABI says that objects larger than 128 bits are returned in memory. Note, int_size_in_bytes can return -1 if the size of the object is variable or larger than the maximum value that can be expressed as - a HOST_WIDE_INT. */ + a HOST_WIDE_INT. It can also return zero for an empty type. The + simplest way to handle variable and empty types is to pass them in + memory. This avoids problems in defining the boundaries of argument + slots, allocating registers, etc. */ #define RETURN_IN_MEMORY(TYPE) \ - ((unsigned HOST_WIDE_INT) int_size_in_bytes (TYPE) > (TARGET_64BIT ? 16 : 8)) + (int_size_in_bytes (TYPE) > (TARGET_64BIT ? 16 : 8) \ + || int_size_in_bytes (TYPE) <= 0) /* Register in which address to store a structure value is passed to a function. */ @@ -681,16 +685,18 @@ extern struct rtx_def *hppa_pic_save_rtx PARAMS ((void)); otherwise, FUNC is 0. */ /* On the HP-PA the value is found in register(s) 28(-29), unless - the mode is SF or DF. Then the value is returned in fr4 (32, ) */ + the mode is SF or DF. Then the value is returned in fr4 (32). */ /* This must perform the same promotions as PROMOTE_MODE, else PROMOTE_FUNCTION_RETURN will not work correctly. */ -#define FUNCTION_VALUE(VALTYPE, FUNC) \ - gen_rtx_REG (((INTEGRAL_TYPE_P (VALTYPE) \ - && TYPE_PRECISION (VALTYPE) < BITS_PER_WORD) \ - || POINTER_TYPE_P (VALTYPE)) \ - ? word_mode : TYPE_MODE (VALTYPE), \ - TREE_CODE (VALTYPE) == REAL_TYPE && !TARGET_SOFT_FLOAT ? 32 : 28) +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + gen_rtx_REG (((INTEGRAL_TYPE_P (VALTYPE) \ + && TYPE_PRECISION (VALTYPE) < BITS_PER_WORD) \ + || POINTER_TYPE_P (VALTYPE)) \ + ? word_mode : TYPE_MODE (VALTYPE), \ + (TREE_CODE (VALTYPE) == REAL_TYPE \ + && TYPE_MODE (VALTYPE) != TFmode \ + && !TARGET_SOFT_FLOAT) ? 32 : 28) /* Define how to find the value returned by a library function assuming the value has mode MODE. */ @@ -745,7 +751,9 @@ struct hppa_args {int words, nargs_prototype, indirect; }; (CUM).indirect = 0, \ (CUM).nargs_prototype = 1000 -/* Figure out the size in words of the function argument. */ +/* Figure out the size in words of the function argument. The size + returned by this macro should always be greater than zero because + we pass variable and zero sized objects by reference. */ #define FUNCTION_ARG_SIZE(MODE, TYPE) \ ((((MODE) != BLKmode \ @@ -817,6 +825,12 @@ struct hppa_args {int words, nargs_prototype, indirect; }; #define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ function_arg (&CUM, MODE, TYPE, NAMED, 0) +/* Nonzero if we do not know how to pass TYPE solely in registers. */ +#define MUST_PASS_IN_STACK(MODE,TYPE) \ + ((TYPE) != 0 \ + && (TREE_CODE (TYPE_SIZE (TYPE)) != INTEGER_CST \ + || TREE_ADDRESSABLE (TYPE))) + #define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) \ function_arg (&CUM, MODE, TYPE, NAMED, 1) @@ -833,33 +847,37 @@ struct hppa_args {int words, nargs_prototype, indirect; }; bits, of an argument with the specified mode and type. If it is not defined, `PARM_BOUNDARY' is used for all arguments. */ -#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \ - (((TYPE) != 0) \ - ? ((integer_zerop (TYPE_SIZE (TYPE)) \ - || ! TREE_CONSTANT (TYPE_SIZE (TYPE))) \ - ? BITS_PER_UNIT \ - : (((int_size_in_bytes (TYPE)) + UNITS_PER_WORD - 1) \ - / UNITS_PER_WORD) * BITS_PER_WORD) \ - : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY) \ - ? PARM_BOUNDARY : GET_MODE_ALIGNMENT(MODE))) - -/* Arguments larger than eight bytes are passed by invisible reference */ - -/* PA64 does not pass anything by invisible reference. */ +/* Arguments larger than one word are double word aligned. */ + +#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \ + (((TYPE) \ + ? (integer_zerop (TYPE_SIZE (TYPE)) \ + || !TREE_CONSTANT (TYPE_SIZE (TYPE)) \ + || int_size_in_bytes (TYPE) <= UNITS_PER_WORD) \ + : GET_MODE_SIZE(MODE) <= UNITS_PER_WORD) \ + ? PARM_BOUNDARY : MAX_PARM_BOUNDARY) + +/* In the 32-bit runtime, arguments larger than eight bytes are passed + by invisible reference. As a GCC extension, we also pass anything + with a zero or variable size by reference. + + The 64-bit runtime does not describe passing any types by invisible + reference. The internals of GCC can't currently handle passing + empty structures, and zero or variable length arrays when they are + not passed entirely on the stack or by reference. Thus, as a GCC + extension, we pass these types by reference. The HP compiler doesn't + support these types, so hopefully there shouldn't be any compatibility + issues. This may have to be revisited when HP releases a C99 compiler + or updates the ABI. */ #define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ (TARGET_64BIT \ - ? 0 \ - : (((TYPE) && int_size_in_bytes (TYPE) > 8) \ + ? ((TYPE) && int_size_in_bytes (TYPE) <= 0) \ + : (((TYPE) && (int_size_in_bytes (TYPE) > 8 \ + || int_size_in_bytes (TYPE) <= 0)) \ || ((MODE) && GET_MODE_SIZE (MODE) > 8))) -/* PA64 does not pass anything by invisible reference. - This should be undef'ed for 64bit, but we'll see if this works. The - problem is that we can't test TARGET_64BIT from the preprocessor. */ -#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) \ - (TARGET_64BIT \ - ? 0 \ - : (((TYPE) && int_size_in_bytes (TYPE) > 8) \ - || ((MODE) && GET_MODE_SIZE (MODE) > 8))) +#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) \ + FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED) extern GTY(()) rtx hppa_compare_op0; |