+ op0 = expand_shift (LSHIFT_EXPR, imode, op0, count,
+ target, 0);
+ op0 = expand_shift (RSHIFT_EXPR, imode, op0, count,
+ target, 0);
+ }
+ }
+
+ return op0;
+ }
+ }
+ goto normal_inner_ref;
+
+ case BIT_FIELD_REF:
+ case ARRAY_RANGE_REF:
+ normal_inner_ref:
+ {
+ enum machine_mode mode1, mode2;
+ HOST_WIDE_INT bitsize, bitpos;
+ tree offset;
+ int volatilep = 0, must_force_mem;
+ tree tem = get_inner_reference (exp, &bitsize, &bitpos, &offset,
+ &mode1, &unsignedp, &volatilep, true);
+ rtx orig_op0, memloc;
+
+ /* If we got back the original object, something is wrong. Perhaps
+ we are evaluating an expression too early. In any event, don't
+ infinitely recurse. */
+ gcc_assert (tem != exp);
+
+ /* If TEM's type is a union of variable size, pass TARGET to the inner
+ computation, since it will need a temporary and TARGET is known
+ to have to do. This occurs in unchecked conversion in Ada. */
+ orig_op0 = op0
+ = expand_expr (tem,
+ (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE
+ && (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem)))
+ != INTEGER_CST)
+ && modifier != EXPAND_STACK_PARM
+ ? target : NULL_RTX),
+ VOIDmode,
+ (modifier == EXPAND_INITIALIZER
+ || modifier == EXPAND_CONST_ADDRESS
+ || modifier == EXPAND_STACK_PARM)
+ ? modifier : EXPAND_NORMAL);
+
+ mode2
+ = CONSTANT_P (op0) ? TYPE_MODE (TREE_TYPE (tem)) : GET_MODE (op0);
+
+ /* If we have either an offset, a BLKmode result, or a reference
+ outside the underlying object, we must force it to memory.
+ Such a case can occur in Ada if we have unchecked conversion
+ of an expression from a scalar type to an aggregate type or
+ for an ARRAY_RANGE_REF whose type is BLKmode, or if we were
+ passed a partially uninitialized object or a view-conversion
+ to a larger size. */
+ must_force_mem = (offset
+ || mode1 == BLKmode
+ || bitpos + bitsize > GET_MODE_BITSIZE (mode2));
+
+ /* Handle CONCAT first. */
+ if (GET_CODE (op0) == CONCAT && !must_force_mem)
+ {
+ if (bitpos == 0
+ && bitsize == GET_MODE_BITSIZE (GET_MODE (op0)))
+ return op0;
+ if (bitpos == 0
+ && bitsize == GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0)))
+ && bitsize)
+ {
+ op0 = XEXP (op0, 0);
+ mode2 = GET_MODE (op0);
+ }
+ else if (bitpos == GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0)))
+ && bitsize == GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 1)))
+ && bitpos
+ && bitsize)
+ {
+ op0 = XEXP (op0, 1);
+ bitpos = 0;
+ mode2 = GET_MODE (op0);
+ }
+ else
+ /* Otherwise force into memory. */
+ must_force_mem = 1;
+ }
+
+ /* If this is a constant, put it in a register if it is a legitimate
+ constant and we don't need a memory reference. */
+ if (CONSTANT_P (op0)
+ && mode2 != BLKmode
+ && LEGITIMATE_CONSTANT_P (op0)
+ && !must_force_mem)
+ op0 = force_reg (mode2, op0);
+
+ /* Otherwise, if this is a constant, try to force it to the constant
+ pool. Note that back-ends, e.g. MIPS, may refuse to do so if it
+ is a legitimate constant. */
+ else if (CONSTANT_P (op0) && (memloc = force_const_mem (mode2, op0)))
+ op0 = validize_mem (memloc);
+
+ /* Otherwise, if this is a constant or the object is not in memory
+ and need be, put it there. */
+ else if (CONSTANT_P (op0) || (!MEM_P (op0) && must_force_mem))
+ {
+ tree nt = build_qualified_type (TREE_TYPE (tem),
+ (TYPE_QUALS (TREE_TYPE (tem))
+ | TYPE_QUAL_CONST));
+ memloc = assign_temp (nt, 1, 1, 1);
+ emit_move_insn (memloc, op0);
+ op0 = memloc;
+ }
+
+ if (offset)
+ {
+ enum machine_mode address_mode;
+ rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode,
+ EXPAND_SUM);
+
+ gcc_assert (MEM_P (op0));
+
+ address_mode
+ = targetm.addr_space.address_mode (MEM_ADDR_SPACE (op0));
+ if (GET_MODE (offset_rtx) != address_mode)
+ offset_rtx = convert_to_mode (address_mode, offset_rtx, 0);
+
+ if (GET_MODE (op0) == BLKmode
+ /* A constant address in OP0 can have VOIDmode, we must
+ not try to call force_reg in that case. */
+ && GET_MODE (XEXP (op0, 0)) != VOIDmode
+ && bitsize != 0
+ && (bitpos % bitsize) == 0
+ && (bitsize % GET_MODE_ALIGNMENT (mode1)) == 0
+ && MEM_ALIGN (op0) == GET_MODE_ALIGNMENT (mode1))
+ {
+ op0 = adjust_address (op0, mode1, bitpos / BITS_PER_UNIT);
+ bitpos = 0;
+ }
+
+ op0 = offset_address (op0, offset_rtx,
+ highest_pow2_factor (offset));
+ }
+
+ /* If OFFSET is making OP0 more aligned than BIGGEST_ALIGNMENT,
+ record its alignment as BIGGEST_ALIGNMENT. */
+ if (MEM_P (op0) && bitpos == 0 && offset != 0
+ && is_aligning_offset (offset, tem))
+ set_mem_align (op0, BIGGEST_ALIGNMENT);
+
+ /* Don't forget about volatility even if this is a bitfield. */
+ if (MEM_P (op0) && volatilep && ! MEM_VOLATILE_P (op0))
+ {
+ if (op0 == orig_op0)
+ op0 = copy_rtx (op0);
+
+ MEM_VOLATILE_P (op0) = 1;
+ }
+
+ /* In cases where an aligned union has an unaligned object
+ as a field, we might be extracting a BLKmode value from
+ an integer-mode (e.g., SImode) object. Handle this case
+ by doing the extract into an object as wide as the field
+ (which we know to be the width of a basic mode), then
+ storing into memory, and changing the mode to BLKmode. */
+ if (mode1 == VOIDmode
+ || REG_P (op0) || GET_CODE (op0) == SUBREG
+ || (mode1 != BLKmode && ! direct_load[(int) mode1]
+ && GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
+ && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT
+ && modifier != EXPAND_CONST_ADDRESS
+ && modifier != EXPAND_INITIALIZER)
+ /* If the field isn't aligned enough to fetch as a memref,
+ fetch it as a bit field. */
+ || (mode1 != BLKmode
+ && (((TYPE_ALIGN (TREE_TYPE (tem)) < GET_MODE_ALIGNMENT (mode)
+ || (bitpos % GET_MODE_ALIGNMENT (mode) != 0)
+ || (MEM_P (op0)
+ && (MEM_ALIGN (op0) < GET_MODE_ALIGNMENT (mode1)
+ || (bitpos % GET_MODE_ALIGNMENT (mode1) != 0))))
+ && ((modifier == EXPAND_CONST_ADDRESS
+ || modifier == EXPAND_INITIALIZER)
+ ? STRICT_ALIGNMENT
+ : SLOW_UNALIGNED_ACCESS (mode1, MEM_ALIGN (op0))))
+ || (bitpos % BITS_PER_UNIT != 0)))
+ /* If the type and the field are a constant size and the
+ size of the type isn't the same size as the bitfield,
+ we must use bitfield operations. */
+ || (bitsize >= 0
+ && TYPE_SIZE (TREE_TYPE (exp))
+ && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) == INTEGER_CST
+ && 0 != compare_tree_int (TYPE_SIZE (TREE_TYPE (exp)),
+ bitsize)))
+ {
+ enum machine_mode ext_mode = mode;
+
+ if (ext_mode == BLKmode
+ && ! (target != 0 && MEM_P (op0)
+ && MEM_P (target)
+ && bitpos % BITS_PER_UNIT == 0))
+ ext_mode = mode_for_size (bitsize, MODE_INT, 1);
+
+ if (ext_mode == BLKmode)
+ {
+ if (target == 0)
+ target = assign_temp (type, 0, 1, 1);
+
+ if (bitsize == 0)
+ return target;
+
+ /* In this case, BITPOS must start at a byte boundary and
+ TARGET, if specified, must be a MEM. */
+ gcc_assert (MEM_P (op0)
+ && (!target || MEM_P (target))
+ && !(bitpos % BITS_PER_UNIT));
+
+ emit_block_move (target,
+ adjust_address (op0, VOIDmode,
+ bitpos / BITS_PER_UNIT),
+ GEN_INT ((bitsize + BITS_PER_UNIT - 1)
+ / BITS_PER_UNIT),
+ (modifier == EXPAND_STACK_PARM
+ ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
+
+ return target;
+ }
+
+ op0 = validize_mem (op0);
+
+ if (MEM_P (op0) && REG_P (XEXP (op0, 0)))
+ mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0));
+
+ op0 = extract_bit_field (op0, bitsize, bitpos, unsignedp,
+ (modifier == EXPAND_STACK_PARM
+ ? NULL_RTX : target),
+ ext_mode, ext_mode);
+
+ /* If the result is a record type and BITSIZE is narrower than
+ the mode of OP0, an integral mode, and this is a big endian
+ machine, we must put the field into the high-order bits. */
+ if (TREE_CODE (type) == RECORD_TYPE && BYTES_BIG_ENDIAN
+ && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
+ && bitsize < (HOST_WIDE_INT) GET_MODE_BITSIZE (GET_MODE (op0)))
+ op0 = expand_shift (LSHIFT_EXPR, GET_MODE (op0), op0,
+ size_int (GET_MODE_BITSIZE (GET_MODE (op0))
+ - bitsize),
+ op0, 1);
+
+ /* If the result type is BLKmode, store the data into a temporary
+ of the appropriate type, but with the mode corresponding to the
+ mode for the data we have (op0's mode). It's tempting to make
+ this a constant type, since we know it's only being stored once,
+ but that can cause problems if we are taking the address of this
+ COMPONENT_REF because the MEM of any reference via that address
+ will have flags corresponding to the type, which will not
+ necessarily be constant. */
+ if (mode == BLKmode)
+ {
+ HOST_WIDE_INT size = GET_MODE_BITSIZE (ext_mode);
+ rtx new_rtx;
+
+ /* If the reference doesn't use the alias set of its type,
+ we cannot create the temporary using that type. */
+ if (component_uses_parent_alias_set (exp))
+ {
+ new_rtx = assign_stack_local (ext_mode, size, 0);
+ set_mem_alias_set (new_rtx, get_alias_set (exp));
+ }
+ else
+ new_rtx = assign_stack_temp_for_type (ext_mode, size, 0, type);
+
+ emit_move_insn (new_rtx, op0);
+ op0 = copy_rtx (new_rtx);
+ PUT_MODE (op0, BLKmode);
+ set_mem_attributes (op0, exp, 1);
+ }
+
+ return op0;
+ }
+
+ /* If the result is BLKmode, use that to access the object
+ now as well. */
+ if (mode == BLKmode)
+ mode1 = BLKmode;