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