blob: 26f40e8088262e7f6b4b102b583eea272b84343b [file] [log] [blame]
/* Medium-level subroutines: convert bit-field store and extract
and shifts, multiplies and divides to rtl instructions.
Copyright (C) 1987-2015 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/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "diagnostic-core.h"
#include "rtl.h"
#include "hash-set.h"
#include "machmode.h"
#include "vec.h"
#include "double-int.h"
#include "input.h"
#include "alias.h"
#include "symtab.h"
#include "wide-int.h"
#include "inchash.h"
#include "tree.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "tm_p.h"
#include "flags.h"
#include "insn-config.h"
#include "hashtab.h"
#include "hard-reg-set.h"
#include "function.h"
#include "statistics.h"
#include "real.h"
#include "fixed-value.h"
#include "expmed.h"
#include "dojump.h"
#include "explow.h"
#include "calls.h"
#include "emit-rtl.h"
#include "varasm.h"
#include "stmt.h"
#include "expr.h"
#include "insn-codes.h"
#include "optabs.h"
#include "recog.h"
#include "langhooks.h"
#include "predict.h"
#include "basic-block.h"
#include "df.h"
#include "target.h"
struct target_expmed default_target_expmed;
#if SWITCHABLE_TARGET
struct target_expmed *this_target_expmed = &default_target_expmed;
#endif
static void store_fixed_bit_field (rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
rtx);
static void store_fixed_bit_field_1 (rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
rtx);
static void store_split_bit_field (rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
rtx);
static rtx extract_fixed_bit_field (machine_mode, rtx,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, rtx, int);
static rtx extract_fixed_bit_field_1 (machine_mode, rtx,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, rtx, int);
static rtx lshift_value (machine_mode, unsigned HOST_WIDE_INT, int);
static rtx extract_split_bit_field (rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, int);
static void do_cmp_and_jump (rtx, rtx, enum rtx_code, machine_mode, rtx_code_label *);
static rtx expand_smod_pow2 (machine_mode, rtx, HOST_WIDE_INT);
static rtx expand_sdiv_pow2 (machine_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 (machine_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) - (unsigned HOST_WIDE_INT) 1)) == 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, machine_mode to_mode,
machine_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
&& exact_log2 (to_size) != -1)
to_size --;
if (GET_MODE_CLASS (from_mode) == MODE_PARTIAL_INT
&& exact_log2 (from_size) != -1)
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, speed));
}
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, speed));
set_neg_cost (speed, mode, set_src_cost (all->neg, speed));
set_mul_cost (speed, mode, set_src_cost (all->mult, speed));
set_sdiv_cost (speed, mode, set_src_cost (all->sdiv, speed));
set_udiv_cost (speed, mode, set_src_cost (all->udiv, speed));
set_sdiv_pow2_cheap (speed, mode, (set_src_cost (all->sdiv_32, speed)
<= 2 * add_cost (speed, mode)));
set_smod_pow2_cheap (speed, mode, (set_src_cost (all->smod_32, 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, speed));
set_shiftadd_cost (speed, mode, m, set_src_cost (all->shift_add, speed));
set_shiftsub0_cost (speed, mode, m, set_src_cost (all->shift_sub0, speed));
set_shiftsub1_cost (speed, mode, m, set_src_cost (all->shift_sub1, speed));
}
if (SCALAR_INT_MODE_P (mode))
{
for (mode_from = MIN_MODE_INT; mode_from <= MAX_MODE_INT;
mode_from = (machine_mode)(mode_from + 1))
init_expmed_one_conv (all, mode, mode_from, speed);
}
if (GET_MODE_CLASS (mode) == MODE_INT)
{
machine_mode wider_mode = GET_MODE_WIDER_MODE (mode);
if (wider_mode != VOIDmode)
{
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 (mode_bitsize);
set_mul_widen_cost (speed, wider_mode,
set_src_cost (all->wide_mult, speed));
set_mul_highpart_cost (speed, mode,
set_src_cost (all->wide_trunc, 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_rtx_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, 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;
}
/* 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 BLKmode, return a 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, machine_mode mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
unsigned HOST_WIDE_INT *new_bitnum)
{
if (mode == BLKmode)
{
*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, mode, offset, size);
}
else
{
unsigned int unit = GET_MODE_BITSIZE (mode);
*new_bitnum = bitnum % unit;
HOST_WIDE_INT offset = (bitnum - *new_bitnum) / BITS_PER_UNIT;
return adjust_bitfield_address (mem, mode, offset);
}
}
/* 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,
unsigned HOST_WIDE_INT bitregion_start,
unsigned HOST_WIDE_INT 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));
machine_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. */
machine_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;
machine_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 (unsigned HOST_WIDE_INT bitnum,
unsigned HOST_WIDE_INT bitsize,
machine_mode struct_mode)
{
if (BYTES_BIG_ENDIAN)
return (bitnum % BITS_PER_UNIT == 0
&& (bitnum + bitsize == GET_MODE_BITSIZE (struct_mode)
|| (bitnum + bitsize) % BITS_PER_WORD == 0));
else
return bitnum % BITS_PER_WORD == 0;
}
/* 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,
machine_mode fieldmode,
unsigned HOST_WIDE_INT bitregion_start,
unsigned HOST_WIDE_INT 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;
/* Non-integral modes likely only happen with packed structures.
Punt. */
if (!SCALAR_INT_MODE_P (fieldmode))
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 (bitregion_end != 0
&& (bitnum - bitnum % modesize < bitregion_start
|| 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. */
static bool
simple_mem_bitfield_p (rtx op0, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, machine_mode mode)
{
return (MEM_P (op0)
&& bitnum % BITS_PER_UNIT == 0
&& bitsize == GET_MODE_BITSIZE (mode)
&& (!SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (op0))
|| (bitnum % GET_MODE_ALIGNMENT (mode) == 0
&& MEM_ALIGN (op0) >= GET_MODE_ALIGNMENT (mode))));
}
/* Try to use instruction INSV to store VALUE into a field of OP0.
BITSIZE and BITNUM are as for store_bit_field. */
static bool
store_bit_field_using_insv (const extraction_insn *insv, rtx op0,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
rtx value)
{
struct expand_operand ops[4];
rtx value1;
rtx xop0 = op0;
rtx_insn *last = get_last_insn ();
bool copy_back = false;
machine_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 (GET_MODE (op0));
/* 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)
/* 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 (GET_MODE (value) != op_mode)
{
if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize)
{
/* Optimization: Don't bother really extending VALUE
if it has all the bits we will actually use. However,
if we must narrow it, be sure we do it correctly. */
if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (op_mode))
{
rtx tmp;
tmp = simplify_subreg (op_mode, value1, GET_MODE (value), 0);
if (! tmp)
tmp = simplify_gen_subreg (op_mode,
force_reg (GET_MODE (value),
value1),
GET_MODE (value), 0);
value1 = tmp;
}
else
value1 = gen_lowpart (op_mode, value1);
}
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, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
unsigned HOST_WIDE_INT bitregion_start,
unsigned HOST_WIDE_INT bitregion_end,
machine_mode fieldmode,
rtx value, bool fallback_p)
{
rtx op0 = str_rtx;
rtx orig_value;
while (GET_CODE (op0) == SUBREG)
{
/* The following line once was done only if WORDS_BIG_ENDIAN,
but I think that is a mistake. WORDS_BIG_ENDIAN is
meaningful at a much higher level; when structures are copied
between memory and regs, the higher-numbered regs
always get higher addresses. */
int inner_mode_size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)));
int outer_mode_size = GET_MODE_SIZE (GET_MODE (op0));
int byte_offset = 0;
/* Paradoxical subregs need special handling on big endian machines. */
if (SUBREG_BYTE (op0) == 0 && inner_mode_size < outer_mode_size)
{
int difference = inner_mode_size - outer_mode_size;
if (WORDS_BIG_ENDIAN)
byte_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
byte_offset += difference % UNITS_PER_WORD;
}
else
byte_offset = SUBREG_BYTE (op0);
bitnum += byte_offset * 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) && bitnum >= GET_MODE_BITSIZE (GET_MODE (op0)))
return true;
/* Use vec_set patterns for inserting parts of vectors whenever
available. */
if (VECTOR_MODE_P (GET_MODE (op0))
&& !MEM_P (op0)
&& optab_handler (vec_set_optab, GET_MODE (op0)) != CODE_FOR_nothing
&& fieldmode == GET_MODE_INNER (GET_MODE (op0))
&& bitsize == GET_MODE_BITSIZE (GET_MODE_INNER (GET_MODE (op0)))
&& !(bitnum % GET_MODE_BITSIZE (GET_MODE_INNER (GET_MODE (op0)))))
{
struct expand_operand ops[3];
machine_mode outermode = GET_MODE (op0);
machine_mode innermode = GET_MODE_INNER (outermode);
enum insn_code icode = optab_handler (vec_set_optab, outermode);
int pos = bitnum / GET_MODE_BITSIZE (innermode);
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)
&& bitsize == GET_MODE_BITSIZE (fieldmode)
&& ((bitsize == GET_MODE_BITSIZE (GET_MODE (op0)) && bitnum == 0)
|| (bitsize % BITS_PER_WORD == 0 && bitnum % BITS_PER_WORD == 0)))
{
/* 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;
if (bitsize == GET_MODE_BITSIZE (GET_MODE (op0)))
{
sub = simplify_gen_subreg (GET_MODE (op0), value, fieldmode, 0);
if (sub)
{
emit_move_insn (op0, sub);
return true;
}
}
else
{
sub = simplify_gen_subreg (fieldmode, op0, GET_MODE (op0),
bitnum / BITS_PER_UNIT);
if (sub)
{
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. */
if (simple_mem_bitfield_p (op0, bitsize, bitnum, fieldmode))
{
op0 = adjust_bitfield_address (op0, fieldmode, bitnum / BITS_PER_UNIT);
emit_move_insn (op0, value);
return true;
}
/* 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. */
{
machine_mode imode = int_mode_for_mode (GET_MODE (op0));
if (imode != GET_MODE (op0))
{
if (MEM_P (op0))
op0 = adjust_bitfield_address_size (op0, imode, 0, MEM_SIZE (op0));
else
{
gcc_assert (imode != BLKmode);
op0 = gen_lowpart (imode, op0);
}
}
}
/* Storing an lsb-aligned field in a register
can be done with a movstrict instruction. */
if (!MEM_P (op0)
&& lowpart_bit_field_p (bitnum, bitsize, GET_MODE (op0))
&& bitsize == GET_MODE_BITSIZE (fieldmode)
&& optab_handler (movstrict_optab, fieldmode) != CODE_FOR_nothing)
{
struct 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))
{
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. */
unsigned int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;
unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
unsigned int i;
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. */
fieldmode = GET_MODE (value);
if (fieldmode == VOIDmode)
fieldmode = smallest_mode_for_size (nwords * BITS_PER_WORD, MODE_INT);
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. */
unsigned int wordnum = (backwards
? GET_MODE_SIZE (fieldmode) / UNITS_PER_WORD
- i - 1
: i);
unsigned int bit_offset = (backwards
? MAX ((int) bitsize - ((int) i + 1)
* BITS_PER_WORD,
0)
: (int) i * BITS_PER_WORD);
rtx value_word = operand_subword_force (value, wordnum, fieldmode);
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)
value_word = simplify_expand_binop (word_mode, lshr_optab,
value_word,
GEN_INT (BITS_PER_WORD
- new_bitsize),
NULL_RTX, true,
OPTAB_LIB_WIDEN);
if (!store_bit_field_1 (op0, new_bitsize,
bitnum + bit_offset,
bitregion_start, bitregion_end,
word_mode,
value_word, 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. */
orig_value = value;
if (GET_MODE (value) != VOIDmode
&& GET_MODE_CLASS (GET_MODE (value)) != MODE_INT
&& GET_MODE_CLASS (GET_MODE (value)) != MODE_PARTIAL_INT)
{
value = gen_reg_rtx (int_mode_for_mode (GET_MODE (value)));
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. */
if (!MEM_P (op0) && GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)
{
op0 = simplify_gen_subreg (word_mode, op0, GET_MODE (op0),
bitnum / BITS_PER_WORD * UNITS_PER_WORD);
gcc_assert (op0);
bitnum %= BITS_PER_WORD;
if (bitnum + bitsize > BITS_PER_WORD)
{
if (!fallback_p)
return false;
store_split_bit_field (op0, bitsize, bitnum, bitregion_start,
bitregion_end, value);
return true;
}
}
/* 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)
&& get_best_reg_extraction_insn (&insv, EP_insv,
GET_MODE_BITSIZE (GET_MODE (op0)),
fieldmode)
&& store_bit_field_using_insv (&insv, op0, bitsize, bitnum, value))
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))
{
if (get_best_mem_extraction_insn (&insv, EP_insv, bitsize, bitnum,
fieldmode)
&& store_bit_field_using_insv (&insv, op0, bitsize, bitnum, value))
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, false))
{
emit_move_insn (xop0, tempreg);
return true;
}
delete_insns_since (last);
}
}
if (!fallback_p)
return false;
store_fixed_bit_field (op0, bitsize, bitnum, bitregion_start,
bitregion_end, value);
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. */
void
store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
unsigned HOST_WIDE_INT bitregion_start,
unsigned HOST_WIDE_INT bitregion_end,
machine_mode fieldmode,
rtx value)
{
/* Handle -fstrict-volatile-bitfields in the cases where it applies. */
if (strict_volatile_bitfield_p (str_rtx, bitsize, bitnum, fieldmode,
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 (bitsize == GET_MODE_BITSIZE (fieldmode))
{
str_rtx = adjust_bitfield_address (str_rtx, fieldmode,
bitnum / BITS_PER_UNIT);
gcc_assert (bitnum % BITS_PER_UNIT == 0);
emit_move_insn (str_rtx, value);
}
else
{
rtx temp;
str_rtx = narrow_bit_field_mem (str_rtx, fieldmode, bitsize, bitnum,
&bitnum);
gcc_assert (bitnum + bitsize <= GET_MODE_BITSIZE (fieldmode));
temp = copy_to_reg (str_rtx);
if (!store_bit_field_1 (temp, bitsize, bitnum, 0, 0,
fieldmode, value, 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) && bitregion_start > 0)
{
machine_mode bestmode;
HOST_WIDE_INT offset, size;
gcc_assert ((bitregion_start % BITS_PER_UNIT) == 0);
offset = bitregion_start / BITS_PER_UNIT;
bitnum -= bitregion_start;
size = (bitnum + bitsize + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
bitregion_end -= bitregion_start;
bitregion_start = 0;
bestmode = get_best_mode (bitsize, bitnum,
bitregion_start, bitregion_end,
MEM_ALIGN (str_rtx), VOIDmode,
MEM_VOLATILE_P (str_rtx));
str_rtx = adjust_bitfield_address_size (str_rtx, bestmode, offset, size);
}
if (!store_bit_field_1 (str_rtx, bitsize, bitnum,
bitregion_start, bitregion_end,
fieldmode, value, true))
gcc_unreachable ();
}
/* Use shifts and boolean operations to store VALUE into a bit field of
width BITSIZE in OP0, starting at bit BITNUM. */
static void
store_fixed_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
unsigned HOST_WIDE_INT bitregion_start,
unsigned HOST_WIDE_INT bitregion_end,
rtx value)
{
/* There is a case not handled here:
a structure with a known alignment of just a halfword
and a field split across two aligned halfwords within the structure.
Or likewise a structure with a known alignment of just a byte
and a field split across two bytes.
Such cases are not supposed to be able to occur. */
if (MEM_P (op0))
{
machine_mode mode = GET_MODE (op0);
if (GET_MODE_BITSIZE (mode) == 0
|| GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (word_mode))
mode = word_mode;
mode = get_best_mode (bitsize, bitnum, bitregion_start, bitregion_end,
MEM_ALIGN (op0), mode, MEM_VOLATILE_P (op0));
if (mode == VOIDmode)
{
/* The only way this should occur is if the field spans word
boundaries. */
store_split_bit_field (op0, bitsize, bitnum, bitregion_start,
bitregion_end, value);
return;
}
op0 = narrow_bit_field_mem (op0, mode, bitsize, bitnum, &bitnum);
}
store_fixed_bit_field_1 (op0, bitsize, bitnum, value);
}
/* Helper function for store_fixed_bit_field, stores
the bit field always using the MODE of OP0. */
static void
store_fixed_bit_field_1 (rtx op0, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
rtx value)
{
machine_mode mode;
rtx temp;
int all_zero = 0;
int all_one = 0;
mode = GET_MODE (op0);
gcc_assert (SCALAR_INT_MODE_P (mode));
/* 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 (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 &= ((unsigned HOST_WIDE_INT) 1 << bitsize) - 1;
if (v == 0)
all_zero = 1;
else if ((bitsize < HOST_BITS_PER_WIDE_INT
&& v == ((unsigned HOST_WIDE_INT) 1 << bitsize) - 1)
|| (bitsize == HOST_BITS_PER_WIDE_INT
&& v == (unsigned HOST_WIDE_INT) -1))
all_one = 1;
value = lshift_value (mode, v, bitnum);
}
else
{
int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize
&& bitnum + bitsize != GET_MODE_BITSIZE (mode));
if (GET_MODE (value) != 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);
}
/* 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)
{
temp = expand_binop (mode, and_optab, temp,
mask_rtx (mode, bitnum, bitsize, 1),
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.
This does not yet handle fields wider than BITS_PER_WORD. */
static void
store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitpos,
unsigned HOST_WIDE_INT bitregion_start,
unsigned HOST_WIDE_INT bitregion_end,
rtx value)
{
unsigned int unit;
unsigned int 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) && GET_MODE_BITSIZE (GET_MODE (op0)) > 0)
unit = MIN (unit, GET_MODE_BITSIZE (GET_MODE (op0)));
/* 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 (GET_MODE (value) != VOIDmode
? GET_MODE (value)
: word_mode, value));
}
while (bitsdone < bitsize)
{
unsigned HOST_WIDE_INT thissize;
rtx part, word;
unsigned HOST_WIDE_INT thispos;
unsigned HOST_WIDE_INT offset;
offset = (bitpos + bitsdone) / unit;
thispos = (bitpos + bitsdone) % unit;
/* 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 (bitregion_end
&& unit > BITS_PER_UNIT
&& 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 (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));
else
{
int total_bits = GET_MODE_BITSIZE (GET_MODE (value));
/* 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, thissize,
total_bits - bitsize + bitsdone,
NULL_RTX, 1);
}
}
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));
else
part = extract_fixed_bit_field (word_mode, value, thissize,
bitsdone, NULL_RTX, 1);
}
/* If OP0 is a register, then handle OFFSET here.
When handling multiword bitfields, extract_bit_field may pass
down a word_mode SUBREG of a larger REG for a bitfield that actually
crosses a word boundary. Thus, for a SUBREG, we must find
the current word starting from the base register. */
if (GET_CODE (op0) == SUBREG)
{
int word_offset = (SUBREG_BYTE (op0) / UNITS_PER_WORD)
+ (offset * unit / BITS_PER_WORD);
machine_mode sub_mode = GET_MODE (SUBREG_REG (op0));
if (sub_mode != BLKmode && GET_MODE_SIZE (sub_mode) < UNITS_PER_WORD)
word = word_offset ? const0_rtx : op0;
else
word = operand_subword_force (SUBREG_REG (op0), word_offset,
GET_MODE (SUBREG_REG (op0)));
offset &= BITS_PER_WORD / unit - 1;
}
else if (REG_P (op0))
{
machine_mode op0_mode = GET_MODE (op0);
if (op0_mode != BLKmode && GET_MODE_SIZE (op0_mode) < UNITS_PER_WORD)
word = offset ? const0_rtx : op0;
else
word = operand_subword_force (op0, offset * unit / BITS_PER_WORD,
GET_MODE (op0));
offset &= BITS_PER_WORD / unit - 1;
}
else
word = op0;
/* 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 (word != const0_rtx)
store_fixed_bit_field (word, thissize, offset * unit + thispos,
bitregion_start, bitregion_end, part);
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))
{
machine_mode smode;
smode = mode_for_size (GET_MODE_BITSIZE (tmode), MODE_INT, 0);
x = convert_to_mode (smode, x, unsignedp);
x = force_reg (smode, 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.
EXT_MODE is the mode of the extraction and the other arguments
are as for extract_bit_field. */
static rtx
extract_bit_field_using_extv (const extraction_insn *extv, rtx op0,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum,
int unsignedp, rtx target,
machine_mode mode, machine_mode tmode)
{
struct expand_operand ops[4];
rtx spec_target = target;
rtx spec_target_subreg = 0;
machine_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 (GET_MODE (op0));
/* 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 && GET_MODE (op0) != ext_mode)
return NULL_RTX;
if (REG_P (op0) && GET_MODE (op0) != 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)
{
/* 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))
{
target = gen_lowpart (ext_mode, target);
if (GET_MODE_PRECISION (ext_mode)
> GET_MODE_PRECISION (GET_MODE (spec_target)))
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;
}
/* 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 fallback_p)
{
rtx op0 = str_rtx;
machine_mode int_mode;
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) && 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)))
{
/* We're trying to extract a full register from itself. */
return op0;
}
/* 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 (; new_mode != VOIDmode ; new_mode = GET_MODE_WIDER_MODE (new_mode))
if (GET_MODE_SIZE (new_mode) == GET_MODE_SIZE (GET_MODE (op0))
&& 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 (VECTOR_MODE_P (GET_MODE (op0))
&& !MEM_P (op0)
&& optab_handler (vec_extract_optab, GET_MODE (op0)) != CODE_FOR_nothing
&& ((bitnum + bitsize - 1) / GET_MODE_BITSIZE (GET_MODE_INNER (GET_MODE (op0)))
== bitnum / GET_MODE_BITSIZE (GET_MODE_INNER (GET_MODE (op0)))))
{
struct expand_operand ops[3];
machine_mode outermode = GET_MODE (op0);
machine_mode innermode = GET_MODE_INNER (outermode);
enum insn_code icode = optab_handler (vec_extract_optab, outermode);
unsigned HOST_WIDE_INT pos = bitnum / GET_MODE_BITSIZE (innermode);
create_output_operand (&ops[0], target, innermode);
create_input_operand (&ops[1], op0, outermode);
create_integer_operand (&ops[2], pos);
if (maybe_expand_insn (icode, 3, ops))
{
target = ops[0].value;
if (GET_MODE (target) != mode)
return gen_lowpart (tmode, target);
return target;
}
}
/* Make sure we are playing with integral modes. Pun with subregs
if we aren't. */
{
machine_mode imode = int_mode_for_mode (GET_MODE (op0));
if (imode != GET_MODE (op0))
{
if (MEM_P (op0))
op0 = adjust_bitfield_address_size (op0, imode, 0, MEM_SIZE (op0));
else if (imode != BLKmode)
{
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 if (REG_P (op0))
{
rtx reg, subreg;
imode = smallest_mode_for_size (GET_MODE_BITSIZE (GET_MODE (op0)),
MODE_INT);
reg = gen_reg_rtx (imode);
subreg = gen_lowpart_SUBREG (GET_MODE (op0), reg);
emit_move_insn (subreg, op0);
op0 = reg;
bitnum += SUBREG_BYTE (subreg) * BITS_PER_UNIT;
}
else
{
HOST_WIDE_INT 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. */
mode1 = mode;
if (SCALAR_INT_MODE_P (tmode))
{
machine_mode try_mode = mode_for_size (bitsize,
GET_MODE_CLASS (tmode), 0);
if (try_mode != BLKmode)
mode1 = try_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)
&& lowpart_bit_field_p (bitnum, bitsize, GET_MODE (op0))
&& bitsize == GET_MODE_BITSIZE (mode1)
&& TRULY_NOOP_TRUNCATION_MODES_P (mode1, GET_MODE (op0)))
{
rtx sub = simplify_gen_subreg (mode1, op0, GET_MODE (op0),
bitnum / BITS_PER_UNIT);
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. */
if (simple_mem_bitfield_p (op0, bitsize, bitnum, mode1))
{
op0 = adjust_bitfield_address (op0, mode1, bitnum / BITS_PER_UNIT);
return convert_extracted_bit_field (op0, mode, tmode, unsignedp);
}
/* 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. */
unsigned int 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);
/* Indicate for flow that the entire target reg is being set. */
emit_clobber (target);
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
? GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD - i - 1
: i);
/* Offset from start of field in OP0. */
unsigned int bit_offset = (backwards
? 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, fallback_p);
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 (GET_MODE_SIZE (GET_MODE (target)) > nwords * UNITS_PER_WORD)
{
unsigned int i, total_words;
total_words = GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD;
for (i = nwords; i < total_words; i++)
emit_move_insn
(operand_subword (target,
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 (GET_MODE (op0)) > UNITS_PER_WORD)
{
op0 = simplify_gen_subreg (word_mode, op0, GET_MODE (op0),
bitnum / BITS_PER_WORD * UNITS_PER_WORD);
bitnum %= BITS_PER_WORD;
if (bitnum + bitsize > BITS_PER_WORD)
{
if (!fallback_p)
return NULL_RTX;
target = extract_split_bit_field (op0, bitsize, bitnum, unsignedp);
return convert_extracted_bit_field (target, mode, tmode, unsignedp);
}
}
/* 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)
/* ??? We could limit the structure size to the part of OP0 that
contains the field, with appropriate checks for endianness
and TRULY_NOOP_TRUNCATION. */
&& get_best_reg_extraction_insn (&extv, pattern,
GET_MODE_BITSIZE (GET_MODE (op0)),
tmode))
{
rtx result = extract_bit_field_using_extv (&extv, op0, 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))
{
if (get_best_mem_extraction_insn (&extv, pattern, bitsize, bitnum,
tmode))
{
rtx result = extract_bit_field_using_extv (&extv, op0, 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, false);
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. */
int_mode = int_mode_for_mode (tmode);
if (int_mode == BLKmode)
int_mode = int_mode_for_mode (mode);
/* Should probably push op0 out to memory and then do a load. */
gcc_assert (int_mode != BLKmode);
target = extract_fixed_bit_field (int_mode, op0, bitsize, bitnum,
target, unsignedp);
return convert_extracted_bit_field (target, mode, tmode, unsignedp);
}
/* 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 a TARGET is specified and we can store in it at no extra cost,
we do so, and return TARGET.
Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred
if they are equally easy. */
rtx
extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, int unsignedp, rtx target,
machine_mode mode, machine_mode tmode)
{
machine_mode mode1;
/* Handle -fstrict-volatile-bitfields in the cases where it applies. */
if (GET_MODE_BITSIZE (GET_MODE (str_rtx)) > 0)
mode1 = GET_MODE (str_rtx);
else if (target && GET_MODE_BITSIZE (GET_MODE (target)) > 0)
mode1 = GET_MODE (target);
else
mode1 = tmode;
if (strict_volatile_bitfield_p (str_rtx, bitsize, bitnum, mode1, 0, 0))
{
/* Extraction of a full MODE1 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 (mode1))
{
rtx result = adjust_bitfield_address (str_rtx, mode1,
bitnum / BITS_PER_UNIT);
gcc_assert (bitnum % BITS_PER_UNIT == 0);
return convert_extracted_bit_field (result, mode, tmode, unsignedp);
}
str_rtx = narrow_bit_field_mem (str_rtx, mode1, bitsize, bitnum,
&bitnum);
gcc_assert (bitnum + bitsize <= GET_MODE_BITSIZE (mode1));
str_rtx = copy_to_reg (str_rtx);
}
return extract_bit_field_1 (str_rtx, bitsize, bitnum, unsignedp,
target, mode, tmode, true);
}
/* Use shifts and boolean operations to extract a field of BITSIZE bits
from bit BITNUM of OP0.
UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value).
If TARGET is nonzero, attempts to store the value there
and return TARGET, but this is not guaranteed.
If TARGET is not used, create a pseudo-reg of mode TMODE for the value. */
static rtx
extract_fixed_bit_field (machine_mode tmode, rtx op0,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, rtx target,
int unsignedp)
{
if (MEM_P (op0))
{
machine_mode mode
= get_best_mode (bitsize, bitnum, 0, 0, MEM_ALIGN (op0), word_mode,
MEM_VOLATILE_P (op0));
if (mode == VOIDmode)
/* The only way this should occur is if the field spans word
boundaries. */
return extract_split_bit_field (op0, bitsize, bitnum, unsignedp);
op0 = narrow_bit_field_mem (op0, mode, bitsize, bitnum, &bitnum);
}
return extract_fixed_bit_field_1 (tmode, op0, bitsize, bitnum,
target, unsignedp);
}
/* Helper function for extract_fixed_bit_field, extracts
the bit field always using the MODE of OP0. */
static rtx
extract_fixed_bit_field_1 (machine_mode tmode, rtx op0,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, rtx target,
int unsignedp)
{
machine_mode mode = GET_MODE (op0);
gcc_assert (SCALAR_INT_MODE_P (mode));
/* 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 (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 (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. */
if (mode != tmode)
op0 = convert_to_mode (tmode, op0, 1);
/* Unless the msb of the field used to be the msb when we shifted,
mask out the upper bits. */
if (GET_MODE_BITSIZE (mode) != bitnum + bitsize)
return expand_binop (GET_MODE (op0), and_optab, op0,
mask_rtx (GET_MODE (op0), 0, bitsize, 0),
target, 1, OPTAB_LIB_WIDEN);
return op0;
}
/* 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. */
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
if (GET_MODE_BITSIZE (mode) >= bitsize + bitnum)
{
op0 = convert_to_mode (mode, op0, 0);
break;
}
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. */
static rtx
extract_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitpos, int unsignedp)
{
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, word;
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.
When handling multiword bitfields, extract_bit_field may pass
down a word_mode SUBREG of a larger REG for a bitfield that actually
crosses a word boundary. Thus, for a SUBREG, we must find
the current word starting from the base register. */
if (GET_CODE (op0) == SUBREG)
{
int word_offset = (SUBREG_BYTE (op0) / UNITS_PER_WORD) + offset;
word = operand_subword_force (SUBREG_REG (op0), word_offset,
GET_MODE (SUBREG_REG (op0)));
offset = 0;
}
else if (REG_P (op0))
{
word = operand_subword_force (op0, offset, GET_MODE (op0));
offset = 0;
}
else
word = op0;
/* 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, word, thissize,
offset * unit + thispos, 0, 1);
bitsdone += thissize;
/* Shift this part into place for the result. */
if (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 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)
{
machine_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. */
unsigned int 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 (GET_MODE_BITSIZE (mode) == GET_MODE_BITSIZE (src_mode)
&& MODES_TIEABLE_P (mode, src_mode))
{
rtx x = gen_lowpart_common (mode, src);
if (x)
return x;
}
src_int_mode = int_mode_for_mode (src_mode);
int_mode = int_mode_for_mode (mode);
if (src_int_mode == BLKmode || int_mode == BLKmode)
return NULL_RTX;
if (!MODES_TIEABLE_P (src_int_mode, src_mode))
return NULL_RTX;
if (!MODES_TIEABLE_P (int_mode, mode))
return NULL_RTX;
src = gen_lowpart (src_int_mode, src);
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. */
static rtx
expand_shift_1 (enum tree_code code, machine_mode mode, rtx shifted,
rtx amount, rtx target, int unsignedp)
{
rtx op1, temp = 0;
int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR);
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;
machine_mode scalar_mode = mode;
int attempt;
bool speed = optimize_insn_for_speed_p ();
if (VECTOR_MODE_P (mode))
scalar_mode = GET_MODE_INNER (mode);
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 ((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 (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, HImode) != CODE_FOR_nothing)
return expand_unop (HImode, 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 (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);
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,
int amount, rtx target, int unsignedp)
{
return expand_shift_1 (code, mode,
shifted, GEN_INT (amount), target, unsignedp);
}
/* 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);
}
/* Indicates the type of fixup needed after a constant multiplication.
BASIC_VARIANT means no fixup is needed, NEGATE_VARIANT means that
the result should be negated, and ADD_VARIANT means that the
multiplicand should be added to the result. */
enum mult_variant {basic_variant, negate_variant, add_variant};
static void synth_mult (struct algorithm *, unsigned HOST_WIDE_INT,
const struct mult_cost *, machine_mode mode);
static bool choose_mult_variant (machine_mode, HOST_WIDE_INT,
struct algorithm *, enum mult_variant *, int);
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 (machine_mode, rtx);
static rtx expmed_mult_highpart (machine_mode, rtx, rtx, rtx, int, int);
static rtx expmed_mult_highpart_optab (machine_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 ();
machine_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->latency + 1;
if (cost_limit->cost < 0
|| (cost_limit->cost == 0 && cost_limit->latency <= 0))
return;
/* Be prepared for vector modes. */
imode = GET_MODE_INNER (mode);
if (imode == VOIDmode)
imode = mode;
maxm = MIN (BITS_PER_WORD, GET_MODE_BITSIZE (imode));
/* Restrict the bits of "t" to the multiplication's mode. */
t &= GET_MODE_MASK (imode);
/* t == 1 can be done in zero cost. */
if (t == 1)
{
alg_out->ops = 1;
alg_out->cost.cost = 0;
alg_out->cost.latency = 0;
alg_out->op[0] = alg_m;
return;
}
/* t == 0 sometimes has a cost. If it does and it exceeds our limit,
fail now. */
if (t == 0)
{
if (MULT_COST_LESS (cost_limit, zero_cost (speed)))
return;
else
{
alg_out->ops = 1;
alg_out->cost.cost = zero_cost (speed);
alg_out->cost.latency = zero_cost (speed);
alg_out->op[0] = alg_zero;
return;
}
}
/* We'll be needing a couple extra algorithm structures now. */
alg_in = XALLOCA (struct algorithm);
best_alg = XALLOCA (struct algorithm);
best_cost = *cost_limit;
/* Compute the hash index. */
hash_index = (t ^ (unsigned int) mode ^ (speed * 256)) % NUM_ALG_HASH_ENTRIES;
/* See if we already know what to do for T. */
entry_ptr = alg_hash_entry_ptr (hash_index);
if (entry_ptr->t == t
&& entry_ptr->mode == mode
&& entry_ptr->mode == mode
&& entry_ptr->speed == speed
&& entry_ptr->alg != alg_unknown)
{
cache_alg = entry_ptr->alg;
if (cache_alg == alg_impossible)
{
/* The cache tells us that it's impossible to synthesize
multiplication by T within entry_ptr->cost. */
if (!CHEAPER_MULT_COST (&entry_ptr->cost, cost_limit))
/* COST_LIMIT is at least as restrictive as the one
recorded in the hash table, in which case we have no
hope of synthesizing a multiplication. Just
return. */
return;
/* If we get here, COST_LIMIT is less restrictive than the
one recorded in the hash table, so we may be able to
synthesize a multiplication. Proceed as if we didn't
have the cache entry. */
}
else
{
if (CHEAPER_MULT_COST (cost_limit, &entry_ptr->cost))
/* The cached algorithm shows that this multiplication
requires more cost than COST_LIMIT. Just return. This
way, we don't clobber this cache entry with
alg_impossible but retain useful information. */
return;
cache_hit = true;
switch (cache_alg)
{
case alg_shift:
goto do_alg_shift;
case alg_add_t_m2:
case alg_sub_t_m2:
goto do_alg_addsub_t_m2;
case alg_add_factor:
case alg_sub_factor:
goto do_alg_addsub_factor;
case alg_add_t2_m:
goto do_alg_add_t2_m;
case alg_sub_t2_m:
goto do_alg_sub_t2_m;
default:
gcc_unreachable ();
}
}
}
/* If we have a group of zero bits at the low-order part of T, try
multiplying by the remaining bits and then doing a shift. */
if ((t & 1) == 0)
{
do_alg_shift:
m = floor_log2 (t & -t); /* m = number of low zero bits */
if (m < maxm)
{
q = t >> m;
/* The function expand_shift will choose between a shift and
a sequence of additions, so the observed cost is given as
MIN (m * add_cost(speed, mode), shift_cost(speed, mode, m)). */
op_cost = m * add_cost (speed, mode);
if (shift_cost (speed, mode, m) < op_cost)
op_cost = shift_cost (speed, mode, m);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_cost;
synth_mult (alg_in, q, &new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_shift;
}
/* See if treating ORIG_T as a signed number yields a better
sequence. Try this sequence only for a negative ORIG_T
as it would be useless for a non-negative ORIG_T. */
if ((HOST_WIDE_INT) orig_t < 0)
{
/* Shift ORIG_T as follows because a right shift of a
negative-valued signed type is implementation
defined. */
q = ~(~orig_t >> m);
/* The function expand_shift will choose between a shift
and a sequence of additions, so the observed cost is
given as MIN (m * add_cost(speed, mode),
shift_cost(speed, mode, m)). */
op_cost = m * add_cost (speed, mode);
if (shift_cost (speed, mode, m) < op_cost)
op_cost = shift_cost (speed, mode, m);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_cost;
synth_mult (alg_in, q, &new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_shift;
}
}
}
if (cache_hit)
goto done;
}
/* If we have an odd number, add or subtract one. */
if ((t & 1) != 0)
{
unsigned HOST_WIDE_INT w;
do_alg_addsub_t_m2:
for (w = 1; (w & t) != 0; w <<= 1)
;
/* If T was -1, then W will be zero after the loop. This is another
case where T ends with ...111. Handling this with (T + 1) and
subtract 1 produces slightly better code and results in algorithm
selection much faster than treating it like the ...0111 case
below. */
if (w == 0
|| (w > 2
/* Reject the case where t is 3.
Thus we prefer addition in that case. */
&& t != 3))
{
/* T ends with ...111. Multiply by (T + 1) and subtract T. */
op_cost = add_cost (speed, mode);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_cost;
synth_mult (alg_in, t + 1, &new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = 0;
best_alg->op[best_alg->ops] = alg_sub_t_m2;
}
}
else
{
/* T ends with ...01 or ...011. Multiply by (T - 1) and add T. */
op_cost = add_cost (speed, mode);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_cost;
synth_mult (alg_in, t - 1, &new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = 0;
best_alg->op[best_alg->ops] = alg_add_t_m2;
}
}
/* We may be able to calculate a * -7, a * -15, a * -31, etc
quickly with a - a * n for some appropriate constant n. */
m = exact_log2 (-orig_t + 1);
if (m >= 0 && m < maxm)
{
op_cost = shiftsub1_cost (speed, mode, m);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_cost;
synth_mult (alg_in, (unsigned HOST_WIDE_INT) (-orig_t + 1) >> m,
&new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_sub_t_m2;
}
}
if (cache_hit)
goto done;
}
/* Look for factors of t of the form
t = q(2**m +- 1), 2 <= m <= floor(log2(t - 1)).
If we find such a factor, we can multiply by t using an algorithm that
multiplies by q, shift the result by m and add/subtract it to itself.
We search for large factors first and loop down, even if large factors
are less probable than small; if we find a large factor we will find a
good sequence quickly, and therefore be able to prune (by decreasing
COST_LIMIT) the search. */
do_alg_addsub_factor:
for (m = floor_log2 (t - 1); m >= 2; m--)
{
unsigned HOST_WIDE_INT d;
d = ((unsigned HOST_WIDE_INT) 1 << m) + 1;
if (t % d == 0 && t > d && m < maxm
&& (!cache_hit || cache_alg == alg_add_factor))
{
/* If the target has a cheap shift-and-add instruction use
that in preference to a shift insn followed by an add insn.
Assume that the shift-and-add is "atomic" with a latency
equal to its cost, otherwise assume that on superscalar
hardware the shift may be executed concurrently with the
earlier steps in the algorithm. */
op_cost = add_cost (speed, mode) + shift_cost (speed, mode, m);
if (shiftadd_cost (speed, mode, m) < op_cost)
{
op_cost = shiftadd_cost (speed, mode, m);
op_latency = op_cost;
}
else
op_latency = add_cost (speed, mode);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_latency;
synth_mult (alg_in, t / d, &new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_latency;
if (alg_in->cost.latency < op_cost)
alg_in->cost.latency = op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_add_factor;
}
/* Other factors will have been taken care of in the recursion. */
break;
}
d = ((unsigned HOST_WIDE_INT) 1 << m) - 1;
if (t % d == 0 && t > d && m < maxm
&& (!cache_hit || cache_alg == alg_sub_factor))
{
/* If the target has a cheap shift-and-subtract insn use
that in preference to a shift insn followed by a sub insn.
Assume that the shift-and-sub is "atomic" with a latency
equal to it's cost, otherwise assume that on superscalar
hardware the shift may be executed concurrently with the
earlier steps in the algorithm. */
op_cost = add_cost (speed, mode) + shift_cost (speed, mode, m);
if (shiftsub0_cost (speed, mode, m) < op_cost)
{
op_cost = shiftsub0_cost (speed, mode, m);
op_latency = op_cost;
}
else
op_latency = add_cost (speed, mode);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_latency;
synth_mult (alg_in, t / d, &new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_latency;
if (alg_in->cost.latency < op_cost)
alg_in->cost.latency = op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_sub_factor;
}
break;
}
}
if (cache_hit)
goto done;
/* Try shift-and-add (load effective address) instructions,
i.e. do a*3, a*5, a*9. */
if ((t & 1) != 0)
{
do_alg_add_t2_m:
q = t - 1;
q = q & -q;
m = exact_log2 (q);
if (m >= 0 && m < maxm)
{
op_cost = shiftadd_cost (speed, mode, m);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_cost;
synth_mult (alg_in, (t - 1) >> m, &new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_add_t2_m;
}
}
if (cache_hit)
goto done;
do_alg_sub_t2_m:
q = t + 1;
q = q & -q;
m = exact_log2 (q);
if (m >= 0 && m < maxm)
{
op_cost = shiftsub0_cost (speed, mode, m);
new_limit.cost = best_cost.cost - op_cost;
new_limit.latency = best_cost.latency - op_cost;
synth_mult (alg_in, (t + 1) >> m, &new_limit, mode);
alg_in->cost.cost += op_cost;
alg_in->cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost))
{
best_cost = alg_in->cost;
std::swap (alg_in, best_alg);
best_alg->log[best_alg->ops] = m;
best_alg->op[best_alg->ops] = alg_sub_t2_m;
}
}
if (cache_hit)
goto done;
}
done:
/* If best_cost has not decreased, we have not found any algorithm. */
if (!CHEAPER_MULT_COST (&best_cost, cost_limit))
{
/* We failed to find an algorithm. Record alg_impossible for
this case (that is, <T, MODE, COST_LIMIT>) so that next time
we are asked to find an algorithm for T within the same or
lower COST_LIMIT, we can immediately return to the
caller. */
entry_ptr->t = t;
entry_ptr->mode = mode;
entry_ptr->speed = speed;
entry_ptr->alg = alg_impossible;
entry_ptr->cost = *cost_limit;
return;
}
/* Cache the result. */
if (!cache_hit)
{
entry_ptr->t = t;
entry_ptr->mode = mode;
entry_ptr->speed = speed;
entry_ptr->alg = best_alg->op[best_alg->ops];
entry_ptr->cost.cost = best_cost.cost;
entry_ptr->cost.latency = best_cost.latency;
}
/* If we are getting a too long sequence for `struct algorithm'
to record, make this search fail. */
if (best_alg->ops == MAX_BITS_PER_WORD)
return;
/* Copy the algorithm from temporary space to the space at alg_out.
We avoid using structure assignment because the majority of
best_alg is normally undefined, and this is a critical function. */
alg_out->ops = best_alg->ops + 1;
alg_out->cost = best_cost;
memcpy (alg_out->op, best_alg->op,
alg_out->ops * sizeof *alg_out->op);
memcpy (alg_out->log, best_alg->log,
alg_out->ops * sizeof *alg_out->log);
}
/* Find the cheapest way of multiplying a value of mode MODE by VAL.
Try three variations:
- a shift/add sequence based on VAL itself
- a shift/add sequence based on -VAL, followed by a negation
- a shift/add sequence based on VAL - 1, followed by an addition.
Return true if the cheapest of these cost less than MULT_COST,
describing the algorithm in *ALG and final fixup in *VARIANT. */
static bool
choose_mult_variant (machine_mode mode, HOST_WIDE_INT val,
struct algorithm *alg, enum mult_variant *variant,
int mult_cost)
{
struct algorithm alg2;
struct mult_cost limit;
int op_cost;
bool speed = optimize_insn_for_speed_p ();
/* Fail quickly for impossible bounds. */
if (mult_cost < 0)
return false;
/* Ensure that mult_cost provides a reasonable upper bound.
Any constant multiplication can be performed with less
than 2 * bits additions. */
op_cost = 2 * GET_MODE_UNIT_BITSIZE (mode) * add_cost (speed, mode);
if (mult_cost > op_cost)
mult_cost = op_cost;
*variant = basic_variant;
limit.cost = mult_cost;
limit.latency = mult_cost;
synth_mult (alg, val, &limit, mode);
/* This works only if the inverted value actually fits in an
`unsigned int' */
if (HOST_BITS_PER_INT >= GET_MODE_UNIT_BITSIZE (mode))
{
op_cost = neg_cost (speed, mode);
if (MULT_COST_LESS (&alg->cost, mult_cost))
{
limit.cost = alg->cost.cost - op_cost;
limit.latency = alg->cost.latency - op_cost;
}
else
{
limit.cost = mult_cost - op_cost;
limit.latency = mult_cost - op_cost;
}
synth_mult (&alg2, -val, &limit, mode);
alg2.cost.cost += op_cost;
alg2.cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg2.cost, &alg->cost))
*alg = alg2, *variant = negate_variant;
}
/* This proves very useful for division-by-constant. */
op_cost = add_cost (speed, mode);
if (MULT_COST_LESS (&alg->cost, mult_cost))
{
limit.cost = alg->cost.cost - op_cost;
limit.latency = alg->cost.latency - op_cost;
}
else
{
limit.cost = mult_cost - op_cost;
limit.latency = mult_cost - op_cost;
}
synth_mult (&alg2, val - 1, &limit, mode);
alg2.cost.cost += op_cost;
alg2.cost.latency += op_cost;
if (CHEAPER_MULT_COST (&alg2.cost, &alg->cost))
*alg = alg2, *variant = add_variant;
return MULT_COST_LESS (&alg->cost, mult_cost);
}
/* A subroutine of expand_mult, used for constant multiplications.
Multiply OP0 by VAL in mode MODE, storing the result in TARGET if
convenient. Use the shift/add sequence described by ALG and apply
the final fixup specified by VARIANT. */
static rtx
expand_mult_const (machine_mode mode, rtx op0, HOST_WIDE_INT val,
rtx target, const struct algorithm *alg,
enum mult_variant variant)
{
HOST_WIDE_INT val_so_far;
rtx_insn *insn;
rtx accum, tem;
int opno;
machine_mode nmode;
/* Avoid referencing memory over and over and invalid sharing
on SUBREGs. */
op0 = force_reg (mode, op0);
/* ACCUM starts out either as OP0 or as a zero, depending on
the first operation. */
if (alg->op[0] == alg_zero)
{
accum = copy_to_mode_reg (mode, CONST0_RTX (mode));
val_so_far = 0;
}
else if (alg->op[0] == alg_m)
{
accum = copy_to_mode_reg (mode, op0);
val_so_far = 1;
}
else
gcc_unreachable ();
for (opno = 1; opno < alg->ops; opno++)
{
int log = alg->log[opno];
rtx shift_subtarget = optimize ? 0 : accum;
rtx add_target
= (opno == alg->ops - 1 && target != 0 && variant != add_variant
&& !optimize)
? target : 0;
rtx accum_target = optimize ? 0 : accum;
rtx accum_inner;
switch (alg->op[opno])
{
case alg_shift:
tem = expand_shift (LSHIFT_EXPR, mode, accum, log, NULL_RTX, 0);
/* REG_EQUAL note will be attached to the following insn. */
emit_move_insn (accum, tem);
val_so_far <<= log;
break;
case alg_add_t_m2:
tem = expand_shift (LSHIFT_EXPR, mode, op0, log, NULL_RTX, 0);
accum = force_operand (gen_rtx_PLUS (mode, accum, tem),
add_target ? add_target : accum_target);
val_so_far += (HOST_WIDE_INT) 1 << log;
break;
case alg_sub_t_m2:
tem = expand_shift (LSHIFT_EXPR, mode, op0, log, NULL_RTX, 0);
accum = force_operand (gen_rtx_MINUS (mode, accum, tem),
add_target ? add_target : accum_target);
val_so_far -= (HOST_WIDE_INT) 1 << log;
break;
case alg_add_t2_m:
accum = expand_shift (LSHIFT_EXPR, mode, accum,
log, shift_subtarget, 0);
accum = force_operand (gen_rtx_PLUS (mode, accum, op0),
add_target ? add_target : accum_target);
val_so_far = (val_so_far << log) + 1;
break;
case alg_sub_t2_m:
accum = expand_shift (LSHIFT_EXPR, mode, accum,
log, shift_subtarget, 0);
accum = force_operand (gen_rtx_MINUS (mode, accum, op0),
add_target ? add_target : accum_target);
val_so_far = (val_so_far << log) - 1;
break;
case alg_add_factor:
tem = expand_shift (LSHIFT_EXPR, mode, accum, log, NULL_RTX,