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