/* Medium-level subroutines: convert bit-field store and extract
and shifts, multiplies and divides to rtl instructions.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "toplev.h"
#include "rtl.h"
#include "tree.h"
static void do_cmp_and_jump PARAMS ((rtx, rtx, enum rtx_code,
enum machine_mode, rtx));
-/* Non-zero means divides or modulus operations are relatively cheap for
+/* Nonzero means divides or modulus operations are relatively cheap for
powers of two, so don't use branches; emit the operation instead.
Usually, this will mean that the MD file will emit non-branch
sequences. */
void
init_expmed ()
{
- /* This is "some random pseudo register" for purposes of calling recog
- to see what insns exist. */
- rtx reg = gen_rtx_REG (word_mode, 10000);
- rtx shift_insn, shiftadd_insn, shiftsub_insn;
+ rtx reg, shift_insn, shiftadd_insn, shiftsub_insn;
int dummy;
int m;
enum machine_mode mode, wider_mode;
start_sequence ();
+ /* This is "some random pseudo register" for purposes of calling recog
+ to see what insns exist. */
reg = gen_rtx_REG (word_mode, 10000);
zero_cost = rtx_cost (const0_rtx, 0);
structure fields. */
if (GET_MODE_CLASS (GET_MODE (value)) != MODE_INT
&& GET_MODE_CLASS (GET_MODE (value)) != MODE_PARTIAL_INT)
- value = gen_lowpart (word_mode, value);
+ value = gen_lowpart ((GET_MODE (value) == VOIDmode
+ ? word_mode : int_mode_for_mode (GET_MODE (value))),
+ value);
/* Now OFFSET is nonzero only if OP0 is memory
and is therefore always measured in bytes. */
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 += SUBREG_BYTE (op0) * BITS_PER_UNIT;
+ if (bitpos > unit)
{
- bitpos += inner_size - outer_size;
- if (bitpos > unit)
- {
- offset += (bitpos / unit);
- bitpos %= unit;
- }
+ offset += (bitpos / unit);
+ bitpos %= unit;
}
-
op0 = SUBREG_REG (op0);
}
set_mem_expr (op0, 0);
}
- /* ??? We currently assume TARGET is at least as big as BITSIZE.
- If that's wrong, the solution is to test for it and set TARGET to 0
- if needed. */
+ /* Extraction of a full-word or multi-word value from a structure
+ in a register or aligned memory can be done with just a SUBREG.
+ A subword value in the least significant part of a register
+ can also be extracted with a SUBREG. For this, we need the
+ byte offset of the value in op0. */
+
+ byte_offset = bitpos / BITS_PER_UNIT + offset * UNITS_PER_WORD;
/* If OP0 is a register, BITPOS must count within a word.
But as we have it, it counts within whatever size OP0 now has.
&& unit > GET_MODE_BITSIZE (GET_MODE (op0)))
bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
- /* Extracting a full-word or multi-word value
- from a structure in a register or aligned memory.
- This can be done with just SUBREG.
- So too extracting a subword value in
- the least significant part of the register. */
-
- byte_offset = (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
- + (offset * UNITS_PER_WORD);
+ /* ??? We currently assume TARGET is at least as big as BITSIZE.
+ If that's wrong, the solution is to test for it and set TARGET to 0
+ if needed. */
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
- /* ??? The big endian test here is wrong. This is correct
- if the value is in a register, and if mode_for_size is not
- the same mode as op0. This causes us to get unnecessarily
- inefficient code from the Thumb port when -mbig-endian. */
- && (BYTES_BIG_ENDIAN
- ? bitpos + bitsize == BITS_PER_WORD
- : bitpos == 0))))
+ if (((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode)
+ && bitpos % BITS_PER_WORD == 0)
+ || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode
+ /* ??? The big endian test here is wrong. This is correct
+ if the value is in a register, and if mode_for_size is not
+ the same mode as op0. This causes us to get unnecessarily
+ inefficient code from the Thumb port when -mbig-endian. */
+ && (BYTES_BIG_ENDIAN
+ ? bitpos + bitsize == BITS_PER_WORD
+ : bitpos == 0)))
+ && ((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)))))
{
if (mode1 != GET_MODE (op0))
{
{
HOST_WIDE_INT masklow, maskhigh;
- if (bitpos < HOST_BITS_PER_WIDE_INT)
+ if (bitsize == 0)
+ masklow = 0;
+ else if (bitpos < HOST_BITS_PER_WIDE_INT)
masklow = (HOST_WIDE_INT) -1 << bitpos;
else
masklow = 0;
else
maskhigh = (HOST_WIDE_INT) -1 << (bitpos - HOST_BITS_PER_WIDE_INT);
- if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT)
+ if (bitsize == 0)
+ maskhigh = 0;
+ else if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT)
maskhigh &= ((unsigned HOST_WIDE_INT) -1
>> (2 * HOST_BITS_PER_WIDE_INT - bitpos - bitsize));
else
the result is exact for inputs up to 0x1fffffff.
The input range can be reduced by using cross-sum rules.
For odd divisors >= 3, the following table gives right shift counts
- so that if an number is shifted by an integer multiple of the given
+ so that if a number is shifted by an integer multiple of the given
amount, the remainder stays the same:
2, 4, 3, 6, 10, 12, 4, 8, 18, 6, 11, 20, 18, 0, 5, 10, 12, 0, 12, 20,
14, 12, 23, 21, 8, 0, 20, 18, 0, 0, 6, 12, 0, 22, 0, 18, 20, 30, 0, 0,
int size;
rtx insn, set;
optab optab1, optab2;
- int op1_is_constant, op1_is_pow2;
+ int op1_is_constant, op1_is_pow2 = 0;
int max_cost, extra_cost;
static HOST_WIDE_INT last_div_const = 0;
+ static HOST_WIDE_INT ext_op1;
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_is_constant)
+ {
+ ext_op1 = INTVAL (op1);
+ if (unsignedp)
+ ext_op1 &= GET_MODE_MASK (mode);
+ op1_is_pow2 = ((EXACT_POWER_OF_2_OR_ZERO_P (ext_op1)
+ || (! unsignedp && EXACT_POWER_OF_2_OR_ZERO_P (-ext_op1))));
+ }
/*
This is the structure of expand_divmod:
not straightforward to generalize this. Maybe we should make an array
of possible modes in init_expmed? Save this for GCC 2.7. */
- optab1 = (op1_is_pow2 ? (unsignedp ? lshr_optab : ashr_optab)
+ optab1 = ((op1_is_pow2 && op1 != const0_rtx)
+ ? (unsignedp ? lshr_optab : ashr_optab)
: (unsignedp ? udiv_optab : sdiv_optab));
- optab2 = (op1_is_pow2 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab));
+ optab2 = ((op1_is_pow2 && op1 != const0_rtx)
+ ? optab1
+ : (unsignedp ? udivmod_optab : sdivmod_optab));
for (compute_mode = mode; compute_mode != VOIDmode;
compute_mode = GET_MODE_WIDER_MODE (compute_mode))
unsigned HOST_WIDE_INT mh, ml;
int pre_shift, post_shift;
int dummy;
- unsigned HOST_WIDE_INT d = INTVAL (op1);
+ unsigned HOST_WIDE_INT d = (INTVAL (op1)
+ & GET_MODE_MASK (compute_mode));
if (EXACT_POWER_OF_2_OR_ZERO_P (d))
{
build (TRUNC_DIV_EXPR, t,
make_tree (t, XEXP (x, 0)),
make_tree (t, XEXP (x, 1)))));
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ t = (*lang_hooks.types.type_for_mode) (GET_MODE (XEXP (x, 0)),
+ GET_CODE (x) == ZERO_EXTEND);
+ return fold (convert (type, make_tree (t, XEXP (x, 0))));
+
default:
t = make_node (RTL_EXPR);
TREE_TYPE (t) = type;
MODE is the machine mode for the computation.
X and MULT must have mode MODE. ADD may have a different mode.
So can X (defaults to same as MODE).
- UNSIGNEDP is non-zero to do unsigned multiplication. */
+ UNSIGNEDP is nonzero to do unsigned multiplication. */
bool
const_mult_add_overflow_p (x, mult, add, mode, unsignedp)
MODE is the machine mode for the computation.
X and MULT must have mode MODE. ADD may have a different mode.
So can X (defaults to same as MODE).
- UNSIGNEDP is non-zero to do unsigned multiplication.
+ UNSIGNEDP is nonzero to do unsigned multiplication.
This may emit insns. */
rtx
{
if (code == EQ || code == NE)
{
+ rtx op00, op01, op0both;
+
/* Do a logical OR of the two words and compare the result. */
- 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);
+ op00 = simplify_gen_subreg (word_mode, op0, mode, 0);
+ op01 = simplify_gen_subreg (word_mode, op0, mode, UNITS_PER_WORD);
+ op0both = expand_binop (word_mode, ior_optab, op00, op01,
+ 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)
- /* If testing the sign bit, can just test on high word. */
- return emit_store_flag (target, code, gen_highpart (word_mode, op0),
- op1, word_mode, unsignedp, normalizep);
+ {
+ rtx op0h;
+
+ /* If testing the sign bit, can just test on high word. */
+ op0h = simplify_gen_subreg (word_mode, op0, mode,
+ subreg_highpart_offset (word_mode, mode));
+ return emit_store_flag (target, code, op0h, op1, word_mode,
+ unsignedp, normalizep);
+ }
}
/* From now on, we won't change CODE, so set ICODE now. */
&& (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)))))
+ == (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))))
{
subtarget = target;
if (code == EQ || code == NE)
{
/* For EQ or NE, one way to do the comparison is to apply an operation
- that converts the operand into a positive number if it is non-zero
+ that converts the operand into a positive number if it is nonzero
or zero if it was originally zero. Then, for EQ, we subtract 1 and
for NE we negate. This puts the result in the sign bit. Then we
normalize with a shift, if needed.