#include "config.h"
#include "system.h"
#include "toplev.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "flags.h"
#include "insn-config.h"
#include "expr.h"
#include "optabs.h"
#include "real.h"
#include "recog.h"
static void store_fixed_bit_field PARAMS ((rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, rtx));
static void store_split_bit_field PARAMS ((rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, rtx));
static rtx extract_fixed_bit_field PARAMS ((enum machine_mode, rtx,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
rtx, int));
static rtx mask_rtx PARAMS ((enum machine_mode, int,
int, int));
static rtx lshift_value PARAMS ((enum machine_mode, rtx,
int, int));
static rtx extract_split_bit_field PARAMS ((rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, int));
static void do_cmp_and_jump PARAMS ((rtx, rtx, enum rtx_code,
enum machine_mode, rtx));
static int sdiv_pow2_cheap, smod_pow2_cheap;
#ifndef SLOW_UNALIGNED_ACCESS
#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) STRICT_ALIGNMENT
#endif
#ifndef MAX_BITS_PER_WORD
#define MAX_BITS_PER_WORD BITS_PER_WORD
#endif
#ifndef HAVE_insv
#define HAVE_insv 0
#define CODE_FOR_insv CODE_FOR_nothing
#define gen_insv(a,b,c,d) NULL_RTX
#endif
#ifndef HAVE_extv
#define HAVE_extv 0
#define CODE_FOR_extv CODE_FOR_nothing
#define gen_extv(a,b,c,d) NULL_RTX
#endif
#ifndef HAVE_extzv
#define HAVE_extzv 0
#define CODE_FOR_extzv CODE_FOR_nothing
#define gen_extzv(a,b,c,d) NULL_RTX
#endif
static int add_cost, negate_cost, zero_cost;
static int shift_cost[MAX_BITS_PER_WORD];
static int shiftadd_cost[MAX_BITS_PER_WORD];
static int shiftsub_cost[MAX_BITS_PER_WORD];
static int mul_cost[NUM_MACHINE_MODES];
static int div_cost[NUM_MACHINE_MODES];
static int mul_widen_cost[NUM_MACHINE_MODES];
static int mul_highpart_cost[NUM_MACHINE_MODES];
void
init_expmed ()
{
rtx reg = gen_rtx_REG (word_mode, 10000);
rtx shift_insn, shiftadd_insn, shiftsub_insn;
int dummy;
int m;
enum machine_mode mode, wider_mode;
start_sequence ();
reg = gen_rtx_REG (word_mode, 10000);
zero_cost = rtx_cost (const0_rtx, 0);
add_cost = rtx_cost (gen_rtx_PLUS (word_mode, reg, reg), SET);
shift_insn = emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_ASHIFT (word_mode, reg,
const0_rtx)));
shiftadd_insn
= emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_PLUS (word_mode,
gen_rtx_MULT (word_mode,
reg, const0_rtx),
reg)));
shiftsub_insn
= emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_MINUS (word_mode,
gen_rtx_MULT (word_mode,
reg, const0_rtx),
reg)));
init_recog ();
shift_cost[0] = 0;
shiftadd_cost[0] = shiftsub_cost[0] = add_cost;
for (m = 1; m < MAX_BITS_PER_WORD; m++)
{
shift_cost[m] = shiftadd_cost[m] = shiftsub_cost[m] = 32000;
XEXP (SET_SRC (PATTERN (shift_insn)), 1) = GEN_INT (m);
if (recog (PATTERN (shift_insn), shift_insn, &dummy) >= 0)
shift_cost[m] = rtx_cost (SET_SRC (PATTERN (shift_insn)), SET);
XEXP (XEXP (SET_SRC (PATTERN (shiftadd_insn)), 0), 1)
= GEN_INT ((HOST_WIDE_INT) 1 << m);
if (recog (PATTERN (shiftadd_insn), shiftadd_insn, &dummy) >= 0)
shiftadd_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftadd_insn)), SET);
XEXP (XEXP (SET_SRC (PATTERN (shiftsub_insn)), 0), 1)
= GEN_INT ((HOST_WIDE_INT) 1 << m);
if (recog (PATTERN (shiftsub_insn), shiftsub_insn, &dummy) >= 0)
shiftsub_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftsub_insn)), SET);
}
negate_cost = rtx_cost (gen_rtx_NEG (word_mode, reg), SET);
sdiv_pow2_cheap
= (rtx_cost (gen_rtx_DIV (word_mode, reg, GEN_INT (32)), SET)
<= 2 * add_cost);
smod_pow2_cheap
= (rtx_cost (gen_rtx_MOD (word_mode, reg, GEN_INT (32)), SET)
<= 2 * add_cost);
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
{
reg = gen_rtx_REG (mode, 10000);
div_cost[(int) mode] = rtx_cost (gen_rtx_UDIV (mode, reg, reg), SET);
mul_cost[(int) mode] = rtx_cost (gen_rtx_MULT (mode, reg, reg), SET);
wider_mode = GET_MODE_WIDER_MODE (mode);
if (wider_mode != VOIDmode)
{
mul_widen_cost[(int) wider_mode]
= rtx_cost (gen_rtx_MULT (wider_mode,
gen_rtx_ZERO_EXTEND (wider_mode, reg),
gen_rtx_ZERO_EXTEND (wider_mode, reg)),
SET);
mul_highpart_cost[(int) mode]
= rtx_cost (gen_rtx_TRUNCATE
(mode,
gen_rtx_LSHIFTRT (wider_mode,
gen_rtx_MULT (wider_mode,
gen_rtx_ZERO_EXTEND
(wider_mode, reg),
gen_rtx_ZERO_EXTEND
(wider_mode, reg)),
GEN_INT (GET_MODE_BITSIZE (mode)))),
SET);
}
}
end_sequence ();
}
rtx
negate_rtx (mode, x)
enum machine_mode mode;
rtx x;
{
rtx result = simplify_unary_operation (NEG, mode, x, mode);
if (result == 0)
result = expand_unop (mode, neg_optab, x, NULL_RTX, 0);
return result;
}
enum machine_mode
mode_for_extraction (pattern, opno)
enum extraction_pattern pattern;
int opno;
{
const struct insn_data *data;
switch (pattern)
{
case EP_insv:
if (HAVE_insv)
{
data = &insn_data[CODE_FOR_insv];
break;
}
return MAX_MACHINE_MODE;
case EP_extv:
if (HAVE_extv)
{
data = &insn_data[CODE_FOR_extv];
break;
}
return MAX_MACHINE_MODE;
case EP_extzv:
if (HAVE_extzv)
{
data = &insn_data[CODE_FOR_extzv];
break;
}
return MAX_MACHINE_MODE;
default:
abort ();
}
if (opno == -1)
return VOIDmode;
if (data->operand[opno].mode == VOIDmode)
return word_mode;
return data->operand[opno].mode;
}
rtx
store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, total_size)
rtx str_rtx;
unsigned HOST_WIDE_INT bitsize;
unsigned HOST_WIDE_INT bitnum;
enum machine_mode fieldmode;
rtx value;
HOST_WIDE_INT total_size;
{
unsigned int unit
= (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
unsigned HOST_WIDE_INT offset = bitnum / unit;
unsigned HOST_WIDE_INT bitpos = bitnum % unit;
rtx op0 = str_rtx;
int byte_offset;
enum machine_mode op_mode = mode_for_extraction (EP_insv, 3);
if (total_size >= 0)
total_size -= (bitpos / BIGGEST_ALIGNMENT
* (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
while (GET_CODE (op0) == SUBREG)
{
offset += (SUBREG_BYTE (op0) / UNITS_PER_WORD);
op0 = SUBREG_REG (op0);
}
value = protect_from_queue (value, 0);
if (flag_force_mem)
{
int old_generating_concat_p = generating_concat_p;
generating_concat_p = 0;
value = force_not_mem (value);
generating_concat_p = old_generating_concat_p;
}
byte_offset = (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
+ (offset * UNITS_PER_WORD);
if (bitpos == 0
&& bitsize == GET_MODE_BITSIZE (fieldmode)
&& (GET_CODE (op0) != MEM
? ((GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD
|| GET_MODE_SIZE (GET_MODE (op0)) == GET_MODE_SIZE (fieldmode))
&& byte_offset % GET_MODE_SIZE (fieldmode) == 0)
: (! SLOW_UNALIGNED_ACCESS (fieldmode, MEM_ALIGN (op0))
|| (offset * BITS_PER_UNIT % bitsize == 0
&& MEM_ALIGN (op0) % GET_MODE_BITSIZE (fieldmode) == 0))))
{
if (GET_MODE (op0) != fieldmode)
{
if (GET_CODE (op0) == SUBREG)
{
if (GET_MODE (SUBREG_REG (op0)) == fieldmode
|| GET_MODE_CLASS (fieldmode) == MODE_INT
|| GET_MODE_CLASS (fieldmode) == MODE_PARTIAL_INT)
op0 = SUBREG_REG (op0);
else
abort ();
}
if (GET_CODE (op0) == REG)
op0 = gen_rtx_SUBREG (fieldmode, op0, byte_offset);
else
op0 = adjust_address (op0, fieldmode, offset);
}
emit_move_insn (op0, value);
return value;
}
{
enum machine_mode imode = int_mode_for_mode (GET_MODE (op0));
if (imode != GET_MODE (op0))
{
if (GET_CODE (op0) == MEM)
op0 = adjust_address (op0, imode, 0);
else if (imode != BLKmode)
op0 = gen_lowpart (imode, op0);
else
abort ();
}
}
if (GET_CODE (op0) == MEM)
{
op0 = shallow_copy_rtx (op0);
set_mem_alias_set (op0, 0);
set_mem_expr (op0, 0);
}
if (BYTES_BIG_ENDIAN
&& GET_CODE (op0) != MEM
&& unit > GET_MODE_BITSIZE (GET_MODE (op0)))
bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
if (GET_CODE (op0) != MEM
&& (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0)
&& bitsize == GET_MODE_BITSIZE (fieldmode)
&& (movstrict_optab->handlers[(int) fieldmode].insn_code
!= CODE_FOR_nothing))
{
int icode = movstrict_optab->handlers[(int) fieldmode].insn_code;
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 (! (*insn_data[icode].operand[1].predicate) (value, fieldmode))
value = copy_to_mode_reg (fieldmode, value);
if (GET_CODE (op0) == SUBREG)
{
if (GET_MODE (SUBREG_REG (op0)) == fieldmode
|| GET_MODE_CLASS (fieldmode) == MODE_INT
|| GET_MODE_CLASS (fieldmode) == MODE_PARTIAL_INT)
op0 = SUBREG_REG (op0);
else
abort ();
}
emit_insn (GEN_FCN (icode)
(gen_rtx_SUBREG (fieldmode, op0,
(bitnum % BITS_PER_WORD) / BITS_PER_UNIT
+ (offset * UNITS_PER_WORD)),
value));
return value;
}
if (bitsize > BITS_PER_WORD)
{
unsigned int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;
unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
unsigned int i;
fieldmode = smallest_mode_for_size (nwords * BITS_PER_WORD, MODE_INT);
for (i = 0; i < nwords; i++)
{
unsigned int wordnum = (backwards ? nwords - i - 1 : i);
unsigned int bit_offset = (backwards
? MAX ((int) bitsize - ((int) i + 1)
* BITS_PER_WORD,
0)
: (int) i * BITS_PER_WORD);
store_bit_field (op0, MIN (BITS_PER_WORD,
bitsize - i * BITS_PER_WORD),
bitnum + bit_offset, word_mode,
operand_subword_force (value, wordnum,
(GET_MODE (value) == VOIDmode
? fieldmode
: GET_MODE (value))),
total_size);
}
return value;
}
if (GET_CODE (op0) != MEM)
{
if (offset != 0
|| GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)
{
if (GET_CODE (op0) != REG)
{
if (GET_CODE (op0) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (op0))
== GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
op0 = SUBREG_REG (op0);
else
abort ();
}
op0 = gen_rtx_SUBREG (mode_for_size (BITS_PER_WORD, MODE_INT, 0),
op0, (offset * UNITS_PER_WORD));
}
offset = 0;
}
else
op0 = protect_from_queue (op0, 1);
if (GET_MODE_CLASS (GET_MODE (value)) != MODE_INT
&& GET_MODE_CLASS (GET_MODE (value)) != MODE_PARTIAL_INT)
value = gen_lowpart (word_mode, value);
if (HAVE_insv
&& GET_MODE (value) != BLKmode
&& !(bitsize == 1 && GET_CODE (value) == CONST_INT)
&& (GET_MODE_BITSIZE (op_mode) >= bitsize)
&& ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
&& (bitsize + bitpos > GET_MODE_BITSIZE (op_mode))))
{
int xbitpos = bitpos;
rtx value1;
rtx xop0 = op0;
rtx last = get_last_insn ();
rtx pat;
enum machine_mode maxmode = mode_for_extraction (EP_insv, 3);
int save_volatile_ok = volatile_ok;
volatile_ok = 1;
if (GET_CODE (op0) == MEM
&& ! ((*insn_data[(int) CODE_FOR_insv].operand[0].predicate)
(op0, VOIDmode)))
{
rtx tempreg;
enum machine_mode bestmode;
if (GET_MODE (op0) == BLKmode
|| GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (maxmode))
bestmode
= get_best_mode (bitsize, bitnum, MEM_ALIGN (op0), maxmode,
MEM_VOLATILE_P (op0));
else
bestmode = GET_MODE (op0);
if (bestmode == VOIDmode
|| (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
goto insv_loses;
unit = GET_MODE_BITSIZE (bestmode);
offset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
bitpos = bitnum % unit;
op0 = adjust_address (op0, bestmode, offset);
tempreg = copy_to_reg (op0);
store_bit_field (tempreg, bitsize, bitpos,
(GET_MODE_CLASS (fieldmode) != MODE_INT
&& GET_MODE_CLASS (fieldmode) != MODE_PARTIAL_INT)
? GET_MODE (value) : fieldmode,
value, total_size);
emit_move_insn (op0, tempreg);
return value;
}
volatile_ok = save_volatile_ok;
if (GET_CODE (xop0) == MEM)
xop0 = adjust_address (xop0, byte_mode, offset);
if (GET_CODE (xop0) == SUBREG)
xop0 = gen_rtx_SUBREG (maxmode, SUBREG_REG (xop0), SUBREG_BYTE (xop0));
if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
xbitpos = unit - bitsize - xbitpos;
if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM)
xbitpos += GET_MODE_BITSIZE (maxmode) - unit;
unit = GET_MODE_BITSIZE (maxmode);
value1 = value;
if (GET_MODE (value) != maxmode)
{
if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize)
{
if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode))
{
rtx tmp;
tmp = simplify_subreg (maxmode, value1, GET_MODE (value), 0);
if (! tmp)
tmp = simplify_gen_subreg (maxmode,
force_reg (GET_MODE (value),
value1),
GET_MODE (value), 0);
value1 = tmp;
}
else
value1 = gen_lowpart (maxmode, value1);
}
else if (GET_CODE (value) == CONST_INT)
value1 = GEN_INT (trunc_int_for_mode (INTVAL (value), maxmode));
else if (!CONSTANT_P (value))
abort ();
}
if (! ((*insn_data[(int) CODE_FOR_insv].operand[3].predicate)
(value1, maxmode)))
value1 = force_reg (maxmode, value1);
pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1);
if (pat)
emit_insn (pat);
else
{
delete_insns_since (last);
store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
}
}
else
insv_loses:
store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
return value;
}
static void
store_fixed_bit_field (op0, offset, bitsize, bitpos, value)
rtx op0;
unsigned HOST_WIDE_INT offset, bitsize, bitpos;
rtx value;
{
enum machine_mode mode;
unsigned int total_bits = BITS_PER_WORD;
rtx subtarget, temp;
int all_zero = 0;
int all_one = 0;
if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
{
if (offset != 0)
abort ();
if (bitsize + bitpos > BITS_PER_WORD)
{
store_split_bit_field (op0, bitsize, bitpos, value);
return;
}
}
else
{
mode = GET_MODE (op0);
if (GET_MODE_BITSIZE (mode) == 0
|| GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (word_mode))
mode = word_mode;
mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
MEM_ALIGN (op0), mode, MEM_VOLATILE_P (op0));
if (mode == VOIDmode)
{
store_split_bit_field (op0, bitsize, bitpos + offset * BITS_PER_UNIT,
value);
return;
}
total_bits = GET_MODE_BITSIZE (mode);
if (bitpos >= total_bits)
{
offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT);
bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT)
* BITS_PER_UNIT);
}
bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
offset -= (offset % (total_bits / BITS_PER_UNIT));
op0 = adjust_address (op0, mode, offset);
}
mode = GET_MODE (op0);
if (BYTES_BIG_ENDIAN)
bitpos = total_bits - bitsize - bitpos;
if (GET_CODE (value) == CONST_INT)
{
HOST_WIDE_INT v = INTVAL (value);
if (bitsize < HOST_BITS_PER_WIDE_INT)
v &= ((HOST_WIDE_INT) 1 << bitsize) - 1;
if (v == 0)
all_zero = 1;
else if ((bitsize < HOST_BITS_PER_WIDE_INT
&& v == ((HOST_WIDE_INT) 1 << bitsize) - 1)
|| (bitsize == HOST_BITS_PER_WIDE_INT && v == -1))
all_one = 1;
value = lshift_value (mode, value, bitpos, bitsize);
}
else
{
int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize
&& bitpos + bitsize != GET_MODE_BITSIZE (mode));
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)
value = expand_binop (mode, and_optab, value,
mask_rtx (mode, 0, bitsize, 0),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
if (bitpos > 0)
value = expand_shift (LSHIFT_EXPR, mode, value,
build_int_2 (bitpos, 0), NULL_RTX, 1);
}
subtarget = (GET_CODE (op0) == REG || ! flag_force_mem) ? op0 : 0;
if (! all_one)
{
temp = expand_binop (mode, and_optab, op0,
mask_rtx (mode, bitpos, bitsize, 1),
subtarget, 1, OPTAB_LIB_WIDEN);
subtarget = temp;
}
else
temp = op0;
if (! all_zero)
temp = expand_binop (mode, ior_optab, temp, value,
subtarget, 1, OPTAB_LIB_WIDEN);
if (op0 != temp)
emit_move_insn (op0, temp);
}
static void
store_split_bit_field (op0, bitsize, bitpos, value)
rtx op0;
unsigned HOST_WIDE_INT bitsize, bitpos;
rtx value;
{
unsigned int unit;
unsigned int bitsdone = 0;
if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
unit = BITS_PER_WORD;
else
unit = MIN (MEM_ALIGN (op0), BITS_PER_WORD);
if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT)
{
rtx word = gen_lowpart_common (word_mode, value);
if (word && (value != word))
value = word;
else
value = gen_lowpart_common (word_mode,
force_reg (GET_MODE (value) != VOIDmode
? GET_MODE (value)
: word_mode, value));
}
else if (GET_CODE (value) == ADDRESSOF)
value = copy_to_reg (value);
while (bitsdone < bitsize)
{
unsigned HOST_WIDE_INT thissize;
rtx part, word;
unsigned HOST_WIDE_INT thispos;
unsigned HOST_WIDE_INT offset;
offset = (bitpos + bitsdone) / unit;
thispos = (bitpos + bitsdone) % unit;
thissize = MIN (bitsize - bitsdone, BITS_PER_WORD);
thissize = MIN (thissize, unit - thispos);
if (BYTES_BIG_ENDIAN)
{
int total_bits;
if (GET_CODE (value) != MEM || GET_MODE (value) == BLKmode)
total_bits = BITS_PER_WORD;
else
total_bits = GET_MODE_BITSIZE (GET_MODE (value));
if (GET_CODE (value) == CONST_INT)
part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
>> (bitsize - bitsdone - thissize))
& (((HOST_WIDE_INT) 1 << thissize) - 1));
else
part = extract_fixed_bit_field (word_mode, value, 0, thissize,
total_bits - bitsize + bitsdone,
NULL_RTX, 1);
}
else
{
if (GET_CODE (value) == CONST_INT)
part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
>> bitsdone)
& (((HOST_WIDE_INT) 1 << thissize) - 1));
else
part = extract_fixed_bit_field (word_mode, value, 0, thissize,
bitsdone, NULL_RTX, 1);
}
if (GET_CODE (op0) == SUBREG)
{
int word_offset = (SUBREG_BYTE (op0) / UNITS_PER_WORD) + offset;
word = operand_subword_force (SUBREG_REG (op0), word_offset,
GET_MODE (SUBREG_REG (op0)));
offset = 0;
}
else if (GET_CODE (op0) == REG)
{
word = operand_subword_force (op0, offset, GET_MODE (op0));
offset = 0;
}
else
word = op0;
store_fixed_bit_field (word, offset * unit / BITS_PER_UNIT, thissize,
thispos, part);
bitsdone += thissize;
}
}
rtx
extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
target, mode, tmode, total_size)
rtx str_rtx;
unsigned HOST_WIDE_INT bitsize;
unsigned HOST_WIDE_INT bitnum;
int unsignedp;
rtx target;
enum machine_mode mode, tmode;
HOST_WIDE_INT total_size;
{
unsigned int unit
= (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
unsigned HOST_WIDE_INT offset = bitnum / unit;
unsigned HOST_WIDE_INT bitpos = bitnum % unit;
rtx op0 = str_rtx;
rtx spec_target = target;
rtx spec_target_subreg = 0;
enum machine_mode int_mode;
enum machine_mode extv_mode = mode_for_extraction (EP_extv, 0);
enum machine_mode extzv_mode = mode_for_extraction (EP_extzv, 0);
enum machine_mode mode1;
int byte_offset;
if (total_size >= 0)
total_size -= (bitpos / BIGGEST_ALIGNMENT
* (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
if (tmode == VOIDmode)
tmode = mode;
while (GET_CODE (op0) == SUBREG)
{
int outer_size = GET_MODE_BITSIZE (GET_MODE (op0));
int inner_size = GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)));
offset += SUBREG_BYTE (op0) / UNITS_PER_WORD;
inner_size = MIN (inner_size, BITS_PER_WORD);
if (BYTES_BIG_ENDIAN && (outer_size < inner_size))
{
bitpos += inner_size - outer_size;
if (bitpos > unit)
{
offset += (bitpos / unit);
bitpos %= unit;
}
}
op0 = SUBREG_REG (op0);
}
if (GET_CODE (op0) == REG
&& mode == GET_MODE (op0)
&& bitnum == 0
&& bitsize == GET_MODE_BITSIZE (GET_MODE (op0)))
{
return op0;
}
{
enum machine_mode imode = int_mode_for_mode (GET_MODE (op0));
if (imode != GET_MODE (op0))
{
if (GET_CODE (op0) == MEM)
op0 = adjust_address (op0, imode, 0);
else if (imode != BLKmode)
op0 = gen_lowpart (imode, op0);
else
abort ();
}
}
if (GET_CODE (op0) == MEM)
{
op0 = shallow_copy_rtx (op0);
set_mem_alias_set (op0, 0);
set_mem_expr (op0, 0);
}
if (BYTES_BIG_ENDIAN
&& GET_CODE (op0) != MEM
&& unit > GET_MODE_BITSIZE (GET_MODE (op0)))
bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
byte_offset = (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
+ (offset * UNITS_PER_WORD);
mode1 = (VECTOR_MODE_P (tmode)
? mode
: mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0));
if (((GET_CODE (op0) != MEM
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
GET_MODE_BITSIZE (GET_MODE (op0)))
&& GET_MODE_SIZE (mode1) != 0
&& byte_offset % GET_MODE_SIZE (mode1) == 0)
|| (GET_CODE (op0) == MEM
&& (! SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (op0))
|| (offset * BITS_PER_UNIT % bitsize == 0
&& MEM_ALIGN (op0) % bitsize == 0))))
&& ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode)
&& bitpos % BITS_PER_WORD == 0)
|| (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode
&& (BYTES_BIG_ENDIAN
? bitpos + bitsize == BITS_PER_WORD
: bitpos == 0))))
{
if (mode1 != GET_MODE (op0))
{
if (GET_CODE (op0) == SUBREG)
{
if (GET_MODE (SUBREG_REG (op0)) == mode1
|| GET_MODE_CLASS (mode1) == MODE_INT
|| GET_MODE_CLASS (mode1) == MODE_PARTIAL_INT)
op0 = SUBREG_REG (op0);
else
abort ();
}
if (GET_CODE (op0) == REG)
op0 = gen_rtx_SUBREG (mode1, op0, byte_offset);
else
op0 = adjust_address (op0, mode1, offset);
}
if (mode1 != mode)
return convert_to_mode (tmode, op0, unsignedp);
return op0;
}
if (bitsize > BITS_PER_WORD)
{
unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
unsigned int i;
if (target == 0 || GET_CODE (target) != REG)
target = gen_reg_rtx (mode);
emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
for (i = 0; i < nwords; i++)
{
unsigned int wordnum
= (WORDS_BIG_ENDIAN
? GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD - i - 1
: i);
unsigned int bit_offset = (WORDS_BIG_ENDIAN
? MAX (0, ((int) bitsize - ((int) i + 1)
* (int) BITS_PER_WORD))
: (int) i * BITS_PER_WORD);
rtx target_part = operand_subword (target, wordnum, 1, VOIDmode);
rtx result_part
= extract_bit_field (op0, MIN (BITS_PER_WORD,
bitsize - i * BITS_PER_WORD),
bitnum + bit_offset, 1, target_part, mode,
word_mode, total_size);
if (target_part == 0)
abort ();
if (result_part != target_part)
emit_move_insn (target_part, result_part);
}
if (unsignedp)
{
if (GET_MODE_SIZE (GET_MODE (target)) > nwords * UNITS_PER_WORD)
{
unsigned int i, total_words;
total_words = GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD;
for (i = nwords; i < total_words; i++)
emit_move_insn
(operand_subword (target,
WORDS_BIG_ENDIAN ? total_words - i - 1 : i,
1, VOIDmode),
const0_rtx);
}
return target;
}
target = expand_shift (LSHIFT_EXPR, mode, target,
build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
NULL_RTX, 0);
return expand_shift (RSHIFT_EXPR, mode, target,
build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
NULL_RTX, 0);
}
int_mode = int_mode_for_mode (tmode);
if (int_mode == BLKmode)
int_mode = int_mode_for_mode (mode);
if (int_mode == BLKmode)
abort ();
if (GET_CODE (op0) != MEM)
{
if (offset != 0
|| GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)
{
if (GET_CODE (op0) != REG)
op0 = copy_to_reg (op0);
op0 = gen_rtx_SUBREG (mode_for_size (BITS_PER_WORD, MODE_INT, 0),
op0, (offset * UNITS_PER_WORD));
}
offset = 0;
}
else
op0 = protect_from_queue (str_rtx, 1);
if (unsignedp)
{
if (HAVE_extzv
&& (GET_MODE_BITSIZE (extzv_mode) >= bitsize)
&& ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
&& (bitsize + bitpos > GET_MODE_BITSIZE (extzv_mode))))
{
unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
rtx bitsize_rtx, bitpos_rtx;
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;
enum machine_mode maxmode = mode_for_extraction (EP_extzv, 0);
if (GET_CODE (xop0) == MEM)
{
int save_volatile_ok = volatile_ok;
volatile_ok = 1;
if (! ((*insn_data[(int) CODE_FOR_extzv].operand[1].predicate)
(xop0, GET_MODE (xop0))))
{
enum machine_mode bestmode;
if (GET_MODE (xop0) == BLKmode
|| (GET_MODE_SIZE (GET_MODE (op0))
> GET_MODE_SIZE (maxmode)))
bestmode = get_best_mode (bitsize, bitnum,
MEM_ALIGN (xop0), maxmode,
MEM_VOLATILE_P (xop0));
else
bestmode = GET_MODE (xop0);
if (bestmode == VOIDmode
|| (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (xop0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (xop0)))
goto extzv_loses;
unit = GET_MODE_BITSIZE (bestmode);
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
xbitpos = bitnum % unit;
xop0 = adjust_address (xop0, bestmode, xoffset);
xop0 = force_reg (bestmode, xop0);
}
else
xop0 = adjust_address (xop0, byte_mode, xoffset);
volatile_ok = save_volatile_ok;
}
if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode)
goto extzv_loses;
if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
xbitpos = unit - bitsize - xbitpos;
if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM)
xbitpos += GET_MODE_BITSIZE (maxmode) - unit;
unit = GET_MODE_BITSIZE (maxmode);
if (xtarget == 0
|| (flag_force_mem && GET_CODE (xtarget) == MEM))
xtarget = xspec_target = gen_reg_rtx (tmode);
if (GET_MODE (xtarget) != maxmode)
{
if (GET_CODE (xtarget) == REG)
{
int wider = (GET_MODE_SIZE (maxmode)
> GET_MODE_SIZE (GET_MODE (xtarget)));
xtarget = gen_lowpart (maxmode, xtarget);
if (wider)
xspec_target_subreg = xtarget;
}
else
xtarget = gen_reg_rtx (maxmode);
}
if (! ((*insn_data[(int) CODE_FOR_extzv].operand[0].predicate)
(xtarget, maxmode)))
xtarget = gen_reg_rtx (maxmode);
bitsize_rtx = GEN_INT (bitsize);
bitpos_rtx = GEN_INT (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 (int_mode, op0, offset, bitsize,
bitpos, target, 1);
}
}
else
extzv_loses:
target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
bitpos, target, 1);
}
else
{
if (HAVE_extv
&& (GET_MODE_BITSIZE (extv_mode) >= bitsize)
&& ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
&& (bitsize + bitpos > GET_MODE_BITSIZE (extv_mode))))
{
int xbitpos = bitpos, xoffset = offset;
rtx bitsize_rtx, bitpos_rtx;
rtx last = get_last_insn ();
rtx xop0 = op0, xtarget = target;
rtx xspec_target = spec_target;
rtx xspec_target_subreg = spec_target_subreg;
rtx pat;
enum machine_mode maxmode = mode_for_extraction (EP_extv, 0);
if (GET_CODE (xop0) == MEM)
{
if (! ((*insn_data[(int) CODE_FOR_extv].operand[1].predicate)
(xop0, GET_MODE (xop0))))
{
enum machine_mode bestmode;
if (GET_MODE (xop0) == BLKmode
|| (GET_MODE_SIZE (GET_MODE (op0))
> GET_MODE_SIZE (maxmode)))
bestmode = get_best_mode (bitsize, bitnum,
MEM_ALIGN (xop0), maxmode,
MEM_VOLATILE_P (xop0));
else
bestmode = GET_MODE (xop0);
if (bestmode == VOIDmode
|| (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (xop0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (xop0)))
goto extv_loses;
unit = GET_MODE_BITSIZE (bestmode);
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
xbitpos = bitnum % unit;
xop0 = adjust_address (xop0, bestmode, xoffset);
xop0 = force_reg (bestmode, xop0);
}
else
xop0 = adjust_address (xop0, byte_mode, xoffset);
}
if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode)
goto extv_loses;
if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
xbitpos = unit - bitsize - xbitpos;
if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM)
xbitpos += (GET_MODE_BITSIZE (maxmode) - unit);
unit = GET_MODE_BITSIZE (maxmode);
if (xtarget == 0
|| (flag_force_mem && GET_CODE (xtarget) == MEM))
xtarget = xspec_target = gen_reg_rtx (tmode);
if (GET_MODE (xtarget) != maxmode)
{
if (GET_CODE (xtarget) == REG)
{
int wider = (GET_MODE_SIZE (maxmode)
> GET_MODE_SIZE (GET_MODE (xtarget)));
xtarget = gen_lowpart (maxmode, xtarget);
if (wider)
xspec_target_subreg = xtarget;
}
else
xtarget = gen_reg_rtx (maxmode);
}
if (! ((*insn_data[(int) CODE_FOR_extv].operand[0].predicate)
(xtarget, maxmode)))
xtarget = gen_reg_rtx (maxmode);
bitsize_rtx = GEN_INT (bitsize);
bitpos_rtx = GEN_INT (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 (int_mode, op0, offset, bitsize,
bitpos, target, 0);
}
}
else
extv_loses:
target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
bitpos, target, 0);
}
if (target == spec_target)
return target;
if (target == spec_target_subreg)
return spec_target;
if (GET_MODE (target) != tmode && GET_MODE (target) != mode)
{
if (GET_MODE_CLASS (tmode) != MODE_INT
&& GET_MODE_CLASS (tmode) != MODE_PARTIAL_INT)
{
target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode),
MODE_INT, 0),
target, unsignedp);
return gen_lowpart (tmode, target);
}
else
return convert_to_mode (tmode, target, unsignedp);
}
return target;
}
static rtx
extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
target, unsignedp)
enum machine_mode tmode;
rtx op0, target;
unsigned HOST_WIDE_INT offset, bitsize, bitpos;
int unsignedp;
{
unsigned int total_bits = BITS_PER_WORD;
enum machine_mode mode;
if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG)
{
if (bitsize + bitpos > BITS_PER_WORD)
return extract_split_bit_field (op0, bitsize, bitpos, unsignedp);
}
else
{
mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
MEM_ALIGN (op0), word_mode, MEM_VOLATILE_P (op0));
if (mode == VOIDmode)
return extract_split_bit_field (op0, bitsize,
bitpos + offset * BITS_PER_UNIT,
unsignedp);
total_bits = GET_MODE_BITSIZE (mode);
if (bitpos >= total_bits)
{
offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT);
bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT)
* BITS_PER_UNIT);
}
bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
offset -= (offset % (total_bits / BITS_PER_UNIT));
op0 = adjust_address (op0, mode, offset);
}
mode = GET_MODE (op0);
if (BYTES_BIG_ENDIAN)
bitpos = total_bits - bitsize - bitpos;
if (unsignedp)
{
if (bitpos)
{
tree amount = build_int_2 (bitpos, 0);
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);
}
if (mode != tmode)
op0 = convert_to_mode (tmode, op0, 1);
if (GET_MODE_BITSIZE (mode) != bitpos + bitsize)
return expand_binop (GET_MODE (op0), and_optab, op0,
mask_rtx (GET_MODE (op0), 0, bitsize, 0),
target, 1, OPTAB_LIB_WIDEN);
return op0;
}
op0 = force_reg (mode, op0);
if (mode != tmode)
target = 0;
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
if (GET_MODE_BITSIZE (mode) >= bitsize + bitpos)
{
op0 = convert_to_mode (mode, op0, 0);
break;
}
if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos))
{
tree amount
= build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0);
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);
}
static rtx
mask_rtx (mode, bitpos, bitsize, complement)
enum machine_mode mode;
int bitpos, bitsize, complement;
{
HOST_WIDE_INT masklow, maskhigh;
if (bitpos < HOST_BITS_PER_WIDE_INT)
masklow = (HOST_WIDE_INT) -1 << bitpos;
else
masklow = 0;
if (bitpos + bitsize < HOST_BITS_PER_WIDE_INT)
masklow &= ((unsigned HOST_WIDE_INT) -1
>> (HOST_BITS_PER_WIDE_INT - bitpos - bitsize));
if (bitpos <= HOST_BITS_PER_WIDE_INT)
maskhigh = -1;
else
maskhigh = (HOST_WIDE_INT) -1 << (bitpos - HOST_BITS_PER_WIDE_INT);
if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT)
maskhigh &= ((unsigned HOST_WIDE_INT) -1
>> (2 * HOST_BITS_PER_WIDE_INT - bitpos - bitsize));
else
maskhigh = 0;
if (complement)
{
maskhigh = ~maskhigh;
masklow = ~masklow;
}
return immed_double_const (masklow, maskhigh, mode);
}
static rtx
lshift_value (mode, value, bitpos, bitsize)
enum machine_mode mode;
rtx value;
int bitpos, bitsize;
{
unsigned HOST_WIDE_INT v = INTVAL (value);
HOST_WIDE_INT low, high;
if (bitsize < HOST_BITS_PER_WIDE_INT)
v &= ~((HOST_WIDE_INT) -1 << bitsize);
if (bitpos < HOST_BITS_PER_WIDE_INT)
{
low = v << bitpos;
high = (bitpos > 0 ? (v >> (HOST_BITS_PER_WIDE_INT - bitpos)) : 0);
}
else
{
low = 0;
high = v << (bitpos - HOST_BITS_PER_WIDE_INT);
}
return immed_double_const (low, high, mode);
}
static rtx
extract_split_bit_field (op0, bitsize, bitpos, unsignedp)
rtx op0;
unsigned HOST_WIDE_INT bitsize, bitpos;
int unsignedp;
{
unsigned int unit;
unsigned int bitsdone = 0;
rtx result = NULL_RTX;
int first = 1;
if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
unit = BITS_PER_WORD;
else
unit = MIN (MEM_ALIGN (op0), BITS_PER_WORD);
while (bitsdone < bitsize)
{
unsigned HOST_WIDE_INT thissize;
rtx part, word;
unsigned HOST_WIDE_INT thispos;
unsigned HOST_WIDE_INT offset;
offset = (bitpos + bitsdone) / unit;
thispos = (bitpos + bitsdone) % unit;
thissize = MIN (bitsize - bitsdone, BITS_PER_WORD);
thissize = MIN (thissize, unit - thispos);
if (GET_CODE (op0) == SUBREG)
{
int word_offset = (SUBREG_BYTE (op0) / UNITS_PER_WORD) + offset;
word = operand_subword_force (SUBREG_REG (op0), word_offset,
GET_MODE (SUBREG_REG (op0)));
offset = 0;
}
else if (GET_CODE (op0) == REG)
{
word = operand_subword_force (op0, offset, GET_MODE (op0));
offset = 0;
}
else
word = op0;
part = extract_fixed_bit_field (word_mode, word,
offset * unit / BITS_PER_UNIT,
thissize, thispos, 0, 1);
bitsdone += thissize;
if (BYTES_BIG_ENDIAN)
{
if (bitsize != bitsdone)
part = expand_shift (LSHIFT_EXPR, word_mode, part,
build_int_2 (bitsize - bitsdone, 0), 0, 1);
}
else
{
if (bitsdone != thissize)
part = expand_shift (LSHIFT_EXPR, word_mode, part,
build_int_2 (bitsdone - thissize, 0), 0, 1);
}
if (first)
result = part;
else
result = expand_binop (word_mode, ior_optab, part, result, NULL_RTX, 1,
OPTAB_LIB_WIDEN);
first = 0;
}
if (unsignedp)
return result;
result = expand_shift (LSHIFT_EXPR, word_mode, result,
build_int_2 (BITS_PER_WORD - bitsize, 0),
NULL_RTX, 0);
return expand_shift (RSHIFT_EXPR, word_mode, result,
build_int_2 (BITS_PER_WORD - bitsize, 0), NULL_RTX, 0);
}
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);
}
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);
}
rtx
expand_shift (code, mode, shifted, amount, target, unsignedp)
enum tree_code code;
enum machine_mode mode;
rtx shifted;
tree amount;
rtx target;
int unsignedp;
{
rtx op1, temp = 0;
int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR);
int try;
op1 = expand_expr (amount, NULL_RTX, VOIDmode, 0);
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
{
if (GET_CODE (op1) == CONST_INT
&& ((unsigned HOST_WIDE_INT) INTVAL (op1) >=
(unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (mode)))
op1 = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (op1)
% GET_MODE_BITSIZE (mode));
else if (GET_CODE (op1) == SUBREG
&& SUBREG_BYTE (op1) == 0)
op1 = SUBREG_REG (op1);
}
#endif
if (op1 == const0_rtx)
return shifted;
for (try = 0; temp == 0 && try < 3; try++)
{
enum optab_methods methods;
if (try == 0)
methods = OPTAB_DIRECT;
else if (try == 1)
methods = OPTAB_WIDEN;
else
methods = OPTAB_LIB_WIDEN;
if (rotate)
{
if (methods == OPTAB_WIDEN)
continue;
else if (methods == OPTAB_LIB_WIDEN)
{
rtx subtarget = target == shifted ? 0 : target;
rtx temp1;
tree type = TREE_TYPE (amount);
tree new_amount = make_tree (type, op1);
tree other_amount
= fold (build (MINUS_EXPR, type,
convert (type,
build_int_2 (GET_MODE_BITSIZE (mode),
0)),
amount));
shifted = force_reg (mode, shifted);
temp = expand_shift (left ? LSHIFT_EXPR : RSHIFT_EXPR,
mode, shifted, new_amount, subtarget, 1);
temp1 = expand_shift (left ? RSHIFT_EXPR : LSHIFT_EXPR,
mode, shifted, other_amount, 0, 1);
return expand_binop (mode, ior_optab, temp, temp1, target,
unsignedp, methods);
}
temp = expand_binop (mode,
left ? rotl_optab : rotr_optab,
shifted, op1, target, unsignedp, methods);
if (temp == 0 && GET_CODE (op1) == CONST_INT
&& INTVAL (op1) > 0
&& (unsigned int) INTVAL (op1) < GET_MODE_BITSIZE (mode))
temp = expand_binop (mode,
left ? rotr_optab : rotl_optab,
shifted,
GEN_INT (GET_MODE_BITSIZE (mode)
- INTVAL (op1)),
target, unsignedp, methods);
}
else if (unsignedp)
temp = expand_binop (mode,
left ? ashl_optab : lshr_optab,
shifted, op1, target, unsignedp, methods);
if (temp == 0 && ! rotate
&& (! unsignedp || (! left && methods == OPTAB_WIDEN)))
{
enum optab_methods methods1 = methods;
if (unsignedp)
methods1 = OPTAB_MUST_WIDEN;
temp = expand_binop (mode,
left ? ashl_optab : ashr_optab,
shifted, op1, target, unsignedp, methods1);
}
}
if (temp == 0)
abort ();
return temp;
}
enum alg_code { alg_zero, alg_m, alg_shift,
alg_add_t_m2, alg_sub_t_m2,
alg_add_factor, alg_sub_factor,
alg_add_t2_m, alg_sub_t2_m,
alg_add, alg_subtract, alg_factor, alg_shiftop };
struct algorithm
{
short cost;
short ops;
enum alg_code op[MAX_BITS_PER_WORD];
char log[MAX_BITS_PER_WORD];
};
static void synth_mult PARAMS ((struct algorithm *,
unsigned HOST_WIDE_INT,
int));
static unsigned HOST_WIDE_INT choose_multiplier PARAMS ((unsigned HOST_WIDE_INT,
int, int,
unsigned HOST_WIDE_INT *,
int *, int *));
static unsigned HOST_WIDE_INT invert_mod2n PARAMS ((unsigned HOST_WIDE_INT,
int));
static void
synth_mult (alg_out, t, cost_limit)
struct algorithm *alg_out;
unsigned HOST_WIDE_INT t;
int cost_limit;
{
int m;
struct algorithm *alg_in, *best_alg;
int cost;
unsigned HOST_WIDE_INT q;
alg_out->cost = cost_limit;
if (cost_limit <= 0)
return;
if (t == 1)
{
alg_out->ops = 1;
alg_out->cost = 0;
alg_out->op[0] = alg_m;
return;
}
if (t == 0)
{
if (zero_cost >= cost_limit)
return;
else
{
alg_out->ops = 1;
alg_out->cost = zero_cost;
alg_out->op[0] = alg_zero;
return;
}
}
alg_in = (struct algorithm *)alloca (sizeof (struct algorithm));
best_alg = (struct algorithm *)alloca (sizeof (struct algorithm));
if ((t & 1) == 0)
{
m = floor_log2 (t & -t);
if (m < BITS_PER_WORD)
{
q = t >> m;
cost = shift_cost[m];
synth_mult (alg_in, q, cost_limit - cost);
cost += alg_in->cost;
if (cost < cost_limit)
{
struct algorithm *x;
x = alg_in, alg_in = best_alg, best_alg = x;
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_shift;
cost_limit = cost;
}
}
}
if ((t & 1) != 0)
{
unsigned HOST_WIDE_INT w;
for (w = 1; (w & t) != 0; w <<= 1)
;
if (w == 0
|| (w > 2
&& t != 3))
{
cost = add_cost;
synth_mult (alg_in, t + 1, cost_limit - cost);
cost += alg_in->cost;
if (cost < cost_limit)
{
struct algorithm *x;
x = alg_in, alg_in = best_alg, best_alg = x;
best_alg->log[best_alg->ops] = 0;
best_alg->op[best_alg->ops] = alg_sub_t_m2;
cost_limit = cost;
}
}
else
{
cost = add_cost;
synth_mult (alg_in, t - 1, cost_limit - cost);
cost += alg_in->cost;
if (cost < cost_limit)
{
struct algorithm *x;
x = alg_in, alg_in = best_alg, best_alg = x;
best_alg->log[best_alg->ops] = 0;
best_alg->op[best_alg->ops] = alg_add_t_m2;
cost_limit = cost;
}
}
}
for (m = floor_log2 (t - 1); m >= 2; m--)
{
unsigned HOST_WIDE_INT d;
d = ((unsigned HOST_WIDE_INT) 1 << m) + 1;
if (t % d == 0 && t > d && m < BITS_PER_WORD)
{
cost = MIN (shiftadd_cost[m], add_cost + shift_cost[m]);
synth_mult (alg_in, t / d, cost_limit - cost);
cost += alg_in->cost;
if (cost < cost_limit)
{
struct algorithm *x;
x = alg_in, alg_in = best_alg, best_alg = x;
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_add_factor;
cost_limit = cost;
}
break;
}
d = ((unsigned HOST_WIDE_INT) 1 << m) - 1;
if (t % d == 0 && t > d && m < BITS_PER_WORD)
{
cost = MIN (shiftsub_cost[m], add_cost + shift_cost[m]);
synth_mult (alg_in, t / d, cost_limit - cost);
cost += alg_in->cost;
if (cost < cost_limit)
{
struct algorithm *x;
x = alg_in, alg_in = best_alg, best_alg = x;
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_sub_factor;
cost_limit = cost;
}
break;
}
}
if ((t & 1) != 0)
{
q = t - 1;
q = q & -q;
m = exact_log2 (q);
if (m >= 0 && m < BITS_PER_WORD)
{
cost = shiftadd_cost[m];
synth_mult (alg_in, (t - 1) >> m, cost_limit - cost);
cost += alg_in->cost;
if (cost < cost_limit)
{
struct algorithm *x;
x = alg_in, alg_in = best_alg, best_alg = x;
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_add_t2_m;
cost_limit = cost;
}
}
q = t + 1;
q = q & -q;
m = exact_log2 (q);
if (m >= 0 && m < BITS_PER_WORD)
{
cost = shiftsub_cost[m];
synth_mult (alg_in, (t + 1) >> m, cost_limit - cost);
cost += alg_in->cost;
if (cost < cost_limit)
{
struct algorithm *x;
x = alg_in, alg_in = best_alg, best_alg = x;
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_sub_t2_m;
cost_limit = cost;
}
}
}
if (cost_limit == alg_out->cost)
return;
if (best_alg->ops == MAX_BITS_PER_WORD)
return;
alg_out->ops = best_alg->ops + 1;
alg_out->cost = cost_limit;
memcpy (alg_out->op, best_alg->op,
alg_out->ops * sizeof *alg_out->op);
memcpy (alg_out->log, best_alg->log,
alg_out->ops * sizeof *alg_out->log);
}
rtx
expand_mult (mode, op0, op1, target, unsignedp)
enum machine_mode mode;
rtx op0, op1, target;
int unsignedp;
{
rtx const_op1 = op1;
if (GET_CODE (op1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op1)) == MODE_INT
&& HOST_BITS_PER_INT >= BITS_PER_WORD
&& CONST_DOUBLE_HIGH (op1) == 0)
const_op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
else if (HOST_BITS_PER_INT < GET_MODE_BITSIZE (mode)
&& GET_CODE (op1) == CONST_INT
&& INTVAL (op1) < 0)
const_op1 = 0;
if (const_op1 && GET_CODE (const_op1) == CONST_INT
&& (unsignedp || ! flag_trapv))
{
struct algorithm alg;
struct algorithm alg2;
HOST_WIDE_INT val = INTVAL (op1);
HOST_WIDE_INT val_so_far;
rtx insn;
int mult_cost;
enum {basic_variant, negate_variant, add_variant} variant = basic_variant;
op0 = force_reg (mode, op0);
mult_cost = rtx_cost (gen_rtx_MULT (mode, op0, op1), SET);
mult_cost = MIN (12 * add_cost, mult_cost);
synth_mult (&alg, val, mult_cost);
if (HOST_BITS_PER_INT >= GET_MODE_BITSIZE (mode))
{
synth_mult (&alg2, - val,
(alg.cost < mult_cost ? alg.cost : mult_cost) - negate_cost);
if (alg2.cost + negate_cost < alg.cost)
alg = alg2, variant = negate_variant;
}
synth_mult (&alg2, val - 1,
(alg.cost < mult_cost ? alg.cost : mult_cost) - add_cost);
if (alg2.cost + add_cost < alg.cost)
alg = alg2, variant = add_variant;
if (alg.cost < mult_cost)
{
int opno;
rtx accum, tem;
enum machine_mode nmode;
op0 = protect_from_queue (op0, 0);
if (GET_CODE (op0) == MEM)
op0 = force_reg (mode, op0);
if (alg.op[0] == alg_zero)
{
accum = copy_to_mode_reg (mode, const0_rtx);
val_so_far = 0;
}
else if (alg.op[0] == alg_m)
{
accum = copy_to_mode_reg (mode, op0);
val_so_far = 1;
}
else
abort ();
for (opno = 1; opno < alg.ops; opno++)
{
int log = alg.log[opno];
int preserve = preserve_subexpressions_p ();
rtx shift_subtarget = preserve ? 0 : accum;
rtx add_target
= (opno == alg.ops - 1 && target != 0 && variant != add_variant
&& ! preserve)
? target : 0;
rtx accum_target = preserve ? 0 : accum;
switch (alg.op[opno])
{
case alg_shift:
accum = expand_shift (LSHIFT_EXPR, mode, accum,
build_int_2 (log, 0), NULL_RTX, 0);
val_so_far <<= log;
break;
case alg_add_t_m2:
tem = expand_shift (LSHIFT_EXPR, mode, op0,
build_int_2 (log, 0), NULL_RTX, 0);
accum = force_operand (gen_rtx_PLUS (mode, accum, tem),
add_target
? add_target : accum_target);
val_so_far += (HOST_WIDE_INT) 1 << log;
break;
case alg_sub_t_m2:
tem = expand_shift (LSHIFT_EXPR, mode, op0,
build_int_2 (log, 0), NULL_RTX, 0);
accum = force_operand (gen_rtx_MINUS (mode, accum, tem),
add_target
? add_target : accum_target);
val_so_far -= (HOST_WIDE_INT) 1 << log;
break;
case alg_add_t2_m:
accum = expand_shift (LSHIFT_EXPR, mode, accum,
build_int_2 (log, 0), shift_subtarget,
0);
accum = force_operand (gen_rtx_PLUS (mode, accum, op0),
add_target
? add_target : accum_target);
val_so_far = (val_so_far << log) + 1;
break;
case alg_sub_t2_m:
accum = expand_shift (LSHIFT_EXPR, mode, accum,
build_int_2 (log, 0), shift_subtarget,
0);
accum = force_operand (gen_rtx_MINUS (mode, accum, op0),
add_target
? add_target : accum_target);
val_so_far = (val_so_far << log) - 1;
break;
case alg_add_factor:
tem = expand_shift (LSHIFT_EXPR, mode, accum,
build_int_2 (log, 0), NULL_RTX, 0);
accum = force_operand (gen_rtx_PLUS (mode, accum, tem),
add_target
? add_target : accum_target);
val_so_far += val_so_far << log;
break;
case alg_sub_factor:
tem = expand_shift (LSHIFT_EXPR, mode, accum,
build_int_2 (log, 0), NULL_RTX, 0);
accum = force_operand (gen_rtx_MINUS (mode, tem, accum),
(add_target ? add_target
: preserve ? 0 : tem));
val_so_far = (val_so_far << log) - val_so_far;
break;
default:
abort ();
}
tem = op0, nmode = mode;
if (GET_CODE (accum) == SUBREG)
{
nmode = GET_MODE (SUBREG_REG (accum));
tem = gen_lowpart (nmode, op0);
}
insn = get_last_insn ();
set_unique_reg_note (insn,
REG_EQUAL,
gen_rtx_MULT (nmode, tem,
GEN_INT (val_so_far)));
}
if (variant == negate_variant)
{
val_so_far = - val_so_far;
accum = expand_unop (mode, neg_optab, accum, target, 0);
}
else if (variant == add_variant)
{
val_so_far = val_so_far + 1;
accum = force_operand (gen_rtx_PLUS (mode, accum, op0), target);
}
if (val != val_so_far)
abort ();
return accum;
}
}
op0 = expand_binop (mode,
! unsignedp
&& flag_trapv && (GET_MODE_CLASS(mode) == MODE_INT)
? smulv_optab : smul_optab,
op0, op1, target, unsignedp, OPTAB_LIB_WIDEN);
if (op0 == 0)
abort ();
return op0;
}
int
ceil_log2 (x)
unsigned HOST_WIDE_INT x;
{
return floor_log2 (x - 1) + 1;
}
static
unsigned HOST_WIDE_INT
choose_multiplier (d, n, precision, multiplier_ptr, post_shift_ptr, lgup_ptr)
unsigned HOST_WIDE_INT d;
int n;
int precision;
unsigned HOST_WIDE_INT *multiplier_ptr;
int *post_shift_ptr;
int *lgup_ptr;
{
HOST_WIDE_INT mhigh_hi, mlow_hi;
unsigned HOST_WIDE_INT mhigh_lo, mlow_lo;
int lgup, post_shift;
int pow, pow2;
unsigned HOST_WIDE_INT nl, dummy1;
HOST_WIDE_INT nh, dummy2;
lgup = ceil_log2 (d);
if (lgup > n)
abort ();
pow = n + lgup;
pow2 = n + lgup - precision;
if (pow == 2 * HOST_BITS_PER_WIDE_INT)
{
abort ();
}
if (pow >= HOST_BITS_PER_WIDE_INT)
{
nh = (HOST_WIDE_INT) 1 << (pow - HOST_BITS_PER_WIDE_INT);
nl = 0;
}
else
{
nh = 0;
nl = (unsigned HOST_WIDE_INT) 1 << pow;
}
div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0,
&mlow_lo, &mlow_hi, &dummy1, &dummy2);
if (pow2 >= HOST_BITS_PER_WIDE_INT)
nh |= (HOST_WIDE_INT) 1 << (pow2 - HOST_BITS_PER_WIDE_INT);
else
nl |= (unsigned HOST_WIDE_INT) 1 << pow2;
div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0,
&mhigh_lo, &mhigh_hi, &dummy1, &dummy2);
if (mhigh_hi && nh - d >= d)
abort ();
if (mhigh_hi > 1 || mlow_hi > 1)
abort ();
if (! (mlow_hi < mhigh_hi || (mlow_hi == mhigh_hi && mlow_lo < mhigh_lo)))
abort ();
for (post_shift = lgup; post_shift > 0; post_shift--)
{
unsigned HOST_WIDE_INT ml_lo = (mlow_hi << (HOST_BITS_PER_WIDE_INT - 1)) | (mlow_lo >> 1);
unsigned HOST_WIDE_INT mh_lo = (mhigh_hi << (HOST_BITS_PER_WIDE_INT - 1)) | (mhigh_lo >> 1);
if (ml_lo >= mh_lo)
break;
mlow_hi = 0;
mlow_lo = ml_lo;
mhigh_hi = 0;
mhigh_lo = mh_lo;
}
*post_shift_ptr = post_shift;
*lgup_ptr = lgup;
if (n < HOST_BITS_PER_WIDE_INT)
{
unsigned HOST_WIDE_INT mask = ((unsigned HOST_WIDE_INT) 1 << n) - 1;
*multiplier_ptr = mhigh_lo & mask;
return mhigh_lo >= mask;
}
else
{
*multiplier_ptr = mhigh_lo;
return mhigh_hi;
}
}
static unsigned HOST_WIDE_INT
invert_mod2n (x, n)
unsigned HOST_WIDE_INT x;
int n;
{
unsigned HOST_WIDE_INT mask;
unsigned HOST_WIDE_INT y = x;
int nbit = 3;
mask = (n == HOST_BITS_PER_WIDE_INT
? ~(unsigned HOST_WIDE_INT) 0
: ((unsigned HOST_WIDE_INT) 1 << n) - 1);
while (nbit < n)
{
y = y * (2 - x*y) & mask;
nbit *= 2;
}
return y;
}
rtx
expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
enum machine_mode mode;
rtx adj_operand, op0, op1, target;
int unsignedp;
{
rtx tem;
enum rtx_code adj_code = unsignedp ? PLUS : MINUS;
tem = expand_shift (RSHIFT_EXPR, mode, op0,
build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
NULL_RTX, 0);
tem = expand_and (mode, tem, op1, NULL_RTX);
adj_operand
= force_operand (gen_rtx_fmt_ee (adj_code, mode, adj_operand, tem),
adj_operand);
tem = expand_shift (RSHIFT_EXPR, mode, op1,
build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
NULL_RTX, 0);
tem = expand_and (mode, tem, op0, NULL_RTX);
target = force_operand (gen_rtx_fmt_ee (adj_code, mode, adj_operand, tem),
target);
return target;
}
rtx
expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
enum machine_mode mode;
rtx op0, target;
unsigned HOST_WIDE_INT cnst1;
int unsignedp;
int max_cost;
{
enum machine_mode wider_mode = GET_MODE_WIDER_MODE (mode);
optab mul_highpart_optab;
optab moptab;
rtx tem;
int size = GET_MODE_BITSIZE (mode);
rtx op1, wide_op1;
if (size > HOST_BITS_PER_WIDE_INT)
abort ();
op1 = GEN_INT (trunc_int_for_mode (cnst1, mode));
wide_op1
= immed_double_const (cnst1,
(unsignedp
? (HOST_WIDE_INT) 0
: -(cnst1 >> (HOST_BITS_PER_WIDE_INT - 1))),
wider_mode);
if (size < BITS_PER_WORD
&& mul_cost[(int) wider_mode] + shift_cost[size-1] < max_cost)
{
op0 = convert_to_mode (wider_mode, op0, unsignedp);
tem = expand_mult (wider_mode, op0, wide_op1, NULL_RTX, 0);
tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
build_int_2 (size, 0), NULL_RTX, 1);
return convert_modes (mode, wider_mode, tem, unsignedp);
}
if (target == 0)
target = gen_reg_rtx (mode);
if (mul_highpart_cost[(int) mode] < max_cost)
{
mul_highpart_optab = unsignedp ? umul_highpart_optab : smul_highpart_optab;
target = expand_binop (mode, mul_highpart_optab,
op0, op1, target, unsignedp, OPTAB_DIRECT);
if (target)
return target;
}
if (size - 1 < BITS_PER_WORD
&& (mul_highpart_cost[(int) mode] + 2 * shift_cost[size-1] + 4 * add_cost
< max_cost))
{
mul_highpart_optab = unsignedp ? smul_highpart_optab : umul_highpart_optab;
target = expand_binop (mode, mul_highpart_optab,
op0, op1, target, unsignedp, OPTAB_DIRECT);
if (target)
return expand_mult_highpart_adjust (mode, target, op0,
op1, target, unsignedp);
}
moptab = unsignedp ? umul_widen_optab : smul_widen_optab;
if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
&& mul_widen_cost[(int) wider_mode] < max_cost)
{
op1 = force_reg (mode, op1);
goto try;
}
moptab = smul_optab;
if (smul_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
&& size - 1 < BITS_PER_WORD
&& mul_cost[(int) wider_mode] + shift_cost[size-1] < max_cost)
{
op1 = wide_op1;
goto try;
}
moptab = unsignedp ? smul_widen_optab : umul_widen_optab;
if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
&& size - 1 < BITS_PER_WORD
&& (mul_widen_cost[(int) wider_mode]
+ 2 * shift_cost[size-1] + 4 * add_cost < max_cost))
{
rtx regop1 = force_reg (mode, op1);
tem = expand_binop (wider_mode, moptab, op0, regop1,
NULL_RTX, ! unsignedp, OPTAB_WIDEN);
if (tem != 0)
{
tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
build_int_2 (size, 0), NULL_RTX, 1);
tem = convert_modes (mode, wider_mode, tem, unsignedp);
return expand_mult_highpart_adjust (mode, tem, op0, op1,
target, unsignedp);
}
}
return 0;
try:
tem = expand_binop (wider_mode, moptab, op0, op1,
NULL_RTX, unsignedp, OPTAB_WIDEN);
if (tem == 0)
return 0;
if (mode == word_mode)
{
return gen_highpart (mode, tem);
}
else
{
tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
build_int_2 (size, 0), NULL_RTX, 1);
return convert_modes (mode, wider_mode, tem, unsignedp);
}
}
#define EXACT_POWER_OF_2_OR_ZERO_P(x) (((x) & ((x) - 1)) == 0)
rtx
expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
int rem_flag;
enum tree_code code;
enum machine_mode mode;
rtx op0, op1, target;
int unsignedp;
{
enum machine_mode compute_mode;
rtx tquotient;
rtx quotient = 0, remainder = 0;
rtx last;
int size;
rtx insn, set;
optab optab1, optab2;
int op1_is_constant, op1_is_pow2;
int max_cost, extra_cost;
static HOST_WIDE_INT last_div_const = 0;
op1_is_constant = GET_CODE (op1) == CONST_INT;
op1_is_pow2 = (op1_is_constant
&& ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
|| (! unsignedp && EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1))))));
if (op1 == const1_rtx)
return rem_flag ? const0_rtx : op0;
if (! unsignedp && op1 == constm1_rtx)
{
if (rem_flag)
return const0_rtx;
return expand_unop (mode, flag_trapv && GET_MODE_CLASS(mode) == MODE_INT
? negv_optab : neg_optab, op0, target, 0);
}
if (target
&& ((REG_P (target) && REG_FUNCTION_VALUE_P (target))
|| ((rem_flag || op1_is_constant)
&& (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;
optab1 = (op1_is_pow2 ? (unsignedp ? lshr_optab : ashr_optab)
: (unsignedp ? udiv_optab : sdiv_optab));
optab2 = (op1_is_pow2 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab));
for (compute_mode = mode; compute_mode != VOIDmode;
compute_mode = GET_MODE_WIDER_MODE (compute_mode))
if (optab1->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing
|| optab2->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing)
break;
if (compute_mode == VOIDmode)
for (compute_mode = mode; compute_mode != VOIDmode;
compute_mode = GET_MODE_WIDER_MODE (compute_mode))
if (optab1->handlers[(int) compute_mode].libfunc
|| optab2->handlers[(int) compute_mode].libfunc)
break;
if (compute_mode == VOIDmode)
compute_mode = mode;
if (target && GET_MODE (target) == compute_mode)
tquotient = target;
else
tquotient = gen_reg_rtx (compute_mode);
size = GET_MODE_BITSIZE (compute_mode);
#if 0
size = GET_MODE_BITSIZE (mode);
#endif
max_cost = div_cost[(int) compute_mode]
- (rem_flag && ! (last_div_const != 0 && op1_is_constant
&& INTVAL (op1) == last_div_const)
? mul_cost[(int) compute_mode] + add_cost : 0);
last_div_const = ! rem_flag && op1_is_constant ? INTVAL (op1) : 0;
if (compute_mode != mode)
{
op0 = convert_modes (compute_mode, mode, op0, unsignedp);
op1 = convert_modes (compute_mode, mode, op1, unsignedp);
op1_is_constant = GET_CODE (op1) == CONST_INT;
op1_is_pow2 = (op1_is_constant
&& ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
|| (! unsignedp
&& EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1)))))) ;
}
if (GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0))
op0 = force_reg (compute_mode, op0);
if (GET_CODE (op1) == MEM && MEM_VOLATILE_P (op1))
op1 = force_reg (compute_mode, op1);
if (rem_flag || op1_is_constant)
op0 = force_reg (compute_mode, op0);
last = get_last_insn ();
if (unsignedp)
{
if (code == FLOOR_DIV_EXPR)
code = TRUNC_DIV_EXPR;
if (code == FLOOR_MOD_EXPR)
code = TRUNC_MOD_EXPR;
if (code == EXACT_DIV_EXPR && op1_is_pow2)
code = TRUNC_DIV_EXPR;
}
if (op1 != const0_rtx)
switch (code)
{
case TRUNC_MOD_EXPR:
case TRUNC_DIV_EXPR:
if (op1_is_constant)
{
if (unsignedp)
{
unsigned HOST_WIDE_INT mh, ml;
int pre_shift, post_shift;
int dummy;
unsigned HOST_WIDE_INT d = INTVAL (op1);
if (EXACT_POWER_OF_2_OR_ZERO_P (d))
{
pre_shift = floor_log2 (d);
if (rem_flag)
{
remainder
= expand_binop (compute_mode, and_optab, op0,
GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1),
remainder, 1,
OPTAB_LIB_WIDEN);
if (remainder)
return gen_lowpart (mode, remainder);
}
quotient = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (pre_shift, 0),
tquotient, 1);
}
else if (size <= HOST_BITS_PER_WIDE_INT)
{
if (d >= ((unsigned HOST_WIDE_INT) 1 << (size - 1)))
{
quotient = emit_store_flag (tquotient, GEU, op0, op1,
compute_mode, 1, 1);
if (quotient == 0)
goto fail1;
}
else
{
mh = choose_multiplier (d, size, size,
&ml, &post_shift, &dummy);
if (mh != 0 && (d & 1) == 0)
{
pre_shift = floor_log2 (d & -d);
mh = choose_multiplier (d >> pre_shift, size,
size - pre_shift,
&ml, &post_shift, &dummy);
if (mh)
abort ();
}
else
pre_shift = 0;
if (mh != 0)
{
rtx t1, t2, t3, t4;
if (post_shift - 1 >= BITS_PER_WORD)
goto fail1;
extra_cost = (shift_cost[post_shift - 1]
+ shift_cost[1] + 2 * add_cost);
t1 = expand_mult_highpart (compute_mode, op0, ml,
NULL_RTX, 1,
max_cost - extra_cost);
if (t1 == 0)
goto fail1;
t2 = force_operand (gen_rtx_MINUS (compute_mode,
op0, t1),
NULL_RTX);
t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
build_int_2 (1, 0), NULL_RTX,1);
t4 = force_operand (gen_rtx_PLUS (compute_mode,
t1, t3),
NULL_RTX);
quotient
= expand_shift (RSHIFT_EXPR, compute_mode, t4,
build_int_2 (post_shift - 1, 0),
tquotient, 1);
}
else
{
rtx t1, t2;
if (pre_shift >= BITS_PER_WORD
|| post_shift >= BITS_PER_WORD)
goto fail1;
t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (pre_shift, 0),
NULL_RTX, 1);
extra_cost = (shift_cost[pre_shift]
+ shift_cost[post_shift]);
t2 = expand_mult_highpart (compute_mode, t1, ml,
NULL_RTX, 1,
max_cost - extra_cost);
if (t2 == 0)
goto fail1;
quotient
= expand_shift (RSHIFT_EXPR, compute_mode, t2,
build_int_2 (post_shift, 0),
tquotient, 1);
}
}
}
else
break;
insn = get_last_insn ();
if (insn != last
&& (set = single_set (insn)) != 0
&& SET_DEST (set) == quotient)
set_unique_reg_note (insn,
REG_EQUAL,
gen_rtx_UDIV (compute_mode, op0, op1));
}
else
{
unsigned HOST_WIDE_INT ml;
int lgup, post_shift;
HOST_WIDE_INT d = INTVAL (op1);
unsigned HOST_WIDE_INT abs_d = d >= 0 ? d : -d;
if (rem_flag && d < 0)
{
d = abs_d;
op1 = GEN_INT (trunc_int_for_mode (abs_d, compute_mode));
}
if (d == 1)
quotient = op0;
else if (d == -1)
quotient = expand_unop (compute_mode, neg_optab, op0,
tquotient, 0);
else if (abs_d == (unsigned HOST_WIDE_INT) 1 << (size - 1))
{
quotient = emit_store_flag (tquotient, EQ, op0, op1,
compute_mode, 1, 1);
if (quotient == 0)
goto fail1;
}
else if (EXACT_POWER_OF_2_OR_ZERO_P (d)
&& (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap)
&& (((rem_flag ? smod_optab : sdiv_optab)
->handlers[(int) compute_mode].insn_code
!= CODE_FOR_nothing)
|| (sdivmod_optab->handlers[(int) compute_mode]
.insn_code != CODE_FOR_nothing)))
;
else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d))
{
lgup = floor_log2 (abs_d);
if (BRANCH_COST < 1 || (abs_d != 2 && BRANCH_COST < 3))
{
rtx label = gen_label_rtx ();
rtx t1;
t1 = copy_to_mode_reg (compute_mode, op0);
do_cmp_and_jump (t1, const0_rtx, GE,
compute_mode, label);
expand_inc (t1, GEN_INT (trunc_int_for_mode
(abs_d - 1, compute_mode)));
emit_label (label);
quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1,
build_int_2 (lgup, 0),
tquotient, 0);
}
else
{
rtx t1, t2, t3;
t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (size - 1, 0),
NULL_RTX, 0);
t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1,
build_int_2 (size - lgup, 0),
NULL_RTX, 1);
t3 = force_operand (gen_rtx_PLUS (compute_mode,
op0, t2),
NULL_RTX);
quotient = expand_shift (RSHIFT_EXPR, compute_mode, t3,
build_int_2 (lgup, 0),
tquotient, 0);
}
if (d < 0)
{
insn = get_last_insn ();
if (insn != last
&& (set = single_set (insn)) != 0
&& SET_DEST (set) == quotient
&& abs_d < ((unsigned HOST_WIDE_INT) 1
<< (HOST_BITS_PER_WIDE_INT - 1)))
set_unique_reg_note (insn,
REG_EQUAL,
gen_rtx_DIV (compute_mode,
op0,
GEN_INT
(trunc_int_for_mode
(abs_d,
compute_mode))));
quotient = expand_unop (compute_mode, neg_optab,
quotient, quotient, 0);
}
}
else if (size <= HOST_BITS_PER_WIDE_INT)
{
choose_multiplier (abs_d, size, size - 1,
&ml, &post_shift, &lgup);
if (ml < (unsigned HOST_WIDE_INT) 1 << (size - 1))
{
rtx t1, t2, t3;
if (post_shift >= BITS_PER_WORD
|| size - 1 >= BITS_PER_WORD)
goto fail1;
extra_cost = (shift_cost[post_shift]
+ shift_cost[size - 1] + add_cost);
t1 = expand_mult_highpart (compute_mode, op0, ml,
NULL_RTX, 0,
max_cost - extra_cost);
if (t1 == 0)
goto fail1;
t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1,
build_int_2 (post_shift, 0), NULL_RTX, 0);
t3 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (size - 1, 0), NULL_RTX, 0);
if (d < 0)
quotient
= force_operand (gen_rtx_MINUS (compute_mode,
t3, t2),
tquotient);
else
quotient
= force_operand (gen_rtx_MINUS (compute_mode,
t2, t3),
tquotient);
}
else
{
rtx t1, t2, t3, t4;
if (post_shift >= BITS_PER_WORD
|| size - 1 >= BITS_PER_WORD)
goto fail1;
ml |= (~(unsigned HOST_WIDE_INT) 0) << (size - 1);
extra_cost = (shift_cost[post_shift]
+ shift_cost[size - 1] + 2 * add_cost);
t1 = expand_mult_highpart (compute_mode, op0, ml,
NULL_RTX, 0,
max_cost - extra_cost);
if (t1 == 0)
goto fail1;
t2 = force_operand (gen_rtx_PLUS (compute_mode,
t1, op0),
NULL_RTX);
t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
build_int_2 (post_shift, 0),
NULL_RTX, 0);
t4 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (size - 1, 0),
NULL_RTX, 0);
if (d < 0)
quotient
= force_operand (gen_rtx_MINUS (compute_mode,
t4, t3),
tquotient);
else
quotient
= force_operand (gen_rtx_MINUS (compute_mode,
t3, t4),
tquotient);
}
}
else
break;
insn = get_last_insn ();
if (insn != last
&& (set = single_set (insn)) != 0
&& SET_DEST (set) == quotient)
set_unique_reg_note (insn,
REG_EQUAL,
gen_rtx_DIV (compute_mode, op0, op1));
}
break;
}
fail1:
delete_insns_since (last);
break;
case FLOOR_DIV_EXPR:
case FLOOR_MOD_EXPR:
if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size)
{
unsigned HOST_WIDE_INT mh, ml;
int pre_shift, lgup, post_shift;
HOST_WIDE_INT d = INTVAL (op1);
if (d > 0)
{
if (EXACT_POWER_OF_2_OR_ZERO_P (d))
{
pre_shift = floor_log2 (d);
if (rem_flag)
{
remainder = expand_binop (compute_mode, and_optab, op0,
GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1),
remainder, 0, OPTAB_LIB_WIDEN);
if (remainder)
return gen_lowpart (mode, remainder);
}
quotient = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (pre_shift, 0),
tquotient, 0);
}
else
{
rtx t1, t2, t3, t4;
mh = choose_multiplier (d, size, size - 1,
&ml, &post_shift, &lgup);
if (mh)
abort ();
if (post_shift < BITS_PER_WORD
&& size - 1 < BITS_PER_WORD)
{
t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (size - 1, 0),
NULL_RTX, 0);
t2 = expand_binop (compute_mode, xor_optab, op0, t1,
NULL_RTX, 0, OPTAB_WIDEN);
extra_cost = (shift_cost[post_shift]
+ shift_cost[size - 1] + 2 * add_cost);
t3 = expand_mult_highpart (compute_mode, t2, ml,
NULL_RTX, 1,
max_cost - extra_cost);
if (t3 != 0)
{
t4 = expand_shift (RSHIFT_EXPR, compute_mode, t3,
build_int_2 (post_shift, 0),
NULL_RTX, 1);
quotient = expand_binop (compute_mode, xor_optab,
t4, t1, tquotient, 0,
OPTAB_WIDEN);
}
}
}
}
else
{
rtx nsign, t1, t2, t3, t4;
t1 = force_operand (gen_rtx_PLUS (compute_mode,
op0, constm1_rtx), NULL_RTX);
t2 = expand_binop (compute_mode, ior_optab, op0, t1, NULL_RTX,
0, OPTAB_WIDEN);
nsign = expand_shift (RSHIFT_EXPR, compute_mode, t2,
build_int_2 (size - 1, 0), NULL_RTX, 0);
t3 = force_operand (gen_rtx_MINUS (compute_mode, t1, nsign),
NULL_RTX);
t4 = expand_divmod (0, TRUNC_DIV_EXPR, compute_mode, t3, op1,
NULL_RTX, 0);
if (t4)
{
rtx t5;
t5 = expand_unop (compute_mode, one_cmpl_optab, nsign,
NULL_RTX, 0);
quotient = force_operand (gen_rtx_PLUS (compute_mode,
t4, t5),
tquotient);
}
}
}
if (quotient != 0)
break;
delete_insns_since (last);
if (target == 0 || GET_MODE (target) != compute_mode)
target = gen_reg_rtx (compute_mode);
if (rem_flag)
{
remainder
= GET_CODE (target) == REG ? target : gen_reg_rtx (compute_mode);
quotient = gen_reg_rtx (compute_mode);
}
else
{
quotient
= GET_CODE (target) == REG ? target : gen_reg_rtx (compute_mode);
remainder = gen_reg_rtx (compute_mode);
}
if (expand_twoval_binop (sdivmod_optab, op0, op1,
quotient, remainder, 0))
{
rtx tem;
rtx label = gen_label_rtx ();
do_cmp_and_jump (remainder, const0_rtx, EQ, compute_mode, label);
tem = expand_binop (compute_mode, xor_optab, op0, op1,
NULL_RTX, 0, OPTAB_WIDEN);
do_cmp_and_jump (tem, const0_rtx, GE, compute_mode, label);
expand_dec (quotient, const1_rtx);
expand_inc (remainder, op1);
emit_label (label);
return gen_lowpart (mode, rem_flag ? remainder : quotient);
}
{
rtx label1, label2, label3, label4, label5;
rtx adjusted_op0;
rtx tem;
quotient = gen_reg_rtx (compute_mode);
adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
label1 = gen_label_rtx ();
label2 = gen_label_rtx ();
label3 = gen_label_rtx ();
label4 = gen_label_rtx ();
label5 = gen_label_rtx ();
do_cmp_and_jump (op1, const0_rtx, LT, compute_mode, label2);
do_cmp_and_jump (adjusted_op0, const0_rtx, LT, compute_mode, label1);
tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
quotient, 0, OPTAB_LIB_WIDEN);
if (tem != quotient)
emit_move_insn (quotient, tem);
emit_jump_insn (gen_jump (label5));
emit_barrier ();
emit_label (label1);
expand_inc (adjusted_op0, const1_rtx);
emit_jump_insn (gen_jump (label4));
emit_barrier ();
emit_label (label2);
do_cmp_and_jump (adjusted_op0, const0_rtx, GT, compute_mode, label3);
tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
quotient, 0, OPTAB_LIB_WIDEN);
if (tem != quotient)
emit_move_insn (quotient, tem);
emit_jump_insn (gen_jump (label5));
emit_barrier ();
emit_label (label3);
expand_dec (adjusted_op0, const1_rtx);
emit_label (label4);
tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
quotient, 0, OPTAB_LIB_WIDEN);
if (tem != quotient)
emit_move_insn (quotient, tem);
expand_dec (quotient, const1_rtx);
emit_label (label5);
}
break;
case CEIL_DIV_EXPR:
case CEIL_MOD_EXPR:
if (unsignedp)
{
if (op1_is_constant && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1)))
{
rtx t1, t2, t3;
unsigned HOST_WIDE_INT d = INTVAL (op1);
t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (floor_log2 (d), 0),
tquotient, 1);
t2 = expand_binop (compute_mode, and_optab, op0,
GEN_INT (d - 1),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
t3 = gen_reg_rtx (compute_mode);
t3 = emit_store_flag (t3, NE, t2, const0_rtx,
compute_mode, 1, 1);
if (t3 == 0)
{
rtx lab;
lab = gen_label_rtx ();
do_cmp_and_jump (t2, const0_rtx, EQ, compute_mode, lab);
expand_inc (t1, const1_rtx);
emit_label (lab);
quotient = t1;
}
else
quotient = force_operand (gen_rtx_PLUS (compute_mode,
t1, t3),
tquotient);
break;
}
if (target == 0 || GET_MODE (target) != compute_mode)
target = gen_reg_rtx (compute_mode);
if (rem_flag)
{
remainder = (GET_CODE (target) == REG
? target : gen_reg_rtx (compute_mode));
quotient = gen_reg_rtx (compute_mode);
}
else
{
quotient = (GET_CODE (target) == REG
? target : gen_reg_rtx (compute_mode));
remainder = gen_reg_rtx (compute_mode);
}
if (expand_twoval_binop (udivmod_optab, op0, op1, quotient,
remainder, 1))
{
rtx label = gen_label_rtx ();
do_cmp_and_jump (remainder, const0_rtx, EQ,
compute_mode, label);
expand_inc (quotient, const1_rtx);
expand_dec (remainder, op1);
emit_label (label);
return gen_lowpart (mode, rem_flag ? remainder : quotient);
}
{
rtx label1, label2;
rtx adjusted_op0, tem;
quotient = gen_reg_rtx (compute_mode);
adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
label1 = gen_label_rtx ();
label2 = gen_label_rtx ();
do_cmp_and_jump (adjusted_op0, const0_rtx, NE,
compute_mode, label1);
emit_move_insn (quotient, const0_rtx);
emit_jump_insn (gen_jump (label2));
emit_barrier ();
emit_label (label1);
expand_dec (adjusted_op0, const1_rtx);
tem = expand_binop (compute_mode, udiv_optab, adjusted_op0, op1,
quotient, 1, OPTAB_LIB_WIDEN);
if (tem != quotient)
emit_move_insn (quotient, tem);
expand_inc (quotient, const1_rtx);
emit_label (label2);
}
}
else
{
if (op1_is_constant && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
&& INTVAL (op1) >= 0)
{
rtx t1, t2, t3;
unsigned HOST_WIDE_INT d = INTVAL (op1);
t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (floor_log2 (d), 0),
tquotient, 0);
t2 = expand_binop (compute_mode, and_optab, op0,
GEN_INT (d - 1),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
t3 = gen_reg_rtx (compute_mode);
t3 = emit_store_flag (t3, NE, t2, const0_rtx,
compute_mode, 1, 1);
if (t3 == 0)
{
rtx lab;
lab = gen_label_rtx ();
do_cmp_and_jump (t2, const0_rtx, EQ, compute_mode, lab);
expand_inc (t1, const1_rtx);
emit_label (lab);
quotient = t1;
}
else
quotient = force_operand (gen_rtx_PLUS (compute_mode,
t1, t3),
tquotient);
break;
}
if (target == 0 || GET_MODE (target) != compute_mode)
target = gen_reg_rtx (compute_mode);
if (rem_flag)
{
remainder= (GET_CODE (target) == REG
? target : gen_reg_rtx (compute_mode));
quotient = gen_reg_rtx (compute_mode);
}
else
{
quotient = (GET_CODE (target) == REG
? target : gen_reg_rtx (compute_mode));
remainder = gen_reg_rtx (compute_mode);
}
if (expand_twoval_binop (sdivmod_optab, op0, op1, quotient,
remainder, 0))
{
rtx tem;
rtx label = gen_label_rtx ();
do_cmp_and_jump (remainder, const0_rtx, EQ,
compute_mode, label);
tem = expand_binop (compute_mode, xor_optab, op0, op1,
NULL_RTX, 0, OPTAB_WIDEN);
do_cmp_and_jump (tem, const0_rtx, LT, compute_mode, label);
expand_inc (quotient, const1_rtx);
expand_dec (remainder, op1);
emit_label (label);
return gen_lowpart (mode, rem_flag ? remainder : quotient);
}
{
rtx label1, label2, label3, label4, label5;
rtx adjusted_op0;
rtx tem;
quotient = gen_reg_rtx (compute_mode);
adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
label1 = gen_label_rtx ();
label2 = gen_label_rtx ();
label3 = gen_label_rtx ();
label4 = gen_label_rtx ();
label5 = gen_label_rtx ();
do_cmp_and_jump (op1, const0_rtx, LT, compute_mode, label2);
do_cmp_and_jump (adjusted_op0, const0_rtx, GT,
compute_mode, label1);
tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
quotient, 0, OPTAB_LIB_WIDEN);
if (tem != quotient)
emit_move_insn (quotient, tem);
emit_jump_insn (gen_jump (label5));
emit_barrier ();
emit_label (label1);
expand_dec (adjusted_op0, const1_rtx);
emit_jump_insn (gen_jump (label4));
emit_barrier ();
emit_label (label2);
do_cmp_and_jump (adjusted_op0, const0_rtx, LT,
compute_mode, label3);
tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
quotient, 0, OPTAB_LIB_WIDEN);
if (tem != quotient)
emit_move_insn (quotient, tem);
emit_jump_insn (gen_jump (label5));
emit_barrier ();
emit_label (label3);
expand_inc (adjusted_op0, const1_rtx);
emit_label (label4);
tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
quotient, 0, OPTAB_LIB_WIDEN);
if (tem != quotient)
emit_move_insn (quotient, tem);
expand_inc (quotient, const1_rtx);
emit_label (label5);
}
}
break;
case EXACT_DIV_EXPR:
if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size)
{
HOST_WIDE_INT d = INTVAL (op1);
unsigned HOST_WIDE_INT ml;
int pre_shift;
rtx t1;
pre_shift = floor_log2 (d & -d);
ml = invert_mod2n (d >> pre_shift, size);
t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
build_int_2 (pre_shift, 0), NULL_RTX, unsignedp);
quotient = expand_mult (compute_mode, t1,
GEN_INT (trunc_int_for_mode
(ml, compute_mode)),
NULL_RTX, 0);
insn = get_last_insn ();
set_unique_reg_note (insn,
REG_EQUAL,
gen_rtx_fmt_ee (unsignedp ? UDIV : DIV,
compute_mode,
op0, op1));
}
break;
case ROUND_DIV_EXPR:
case ROUND_MOD_EXPR:
if (unsignedp)
{
rtx tem;
rtx label;
label = gen_label_rtx ();
quotient = gen_reg_rtx (compute_mode);
remainder = gen_reg_rtx (compute_mode);
if (expand_twoval_binop (udivmod_optab, op0, op1, quotient, remainder, 1) == 0)
{
rtx tem;
quotient = expand_binop (compute_mode, udiv_optab, op0, op1,
quotient, 1, OPTAB_LIB_WIDEN);
tem = expand_mult (compute_mode, quotient, op1, NULL_RTX, 1);
remainder = expand_binop (compute_mode, sub_optab, op0, tem,
remainder, 1, OPTAB_LIB_WIDEN);
}
tem = plus_constant (op1, -1);
tem = expand_shift (RSHIFT_EXPR, compute_mode, tem,
build_int_2 (1, 0), NULL_RTX, 1);
do_cmp_and_jump (remainder, tem, LEU, compute_mode, label);
expand_inc (quotient, const1_rtx);
expand_dec (remainder, op1);
emit_label (label);
}
else
{
rtx abs_rem, abs_op1, tem, mask;
rtx label;
label = gen_label_rtx ();
quotient = gen_reg_rtx (compute_mode);
remainder = gen_reg_rtx (compute_mode);
if (expand_twoval_binop (sdivmod_optab, op0, op1, quotient, remainder, 0) == 0)
{
rtx tem;
quotient = expand_binop (compute_mode, sdiv_optab, op0, op1,
quotient, 0, OPTAB_LIB_WIDEN);
tem = expand_mult (compute_mode, quotient, op1, NULL_RTX, 0);
remainder = expand_binop (compute_mode, sub_optab, op0, tem,
remainder, 0, OPTAB_LIB_WIDEN);
}
abs_rem = expand_abs (compute_mode, remainder, NULL_RTX, 1, 0);
abs_op1 = expand_abs (compute_mode, op1, NULL_RTX, 1, 0);
tem = expand_shift (LSHIFT_EXPR, compute_mode, abs_rem,
build_int_2 (1, 0), NULL_RTX, 1);
do_cmp_and_jump (tem, abs_op1, LTU, compute_mode, label);
tem = expand_binop (compute_mode, xor_optab, op0, op1,
NULL_RTX, 0, OPTAB_WIDEN);
mask = expand_shift (RSHIFT_EXPR, compute_mode, tem,
build_int_2 (size - 1, 0), NULL_RTX, 0);
tem = expand_binop (compute_mode, xor_optab, mask, const1_rtx,
NULL_RTX, 0, OPTAB_WIDEN);
tem = expand_binop (compute_mode, sub_optab, tem, mask,
NULL_RTX, 0, OPTAB_WIDEN);
expand_inc (quotient, tem);
tem = expand_binop (compute_mode, xor_optab, mask, op1,
NULL_RTX, 0, OPTAB_WIDEN);
tem = expand_binop (compute_mode, sub_optab, tem, mask,
NULL_RTX, 0, OPTAB_WIDEN);
expand_dec (remainder, tem);
emit_label (label);
}
return gen_lowpart (mode, rem_flag ? remainder : quotient);
default:
abort ();
}
if (quotient == 0)
{
if (target && GET_MODE (target) != compute_mode)
target = 0;
if (rem_flag)
{
remainder
= sign_expand_binop (compute_mode, umod_optab, smod_optab,
op0, op1, target,
unsignedp,
((optab2->handlers[(int) compute_mode].insn_code
!= CODE_FOR_nothing)
? OPTAB_DIRECT : OPTAB_WIDEN));
if (remainder == 0)
{
remainder = gen_reg_rtx (compute_mode);
if (! expand_twoval_binop ((unsignedp
? udivmod_optab
: sdivmod_optab),
op0, op1,
NULL_RTX, remainder, unsignedp))
remainder = 0;
}
if (remainder)
return gen_lowpart (mode, remainder);
}
quotient
= sign_expand_binop (compute_mode, udiv_optab, sdiv_optab,
op0, op1, rem_flag ? NULL_RTX : target,
unsignedp,
((optab2->handlers[(int) compute_mode].insn_code
!= CODE_FOR_nothing)
? OPTAB_DIRECT : OPTAB_WIDEN));
if (quotient == 0)
{
quotient = gen_reg_rtx (compute_mode);
if (! expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab,
op0, op1,
quotient, NULL_RTX, unsignedp))
{
quotient = 0;
if (! rem_flag)
quotient = sign_expand_binop (compute_mode,
udiv_optab, sdiv_optab,
op0, op1, target,
unsignedp, OPTAB_LIB_WIDEN);
}
}
}
if (rem_flag)
{
if (target && GET_MODE (target) != compute_mode)
target = 0;
if (quotient == 0)
remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab,
op0, op1, target,
unsignedp, OPTAB_LIB_WIDEN);
else
{
remainder = expand_mult (compute_mode, quotient, op1,
NULL_RTX, unsignedp);
remainder = expand_binop (compute_mode, sub_optab, op0,
remainder, target, unsignedp,
OPTAB_LIB_WIDEN);
}
}
return gen_lowpart (mode, rem_flag ? remainder : quotient);
}
tree
make_tree (type, x)
tree type;
rtx x;
{
tree t;
switch (GET_CODE (x))
{
case CONST_INT:
t = build_int_2 (INTVAL (x),
(TREE_UNSIGNED (type)
&& (GET_MODE_BITSIZE (TYPE_MODE (type)) < HOST_BITS_PER_WIDE_INT))
|| INTVAL (x) >= 0 ? 0 : -1);
TREE_TYPE (t) = type;
return t;
case CONST_DOUBLE:
if (GET_MODE (x) == VOIDmode)
{
t = build_int_2 (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x));
TREE_TYPE (t) = type;
}
else
{
REAL_VALUE_TYPE d;
REAL_VALUE_FROM_CONST_DOUBLE (d, x);
t = build_real (type, d);
}
return t;
case CONST_VECTOR:
{
int i, units;
rtx elt;
tree t = NULL_TREE;
units = CONST_VECTOR_NUNITS (x);
for (i = units - 1; i >= 0; --i)
{
elt = CONST_VECTOR_ELT (x, i);
t = tree_cons (NULL_TREE, make_tree (type, elt), t);
}
return build_vector (type, t);
}
case PLUS:
return fold (build (PLUS_EXPR, type, make_tree (type, XEXP (x, 0)),
make_tree (type, XEXP (x, 1))));
case MINUS:
return fold (build (MINUS_EXPR, type, make_tree (type, XEXP (x, 0)),
make_tree (type, XEXP (x, 1))));
case NEG:
return fold (build1 (NEGATE_EXPR, type, make_tree (type, XEXP (x, 0))));
case MULT:
return fold (build (MULT_EXPR, type, make_tree (type, XEXP (x, 0)),
make_tree (type, XEXP (x, 1))));
case ASHIFT:
return fold (build (LSHIFT_EXPR, type, make_tree (type, XEXP (x, 0)),
make_tree (type, XEXP (x, 1))));
case LSHIFTRT:
return fold (convert (type,
build (RSHIFT_EXPR, unsigned_type (type),
make_tree (unsigned_type (type),
XEXP (x, 0)),
make_tree (type, XEXP (x, 1)))));
case ASHIFTRT:
return fold (convert (type,
build (RSHIFT_EXPR, signed_type (type),
make_tree (signed_type (type), XEXP (x, 0)),
make_tree (type, XEXP (x, 1)))));
case DIV:
if (TREE_CODE (type) != REAL_TYPE)
t = signed_type (type);
else
t = type;
return fold (convert (type,
build (TRUNC_DIV_EXPR, t,
make_tree (t, XEXP (x, 0)),
make_tree (t, XEXP (x, 1)))));
case UDIV:
t = unsigned_type (type);
return fold (convert (type,
build (TRUNC_DIV_EXPR, t,
make_tree (t, XEXP (x, 0)),
make_tree (t, XEXP (x, 1)))));
default:
t = make_node (RTL_EXPR);
TREE_TYPE (t) = type;
#ifdef POINTERS_EXTEND_UNSIGNED
if (POINTER_TYPE_P (type) && GET_MODE (x) != TYPE_MODE (type))
x = convert_memory_address (TYPE_MODE (type), x);
#endif
RTL_EXPR_RTL (t) = x;
RTL_EXPR_SEQUENCE (t) = 0;
return t;
}
}
rtx
expand_mult_add (x, target, mult, add, mode, unsignedp)
rtx x, target, mult, add;
enum machine_mode mode;
int unsignedp;
{
tree type = type_for_mode (mode, unsignedp);
tree add_type = (GET_MODE (add) == VOIDmode
? type : type_for_mode (GET_MODE (add), unsignedp));
tree result = fold (build (PLUS_EXPR, type,
fold (build (MULT_EXPR, type,
make_tree (type, x),
make_tree (type, mult))),
make_tree (add_type, add)));
return expand_expr (result, target, VOIDmode, 0);
}
rtx
expand_and (mode, op0, op1, target)
enum machine_mode mode;
rtx op0, op1, target;
{
rtx tem = 0;
if (GET_MODE (op0) == VOIDmode && GET_MODE (op1) == VOIDmode)
tem = simplify_binary_operation (AND, mode, op0, op1);
if (tem == 0)
tem = expand_binop (mode, and_optab, op0, op1, target, 0, OPTAB_LIB_WIDEN);
if (target == 0)
target = tem;
else if (tem != target)
emit_move_insn (target, tem);
return target;
}
rtx
emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
rtx target;
enum rtx_code code;
rtx op0, op1;
enum machine_mode mode;
int unsignedp;
int normalizep;
{
rtx subtarget;
enum insn_code icode;
enum machine_mode compare_mode;
enum machine_mode target_mode = GET_MODE (target);
rtx tem;
rtx last = get_last_insn ();
rtx pattern, comparison;
op0 = protect_from_queue (op0, 0);
op1 = protect_from_queue (op1, 0);
if (unsignedp)
code = unsigned_condition (code);
if (swap_commutative_operands_p (op0, op1))
{
tem = op0;
op0 = op1;
op1 = tem;
code = swap_condition (code);
}
if (mode == VOIDmode)
mode = GET_MODE (op0);
switch (code)
{
case LT:
if (op1 == const1_rtx)
op1 = const0_rtx, code = LE;
break;
case LE:
if (op1 == constm1_rtx)
op1 = const0_rtx, code = LT;
break;
case GE:
if (op1 == const1_rtx)
op1 = const0_rtx, code = GT;
break;
case GT:
if (op1 == constm1_rtx)
op1 = const0_rtx, code = GE;
break;
case GEU:
if (op1 == const1_rtx)
op1 = const0_rtx, code = NE;
break;
case LTU:
if (op1 == const1_rtx)
op1 = const0_rtx, code = EQ;
break;
default:
break;
}
if (GET_MODE_BITSIZE (mode) == BITS_PER_WORD * 2
&& GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx
&& (GET_CODE (op0) != MEM || ! MEM_VOLATILE_P (op0)))
{
if (code == EQ || code == NE)
{
rtx op0h = gen_highpart (word_mode, op0);
rtx op0l = gen_lowpart (word_mode, op0);
rtx op0both = expand_binop (word_mode, ior_optab, op0h, op0l,
NULL_RTX, unsignedp, OPTAB_DIRECT);
if (op0both != 0)
return emit_store_flag (target, code, op0both, op1, word_mode,
unsignedp, normalizep);
}
else if (code == LT || code == GE)
return emit_store_flag (target, code, gen_highpart (word_mode, op0),
op1, word_mode, unsignedp, normalizep);
}
icode = setcc_gen_code[(int) code];
if (op1 == const0_rtx && (code == LT || code == GE)
&& GET_MODE_CLASS (mode) == MODE_INT
&& (normalizep || STORE_FLAG_VALUE == 1
|| (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
== (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))))
{
subtarget = target;
if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (mode))
{
op0 = protect_from_queue (op0, 0);
op0 = convert_modes (target_mode, mode, op0, 0);
mode = target_mode;
}
if (target_mode != mode)
subtarget = 0;
if (code == GE)
op0 = expand_unop (mode, one_cmpl_optab, op0,
((STORE_FLAG_VALUE == 1 || normalizep)
? 0 : subtarget), 0);
if (STORE_FLAG_VALUE == 1 || normalizep)
op0 = expand_shift (RSHIFT_EXPR, mode, op0,
size_int (GET_MODE_BITSIZE (mode) - 1),
subtarget, normalizep != -1);
if (mode != target_mode)
op0 = convert_modes (target_mode, mode, op0, 0);
return op0;
}
if (icode != CODE_FOR_nothing)
{
insn_operand_predicate_fn pred;
emit_queue ();
do_pending_stack_adjust ();
last = get_last_insn ();
comparison
= compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX);
if (GET_CODE (comparison) == CONST_INT)
return (comparison == const0_rtx ? const0_rtx
: normalizep == 1 ? const1_rtx
: normalizep == -1 ? constm1_rtx
: const_true_rtx);
code = GET_CODE (comparison);
compare_mode = insn_data[(int) icode].operand[0].mode;
subtarget = target;
pred = insn_data[(int) icode].operand[0].predicate;
if (preserve_subexpressions_p ()
|| ! (*pred) (subtarget, compare_mode))
subtarget = gen_reg_rtx (compare_mode);
pattern = GEN_FCN (icode) (subtarget);
if (pattern)
{
emit_insn (pattern);
if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (compare_mode))
{
convert_move (target, subtarget,
(GET_MODE_BITSIZE (compare_mode)
<= HOST_BITS_PER_WIDE_INT)
&& 0 == (STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (compare_mode) -1))));
op0 = target;
compare_mode = target_mode;
}
else
op0 = subtarget;
if (preserve_subexpressions_p ())
subtarget = 0;
if (normalizep == 0 || normalizep == STORE_FLAG_VALUE)
;
else if (- normalizep == STORE_FLAG_VALUE)
op0 = expand_unop (compare_mode, neg_optab, op0, subtarget, 0);
else if (GET_MODE_BITSIZE (compare_mode) <= HOST_BITS_PER_WIDE_INT
&& (STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (compare_mode) - 1))))
op0 = expand_shift (RSHIFT_EXPR, compare_mode, op0,
size_int (GET_MODE_BITSIZE (compare_mode) - 1),
subtarget, normalizep == 1);
else if (STORE_FLAG_VALUE & 1)
{
op0 = expand_and (compare_mode, op0, const1_rtx, subtarget);
if (normalizep == -1)
op0 = expand_unop (compare_mode, neg_optab, op0, op0, 0);
}
else
abort ();
if (target_mode != compare_mode)
{
convert_move (target, op0, 0);
return target;
}
else
return op0;
}
}
delete_insns_since (last);
subtarget = (!flag_expensive_optimizations
&& (target_mode == mode)) ? target : NULL_RTX;
if (BRANCH_COST > 0
&& GET_MODE_CLASS (mode) == MODE_INT && (code == EQ || code == NE)
&& op1 != const0_rtx)
{
tem = expand_binop (mode, xor_optab, op0, op1, subtarget, 1,
OPTAB_WIDEN);
if (tem == 0)
tem = expand_binop (mode, sub_optab, op0, op1, subtarget, 1,
OPTAB_WIDEN);
if (tem != 0)
tem = emit_store_flag (target, code, tem, const0_rtx,
mode, unsignedp, normalizep);
if (tem == 0)
delete_insns_since (last);
return tem;
}
if (BRANCH_COST == 0
|| GET_MODE_CLASS (mode) != MODE_INT || op1 != const0_rtx
|| (code != EQ && code != NE
&& (BRANCH_COST <= 1 || (code != LE && code != GT))))
return 0;
if (normalizep == 0)
{
if (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
normalizep = STORE_FLAG_VALUE;
else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
== (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
;
else
return 0;
}
tem = 0;
if (code == LE)
{
if (rtx_equal_p (subtarget, op0))
subtarget = 0;
tem = expand_binop (mode, sub_optab, op0, const1_rtx, subtarget, 0,
OPTAB_WIDEN);
if (tem)
tem = expand_binop (mode, ior_optab, op0, tem, subtarget, 0,
OPTAB_WIDEN);
}
if (code == GT)
{
if (rtx_equal_p (subtarget, op0))
subtarget = 0;
tem = expand_shift (RSHIFT_EXPR, mode, op0,
size_int (GET_MODE_BITSIZE (mode) - 1),
subtarget, 0);
tem = expand_binop (mode, sub_optab, tem, op0, subtarget, 0,
OPTAB_WIDEN);
}
if (code == EQ || code == NE)
{
if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
tem = expand_unop (mode, abs_optab, op0, subtarget, 1);
else if (ffs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
tem = expand_unop (mode, ffs_optab, op0, subtarget, 1);
else if (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
{
op0 = protect_from_queue (op0, 0);
tem = convert_modes (word_mode, mode, op0, 1);
mode = word_mode;
}
if (tem != 0)
{
if (code == EQ)
tem = expand_binop (mode, sub_optab, tem, const1_rtx, subtarget,
0, OPTAB_WIDEN);
else
tem = expand_unop (mode, neg_optab, tem, subtarget, 0);
}
if (tem == 0 && (code == NE || BRANCH_COST > 1))
{
if (rtx_equal_p (subtarget, op0))
subtarget = 0;
tem = expand_unop (mode, neg_optab, op0, subtarget, 0);
tem = expand_binop (mode, ior_optab, tem, op0, subtarget, 0,
OPTAB_WIDEN);
if (tem && code == EQ)
tem = expand_unop (mode, one_cmpl_optab, tem, subtarget, 0);
}
}
if (tem && normalizep)
tem = expand_shift (RSHIFT_EXPR, mode, tem,
size_int (GET_MODE_BITSIZE (mode) - 1),
subtarget, normalizep == 1);
if (tem)
{
if (GET_MODE (tem) != target_mode)
{
convert_move (target, tem, 0);
tem = target;
}
else if (!subtarget)
{
emit_move_insn (target, tem);
tem = target;
}
}
else
delete_insns_since (last);
return tem;
}
rtx
emit_store_flag_force (target, code, op0, op1, mode, unsignedp, normalizep)
rtx target;
enum rtx_code code;
rtx op0, op1;
enum machine_mode mode;
int unsignedp;
int normalizep;
{
rtx tem, label;
tem = emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep);
if (tem != 0)
return tem;
if (normalizep == 0)
normalizep = 1;
if (GET_CODE (target) != REG
|| reg_mentioned_p (target, op0) || reg_mentioned_p (target, op1))
target = gen_reg_rtx (GET_MODE (target));
emit_move_insn (target, const1_rtx);
label = gen_label_rtx ();
do_compare_rtx_and_jump (op0, op1, code, unsignedp, mode, NULL_RTX,
NULL_RTX, label);
emit_move_insn (target, const0_rtx);
emit_label (label);
return target;
}
static void
do_cmp_and_jump (arg1, arg2, op, mode, label)
rtx arg1, arg2, label;
enum rtx_code op;
enum machine_mode mode;
{
if (GET_MODE_CLASS (mode) == MODE_INT
&& ! can_compare_p (op, mode, ccp_jump))
{
rtx label2 = gen_label_rtx ();
switch (op)
{
case LTU:
do_jump_by_parts_greater_rtx (mode, 1, arg2, arg1, label2, label);
break;
case LEU:
do_jump_by_parts_greater_rtx (mode, 1, arg1, arg2, label, label2);
break;
case LT:
do_jump_by_parts_greater_rtx (mode, 0, arg2, arg1, label2, label);
break;
case GT:
do_jump_by_parts_greater_rtx (mode, 0, arg1, arg2, label2, label);
break;
case GE:
do_jump_by_parts_greater_rtx (mode, 0, arg2, arg1, label, label2);
break;
case EQ:
if (arg2 != const0_rtx || mode != GET_MODE(arg1))
abort ();
do_jump_by_parts_equality_rtx (arg1, label2, label);
break;
case NE:
if (arg2 != const0_rtx || mode != GET_MODE(arg1))
abort ();
do_jump_by_parts_equality_rtx (arg1, label, label2);
break;
default:
abort ();
}
emit_label (label2);
}
else
emit_cmp_and_jump_insns (arg1, arg2, op, NULL_RTX, mode, 0, label);
}