1639 lines
52 KiB
C
1639 lines
52 KiB
C
/* Medium-level subroutines: convert bit-field store and extract
|
||
and shifts, multiplies and divides to rtl instructions.
|
||
Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc.
|
||
|
||
This file is part of GNU CC.
|
||
|
||
GNU CC is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 1, or (at your option)
|
||
any later version.
|
||
|
||
GNU CC is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU CC; see the file COPYING. If not, write to
|
||
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||
|
||
|
||
#include "config.h"
|
||
#include "rtl.h"
|
||
#include "tree.h"
|
||
#include "flags.h"
|
||
#include "insn-flags.h"
|
||
#include "insn-codes.h"
|
||
#include "insn-config.h"
|
||
#include "expr.h"
|
||
#include "recog.h"
|
||
|
||
static rtx extract_split_bit_field ();
|
||
static rtx extract_fixed_bit_field ();
|
||
static void store_split_bit_field ();
|
||
static void store_fixed_bit_field ();
|
||
|
||
/* Return an rtx representing minus the value of X.
|
||
MODE is the intended mode of the result,
|
||
useful if X is a CONST_INT. */
|
||
|
||
rtx
|
||
negate_rtx (mode, x)
|
||
enum machine_mode mode;
|
||
rtx x;
|
||
{
|
||
if (GET_CODE (x) == CONST_INT)
|
||
{
|
||
int val = - INTVAL (x);
|
||
if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_INT)
|
||
{
|
||
/* Sign extend the value from the bits that are significant. */
|
||
if (val & (1 << (GET_MODE_BITSIZE (mode) - 1)))
|
||
val |= (-1) << GET_MODE_BITSIZE (mode);
|
||
else
|
||
val &= (1 << GET_MODE_BITSIZE (mode)) - 1;
|
||
}
|
||
return gen_rtx (CONST_INT, VOIDmode, val);
|
||
}
|
||
else
|
||
return expand_unop (GET_MODE (x), neg_optab, x, 0, 0);
|
||
}
|
||
|
||
/* Generate code to store value from rtx VALUE
|
||
into a bit-field within structure STR_RTX
|
||
containing BITSIZE bits starting at bit BITNUM.
|
||
FIELDMODE is the machine-mode of the FIELD_DECL node for this field.
|
||
ALIGN is the alignment that STR_RTX is known to have, measured in bytes. */
|
||
|
||
rtx
|
||
store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align)
|
||
rtx str_rtx;
|
||
register int bitsize;
|
||
int bitnum;
|
||
enum machine_mode fieldmode;
|
||
rtx value;
|
||
int align;
|
||
{
|
||
int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
|
||
register int offset = bitnum / unit;
|
||
register int bitpos = bitnum % unit;
|
||
register rtx op0 = str_rtx;
|
||
rtx value1;
|
||
|
||
while (GET_CODE (op0) == SUBREG)
|
||
{
|
||
offset += SUBREG_WORD (op0);
|
||
op0 = SUBREG_REG (op0);
|
||
}
|
||
|
||
value = protect_from_queue (value, 0);
|
||
|
||
if (flag_force_mem)
|
||
value = force_not_mem (value);
|
||
|
||
if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD)
|
||
{
|
||
/* Storing in a full-word or multi-word field in a register
|
||
can be done with just SUBREG. */
|
||
if (GET_MODE (op0) != fieldmode)
|
||
op0 = gen_rtx (SUBREG, fieldmode, op0, offset);
|
||
emit_move_insn (op0, value);
|
||
return value;
|
||
}
|
||
|
||
#ifdef BYTES_BIG_ENDIAN
|
||
/* If OP0 is a register, BITPOS must count within a word.
|
||
But as we have it, it counts within whatever size OP0 now has.
|
||
On a bigendian machine, these are not the same, so convert. */
|
||
if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
|
||
bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
|
||
#endif
|
||
|
||
/* Storing an lsb-aligned field in a register
|
||
can be done with a movestrict instruction. */
|
||
|
||
if (GET_CODE (op0) != MEM
|
||
#ifdef BYTES_BIG_ENDIAN
|
||
&& bitpos + bitsize == unit
|
||
#else
|
||
&& bitpos == 0
|
||
#endif
|
||
&& (GET_MODE (op0) == fieldmode
|
||
|| (movstrict_optab->handlers[(int) fieldmode].insn_code
|
||
!= CODE_FOR_nothing)))
|
||
{
|
||
/* Get appropriate low part of the value being stored. */
|
||
if (GET_CODE (value) == CONST_INT || GET_CODE (value) == REG)
|
||
value = gen_lowpart (fieldmode, value);
|
||
else if (!(GET_CODE (value) == SYMBOL_REF
|
||
|| GET_CODE (value) == LABEL_REF
|
||
|| GET_CODE (value) == CONST))
|
||
value = convert_to_mode (fieldmode, value, 0);
|
||
|
||
if (GET_MODE (op0) == fieldmode)
|
||
emit_move_insn (op0, value);
|
||
else
|
||
emit_insn (GEN_FCN (movstrict_optab->handlers[(int) fieldmode].insn_code)
|
||
(gen_rtx (SUBREG, fieldmode, op0, offset), value));
|
||
|
||
return value;
|
||
}
|
||
|
||
/* From here on we can assume that the field to be stored in is an integer,
|
||
since it is shorter than a word. */
|
||
|
||
/* OFFSET is the number of words or bytes (UNIT says which)
|
||
from STR_RTX to the first word or byte containing part of the field. */
|
||
|
||
if (GET_CODE (op0) == REG)
|
||
{
|
||
if (offset != 0
|
||
|| GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (SImode))
|
||
op0 = gen_rtx (SUBREG, SImode, op0, offset);
|
||
offset = 0;
|
||
}
|
||
else
|
||
{
|
||
op0 = protect_from_queue (op0, 1);
|
||
}
|
||
|
||
/* Now OFFSET is nonzero only if OP0 is memory
|
||
and is therefore always measured in bytes. */
|
||
|
||
#ifdef HAVE_insv
|
||
if (HAVE_insv
|
||
&& !(bitsize == 1 && GET_CODE (value) == CONST_INT))
|
||
{
|
||
int xbitpos = bitpos;
|
||
rtx xop0 = op0;
|
||
rtx last = get_last_insn ();
|
||
rtx pat;
|
||
|
||
/* If this machine's insv can only insert into a register,
|
||
copy OP0 into a register and save it back later. */
|
||
if (GET_CODE (op0) == MEM
|
||
&& ! (*insn_operand_predicate[(int) CODE_FOR_insv][0]) (op0, VOIDmode))
|
||
{
|
||
rtx tempreg;
|
||
enum machine_mode trymode, bestmode = VOIDmode, insn_mode;
|
||
int maxsize = GET_MODE_SIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]);
|
||
|
||
/* Find biggest machine mode we can safely use
|
||
to fetch from this structure.
|
||
But don't use a bigger mode than the insn wants. */
|
||
for (trymode = QImode;
|
||
trymode && GET_MODE_SIZE (trymode) <= maxsize;
|
||
trymode = GET_MODE_WIDER_MODE (trymode))
|
||
if (GET_MODE_SIZE (trymode) <= align)
|
||
bestmode = trymode;
|
||
if (! bestmode)
|
||
abort ();
|
||
/* Adjust address to point to the containing unit of that mode. */
|
||
unit = GET_MODE_BITSIZE (bestmode);
|
||
/* Compute offset as multiple of this unit, counting in bytes. */
|
||
offset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
|
||
bitpos = bitnum % unit;
|
||
op0 = change_address (op0, bestmode,
|
||
plus_constant (XEXP (op0, 0), offset));
|
||
|
||
/* Fetch that unit, store the bitfield in it, then store the unit. */
|
||
tempreg = copy_to_reg (op0);
|
||
/* To actually store in TEMPREG,
|
||
look at it in the mode this insn calls for.
|
||
(Probably SImode.) */
|
||
insn_mode = insn_operand_mode[(int) CODE_FOR_insv][0];
|
||
#ifdef BITS_BIG_ENDIAN
|
||
if (GET_MODE_BITSIZE (insn_mode) > unit)
|
||
bitpos += GET_MODE_BITSIZE (insn_mode) - unit;
|
||
#endif
|
||
store_bit_field (gen_rtx (SUBREG, insn_mode, tempreg, 0),
|
||
bitsize, bitpos, fieldmode, value, align);
|
||
emit_move_insn (op0, tempreg);
|
||
return value;
|
||
}
|
||
|
||
/* Add OFFSET into OP0's address. */
|
||
if (GET_CODE (xop0) == MEM)
|
||
xop0 = change_address (xop0, QImode,
|
||
plus_constant (XEXP (xop0, 0), offset));
|
||
|
||
/* If xop0 is a register, we need it in SImode
|
||
to make it acceptable to the format of insv. */
|
||
if (GET_CODE (xop0) == SUBREG)
|
||
PUT_MODE (xop0, SImode);
|
||
if (GET_CODE (xop0) == REG && GET_MODE (xop0) != SImode)
|
||
xop0 = gen_rtx (SUBREG, SImode, xop0, 0);
|
||
|
||
/* Convert VALUE to SImode (which insv insn wants) in VALUE1. */
|
||
value1 = value;
|
||
if (GET_MODE (value) != SImode)
|
||
{
|
||
if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize)
|
||
{
|
||
/* Optimization: Don't bother really extending VALUE
|
||
if it has all the bits we will actually use. */
|
||
|
||
/* Avoid making subreg of a subreg, or of a mem. */
|
||
if (GET_CODE (value1) != REG)
|
||
value1 = copy_to_reg (value1);
|
||
value1 = gen_rtx (SUBREG, SImode, value1, 0);
|
||
}
|
||
else if (!CONSTANT_P (value))
|
||
/* Parse phase is supposed to make VALUE's data type
|
||
match that of the component reference, which is a type
|
||
at least as wide as the field; so VALUE should have
|
||
a mode that corresponds to that type. */
|
||
abort ();
|
||
}
|
||
|
||
/* If this machine's insv insists on a register,
|
||
get VALUE1 into a register. */
|
||
if (! (*insn_operand_predicate[(int) CODE_FOR_insv][3]) (value1, SImode))
|
||
value1 = force_reg (SImode, value1);
|
||
|
||
/* On big-endian machines, we count bits from the most significant.
|
||
If the bit field insn does not, we must invert. */
|
||
|
||
#if defined (BITS_BIG_ENDIAN) != defined (BYTES_BIG_ENDIAN)
|
||
xbitpos = unit - 1 - xbitpos;
|
||
#endif
|
||
|
||
pat = gen_insv (xop0,
|
||
gen_rtx (CONST_INT, VOIDmode, bitsize),
|
||
gen_rtx (CONST_INT, VOIDmode, xbitpos),
|
||
value1);
|
||
if (pat)
|
||
emit_insn (pat);
|
||
else
|
||
{
|
||
delete_insns_since (last);
|
||
store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
/* Insv is not available; store using shifts and boolean ops. */
|
||
store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);
|
||
return value;
|
||
}
|
||
|
||
/* Use shifts and boolean operations to store VALUE
|
||
into a bit field of width BITSIZE
|
||
in a memory location specified by OP0 except offset by OFFSET bytes.
|
||
(OFFSET must be 0 if OP0 is a register.)
|
||
The field starts at position BITPOS within the byte.
|
||
(If OP0 is a register, it may be SImode or a narrower mode,
|
||
but BITPOS still counts within a full word,
|
||
which is significant on bigendian machines.)
|
||
STRUCT_ALIGN is the alignment the structure is known to have (in bytes).
|
||
|
||
Note that protect_from_queue has already been done on OP0 and VALUE. */
|
||
|
||
static void
|
||
store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
|
||
register rtx op0;
|
||
register int offset, bitsize, bitpos;
|
||
register rtx value;
|
||
int struct_align;
|
||
{
|
||
register enum machine_mode mode;
|
||
int total_bits = BITS_PER_WORD;
|
||
rtx subtarget;
|
||
int all_zero = 0;
|
||
int all_one = 0;
|
||
|
||
/* Add OFFSET to OP0's address (if it is in memory)
|
||
and if a single byte contains the whole bit field
|
||
change OP0 to a byte. */
|
||
|
||
/* There is a case not handled here:
|
||
a structure with a known alignment of just a halfword
|
||
and a field split across two aligned halfwords within the structure.
|
||
Or likewise a structure with a known alignment of just a byte
|
||
and a field split across two bytes.
|
||
Such cases are not supposed to be able to occur. */
|
||
|
||
if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
|
||
{
|
||
if (offset != 0)
|
||
abort ();
|
||
/* Special treatment for a bit field split across two registers. */
|
||
if (bitsize + bitpos > BITS_PER_WORD)
|
||
{
|
||
store_split_bit_field (op0, bitsize, bitpos, value, BITS_PER_WORD);
|
||
return;
|
||
}
|
||
}
|
||
else if (bitsize + bitpos <= BITS_PER_UNIT
|
||
&& (! SLOW_BYTE_ACCESS
|
||
|| (struct_align == 1
|
||
&& BIGGEST_ALIGNMENT > 1)))
|
||
{
|
||
/* It fits in one byte, and either bytes are fast
|
||
or the alignment won't let us use anything bigger. */
|
||
total_bits = BITS_PER_UNIT;
|
||
op0 = change_address (op0, QImode,
|
||
plus_constant (XEXP (op0, 0), offset));
|
||
}
|
||
else if ((bitsize + bitpos + (offset % GET_MODE_SIZE (HImode)) * BITS_PER_UNIT
|
||
<= GET_MODE_BITSIZE (HImode))
|
||
/* If halfwords are fast, use them whenever valid. */
|
||
&& (! SLOW_BYTE_ACCESS
|
||
/* Use halfwords if larger is invalid due to alignment. */
|
||
|| (struct_align == GET_MODE_SIZE (HImode)
|
||
&& BIGGEST_ALIGNMENT > GET_MODE_SIZE (HImode))))
|
||
{
|
||
/* It fits in an aligned halfword within the structure,
|
||
and either halfwords are fast
|
||
or the alignment won't let us use anything bigger. */
|
||
total_bits = GET_MODE_BITSIZE (HImode);
|
||
|
||
/* Get ref to halfword containing the field. */
|
||
bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
|
||
offset -= (offset % (total_bits / BITS_PER_UNIT));
|
||
op0 = change_address (op0, HImode,
|
||
plus_constant (XEXP (op0, 0), offset));
|
||
}
|
||
else
|
||
{
|
||
/* Get ref to an aligned word containing the field. */
|
||
/* Adjust BITPOS to be position within a word,
|
||
and OFFSET to be the offset of that word.
|
||
Then alter OP0 to refer to that word. */
|
||
bitpos += (offset % (BITS_PER_WORD / BITS_PER_UNIT)) * BITS_PER_UNIT;
|
||
offset -= (offset % (BITS_PER_WORD / BITS_PER_UNIT));
|
||
op0 = change_address (op0, SImode,
|
||
plus_constant (XEXP (op0, 0), offset));
|
||
|
||
/* Special treatment for a bit field split across two aligned words. */
|
||
if (bitsize + bitpos > BITS_PER_WORD)
|
||
{
|
||
store_split_bit_field (op0, bitsize, bitpos, value, struct_align);
|
||
return;
|
||
}
|
||
}
|
||
|
||
mode = GET_MODE (op0);
|
||
|
||
/* Now MODE is either QImode, HImode or SImode for a MEM as OP0,
|
||
or is SImode for a REG as OP0. TOTAL_BITS corresponds.
|
||
The bit field is contained entirely within OP0.
|
||
BITPOS is the starting bit number within OP0.
|
||
(OP0's mode may actually be narrower than MODE.) */
|
||
|
||
#ifdef BYTES_BIG_ENDIAN
|
||
/* BITPOS is the distance between our msb
|
||
and that of the containing datum.
|
||
Convert it to the distance from the lsb. */
|
||
|
||
bitpos = total_bits - bitsize - bitpos;
|
||
#endif
|
||
/* Now BITPOS is always the distance between our lsb
|
||
and that of OP0. */
|
||
|
||
/* Shift VALUE left by BITPOS bits. If VALUE is not constant,
|
||
we must first convert its mode to MODE. */
|
||
|
||
if (GET_CODE (value) == CONST_INT)
|
||
{
|
||
register int v = INTVAL (value);
|
||
|
||
if (bitsize < HOST_BITS_PER_INT)
|
||
v &= (1 << bitsize) - 1;
|
||
|
||
if (v == 0)
|
||
all_zero = 1;
|
||
else if (bitsize < HOST_BITS_PER_INT && v == (1 << bitsize) - 1)
|
||
all_one = 1;
|
||
|
||
value = gen_rtx (CONST_INT, VOIDmode, v << bitpos);
|
||
}
|
||
else
|
||
{
|
||
int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize);
|
||
|
||
if (GET_MODE (value) != mode)
|
||
{
|
||
if ((GET_CODE (value) == REG || GET_CODE (value) == SUBREG)
|
||
&& GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (value)))
|
||
value = gen_lowpart (mode, value);
|
||
else
|
||
value = convert_to_mode (mode, value, 1);
|
||
}
|
||
|
||
if (must_and && bitsize < HOST_BITS_PER_INT)
|
||
value = expand_bit_and (mode, value,
|
||
gen_rtx (CONST_INT, VOIDmode,
|
||
(1 << bitsize) - 1),
|
||
0);
|
||
if (bitpos > 0)
|
||
value = expand_shift (LSHIFT_EXPR, mode, value,
|
||
build_int_2 (bitpos, 0), 0, 1);
|
||
}
|
||
|
||
/* Now clear the chosen bits in OP0,
|
||
except that if VALUE is -1 we need not bother. */
|
||
|
||
subtarget = op0;
|
||
|
||
if (! all_one)
|
||
subtarget = expand_bit_and (mode, op0,
|
||
gen_rtx (CONST_INT, VOIDmode,
|
||
(~ (((unsigned) ~0
|
||
>> (HOST_BITS_PER_INT - bitsize))
|
||
<< bitpos))
|
||
& ((GET_MODE_BITSIZE (mode)
|
||
== HOST_BITS_PER_INT)
|
||
? -1
|
||
: ((1 << GET_MODE_BITSIZE (mode)) - 1))),
|
||
subtarget);
|
||
|
||
/* Now logical-or VALUE into OP0, unless it is zero. */
|
||
|
||
if (! all_zero)
|
||
subtarget = expand_binop (mode, ior_optab, subtarget, value,
|
||
op0, 1, OPTAB_LIB_WIDEN);
|
||
if (op0 != subtarget)
|
||
emit_move_insn (op0, subtarget);
|
||
}
|
||
|
||
/* Store a bit field that is split across two words.
|
||
|
||
OP0 is the REG, SUBREG or MEM rtx for the first of the two words.
|
||
BITSIZE is the field width; BITPOS the position of its first bit
|
||
(within the word).
|
||
VALUE is the value to store. */
|
||
|
||
static void
|
||
store_split_bit_field (op0, bitsize, bitpos, value, align)
|
||
rtx op0;
|
||
int bitsize, bitpos;
|
||
rtx value;
|
||
int align;
|
||
{
|
||
/* BITSIZE_1 is size of the part in the first word. */
|
||
int bitsize_1 = BITS_PER_WORD - bitpos;
|
||
/* BITSIZE_2 is size of the rest (in the following word). */
|
||
int bitsize_2 = bitsize - bitsize_1;
|
||
rtx part1, part2;
|
||
|
||
/* Alignment of VALUE, after conversion. */
|
||
int valalign = GET_MODE_SIZE (SImode);
|
||
|
||
if (GET_MODE (value) != VOIDmode)
|
||
value = convert_to_mode (SImode, value, 1);
|
||
if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT)
|
||
value = copy_to_reg (value);
|
||
|
||
/* Split the value into two parts:
|
||
PART1 gets that which goes in the first word; PART2 the other. */
|
||
#ifdef BYTES_BIG_ENDIAN
|
||
/* PART1 gets the more significant part. */
|
||
if (GET_CODE (value) == CONST_INT)
|
||
{
|
||
part1 = gen_rtx (CONST_INT, VOIDmode,
|
||
(unsigned) (INTVAL (value)) >> bitsize_2);
|
||
part2 = gen_rtx (CONST_INT, VOIDmode,
|
||
(unsigned) (INTVAL (value)) & ((1 << bitsize_2) - 1));
|
||
}
|
||
else
|
||
{
|
||
part1 = extract_fixed_bit_field (SImode, value, 0, bitsize_1,
|
||
BITS_PER_WORD - bitsize, 0, 1, valalign);
|
||
part2 = extract_fixed_bit_field (SImode, value, 0, bitsize_2,
|
||
BITS_PER_WORD - bitsize_2, 0, 1, valalign);
|
||
}
|
||
#else
|
||
/* PART1 gets the less significant part. */
|
||
if (GET_CODE (value) == CONST_INT)
|
||
{
|
||
part1 = gen_rtx (CONST_INT, VOIDmode,
|
||
(unsigned) (INTVAL (value)) & ((1 << bitsize_1) - 1));
|
||
part2 = gen_rtx (CONST_INT, VOIDmode,
|
||
(unsigned) (INTVAL (value)) >> bitsize_1);
|
||
}
|
||
else
|
||
{
|
||
part1 = extract_fixed_bit_field (SImode, value, 0, bitsize_1, 0,
|
||
0, 1, valalign);
|
||
part2 = extract_fixed_bit_field (SImode, value, 0, bitsize_2,
|
||
bitsize_1, 0, 1, valalign);
|
||
}
|
||
#endif
|
||
|
||
/* Store PART1 into the first word. */
|
||
store_fixed_bit_field (op0, 0, bitsize_1, bitpos, part1, align);
|
||
|
||
/* Offset op0 to get to the following word. */
|
||
if (GET_CODE (op0) == MEM)
|
||
op0 = change_address (op0, SImode,
|
||
plus_constant (XEXP (op0, 0), UNITS_PER_WORD));
|
||
else if (GET_CODE (op0) == REG)
|
||
op0 = gen_rtx (SUBREG, SImode, op0, 1);
|
||
else if (GET_CODE (op0) == SUBREG)
|
||
op0 = gen_rtx (SUBREG, SImode, SUBREG_REG (op0), SUBREG_WORD (op0) + 1);
|
||
|
||
/* Store PART2 into the second word. */
|
||
store_fixed_bit_field (op0, 0, bitsize_2, 0, part2, align);
|
||
}
|
||
|
||
/* Generate code to extract a byte-field from STR_RTX
|
||
containing BITSIZE bits, starting at BITNUM,
|
||
and put it in TARGET if possible (if TARGET is nonzero).
|
||
Regardless of TARGET, we return the rtx for where the value is placed.
|
||
It may be a QUEUED.
|
||
|
||
STR_RTX is the structure containing the byte (a REG or MEM).
|
||
UNSIGNEDP is nonzero if this is an unsigned bit field.
|
||
MODE is the natural mode of the field value once extracted.
|
||
TMODE is the mode the caller would like the value to have;
|
||
but the value may be returned with type MODE instead.
|
||
|
||
ALIGN is the alignment that STR_RTX is known to have, measured in bytes.
|
||
|
||
If a TARGET is specified and we can store in it at no extra cost,
|
||
we do so, and return TARGET.
|
||
Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred
|
||
if they are equally easy. */
|
||
|
||
rtx
|
||
extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
|
||
target, mode, tmode, align)
|
||
rtx str_rtx;
|
||
register int bitsize;
|
||
int bitnum;
|
||
int unsignedp;
|
||
rtx target;
|
||
enum machine_mode mode, tmode;
|
||
int align;
|
||
{
|
||
int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
|
||
register int offset = bitnum / unit;
|
||
register int bitpos = bitnum % unit;
|
||
register rtx op0 = str_rtx;
|
||
rtx spec_target = target;
|
||
rtx bitsize_rtx, bitpos_rtx;
|
||
rtx spec_target_subreg = 0;
|
||
|
||
if (tmode == VOIDmode)
|
||
tmode = mode;
|
||
|
||
while (GET_CODE (op0) == SUBREG)
|
||
{
|
||
offset += SUBREG_WORD (op0);
|
||
op0 = SUBREG_REG (op0);
|
||
}
|
||
|
||
#ifdef BYTES_BIG_ENDIAN
|
||
/* If OP0 is a register, BITPOS must count within a word.
|
||
But as we have it, it counts within whatever size OP0 now has.
|
||
On a bigendian machine, these are not the same, so convert. */
|
||
if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
|
||
bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
|
||
#endif
|
||
|
||
/* Extracting a full-word or multi-word value
|
||
from a structure in a register.
|
||
This can be done with just SUBREG.
|
||
So too extracting a subword value in
|
||
the least significant part of the register. */
|
||
|
||
if (GET_CODE (op0) == REG
|
||
&& (bitsize >= BITS_PER_WORD
|
||
|| ((bitsize == GET_MODE_BITSIZE (mode)
|
||
|| bitsize == GET_MODE_BITSIZE (QImode)
|
||
|| bitsize == GET_MODE_BITSIZE (HImode))
|
||
#ifdef BYTES_BIG_ENDIAN
|
||
&& bitpos + bitsize == BITS_PER_WORD
|
||
#else
|
||
&& bitpos == 0
|
||
#endif
|
||
)))
|
||
{
|
||
enum machine_mode mode1 = mode;
|
||
|
||
if (bitsize == GET_MODE_BITSIZE (QImode))
|
||
mode1 = QImode;
|
||
if (bitsize == GET_MODE_BITSIZE (HImode))
|
||
mode1 = HImode;
|
||
|
||
if (mode1 != GET_MODE (op0))
|
||
op0 = gen_rtx (SUBREG, mode1, op0, offset);
|
||
|
||
if (mode1 != mode)
|
||
return convert_to_mode (tmode, op0, unsignedp);
|
||
return op0;
|
||
}
|
||
|
||
/* From here on we know the desired field is smaller than a word
|
||
so we can assume it is an integer. So we can safely extract it as one
|
||
size of integer, if necessary, and then truncate or extend
|
||
to the size that is wanted. */
|
||
|
||
/* OFFSET is the number of words or bytes (UNIT says which)
|
||
from STR_RTX to the first word or byte containing part of the field. */
|
||
|
||
if (GET_CODE (op0) == REG)
|
||
{
|
||
if (offset != 0
|
||
|| GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (SImode))
|
||
op0 = gen_rtx (SUBREG, SImode, op0, offset);
|
||
offset = 0;
|
||
}
|
||
else
|
||
{
|
||
op0 = protect_from_queue (str_rtx, 1);
|
||
}
|
||
|
||
/* Now OFFSET is nonzero only for memory operands. */
|
||
|
||
if (unsignedp)
|
||
{
|
||
#ifdef HAVE_extzv
|
||
if (HAVE_extzv)
|
||
{
|
||
int xbitpos = bitpos, xoffset = offset;
|
||
rtx last = get_last_insn();
|
||
rtx xop0 = op0;
|
||
rtx xtarget = target;
|
||
rtx xspec_target = spec_target;
|
||
rtx xspec_target_subreg = spec_target_subreg;
|
||
rtx pat;
|
||
|
||
/* Get ref to first byte containing part of the field. */
|
||
if (GET_CODE (xop0) == MEM)
|
||
{
|
||
if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1])
|
||
(xop0, GET_MODE (xop0))))
|
||
{
|
||
enum machine_mode bestmode = VOIDmode, trymode;
|
||
int maxsize = GET_MODE_SIZE (insn_operand_mode[(int) CODE_FOR_extzv][1]);
|
||
|
||
/* Find biggest machine mode we can safely use
|
||
to fetch from this structure.
|
||
But don't use a bigger mode than the insn wants. */
|
||
for (trymode = QImode;
|
||
trymode && GET_MODE_SIZE (trymode) <= maxsize;
|
||
trymode = GET_MODE_WIDER_MODE (trymode))
|
||
if (GET_MODE_SIZE (trymode) <= align)
|
||
bestmode = trymode;
|
||
if (! bestmode)
|
||
abort ();
|
||
unit = GET_MODE_BITSIZE (bestmode);
|
||
|
||
/* Compute offset as multiple of this unit,
|
||
counting in bytes. */
|
||
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
|
||
xbitpos = bitnum % unit;
|
||
xop0 = change_address (xop0, bestmode,
|
||
plus_constant (XEXP (xop0, 0),
|
||
xoffset));
|
||
/* Fetch it to a register in that size. */
|
||
xop0 = force_reg (bestmode, xop0);
|
||
|
||
/* Now ref the register in the mode extzv wants. */
|
||
xop0 = gen_rtx (SUBREG, insn_operand_mode[(int) CODE_FOR_extzv][1],
|
||
xop0, 0);
|
||
#ifdef BITS_BIG_ENDIAN
|
||
if (GET_MODE_BITSIZE (GET_MODE (xop0)) > unit)
|
||
xbitpos += GET_MODE_BITSIZE (GET_MODE (xop0)) - unit;
|
||
#endif
|
||
}
|
||
else
|
||
xop0 = change_address (xop0, QImode,
|
||
plus_constant (XEXP (xop0, 0), xoffset));
|
||
}
|
||
|
||
/* If op0 is a register, we need it in SImode
|
||
to make it acceptable to the format of extzv. */
|
||
if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != SImode)
|
||
abort ();
|
||
if (GET_CODE (xop0) == REG && GET_MODE (xop0) != SImode)
|
||
xop0 = gen_rtx (SUBREG, SImode, xop0, 0);
|
||
|
||
if (xtarget == 0
|
||
|| (flag_force_mem && GET_CODE (xtarget) == MEM))
|
||
xtarget = xspec_target = gen_reg_rtx (tmode);
|
||
|
||
if (GET_MODE (xtarget) != SImode)
|
||
{
|
||
if (GET_CODE (xtarget) == REG)
|
||
xspec_target_subreg = xtarget = gen_lowpart (SImode, xtarget);
|
||
else
|
||
xtarget = gen_reg_rtx (SImode);
|
||
}
|
||
|
||
/* If this machine's extzv insists on a register target,
|
||
make sure we have one. */
|
||
if (! (*insn_operand_predicate[(int) CODE_FOR_extzv][0]) (xtarget, SImode))
|
||
xtarget = gen_reg_rtx (SImode);
|
||
|
||
/* On big-endian machines, we count bits from the most significant.
|
||
If the bit field insn does not, we must invert. */
|
||
#if defined (BITS_BIG_ENDIAN) != defined (BYTES_BIG_ENDIAN)
|
||
xbitpos = unit - 1 - xbitpos;
|
||
#endif
|
||
|
||
bitsize_rtx = gen_rtx (CONST_INT, VOIDmode, bitsize);
|
||
bitpos_rtx = gen_rtx (CONST_INT, VOIDmode, xbitpos);
|
||
|
||
pat = gen_extzv (protect_from_queue (xtarget, 1),
|
||
xop0, bitsize_rtx, bitpos_rtx);
|
||
if (pat)
|
||
{
|
||
emit_insn (pat);
|
||
target = xtarget;
|
||
spec_target = xspec_target;
|
||
spec_target_subreg = xspec_target_subreg;
|
||
}
|
||
else
|
||
{
|
||
delete_insns_since (last);
|
||
target = extract_fixed_bit_field (tmode, op0, offset, bitsize,
|
||
bitpos, target, 1, align);
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
|
||
target, 1, align);
|
||
}
|
||
else
|
||
{
|
||
#ifdef HAVE_extv
|
||
if (HAVE_extv)
|
||
{
|
||
int xbitpos = bitpos, xoffset = offset;
|
||
rtx last = get_last_insn();
|
||
rtx xop0 = op0, xtarget = target;
|
||
rtx xspec_target = spec_target;
|
||
rtx xspec_target_subreg = spec_target_subreg;
|
||
rtx pat;
|
||
|
||
/* Get ref to first byte containing part of the field. */
|
||
if (GET_CODE (xop0) == MEM)
|
||
{
|
||
if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][1])
|
||
(xop0, GET_MODE (xop0))))
|
||
{
|
||
enum machine_mode bestmode = VOIDmode, trymode;
|
||
int maxsize = GET_MODE_SIZE (insn_operand_mode[(int) CODE_FOR_extzv][1]);
|
||
|
||
/* Find biggest machine mode we can safely use
|
||
to fetch from this structure.
|
||
But don't use a bigger mode than the insn wants. */
|
||
for (trymode = QImode;
|
||
trymode && GET_MODE_SIZE (trymode) <= maxsize;
|
||
trymode = GET_MODE_WIDER_MODE (trymode))
|
||
if (GET_MODE_SIZE (trymode) <= align)
|
||
bestmode = trymode;
|
||
if (! bestmode)
|
||
abort ();
|
||
unit = GET_MODE_BITSIZE (bestmode);
|
||
|
||
/* Compute offset as multiple of this unit,
|
||
counting in bytes. */
|
||
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
|
||
xbitpos = bitnum % unit;
|
||
xop0 = change_address (xop0, bestmode,
|
||
plus_constant (XEXP (xop0, 0),
|
||
xoffset));
|
||
/* Fetch it to a register in that size. */
|
||
xop0 = force_reg (bestmode, xop0);
|
||
|
||
/* Now ref the register in the mode extv wants. */
|
||
xop0 = gen_rtx (SUBREG, insn_operand_mode[(int) CODE_FOR_extv][1],
|
||
xop0, 0);
|
||
#ifdef BITS_BIG_ENDIAN
|
||
if (GET_MODE_BITSIZE (GET_MODE (xop0)) > unit)
|
||
xbitpos += GET_MODE_BITSIZE (GET_MODE (xop0)) - unit;
|
||
#endif
|
||
}
|
||
else
|
||
xop0 = change_address (xop0, QImode,
|
||
plus_constant (XEXP (xop0, 0), xoffset));
|
||
}
|
||
|
||
/* If op0 is a register, we need it in SImode
|
||
to make it acceptable to the format of extv. */
|
||
if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != SImode)
|
||
abort ();
|
||
if (GET_CODE (xop0) == REG && GET_MODE (xop0) != SImode)
|
||
xop0 = gen_rtx (SUBREG, SImode, xop0, 0);
|
||
|
||
if (xtarget == 0
|
||
|| (flag_force_mem && GET_CODE (xtarget) == MEM))
|
||
xtarget = xspec_target = gen_reg_rtx (tmode);
|
||
|
||
if (GET_MODE (xtarget) != SImode)
|
||
{
|
||
if (GET_CODE (xtarget) == REG)
|
||
xspec_target_subreg = xtarget = gen_lowpart (SImode, xtarget);
|
||
else
|
||
xtarget = gen_reg_rtx (SImode);
|
||
}
|
||
|
||
/* If this machine's extv insists on a register target,
|
||
make sure we have one. */
|
||
if (! (*insn_operand_predicate[(int) CODE_FOR_extv][0]) (xtarget, SImode))
|
||
xtarget = gen_reg_rtx (SImode);
|
||
|
||
/* On big-endian machines, we count bits from the most significant.
|
||
If the bit field insn does not, we must invert. */
|
||
#if defined (BITS_BIG_ENDIAN) != defined (BYTES_BIG_ENDIAN)
|
||
xbitpos = unit - 1 - xbitpos;
|
||
#endif
|
||
|
||
bitsize_rtx = gen_rtx (CONST_INT, VOIDmode, bitsize);
|
||
bitpos_rtx = gen_rtx (CONST_INT, VOIDmode, xbitpos);
|
||
|
||
pat = gen_extv (protect_from_queue (xtarget, 1),
|
||
xop0, bitsize_rtx, bitpos_rtx);
|
||
if (pat)
|
||
{
|
||
emit_insn (pat);
|
||
target = xtarget;
|
||
spec_target = xspec_target;
|
||
spec_target_subreg = xspec_target_subreg;
|
||
}
|
||
else
|
||
{
|
||
delete_insns_since (last);
|
||
target = extract_fixed_bit_field (tmode, op0, offset, bitsize,
|
||
bitpos, target, 0, align);
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
|
||
target, 0, align);
|
||
}
|
||
if (target == spec_target)
|
||
return target;
|
||
if (target == spec_target_subreg)
|
||
return spec_target;
|
||
if (GET_MODE (target) != tmode && GET_MODE (target) != mode)
|
||
return convert_to_mode (tmode, target, unsignedp);
|
||
return target;
|
||
}
|
||
|
||
/* Extract a bit field using shifts and boolean operations
|
||
Returns an rtx to represent the value.
|
||
OP0 addresses a register (word) or memory (byte).
|
||
BITPOS says which bit within the word or byte the bit field starts in.
|
||
OFFSET says how many bytes farther the bit field starts;
|
||
it is 0 if OP0 is a register.
|
||
BITSIZE says how many bits long the bit field is.
|
||
(If OP0 is a register, it may be narrower than SImode,
|
||
but BITPOS still counts within a full word,
|
||
which is significant on bigendian machines.)
|
||
|
||
UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value).
|
||
If TARGET is nonzero, attempts to store the value there
|
||
and return TARGET, but this is not guaranteed.
|
||
If TARGET is not used, create a pseudo-reg of mode TMODE for the value.
|
||
|
||
ALIGN is the alignment that STR_RTX is known to have, measured in bytes. */
|
||
|
||
static rtx
|
||
extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
|
||
target, unsignedp, align)
|
||
enum machine_mode tmode;
|
||
register rtx op0, target;
|
||
register int offset, bitsize, bitpos;
|
||
int unsignedp;
|
||
int align;
|
||
{
|
||
int total_bits = BITS_PER_WORD;
|
||
enum machine_mode mode;
|
||
|
||
if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG)
|
||
{
|
||
/* Special treatment for a bit field split across two registers. */
|
||
if (bitsize + bitpos > BITS_PER_WORD)
|
||
return extract_split_bit_field (op0, bitsize, bitpos,
|
||
unsignedp, align);
|
||
}
|
||
else if (bitsize + bitpos <= BITS_PER_UNIT
|
||
&& (! SLOW_BYTE_ACCESS
|
||
|| (align == 1
|
||
&& BIGGEST_ALIGNMENT > 1)))
|
||
{
|
||
/* It fits in one byte, and either bytes are fast
|
||
or the alignment won't let us use anything bigger. */
|
||
total_bits = BITS_PER_UNIT;
|
||
op0 = change_address (op0, QImode,
|
||
plus_constant (XEXP (op0, 0), offset));
|
||
}
|
||
else if ((bitsize + bitpos + (offset % GET_MODE_SIZE (HImode)) * BITS_PER_UNIT
|
||
<= GET_MODE_BITSIZE (HImode))
|
||
/* If halfwords are fast, use them whenever valid. */
|
||
&& (! SLOW_BYTE_ACCESS
|
||
/* Use halfwords if larger is invalid due to alignment. */
|
||
|| (align == GET_MODE_SIZE (HImode)
|
||
&& BIGGEST_ALIGNMENT > GET_MODE_SIZE (HImode))))
|
||
{
|
||
/* It fits in an aligned halfword, and either halfwords are fast
|
||
or the alignment won't let us use anything bigger. */
|
||
total_bits = GET_MODE_BITSIZE (HImode);
|
||
|
||
/* Get ref to halfword containing the field. */
|
||
bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
|
||
offset -= (offset % (total_bits / BITS_PER_UNIT));
|
||
op0 = change_address (op0, HImode,
|
||
plus_constant (XEXP (op0, 0), offset));
|
||
}
|
||
else
|
||
{
|
||
/* Get ref to word containing the field. */
|
||
/* Adjust BITPOS to be position within a word,
|
||
and OFFSET to be the offset of that word. */
|
||
bitpos += (offset % (BITS_PER_WORD / BITS_PER_UNIT)) * BITS_PER_UNIT;
|
||
offset -= (offset % (BITS_PER_WORD / BITS_PER_UNIT));
|
||
op0 = change_address (op0, SImode,
|
||
plus_constant (XEXP (op0, 0), offset));
|
||
|
||
/* Special treatment for a bit field split across two words. */
|
||
if (bitsize + bitpos > BITS_PER_WORD)
|
||
return extract_split_bit_field (op0, bitsize, bitpos,
|
||
unsignedp, align);
|
||
}
|
||
|
||
mode = GET_MODE (op0);
|
||
|
||
#ifdef BYTES_BIG_ENDIAN
|
||
/* BITPOS is the distance between our msb and that of OP0.
|
||
Convert it to the distance from the lsb. */
|
||
|
||
bitpos = total_bits - bitsize - bitpos;
|
||
#endif
|
||
/* Now BITPOS is always the distance between the field's lsb and that of OP0.
|
||
We have reduced the big-endian case to the little-endian case. */
|
||
|
||
if (unsignedp)
|
||
{
|
||
if (bitpos)
|
||
{
|
||
/* If the field does not already start at the lsb,
|
||
shift it so it does. */
|
||
tree amount = build_int_2 (bitpos, 0);
|
||
/* Maybe propagate the target for the shift. */
|
||
/* But not if we will return it--could confuse integrate.c. */
|
||
rtx subtarget = (target != 0 && GET_CODE (target) == REG
|
||
&& !REG_FUNCTION_VALUE_P (target)
|
||
? target : 0);
|
||
if (tmode != mode) subtarget = 0;
|
||
op0 = expand_shift (RSHIFT_EXPR, mode, op0, amount, subtarget, 1);
|
||
}
|
||
/* Convert the value to the desired mode. */
|
||
if (mode != tmode)
|
||
op0 = convert_to_mode (tmode, op0, 1);
|
||
|
||
/* Unless the msb of the field used to be the msb when we shifted,
|
||
mask out the upper bits. */
|
||
|
||
if ((GET_MODE_BITSIZE (mode) != bitpos + bitsize
|
||
#if 0
|
||
#ifdef SLOW_ZERO_EXTEND
|
||
/* Always generate an `and' if
|
||
we just zero-extended op0 and SLOW_ZERO_EXTEND, since it
|
||
will combine fruitfully with the zero-extend. */
|
||
|| tmode != mode
|
||
#endif
|
||
#endif
|
||
)
|
||
&& bitsize < HOST_BITS_PER_INT)
|
||
return expand_bit_and (GET_MODE (op0), op0,
|
||
gen_rtx (CONST_INT, VOIDmode, (1 << bitsize) - 1),
|
||
target);
|
||
return op0;
|
||
}
|
||
|
||
/* To extract a signed bit-field, first shift its msb to the msb of the word,
|
||
then arithmetic-shift its lsb to the lsb of the word. */
|
||
op0 = force_reg (mode, op0);
|
||
if (mode != tmode)
|
||
target = 0;
|
||
if (GET_MODE_BITSIZE (QImode) < GET_MODE_BITSIZE (mode)
|
||
&& GET_MODE_BITSIZE (QImode) >= bitsize + bitpos)
|
||
mode = QImode, op0 = convert_to_mode (QImode, op0, 0);
|
||
if (GET_MODE_BITSIZE (HImode) < GET_MODE_BITSIZE (mode)
|
||
&& GET_MODE_BITSIZE (HImode) >= bitsize + bitpos)
|
||
mode = HImode, op0 = convert_to_mode (HImode, op0, 0);
|
||
if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos))
|
||
{
|
||
tree amount = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0);
|
||
/* Maybe propagate the target for the shift. */
|
||
/* But not if we will return the result--could confuse integrate.c. */
|
||
rtx subtarget = (target != 0 && GET_CODE (target) == REG
|
||
&& ! REG_FUNCTION_VALUE_P (target)
|
||
? target : 0);
|
||
op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1);
|
||
}
|
||
|
||
return expand_shift (RSHIFT_EXPR, mode, op0,
|
||
build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
|
||
target, 0);
|
||
}
|
||
|
||
/* Extract a bit field that is split across two words
|
||
and return an RTX for the result.
|
||
|
||
OP0 is the REG, SUBREG or MEM rtx for the first of the two words.
|
||
BITSIZE is the field width; BITPOS, position of its first bit, in the word.
|
||
UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend. */
|
||
|
||
static rtx
|
||
extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
|
||
rtx op0;
|
||
int bitsize, bitpos, unsignedp, align;
|
||
{
|
||
/* BITSIZE_1 is size of the part in the first word. */
|
||
int bitsize_1 = BITS_PER_WORD - bitpos;
|
||
/* BITSIZE_2 is size of the rest (in the following word). */
|
||
int bitsize_2 = bitsize - bitsize_1;
|
||
rtx part1, part2, result;
|
||
|
||
/* Get the part of the bit field from the first word. */
|
||
part1 = extract_fixed_bit_field (SImode, op0, 0, bitsize_1, bitpos,
|
||
0, 1, align);
|
||
|
||
/* Offset op0 by 1 word to get to the following one. */
|
||
if (GET_CODE (op0) == MEM)
|
||
op0 = change_address (op0, SImode,
|
||
plus_constant (XEXP (op0, 0), UNITS_PER_WORD));
|
||
else if (GET_CODE (op0) == REG)
|
||
op0 = gen_rtx (SUBREG, SImode, op0, 1);
|
||
else
|
||
op0 = gen_rtx (SUBREG, SImode, SUBREG_REG (op0), SUBREG_WORD (op0) + 1);
|
||
|
||
/* Get the part of the bit field from the second word. */
|
||
part2 = extract_fixed_bit_field (SImode, op0, 0, bitsize_2, 0, 0, 1, align);
|
||
|
||
/* Shift the more significant part up to fit above the other part. */
|
||
#ifdef BYTES_BIG_ENDIAN
|
||
part1 = expand_shift (LSHIFT_EXPR, SImode, part1,
|
||
build_int_2 (bitsize_2, 0), 0, 1);
|
||
#else
|
||
part2 = expand_shift (LSHIFT_EXPR, SImode, part2,
|
||
build_int_2 (bitsize_1, 0), 0, 1);
|
||
#endif
|
||
|
||
/* Combine the two parts with bitwise or. This works
|
||
because we extracted both parts as unsigned bit fields. */
|
||
result = expand_binop (SImode, ior_optab, part1, part2, 0, 1,
|
||
OPTAB_LIB_WIDEN);
|
||
|
||
/* Unsigned bit field: we are done. */
|
||
if (unsignedp)
|
||
return result;
|
||
/* Signed bit field: sign-extend with two arithmetic shifts. */
|
||
result = expand_shift (LSHIFT_EXPR, SImode, result,
|
||
build_int_2 (BITS_PER_WORD - bitsize, 0), 0, 0);
|
||
return expand_shift (RSHIFT_EXPR, SImode, result,
|
||
build_int_2 (BITS_PER_WORD - bitsize, 0), 0, 0);
|
||
}
|
||
|
||
/* Add INC into TARGET. */
|
||
|
||
void
|
||
expand_inc (target, inc)
|
||
rtx target, inc;
|
||
{
|
||
rtx value = expand_binop (GET_MODE (target), add_optab,
|
||
target, inc,
|
||
target, 0, OPTAB_LIB_WIDEN);
|
||
if (value != target)
|
||
emit_move_insn (target, value);
|
||
}
|
||
|
||
/* Subtract INC from TARGET. */
|
||
|
||
void
|
||
expand_dec (target, dec)
|
||
rtx target, dec;
|
||
{
|
||
rtx value = expand_binop (GET_MODE (target), sub_optab,
|
||
target, dec,
|
||
target, 0, OPTAB_LIB_WIDEN);
|
||
if (value != target)
|
||
emit_move_insn (target, value);
|
||
}
|
||
|
||
/* Output a shift instruction for expression code CODE,
|
||
with SHIFTED being the rtx for the value to shift,
|
||
and AMOUNT the tree for the amount to shift by.
|
||
Store the result in the rtx TARGET, if that is convenient.
|
||
If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic.
|
||
Return the rtx for where the value is. */
|
||
|
||
/* Pastel, for shifts, converts shift count to SImode here
|
||
independent of the mode being shifted.
|
||
Should that be done in an earlier pass?
|
||
It turns out not to matter for C. */
|
||
|
||
rtx
|
||
expand_shift (code, mode, shifted, amount, target, unsignedp)
|
||
enum tree_code code;
|
||
register enum machine_mode mode;
|
||
rtx shifted;
|
||
tree amount;
|
||
register rtx target;
|
||
int unsignedp;
|
||
{
|
||
register rtx op1, temp = 0;
|
||
register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
|
||
int try;
|
||
int rotate = code == LROTATE_EXPR || code == RROTATE_EXPR;
|
||
rtx last;
|
||
|
||
/* Previously detected shift-counts computed by NEGATE_EXPR
|
||
and shifted in the other direction; but that does not work
|
||
on all machines. */
|
||
|
||
op1 = expand_expr (amount, 0, VOIDmode, 0);
|
||
|
||
last = get_last_insn ();
|
||
|
||
for (try = 0; temp == 0 && try < 3; try++)
|
||
{
|
||
enum optab_methods methods;
|
||
delete_insns_since (last);
|
||
|
||
if (try == 0)
|
||
methods = OPTAB_DIRECT;
|
||
else if (try == 1)
|
||
methods = OPTAB_WIDEN;
|
||
else
|
||
methods = OPTAB_LIB_WIDEN;
|
||
|
||
if (rotate)
|
||
{
|
||
/* Widening does not work for rotation. */
|
||
if (methods != OPTAB_DIRECT)
|
||
methods = OPTAB_LIB;
|
||
|
||
temp = expand_binop (mode,
|
||
left ? rotl_optab : rotr_optab,
|
||
shifted, op1, target, -1, methods);
|
||
}
|
||
else if (unsignedp)
|
||
{
|
||
temp = expand_binop (mode,
|
||
left ? lshl_optab : lshr_optab,
|
||
shifted, op1, target, unsignedp, methods);
|
||
if (temp == 0 && left)
|
||
temp = expand_binop (mode, ashl_optab,
|
||
shifted, op1, target, unsignedp, methods);
|
||
if (temp != 0)
|
||
return temp;
|
||
}
|
||
/* Do arithmetic shifts.
|
||
Also, if we are going to widen the operand, we can just as well
|
||
use an arithmetic right-shift instead of a logical one. */
|
||
if (! rotate && (! unsignedp || (! left && methods == OPTAB_WIDEN)))
|
||
{
|
||
enum optab_methods methods1 = methods;
|
||
|
||
/* If trying to widen a log shift to an arithmetic shift,
|
||
don't accept an arithmetic shift of the same size. */
|
||
if (unsignedp)
|
||
methods1 = OPTAB_MUST_WIDEN;
|
||
|
||
/* Arithmetic shift */
|
||
|
||
temp = expand_binop (mode,
|
||
left ? ashl_optab : ashr_optab,
|
||
shifted, op1, target, unsignedp, methods1);
|
||
if (temp != 0)
|
||
return temp;
|
||
}
|
||
|
||
if (unsignedp)
|
||
{
|
||
/* No logical shift insn in either direction =>
|
||
try a bit-field extract instruction if we have one. */
|
||
#ifdef HAVE_extzv
|
||
#ifndef BITS_BIG_ENDIAN
|
||
if (HAVE_extzv && !left
|
||
&& ((methods == OPTAB_DIRECT && mode == SImode)
|
||
|| (methods == OPTAB_WIDEN
|
||
&& GET_MODE_SIZE (mode) < GET_MODE_SIZE (SImode))))
|
||
{
|
||
rtx shifted1 = convert_to_mode (SImode, shifted, 1);
|
||
rtx target1 = target;
|
||
|
||
/* If -fforce-mem, don't let the operand be in memory. */
|
||
if (flag_force_mem && GET_CODE (shifted1) == MEM)
|
||
shifted1 = force_not_mem (shifted1);
|
||
|
||
/* If this machine's extzv insists on a register for
|
||
operand 1, arrange for that. */
|
||
if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1])
|
||
(shifted1, SImode)))
|
||
shifted1 = force_reg (SImode, shifted1);
|
||
|
||
/* If we don't have or cannot use a suggested target,
|
||
make a place for the result, in the proper mode. */
|
||
if (methods == OPTAB_WIDEN || target1 == 0
|
||
|| ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0])
|
||
(target1, SImode)))
|
||
target1 = gen_reg_rtx (SImode);
|
||
|
||
op1 = convert_to_mode (SImode, op1, 0);
|
||
|
||
/* If this machine's extzv insists on a register for
|
||
operand 3, arrange for that. */
|
||
if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][3])
|
||
(op1, SImode)))
|
||
op1 = force_reg (SImode, op1);
|
||
|
||
op1 = protect_from_queue (op1, 1);
|
||
|
||
/* TEMP gets the width of the bit field to extract:
|
||
wordsize minus # bits to shift by. */
|
||
if (GET_CODE (op1) == CONST_INT)
|
||
temp = gen_rtx (CONST_INT, VOIDmode,
|
||
(GET_MODE_BITSIZE (mode) - INTVAL (op1)));
|
||
else
|
||
temp = expand_binop (SImode, sub_optab,
|
||
gen_rtx (CONST_INT, VOIDmode,
|
||
GET_MODE_BITSIZE (mode)),
|
||
op1, gen_reg_rtx (SImode),
|
||
0, OPTAB_LIB_WIDEN);
|
||
/* Now extract with width TEMP, omitting OP1 least sig bits. */
|
||
emit_insn (gen_extzv (protect_from_queue (target1, 1),
|
||
protect_from_queue (shifted1, 0),
|
||
temp, op1));
|
||
return convert_to_mode (mode, target1, 1);
|
||
}
|
||
/* Can also do logical shift with signed bit-field extract
|
||
followed by inserting the bit-field at a different position.
|
||
That strategy is not yet implemented. */
|
||
#endif /* not BITS_BIG_ENDIAN */
|
||
#endif /* HAVE_extzv */
|
||
/* We have failed to generate the logical shift and will abort. */
|
||
}
|
||
}
|
||
if (temp == 0)
|
||
abort ();
|
||
return temp;
|
||
}
|
||
|
||
/* Output an instruction or two to bitwise-and OP0 with OP1
|
||
in mode MODE, with output to TARGET if convenient and TARGET is not zero.
|
||
Returns where the result is. */
|
||
/* This function used to do more; now it could be eliminated. */
|
||
|
||
rtx
|
||
expand_bit_and (mode, op0, op1, target)
|
||
enum machine_mode mode;
|
||
rtx op0, op1, target;
|
||
{
|
||
register rtx temp;
|
||
|
||
/* First try to open-code it directly. */
|
||
temp = expand_binop (mode, and_optab, op0, op1, target, 1, OPTAB_LIB_WIDEN);
|
||
if (temp == 0)
|
||
abort ();
|
||
return temp;
|
||
}
|
||
|
||
/* Perform a multiplication and return an rtx for the result.
|
||
MODE is mode of value; OP0 and OP1 are what to multiply (rtx's);
|
||
TARGET is a suggestion for where to store the result (an rtx).
|
||
|
||
We check specially for a constant integer as OP1.
|
||
If you want this check for OP0 as well, then before calling
|
||
you should swap the two operands if OP0 would be constant. */
|
||
|
||
rtx
|
||
expand_mult (mode, op0, op1, target, unsignedp)
|
||
enum machine_mode mode;
|
||
register rtx op0, op1, target;
|
||
int unsignedp;
|
||
{
|
||
/* Don't use the function value register as a target
|
||
since we have to read it as well as write it,
|
||
and function-inlining gets confused by this. */
|
||
if (target && REG_P (target) && REG_FUNCTION_VALUE_P (target))
|
||
target = 0;
|
||
|
||
if (GET_CODE (op1) == CONST_INT)
|
||
{
|
||
register int foo;
|
||
int bar;
|
||
int negate = INTVAL (op1) < 0;
|
||
int absval = INTVAL (op1) * (negate ? -1 : 1);
|
||
|
||
/* Is multiplier a power of 2, or minus that? */
|
||
foo = exact_log2 (absval);
|
||
if (foo >= 0)
|
||
{
|
||
rtx tem;
|
||
if (foo == 0)
|
||
tem = op0;
|
||
else
|
||
tem = expand_shift (LSHIFT_EXPR, mode, op0,
|
||
build_int_2 (foo, 0),
|
||
target, 0);
|
||
return (negate
|
||
? expand_unop (mode, neg_optab, tem, target, 0)
|
||
: tem);
|
||
}
|
||
/* Is multiplier a sum of two powers of 2, or minus that? */
|
||
bar = floor_log2 (absval);
|
||
foo = exact_log2 (absval - (1 << bar));
|
||
if (bar >= 0 && foo >= 0)
|
||
{
|
||
rtx tem =
|
||
force_operand (gen_rtx (PLUS, mode,
|
||
expand_shift (LSHIFT_EXPR, mode, op0,
|
||
build_int_2 (bar - foo, 0),
|
||
0, 0),
|
||
op0),
|
||
((foo == 0 && ! negate) ? target : 0));
|
||
|
||
if (foo != 0)
|
||
tem = expand_shift (LSHIFT_EXPR, mode, tem,
|
||
build_int_2 (foo, 0),
|
||
negate ? 0 : target, 0);
|
||
|
||
return negate ? expand_unop (mode, neg_optab, tem, target, 0) : tem;
|
||
}
|
||
}
|
||
/* This used to use umul_optab if unsigned,
|
||
but I think that for non-widening multiply there is no difference
|
||
between signed and unsigned. */
|
||
op0 = expand_binop (mode, smul_optab,
|
||
op0, op1, target, unsignedp, OPTAB_LIB_WIDEN);
|
||
if (op0 == 0)
|
||
abort ();
|
||
return op0;
|
||
}
|
||
|
||
/* Emit the code to divide OP0 by OP1, putting the result in TARGET
|
||
if that is convenient, and returning where the result is.
|
||
You may request either the quotient or the remainder as the result;
|
||
specify REM_FLAG nonzero to get the remainder.
|
||
|
||
CODE is the expression code for which kind of division this is;
|
||
it controls how rounding is done. MODE is the machine mode to use.
|
||
UNSIGNEDP nonzero means do unsigned division. */
|
||
|
||
/* ??? For CEIL_MOD_EXPR, can compute incorrect remainder with ANDI
|
||
and then correct it by or'ing in missing high bits
|
||
if result of ANDI is nonzero.
|
||
For ROUND_MOD_EXPR, can use ANDI and then sign-extend the result.
|
||
This could optimize to a bfexts instruction.
|
||
But C doesn't use these operations, so their optimizations are
|
||
left for later. */
|
||
|
||
rtx
|
||
expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
|
||
int rem_flag;
|
||
enum tree_code code;
|
||
enum machine_mode mode;
|
||
register rtx op0, op1, target;
|
||
int unsignedp;
|
||
{
|
||
register rtx temp;
|
||
int log = -1;
|
||
int can_clobber_op0;
|
||
int mod_insn_no_good = 0;
|
||
rtx adjusted_op0 = op0;
|
||
|
||
/* Don't use the function value register as a target
|
||
since we have to read it as well as write it,
|
||
and function-inlining gets confused by this. */
|
||
if (target && REG_P (target) && REG_FUNCTION_VALUE_P (target))
|
||
target = 0;
|
||
|
||
/* Don't clobber an operand while doing a multi-step calculation. */
|
||
if (target)
|
||
if ((rem_flag && (reg_mentioned_p (target, op0)
|
||
|| (GET_CODE (op0) == MEM && GET_CODE (target) == MEM)))
|
||
|| reg_mentioned_p (target, op1)
|
||
|| (GET_CODE (op1) == MEM && GET_CODE (target) == MEM))
|
||
target = 0;
|
||
|
||
if (target == 0)
|
||
target = gen_reg_rtx (mode);
|
||
|
||
can_clobber_op0 = (GET_CODE (op0) == REG && op0 == target);
|
||
|
||
if (GET_CODE (op1) == CONST_INT)
|
||
log = exact_log2 (INTVAL (op1));
|
||
|
||
/* If log is >= 0, we are dividing by 2**log, and will do it by shifting,
|
||
which is really floor-division. Otherwise we will really do a divide,
|
||
and we assume that is trunc-division.
|
||
|
||
We must correct the dividend by adding or subtracting something
|
||
based on the divisor, in order to do the kind of rounding specified
|
||
by CODE. The correction depends on what kind of rounding is actually
|
||
available, and that depends on whether we will shift or divide. */
|
||
|
||
switch (code)
|
||
{
|
||
case TRUNC_MOD_EXPR:
|
||
case TRUNC_DIV_EXPR:
|
||
if (log >= 0 && ! unsignedp)
|
||
{
|
||
rtx label = gen_label_rtx ();
|
||
if (! can_clobber_op0)
|
||
adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target);
|
||
emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0);
|
||
emit_jump_insn (gen_bge (label));
|
||
expand_inc (adjusted_op0, plus_constant (op1, -1));
|
||
emit_label (label);
|
||
mod_insn_no_good = 1;
|
||
}
|
||
break;
|
||
|
||
case FLOOR_DIV_EXPR:
|
||
case FLOOR_MOD_EXPR:
|
||
if (log < 0 && ! unsignedp)
|
||
{
|
||
rtx label = gen_label_rtx ();
|
||
if (! can_clobber_op0)
|
||
adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target);
|
||
emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0);
|
||
emit_jump_insn (gen_bge (label));
|
||
expand_dec (adjusted_op0, op1);
|
||
expand_inc (adjusted_op0, const1_rtx);
|
||
emit_label (label);
|
||
mod_insn_no_good = 1;
|
||
}
|
||
break;
|
||
|
||
case CEIL_DIV_EXPR:
|
||
case CEIL_MOD_EXPR:
|
||
if (! can_clobber_op0)
|
||
adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target);
|
||
if (log < 0)
|
||
{
|
||
rtx label = 0;
|
||
if (! unsignedp)
|
||
{
|
||
label = gen_label_rtx ();
|
||
emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0);
|
||
emit_jump_insn (gen_ble (label));
|
||
}
|
||
expand_inc (adjusted_op0, op1);
|
||
expand_dec (adjusted_op0, const1_rtx);
|
||
if (! unsignedp)
|
||
emit_label (label);
|
||
}
|
||
else
|
||
{
|
||
adjusted_op0 = expand_binop (GET_MODE (target), add_optab,
|
||
adjusted_op0, plus_constant (op1, -1),
|
||
0, 0, OPTAB_LIB_WIDEN);
|
||
}
|
||
mod_insn_no_good = 1;
|
||
break;
|
||
|
||
case ROUND_DIV_EXPR:
|
||
case ROUND_MOD_EXPR:
|
||
if (! can_clobber_op0)
|
||
adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target);
|
||
if (log < 0)
|
||
{
|
||
op1 = expand_shift (RSHIFT_EXPR, mode, op1, integer_one_node, 0, 0);
|
||
if (! unsignedp)
|
||
{
|
||
rtx label = gen_label_rtx ();
|
||
emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0);
|
||
emit_jump_insn (gen_bge (label));
|
||
expand_unop (mode, neg_optab, op1, op1, 0);
|
||
emit_label (label);
|
||
}
|
||
expand_inc (adjusted_op0, op1);
|
||
}
|
||
else
|
||
{
|
||
op1 = gen_rtx (CONST_INT, VOIDmode, INTVAL (op1) / 2);
|
||
expand_inc (adjusted_op0, op1);
|
||
}
|
||
mod_insn_no_good = 1;
|
||
break;
|
||
}
|
||
|
||
if (rem_flag && !mod_insn_no_good)
|
||
{
|
||
/* Try to produce the remainder directly */
|
||
if (log >= 0)
|
||
{
|
||
return expand_bit_and (mode, adjusted_op0,
|
||
gen_rtx (CONST_INT, VOIDmode,
|
||
INTVAL (op1) - 1),
|
||
target);
|
||
}
|
||
else
|
||
{
|
||
/* See if we can do remainder without a library call. */
|
||
temp = sign_expand_binop (mode, umod_optab, smod_optab,
|
||
adjusted_op0, op1, target,
|
||
unsignedp, OPTAB_WIDEN);
|
||
if (temp != 0)
|
||
return temp;
|
||
/* No luck there.
|
||
Can we do remainder and divide at once without a library call? */
|
||
temp = gen_reg_rtx (mode);
|
||
if (expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab,
|
||
adjusted_op0, op1,
|
||
0, temp, unsignedp))
|
||
return temp;
|
||
temp = 0;
|
||
}
|
||
}
|
||
|
||
/* Produce the quotient. */
|
||
if (log >= 0)
|
||
temp = expand_shift (RSHIFT_EXPR, mode, adjusted_op0,
|
||
build_int_2 (exact_log2 (INTVAL (op1)), 0),
|
||
target, unsignedp);
|
||
else if (rem_flag && !mod_insn_no_good)
|
||
/* If producing quotient in order to subtract for remainder,
|
||
and a remainder subroutine would be ok,
|
||
don't use a divide subroutine. */
|
||
temp = sign_expand_binop (mode, udiv_optab, sdiv_optab,
|
||
adjusted_op0, op1, target,
|
||
unsignedp, OPTAB_WIDEN);
|
||
else
|
||
temp = sign_expand_binop (mode, udiv_optab, sdiv_optab,
|
||
adjusted_op0, op1, target,
|
||
unsignedp, OPTAB_LIB_WIDEN);
|
||
|
||
/* If we really want the remainder, get it by subtraction. */
|
||
if (rem_flag)
|
||
{
|
||
if (temp == 0)
|
||
{
|
||
/* No divide instruction either. Use library for remainder. */
|
||
temp = sign_expand_binop (mode, umod_optab, smod_optab,
|
||
op0, op1, target,
|
||
unsignedp, OPTAB_LIB_WIDEN);
|
||
}
|
||
else
|
||
{
|
||
/* We divided. Now finish doing X - Y * (X / Y). */
|
||
temp = expand_mult (mode, temp, op1, temp, unsignedp);
|
||
if (! temp) abort ();
|
||
temp = expand_binop (mode, sub_optab, op0,
|
||
temp, target, unsignedp, OPTAB_LIB_WIDEN);
|
||
}
|
||
}
|
||
|
||
if (temp == 0)
|
||
abort ();
|
||
return temp;
|
||
}
|
||
|
||
/* Return a tree node with data type TYPE, describing the value of X.
|
||
Usually this is an RTL_EXPR, if there is no obvious better choice. */
|
||
|
||
static tree
|
||
make_tree (type, x)
|
||
tree type;
|
||
rtx x;
|
||
{
|
||
tree t;
|
||
switch (GET_CODE (x))
|
||
{
|
||
case CONST_INT:
|
||
t = build_int_2 (INTVAL (x), 0);
|
||
TREE_TYPE (t) = type;
|
||
return fold (t);
|
||
|
||
default:
|
||
t = make_node (RTL_EXPR);
|
||
TREE_TYPE (t) = type;
|
||
RTL_EXPR_RTL (t) = x;
|
||
/* There are no insns to be output
|
||
when this rtl_expr is used. */
|
||
RTL_EXPR_SEQUENCE (t) = 0;
|
||
return t;
|
||
}
|
||
}
|
||
|
||
/* Return an rtx representing the value of X * MULT + ADD.
|
||
MODE is the machine mode for the computation.
|
||
UNSIGNEDP is non-zero to do unsigned multiplication.
|
||
This may emit insns. */
|
||
|
||
rtx
|
||
expand_mult_add (x, mult, add, mode, unsignedp)
|
||
rtx x, mult, add;
|
||
enum machine_mode mode;
|
||
int unsignedp;
|
||
{
|
||
tree type = type_for_size (GET_MODE_BITSIZE (mode), unsignedp);
|
||
tree prod = fold (build (MULT_EXPR, type, make_tree (type, x),
|
||
make_tree (type, mult)));
|
||
tree sum = fold (build (PLUS_EXPR, type, prod, make_tree (type, add)));
|
||
return expand_expr (sum, 0, VOIDmode, 0);
|
||
}
|