/* Medium-level subroutines: convert bit-field store and extract
and shifts, multiplies and divides to rtl instructions.
- Copyright (C) 1987-2017 Free Software Foundation, Inc.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
This file is part of GCC.
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+/* Work around tree-optimization/91825. */
+#pragma GCC diagnostic warning "-Wmaybe-uninitialized"
#include "config.h"
#include "system.h"
static bool store_integral_bit_field (rtx, opt_scalar_int_mode,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
- unsigned HOST_WIDE_INT,
- unsigned HOST_WIDE_INT,
+ poly_uint64, poly_uint64,
machine_mode, rtx, bool, bool);
static void store_fixed_bit_field (rtx, opt_scalar_int_mode,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
- unsigned HOST_WIDE_INT,
- unsigned HOST_WIDE_INT,
+ poly_uint64, poly_uint64,
rtx, scalar_int_mode, bool);
static void store_fixed_bit_field_1 (rtx, scalar_int_mode,
unsigned HOST_WIDE_INT,
static void store_split_bit_field (rtx, opt_scalar_int_mode,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
- unsigned HOST_WIDE_INT,
- unsigned HOST_WIDE_INT,
+ poly_uint64, poly_uint64,
rtx, scalar_int_mode, bool);
+static rtx extract_integral_bit_field (rtx, opt_scalar_int_mode,
+ unsigned HOST_WIDE_INT,
+ unsigned HOST_WIDE_INT, int, rtx,
+ machine_mode, machine_mode, bool, bool);
static rtx extract_fixed_bit_field (machine_mode, rtx, opt_scalar_int_mode,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, rtx, int, bool);
adjust_bit_field_mem_for_reg (enum extraction_pattern pattern,
rtx op0, HOST_WIDE_INT bitsize,
HOST_WIDE_INT bitnum,
- unsigned HOST_WIDE_INT bitregion_start,
- unsigned HOST_WIDE_INT bitregion_end,
+ poly_uint64 bitregion_start,
+ poly_uint64 bitregion_end,
machine_mode fieldmode,
unsigned HOST_WIDE_INT *new_bitnum)
{
offset is then BITNUM / BITS_PER_UNIT. */
static bool
-lowpart_bit_field_p (unsigned HOST_WIDE_INT bitnum,
- unsigned HOST_WIDE_INT bitsize,
+lowpart_bit_field_p (poly_uint64 bitnum, poly_uint64 bitsize,
machine_mode struct_mode)
{
- unsigned HOST_WIDE_INT regsize = REGMODE_NATURAL_SIZE (struct_mode);
+ poly_uint64 regsize = REGMODE_NATURAL_SIZE (struct_mode);
if (BYTES_BIG_ENDIAN)
- return (bitnum % BITS_PER_UNIT == 0
- && (bitnum + bitsize == GET_MODE_BITSIZE (struct_mode)
- || (bitnum + bitsize) % (regsize * BITS_PER_UNIT) == 0));
+ return (multiple_p (bitnum, BITS_PER_UNIT)
+ && (known_eq (bitnum + bitsize, GET_MODE_BITSIZE (struct_mode))
+ || multiple_p (bitnum + bitsize,
+ regsize * BITS_PER_UNIT)));
else
- return bitnum % (regsize * BITS_PER_UNIT) == 0;
+ return multiple_p (bitnum, regsize * BITS_PER_UNIT);
}
/* Return true if -fstrict-volatile-bitfields applies to an access of OP0
strict_volatile_bitfield_p (rtx op0, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
scalar_int_mode fieldmode,
- unsigned HOST_WIDE_INT bitregion_start,
- unsigned HOST_WIDE_INT bitregion_end)
+ poly_uint64 bitregion_start,
+ poly_uint64 bitregion_end)
{
unsigned HOST_WIDE_INT modesize = GET_MODE_BITSIZE (fieldmode);
return false;
/* Check for cases where the C++ memory model applies. */
- if (bitregion_end != 0
- && (bitnum - bitnum % modesize < bitregion_start
- || bitnum - bitnum % modesize + modesize - 1 > bitregion_end))
+ if (maybe_ne (bitregion_end, 0U)
+ && (maybe_lt (bitnum - bitnum % modesize, bitregion_start)
+ || maybe_gt (bitnum - bitnum % modesize + modesize - 1,
+ bitregion_end)))
return false;
return true;
unsigned HOST_WIDE_INT bitnum,
rtx value, scalar_int_mode value_mode)
{
- struct expand_operand ops[4];
+ class expand_operand ops[4];
rtx value1;
rtx xop0 = op0;
rtx_insn *last = get_last_insn ();
static bool
store_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
- unsigned HOST_WIDE_INT bitregion_start,
- unsigned HOST_WIDE_INT bitregion_end,
+ poly_uint64 bitregion_start, poly_uint64 bitregion_end,
machine_mode fieldmode,
rtx value, bool reverse, bool fallback_p)
{
&& known_eq (bitsize, GET_MODE_BITSIZE (innermode))
&& multiple_p (bitnum, GET_MODE_BITSIZE (innermode), &pos))
{
- struct expand_operand ops[3];
+ class expand_operand ops[3];
enum insn_code icode = optab_handler (vec_set_optab, outermode);
create_fixed_operand (&ops[0], op0);
In the latter case, use subreg on the rhs side, not lhs. */
rtx sub;
HOST_WIDE_INT regnum;
- HOST_WIDE_INT regsize = REGMODE_NATURAL_SIZE (GET_MODE (op0));
+ poly_uint64 regsize = REGMODE_NATURAL_SIZE (GET_MODE (op0));
if (known_eq (bitnum, 0U)
&& known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (op0))))
{
if (MEM_P (op0))
op0 = adjust_bitfield_address_size (op0, op0_mode.else_blk (),
0, MEM_SIZE (op0));
+ else if (!op0_mode.exists ())
+ {
+ if (ibitnum == 0
+ && known_eq (ibitsize, GET_MODE_BITSIZE (GET_MODE (op0)))
+ && MEM_P (value)
+ && !reverse)
+ {
+ value = adjust_address (value, GET_MODE (op0), 0);
+ emit_move_insn (op0, value);
+ return true;
+ }
+ if (!fallback_p)
+ return false;
+ rtx temp = assign_stack_temp (GET_MODE (op0),
+ GET_MODE_SIZE (GET_MODE (op0)));
+ emit_move_insn (temp, op0);
+ store_bit_field_1 (temp, bitsize, bitnum, 0, 0, fieldmode, value,
+ reverse, fallback_p);
+ emit_move_insn (op0, temp);
+ return true;
+ }
else
op0 = gen_lowpart (op0_mode.require (), op0);
}
store_integral_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
- unsigned HOST_WIDE_INT bitregion_start,
- unsigned HOST_WIDE_INT bitregion_end,
+ poly_uint64 bitregion_start,
+ poly_uint64 bitregion_end,
machine_mode fieldmode,
rtx value, bool reverse, bool fallback_p)
{
if (!MEM_P (op0)
&& !reverse
&& lowpart_bit_field_p (bitnum, bitsize, op0_mode.require ())
- && bitsize == GET_MODE_BITSIZE (fieldmode)
+ && known_eq (bitsize, GET_MODE_BITSIZE (fieldmode))
&& optab_handler (movstrict_optab, fieldmode) != CODE_FOR_nothing)
{
- struct expand_operand ops[2];
+ class expand_operand ops[2];
enum insn_code icode = optab_handler (movstrict_optab, fieldmode);
rtx arg0 = op0;
unsigned HOST_WIDE_INT subreg_off;
However, only do that if the value is not BLKmode. */
const bool backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;
- unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
- unsigned int i;
+ const int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
rtx_insn *last;
/* This is the mode we must force value to, so that there will be enough
value_mode = smallest_int_mode_for_size (nwords * BITS_PER_WORD);
last = get_last_insn ();
- for (i = 0; i < nwords; i++)
+ for (int i = 0; i < nwords; i++)
{
- /* If I is 0, use the low-order word in both field and target;
- if I is 1, use the next to lowest word; and so on. */
- unsigned int wordnum = (backwards
- ? GET_MODE_SIZE (value_mode) / UNITS_PER_WORD
- - i - 1
- : i);
- unsigned int bit_offset = (backwards ^ reverse
- ? MAX ((int) bitsize - ((int) i + 1)
- * BITS_PER_WORD,
- 0)
- : (int) i * BITS_PER_WORD);
- rtx value_word = operand_subword_force (value, wordnum, value_mode);
- unsigned HOST_WIDE_INT new_bitsize =
- MIN (BITS_PER_WORD, bitsize - i * BITS_PER_WORD);
-
- /* If the remaining chunk doesn't have full wordsize we have
- to make sure that for big-endian machines the higher order
- bits are used. */
- if (new_bitsize < BITS_PER_WORD && BYTES_BIG_ENDIAN && !backwards)
- {
- int shift = BITS_PER_WORD - new_bitsize;
- rtx shift_rtx = gen_int_shift_amount (word_mode, shift);
- value_word = simplify_expand_binop (word_mode, lshr_optab,
- value_word, shift_rtx,
- NULL_RTX, true,
- OPTAB_LIB_WIDEN);
- }
+ /* Number of bits to be stored in this iteration, i.e. BITS_PER_WORD
+ except maybe for the last iteration. */
+ const unsigned HOST_WIDE_INT new_bitsize
+ = MIN (BITS_PER_WORD, bitsize - i * BITS_PER_WORD);
+ /* Bit offset from the starting bit number in the target. */
+ const unsigned int bit_offset
+ = backwards ^ reverse
+ ? MAX ((int) bitsize - (i + 1) * BITS_PER_WORD, 0)
+ : i * BITS_PER_WORD;
+ /* Starting word number in the value. */
+ const unsigned int wordnum
+ = backwards
+ ? GET_MODE_SIZE (value_mode) / UNITS_PER_WORD - (i + 1)
+ : i;
+ /* The chunk of the value in word_mode. We use bit-field extraction
+ in BLKmode to handle unaligned memory references and to shift the
+ last chunk right on big-endian machines if need be. */
+ rtx value_word
+ = fieldmode == BLKmode
+ ? extract_bit_field (value, new_bitsize, wordnum * BITS_PER_WORD,
+ 1, NULL_RTX, word_mode, word_mode, false,
+ NULL)
+ : operand_subword_force (value, wordnum, value_mode);
if (!store_bit_field_1 (op0, new_bitsize,
bitnum + bit_offset,
void
store_bit_field (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
- unsigned HOST_WIDE_INT bitregion_start,
- unsigned HOST_WIDE_INT bitregion_end,
+ poly_uint64 bitregion_start, poly_uint64 bitregion_end,
machine_mode fieldmode,
rtx value, bool reverse)
{
/* Under the C++0x memory model, we must not touch bits outside the
bit region. Adjust the address to start at the beginning of the
bit region. */
- if (MEM_P (str_rtx) && bitregion_start > 0)
+ if (MEM_P (str_rtx) && maybe_ne (bitregion_start, 0U))
{
scalar_int_mode best_mode;
machine_mode addr_mode = VOIDmode;
- HOST_WIDE_INT offset;
-
- gcc_assert ((bitregion_start % BITS_PER_UNIT) == 0);
- offset = bitregion_start / BITS_PER_UNIT;
+ poly_uint64 offset = exact_div (bitregion_start, BITS_PER_UNIT);
bitnum -= bitregion_start;
poly_int64 size = bits_to_bytes_round_up (bitnum + bitsize);
bitregion_end -= bitregion_start;
store_fixed_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
- unsigned HOST_WIDE_INT bitregion_start,
- unsigned HOST_WIDE_INT bitregion_end,
+ poly_uint64 bitregion_start, poly_uint64 bitregion_end,
rtx value, scalar_int_mode value_mode, bool reverse)
{
/* There is a case not handled here:
store_split_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitpos,
- unsigned HOST_WIDE_INT bitregion_start,
- unsigned HOST_WIDE_INT bitregion_end,
+ poly_uint64 bitregion_start, poly_uint64 bitregion_end,
rtx value, scalar_int_mode value_mode, bool reverse)
{
unsigned int unit, total_bits, bitsdone = 0;
UNIT close to the end of the region as needed. If op0 is a REG
or SUBREG of REG, don't do this, as there can't be data races
on a register and we can expand shorter code in some cases. */
- if (bitregion_end
+ if (maybe_ne (bitregion_end, 0U)
&& unit > BITS_PER_UNIT
- && bitpos + bitsdone - thispos + unit > bitregion_end + 1
+ && maybe_gt (bitpos + bitsdone - thispos + unit, bitregion_end + 1)
&& !REG_P (op0)
&& (GET_CODE (op0) != SUBREG || !REG_P (SUBREG_REG (op0))))
{
int unsignedp, rtx target,
machine_mode mode, machine_mode tmode)
{
- struct expand_operand ops[4];
+ class expand_operand ops[4];
rtx spec_target = target;
rtx spec_target_subreg = 0;
scalar_int_mode ext_mode = extv->field_mode;
return NULL_RTX;
}
+/* See whether it would be valid to extract the part of OP0 described
+ by BITNUM and BITSIZE into a value of mode MODE using a subreg
+ operation. Return the subreg if so, otherwise return null. */
+
+static rtx
+extract_bit_field_as_subreg (machine_mode mode, rtx op0,
+ poly_uint64 bitsize, poly_uint64 bitnum)
+{
+ poly_uint64 bytenum;
+ if (multiple_p (bitnum, BITS_PER_UNIT, &bytenum)
+ && known_eq (bitsize, GET_MODE_BITSIZE (mode))
+ && lowpart_bit_field_p (bitnum, bitsize, GET_MODE (op0))
+ && TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (op0)))
+ return simplify_gen_subreg (mode, op0, GET_MODE (op0), bytenum);
+ return NULL_RTX;
+}
+
/* A subroutine of extract_bit_field, with the same arguments.
If FALLBACK_P is true, fall back to extract_fixed_bit_field
if we can find no other means of implementing the operation.
if FALLBACK_P is false, return NULL instead. */
static rtx
-extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
- unsigned HOST_WIDE_INT bitnum, int unsignedp, rtx target,
- machine_mode mode, machine_mode tmode,
- bool reverse, bool fallback_p, rtx *alt_rtl)
+extract_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
+ int unsignedp, rtx target, machine_mode mode,
+ machine_mode tmode, bool reverse, bool fallback_p,
+ rtx *alt_rtl)
{
rtx op0 = str_rtx;
machine_mode mode1;
/* If we have an out-of-bounds access to a register, just return an
uninitialized register of the required mode. This can occur if the
source code contains an out-of-bounds access to a small array. */
- if (REG_P (op0) && bitnum >= GET_MODE_BITSIZE (GET_MODE (op0)))
+ if (REG_P (op0) && known_ge (bitnum, GET_MODE_BITSIZE (GET_MODE (op0))))
return gen_reg_rtx (tmode);
if (REG_P (op0)
&& mode == GET_MODE (op0)
- && bitnum == 0
- && bitsize == GET_MODE_BITSIZE (GET_MODE (op0)))
+ && known_eq (bitnum, 0U)
+ && known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (op0))))
{
if (reverse)
op0 = flip_storage_order (mode, op0);
if (VECTOR_MODE_P (GET_MODE (op0))
&& !MEM_P (op0)
&& VECTOR_MODE_P (tmode)
- && GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (tmode))
+ && known_eq (bitsize, GET_MODE_BITSIZE (tmode))
+ && maybe_gt (GET_MODE_SIZE (GET_MODE (op0)), GET_MODE_SIZE (tmode)))
{
machine_mode new_mode = GET_MODE (op0);
if (GET_MODE_INNER (new_mode) != GET_MODE_INNER (tmode))
{
scalar_mode inner_mode = GET_MODE_INNER (tmode);
- unsigned int nunits = (GET_MODE_BITSIZE (GET_MODE (op0))
- / GET_MODE_UNIT_BITSIZE (tmode));
- if (!mode_for_vector (inner_mode, nunits).exists (&new_mode)
- || !VECTOR_MODE_P (new_mode)
- || GET_MODE_SIZE (new_mode) != GET_MODE_SIZE (GET_MODE (op0))
- || GET_MODE_INNER (new_mode) != GET_MODE_INNER (tmode)
- || !targetm.vector_mode_supported_p (new_mode))
+ poly_uint64 nunits;
+ if (!multiple_p (GET_MODE_BITSIZE (GET_MODE (op0)),
+ GET_MODE_UNIT_BITSIZE (tmode), &nunits)
+ || !related_vector_mode (tmode, inner_mode,
+ nunits).exists (&new_mode)
+ || maybe_ne (GET_MODE_SIZE (new_mode),
+ GET_MODE_SIZE (GET_MODE (op0))))
new_mode = VOIDmode;
}
+ poly_uint64 pos;
if (new_mode != VOIDmode
&& (convert_optab_handler (vec_extract_optab, new_mode, tmode)
!= CODE_FOR_nothing)
- && ((bitnum + bitsize - 1) / GET_MODE_BITSIZE (tmode)
- == bitnum / GET_MODE_BITSIZE (tmode)))
+ && multiple_p (bitnum, GET_MODE_BITSIZE (tmode), &pos))
{
- struct expand_operand ops[3];
+ class expand_operand ops[3];
machine_mode outermode = new_mode;
machine_mode innermode = tmode;
enum insn_code icode
= convert_optab_handler (vec_extract_optab, outermode, innermode);
- unsigned HOST_WIDE_INT pos = bitnum / GET_MODE_BITSIZE (innermode);
if (new_mode != GET_MODE (op0))
op0 = gen_lowpart (new_mode, op0);
new_mode = MIN_MODE_VECTOR_INT;
FOR_EACH_MODE_FROM (new_mode, new_mode)
- if (GET_MODE_SIZE (new_mode) == GET_MODE_SIZE (GET_MODE (op0))
- && GET_MODE_UNIT_SIZE (new_mode) == GET_MODE_SIZE (tmode)
+ if (known_eq (GET_MODE_SIZE (new_mode), GET_MODE_SIZE (GET_MODE (op0)))
+ && known_eq (GET_MODE_UNIT_SIZE (new_mode), GET_MODE_SIZE (tmode))
&& targetm.vector_mode_supported_p (new_mode))
break;
if (new_mode != VOIDmode)
}
/* Use vec_extract patterns for extracting parts of vectors whenever
- available. */
+ available. If that fails, see whether the current modes and bitregion
+ give a natural subreg. */
machine_mode outermode = GET_MODE (op0);
- scalar_mode innermode = GET_MODE_INNER (outermode);
- if (VECTOR_MODE_P (outermode)
- && !MEM_P (op0)
- && (convert_optab_handler (vec_extract_optab, outermode, innermode)
- != CODE_FOR_nothing)
- && ((bitnum + bitsize - 1) / GET_MODE_BITSIZE (innermode)
- == bitnum / GET_MODE_BITSIZE (innermode)))
+ if (VECTOR_MODE_P (outermode) && !MEM_P (op0))
{
- struct expand_operand ops[3];
+ scalar_mode innermode = GET_MODE_INNER (outermode);
enum insn_code icode
= convert_optab_handler (vec_extract_optab, outermode, innermode);
- unsigned HOST_WIDE_INT pos = bitnum / GET_MODE_BITSIZE (innermode);
+ poly_uint64 pos;
+ if (icode != CODE_FOR_nothing
+ && known_eq (bitsize, GET_MODE_BITSIZE (innermode))
+ && multiple_p (bitnum, GET_MODE_BITSIZE (innermode), &pos))
+ {
+ class expand_operand ops[3];
- create_output_operand (&ops[0], target, innermode);
- ops[0].target = 1;
- create_input_operand (&ops[1], op0, outermode);
- create_integer_operand (&ops[2], pos);
- if (maybe_expand_insn (icode, 3, ops))
+ create_output_operand (&ops[0], target, innermode);
+ ops[0].target = 1;
+ create_input_operand (&ops[1], op0, outermode);
+ create_integer_operand (&ops[2], pos);
+ if (maybe_expand_insn (icode, 3, ops))
+ {
+ if (alt_rtl && ops[0].target)
+ *alt_rtl = target;
+ target = ops[0].value;
+ if (GET_MODE (target) != mode)
+ return gen_lowpart (tmode, target);
+ return target;
+ }
+ }
+ /* Using subregs is useful if we're extracting one register vector
+ from a multi-register vector. extract_bit_field_as_subreg checks
+ for valid bitsize and bitnum, so we don't need to do that here. */
+ if (VECTOR_MODE_P (mode))
{
- if (alt_rtl && ops[0].target)
- *alt_rtl = target;
- target = ops[0].value;
- if (GET_MODE (target) != mode)
- return gen_lowpart (tmode, target);
- return target;
+ rtx sub = extract_bit_field_as_subreg (mode, op0, bitsize, bitnum);
+ if (sub)
+ return sub;
}
}
}
else
{
- HOST_WIDE_INT size = GET_MODE_SIZE (GET_MODE (op0));
+ poly_int64 size = GET_MODE_SIZE (GET_MODE (op0));
rtx mem = assign_stack_temp (GET_MODE (op0), size);
emit_move_insn (mem, op0);
op0 = adjust_bitfield_address_size (mem, BLKmode, 0, size);
/* Extraction of a full MODE1 value can be done with a subreg as long
as the least significant bit of the value is the least significant
bit of either OP0 or a word of OP0. */
- if (!MEM_P (op0)
- && !reverse
- && lowpart_bit_field_p (bitnum, bitsize, op0_mode.require ())
- && bitsize == GET_MODE_BITSIZE (mode1)
- && TRULY_NOOP_TRUNCATION_MODES_P (mode1, op0_mode.require ()))
+ if (!MEM_P (op0) && !reverse)
{
- rtx sub = simplify_gen_subreg (mode1, op0, op0_mode.require (),
- bitnum / BITS_PER_UNIT);
+ rtx sub = extract_bit_field_as_subreg (mode1, op0, bitsize, bitnum);
if (sub)
return convert_extracted_bit_field (sub, mode, tmode, unsignedp);
}
return convert_extracted_bit_field (op0, mode, tmode, unsignedp);
}
+ /* If we have a memory source and a non-constant bit offset, restrict
+ the memory to the referenced bytes. This is a worst-case fallback
+ but is useful for things like vector booleans. */
+ if (MEM_P (op0) && !bitnum.is_constant ())
+ {
+ bytenum = bits_to_bytes_round_down (bitnum);
+ bitnum = num_trailing_bits (bitnum);
+ poly_uint64 bytesize = bits_to_bytes_round_up (bitnum + bitsize);
+ op0 = adjust_bitfield_address_size (op0, BLKmode, bytenum, bytesize);
+ op0_mode = opt_scalar_int_mode ();
+ }
+
+ /* It's possible we'll need to handle other cases here for
+ polynomial bitnum and bitsize. */
+
+ /* From here on we need to be looking at a fixed-size insertion. */
+ return extract_integral_bit_field (op0, op0_mode, bitsize.to_constant (),
+ bitnum.to_constant (), unsignedp,
+ target, mode, tmode, reverse, fallback_p);
+}
+
+/* Subroutine of extract_bit_field_1, with the same arguments, except
+ that BITSIZE and BITNUM are constant. Handle cases specific to
+ integral modes. If OP0_MODE is defined, it is the mode of OP0,
+ otherwise OP0 is a BLKmode MEM. */
+
+static rtx
+extract_integral_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
+ unsigned HOST_WIDE_INT bitsize,
+ unsigned HOST_WIDE_INT bitnum, int unsignedp,
+ rtx target, machine_mode mode, machine_mode tmode,
+ bool reverse, bool fallback_p)
+{
/* Handle fields bigger than a word. */
if (bitsize > BITS_PER_WORD)
/* In case we're about to clobber a base register or something
(see gcc.c-torture/execute/20040625-1.c). */
- if (reg_mentioned_p (target, str_rtx))
+ if (reg_mentioned_p (target, op0))
target = gen_reg_rtx (mode);
/* Indicate for flow that the entire target reg is being set. */
emit_clobber (target);
+ /* The mode must be fixed-size, since extract_bit_field_1 handles
+ extractions from variable-sized objects before calling this
+ function. */
+ unsigned int target_size
+ = GET_MODE_SIZE (GET_MODE (target)).to_constant ();
last = get_last_insn ();
for (i = 0; i < nwords; i++)
{
if I is 1, use the next to lowest word; and so on. */
/* Word number in TARGET to use. */
unsigned int wordnum
- = (backwards
- ? GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD - i - 1
- : i);
+ = (backwards ? target_size / UNITS_PER_WORD - i - 1 : i);
/* Offset from start of field in OP0. */
unsigned int bit_offset = (backwards ^ reverse
? MAX ((int) bitsize - ((int) i + 1)
{
/* Unless we've filled TARGET, the upper regs in a multi-reg value
need to be zero'd out. */
- if (GET_MODE_SIZE (GET_MODE (target)) > nwords * UNITS_PER_WORD)
+ if (target_size > nwords * UNITS_PER_WORD)
{
unsigned int i, total_words;
- total_words = GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD;
+ total_words = target_size / UNITS_PER_WORD;
for (i = nwords; i < total_words; i++)
emit_move_insn
(operand_subword (target,
If a TARGET is specified and we can store in it at no extra cost,
we do so, and return TARGET.
Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred
- if they are equally easy. */
+ if they are equally easy.
+
+ If the result can be stored at TARGET, and ALT_RTL is non-NULL,
+ then *ALT_RTL is set to TARGET (before legitimziation). */
rtx
-extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
- unsigned HOST_WIDE_INT bitnum, int unsignedp, rtx target,
- machine_mode mode, machine_mode tmode, bool reverse,
- rtx *alt_rtl)
+extract_bit_field (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
+ int unsignedp, rtx target, machine_mode mode,
+ machine_mode tmode, bool reverse, rtx *alt_rtl)
{
machine_mode mode1;
/* Handle -fstrict-volatile-bitfields in the cases where it applies. */
- if (GET_MODE_BITSIZE (GET_MODE (str_rtx)) > 0)
+ if (maybe_ne (GET_MODE_BITSIZE (GET_MODE (str_rtx)), 0))
mode1 = GET_MODE (str_rtx);
- else if (target && GET_MODE_BITSIZE (GET_MODE (target)) > 0)
+ else if (target && maybe_ne (GET_MODE_BITSIZE (GET_MODE (target)), 0))
mode1 = GET_MODE (target);
else
mode1 = tmode;
+ unsigned HOST_WIDE_INT ibitsize, ibitnum;
scalar_int_mode int_mode;
- if (is_a <scalar_int_mode> (mode1, &int_mode)
- && strict_volatile_bitfield_p (str_rtx, bitsize, bitnum, int_mode, 0, 0))
+ if (bitsize.is_constant (&ibitsize)
+ && bitnum.is_constant (&ibitnum)
+ && is_a <scalar_int_mode> (mode1, &int_mode)
+ && strict_volatile_bitfield_p (str_rtx, ibitsize, ibitnum,
+ int_mode, 0, 0))
{
/* Extraction of a full INT_MODE value can be done with a simple load.
We know here that the field can be accessed with one single
instruction. For targets that support unaligned memory,
an unaligned access may be necessary. */
- if (bitsize == GET_MODE_BITSIZE (int_mode))
+ if (ibitsize == GET_MODE_BITSIZE (int_mode))
{
rtx result = adjust_bitfield_address (str_rtx, int_mode,
- bitnum / BITS_PER_UNIT);
+ ibitnum / BITS_PER_UNIT);
if (reverse)
result = flip_storage_order (int_mode, result);
- gcc_assert (bitnum % BITS_PER_UNIT == 0);
+ gcc_assert (ibitnum % BITS_PER_UNIT == 0);
return convert_extracted_bit_field (result, mode, tmode, unsignedp);
}
- str_rtx = narrow_bit_field_mem (str_rtx, int_mode, bitsize, bitnum,
- &bitnum);
- gcc_assert (bitnum + bitsize <= GET_MODE_BITSIZE (int_mode));
+ str_rtx = narrow_bit_field_mem (str_rtx, int_mode, ibitsize, ibitnum,
+ &ibitnum);
+ gcc_assert (ibitnum + ibitsize <= GET_MODE_BITSIZE (int_mode));
str_rtx = copy_to_reg (str_rtx);
+ return extract_bit_field_1 (str_rtx, ibitsize, ibitnum, unsignedp,
+ target, mode, tmode, reverse, true, alt_rtl);
}
return extract_bit_field_1 (str_rtx, bitsize, bitnum, unsignedp,
/* simplify_gen_subreg can't be used here, as if simplify_subreg
fails, it will happily create (subreg (symbol_ref)) or similar
invalid SUBREGs. */
- unsigned int byte = subreg_lowpart_offset (mode, src_mode);
+ poly_uint64 byte = subreg_lowpart_offset (mode, src_mode);
rtx ret = simplify_subreg (mode, src, src_mode, byte);
if (ret)
return ret;
if (GET_MODE_CLASS (mode) == MODE_CC || GET_MODE_CLASS (src_mode) == MODE_CC)
return NULL_RTX;
- if (GET_MODE_BITSIZE (mode) == GET_MODE_BITSIZE (src_mode)
+ if (known_eq (GET_MODE_BITSIZE (mode), GET_MODE_BITSIZE (src_mode))
&& targetm.modes_tieable_p (mode, src_mode))
{
rtx x = gen_lowpart_common (mode, src);
return NULL_RTX;
src = gen_lowpart (src_int_mode, src);
+ if (!validate_subreg (int_mode, src_int_mode, src,
+ subreg_lowpart_offset (int_mode, src_int_mode)))
+ return NULL_RTX;
+
src = convert_modes (int_mode, src_int_mode, src, true);
src = gen_lowpart (mode, src);
return src;
&& CONST_INT_P (op1)
&& INTVAL (op1) == BITS_PER_UNIT
&& GET_MODE_SIZE (scalar_mode) == 2
- && optab_handler (bswap_optab, HImode) != CODE_FOR_nothing)
- return expand_unop (HImode, bswap_optab, shifted, NULL_RTX,
- unsignedp);
+ && optab_handler (bswap_optab, mode) != CODE_FOR_nothing)
+ return expand_unop (mode, bswap_optab, shifted, NULL_RTX, unsignedp);
if (op1 == const0_rtx)
return shifted;
/* Write a REG_EQUAL note on the last insn so that we can cse
multiplication sequences. Note that if ACCUM is a SUBREG,
we've set the inner register and must properly indicate that. */
- tem = op0, nmode = mode;
- accum_inner = accum;
- if (GET_CODE (accum) == SUBREG)
+ tem = op0, nmode = mode;
+ accum_inner = accum;
+ if (GET_CODE (accum) == SUBREG)
{
accum_inner = SUBREG_REG (accum);
nmode = GET_MODE (accum_inner);
tem = gen_lowpart (nmode, op0);
}
- insn = get_last_insn ();
- set_dst_reg_note (insn, REG_EQUAL,
- gen_rtx_MULT (nmode, tem,
- gen_int_mode (val_so_far, nmode)),
- accum_inner);
+ /* Don't add a REG_EQUAL note if tem is a paradoxical SUBREG.
+ In that case, only the low bits of accum would be guaranteed to
+ be equal to the content of the REG_EQUAL note, the upper bits
+ can be anything. */
+ if (!paradoxical_subreg_p (tem))
+ {
+ insn = get_last_insn ();
+ wide_int wval_so_far
+ = wi::uhwi (val_so_far,
+ GET_MODE_PRECISION (as_a <scalar_mode> (nmode)));
+ rtx c = immed_wide_int_const (wval_so_far, nmode);
+ set_dst_reg_note (insn, REG_EQUAL, gen_rtx_MULT (nmode, tem, c),
+ accum_inner);
+ }
}
}
{
unsigned HOST_WIDE_INT mask = (HOST_WIDE_INT_1U << n) - 1;
*multiplier_ptr = mhigh.to_uhwi () & mask;
- return mhigh.to_uhwi () >= mask;
+ return mhigh.to_uhwi () > mask;
}
else
{
/* Emit code to multiply OP0 and OP1 (where OP1 is an integer constant),
putting the high half of the result in TARGET if that is convenient,
- and return where the result is. If the operation can not be performed,
+ and return where the result is. If the operation cannot be performed,
0 is returned.
MODE is the mode of operation and result.
HOST_WIDE_INT d = INTVAL (op1);
unsigned HOST_WIDE_INT abs_d;
+ /* Not prepared to handle division/remainder by
+ 0xffffffffffffffff8000000000000000 etc. */
+ if (d == HOST_WIDE_INT_MIN && size > HOST_BITS_PER_WIDE_INT)
+ break;
+
/* Since d might be INT_MIN, we have to cast to
unsigned HOST_WIDE_INT before negating to avoid
undefined signed overflow. */
|| (optab_handler (sdivmod_optab, int_mode)
!= CODE_FOR_nothing)))
;
- else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d)
- && (size <= HOST_BITS_PER_WIDE_INT
- || abs_d != (unsigned HOST_WIDE_INT) d))
+ else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d))
{
if (rem_flag)
{
case CONST_VECTOR:
{
- int units = CONST_VECTOR_NUNITS (x);
+ unsigned int npatterns = CONST_VECTOR_NPATTERNS (x);
+ unsigned int nelts_per_pattern = CONST_VECTOR_NELTS_PER_PATTERN (x);
tree itype = TREE_TYPE (type);
- int i;
/* Build a tree with vector elements. */
- tree_vector_builder elts (type, units, 1);
- for (i = 0; i < units; ++i)
+ tree_vector_builder elts (type, npatterns, nelts_per_pattern);
+ unsigned int count = elts.encoded_nelts ();
+ for (unsigned int i = 0; i < count; ++i)
{
rtx elt = CONST_VECTOR_ELT (x, i);
elts.quick_push (make_tree (itype, elt));
return fold_convert (type, make_tree (t, XEXP (x, 0)));
case CONST:
- {
- rtx op = XEXP (x, 0);
- if (GET_CODE (op) == VEC_DUPLICATE)
- {
- tree elt_tree = make_tree (TREE_TYPE (type), XEXP (op, 0));
- return build_vector_from_val (type, elt_tree);
- }
- if (GET_CODE (op) == VEC_SERIES)
- {
- tree itype = TREE_TYPE (type);
- tree base_tree = make_tree (itype, XEXP (op, 0));
- tree step_tree = make_tree (itype, XEXP (op, 1));
- return build_vec_series (type, base_tree, step_tree);
- }
- return make_tree (type, op);
- }
+ return make_tree (type, XEXP (x, 0));
case SYMBOL_REF:
t = SYMBOL_REF_DECL (x);
int unsignedp, rtx x, rtx y, int normalizep,
machine_mode target_mode)
{
- struct expand_operand ops[4];
+ class expand_operand ops[4];
rtx op0, comparison, subtarget;
rtx_insn *last;
scalar_int_mode result_mode = targetm.cstore_mode (icode);
If STORE_FLAG_VALUE does not have the sign bit set when
interpreted in MODE, we can do this conversion as unsigned, which
is usually more efficient. */
- if (GET_MODE_SIZE (int_target_mode) > GET_MODE_SIZE (result_mode))
+ if (GET_MODE_PRECISION (int_target_mode) > GET_MODE_PRECISION (result_mode))
{
- convert_move (target, subtarget,
- val_signbit_known_clear_p (result_mode,
- STORE_FLAG_VALUE));
+ gcc_assert (GET_MODE_PRECISION (result_mode) != 1
+ || STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1);
+
+ bool unsignedp = (STORE_FLAG_VALUE >= 0);
+ convert_move (target, subtarget, unsignedp);
+
op0 = target;
result_mode = int_target_mode;
}
if (mode == VOIDmode)
mode = GET_MODE (op0);
+ if (CONST_SCALAR_INT_P (op1))
+ canonicalize_comparison (mode, &code, &op1);
+
/* For some comparisons with 1 and -1, we can convert this to
comparisons with zero. This will often produce more opportunities for
store-flag insns. */
if (!HAVE_conditional_move)
return 0;
+ /* Do not turn a trapping comparison into a non-trapping one. */
+ if ((code != EQ && code != NE && code != UNEQ && code != LTGT)
+ && flag_trapping_math)
+ return 0;
+
/* Try using a setcc instruction for ORDERED/UNORDERED, followed by a
conditional move. */
tem = emit_store_flag_1 (subtarget, first_code, op0, op1, mode, 0,
if (tem != 0)
return tem;
+ /* If one operand is constant, make it the second one. Only do this
+ if the other operand is not constant as well. */
+ if (swap_commutative_operands_p (op0, op1))
+ {
+ std::swap (op0, op1);
+ code = swap_condition (code);
+ }
+
+ if (mode == VOIDmode)
+ mode = GET_MODE (op0);
+
if (!target)
target = gen_reg_rtx (word_mode);
return target;
}
+
+/* Helper function for canonicalize_cmp_for_target. Swap between inclusive
+ and exclusive ranges in order to create an equivalent comparison. See
+ canonicalize_cmp_for_target for the possible cases. */
+
+static enum rtx_code
+equivalent_cmp_code (enum rtx_code code)
+{
+ switch (code)
+ {
+ case GT:
+ return GE;
+ case GE:
+ return GT;
+ case LT:
+ return LE;
+ case LE:
+ return LT;
+ case GTU:
+ return GEU;
+ case GEU:
+ return GTU;
+ case LTU:
+ return LEU;
+ case LEU:
+ return LTU;
+
+ default:
+ return code;
+ }
+}
+
+/* Choose the more appropiate immediate in scalar integer comparisons. The
+ purpose of this is to end up with an immediate which can be loaded into a
+ register in fewer moves, if possible.
+
+ For each integer comparison there exists an equivalent choice:
+ i) a > b or a >= b + 1
+ ii) a <= b or a < b + 1
+ iii) a >= b or a > b - 1
+ iv) a < b or a <= b - 1
+
+ MODE is the mode of the first operand.
+ CODE points to the comparison code.
+ IMM points to the rtx containing the immediate. *IMM must satisfy
+ CONST_SCALAR_INT_P on entry and continues to satisfy CONST_SCALAR_INT_P
+ on exit. */
+
+void
+canonicalize_comparison (machine_mode mode, enum rtx_code *code, rtx *imm)
+{
+ if (!SCALAR_INT_MODE_P (mode))
+ return;
+
+ int to_add = 0;
+ enum signop sgn = unsigned_condition_p (*code) ? UNSIGNED : SIGNED;
+
+ /* Extract the immediate value from the rtx. */
+ wide_int imm_val = rtx_mode_t (*imm, mode);
+
+ if (*code == GT || *code == GTU || *code == LE || *code == LEU)
+ to_add = 1;
+ else if (*code == GE || *code == GEU || *code == LT || *code == LTU)
+ to_add = -1;
+ else
+ return;
+
+ /* Check for overflow/underflow in the case of signed values and
+ wrapping around in the case of unsigned values. If any occur
+ cancel the optimization. */
+ wi::overflow_type overflow = wi::OVF_NONE;
+ wide_int imm_modif;
+
+ if (to_add == 1)
+ imm_modif = wi::add (imm_val, 1, sgn, &overflow);
+ else
+ imm_modif = wi::sub (imm_val, 1, sgn, &overflow);
+
+ if (overflow)
+ return;
+
+ /* The following creates a pseudo; if we cannot do that, bail out. */
+ if (!can_create_pseudo_p ())
+ return;
+
+ rtx reg = gen_rtx_REG (mode, LAST_VIRTUAL_REGISTER + 1);
+ rtx new_imm = immed_wide_int_const (imm_modif, mode);
+
+ rtx_insn *old_rtx = gen_move_insn (reg, *imm);
+ rtx_insn *new_rtx = gen_move_insn (reg, new_imm);
+
+ /* Update the immediate and the code. */
+ if (insn_cost (old_rtx, true) > insn_cost (new_rtx, true))
+ {
+ *code = equivalent_cmp_code (*code);
+ *imm = new_imm;
+ }
+}
+
+
\f
/* Perform possibly multi-word comparison and conditional jump to LABEL
if ARG1 OP ARG2 true where ARG1 and ARG2 are of mode MODE. This is