summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>2012-11-18 17:33:38 +0000
committerrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>2012-11-18 17:33:38 +0000
commit2642796685af2c90aa6c18a3bba03f5c571f87bd (patch)
tree322c854e5205121b0545f58847a6f3aa5e4c0b36 /gcc
parent4ed6cf776004b2e0a22047c3c0296c8f0493fe36 (diff)
downloadgcc-2642796685af2c90aa6c18a3bba03f5c571f87bd.tar.gz
gcc/
* Makefile.in (recog.o): Add insn-codes.h. * expr.h (extraction_pattern): Move to optabs.h. (mode_for_extraction): Delete. * optabs.h (extraction_insn): New structure. (extraction_pattern): Moved from expr.h. (get_best_reg_extraction_insn, get_best_mem_extraction_insn): Declare. * optabs.c (HAVE_insv, CODE_FOR_insv, HAVE_extv, CODE_FOR_extv) (HAVE_extzv, CODE_FOR_extzv): Provide defaults. (extraction_type): New enum. (get_traditional_extraction_insn, get_extraction_insn) (get_best_reg_extraction_insn, get_best_mem_extraction_insn): New functions. * combine.c (make_extraction): Use get_best_reg_extraction_insn instead of mode_for_extraction. * expmed.c (HAVE_insv, CODE_FOR_insv, gen_insv, HAVE_extv) (CODE_FOR_extv, gen_extv, HAVE_extzv, CODE_FOR_extzv, gen_extzv): Remove fallback definitions. (mode_for_extraction): Delete. (adjust_bit_field_mem_for_reg): New function. (store_bit_field_using_insv): Replace OP_MODE parameter with an extraction_insn. Pass struct_mode to narrow_bit_field_mem. (extract_bit_field_using_extv): Likewise EXT_MODE. (store_bit_field_1): Use get_best_reg_extraction_insn and get_best_mem_extraction_insn instead of mode_for_extraction. Use adjust_bit_field_mem_for_reg when forcing memory to a register and doing a register insertion. Update calls to store_bit_field_using_insv. (extract_bit_field_1): Likewise extractions and calls to extract_bit_field_using_extv. (store_Bit_field): When narrowing to a bitregion, don't use the insv mode as a limit. * recog.c: (HAVE_extv, CODE_FOR_extv, HAVE_extzv, CODE_FOR_extzv): Provide defaults. (simplify_while_replacing): Use insn_data instead of mode_for_extraction. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@193605 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog38
-rw-r--r--gcc/Makefile.in2
-rw-r--r--gcc/combine.c38
-rw-r--r--gcc/expmed.c306
-rw-r--r--gcc/expr.h8
-rw-r--r--gcc/optabs.c173
-rw-r--r--gcc/optabs.h32
-rw-r--r--gcc/recog.c29
8 files changed, 396 insertions, 230 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index c153f732015..9361e357aea 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,43 @@
2012-11-18 Richard Sandiford <rdsandiford@googlemail.com>
+ * Makefile.in (recog.o): Add insn-codes.h.
+ * expr.h (extraction_pattern): Move to optabs.h.
+ (mode_for_extraction): Delete.
+ * optabs.h (extraction_insn): New structure.
+ (extraction_pattern): Moved from expr.h.
+ (get_best_reg_extraction_insn, get_best_mem_extraction_insn): Declare.
+ * optabs.c (HAVE_insv, CODE_FOR_insv, HAVE_extv, CODE_FOR_extv)
+ (HAVE_extzv, CODE_FOR_extzv): Provide defaults.
+ (extraction_type): New enum.
+ (get_traditional_extraction_insn, get_extraction_insn)
+ (get_best_reg_extraction_insn, get_best_mem_extraction_insn):
+ New functions.
+ * combine.c (make_extraction): Use get_best_reg_extraction_insn
+ instead of mode_for_extraction.
+ * expmed.c (HAVE_insv, CODE_FOR_insv, gen_insv, HAVE_extv)
+ (CODE_FOR_extv, gen_extv, HAVE_extzv, CODE_FOR_extzv, gen_extzv):
+ Remove fallback definitions.
+ (mode_for_extraction): Delete.
+ (adjust_bit_field_mem_for_reg): New function.
+ (store_bit_field_using_insv): Replace OP_MODE parameter with
+ an extraction_insn. Pass struct_mode to narrow_bit_field_mem.
+ (extract_bit_field_using_extv): Likewise EXT_MODE.
+ (store_bit_field_1): Use get_best_reg_extraction_insn and
+ get_best_mem_extraction_insn instead of mode_for_extraction.
+ Use adjust_bit_field_mem_for_reg when forcing memory to a
+ register and doing a register insertion. Update calls to
+ store_bit_field_using_insv.
+ (extract_bit_field_1): Likewise extractions and calls to
+ extract_bit_field_using_extv.
+ (store_Bit_field): When narrowing to a bitregion, don't use the
+ insv mode as a limit.
+ * recog.c: (HAVE_extv, CODE_FOR_extv, HAVE_extzv, CODE_FOR_extzv):
+ Provide defaults.
+ (simplify_while_replacing): Use insn_data instead of
+ mode_for_extraction.
+
+2012-11-18 Richard Sandiford <rdsandiford@googlemail.com>
+
* stor-layout.c (bit_field_mode_iterator::bit_field_mode_iterator):
Set up a default value of bitregion_end_.
(bit_field_mode_iterator::next_mode): Always apply bitregion_end_
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 0a46425a409..d74e7b3c0dc 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -3355,7 +3355,7 @@ recog.o : recog.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_ERROR_H) \
$(FUNCTION_H) $(BASIC_BLOCK_H) $(REGS_H) $(RECOG_H) $(EXPR_H) \
$(FLAGS_H) insn-config.h $(INSN_ATTR_H) reload.h \
addresses.h $(TM_P_H) $(TREE_PASS_H) hard-reg-set.h \
- $(DF_H) $(DBGCNT_H) $(TARGET_H) $(DIAGNOSTIC_CORE_H)
+ $(DF_H) $(DBGCNT_H) $(TARGET_H) $(DIAGNOSTIC_CORE_H) insn-codes.h
reg-stack.o : reg-stack.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_ERROR_H) $(TREE_H) $(RECOG_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) \
insn-config.h reload.h $(FUNCTION_H) $(TM_P_H) $(GGC_H) \
diff --git a/gcc/combine.c b/gcc/combine.c
index a3583e2013a..abd67e8ab9c 100644
--- a/gcc/combine.c
+++ b/gcc/combine.c
@@ -7179,29 +7179,24 @@ make_extraction (enum machine_mode mode, rtx inner, HOST_WIDE_INT pos,
|| (pos_rtx != 0 && len != 1)))
return 0;
- /* Get the mode to use should INNER not be a MEM, the mode for the position,
- and the mode for the result. */
- if (in_dest && mode_for_extraction (EP_insv, -1) != MAX_MACHINE_MODE)
- {
- wanted_inner_reg_mode = mode_for_extraction (EP_insv, 0);
- pos_mode = mode_for_extraction (EP_insv, 2);
- extraction_mode = mode_for_extraction (EP_insv, 3);
- }
+ enum extraction_pattern pattern = (in_dest ? EP_insv
+ : unsignedp ? EP_extzv : EP_extv);
- if (! in_dest && unsignedp
- && mode_for_extraction (EP_extzv, -1) != MAX_MACHINE_MODE)
- {
- wanted_inner_reg_mode = mode_for_extraction (EP_extzv, 1);
- pos_mode = mode_for_extraction (EP_extzv, 3);
- extraction_mode = mode_for_extraction (EP_extzv, 0);
- }
+ /* If INNER is not from memory, we want it to have the mode of a register
+ extraction pattern's structure operand, or word_mode if there is no
+ such pattern. The same applies to extraction_mode and pos_mode
+ and their respective operands.
- if (! in_dest && ! unsignedp
- && mode_for_extraction (EP_extv, -1) != MAX_MACHINE_MODE)
+ For memory, assume that the desired extraction_mode and pos_mode
+ are the same as for a register operation, since at present we don't
+ have named patterns for aligned memory structures. */
+ struct extraction_insn insn;
+ if (get_best_reg_extraction_insn (&insn, pattern,
+ GET_MODE_BITSIZE (inner_mode), mode))
{
- wanted_inner_reg_mode = mode_for_extraction (EP_extv, 1);
- pos_mode = mode_for_extraction (EP_extv, 3);
- extraction_mode = mode_for_extraction (EP_extv, 0);
+ wanted_inner_reg_mode = insn.struct_mode;
+ pos_mode = insn.pos_mode;
+ extraction_mode = insn.field_mode;
}
/* Never narrow an object, since that might not be safe. */
@@ -7210,9 +7205,6 @@ make_extraction (enum machine_mode mode, rtx inner, HOST_WIDE_INT pos,
&& GET_MODE_SIZE (extraction_mode) < GET_MODE_SIZE (mode))
extraction_mode = mode;
- /* If this is not from memory, the desired mode is the preferred mode
- for an extraction pattern's first input operand, or word_mode if there
- is none. */
if (!MEM_P (inner))
wanted_inner_mode = wanted_inner_reg_mode;
else
diff --git a/gcc/expmed.c b/gcc/expmed.c
index 106b78b7021..752aecdaf17 100644
--- a/gcc/expmed.c
+++ b/gcc/expmed.c
@@ -69,23 +69,6 @@ static rtx expand_sdiv_pow2 (enum machine_mode, rtx, HOST_WIDE_INT);
/* Test whether a value is zero of a power of two. */
#define EXACT_POWER_OF_2_OR_ZERO_P(x) (((x) & ((x) - 1)) == 0)
-/* Reduce conditional compilation elsewhere. */
-#ifndef HAVE_insv
-#define HAVE_insv 0
-#define CODE_FOR_insv CODE_FOR_nothing
-#define gen_insv(a,b,c,d) NULL_RTX
-#endif
-#ifndef HAVE_extv
-#define HAVE_extv 0
-#define CODE_FOR_extv CODE_FOR_nothing
-#define gen_extv(a,b,c,d) NULL_RTX
-#endif
-#ifndef HAVE_extzv
-#define HAVE_extzv 0
-#define CODE_FOR_extzv CODE_FOR_nothing
-#define gen_extzv(a,b,c,d) NULL_RTX
-#endif
-
struct init_expmed_rtl
{
struct rtx_def reg; rtunion reg_fld[2];
@@ -338,55 +321,6 @@ negate_rtx (enum machine_mode mode, rtx x)
return result;
}
-/* Report on the availability of insv/extv/extzv and the desired mode
- of each of their operands. Returns MAX_MACHINE_MODE if HAVE_foo
- is false; else the mode of the specified operand. If OPNO is -1,
- all the caller cares about is whether the insn is available. */
-enum machine_mode
-mode_for_extraction (enum extraction_pattern pattern, int opno)
-{
- const struct insn_data_d *data;
-
- switch (pattern)
- {
- case EP_insv:
- if (HAVE_insv)
- {
- data = &insn_data[CODE_FOR_insv];
- break;
- }
- return MAX_MACHINE_MODE;
-
- case EP_extv:
- if (HAVE_extv)
- {
- data = &insn_data[CODE_FOR_extv];
- break;
- }
- return MAX_MACHINE_MODE;
-
- case EP_extzv:
- if (HAVE_extzv)
- {
- data = &insn_data[CODE_FOR_extzv];
- break;
- }
- return MAX_MACHINE_MODE;
-
- default:
- gcc_unreachable ();
- }
-
- if (opno == -1)
- return VOIDmode;
-
- /* Everyone who uses this function used to follow it with
- if (result == VOIDmode) result = word_mode; */
- if (data->operand[opno].mode == VOIDmode)
- return word_mode;
- return data->operand[opno].mode;
-}
-
/* 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.
@@ -415,6 +349,57 @@ narrow_bit_field_mem (rtx mem, enum machine_mode mode,
}
}
+/* 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,
+ enum 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));
+ enum 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. */
+ enum 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;
+
+ enum 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. */
@@ -432,14 +417,13 @@ lowpart_bit_field_p (unsigned HOST_WIDE_INT bitnum,
return bitnum % BITS_PER_WORD == 0;
}
-/* Try to use an insv pattern to store VALUE into a field of OP0.
- OP_MODE is the mode of the insertion and BITSIZE and BITNUM are
- as for store_bit_field. */
+/* 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 (rtx op0, unsigned HOST_WIDE_INT bitsize,
- unsigned HOST_WIDE_INT bitnum, rtx value,
- enum machine_mode op_mode)
+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;
@@ -447,13 +431,15 @@ store_bit_field_using_insv (rtx op0, unsigned HOST_WIDE_INT bitsize,
rtx last = get_last_insn ();
bool copy_back = false;
+ enum 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, byte_mode, bitsize, bitnum, &bitnum);
+ xop0 = narrow_bit_field_mem (xop0, insv->struct_mode, bitsize, bitnum,
+ &bitnum);
else
{
/* Convert from counting within OP0 to counting in OP_MODE. */
@@ -533,7 +519,7 @@ store_bit_field_using_insv (rtx op0, unsigned HOST_WIDE_INT bitsize,
create_integer_operand (&ops[1], bitsize);
create_integer_operand (&ops[2], bitnum);
create_input_operand (&ops[3], value1, op_mode);
- if (maybe_expand_insn (CODE_FOR_insv, 4, ops))
+ if (maybe_expand_insn (insv->icode, 4, ops))
{
if (copy_back)
convert_move (op0, xop0, true);
@@ -807,68 +793,38 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
within a word. If the destination is a register, it too fits
in a word. */
- enum machine_mode op_mode = mode_for_extraction (EP_insv, 3);
- if (op_mode != MAX_MACHINE_MODE
- && !MEM_P (op0)
- && store_bit_field_using_insv (op0, bitsize, bitnum, value, op_mode))
+ 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 (op_mode != MAX_MACHINE_MODE && MEM_P (op0))
+ if (MEM_P (op0))
{
- enum machine_mode bestmode;
- unsigned HOST_WIDE_INT maxbits = MAX_FIXED_MODE_SIZE;
-
- /* Do not use insv for volatile bitfields when
- -fstrict-volatile-bitfields is in effect. */
- if (!(MEM_VOLATILE_P (op0) && flag_strict_volatile_bitfields > 0)
- /* Do not use insv if the bit region is restricted and
- an op_mode integer doesn't fit into the restricted region. */
- && !(bitregion_end
- && (bitnum - (bitnum % BITS_PER_UNIT)
- + GET_MODE_BITSIZE (op_mode)
- > bitregion_end + 1))
- && store_bit_field_using_insv (op0, bitsize, bitnum, value, op_mode))
+ /* Do not use unaligned memory insvs for volatile bitfields when
+ -fstrict-volatile-bitfields is in effect. */
+ if (!(MEM_VOLATILE_P (op0)
+ && flag_strict_volatile_bitfields > 0)
+ && get_best_mem_extraction_insn (&insv, EP_insv, bitsize, bitnum,
+ fieldmode)
+ && store_bit_field_using_insv (&insv, op0, bitsize, bitnum, value))
return true;
- if (bitregion_end)
- maxbits = bitregion_end - bitregion_start + 1;
-
- /* Get the mode to use for inserting into this field. If OP0 is
- BLKmode, get the smallest mode consistent with the alignment. If
- OP0 is a non-BLKmode object that is no wider than OP_MODE, use its
- mode. Otherwise, use the smallest mode containing the field. */
-
- if (GET_MODE (op0) == BLKmode
- || GET_MODE_BITSIZE (GET_MODE (op0)) > maxbits
- || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (op_mode))
- bestmode = get_best_mode (bitsize, bitnum,
- bitregion_start, bitregion_end,
- MEM_ALIGN (op0), op_mode,
- MEM_VOLATILE_P (op0));
- else
- bestmode = GET_MODE (op0);
+ rtx last = get_last_insn ();
- if (bestmode != VOIDmode
- && GET_MODE_SIZE (bestmode) >= GET_MODE_SIZE (fieldmode)
- && !(SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
- && GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
+ /* 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 last, tempreg, xop0;
- unsigned HOST_WIDE_INT bitpos;
-
- last = get_last_insn ();
-
- /* Adjust address to point to the containing unit of
- that mode. Compute the offset as a multiple of this unit,
- counting in bytes. */
- xop0 = narrow_bit_field_mem (op0, bestmode, bitsize, bitnum,
- &bitpos);
-
- /* Fetch that unit, store the bitfield in it, then store
- the unit. */
- tempreg = copy_to_reg (xop0);
+ rtx tempreg = copy_to_reg (xop0);
if (store_bit_field_1 (tempreg, bitsize, bitpos,
bitregion_start, bitregion_end,
fieldmode, orig_value, false))
@@ -913,13 +869,8 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
if (MEM_P (str_rtx) && bitregion_start > 0)
{
enum machine_mode bestmode;
- enum machine_mode op_mode;
unsigned HOST_WIDE_INT offset;
- op_mode = mode_for_extraction (EP_insv, 3);
- if (op_mode == MAX_MACHINE_MODE)
- op_mode = VOIDmode;
-
gcc_assert ((bitregion_start % BITS_PER_UNIT) == 0);
offset = bitregion_start / BITS_PER_UNIT;
@@ -928,8 +879,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
bitregion_start = 0;
bestmode = get_best_mode (bitsize, bitnum,
bitregion_start, bitregion_end,
- MEM_ALIGN (str_rtx),
- op_mode,
+ MEM_ALIGN (str_rtx), VOIDmode,
MEM_VOLATILE_P (str_rtx));
str_rtx = adjust_address (str_rtx, bestmode, offset);
}
@@ -1251,15 +1201,16 @@ convert_extracted_bit_field (rtx x, enum machine_mode mode,
are as for extract_bit_field. */
static rtx
-extract_bit_field_using_extv (rtx op0, unsigned HOST_WIDE_INT bitsize,
+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,
- enum machine_mode mode, enum machine_mode tmode,
- enum machine_mode ext_mode)
+ enum machine_mode mode, enum machine_mode tmode)
{
struct expand_operand ops[4];
rtx spec_target = target;
rtx spec_target_subreg = 0;
+ enum machine_mode ext_mode = extv->field_mode;
unsigned unit = GET_MODE_BITSIZE (ext_mode);
if (bitsize == 0 || unit < bitsize)
@@ -1267,7 +1218,8 @@ extract_bit_field_using_extv (rtx op0, unsigned HOST_WIDE_INT bitsize,
if (MEM_P (op0))
/* Get a reference to the first byte of the field. */
- op0 = narrow_bit_field_mem (op0, byte_mode, bitsize, bitnum, &bitnum);
+ op0 = narrow_bit_field_mem (op0, extv->struct_mode, bitsize, bitnum,
+ &bitnum);
else
{
/* Convert from counting within OP0 to counting in EXT_MODE. */
@@ -1315,7 +1267,7 @@ extract_bit_field_using_extv (rtx op0, unsigned HOST_WIDE_INT bitsize,
create_fixed_operand (&ops[1], op0);
create_integer_operand (&ops[2], bitsize);
create_integer_operand (&ops[3], bitnum);
- if (maybe_expand_insn (unsignedp ? CODE_FOR_extzv : CODE_FOR_extv, 4, ops))
+ if (maybe_expand_insn (extv->icode, 4, ops))
{
target = ops[0].value;
if (target == spec_target)
@@ -1341,7 +1293,6 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
{
rtx op0 = str_rtx;
enum machine_mode int_mode;
- enum machine_mode ext_mode;
enum machine_mode mode1;
if (tmode == VOIDmode)
@@ -1612,74 +1563,53 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
/* From here on we know the desired field is smaller than a word.
If OP0 is a register, it too fits within a word. */
-
- ext_mode = mode_for_extraction (unsignedp ? EP_extzv : EP_extv, 0);
- if (ext_mode != MAX_MACHINE_MODE && !MEM_P (op0))
+ enum extraction_pattern pattern = unsignedp ? EP_extzv : EP_extv;
+ extraction_insn extv;
+ if (!MEM_P (op0)
+ && get_best_reg_extraction_insn (&extv, pattern, bitnum + bitsize,
+ tmode))
{
- rtx result = extract_bit_field_using_extv (op0, bitsize, bitnum,
+ rtx result = extract_bit_field_using_extv (&extv, op0, bitsize, bitnum,
unsignedp, target, mode,
- tmode, ext_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 (ext_mode != MAX_MACHINE_MODE && MEM_P (op0))
+ if (MEM_P (op0))
{
- enum machine_mode bestmode;
-
/* Do not use extv/extzv for volatile bitfields when
-fstrict-volatile-bitfields is in effect. */
- if (!(MEM_VOLATILE_P (op0) && flag_strict_volatile_bitfields > 0))
+ if (!(MEM_VOLATILE_P (op0) && flag_strict_volatile_bitfields > 0)
+ && get_best_mem_extraction_insn (&extv, pattern, bitsize, bitnum,
+ tmode))
{
- rtx result = extract_bit_field_using_extv (op0, bitsize, bitnum,
- unsignedp, target, mode,
- tmode, ext_mode);
+ rtx result = extract_bit_field_using_extv (&extv, op0, bitsize,
+ bitnum, unsignedp,
+ target, mode,
+ tmode);
if (result)
return result;
}
- /* Get the mode to use for inserting into this field. If
- OP0 is BLKmode, get the smallest mode consistent with the
- alignment. If OP0 is a non-BLKmode object that is no
- wider than EXT_MODE, use its mode. Otherwise, use the
- smallest mode containing the field. */
-
- if (GET_MODE (op0) == BLKmode
- || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (ext_mode))
- bestmode = get_best_mode (bitsize, bitnum, 0, 0, MEM_ALIGN (op0),
- ext_mode, MEM_VOLATILE_P (op0));
- else
- bestmode = GET_MODE (op0);
+ rtx last = get_last_insn ();
- if (bestmode != VOIDmode
- && !(SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
- && GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
+ /* 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)
{
- unsigned HOST_WIDE_INT bitpos;
- rtx xop0 = narrow_bit_field_mem (op0, bestmode, bitsize, bitnum,
- &bitpos);
-
- /* Make sure the register is big enough for the whole field.
- (It might not be if bestmode == GET_MODE (op0) and the input
- code was invalid.) */
- if (bitpos + bitsize <= GET_MODE_BITSIZE (bestmode))
- {
- rtx last, result;
-
- last = get_last_insn ();
-
- /* Fetch it to a register in that size. */
- xop0 = force_reg (bestmode, xop0);
- result = extract_bit_field_1 (xop0, bitsize, bitpos,
+ xop0 = copy_to_reg (xop0);
+ rtx result = extract_bit_field_1 (xop0, bitsize, bitpos,
unsignedp, packedp, target,
mode, tmode, false);
- if (result)
- return result;
-
- delete_insns_since (last);
- }
+ if (result)
+ return result;
+ delete_insns_since (last);
}
}
diff --git a/gcc/expr.h b/gcc/expr.h
index f94f1db2224..c2168287cff 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -698,14 +698,6 @@ extern void probe_stack_range (HOST_WIDE_INT, rtx);
in its original home. This becomes invalid if any more code is emitted. */
extern rtx hard_libcall_value (enum machine_mode, rtx);
-/* Return the mode desired by operand N of a particular bitfield
- insert/extract insn, or MAX_MACHINE_MODE if no such insn is
- available. */
-
-enum extraction_pattern { EP_insv, EP_extv, EP_extzv };
-extern enum machine_mode
-mode_for_extraction (enum extraction_pattern, int);
-
extern void store_bit_field (rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
diff --git a/gcc/optabs.c b/gcc/optabs.c
index bba93c2d561..66c0337b0fd 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -8239,4 +8239,177 @@ expand_jump_insn (enum insn_code icode, unsigned int nops,
gcc_unreachable ();
}
+/* Reduce conditional compilation elsewhere. */
+#ifndef HAVE_insv
+#define HAVE_insv 0
+#define CODE_FOR_insv CODE_FOR_nothing
+#endif
+#ifndef HAVE_extv
+#define HAVE_extv 0
+#define CODE_FOR_extv CODE_FOR_nothing
+#endif
+#ifndef HAVE_extzv
+#define HAVE_extzv 0
+#define CODE_FOR_extzv CODE_FOR_nothing
+#endif
+
+/* Enumerates the possible types of structure operand to an
+ extraction_insn. */
+enum extraction_type { ET_unaligned_mem, ET_reg };
+
+/* Check whether insv, extv or extzv pattern ICODE can be used for an
+ insertion or extraction of type TYPE on a structure of mode MODE.
+ Return true if so and fill in *INSN accordingly. STRUCT_OP is the
+ operand number of the structure (the first sign_extract or zero_extract
+ operand) and FIELD_OP is the operand number of the field (the other
+ side of the set from the sign_extract or zero_extract). */
+
+static bool
+get_traditional_extraction_insn (extraction_insn *insn,
+ enum extraction_type type,
+ enum machine_mode mode,
+ enum insn_code icode,
+ int struct_op, int field_op)
+{
+ const struct insn_data_d *data = &insn_data[icode];
+
+ enum machine_mode struct_mode = data->operand[struct_op].mode;
+ if (struct_mode == VOIDmode)
+ struct_mode = word_mode;
+ if (mode != struct_mode)
+ return false;
+
+ enum machine_mode field_mode = data->operand[field_op].mode;
+ if (field_mode == VOIDmode)
+ field_mode = word_mode;
+
+ enum machine_mode pos_mode = data->operand[struct_op + 2].mode;
+ if (pos_mode == VOIDmode)
+ pos_mode = word_mode;
+
+ insn->icode = icode;
+ insn->field_mode = field_mode;
+ insn->struct_mode = (type == ET_unaligned_mem ? byte_mode : struct_mode);
+ insn->pos_mode = pos_mode;
+ return true;
+}
+
+/* Return true if an instruction exists to perform an insertion or
+ extraction (PATTERN says which) of type TYPE in mode MODE.
+ Describe the instruction in *INSN if so. */
+
+static bool
+get_extraction_insn (extraction_insn *insn,
+ enum extraction_pattern pattern,
+ enum extraction_type type,
+ enum machine_mode mode)
+{
+ switch (pattern)
+ {
+ case EP_insv:
+ if (HAVE_insv
+ && get_traditional_extraction_insn (insn, type, mode,
+ CODE_FOR_insv, 0, 3))
+ return true;
+ return false;
+
+ case EP_extv:
+ if (HAVE_extv
+ && get_traditional_extraction_insn (insn, type, mode,
+ CODE_FOR_extv, 1, 0))
+ return true;
+ return false;
+
+ case EP_extzv:
+ if (HAVE_extzv
+ && get_traditional_extraction_insn (insn, type, mode,
+ CODE_FOR_extzv, 1, 0))
+ return true;
+ return false;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return true if an instruction exists to access a field of mode
+ FIELDMODE in a structure that has STRUCT_BITS significant bits.
+ Describe the "best" such instruction in *INSN if so. PATTERN and
+ TYPE describe the type of insertion or extraction we want to perform.
+
+ For an insertion, the number of significant structure bits includes
+ all bits of the target. For an extraction, it need only include the
+ most significant bit of the field. Larger widths are acceptable
+ in both cases. */
+
+static bool
+get_best_extraction_insn (extraction_insn *insn,
+ enum extraction_pattern pattern,
+ enum extraction_type type,
+ unsigned HOST_WIDE_INT struct_bits,
+ enum machine_mode field_mode)
+{
+ enum machine_mode mode = smallest_mode_for_size (struct_bits, MODE_INT);
+ while (mode != VOIDmode)
+ {
+ if (get_extraction_insn (insn, pattern, type, mode))
+ {
+ while (mode != VOIDmode
+ && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (field_mode)
+ && !TRULY_NOOP_TRUNCATION_MODES_P (insn->field_mode,
+ field_mode))
+ {
+ get_extraction_insn (insn, pattern, type, mode);
+ mode = GET_MODE_WIDER_MODE (mode);
+ }
+ return true;
+ }
+ mode = GET_MODE_WIDER_MODE (mode);
+ }
+ return false;
+}
+
+/* Return true if an instruction exists to access a field of mode
+ FIELDMODE in a register structure that has STRUCT_BITS significant bits.
+ Describe the "best" such instruction in *INSN if so. PATTERN describes
+ the type of insertion or extraction we want to perform.
+
+ For an insertion, the number of significant structure bits includes
+ all bits of the target. For an extraction, it need only include the
+ most significant bit of the field. Larger widths are acceptable
+ in both cases. */
+
+bool
+get_best_reg_extraction_insn (extraction_insn *insn,
+ enum extraction_pattern pattern,
+ unsigned HOST_WIDE_INT struct_bits,
+ enum machine_mode field_mode)
+{
+ return get_best_extraction_insn (insn, pattern, ET_reg, struct_bits,
+ field_mode);
+}
+
+/* Return true if an instruction exists to access a field of BITSIZE
+ bits starting BITNUM bits into a memory structure. Describe the
+ "best" such instruction in *INSN if so. PATTERN describes the type
+ of insertion or extraction we want to perform and FIELDMODE is the
+ natural mode of the extracted field.
+
+ The instructions considered here only access bytes that overlap
+ the bitfield; they do not touch any surrounding bytes. */
+
+bool
+get_best_mem_extraction_insn (extraction_insn *insn,
+ enum extraction_pattern pattern,
+ HOST_WIDE_INT bitsize, HOST_WIDE_INT bitnum,
+ enum machine_mode field_mode)
+{
+ unsigned HOST_WIDE_INT struct_bits = (bitnum % BITS_PER_UNIT
+ + bitsize
+ + BITS_PER_UNIT - 1);
+ struct_bits -= struct_bits % BITS_PER_UNIT;
+ return get_best_extraction_insn (insn, pattern, ET_unaligned_mem,
+ struct_bits, field_mode);
+}
+
#include "gt-optabs.h"
diff --git a/gcc/optabs.h b/gcc/optabs.h
index e0be2bac4fd..81aa1d049dc 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -323,6 +323,38 @@ extern rtx optab_libfunc (optab optab, enum machine_mode mode);
extern rtx convert_optab_libfunc (convert_optab optab, enum machine_mode mode1,
enum machine_mode mode2);
+/* Describes an instruction that inserts or extracts a bitfield. */
+struct extraction_insn
+{
+ /* The code of the instruction. */
+ enum insn_code icode;
+
+ /* The mode that the structure operand should have. This is byte_mode
+ when using the legacy insv, extv and extzv patterns to access memory. */
+ enum machine_mode struct_mode;
+
+ /* The mode of the field to be inserted or extracted, and by extension
+ the mode of the insertion or extraction itself. */
+ enum machine_mode field_mode;
+
+ /* The mode of the field's bit position. This is only important
+ when the position is variable rather than constant. */
+ enum machine_mode pos_mode;
+};
+
+/* Enumerates the possible extraction_insn operations. */
+enum extraction_pattern { EP_insv, EP_extv, EP_extzv };
+
+extern bool get_best_reg_extraction_insn (extraction_insn *,
+ enum extraction_pattern,
+ unsigned HOST_WIDE_INT,
+ enum machine_mode);
+
+extern bool get_best_mem_extraction_insn (extraction_insn *,
+ enum extraction_pattern,
+ HOST_WIDE_INT, HOST_WIDE_INT,
+ enum machine_mode);
+
extern bool insn_operand_matches (enum insn_code icode, unsigned int opno,
rtx operand);
diff --git a/gcc/recog.c b/gcc/recog.c
index 47e7f75e1dd..3c56703b1f5 100644
--- a/gcc/recog.c
+++ b/gcc/recog.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
#include "target.h"
#include "tree-pass.h"
#include "df.h"
+#include "insn-codes.h"
#ifndef STACK_PUSH_CODE
#ifdef STACK_GROWS_DOWNWARD
@@ -542,6 +543,16 @@ cancel_changes (int num)
num_changes = num;
}
+/* Reduce conditional compilation elsewhere. */
+#ifndef HAVE_extv
+#define HAVE_extv 0
+#define CODE_FOR_extv CODE_FOR_nothing
+#endif
+#ifndef HAVE_extzv
+#define HAVE_extzv 0
+#define CODE_FOR_extzv CODE_FOR_nothing
+#endif
+
/* A subroutine of validate_replace_rtx_1 that tries to simplify the resulting
rtx. */
@@ -628,19 +639,17 @@ simplify_while_replacing (rtx *loc, rtx to, rtx object,
enum machine_mode is_mode = GET_MODE (XEXP (x, 0));
int pos = INTVAL (XEXP (x, 2));
- if (GET_CODE (x) == ZERO_EXTRACT)
+ if (GET_CODE (x) == ZERO_EXTRACT && HAVE_extzv)
{
- enum machine_mode new_mode
- = mode_for_extraction (EP_extzv, 1);
- if (new_mode != MAX_MACHINE_MODE)
- wanted_mode = new_mode;
+ wanted_mode = insn_data[CODE_FOR_extzv].operand[1].mode;
+ if (wanted_mode == VOIDmode)
+ wanted_mode = word_mode;
}
- else if (GET_CODE (x) == SIGN_EXTRACT)
+ else if (GET_CODE (x) == SIGN_EXTRACT && HAVE_extv)
{
- enum machine_mode new_mode
- = mode_for_extraction (EP_extv, 1);
- if (new_mode != MAX_MACHINE_MODE)
- wanted_mode = new_mode;
+ wanted_mode = insn_data[CODE_FOR_extv].operand[1].mode;
+ if (wanted_mode == VOIDmode)
+ wanted_mode = word_mode;
}
/* If we have a narrower mode, we can do something. */