[PATCH] PR/14262 (ABI corner case confusion)
Ulrich Weigand
weigand@i1.informatik.uni-erlangen.de
Sat Mar 6 04:12:00 GMT 2004
Hello,
PR target/14262 is caused by GCC being incompatible with itself
in a certain ABI corner case: passing a 4-byte BLKmode structure
in a register on s390x-ibm-linux. The caller would load the
argument into the high 4 bytes of the register, while the callee
would expect them in the low 4 bytes. According to our ABI,
the latter is in fact correct. (Note that 4-byte BLKmode structs
are quite rare; for most 4-byte structs some integer or floating
point mode is used.)
Now, I can define BLOCK_REG_PADDING to make the problem go away.
However, I think that this should not be necessary: as far as
I understand that macro, if it is not defined, the behaviour
should be the same as if it were defined to the default
FUNCTION_ARG_PADDING setting, which for big-endian targets
is: small structs are padded downward. (Just as our ABI says.)
Unfortunately, there is one chunk in load_register_parameters
that does not behave that way; if BLOCK_REG_PADDING is not
defined, it always loads the value into the high-order part
of the register on big-endian targets. This seems to be a bug:
it is incompatible with what the callee-side expects, and it is
also different from what store_unaligned_arguments_into_pseudos
would do in ports that define STRICT_ALIGNMENT.
Thus I'd suggest the following patch, which makes
load_register_parameters pass small structs in the low-order
part of the register on both big- and little-endian ports.
Bootstrapped/regtested on s390-ibm-linux and s390x-ibm-linux
on both 3.4 and mainline. OK?
Bye,
Ulrich
ChangeLog:
PR target/14262
* calls.c (load_register_parameters): If BLOCK_REG_PADDING is not
defined, pass small BLKmode values in registers in the low-order part.
testsuite/ChangeLog:
* gcc.dg/20040305-2.c: New test.
Index: gcc/calls.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/calls.c,v
retrieving revision 1.315.2.3
diff -c -p -r1.315.2.3 calls.c
*** gcc/calls.c 18 Feb 2004 00:09:03 -0000 1.315.2.3
--- gcc/calls.c 5 Mar 2004 21:53:35 -0000
*************** load_register_parameters (struct arg_dat
*** 1684,1693 ****
{
rtx mem = validize_mem (args[i].value);
- #ifdef BLOCK_REG_PADDING
/* Handle a BLKmode that needs shifting. */
if (nregs == 1 && size < UNITS_PER_WORD
! && args[i].locate.where_pad == downward)
{
rtx tem = operand_subword_force (mem, 0, args[i].mode);
rtx ri = gen_rtx_REG (word_mode, REGNO (reg));
--- 1684,1697 ----
{
rtx mem = validize_mem (args[i].value);
/* Handle a BLKmode that needs shifting. */
if (nregs == 1 && size < UNITS_PER_WORD
! #ifdef BLOCK_REG_PADDING
! && args[i].locate.where_pad == downward
! #else
! && BYTES_BIG_ENDIAN
! #endif
! )
{
rtx tem = operand_subword_force (mem, 0, args[i].mode);
rtx ri = gen_rtx_REG (word_mode, REGNO (reg));
*************** load_register_parameters (struct arg_dat
*** 1702,1708 ****
emit_move_insn (ri, x);
}
else
- #endif
move_block_to_reg (REGNO (reg), mem, nregs, args[i].mode);
}
--- 1706,1711 ----
*** /dev/null Mon Aug 11 14:39:14 2003
--- gcc/testsuite/gcc.dg/20040305-2.c Sat Mar 6 05:09:25 2004
***************
*** 0 ****
--- 1,44 ----
+ /* PR target/14262 */
+ /* { dg-do run } */
+
+ typedef char ACS;
+ typedef char LSM;
+ typedef char PANEL;
+ typedef char DRIVE;
+ typedef struct {
+ ACS acs;
+ LSM lsm;
+ } LSMID;
+ typedef struct {
+ LSMID lsm_id;
+ PANEL panel;
+ } PANELID;
+ typedef struct {
+ PANELID panel_id;
+ DRIVE drive;
+ } DRIVEID;
+
+ void sub (DRIVEID driveid)
+ {
+ if (driveid.drive != 1)
+ abort ();
+ if (driveid.panel_id.panel != 2)
+ abort ();
+ if (driveid.panel_id.lsm_id.lsm != 3)
+ abort ();
+ if (driveid.panel_id.lsm_id.acs != 4)
+ abort ();
+ }
+
+ int main (void)
+ {
+ DRIVEID driveid;
+
+ driveid.drive = 1;
+ driveid.panel_id.panel = 2;
+ driveid.panel_id.lsm_id.lsm = 3;
+ driveid.panel_id.lsm_id.acs = 4;
+
+ sub(driveid);
+ return 0;
+ }
--
Dr. Ulrich Weigand
weigand@informatik.uni-erlangen.de
More information about the Gcc-patches
mailing list