[gnu.gcc.bug] Patch: better handling of double-word structures

jrose@SUN.COM (John Rose) (10/01/89)

This patch allows GCC to implement 8-byte structures as double-words, on
machines where such double-words must be aligned 0 mod 8.

The changes apply to stor-layout.c, of GCC 1.35.

The compiler normally fails to put "x" and "y" in registers, when
compiling the following code for SPARC:
	typedef union { double algn; struct { long lo, hi; } s; } T;
	T swap(T x){ T y; y.s.lo = x.s.hi; y.s.hi = x.s.lo; return y; }
With the patch, no stack temporaries are required by this code, and
everything stays in registers.

					-- John Rose

*** Original-1.35/stor-layout.c	Sun Apr  2 17:20:10 1989
--- stor-layout.c	Sat Sep 30 16:47:07 1989
***************
*** 267,285 ****
  	     build_int (inunits)); /* convert to bits */
    return genop (CEIL_DIV_EXPR, t,
  		build_int (outunits)); /* then to outunits */
  }
  
  /* Set the size, mode and alignment of a ..._DECL node.
     Note that LABEL_DECL, TYPE_DECL and CONST_DECL nodes do not need this,
     and FUNCTION_DECL nodes have them set up in a special (and simple) way.
     Don't call layout_decl for them.
  
     KNOWN_ALIGN is the amount of alignment we can assume this
     decl has with no special effort.  It is relevant only for FIELD_DECLs
!    and depends on the previous fields.
!    All that matters about KNOWN_ALIGN is which powers of 2 divide it.
     If KNOWN_ALIGN is 0, it means, "as much alignment as you like":
     the record will be aligned to suit.  */
  
  void
  layout_decl (decl, known_align)
--- 267,320 ----
  	     build_int (inunits)); /* convert to bits */
    return genop (CEIL_DIV_EXPR, t,
  		build_int (outunits)); /* then to outunits */
  }
  
+ /* For an aggregate type, choose a register mode (a mode other than BLKmode)
+    which is appropriate.  This takes into account the alignment ALIGN,
+    which if more restrictive than the TYPE_ALIGN, may be more conducive to
+    representing the aggregate as a scalar.  No change is made to the type.  */
+ static enum machine_mode
+ agg_choose_mode(type, align)
+     tree type; int align;
+ {
+   if (TYPE_MODE (type) != BLKmode)
+ 	  return TYPE_MODE (type);
+   if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+       /* If structure's known alignment is less than
+ 	 what the scalar mode would need, and it matters,
+ 	 then stick with BLKmode.  */
+ #ifdef STRICT_ALIGNMENT
+       && (align >= BIGGEST_ALIGNMENT
+ 	  || align >= (TREE_INT_CST_LOW (TYPE_SIZE (type))
+ 		       * TYPE_SIZE_UNIT (type)))
+ #endif
+       )
+ 	  {
+ 	    tree field;
+ 	    /* A record which has any BLKmode members must itself be BLKmode;
+ 	       it can't go in a register.  */
+ 	    for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ 		    if (TYPE_MODE (TREE_TYPE (field)) == BLKmode)
+ 			    goto lose;
+ 
+ 	    return agg_mode (TREE_INT_CST_LOW (TYPE_SIZE (type))
+ 			     * TYPE_SIZE_UNIT (type));
+ 	  }
+  lose:
+   return TYPE_MODE (type);
+ }
+ 
  /* Set the size, mode and alignment of a ..._DECL node.
     Note that LABEL_DECL, TYPE_DECL and CONST_DECL nodes do not need this,
     and FUNCTION_DECL nodes have them set up in a special (and simple) way.
     Don't call layout_decl for them.
  
     KNOWN_ALIGN is the amount of alignment we can assume this
     decl has with no special effort.  It is relevant only for FIELD_DECLs
!    and depends on the previous fields.  It is a power of 2, like
!    TYPE_ALIGN and DECL_ALIGN values.
     If KNOWN_ALIGN is 0, it means, "as much alignment as you like":
     the record will be aligned to suit.  */
  
  void
  layout_decl (decl, known_align)
***************
*** 342,351 ****
--- 377,401 ----
  
    /* See if we can use a scalar mode such as QImode or SImode
       in place of BLKmode or a packed byte mode.  */
    /* Conditions are: a fixed size that is correct for another mode
       and occupying a complete byte or bytes on proper boundary.  */
+   if (DECL_MODE (decl) == BLKmode && TYPE_MODE (type) == BLKmode
+       && (TREE_CODE (type) == RECORD_TYPE ||TREE_CODE (type) == UNION_TYPE)
+       && known_align > TYPE_ALIGN (type))
+ 	  /* Reconsider BLKmode if better alignment info is available. */
+ 	    {
+ 	      register enum machine_mode xmode =
+ 		      agg_choose_mode (type, known_align);
+ 	      if (xmode != BLKmode)
+ 			{
+ 			  type = copy_node (type);
+ 			  TYPE_ALIGN (type) = DECL_ALIGN (decl);
+ 			  TYPE_MODE (type) = xmode;
+ 			  TREE_TYPE (decl) = type;
+ 			}
+ 	    }
    if ((DECL_MODE (decl) == BLKmode
         || DECL_MODE (decl) == BImode)
        /* Don't do this if DECL's type requires it to be BLKmode.  */
        && TYPE_MODE (type) != BLKmode
        && TYPE_SIZE (type) != 0
***************
*** 461,474 ****
  	  pending_statics = tree_cons (NULL, field, pending_statics);
  	  continue;
  	}
  
        /* Lay out the field so we know what alignment it needs.
! 	 For KNOWN_ALIGN, pass the number of bits from start of record
! 	 or some divisor of it.  */
! 	 
!       layout_decl (field, var_size ? size_unit : const_size);
        desired_align = DECL_ALIGN (field);
  
        /* Record must have at least as much alignment as any field.
  	 Otherwise, the alignment of the field within the record
  	 is meaningless.  */
