expmed.c (extract_force_align_mem_bit_field): New.

* expmed.c (extract_force_align_mem_bit_field): New.
        (extract_split_bit_field): Call it.

From-SVN: r90150
This commit is contained in:
Richard Henderson 2004-11-05 15:58:39 -08:00 committed by Richard Henderson
parent f526a3c8fd
commit abb73f901b
2 changed files with 153 additions and 1 deletions

View File

@ -1,3 +1,8 @@
2004-11-05 Richard Henderson <rth@redhat.com>
* expmed.c (extract_force_align_mem_bit_field): New.
(extract_split_bit_field): Call it.
2004-11-05 Richard Henderson <rth@redhat.com>
* ia64.md (UNSPEC_SHRP): New.

View File

@ -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)
{