Patchwork [7/8] Replace mode_for_extraction with new interface

login
register
mail settings
Submitter Richard Sandiford
Date Nov. 3, 2012, 11:39 a.m.
Message ID <87pq3uewzj.fsf@talisman.home>
Download mbox | patch
Permalink /patch/196826/
State New
Headers show

Comments

Richard Sandiford - Nov. 3, 2012, 11:39 a.m.
This patch adds a new interface for asking about insertion and extraction
instructions, to replace mode_for_extraction.  This first version just uses
the traditional patterns; the next patch actually adds the new optabs.

recog has some odd code to change the mode of a memory involved in a
sign_extract or zero_extract.  It predates svn history, but as best
I can tell, its main purpose was to change the memory reference to
the traditional QImode one in cases where the memory has appeared
through register substitution.  However, this isn't something that
we want for the new optabs, and I doubt many targets would welcome 
it even for extv and extzv.  Rather than keep mode_for_extraction
around for this one use, I thought it would be better to probe insn_data
directly, even though that's in some ways a reversion of Zack's original
clean-up patch.

Tested as described in the covering note.  OK to install?

Richard


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.

Patch

Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	2012-11-03 09:21:20.685369114 +0000
+++ gcc/Makefile.in	2012-11-03 11:32:18.691349886 +0000
@@ -3334,7 +3334,7 @@  recog.o : recog.c $(CONFIG_H) $(SYSTEM_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) \
Index: gcc/expr.h
===================================================================
--- gcc/expr.h	2012-11-03 10:38:48.827357741 +0000
+++ gcc/expr.h	2012-11-03 11:32:18.671349886 +0000
@@ -698,14 +698,6 @@  extern void probe_stack_range (HOST_WIDE
    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,
Index: gcc/optabs.h
===================================================================
--- gcc/optabs.h	2012-11-02 23:45:19.194364048 +0000
+++ gcc/optabs.h	2012-11-03 11:32:18.678349886 +0000
@@ -323,6 +323,38 @@  extern rtx optab_libfunc (optab optab, e
 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);
 
Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	2012-11-03 09:21:20.644369114 +0000
+++ gcc/optabs.c	2012-11-03 11:35:43.109349387 +0000
@@ -8239,4 +8239,177 @@  expand_jump_insn (enum insn_code icode,
     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"
Index: gcc/combine.c
===================================================================
--- gcc/combine.c	2012-11-03 10:38:48.259357742 +0000
+++ gcc/combine.c	2012-11-03 11:36:49.903349223 +0000
@@ -7182,29 +7182,24 @@  make_extraction (enum machine_mode mode,
 	  || (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);
-    }
-
-  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);
-    }
+  enum extraction_pattern pattern = (in_dest ? EP_insv
+				     : unsignedp ? EP_extzv : EP_extv);
 
-  if (! in_dest && ! unsignedp
-      && mode_for_extraction (EP_extv, -1) != MAX_MACHINE_MODE)
+  /* 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.
+
+     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.  */
@@ -7213,9 +7208,6 @@  make_extraction (enum machine_mode mode,
       && 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
Index: gcc/expmed.c
===================================================================
--- gcc/expmed.c	2012-11-03 10:38:49.258357740 +0000
+++ gcc/expmed.c	2012-11-03 11:32:18.669349886 +0000
@@ -69,23 +69,6 @@  static rtx expand_sdiv_pow2 (enum machin
 /* 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
   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,60 @@  narrow_bit_field_mem (rtx mem, enum mach
     }
 }
 
+/* The caller wants to perform insertion of 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)
+{
+  unsigned HOST_WIDE_INT op0_size = (MEM_SIZE_KNOWN_P (op0)
+				     ? MEM_SIZE (op0) * BITS_PER_UNIT
+				     : 0);
+  bit_field_mode_iterator iter (bitsize, bitnum, bitregion_start,
+				bitregion_end, op0_size, 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 +420,13 @@  lowpart_bit_field_p (unsigned HOST_WIDE_
     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 +434,15 @@  store_bit_field_using_insv (rtx op0, uns
   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 +522,7 @@  store_bit_field_using_insv (rtx op0, uns
   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 +796,38 @@  store_bit_field_1 (rtx str_rtx, unsigned
      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;
+      rtx last = get_last_insn ();
 
-      /* 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);
-
-      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)))
-	{
-	  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);
+      /* 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))
@@ -913,13 +872,8 @@  store_bit_field (rtx str_rtx, unsigned H
   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 +882,7 @@  store_bit_field (rtx str_rtx, unsigned H
       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 +1204,16 @@  convert_extracted_bit_field (rtx x, enum
    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 +1221,8 @@  extract_bit_field_using_extv (rtx op0, u
 
   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 +1270,7 @@  extract_bit_field_using_extv (rtx op0, u
   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 +1296,6 @@  extract_bit_field_1 (rtx str_rtx, unsign
 {
   rtx op0 = str_rtx;
   enum machine_mode int_mode;
-  enum machine_mode ext_mode;
   enum machine_mode mode1;
 
   if (tmode == VOIDmode)
@@ -1612,74 +1566,53 @@  extract_bit_field_1 (rtx str_rtx, unsign
 
   /* 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);
-
-      if (bestmode != VOIDmode
-	  && !(SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
-	       && GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
-	{
-	  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 ();
+      rtx 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,
+      /* 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, packedp, target,
 					    mode, tmode, false);
-	      if (result)
-		return result;
-
-	      delete_insns_since (last);
-	    }
+	  if (result)
+	    return result;
+	  delete_insns_since (last);
 	}
     }
 
Index: gcc/recog.c
===================================================================
--- gcc/recog.c	2012-11-03 09:21:20.664369114 +0000
+++ gcc/recog.c	2012-11-03 11:32:18.694349886 +0000
@@ -40,6 +40,7 @@  Software Foundation; either version 3, o
 #include "target.h"
 #include "tree-pass.h"
 #include "df.h"
+#include "insn-codes.h"
 
 #ifndef STACK_PUSH_CODE
 #ifdef STACK_GROWS_DOWNWARD
@@ -550,6 +551,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.  */
 
@@ -637,19 +648,17 @@  simplify_while_replacing (rtx *loc, rtx
 	  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.  */