[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