summaryrefslogtreecommitdiffstats
path: root/gcc/expmed.c
diff options
context:
space:
mode:
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>2004-11-05 23:58:39 +0000
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>2004-11-05 23:58:39 +0000
commit4b1dcd6dcc20679c04ae34268159e2355a592664 (patch)
treef692126d33b801c05fd5dad1b6979c0a1f095eb2 /gcc/expmed.c
parent6d6f3860626e35ac2d4fa5337dc70de1f4067126 (diff)
downloadppe42-gcc-4b1dcd6dcc20679c04ae34268159e2355a592664.tar.gz
ppe42-gcc-4b1dcd6dcc20679c04ae34268159e2355a592664.zip
* expmed.c (extract_force_align_mem_bit_field): New.
(extract_split_bit_field): Call it. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@90150 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/expmed.c')
-rw-r--r--gcc/expmed.c149
1 files changed, 148 insertions, 1 deletions
diff --git a/gcc/expmed.c b/gcc/expmed.c
index 67224413e8a..475cdcf71fb 100644
--- a/gcc/expmed.c
+++ b/gcc/expmed.c
@@ -1853,6 +1853,144 @@ lshift_value (enum machine_mode mode, rtx value, int bitpos, int bitsize)
return immed_double_const (low, high, mode);
}
+/* Extract a bit field from a memory by forcing the alignment of the
+ memory. This efficient only if the field spans at least 4 boundaries.
+
+ OP0 is the MEM.
+ BITSIZE is the field width; BITPOS is the position of the first bit.
+ UNSIGNEDP is true if the result should be zero-extended. */
+
+static rtx
+extract_force_align_mem_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
+ unsigned HOST_WIDE_INT bitpos,
+ int unsignedp)
+{
+ enum machine_mode mode, dmode;
+ unsigned int m_bitsize, m_size;
+ unsigned int sign_shift_up, sign_shift_dn;
+ rtx base, a1, a2, v1, v2, comb, shift, result, start;
+
+ /* Choose a mode that will fit BITSIZE. */
+ mode = smallest_mode_for_size (bitsize, MODE_INT);
+ m_size = GET_MODE_SIZE (mode);
+ m_bitsize = GET_MODE_BITSIZE (mode);
+
+ /* Choose a mode twice as wide. Fail if no such mode exists. */
+ dmode = mode_for_size (m_bitsize * 2, MODE_INT, false);
+ if (dmode == BLKmode)
+ return NULL;
+
+ do_pending_stack_adjust ();
+ start = get_last_insn ();
+
+ /* At the end, we'll need an additional shift to deal with sign/zero
+ extension. By default this will be a left+right shift of the
+ appropriate size. But we may be able to elimitate one of them. */
+ sign_shift_up = sign_shift_dn = m_bitsize - bitsize;
+
+ if (STRICT_ALIGNMENT)
+ {
+ base = plus_constant (XEXP (op0, 0), bitpos / BITS_PER_UNIT);
+ base = force_operand (base, NULL);
+ bitpos %= BITS_PER_UNIT;
+
+ /* Force alignment of the address; load two sequential values. */
+ a1 = expand_simple_binop (Pmode, AND, base,
+ GEN_INT (-(HOST_WIDE_INT)m_size),
+ NULL, true, OPTAB_LIB_WIDEN);
+ mark_reg_pointer (a1, m_bitsize);
+ v1 = gen_rtx_MEM (mode, a1);
+ set_mem_align (v1, m_bitsize);
+ v1 = force_reg (mode, validize_mem (v1));
+
+ a2 = plus_constant (a1, GET_MODE_SIZE (mode));
+ v2 = gen_rtx_MEM (mode, a2);
+ set_mem_align (v2, m_bitsize);
+ v2 = force_reg (mode, validize_mem (v2));
+
+ /* Combine these two values into a double-word value. */
+ if (m_bitsize == BITS_PER_WORD)
+ {
+ comb = gen_reg_rtx (dmode);
+ emit_insn (gen_rtx_CLOBBER (VOIDmode, comb));
+ emit_move_insn (gen_rtx_SUBREG (mode, comb, 0), v1);
+ emit_move_insn (gen_rtx_SUBREG (mode, comb, m_size), v2);
+ }
+ else
+ {
+ if (BYTES_BIG_ENDIAN)
+ comb = v1, v1 = v2, v2 = comb;
+ v1 = convert_modes (dmode, mode, v1, true);
+ if (v1 == NULL)
+ goto fail;
+ v2 = convert_modes (dmode, mode, v2, true);
+ v2 = expand_simple_binop (dmode, ASHIFT, v2, GEN_INT (m_bitsize),
+ NULL, true, OPTAB_LIB_WIDEN);
+ if (v2 == NULL)
+ goto fail;
+ comb = expand_simple_binop (dmode, IOR, v1, v2, NULL,
+ true, OPTAB_LIB_WIDEN);
+ if (comb == NULL)
+ goto fail;
+ }
+
+ shift = expand_simple_binop (Pmode, AND, base, GEN_INT (m_size - 1),
+ NULL, true, OPTAB_LIB_WIDEN);
+ shift = expand_mult (Pmode, shift, GEN_INT (BITS_PER_UNIT), NULL, 1);
+
+ if (bitpos != 0)
+ {
+ if (sign_shift_up <= bitpos)
+ bitpos -= sign_shift_up, sign_shift_up = 0;
+ shift = expand_simple_binop (Pmode, PLUS, shift, GEN_INT (bitpos),
+ NULL, true, OPTAB_LIB_WIDEN);
+ }
+ }
+ else
+ {
+ unsigned HOST_WIDE_INT offset = bitpos / BITS_PER_UNIT;
+ bitpos %= BITS_PER_UNIT;
+
+ /* When strict alignment is not required, we can just load directly
+ from memory without masking. If the remaining BITPOS offset is
+ small enough, we may be able to do all operations in MODE as
+ opposed to DMODE. */
+ if (bitpos + bitsize <= m_bitsize)
+ dmode = mode;
+ comb = adjust_address (op0, dmode, offset);
+
+ if (sign_shift_up <= bitpos)
+ bitpos -= sign_shift_up, sign_shift_up = 0;
+ shift = GEN_INT (bitpos);
+ }
+
+ /* Shift down the double-word such that the requested value is at bit 0. */
+ if (shift != const0_rtx)
+ comb = expand_simple_binop (dmode, unsignedp ? LSHIFTRT : ASHIFTRT,
+ comb, shift, NULL, unsignedp, OPTAB_LIB_WIDEN);
+ if (comb == NULL)
+ goto fail;
+
+ /* If the field exactly matches MODE, then all we need to do is return the
+ lowpart. Otherwise, shift to get the sign bits set properly. */
+ result = force_reg (mode, gen_lowpart (mode, comb));
+
+ if (sign_shift_up)
+ result = expand_simple_binop (mode, ASHIFT, result,
+ GEN_INT (sign_shift_up),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ if (sign_shift_dn)
+ result = expand_simple_binop (mode, unsignedp ? LSHIFTRT : ASHIFTRT,
+ result, GEN_INT (sign_shift_dn),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+
+ return result;
+
+ fail:
+ delete_insns_since (start);
+ return NULL;
+}
+
/* Extract a bit field that is split across two words
and return an RTX for the result.
@@ -1874,7 +2012,16 @@ extract_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
if (REG_P (op0) || GET_CODE (op0) == SUBREG)
unit = BITS_PER_WORD;
else
- unit = MIN (MEM_ALIGN (op0), BITS_PER_WORD);
+ {
+ unit = MIN (MEM_ALIGN (op0), BITS_PER_WORD);
+ if (bitsize / unit > 2)
+ {
+ rtx tmp = extract_force_align_mem_bit_field (op0, bitsize, bitpos,
+ unsignedp);
+ if (tmp)
+ return tmp;
+ }
+ }
while (bitsdone < bitsize)
{
OpenPOWER on IntegriCloud