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);