This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
PATCH: PA back-end fixes to correct various argument passing problems
- From: "John David Anglin" <dave at hiauly1 dot hia dot nrc dot ca>
- To: gcc-patches at gcc dot gnu dot org
- Cc: law at redhat dot com, rth at redhat dot com
- Date: Mon, 16 Sep 2002 16:17:25 -0400 (EDT)
- Subject: PATCH: PA back-end fixes to correct various argument passing problems
This patch fixes various problems related to the passing of aggregates
and va_arg corner cases (variable and zero sized types).
For TARGET_64BIT, the current justification for aggregates is wrong.
We need left justification. I also decided to pass variable and zero
sized objects by reference although nothing else in the 64-bit runtime
is passed by reference. The GCC infrastructure currently doesn't
allow passing variable sized types in registers. I could resolve
to my satisfaction the issues related to zero sized types in the
general registers. So, I decided that if we want to support these
extensions, we should pass these types by reference.
I avoided defining MEMBER_TYPE_FORCES_BLOCK and FUNCTION_ARG_REG_LITTLE_ENDIAN
by doing some fancy footwork in function_arg_padding and function_arg.
function_arg_padding checks for aggregate types. function_arg uses a
PARALLEL for aggregates with a size less than or equal to a word.
For !TARGET_64BIT, the main issue was fixing the structure passing
issues. The most difficult problem was finding how to downward pad
5 to 8 byte structures, both in registers and on the stack. There
was also a fix for structs with a single field.
The patch has been tested on hppa64-hp-hpux11.00, hppa2.0w-hp-hpux11.00
and hppa-linux. No regressions have been observed and it fixes a number
of fails. I have built glibc-2.2.5-15 (debian) using the patched gcc
under hppa-linux.
I will install to the main if the companion patch to calls.c and function.c
is approved.
Dave
--
J. David Anglin dave.anglin@nrc.ca
National Research Council of Canada (613) 990-0752 (FAX: 952-6605)
2002-09-16 John David Anglin <dave@hiauly1.hia.nrc.ca>
* 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.
Index: config/pa/pa-64.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/pa-64.h,v
retrieving revision 1.11
diff -u -3 -p -r1.11 pa-64.h
--- config/pa/pa-64.h 4 Sep 2002 18:09:32 -0000 1.11
+++ config/pa/pa-64.h 16 Sep 2002 17:51:39 -0000
@@ -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))
Index: config/pa/pa.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/pa.c,v
retrieving revision 1.179
diff -u -3 -p -r1.179 pa.c
--- config/pa/pa.c 4 Sep 2002 18:09:32 -0000 1.179
+++ config/pa/pa.c 16 Sep 2002 17:51:43 -0000
@@ -5089,22 +5089,33 @@ function_arg_padding (mode, type)
enum machine_mode mode;
tree type;
{
- int size;
-
- if (mode == BLKmode)
+ if (mode == BLKmode
+ || (TARGET_64BIT && type && AGGREGATE_TYPE_P (type)))
{
- if (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
- size = int_size_in_bytes (type) * BITS_PER_UNIT;
+ /* 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, in
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, in
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, in
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, in
return gen_rtx_PARALLEL (mode, gen_rtvec_v (ub, loc));
}
- }
+ }
else
{
/* If the argument is larger than a word, then we know precisely
@@ -7533,6 +7580,34 @@ function_arg (cum, mode, type, named, in
{
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
Index: config/pa/pa.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/pa.h,v
retrieving revision 1.166
diff -u -3 -p -r1.166 pa.h
--- config/pa/pa.h 21 Aug 2002 02:41:50 -0000 1.166
+++ config/pa/pa.h 16 Sep 2002 17:51:44 -0000
@@ -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
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
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_proto
(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_proto
#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_proto
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 */
+/* Arguments larger than one word are double word aligned. */
-/* PA64 does not pass anything by invisible reference. */
+#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;