blob: bbdd0e71d2063e590ea12d6042b6274a181f6c2c [file] [log] [blame]
/* Medium-level subroutines: convert bit-field store and extract
and shifts, multiplies and divides to rtl instructions.
Copyright (C) 1987-2021 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with 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"
#include "coretypes.h"
#include "backend.h"
#include "target.h"
#include "rtl.h"
#include "tree.h"
#include "predict.h"
#include "memmodel.h"
#include "tm_p.h"
#include "optabs.h"
#include "expmed.h"
#include "regs.h"
#include "emit-rtl.h"
#include "diagnostic-core.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "dojump.h"
#include "explow.h"
#include "expr.h"
#include "langhooks.h"
#include "tree-vector-builder.h"
struct target_expmed default_target_expmed;
#if SWITCHABLE_TARGET
struct target_expmed *this_target_expmed = &default_target_expmed;
#endif
static bool store_integral_bit_field (rtx, opt_scalar_int_mode,
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,
poly_uint64, poly_uint64,
rtx, scalar_int_mode, bool);
static void store_fixed_bit_field_1 (rtx, scalar_int_mode,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
rtx, scalar_int_mode, bool);
static void store_split_bit_field (rtx, opt_scalar_int_mode,
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);
static rtx extract_fixed_bit_field_1 (machine_mode, rtx, scalar_int_mode,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, rtx, int, bool);
static rtx lshift_value (machine_mode, unsigned HOST_WIDE_INT, int);
static rtx extract_split_bit_field (rtx, opt_scalar_int_mode,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, int, bool);
static void do_cmp_and_jump (rtx, rtx, enum rtx_code, machine_mode, rtx_code_label *);
static rtx expand_smod_pow2 (scalar_int_mode, rtx, HOST_WIDE_INT);
static rtx expand_sdiv_pow2 (scalar_int_mode, rtx, HOST_WIDE_INT);
/* Return a constant integer mask value of mode MODE with BITSIZE ones
followed by BITPOS zeros, or the complement of that if COMPLEMENT.
The mask is truncated if necessary to the width of mode MODE. The
mask is zero-extended if BITSIZE+BITPOS is too small for MODE. */
static inline rtx
mask_rtx (scalar_int_mode mode, int bitpos, int bitsize, bool complement)
{
return immed_wide_int_const
(wi::shifted_mask (bitpos, bitsize, complement,
GET_MODE_PRECISION (mode)), mode);
}
/* Test whether a value is zero of a power of two. */
#define EXACT_POWER_OF_2_OR_ZERO_P(x) \
(((x) & ((x) - HOST_WIDE_INT_1U)) == 0)
struct init_expmed_rtl
{
rtx reg;
rtx plus;
rtx neg;
rtx mult;
rtx sdiv;
rtx udiv;
rtx sdiv_32;
rtx smod_32;
rtx wide_mult;
rtx wide_lshr;
rtx wide_trunc;
rtx shift;
rtx shift_mult;
rtx shift_add;
rtx shift_sub0;
rtx shift_sub1;
rtx zext;
rtx trunc;
rtx pow2[MAX_BITS_PER_WORD];
rtx cint[MAX_BITS_PER_WORD];
};
static void
init_expmed_one_conv (struct init_expmed_rtl *all, scalar_int_mode to_mode,
scalar_int_mode from_mode, bool speed)
{
int to_size, from_size;
rtx which;
to_size = GET_MODE_PRECISION (to_mode);
from_size = GET_MODE_PRECISION (from_mode);
/* Most partial integers have a precision less than the "full"
integer it requires for storage. In case one doesn't, for
comparison purposes here, reduce the bit size by one in that
case. */
if (GET_MODE_CLASS (to_mode) == MODE_PARTIAL_INT
&& pow2p_hwi (to_size))
to_size --;
if (GET_MODE_CLASS (from_mode) == MODE_PARTIAL_INT
&& pow2p_hwi (from_size))
from_size --;
/* Assume cost of zero-extend and sign-extend is the same. */
which = (to_size < from_size ? all->trunc : all->zext);
PUT_MODE (all->reg, from_mode);
set_convert_cost (to_mode, from_mode, speed,
set_src_cost (which, to_mode, speed));
/* Restore all->reg's mode. */
PUT_MODE (all->reg, to_mode);
}
static void
init_expmed_one_mode (struct init_expmed_rtl *all,
machine_mode mode, int speed)
{
int m, n, mode_bitsize;
machine_mode mode_from;
mode_bitsize = GET_MODE_UNIT_BITSIZE (mode);
PUT_MODE (all->reg, mode);
PUT_MODE (all->plus, mode);
PUT_MODE (all->neg, mode);
PUT_MODE (all->mult, mode);
PUT_MODE (all->sdiv, mode);
PUT_MODE (all->udiv, mode);
PUT_MODE (all->sdiv_32, mode);
PUT_MODE (all->smod_32, mode);
PUT_MODE (all->wide_trunc, mode);
PUT_MODE (all->shift, mode);
PUT_MODE (all->shift_mult, mode);
PUT_MODE (all->shift_add, mode);
PUT_MODE (all->shift_sub0, mode);
PUT_MODE (all->shift_sub1, mode);
PUT_MODE (all->zext, mode);
PUT_MODE (all->trunc, mode);
set_add_cost (speed, mode, set_src_cost (all->plus, mode, speed));
set_neg_cost (speed, mode, set_src_cost (all->neg, mode, speed));
set_mul_cost (speed, mode, set_src_cost (all->mult, mode, speed));
set_sdiv_cost (speed, mode, set_src_cost (all->sdiv, mode, speed));
set_udiv_cost (speed, mode, set_src_cost (all->udiv, mode, speed));
set_sdiv_pow2_cheap (speed, mode, (set_src_cost (all->sdiv_32, mode, speed)
<= 2 * add_cost (speed, mode)));
set_smod_pow2_cheap (speed, mode, (set_src_cost (all->smod_32, mode, speed)
<= 4 * add_cost (speed, mode)));
set_shift_cost (speed, mode, 0, 0);
{
int cost = add_cost (speed, mode);
set_shiftadd_cost (speed, mode, 0, cost);
set_shiftsub0_cost (speed, mode, 0, cost);
set_shiftsub1_cost (speed, mode, 0, cost);
}
n = MIN (MAX_BITS_PER_WORD, mode_bitsize);
for (m = 1; m < n; m++)
{
XEXP (all->shift, 1) = all->cint[m];
XEXP (all->shift_mult, 1) = all->pow2[m];
set_shift_cost (speed, mode, m, set_src_cost (all->shift, mode, speed));
set_shiftadd_cost (speed, mode, m, set_src_cost (all->shift_add, mode,
speed));
set_shiftsub0_cost (speed, mode, m, set_src_cost (all->shift_sub0, mode,
speed));
set_shiftsub1_cost (speed, mode, m, set_src_cost (all->shift_sub1, mode,
speed));
}
scalar_int_mode int_mode_to;
if (is_a <scalar_int_mode> (mode, &int_mode_to))
{
for (mode_from = MIN_MODE_INT; mode_from <= MAX_MODE_INT;
mode_from = (machine_mode)(mode_from + 1))
init_expmed_one_conv (all, int_mode_to,
as_a <scalar_int_mode> (mode_from), speed);
scalar_int_mode wider_mode;
if (GET_MODE_CLASS (int_mode_to) == MODE_INT
&& GET_MODE_WIDER_MODE (int_mode_to).exists (&wider_mode))
{
PUT_MODE (all->reg, mode);
PUT_MODE (all->zext, wider_mode);
PUT_MODE (all->wide_mult, wider_mode);
PUT_MODE (all->wide_lshr, wider_mode);
XEXP (all->wide_lshr, 1)
= gen_int_shift_amount (wider_mode, mode_bitsize);
set_mul_widen_cost (speed, wider_mode,
set_src_cost (all->wide_mult, wider_mode, speed));
set_mul_highpart_cost (speed, int_mode_to,
set_src_cost (all->wide_trunc,
int_mode_to, speed));
}
}
}
void
init_expmed (void)
{
struct init_expmed_rtl all;
machine_mode mode = QImode;
int m, speed;
memset (&all, 0, sizeof all);
for (m = 1; m < MAX_BITS_PER_WORD; m++)
{
all.pow2[m] = GEN_INT (HOST_WIDE_INT_1 << m);
all.cint[m] = GEN_INT (m);
}
/* Avoid using hard regs in ways which may be unsupported. */
all.reg = gen_raw_REG (mode, LAST_VIRTUAL_REGISTER + 1);
all.plus = gen_rtx_PLUS (mode, all.reg, all.reg);
all.neg = gen_rtx_NEG (mode, all.reg);
all.mult = gen_rtx_MULT (mode, all.reg, all.reg);
all.sdiv = gen_rtx_DIV (mode, all.reg, all.reg);
all.udiv = gen_rtx_UDIV (mode, all.reg, all.reg);
all.sdiv_32 = gen_rtx_DIV (mode, all.reg, all.pow2[5]);
all.smod_32 = gen_rtx_MOD (mode, all.reg, all.pow2[5]);
all.zext = gen_rtx_ZERO_EXTEND (mode, all.reg);
all.wide_mult = gen_rtx_MULT (mode, all.zext, all.zext);
all.wide_lshr = gen_rtx_LSHIFTRT (mode, all.wide_mult, all.reg);
all.wide_trunc = gen_rtx_TRUNCATE (mode, all.wide_lshr);
all.shift = gen_rtx_ASHIFT (mode, all.reg, all.reg);
all.shift_mult = gen_rtx_MULT (mode, all.reg, all.reg);
all.shift_add = gen_rtx_PLUS (mode, all.shift_mult, all.reg);
all.shift_sub0 = gen_rtx_MINUS (mode, all.shift_mult, all.reg);
all.shift_sub1 = gen_rtx_MINUS (mode, all.reg, all.shift_mult);
all.trunc = gen_rtx_TRUNCATE (mode, all.reg);
for (speed = 0; speed < 2; speed++)
{
crtl->maybe_hot_insn_p = speed;
set_zero_cost (speed, set_src_cost (const0_rtx, mode, speed));
for (mode = MIN_MODE_INT; mode <= MAX_MODE_INT;
mode = (machine_mode)(mode + 1))
init_expmed_one_mode (&all, mode, speed);
if (MIN_MODE_PARTIAL_INT != VOIDmode)
for (mode = MIN_MODE_PARTIAL_INT; mode <= MAX_MODE_PARTIAL_INT;
mode = (machine_mode)(mode + 1))
init_expmed_one_mode (&all, mode, speed);
if (MIN_MODE_VECTOR_INT != VOIDmode)
for (mode = MIN_MODE_VECTOR_INT; mode <= MAX_MODE_VECTOR_INT;
mode = (machine_mode)(mode + 1))
init_expmed_one_mode (&all, mode, speed);
}
if (alg_hash_used_p ())
{
struct alg_hash_entry *p = alg_hash_entry_ptr (0);
memset (p, 0, sizeof (*p) * NUM_ALG_HASH_ENTRIES);
}
else
set_alg_hash_used_p (true);
default_rtl_profile ();
ggc_free (all.trunc);
ggc_free (all.shift_sub1);
ggc_free (all.shift_sub0);
ggc_free (all.shift_add);
ggc_free (all.shift_mult);
ggc_free (all.shift);
ggc_free (all.wide_trunc);
ggc_free (all.wide_lshr);
ggc_free (all.wide_mult);
ggc_free (all.zext);
ggc_free (all.smod_32);
ggc_free (all.sdiv_32);
ggc_free (all.udiv);
ggc_free (all.sdiv);
ggc_free (all.mult);
ggc_free (all.neg);
ggc_free (all.plus);
ggc_free (all.reg);
}
/* Return an rtx representing minus the value of X.
MODE is the intended mode of the result,
useful if X is a CONST_INT. */
rtx
negate_rtx (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;
}
/* Whether reverse storage order is supported on the target. */
static int reverse_storage_order_supported = -1;
/* Check whether reverse storage order is supported on the target. */
static void
check_reverse_storage_order_support (void)
{
if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN)
{
reverse_storage_order_supported = 0;
sorry ("reverse scalar storage order");
}
else
reverse_storage_order_supported = 1;
}
/* Whether reverse FP storage order is supported on the target. */
static int reverse_float_storage_order_supported = -1;
/* Check whether reverse FP storage order is supported on the target. */
static void
check_reverse_float_storage_order_support (void)
{
if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
{
reverse_float_storage_order_supported = 0;
sorry ("reverse floating-point scalar storage order");
}
else
reverse_float_storage_order_supported = 1;
}
/* Return an rtx representing value of X with reverse storage order.
MODE is the intended mode of the result,
useful if X is a CONST_INT. */
rtx
flip_storage_order (machine_mode mode, rtx x)
{
scalar_int_mode int_mode;
rtx result;
if (mode == QImode)
return x;
if (COMPLEX_MODE_P (mode))
{
rtx real = read_complex_part (x, false);
rtx imag = read_complex_part (x, true);
real = flip_storage_order (GET_MODE_INNER (mode), real);
imag = flip_storage_order (GET_MODE_INNER (mode), imag);
return gen_rtx_CONCAT (mode, real, imag);
}
if (__builtin_expect (reverse_storage_order_supported < 0, 0))
check_reverse_storage_order_support ();
if (!is_a <scalar_int_mode> (mode, &int_mode))
{
if (FLOAT_MODE_P (mode)
&& __builtin_expect (reverse_float_storage_order_supported < 0, 0))
check_reverse_float_storage_order_support ();
if (!int_mode_for_size (GET_MODE_PRECISION (mode), 0).exists (&int_mode)
|| !targetm.scalar_mode_supported_p (int_mode))
{
sorry ("reverse storage order for %smode", GET_MODE_NAME (mode));
return x;
}
x = gen_lowpart (int_mode, x);
}
result = simplify_unary_operation (BSWAP, int_mode, x, int_mode);
if (result == 0)
result = expand_unop (int_mode, bswap_optab, x, NULL_RTX, 1);
if (int_mode != mode)
result = gen_lowpart (mode, result);
return result;
}
/* If MODE is set, adjust bitfield memory MEM so that it points to the
first unit of mode MODE that contains a bitfield of size BITSIZE at
bit position BITNUM. If MODE is not set, return a BLKmode reference
to every byte in the bitfield. Set *NEW_BITNUM to the bit position
of the field within the new memory. */
static rtx
narrow_bit_field_mem (rtx mem, opt_scalar_int_mode mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
unsigned HOST_WIDE_INT *new_bitnum)
{
scalar_int_mode imode;
if (mode.exists (&imode))
{
unsigned int unit = GET_MODE_BITSIZE (imode);
*new_bitnum = bitnum % unit;
HOST_WIDE_INT offset = (bitnum - *new_bitnum) / BITS_PER_UNIT;
return adjust_bitfield_address (mem, imode, offset);
}
else
{
*new_bitnum = bitnum % BITS_PER_UNIT;
HOST_WIDE_INT offset = bitnum / BITS_PER_UNIT;
HOST_WIDE_INT size = ((*new_bitnum + bitsize + BITS_PER_UNIT - 1)
/ BITS_PER_UNIT);
return adjust_bitfield_address_size (mem, BLKmode, offset, size);
}
}
/* The caller wants to perform insertion or extraction PATTERN on a
bitfield of size BITSIZE at BITNUM bits into memory operand OP0.
BITREGION_START and BITREGION_END are as for store_bit_field
and FIELDMODE is the natural mode of the field.
Search for a mode that is compatible with the memory access
restrictions and (where applicable) with a register insertion or
extraction. Return the new memory on success, storing the adjusted
bit position in *NEW_BITNUM. Return null otherwise. */
static rtx
adjust_bit_field_mem_for_reg (enum extraction_pattern pattern,
rtx op0, HOST_WIDE_INT bitsize,
HOST_WIDE_INT bitnum,
poly_uint64 bitregion_start,
poly_uint64 bitregion_end,
machine_mode fieldmode,
unsigned HOST_WIDE_INT *new_bitnum)
{
bit_field_mode_iterator iter (bitsize, bitnum, bitregion_start,
bitregion_end, MEM_ALIGN (op0),
MEM_VOLATILE_P (op0));
scalar_int_mode best_mode;
if (iter.next_mode (&best_mode))
{
/* We can use a memory in BEST_MODE. See whether this is true for
any wider modes. All other things being equal, we prefer to
use the widest mode possible because it tends to expose more
CSE opportunities. */
if (!iter.prefer_smaller_modes ())
{
/* Limit the search to the mode required by the corresponding
register insertion or extraction instruction, if any. */
scalar_int_mode limit_mode = word_mode;
extraction_insn insn;
if (get_best_reg_extraction_insn (&insn, pattern,
GET_MODE_BITSIZE (best_mode),
fieldmode))
limit_mode = insn.field_mode;
scalar_int_mode wider_mode;
while (iter.next_mode (&wider_mode)
&& GET_MODE_SIZE (wider_mode) <= GET_MODE_SIZE (limit_mode))
best_mode = wider_mode;
}
return narrow_bit_field_mem (op0, best_mode, bitsize, bitnum,
new_bitnum);
}
return NULL_RTX;
}
/* Return true if a bitfield of size BITSIZE at bit number BITNUM within
a structure of mode STRUCT_MODE represents a lowpart subreg. The subreg
offset is then BITNUM / BITS_PER_UNIT. */
static bool
lowpart_bit_field_p (poly_uint64 bitnum, poly_uint64 bitsize,
machine_mode struct_mode)
{
poly_uint64 regsize = REGMODE_NATURAL_SIZE (struct_mode);
if (BYTES_BIG_ENDIAN)
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 multiple_p (bitnum, regsize * BITS_PER_UNIT);
}
/* Return true if -fstrict-volatile-bitfields applies to an access of OP0
containing BITSIZE bits starting at BITNUM, with field mode FIELDMODE.
Return false if the access would touch memory outside the range
BITREGION_START to BITREGION_END for conformance to the C++ memory
model. */
static bool
strict_volatile_bitfield_p (rtx op0, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
scalar_int_mode fieldmode,
poly_uint64 bitregion_start,
poly_uint64 bitregion_end)
{
unsigned HOST_WIDE_INT modesize = GET_MODE_BITSIZE (fieldmode);
/* -fstrict-volatile-bitfields must be enabled and we must have a
volatile MEM. */
if (!MEM_P (op0)
|| !MEM_VOLATILE_P (op0)
|| flag_strict_volatile_bitfields <= 0)
return false;
/* The bit size must not be larger than the field mode, and
the field mode must not be larger than a word. */
if (bitsize > modesize || modesize > BITS_PER_WORD)
return false;
/* Check for cases of unaligned fields that must be split. */
if (bitnum % modesize + bitsize > modesize)
return false;
/* The memory must be sufficiently aligned for a MODESIZE access.
This condition guarantees, that the memory access will not
touch anything after the end of the structure. */
if (MEM_ALIGN (op0) < modesize)
return false;
/* Check for cases where the C++ memory model applies. */
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;
}
/* Return true if OP is a memory and if a bitfield of size BITSIZE at
bit number BITNUM can be treated as a simple value of mode MODE.
Store the byte offset in *BYTENUM if so. */
static bool
simple_mem_bitfield_p (rtx op0, poly_uint64 bitsize, poly_uint64 bitnum,
machine_mode mode, poly_uint64 *bytenum)
{
return (MEM_P (op0)
&& multiple_p (bitnum, BITS_PER_UNIT, bytenum)
&& known_eq (bitsize, GET_MODE_BITSIZE (mode))
&& (!targetm.slow_unaligned_access (mode, MEM_ALIGN (op0))
|| (multiple_p (bitnum, GET_MODE_ALIGNMENT (mode))
&& MEM_ALIGN (op0) >= GET_MODE_ALIGNMENT (mode))));
}
/* Try to use instruction INSV to store VALUE into a field of OP0.
If OP0_MODE is defined, it is the mode of OP0, otherwise OP0 is a
BLKmode MEM. VALUE_MODE is the mode of VALUE. BITSIZE and BITNUM
are as for store_bit_field. */
static bool
store_bit_field_using_insv (const extraction_insn *insv, rtx op0,
opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
rtx value, scalar_int_mode value_mode)
{
class expand_operand ops[4];
rtx value1;
rtx xop0 = op0;
rtx_insn *last = get_last_insn ();
bool copy_back = false;
scalar_int_mode op_mode = insv->field_mode;
unsigned int unit = GET_MODE_BITSIZE (op_mode);
if (bitsize == 0 || bitsize > unit)
return false;
if (MEM_P (xop0))
/* Get a reference to the first byte of the field. */
xop0 = narrow_bit_field_mem (xop0, insv->struct_mode, bitsize, bitnum,
&bitnum);
else
{
/* Convert from counting within OP0 to counting in OP_MODE. */
if (BYTES_BIG_ENDIAN)
bitnum += unit - GET_MODE_BITSIZE (op0_mode.require ());
/* If xop0 is a register, we need it in OP_MODE
to make it acceptable to the format of insv. */
if (GET_CODE (xop0) == SUBREG)
{
/* If such a SUBREG can't be created, give up. */
if (!validate_subreg (op_mode, GET_MODE (SUBREG_REG (xop0)),
SUBREG_REG (xop0), SUBREG_BYTE (xop0)))
return false;
/* We can't just change the mode, because this might clobber op0,
and we will need the original value of op0 if insv fails. */
xop0 = gen_rtx_SUBREG (op_mode, SUBREG_REG (xop0),
SUBREG_BYTE (xop0));
}
if (REG_P (xop0) && GET_MODE (xop0) != op_mode)
xop0 = gen_lowpart_SUBREG (op_mode, xop0);
}
/* If the destination is a paradoxical subreg such that we need a
truncate to the inner mode, perform the insertion on a temporary and
truncate the result to the original destination. Note that we can't
just truncate the paradoxical subreg as (truncate:N (subreg:W (reg:N
X) 0)) is (reg:N X). */
if (GET_CODE (xop0) == SUBREG
&& REG_P (SUBREG_REG (xop0))
&& !TRULY_NOOP_TRUNCATION_MODES_P (GET_MODE (SUBREG_REG (xop0)),
op_mode))
{
rtx tem = gen_reg_rtx (op_mode);
emit_move_insn (tem, xop0);
xop0 = tem;
copy_back = true;
}
/* There are similar overflow check at the start of store_bit_field_1,
but that only check the situation where the field lies completely
outside the register, while there do have situation where the field
lies partialy in the register, we need to adjust bitsize for this
partial overflow situation. Without this fix, pr48335-2.c on big-endian
will broken on those arch support bit insert instruction, like arm, aarch64
etc. */
if (bitsize + bitnum > unit && bitnum < unit)
{
warning (OPT_Wextra, "write of %wu-bit data outside the bound of "
"destination object, data truncated into %wu-bit",
bitsize, unit - bitnum);
bitsize = unit - bitnum;
}
/* If BITS_BIG_ENDIAN is zero on a BYTES_BIG_ENDIAN machine, we count
"backwards" from the size of the unit we are inserting into.
Otherwise, we count bits from the most significant on a
BYTES/BITS_BIG_ENDIAN machine. */
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
bitnum = unit - bitsize - bitnum;
/* Convert VALUE to op_mode (which insv insn wants) in VALUE1. */
value1 = value;
if (value_mode != op_mode)
{
if (GET_MODE_BITSIZE (value_mode) >= bitsize)
{
rtx tmp;
/* Optimization: Don't bother really extending VALUE
if it has all the bits we will actually use. However,
if we must narrow it, be sure we do it correctly. */
if (GET_MODE_SIZE (value_mode) < GET_MODE_SIZE (op_mode))
{
tmp = simplify_subreg (op_mode, value1, value_mode, 0);
if (! tmp)
tmp = simplify_gen_subreg (op_mode,
force_reg (value_mode, value1),
value_mode, 0);
}
else
{
tmp = gen_lowpart_if_possible (op_mode, value1);
if (! tmp)
tmp = gen_lowpart (op_mode, force_reg (value_mode, value1));
}
value1 = tmp;
}
else if (CONST_INT_P (value))
value1 = gen_int_mode (INTVAL (value), op_mode);
else
/* Parse phase is supposed to make VALUE's data type
match that of the component reference, which is a type
at least as wide as the field; so VALUE should have
a mode that corresponds to that type. */
gcc_assert (CONSTANT_P (value));
}
create_fixed_operand (&ops[0], xop0);
create_integer_operand (&ops[1], bitsize);
create_integer_operand (&ops[2], bitnum);
create_input_operand (&ops[3], value1, op_mode);
if (maybe_expand_insn (insv->icode, 4, ops))
{
if (copy_back)
convert_move (op0, xop0, true);
return true;
}
delete_insns_since (last);
return false;
}
/* A subroutine of store_bit_field, with the same arguments. Return true
if the operation could be implemented.
If FALLBACK_P is true, fall back to store_fixed_bit_field if we have
no other way of implementing the operation. If FALLBACK_P is false,
return false instead. */
static bool
store_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
poly_uint64 bitregion_start, poly_uint64 bitregion_end,
machine_mode fieldmode,
rtx value, bool reverse, bool fallback_p)
{
rtx op0 = str_rtx;
while (GET_CODE (op0) == SUBREG)
{
bitnum += subreg_memory_offset (op0) * BITS_PER_UNIT;
op0 = SUBREG_REG (op0);
}
/* No action is needed if the target is a register and if the field
lies completely outside that register. This can occur if the source
code contains an out-of-bounds access to a small array. */
if (REG_P (op0) && known_ge (bitnum, GET_MODE_BITSIZE (GET_MODE (op0))))
return true;
/* Use vec_set patterns for inserting parts of vectors whenever
available. */
machine_mode outermode = GET_MODE (op0);
scalar_mode innermode = GET_MODE_INNER (outermode);
poly_uint64 pos;
if (VECTOR_MODE_P (outermode)
&& !MEM_P (op0)
&& optab_handler (vec_set_optab, outermode) != CODE_FOR_nothing
&& fieldmode == innermode
&& known_eq (bitsize, GET_MODE_BITSIZE (innermode))
&& multiple_p (bitnum, GET_MODE_BITSIZE (innermode), &pos))
{
class expand_operand ops[3];
enum insn_code icode = optab_handler (vec_set_optab, outermode);
create_fixed_operand (&ops[0], op0);
create_input_operand (&ops[1], value, innermode);
create_integer_operand (&ops[2], pos);
if (maybe_expand_insn (icode, 3, ops))
return true;
}
/* If the target is a register, overwriting the entire object, or storing
a full-word or multi-word field can be done with just a SUBREG. */
if (!MEM_P (op0)
&& known_eq (bitsize, GET_MODE_BITSIZE (fieldmode)))
{
/* Use the subreg machinery either to narrow OP0 to the required
words or to cope with mode punning between equal-sized modes.
In the latter case, use subreg on the rhs side, not lhs. */
rtx sub;
HOST_WIDE_INT regnum;
poly_uint64 regsize = REGMODE_NATURAL_SIZE (GET_MODE (op0));
if (known_eq (bitnum, 0U)
&& known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (op0))))
{
sub = simplify_gen_subreg (GET_MODE (op0), value, fieldmode, 0);
if (sub)
{
if (reverse)
sub = flip_storage_order (GET_MODE (op0), sub);
emit_move_insn (op0, sub);
return true;
}
}
else if (constant_multiple_p (bitnum, regsize * BITS_PER_UNIT, &regnum)
&& multiple_p (bitsize, regsize * BITS_PER_UNIT)
&& known_ge (GET_MODE_BITSIZE (GET_MODE (op0)), bitsize))
{
sub = simplify_gen_subreg (fieldmode, op0, GET_MODE (op0),
regnum * regsize);
if (sub)
{
if (reverse)
value = flip_storage_order (fieldmode, value);
emit_move_insn (sub, value);
return true;
}
}
}
/* If the target is memory, storing any naturally aligned field can be
done with a simple store. For targets that support fast unaligned
memory, any naturally sized, unit aligned field can be done directly. */
poly_uint64 bytenum;
if (simple_mem_bitfield_p (op0, bitsize, bitnum, fieldmode, &bytenum))
{
op0 = adjust_bitfield_address (op0, fieldmode, bytenum);
if (reverse)
value = flip_storage_order (fieldmode, value);
emit_move_insn (op0, value);
return true;
}
/* 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. */
unsigned HOST_WIDE_INT ibitsize = bitsize.to_constant ();
unsigned HOST_WIDE_INT ibitnum = bitnum.to_constant ();
/* Make sure we are playing with integral modes. Pun with subregs
if we aren't. This must come after the entire register case above,
since that case is valid for any mode. The following cases are only
valid for integral modes. */
opt_scalar_int_mode op0_mode = int_mode_for_mode (GET_MODE (op0));
scalar_int_mode imode;
if (!op0_mode.exists (&imode) || imode != 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);
}
return store_integral_bit_field (op0, op0_mode, ibitsize, ibitnum,
bitregion_start, bitregion_end,
fieldmode, value, reverse, fallback_p);
}
/* Subroutine of store_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 bool
store_integral_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
poly_uint64 bitregion_start,
poly_uint64 bitregion_end,
machine_mode fieldmode,
rtx value, bool reverse, bool fallback_p)
{
/* Storing an lsb-aligned field in a register
can be done with a movstrict instruction. */
if (!MEM_P (op0)
&& !reverse
&& lowpart_bit_field_p (bitnum, bitsize, op0_mode.require ())
&& known_eq (bitsize, GET_MODE_BITSIZE (fieldmode))
&& optab_handler (movstrict_optab, fieldmode) != CODE_FOR_nothing)
{
class expand_operand ops[2];
enum insn_code icode = optab_handler (movstrict_optab, fieldmode);
rtx arg0 = op0;
unsigned HOST_WIDE_INT subreg_off;
if (GET_CODE (arg0) == SUBREG)
{
/* Else we've got some float mode source being extracted into
a different float mode destination -- this combination of
subregs results in Severe Tire Damage. */
gcc_assert (GET_MODE (SUBREG_REG (arg0)) == fieldmode
|| GET_MODE_CLASS (fieldmode) == MODE_INT
|| GET_MODE_CLASS (fieldmode) == MODE_PARTIAL_INT);
arg0 = SUBREG_REG (arg0);
}
subreg_off = bitnum / BITS_PER_UNIT;
if (validate_subreg (fieldmode, GET_MODE (arg0), arg0, subreg_off)
/* STRICT_LOW_PART must have a non-paradoxical subreg as
operand. */
&& !paradoxical_subreg_p (fieldmode, GET_MODE (arg0)))
{
arg0 = gen_rtx_SUBREG (fieldmode, arg0, subreg_off);
create_fixed_operand (&ops[0], arg0);
/* Shrink the source operand to FIELDMODE. */
create_convert_operand_to (&ops[1], value, fieldmode, false);
if (maybe_expand_insn (icode, 2, ops))
return true;
}
}
/* Handle fields bigger than a word. */
if (bitsize > BITS_PER_WORD)
{
/* Here we transfer the words of the field
in the order least significant first.
This is because the most significant word is the one which may
be less than full.
However, only do that if the value is not BLKmode. */
const bool backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;
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
subwords to extract. Note that fieldmode will often (always?) be
VOIDmode, because that is what store_field uses to indicate that this
is a bit field, but passing VOIDmode to operand_subword_force
is not allowed.
The mode must be fixed-size, since insertions into variable-sized
objects are meant to be handled before calling this function. */
fixed_size_mode value_mode = as_a <fixed_size_mode> (GET_MODE (value));
if (value_mode == VOIDmode)
value_mode = smallest_int_mode_for_size (nwords * BITS_PER_WORD);
last = get_last_insn ();
for (int i = 0; i < nwords; i++)
{
/* 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,
bitregion_start, bitregion_end,
word_mode,
value_word, reverse, fallback_p))
{
delete_insns_since (last);
return false;
}
}
return true;
}
/* If VALUE has a floating-point or complex mode, access it as an
integer of the corresponding size. This can occur on a machine
with 64 bit registers that uses SFmode for float. It can also
occur for unaligned float or complex fields. */
rtx orig_value = value;
scalar_int_mode value_mode;
if (GET_MODE (value) == VOIDmode)
/* By this point we've dealt with values that are bigger than a word,
so word_mode is a conservatively correct choice. */
value_mode = word_mode;
else if (!is_a <scalar_int_mode> (GET_MODE (value), &value_mode))
{
value_mode = int_mode_for_mode (GET_MODE (value)).require ();
value = gen_reg_rtx (value_mode);
emit_move_insn (gen_lowpart (GET_MODE (orig_value), value), orig_value);
}
/* If OP0 is a multi-word register, narrow it to the affected word.
If the region spans two words, defer to store_split_bit_field.
Don't do this if op0 is a single hard register wider than word
such as a float or vector register. */
if (!MEM_P (op0)
&& GET_MODE_SIZE (op0_mode.require ()) > UNITS_PER_WORD
&& (!REG_P (op0)
|| !HARD_REGISTER_P (op0)
|| hard_regno_nregs (REGNO (op0), op0_mode.require ()) != 1))
{
if (bitnum % BITS_PER_WORD + bitsize > BITS_PER_WORD)
{
if (!fallback_p)
return false;
store_split_bit_field (op0, op0_mode, bitsize, bitnum,
bitregion_start, bitregion_end,
value, value_mode, reverse);
return true;
}
op0 = simplify_gen_subreg (word_mode, op0, op0_mode.require (),
bitnum / BITS_PER_WORD * UNITS_PER_WORD);
gcc_assert (op0);
op0_mode = word_mode;
bitnum %= BITS_PER_WORD;
}
/* From here on we can assume that the field to be stored in fits
within a word. If the destination is a register, it too fits
in a word. */
extraction_insn insv;
if (!MEM_P (op0)
&& !reverse
&& get_best_reg_extraction_insn (&insv, EP_insv,
GET_MODE_BITSIZE (op0_mode.require ()),
fieldmode)
&& store_bit_field_using_insv (&insv, op0, op0_mode,
bitsize, bitnum, value, value_mode))
return true;
/* If OP0 is a memory, try copying it to a register and seeing if a
cheap register alternative is available. */
if (MEM_P (op0) && !reverse)
{
if (get_best_mem_extraction_insn (&insv, EP_insv, bitsize, bitnum,
fieldmode)
&& store_bit_field_using_insv (&insv, op0, op0_mode,
bitsize, bitnum, value, value_mode))
return true;
rtx_insn *last = get_last_insn ();
/* Try loading part of OP0 into a register, inserting the bitfield
into that, and then copying the result back to OP0. */
unsigned HOST_WIDE_INT bitpos;
rtx xop0 = adjust_bit_field_mem_for_reg (EP_insv, op0, bitsize, bitnum,
bitregion_start, bitregion_end,
fieldmode, &bitpos);
if (xop0)
{
rtx tempreg = copy_to_reg (xop0);
if (store_bit_field_1 (tempreg, bitsize, bitpos,
bitregion_start, bitregion_end,
fieldmode, orig_value, reverse, false))
{
emit_move_insn (xop0, tempreg);
return true;
}
delete_insns_since (last);
}
}
if (!fallback_p)
return false;
store_fixed_bit_field (op0, op0_mode, bitsize, bitnum, bitregion_start,
bitregion_end, value, value_mode, reverse);
return true;
}
/* Generate code to store value from rtx VALUE
into a bit-field within structure STR_RTX
containing BITSIZE bits starting at bit BITNUM.
BITREGION_START is bitpos of the first bitfield in this region.
BITREGION_END is the bitpos of the ending bitfield in this region.
These two fields are 0, if the C++ memory model does not apply,
or we are not interested in keeping track of bitfield regions.
FIELDMODE is the machine-mode of the FIELD_DECL node for this field.
If REVERSE is true, the store is to be done in reverse order. */
void
store_bit_field (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
poly_uint64 bitregion_start, poly_uint64 bitregion_end,
machine_mode fieldmode,
rtx value, bool reverse)
{
/* Handle -fstrict-volatile-bitfields in the cases where it applies. */
unsigned HOST_WIDE_INT ibitsize = 0, ibitnum = 0;
scalar_int_mode int_mode;
if (bitsize.is_constant (&ibitsize)
&& bitnum.is_constant (&ibitnum)
&& is_a <scalar_int_mode> (fieldmode, &int_mode)
&& strict_volatile_bitfield_p (str_rtx, ibitsize, ibitnum, int_mode,
bitregion_start, bitregion_end))
{
/* Storing of a full word can be done with a simple store.
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 (ibitsize == GET_MODE_BITSIZE (int_mode))
{
str_rtx = adjust_bitfield_address (str_rtx, int_mode,
ibitnum / BITS_PER_UNIT);
if (reverse)
value = flip_storage_order (int_mode, value);
gcc_assert (ibitnum % BITS_PER_UNIT == 0);
emit_move_insn (str_rtx, value);
}
else
{
rtx temp;
str_rtx = narrow_bit_field_mem (str_rtx, int_mode, ibitsize,
ibitnum, &ibitnum);
gcc_assert (ibitnum + ibitsize <= GET_MODE_BITSIZE (int_mode));
temp = copy_to_reg (str_rtx);
if (!store_bit_field_1 (temp, ibitsize, ibitnum, 0, 0,
int_mode, value, reverse, true))
gcc_unreachable ();
emit_move_insn (str_rtx, temp);
}
return;
}
/* 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) && maybe_ne (bitregion_start, 0U))
{
scalar_int_mode best_mode;
machine_mode addr_mode = VOIDmode;
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;
bitregion_start = 0;
if (bitsize.is_constant (&ibitsize)
&& bitnum.is_constant (&ibitnum)
&& get_best_mode (ibitsize, ibitnum,
bitregion_start, bitregion_end,
MEM_ALIGN (str_rtx), INT_MAX,
MEM_VOLATILE_P (str_rtx), &best_mode))
addr_mode = best_mode;
str_rtx = adjust_bitfield_address_size (str_rtx, addr_mode,
offset, size);
}
if (!store_bit_field_1 (str_rtx, bitsize, bitnum,
bitregion_start, bitregion_end,
fieldmode, value, reverse, true))
gcc_unreachable ();
}
/* Use shifts and boolean operations to store VALUE into a bit field of
width BITSIZE in OP0, starting at bit BITNUM. If OP0_MODE is defined,
it is the mode of OP0, otherwise OP0 is a BLKmode MEM. VALUE_MODE is
the mode of VALUE.
If REVERSE is true, the store is to be done in reverse order. */
static void
store_fixed_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
poly_uint64 bitregion_start, poly_uint64 bitregion_end,
rtx value, scalar_int_mode value_mode, bool reverse)
{
/* There is a case not handled here:
a structure with a known alignment of just a halfword
and a field split across two aligned halfwords within the structure.
Or likewise a structure with a known alignment of just a byte
and a field split across two bytes.
Such cases are not supposed to be able to occur. */
scalar_int_mode best_mode;
if (MEM_P (op0))
{
unsigned int max_bitsize = BITS_PER_WORD;
scalar_int_mode imode;
if (op0_mode.exists (&imode) && GET_MODE_BITSIZE (imode) < max_bitsize)
max_bitsize = GET_MODE_BITSIZE (imode);
if (!get_best_mode (bitsize, bitnum, bitregion_start, bitregion_end,
MEM_ALIGN (op0), max_bitsize, MEM_VOLATILE_P (op0),
&best_mode))
{
/* The only way this should occur is if the field spans word
boundaries. */
store_split_bit_field (op0, op0_mode, bitsize, bitnum,
bitregion_start, bitregion_end,
value, value_mode, reverse);
return;
}
op0 = narrow_bit_field_mem (op0, best_mode, bitsize, bitnum, &bitnum);
}
else
best_mode = op0_mode.require ();
store_fixed_bit_field_1 (op0, best_mode, bitsize, bitnum,
value, value_mode, reverse);
}
/* Helper function for store_fixed_bit_field, stores
the bit field always using MODE, which is the mode of OP0. The other
arguments are as for store_fixed_bit_field. */
static void
store_fixed_bit_field_1 (rtx op0, scalar_int_mode mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
rtx value, scalar_int_mode value_mode, bool reverse)
{
rtx temp;
int all_zero = 0;
int all_one = 0;
/* Note that bitsize + bitnum can be greater than GET_MODE_BITSIZE (mode)
for invalid input, such as f5 from gcc.dg/pr48335-2.c. */
if (reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
/* BITNUM is the distance between our msb
and that of the containing datum.
Convert it to the distance from the lsb. */
bitnum = GET_MODE_BITSIZE (mode) - bitsize - bitnum;
/* Now BITNUM is always the distance between our lsb
and that of OP0. */
/* Shift VALUE left by BITNUM bits. If VALUE is not constant,
we must first convert its mode to MODE. */
if (CONST_INT_P (value))
{
unsigned HOST_WIDE_INT v = UINTVAL (value);
if (bitsize < HOST_BITS_PER_WIDE_INT)
v &= (HOST_WIDE_INT_1U << bitsize) - 1;
if (v == 0)
all_zero = 1;
else if ((bitsize < HOST_BITS_PER_WIDE_INT
&& v == (HOST_WIDE_INT_1U << bitsize) - 1)
|| (bitsize == HOST_BITS_PER_WIDE_INT
&& v == HOST_WIDE_INT_M1U))
all_one = 1;
value = lshift_value (mode, v, bitnum);
}
else
{
int must_and = (GET_MODE_BITSIZE (value_mode) != bitsize
&& bitnum + bitsize != GET_MODE_BITSIZE (mode));
if (value_mode != mode)
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 (bitnum > 0)
value = expand_shift (LSHIFT_EXPR, mode, value,
bitnum, NULL_RTX, 1);
}
if (reverse)
value = flip_storage_order (mode, value);
/* Now clear the chosen bits in OP0,
except that if VALUE is -1 we need not bother. */
/* We keep the intermediates in registers to allow CSE to combine
consecutive bitfield assignments. */
temp = force_reg (mode, op0);
if (! all_one)
{
rtx mask = mask_rtx (mode, bitnum, bitsize, 1);
if (reverse)
mask = flip_storage_order (mode, mask);
temp = expand_binop (mode, and_optab, temp, mask,
NULL_RTX, 1, OPTAB_LIB_WIDEN);
temp = force_reg (mode, temp);
}
/* Now logical-or VALUE into OP0, unless it is zero. */
if (! all_zero)
{
temp = expand_binop (mode, ior_optab, temp, value,
NULL_RTX, 1, OPTAB_LIB_WIDEN);
temp = force_reg (mode, temp);
}
if (op0 != temp)
{
op0 = copy_rtx (op0);
emit_move_insn (op0, temp);
}
}
/* Store a bit field that is split across multiple accessible memory objects.
OP0 is the REG, SUBREG or MEM rtx for the first of the objects.
BITSIZE is the field width; BITPOS the position of its first bit
(within the word).
VALUE is the value to store, which has mode VALUE_MODE.
If OP0_MODE is defined, it is the mode of OP0, otherwise OP0 is
a BLKmode MEM.
If REVERSE is true, the store is to be done in reverse order.
This does not yet handle fields wider than BITS_PER_WORD. */
static void
store_split_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitpos,
poly_uint64 bitregion_start, poly_uint64 bitregion_end,
rtx value, scalar_int_mode value_mode, bool reverse)
{
unsigned int unit, total_bits, bitsdone = 0;
/* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
much at a time. */
if (REG_P (op0) || GET_CODE (op0) == SUBREG)
unit = BITS_PER_WORD;
else
unit = MIN (MEM_ALIGN (op0), BITS_PER_WORD);
/* If OP0 is a memory with a mode, then UNIT must not be larger than
OP0's mode as well. Otherwise, store_fixed_bit_field will call us
again, and we will mutually recurse forever. */
if (MEM_P (op0) && op0_mode.exists ())
unit = MIN (unit, GET_MODE_BITSIZE (op0_mode.require ()));
/* If VALUE is a constant other than a CONST_INT, get it into a register in
WORD_MODE. If we can do this using gen_lowpart_common, do so. Note
that VALUE might be a floating-point constant. */
if (CONSTANT_P (value) && !CONST_INT_P (value))
{
rtx word = gen_lowpart_common (word_mode, value);
if (word && (value != word))
value = word;
else
value = gen_lowpart_common (word_mode, force_reg (value_mode, value));
value_mode = word_mode;
}
total_bits = GET_MODE_BITSIZE (value_mode);
while (bitsdone < bitsize)
{
unsigned HOST_WIDE_INT thissize;
unsigned HOST_WIDE_INT thispos;
unsigned HOST_WIDE_INT offset;
rtx part;
offset = (bitpos + bitsdone) / unit;
thispos = (bitpos + bitsdone) % unit;
/* When region of bytes we can touch is restricted, decrease
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 (maybe_ne (bitregion_end, 0U)
&& unit > BITS_PER_UNIT
&& maybe_gt (bitpos + bitsdone - thispos + unit, bitregion_end + 1)
&& !REG_P (op0)
&& (GET_CODE (op0) != SUBREG || !REG_P (SUBREG_REG (op0))))
{
unit = unit / 2;
continue;
}
/* THISSIZE must not overrun a word boundary. Otherwise,
store_fixed_bit_field will call us again, and we will mutually
recurse forever. */
thissize = MIN (bitsize - bitsdone, BITS_PER_WORD);
thissize = MIN (thissize, unit - thispos);
if (reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
{
/* Fetch successively less significant portions. */
if (CONST_INT_P (value))
part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
>> (bitsize - bitsdone - thissize))
& ((HOST_WIDE_INT_1 << thissize) - 1));
/* Likewise, but the source is little-endian. */
else if (reverse)
part = extract_fixed_bit_field (word_mode, value, value_mode,
thissize,
bitsize - bitsdone - thissize,
NULL_RTX, 1, false);
else
/* The args are chosen so that the last part includes the
lsb. Give extract_bit_field the value it needs (with
endianness compensation) to fetch the piece we want. */
part = extract_fixed_bit_field (word_mode, value, value_mode,
thissize,
total_bits - bitsize + bitsdone,
NULL_RTX, 1, false);
}
else
{
/* Fetch successively more significant portions. */
if (CONST_INT_P (value))
part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
>> bitsdone)
& ((HOST_WIDE_INT_1 << thissize) - 1));
/* Likewise, but the source is big-endian. */
else if (reverse)
part = extract_fixed_bit_field (word_mode, value, value_mode,
thissize,
total_bits - bitsdone - thissize,
NULL_RTX, 1, false);
else
part = extract_fixed_bit_field (word_mode, value, value_mode,
thissize, bitsdone, NULL_RTX,
1, false);
}
/* If OP0 is a register, then handle OFFSET here. */
rtx op0_piece = op0;
opt_scalar_int_mode op0_piece_mode = op0_mode;
if (SUBREG_P (op0) || REG_P (op0))
{
scalar_int_mode imode;
if (op0_mode.exists (&imode)
&& GET_MODE_SIZE (imode) < UNITS_PER_WORD)
{
if (offset)
op0_piece = const0_rtx;
}
else
{
op0_piece = operand_subword_force (op0,
offset * unit / BITS_PER_WORD,
GET_MODE (op0));
op0_piece_mode = word_mode;
}
offset &= BITS_PER_WORD / unit - 1;
}
/* OFFSET is in UNITs, and UNIT is in bits. If WORD is const0_rtx,
it is just an out-of-bounds access. Ignore it. */
if (op0_piece != const0_rtx)
store_fixed_bit_field (op0_piece, op0_piece_mode, thissize,
offset * unit + thispos, bitregion_start,
bitregion_end, part, word_mode, reverse);
bitsdone += thissize;
}
}
/* A subroutine of extract_bit_field_1 that converts return value X
to either MODE or TMODE. MODE, TMODE and UNSIGNEDP are arguments
to extract_bit_field. */
static rtx
convert_extracted_bit_field (rtx x, machine_mode mode,
machine_mode tmode, bool unsignedp)
{
if (GET_MODE (x) == tmode || GET_MODE (x) == mode)
return x;
/* If the x mode is not a scalar integral, first convert to the
integer mode of that size and then access it as a floating-point
value via a SUBREG. */
if (!SCALAR_INT_MODE_P (tmode))
{
scalar_int_mode int_mode = int_mode_for_mode (tmode).require ();
x = convert_to_mode (int_mode, x, unsignedp);
x = force_reg (int_mode, x);
return gen_lowpart (tmode, x);
}
return convert_to_mode (tmode, x, unsignedp);
}
/* Try to use an ext(z)v pattern to extract a field from OP0.
Return the extracted value on success, otherwise return null.
EXTV describes the extraction instruction to use. If OP0_MODE
is defined, it is the mode of OP0, otherwise OP0 is a BLKmode MEM.
The other arguments are as for extract_bit_field. */
static rtx
extract_bit_field_using_extv (const extraction_insn *extv, 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)
{
class expand_operand ops[4];
rtx spec_target = target;
rtx spec_target_subreg = 0;
scalar_int_mode ext_mode = extv->field_mode;
unsigned unit = GET_MODE_BITSIZE (ext_mode);
if (bitsize == 0 || unit < bitsize)
return NULL_RTX;
if (MEM_P (op0))
/* Get a reference to the first byte of the field. */
op0 = narrow_bit_field_mem (op0, extv->struct_mode, bitsize, bitnum,
&bitnum);
else
{
/* Convert from counting within OP0 to counting in EXT_MODE. */
if (BYTES_BIG_ENDIAN)
bitnum += unit - GET_MODE_BITSIZE (op0_mode.require ());
/* If op0 is a register, we need it in EXT_MODE to make it
acceptable to the format of ext(z)v. */
if (GET_CODE (op0) == SUBREG && op0_mode.require () != ext_mode)
return NULL_RTX;
if (REG_P (op0) && op0_mode.require () != ext_mode)
op0 = gen_lowpart_SUBREG (ext_mode, op0);
}
/* If BITS_BIG_ENDIAN is zero on a BYTES_BIG_ENDIAN machine, we count
"backwards" from the size of the unit we are extracting from.
Otherwise, we count bits from the most significant on a
BYTES/BITS_BIG_ENDIAN machine. */
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
bitnum = unit - bitsize - bitnum;
if (target == 0)
target = spec_target = gen_reg_rtx (tmode);
if (GET_MODE (target) != ext_mode)
{
rtx temp;
/* Don't use LHS paradoxical subreg if explicit truncation is needed
between the mode of the extraction (word_mode) and the target
mode. Instead, create a temporary and use convert_move to set
the target. */
if (REG_P (target)
&& TRULY_NOOP_TRUNCATION_MODES_P (GET_MODE (target), ext_mode)
&& (temp = gen_lowpart_if_possible (ext_mode, target)))
{
target = temp;
if (partial_subreg_p (GET_MODE (spec_target), ext_mode))
spec_target_subreg = target;
}
else
target = gen_reg_rtx (ext_mode);
}
create_output_operand (&ops[0], target, ext_mode);
create_fixed_operand (&ops[1], op0);
create_integer_operand (&ops[2], bitsize);
create_integer_operand (&ops[3], bitnum);
if (maybe_expand_insn (extv->icode, 4, ops))
{
target = ops[0].value;
if (target == spec_target)
return target;
if (target == spec_target_subreg)
return spec_target;
return convert_extracted_bit_field (target, mode, tmode, unsignedp);
}
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, 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 (tmode == VOIDmode)
tmode = mode;
while (GET_CODE (op0) == SUBREG)
{
bitnum += SUBREG_BYTE (op0) * BITS_PER_UNIT;
op0 = SUBREG_REG (op0);
}
/* 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) && known_ge (bitnum, GET_MODE_BITSIZE (GET_MODE (op0))))
return gen_reg_rtx (tmode);
if (REG_P (op0)
&& mode == GET_MODE (op0)
&& known_eq (bitnum, 0U)
&& known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (op0))))
{
if (reverse)
op0 = flip_storage_order (mode, op0);
/* We're trying to extract a full register from itself. */
return op0;
}
/* First try to check for vector from vector extractions. */
if (VECTOR_MODE_P (GET_MODE (op0))
&& !MEM_P (op0)
&& VECTOR_MODE_P (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);
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)
&& multiple_p (bitnum, GET_MODE_BITSIZE (tmode), &pos))
{
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);
if (new_mode != GET_MODE (op0))
op0 = gen_lowpart (new_mode, op0);
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;
}
}
}
/* See if we can get a better vector mode before extracting. */
if (VECTOR_MODE_P (GET_MODE (op0))
&& !MEM_P (op0)
&& GET_MODE_INNER (GET_MODE (op0)) != tmode)
{
machine_mode new_mode;
if (GET_MODE_CLASS (tmode) == MODE_FLOAT)
new_mode = MIN_MODE_VECTOR_FLOAT;
else if (GET_MODE_CLASS (tmode) == MODE_FRACT)
new_mode = MIN_MODE_VECTOR_FRACT;
else if (GET_MODE_CLASS (tmode) == MODE_UFRACT)
new_mode = MIN_MODE_VECTOR_UFRACT;
else if (GET_MODE_CLASS (tmode) == MODE_ACCUM)
new_mode = MIN_MODE_VECTOR_ACCUM;
else if (GET_MODE_CLASS (tmode) == MODE_UACCUM)
new_mode = MIN_MODE_VECTOR_UACCUM;
else
new_mode = MIN_MODE_VECTOR_INT;
FOR_EACH_MODE_FROM (new_mode, new_mode)
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)
op0 = gen_lowpart (new_mode, op0);
}
/* Use vec_extract patterns for extracting parts of vectors whenever
available. If that fails, see whether the current modes and bitregion
give a natural subreg. */
machine_mode outermode = GET_MODE (op0);
if (VECTOR_MODE_P (outermode) && !MEM_P (op0))
{
scalar_mode innermode = GET_MODE_INNER (outermode);
enum insn_code icode
= convert_optab_handler (vec_extract_optab, outermode, 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))
{
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))
{
rtx sub = extract_bit_field_as_subreg (mode, op0, bitsize, bitnum);
if (sub)
return sub;
}
}
/* Make sure we are playing with integral modes. Pun with subregs
if we aren't. */
opt_scalar_int_mode op0_mode = int_mode_for_mode (GET_MODE (op0));
scalar_int_mode imode;
if (!op0_mode.exists (&imode) || imode != 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 (&imode))
{
op0 = gen_lowpart (imode, op0);
/* If we got a SUBREG, force it into a register since we
aren't going to be able to do another SUBREG on it. */
if (GET_CODE (op0) == SUBREG)
op0 = force_reg (imode, op0);
}
else
{
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);
}
}
/* ??? 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. */
/* Get the mode of the field to use for atomic access or subreg
conversion. */
if (!SCALAR_INT_MODE_P (tmode)
|| !mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0).exists (&mode1))
mode1 = mode;
gcc_assert (mode1 != BLKmode);
/* 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)
{
rtx sub = extract_bit_field_as_subreg (mode1, op0, bitsize, bitnum);
if (sub)
return convert_extracted_bit_field (sub, mode, tmode, unsignedp);
}
/* Extraction of a full MODE1 value can be done with a load as long as
the field is on a byte boundary and is sufficiently aligned. */
poly_uint64 bytenum;
if (simple_mem_bitfield_p (op0, bitsize, bitnum, mode1, &bytenum))
{
op0 = adjust_bitfield_address (op0, mode1, bytenum);
if (reverse)
op0 = flip_storage_order (mode1, op0);
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)
{
/* Here we transfer the words of the field
in the order least significant first.
This is because the most significant word is the one which may
be less than full. */
const bool backwards = WORDS_BIG_ENDIAN;
unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
unsigned int i;
rtx_insn *last;
if (target == 0 || !REG_P (target) || !valid_multiword_target_p (target))
target = gen_reg_rtx (mode);
/* 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, 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 0, use the low-order word in both field and target;
if I is 1, use the next to lowest word; and so on. */
/* Word number in TARGET to use. */
unsigned int wordnum
= (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)
* BITS_PER_WORD,
0)
: (int) i * BITS_PER_WORD);
rtx target_part = operand_subword (target, wordnum, 1, VOIDmode);
rtx result_part
= extract_bit_field_1 (op0, MIN (BITS_PER_WORD,
bitsize - i * BITS_PER_WORD),
bitnum + bit_offset, 1, target_part,
mode, word_mode, reverse, fallback_p, NULL);
gcc_assert (target_part);
if (!result_part)
{
delete_insns_since (last);
return NULL;
}
if (result_part != target_part)
emit_move_insn (target_part, result_part);
}
if (unsignedp)
{
/* Unless we've filled TARGET, the upper regs in a multi-reg value
need to be zero'd out. */
if (target_size > nwords * UNITS_PER_WORD)
{
unsigned int i, total_words;
total_words = target_size / UNITS_PER_WORD;
for (i = nwords; i < total_words; i++)
emit_move_insn
(operand_subword (target,
backwards ? total_words - i - 1 : i,
1, VOIDmode),
const0_rtx);
}
return target;
}
/* Signed bit field: sign-extend with two arithmetic shifts. */
target = expand_shift (LSHIFT_EXPR, mode, target,
GET_MODE_BITSIZE (mode) - bitsize, NULL_RTX, 0);
return expand_shift (RSHIFT_EXPR, mode, target,
GET_MODE_BITSIZE (mode) - bitsize, NULL_RTX, 0);
}
/* If OP0 is a multi-word register, narrow it to the affected word.
If the region spans two words, defer to extract_split_bit_field. */
if (!MEM_P (op0) && GET_MODE_SIZE (op0_mode.require ()) > UNITS_PER_WORD)
{
if (bitnum % BITS_PER_WORD + bitsize > BITS_PER_WORD)
{
if (!fallback_p)
return NULL_RTX;
target = extract_split_bit_field (op0, op0_mode, bitsize, bitnum,
unsignedp, reverse);
return convert_extracted_bit_field (target, mode, tmode, unsignedp);
}
op0 = simplify_gen_subreg (word_mode, op0, op0_mode.require (),
bitnum / BITS_PER_WORD * UNITS_PER_WORD);
op0_mode = word_mode;
bitnum %= BITS_PER_WORD;
}
/* From here on we know the desired field is smaller than a word.
If OP0 is a register, it too fits within a word. */
enum extraction_pattern pattern = unsignedp ? EP_extzv : EP_extv;
extraction_insn extv;
if (!MEM_P (op0)
&& !reverse
/* ??? We could limit the structure size to the part of OP0 that
contains the field, with appropriate checks for endianness
and TARGET_TRULY_NOOP_TRUNCATION. */
&& get_best_reg_extraction_insn (&extv, pattern,
GET_MODE_BITSIZE (op0_mode.require ()),
tmode))
{
rtx result = extract_bit_field_using_extv (&extv, op0, op0_mode,
bitsize, bitnum,
unsignedp, target, mode,
tmode);
if (result)
return result;
}
/* If OP0 is a memory, try copying it to a register and seeing if a
cheap register alternative is available. */
if (MEM_P (op0) & !reverse)
{
if (get_best_mem_extraction_insn (&extv, pattern, bitsize, bitnum,
tmode))
{
rtx result = extract_bit_field_using_extv (&extv, op0, op0_mode,
bitsize, bitnum,
unsignedp, target, mode,
tmode);
if (result)
return result;
}
rtx_insn *last = get_last_insn ();
/* Try loading part of OP0 into a register and extracting the
bitfield from that. */
unsigned HOST_WIDE_INT bitpos;
rtx xop0 = adjust_bit_field_mem_for_reg (pattern, op0, bitsize, bitnum,
0, 0, tmode, &bitpos);
if (xop0)
{
xop0 = copy_to_reg (xop0);
rtx result = extract_bit_field_1 (xop0, bitsize, bitpos,
unsignedp, target,
mode, tmode, reverse, false, NULL);
if (result)
return result;
delete_insns_since (last);
}
}
if (!fallback_p)
return NULL;
/* Find a correspondingly-sized integer field, so we can apply
shifts and masks to it. */
scalar_int_mode int_mode;
if (!int_mode_for_mode (tmode).exists (&int_mode))
/* If this fails, we should probably push op0 out to memory and then
do a load. */
int_mode = int_mode_for_mode (mode).require ();
target = extract_fixed_bit_field (int_mode, op0, op0_mode, bitsize,
bitnum, target, unsignedp, reverse);
/* Complex values must be reversed piecewise, so we need to undo the global
reversal, convert to the complex mode and reverse again. */
if (reverse && COMPLEX_MODE_P (tmode))
{
target = flip_storage_order (int_mode, target);
target = convert_extracted_bit_field (target, mode, tmode, unsignedp);
target = flip_storage_order (tmode, target);
}
else
target = convert_extracted_bit_field (target, mode, tmode, unsignedp);
return target;
}
/* Generate code to extract a byte-field from STR_RTX
containing BITSIZE bits, starting at BITNUM,
and put it in TARGET if possible (if TARGET is nonzero).
Regardless of TARGET, we return the rtx for where the value is placed.
STR_RTX is the structure containing the byte (a REG or MEM).
UNSIGNEDP is nonzero if this is an unsigned bit field.
MODE is the natural mode of the field value once extracted.
TMODE is the mode the caller would like the value to have;
but the value may be returned with type MODE instead.
If REVERSE is true, the extraction is to be done in reverse order.
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 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, 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 (maybe_ne (GET_MODE_BITSIZE (GET_MODE (str_rtx)), 0))
mode1 = GET_MODE (str_rtx);
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 (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 (ibitsize == GET_MODE_BITSIZE (int_mode))
{
rtx result = adjust_bitfield_address (str_rtx, int_mode,
ibitnum / BITS_PER_UNIT);
if (reverse)
result = flip_storage_order (int_mode, result);
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, 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,
target, mode, tmode, reverse, true, alt_rtl);
}
/* Use shifts and boolean operations to extract a field of BITSIZE bits
from bit BITNUM of OP0. If OP0_MODE is defined, it is the mode of OP0,
otherwise OP0 is a BLKmode MEM.
UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value).
If REVERSE is true, the extraction is to be done in reverse order.
If TARGET is nonzero, attempts to store the value there
and return TARGET, but this is not guaranteed.
If TARGET is not used, create a pseudo-reg of mode TMODE for the value. */
static rtx
extract_fixed_bit_field (machine_mode tmode, rtx op0,
opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, rtx target,
int unsignedp, bool reverse)
{
scalar_int_mode mode;
if (MEM_P (op0))
{
if (!get_best_mode (bitsize, bitnum, 0, 0, MEM_ALIGN (op0),
BITS_PER_WORD, MEM_VOLATILE_P (op0), &mode))
/* The only way this should occur is if the field spans word
boundaries. */
return extract_split_bit_field (op0, op0_mode, bitsize, bitnum,
unsignedp, reverse);
op0 = narrow_bit_field_mem (op0, mode, bitsize, bitnum, &bitnum);
}
else
mode = op0_mode.require ();
return extract_fixed_bit_field_1 (tmode, op0, mode, bitsize, bitnum,
target, unsignedp, reverse);
}
/* Helper function for extract_fixed_bit_field, extracts
the bit field always using MODE, which is the mode of OP0.
The other arguments are as for extract_fixed_bit_field. */
static rtx
extract_fixed_bit_field_1 (machine_mode tmode, rtx op0, scalar_int_mode mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, rtx target,
int unsignedp, bool reverse)
{
/* Note that bitsize + bitnum can be greater than GET_MODE_BITSIZE (mode)
for invalid input, such as extract equivalent of f5 from
gcc.dg/pr48335-2.c. */
if (reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
/* BITNUM is the distance between our msb and that of OP0.
Convert it to the distance from the lsb. */
bitnum = GET_MODE_BITSIZE (mode) - bitsize - bitnum;
/* Now BITNUM is always the distance between the field's lsb and that of OP0.
We have reduced the big-endian case to the little-endian case. */
if (reverse)
op0 = flip_storage_order (mode, op0);
if (unsignedp)
{
if (bitnum)
{
/* If the field does not already start at the lsb,
shift it so it does. */
/* Maybe propagate the target for the shift. */
rtx subtarget = (target != 0 && REG_P (target) ? target : 0);
if (tmode != mode)
subtarget = 0;
op0 = expand_shift (RSHIFT_EXPR, mode, op0, bitnum, subtarget, 1);
}
/* Convert the value to the desired mode. TMODE must also be a
scalar integer for this conversion to make sense, since we
shouldn't reinterpret the bits. */
scalar_int_mode new_mode = as_a <scalar_int_mode> (tmode);
if (mode != new_mode)
op0 = convert_to_mode (new_mode, op0, 1);
/* Unless the msb of the field used to be the msb when we shifted,
mask out the upper bits. */
if (GET_MODE_BITSIZE (mode) != bitnum + bitsize)
return expand_binop (new_mode, and_optab, op0,
mask_rtx (new_mode, 0, bitsize, 0),
target, 1, OPTAB_LIB_WIDEN);
return op0;
}
/* To extract a signed bit-field, first shift its msb to the msb of the word,
then arithmetic-shift its lsb to the lsb of the word. */
op0 = force_reg (mode, op0);
/* Find the narrowest integer mode that contains the field. */
opt_scalar_int_mode mode_iter;
FOR_EACH_MODE_IN_CLASS (mode_iter, MODE_INT)
if (GET_MODE_BITSIZE (mode_iter.require ()) >= bitsize + bitnum)
break;
mode = mode_iter.require ();
op0 = convert_to_mode (mode, op0, 0);
if (mode != tmode)
target = 0;
if (GET_MODE_BITSIZE (mode) != (bitsize + bitnum))
{
int amount = GET_MODE_BITSIZE (mode) - (bitsize + bitnum);
/* Maybe propagate the target for the shift. */
rtx subtarget = (target != 0 && REG_P (target) ? target : 0);
op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1);
}
return expand_shift (RSHIFT_EXPR, mode, op0,
GET_MODE_BITSIZE (mode) - bitsize, target, 0);
}
/* Return a constant integer (CONST_INT or CONST_DOUBLE) rtx with the value
VALUE << BITPOS. */
static rtx
lshift_value (machine_mode mode, unsigned HOST_WIDE_INT value,
int bitpos)
{
return immed_wide_int_const (wi::lshift (value, bitpos), mode);
}
/* Extract a bit field that is split across two words
and return an RTX for the result.
OP0 is the REG, SUBREG or MEM rtx for the first of the two words.
BITSIZE is the field width; BITPOS, position of its first bit, in the word.
UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend.
If OP0_MODE is defined, it is the mode of OP0, otherwise OP0 is
a BLKmode MEM.
If REVERSE is true, the extraction is to be done in reverse order. */
static rtx
extract_split_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitpos, int unsignedp,
bool reverse)
{
unsigned int unit;
unsigned int bitsdone = 0;
rtx result = NULL_RTX;
int first = 1;
/* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
much at a time. */
if (REG_P (op0) || 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;
unsigned HOST_WIDE_INT thispos;
unsigned HOST_WIDE_INT offset;
offset = (bitpos + bitsdone) / unit;
thispos = (bitpos + bitsdone) % unit;
/* THISSIZE must not overrun a word boundary. Otherwise,
extract_fixed_bit_field will call us again, and we will mutually
recurse forever. */
thissize = MIN (bitsize - bitsdone, BITS_PER_WORD);
thissize = MIN (thissize, unit - thispos);
/* If OP0 is a register, then handle OFFSET here. */
rtx op0_piece = op0;
opt_scalar_int_mode op0_piece_mode = op0_mode;
if (SUBREG_P (op0) || REG_P (op0))
{
op0_piece = operand_subword_force (op0, offset, op0_mode.require ());
op0_piece_mode = word_mode;
offset = 0;
}
/* Extract the parts in bit-counting order,
whose meaning is determined by BYTES_PER_UNIT.
OFFSET is in UNITs, and UNIT is in bits. */
part = extract_fixed_bit_field (word_mode, op0_piece, op0_piece_mode,
thissize, offset * unit + thispos,
0, 1, reverse);
bitsdone += thissize;
/* Shift this part into place for the result. */
if (reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
{
if (bitsize != bitsdone)
part = expand_shift (LSHIFT_EXPR, word_mode, part,
bitsize - bitsdone, 0, 1);
}
else
{
if (bitsdone != thissize)
part = expand_shift (LSHIFT_EXPR, word_mode, part,
bitsdone - thissize, 0, 1);
}
if (first)
result = part;
else
/* Combine the parts with bitwise or. This works
because we extracted each part as an unsigned bit field. */
result = expand_binop (word_mode, ior_optab, part, result, NULL_RTX, 1,
OPTAB_LIB_WIDEN);
first = 0;
}
/* Unsigned bit field: we are done. */
if (unsignedp)
return result;
/* Signed bit field: sign-extend with two arithmetic shifts. */
result = expand_shift (LSHIFT_EXPR, word_mode, result,
BITS_PER_WORD - bitsize, NULL_RTX, 0);
return expand_shift (RSHIFT_EXPR, word_mode, result,
BITS_PER_WORD - bitsize, NULL_RTX, 0);
}
/* Try to read the low bits of SRC as an rvalue of mode MODE, preserving
the bit pattern. SRC_MODE is the mode of SRC; if this is smaller than
MODE, fill the upper bits with zeros. Fail if the layout of either
mode is unknown (as for CC modes) or if the extraction would involve
unprofitable mode punning. Return the value on success, otherwise
return null.
This is different from gen_lowpart* in these respects:
- the returned value must always be considered an rvalue
- when MODE is wider than SRC_MODE, the extraction involves
a zero extension
- when MODE is smaller than SRC_MODE, the extraction involves
a truncation (and is thus subject to TARGET_TRULY_NOOP_TRUNCATION).
In other words, this routine performs a computation, whereas the
gen_lowpart* routines are conceptually lvalue or rvalue subreg
operations. */
rtx
extract_low_bits (machine_mode mode, machine_mode src_mode, rtx src)
{
scalar_int_mode int_mode, src_int_mode;
if (mode == src_mode)
return src;
if (CONSTANT_P (src))
{
/* simplify_gen_subreg can't be used here, as if simplify_subreg
fails, it will happily create (subreg (symbol_ref)) or similar
invalid SUBREGs. */
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 (src) == VOIDmode
|| !validate_subreg (mode, src_mode, src, byte))
return NULL_RTX;
src = force_reg (GET_MODE (src), src);
return gen_rtx_SUBREG (mode, src, byte);
}
if (GET_MODE_CLASS (mode) == MODE_CC || GET_MODE_CLASS (src_mode) == MODE_CC)
return NULL_RTX;
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);
if (x)
return x;
}
if (!int_mode_for_mode (src_mode).exists (&src_int_mode)
|| !int_mode_for_mode (mode).exists (&int_mode))
return NULL_RTX;
if (!targetm.modes_tieable_p (src_int_mode, src_mode))
return NULL_RTX;
if (!targetm.modes_tieable_p (int_mode, mode))
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;
}
/* Add INC into TARGET. */
void
expand_inc (rtx target, rtx inc)
{
rtx value = expand_binop (GET_MODE (target), add_optab,
target, inc,
target, 0, OPTAB_LIB_WIDEN);
if (value != target)
emit_move_insn (target, value);
}
/* Subtract DEC from TARGET. */
void
expand_dec (rtx target, rtx dec)
{
rtx value = expand_binop (GET_MODE (target), sub_optab,
target, dec,
target, 0, OPTAB_LIB_WIDEN);
if (value != target)
emit_move_insn (target, value);
}
/* Output a shift instruction for expression code CODE,
with SHIFTED being the rtx for the value to shift,
and AMOUNT the rtx for the amount to shift by.
Store the result in the rtx TARGET, if that is convenient.
If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic.
Return the rtx for where the value is.
If that cannot be done, abort the compilation unless MAY_FAIL is true,
in which case 0 is returned. */
static rtx
expand_shift_1 (enum tree_code code, machine_mode mode, rtx shifted,
rtx amount, rtx target, int unsignedp, bool may_fail = false)
{
rtx op1, temp = 0;
int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR);
optab lshift_optab = ashl_optab;
optab rshift_arith_optab = ashr_optab;
optab rshift_uns_optab = lshr_optab;
optab lrotate_optab = rotl_optab;
optab rrotate_optab = rotr_optab;
machine_mode op1_mode;
scalar_mode scalar_mode = GET_MODE_INNER (mode);
int attempt;
bool speed = optimize_insn_for_speed_p ();
op1 = amount;
op1_mode = GET_MODE (op1);
/* Determine whether the shift/rotate amount is a vector, or scalar. If the
shift amount is a vector, use the vector/vector shift patterns. */
if (VECTOR_MODE_P (mode) && VECTOR_MODE_P (op1_mode))
{
lshift_optab = vashl_optab;
rshift_arith_optab = vashr_optab;
rshift_uns_optab = vlshr_optab;
lrotate_optab = vrotl_optab;
rrotate_optab = vrotr_optab;
}
/* Previously detected shift-counts computed by NEGATE_EXPR
and shifted in the other direction; but that does not work
on all machines. */
if (SHIFT_COUNT_TRUNCATED)
{
if (CONST_INT_P (op1)
&& ((unsigned HOST_WIDE_INT) INTVAL (op1) >=
(unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (scalar_mode)))
op1 = gen_int_shift_amount (mode,
(unsigned HOST_WIDE_INT) INTVAL (op1)
% GET_MODE_BITSIZE (scalar_mode));
else if (GET_CODE (op1) == SUBREG
&& subreg_lowpart_p (op1)
&& SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (op1)))
&& SCALAR_INT_MODE_P (GET_MODE (op1)))
op1 = SUBREG_REG (op1);
}
/* Canonicalize rotates by constant amount. If op1 is bitsize / 2,
prefer left rotation, if op1 is from bitsize / 2 + 1 to
bitsize - 1, use other direction of rotate with 1 .. bitsize / 2 - 1
amount instead. */
if (rotate
&& CONST_INT_P (op1)
&& IN_RANGE (INTVAL (op1), GET_MODE_BITSIZE (scalar_mode) / 2 + left,
GET_MODE_BITSIZE (scalar_mode) - 1))
{
op1 = gen_int_shift_amount (mode, (GET_MODE_BITSIZE (scalar_mode)
- INTVAL (op1)));
left = !left;
code = left ? LROTATE_EXPR : RROTATE_EXPR;
}
/* Rotation of 16bit values by 8 bits is effectively equivalent to a bswaphi.
Note that this is not the case for bigger values. For instance a rotation
of 0x01020304 by 16 bits gives 0x03040102 which is different from
0x04030201 (bswapsi). */
if (rotate
&& CONST_INT_P (op1)
&& INTVAL (op1) == BITS_PER_UNIT
&& GET_MODE_SIZE (scalar_mode) == 2
&& optab_handler (bswap_optab, mode) != CODE_FOR_nothing)
return expand_unop (mode, bswap_optab, shifted, NULL_RTX, unsignedp);
if (op1 == const0_rtx)
return shifted;
/* Check whether its cheaper to implement a left shift by a constant
bit count by a sequence of additions. */
if (code == LSHIFT_EXPR
&& CONST_INT_P (op1)
&& INTVAL (op1) > 0
&& INTVAL (op1) < GET_MODE_PRECISION (scalar_mode)
&& INTVAL (op1) < MAX_BITS_PER_WORD
&& (shift_cost (speed, mode, INTVAL (op1))
> INTVAL (op1) * add_cost (speed, mode))
&& shift_cost (speed, mode, INTVAL (op1)) != MAX_COST)
{
int i;
for (i = 0; i < INTVAL (op1); i++)
{
temp = force_reg (mode, shifted);
shifted = expand_binop (mode, add_optab, temp, temp, NULL_RTX,
unsignedp, OPTAB_LIB_WIDEN);
}
return shifted;
}
for (attempt = 0; temp == 0 && attempt < 3; attempt++)
{
enum optab_methods methods;
if (attempt == 0)
methods = OPTAB_DIRECT;
else if (attempt == 1)
methods = OPTAB_WIDEN;
else
methods = OPTAB_LIB_WIDEN;
if (rotate)
{
/* Widening does not work for rotation. */
if (methods == OPTAB_WIDEN)
continue;
else if (methods == OPTAB_LIB_WIDEN)
{
/* If we have been unable to open-code this by a rotation,
do it as the IOR of two shifts. I.e., to rotate A
by N bits, compute
(A << N) | ((unsigned) A >> ((-N) & (C - 1)))
where C is the bitsize of A.
It is theoretically possible that the target machine might
not be able to perform either shift and hence we would
be making two libcalls rather than just the one for the
shift (similarly if IOR could not be done). We will allow
this extremely unlikely lossage to avoid complicating the
code below. */
rtx subtarget = target == shifted ? 0 : target;
rtx new_amount, other_amount;
rtx temp1;
new_amount = op1;
if (op1 == const0_rtx)
return shifted;
else if (CONST_INT_P (op1))
other_amount = gen_int_shift_amount
(mode, GET_MODE_BITSIZE (scalar_mode) - INTVAL (op1));
else
{
other_amount
= simplify_gen_unary (NEG, GET_MODE (op1),
op1, GET_MODE (op1));
HOST_WIDE_INT mask = GET_MODE_PRECISION (scalar_mode) - 1;
other_amount
= simplify_gen_binary (AND, GET_MODE (op1), other_amount,
gen_int_mode (mask, GET_MODE (op1)));
}
shifted = force_reg (mode, shifted);
temp = expand_shift_1 (left ? LSHIFT_EXPR : RSHIFT_EXPR,
mode, shifted, new_amount, 0, 1);
temp1 = expand_shift_1 (left ? RSHIFT_EXPR : LSHIFT_EXPR,
mode, shifted, other_amount,
subtarget, 1);
return expand_binop (mode, ior_optab, temp, temp1, target,
unsignedp, methods);
}
temp = expand_binop (mode,
left ? lrotate_optab : rrotate_optab,
shifted, op1, target, unsignedp, methods);
}
else if (unsignedp)
temp = expand_binop (mode,
left ? lshift_optab : rshift_uns_optab,
shifted, op1, target, unsignedp, methods);
/* Do arithmetic shifts.
Also, if we are going to widen the operand, we can just as well
use an arithmetic right-shift instead of a logical one. */
if (temp == 0 && ! rotate
&& (! unsignedp || (! left && methods == OPTAB_WIDEN)))
{
enum optab_methods methods1 = methods;
/* If trying to widen a log shift to an arithmetic shift,
don't accept an arithmetic shift of the same size. */
if (unsignedp)
methods1 = OPTAB_MUST_WIDEN;
/* Arithmetic shift */
temp = expand_binop (mode,
left ? lshift_optab : rshift_arith_optab,
shifted, op1, target, unsignedp, methods1);
}
/* We used to try extzv here for logical right shifts, but that was
only useful for one machine, the VAX, and caused poor code
generation there for lshrdi3, so the code was deleted and a
define_expand for lshrsi3 was added to vax.md. */
}
gcc_assert (temp != NULL_RTX || may_fail);
return temp;
}
/* Output a shift instruction for expression code CODE,
with SHIFTED being the rtx for the value to shift,
and AMOUNT the amount to shift by.
Store the result in the rtx TARGET, if that is convenient.
If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic.
Return the rtx for where the value is. */
rtx
expand_shift (enum tree_code code, machine_mode mode, rtx shifted,
poly_int64 amount, rtx target, int unsignedp)
{
return expand_shift_1 (code, mode, shifted,
gen_int_shift_amount (mode, amount),
target, unsignedp);
}
/* Likewise, but return 0 if that cannot be done. */
static rtx
maybe_expand_shift (enum tree_code code, machine_mode mode, rtx shifted,
int amount, rtx target, int unsignedp)
{
return expand_shift_1 (code, mode,
shifted, GEN_INT (amount), target, unsignedp, true);
}
/* Output a shift instruction for expression code CODE,
with SHIFTED being the rtx for the value to shift,
and AMOUNT the tree for the amount to shift by.
Store the result in the rtx TARGET, if that is convenient.
If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic.
Return the rtx for where the value is. */
rtx
expand_variable_shift (enum tree_code code, machine_mode mode, rtx shifted,
tree amount, rtx target, int unsignedp)
{
return expand_shift_1 (code, mode,
shifted, expand_normal (amount), target, unsignedp);
}
static void synth_mult (struct algorithm *, unsigned HOST_WIDE_INT,
const struct mult_cost *, machine_mode mode);
static rtx expand_mult_const (machine_mode, rtx, HOST_WIDE_INT, rtx,
const struct algorithm *, enum mult_variant);
static unsigned HOST_WIDE_INT invert_mod2n (unsigned HOST_WIDE_INT, int);
static rtx extract_high_half (scalar_int_mode, rtx);
static rtx expmed_mult_highpart (scalar_int_mode, rtx, rtx, rtx, int, int);
static rtx expmed_mult_highpart_optab (scalar_int_mode, rtx, rtx, rtx,
int, int);
/* Compute and return the best algorithm for multiplying by T.
The algorithm must cost less than cost_limit
If retval.cost >= COST_LIMIT, no algorithm was found and all
other field of the returned struct are undefined.
MODE is the machine mode of the multiplication. */
static void
synth_mult (struct algorithm *alg_out, unsigned HOST_WIDE_INT t,
const struct mult_cost *cost_limit, machine_mode mode)
{
int m;
struct algorithm *alg_in, *best_alg;
struct mult_cost best_cost;
struct mult_cost new_limit;
int op_cost, op_latency;
unsigned HOST_WIDE_INT orig_t = t;
unsigned HOST_WIDE_INT q;
int maxm, hash_index;
bool cache_hit = false;
enum alg_code cache_alg = alg_zero;
bool speed = optimize_insn_for_speed_p ();
scalar_int_mode imode;
struct alg_hash_entry *entry_ptr;
/* Indicate that no algorithm is yet found. If no algorithm
is found, this value will be returned and indicate failure. */
alg_out->cost.cost = cost_limit->cost + 1;
alg_out->cost.latency = cost_limit-><