+static tree
+component_ref_for_mem_expr (ref)
+ tree ref;
+{
+ tree inner = TREE_OPERAND (ref, 0);
+
+ if (TREE_CODE (inner) == COMPONENT_REF)
+ inner = component_ref_for_mem_expr (inner);
+ else
+ {
+ tree placeholder_ptr = 0;
+
+ /* Now remove any conversions: they don't change what the underlying
+ object is. Likewise for SAVE_EXPR. Also handle PLACEHOLDER_EXPR. */
+ while (TREE_CODE (inner) == NOP_EXPR || TREE_CODE (inner) == CONVERT_EXPR
+ || TREE_CODE (inner) == NON_LVALUE_EXPR
+ || TREE_CODE (inner) == VIEW_CONVERT_EXPR
+ || TREE_CODE (inner) == SAVE_EXPR
+ || TREE_CODE (inner) == PLACEHOLDER_EXPR)
+ if (TREE_CODE (inner) == PLACEHOLDER_EXPR)
+ inner = find_placeholder (inner, &placeholder_ptr);
+ else
+ inner = TREE_OPERAND (inner, 0);
+
+ if (! DECL_P (inner))
+ inner = NULL_TREE;
+ }
+
+ if (inner == TREE_OPERAND (ref, 0))
+ return ref;
+ else
+ return build (COMPONENT_REF, TREE_TYPE (ref), inner,
+ TREE_OPERAND (ref, 1));
+}
+
+/* Given REF, a MEM, and T, either the type of X or the expression
+ corresponding to REF, set the memory attributes. OBJECTP is nonzero
+ if we are making a new object of this type. */
+
+void
+set_mem_attributes (ref, t, objectp)
+ rtx ref;
+ tree t;
+ int objectp;
+{
+ HOST_WIDE_INT alias = MEM_ALIAS_SET (ref);
+ tree expr = MEM_EXPR (ref);
+ rtx offset = MEM_OFFSET (ref);
+ rtx size = MEM_SIZE (ref);
+ unsigned int align = MEM_ALIGN (ref);
+ tree type;
+
+ /* It can happen that type_for_mode was given a mode for which there
+ is no language-level type. In which case it returns NULL, which
+ we can see here. */
+ if (t == NULL_TREE)
+ return;
+
+ type = TYPE_P (t) ? t : TREE_TYPE (t);
+
+ /* If we have already set DECL_RTL = ref, get_alias_set will get the
+ wrong answer, as it assumes that DECL_RTL already has the right alias
+ info. Callers should not set DECL_RTL until after the call to
+ set_mem_attributes. */
+ if (DECL_P (t) && ref == DECL_RTL_IF_SET (t))
+ abort ();
+
+ /* Get the alias set from the expression or type (perhaps using a
+ front-end routine) and use it. */
+ alias = get_alias_set (t);
+
+ MEM_VOLATILE_P (ref) = TYPE_VOLATILE (type);
+ MEM_IN_STRUCT_P (ref) = AGGREGATE_TYPE_P (type);
+ RTX_UNCHANGING_P (ref)
+ |= ((lang_hooks.honor_readonly
+ && (TYPE_READONLY (type) || TREE_READONLY (t)))
+ || (! TYPE_P (t) && TREE_CONSTANT (t)));
+
+ /* If we are making an object of this type, or if this is a DECL, we know
+ that it is a scalar if the type is not an aggregate. */
+ if ((objectp || DECL_P (t)) && ! AGGREGATE_TYPE_P (type))
+ MEM_SCALAR_P (ref) = 1;
+
+ /* We can set the alignment from the type if we are making an object,
+ this is an INDIRECT_REF, or if TYPE_ALIGN_OK. */
+ if (objectp || TREE_CODE (t) == INDIRECT_REF || TYPE_ALIGN_OK (type))
+ align = MAX (align, TYPE_ALIGN (type));
+
+ /* If the size is known, we can set that. */
+ if (TYPE_SIZE_UNIT (type) && host_integerp (TYPE_SIZE_UNIT (type), 1))
+ size = GEN_INT (tree_low_cst (TYPE_SIZE_UNIT (type), 1));
+
+ /* If T is not a type, we may be able to deduce some more information about
+ the expression. */
+ if (! TYPE_P (t))
+ {
+ maybe_set_unchanging (ref, t);
+ if (TREE_THIS_VOLATILE (t))
+ MEM_VOLATILE_P (ref) = 1;
+
+ /* Now remove any conversions: they don't change what the underlying
+ object is. Likewise for SAVE_EXPR. */
+ while (TREE_CODE (t) == NOP_EXPR || TREE_CODE (t) == CONVERT_EXPR
+ || TREE_CODE (t) == NON_LVALUE_EXPR
+ || TREE_CODE (t) == VIEW_CONVERT_EXPR
+ || TREE_CODE (t) == SAVE_EXPR)
+ t = TREE_OPERAND (t, 0);
+
+ /* If this expression can't be addressed (e.g., it contains a reference
+ to a non-addressable field), show we don't change its alias set. */
+ if (! can_address_p (t))
+ MEM_KEEP_ALIAS_SET_P (ref) = 1;
+
+ /* If this is a decl, set the attributes of the MEM from it. */
+ if (DECL_P (t))
+ {
+ expr = t;
+ offset = const0_rtx;
+ size = (DECL_SIZE_UNIT (t)
+ && host_integerp (DECL_SIZE_UNIT (t), 1)
+ ? GEN_INT (tree_low_cst (DECL_SIZE_UNIT (t), 1)) : 0);
+ align = DECL_ALIGN (t);
+ }
+
+ /* If this is a constant, we know the alignment. */
+ else if (TREE_CODE_CLASS (TREE_CODE (t)) == 'c')
+ {
+ align = TYPE_ALIGN (type);
+#ifdef CONSTANT_ALIGNMENT
+ align = CONSTANT_ALIGNMENT (t, align);
+#endif
+ }
+
+ /* If this is a field reference and not a bit-field, record it. */
+ /* ??? There is some information that can be gleened from bit-fields,
+ such as the word offset in the structure that might be modified.
+ But skip it for now. */
+ else if (TREE_CODE (t) == COMPONENT_REF
+ && ! DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
+ {
+ expr = component_ref_for_mem_expr (t);
+ offset = const0_rtx;
+ /* ??? Any reason the field size would be different than
+ the size we got from the type? */
+ }
+
+ /* If this is an array reference, look for an outer field reference. */
+ else if (TREE_CODE (t) == ARRAY_REF)
+ {
+ tree off_tree = size_zero_node;
+
+ do
+ {
+ off_tree
+ = fold (build (PLUS_EXPR, sizetype,
+ fold (build (MULT_EXPR, sizetype,
+ TREE_OPERAND (t, 1),
+ TYPE_SIZE_UNIT (TREE_TYPE (t)))),
+ off_tree));
+ t = TREE_OPERAND (t, 0);
+ }
+ while (TREE_CODE (t) == ARRAY_REF);
+
+ if (TREE_CODE (t) == COMPONENT_REF)
+ {
+ expr = component_ref_for_mem_expr (t);
+ if (host_integerp (off_tree, 1))
+ offset = GEN_INT (tree_low_cst (off_tree, 1));
+ /* ??? Any reason the field size would be different than
+ the size we got from the type? */
+ }
+ }
+ }
+
+ /* Now set the attributes we computed above. */
+ MEM_ATTRS (ref)
+ = get_mem_attrs (alias, expr, offset, size, align, GET_MODE (ref));
+
+ /* If this is already known to be a scalar or aggregate, we are done. */
+ if (MEM_IN_STRUCT_P (ref) || MEM_SCALAR_P (ref))
+ return;
+
+ /* If it is a reference into an aggregate, this is part of an aggregate.
+ Otherwise we don't know. */
+ else if (TREE_CODE (t) == COMPONENT_REF || TREE_CODE (t) == ARRAY_REF
+ || TREE_CODE (t) == ARRAY_RANGE_REF
+ || TREE_CODE (t) == BIT_FIELD_REF)
+ MEM_IN_STRUCT_P (ref) = 1;
+}
+
+/* Set the alias set of MEM to SET. */
+
+void
+set_mem_alias_set (mem, set)
+ rtx mem;
+ HOST_WIDE_INT set;
+{
+#ifdef ENABLE_CHECKING
+ /* If the new and old alias sets don't conflict, something is wrong. */
+ if (!alias_sets_conflict_p (set, MEM_ALIAS_SET (mem)))
+ abort ();
+#endif
+
+ MEM_ATTRS (mem) = get_mem_attrs (set, MEM_EXPR (mem), MEM_OFFSET (mem),
+ MEM_SIZE (mem), MEM_ALIGN (mem),
+ GET_MODE (mem));
+}
+
+/* Set the alignment of MEM to ALIGN bits. */
+
+void
+set_mem_align (mem, align)
+ rtx mem;
+ unsigned int align;
+{
+ MEM_ATTRS (mem) = get_mem_attrs (MEM_ALIAS_SET (mem), MEM_EXPR (mem),
+ MEM_OFFSET (mem), MEM_SIZE (mem), align,
+ GET_MODE (mem));
+}
+
+/* Set the expr for MEM to EXPR. */
+
+void
+set_mem_expr (mem, expr)
+ rtx mem;
+ tree expr;
+{
+ MEM_ATTRS (mem)
+ = get_mem_attrs (MEM_ALIAS_SET (mem), expr, MEM_OFFSET (mem),
+ MEM_SIZE (mem), MEM_ALIGN (mem), GET_MODE (mem));
+}
+
+/* Set the offset of MEM to OFFSET. */
+
+void
+set_mem_offset (mem, offset)
+ rtx mem, offset;
+{
+ MEM_ATTRS (mem) = get_mem_attrs (MEM_ALIAS_SET (mem), MEM_EXPR (mem),
+ offset, MEM_SIZE (mem), MEM_ALIGN (mem),
+ GET_MODE (mem));
+}
+\f
+/* Return a memory reference like MEMREF, but with its mode changed to MODE
+ and its address changed to ADDR. (VOIDmode means don't change the mode.
+ NULL for ADDR means don't change the address.) VALIDATE is nonzero if the
+ returned memory location is required to be valid. The memory
+ attributes are not changed. */
+
+static rtx