--- 511,535 ----
  	  pending_statics = tree_cons (NULL, field, pending_statics);
  	  continue;
  	}
  
        /* Lay out the field so we know what alignment it needs.
! 	 Compute KNOWN_ALIGN from the number of bits from start of record
! 	 (or some divisor of it) and the record alignment so far.  */
! 	{
! 	  unsigned offset = var_size ? size_unit : const_size;
! 	  unsigned known_align = 1;
! 	  if (offset == 0)
! 		  known_align = record_align;
! 	  else while ((known_align & offset) == 0) /*find first set*/
! 		    {
! 		      known_align <<= 1;
! 		      if (known_align >= record_align)  break;
! 		    }
! 	  layout_decl (field, known_align);
! 	}
! 
        desired_align = DECL_ALIGN (field);
  
        /* Record must have at least as much alignment as any field.
  	 Otherwise, the alignment of the field within the record
  	 is meaningless.  */
***************
*** 620,630 ****
  	  error ("field `%s' declared static in union",
  		 IDENTIFIER_POINTER (DECL_NAME (field)));
  	  TREE_STATIC (field) = 0;
  	}
   
!       layout_decl (field, 0);
        DECL_OFFSET (field) = 0;
        DECL_VOFFSET (field) = 0;
        DECL_VOFFSET_UNIT (field) = BITS_PER_UNIT;
  
        /* Union must be at least as aligned as any field requires.  */
--- 681,691 ----
  	  error ("field `%s' declared static in union",
  		 IDENTIFIER_POINTER (DECL_NAME (field)));
  	  TREE_STATIC (field) = 0;
  	}
   
!       layout_decl (field, union_align);
        DECL_OFFSET (field) = 0;
        DECL_VOFFSET (field) = 0;
        DECL_VOFFSET_UNIT (field) = BITS_PER_UNIT;
  
        /* Union must be at least as aligned as any field requires.  */
***************
*** 795,855 ****
        }
  
      case RECORD_TYPE:
        layout_record (type);
        TYPE_MODE (type) = BLKmode;
!       if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
! 	  /* If structure's known alignment is less than
! 	     what the scalar mode would need, and it matters,
! 	     then stick with BLKmode.  */
! #ifdef STRICT_ALIGNMENT
! 	  && (TYPE_ALIGN (type) >= BIGGEST_ALIGNMENT
! 	      || TYPE_ALIGN (type) >= (TREE_INT_CST_LOW (TYPE_SIZE (type))
! 				       * TYPE_SIZE_UNIT (type)))
! #endif
! 	  )
! 	{
! 	  tree field;
! 	  /* A record which has any BLKmode members must itself be BLKmode;
! 	     it can't go in a register.  */
! 	  for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
! 	    if (TYPE_MODE (TREE_TYPE (field)) == BLKmode)
! 	      goto record_lose;
! 
! 	  TYPE_MODE (type) 
! 	    = agg_mode (TREE_INT_CST_LOW (TYPE_SIZE (type))
! 			* TYPE_SIZE_UNIT (type));
! 	record_lose: ;
! 	}
        break;
  
      case UNION_TYPE:
        layout_union (type);
        TYPE_MODE (type) = BLKmode;
!       if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
! 	  /* If structure's known alignment is less than
! 	     what the scalar mode would need, and it matters,
! 	     then stick with BLKmode.  */
! #ifdef STRICT_ALIGNMENT
! 	  && (TYPE_ALIGN (type) >= BIGGEST_ALIGNMENT
! 	      || TYPE_ALIGN (type) >= (TREE_INT_CST_LOW (TYPE_SIZE (type))
! 				       * TYPE_SIZE_UNIT (type)))
! #endif
! 	  )
! 	{
! 	  tree field;
! 	  /* A union which has any BLKmode members must itself be BLKmode;
! 	     it can't go in a register.  */
! 	  for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
! 	    if (TYPE_MODE (TREE_TYPE (field)) == BLKmode)
! 	      goto union_lose;
! 
! 	  TYPE_MODE (type) 
! 	    = agg_mode (TREE_INT_CST_LOW (TYPE_SIZE (type))
! 			* TYPE_SIZE_UNIT (type));
! 	union_lose: ;
! 	}
        break;
  
      case FUNCTION_TYPE:
        TYPE_MODE (type) = EPmode;
        TYPE_SIZE (type) = build_int (2 * POINTER_SIZE / BITS_PER_UNIT);
--- 856,872 ----
        }
  
      case RECORD_TYPE:
        layout_record (type);
        TYPE_MODE (type) = BLKmode;
!       TYPE_MODE (type) = agg_choose_mode (type, TYPE_ALIGN (type));
        break;
  
      case UNION_TYPE:
        layout_union (type);
        TYPE_MODE (type) = BLKmode;
!       TYPE_MODE (type) = agg_choose_mode (type, TYPE_ALIGN (type));
        break;
  
      case FUNCTION_TYPE:
        TYPE_MODE (type) = EPmode;
        TYPE_SIZE (type) = build_int (2 * POINTER_SIZE / BITS_PER_UNIT);