public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Cleaning up expand optabs code
@ 2011-03-17 16:33 Richard Sandiford
  2011-03-17 19:20 ` Richard Henderson
  0 siblings, 1 reply; 32+ messages in thread
From: Richard Sandiford @ 2011-03-17 16:33 UTC (permalink / raw)
  To: gcc-patches; +Cc: patches

This patch adds a few helper functions for dealing with optabs:

- insn_operand_matches (ICODE, OPNO, X)

  Return true if X is suitable for operand OPNO of instruction ICODE.

  [ I've deliberately left the mode out here.  We're testing whether
    something matches a match_operand (or suchlike) in the .md file.
    As far as recog is concerned, it's always the mode specified in
    the .md file that should be passed to the predicate, and I think
    that should be the rule for expand as well.

    Of course, in many cases, expand "knows" which mode the .md file
    is supposed to use.  But I think we should honour the .md-file
    mode even then, and abort where there's a mismatch.  We shouldn't
    just ignore what the .md file says.  I suppose this could expose
    a few .md-file bugs though...

    At the moment, some direct predicate calls are careful to use
    the match_operand mode, but others aren't. ]

- (maybe_)legitimize_insn_target (ICODE, OPNO, TARGET, MODE)

  Replaces the idiom:

      if (!TARGET
          || TARGET == const0_rtx
          || GET_MODE (TARGET) != MODE
          || !insn_operand_matches (ICODE, OPNO, TARGET))
        TARGET = gen_reg_rtx (...);

  and the various simplications of it.

  [ I think we should assert here that the new target really is OK,
    rather than passing it to the generator function regardless.
    Passing a bogus value would often trigger a later recog failure,
    but it might not be obvious at that stage where the instruction
    came from. ]

- (maybe_)legitimize_insn_source (ICODE, OPNO, SOURCE, MODE)

  Same idea for:

      if (!insn_operand_matches (ICODE, OPNO, SOURCE))
        SOURCE = copy_to_mode_reg (MODE, SOURCE);

  [ Here too I think we should assert that the new source is OK. ]

The main motivations are:

  * It should be simpler to add new optabs (I want to add a few more...)

  * I hope it'll be easier to avoid the kinds of bug that I came
    across while going through the optabs code (more below).

  * We could be more flexible in future.  E.g. I was told about a port
    in which several named patterns accept restricted memory operands.
    At the moment, I think you have to do something like:

    (define_expand "blah"
      [... (match_operand:M N "memory_operand") ...]
      "..."
      {
        if (!restricted_memory_operand (operands[N], M))
	  operands[N] = replace_equiv_address (operands[N],
					       force_reg (...));
      })

    (define_insn "*blah"
      [... (match_operand:M N "restricted_memory_operand") ...]
      ...

    It'd be nice if expand knew how to handle this automatically.
    It should be fairly easy now that predicates have a more
    declarative form.

A few notes:

- The new functions take insn_code arguments, so various bits of code
  that found it easier to use ints need to be changed to use the
  "proper" type instead.  This includes...

- prepare_operands.  I moved the declaration from expr.h to optabs.h,
  where we can be sure that insn_code is available.  This feels right
  anyway, since the function is defined in optabs.c.

  I've left this as a separate function for now because it can be called
  after reload as well.  I wanted to concentrate on expand in this patch.

- Parts of store_bit_field_1 used sequences rather than the usual
  delete_insns_since approach.  When using the new functions,
  the flow is easier with delete_insns_since.  Using delete_insns_since
  also avoids allocating unnecessary memory.

- If a movstrict expansion failed, the code would rightly try to delete
  any set-up instructions that had been created.  However, it stored the
  results of those set-up instructions in the function-wide "value" variable,
  so the rest of the function could end up using an uninitialised pseudo.

- The followuing functions didn't clean up any set-up instructions if
  the optab couldn't be used:

  - expand_builtin_strlen
  - emit_cstore
  - emit_storent_insn
  - expand_binop_directly (if a mode test failed)

  The insns would probably be deleted as dead by later optimisers,
  but it seems better to get rid of them straight away.

- Arguably the same sort of problem applied to:

  - expand_sync_operation
  - expand_sync_fetch_operation
  - expand_sync_lock_test_and_set

  although the effect here was simply that, if we forced something
  to a register for an insn that we ended up not being able to use,
  we'd keep it in that form anyway for the fallback code.

- The vec_extract code treated an output operand as an input:

      if (! (*insn_data[icode].operand[0].predicate) (dest, mode0))
	dest = copy_to_mode_reg (mode0, dest);

- I first tried to make insn_operand_matches an inline function,
  but I can't think of a good header file that is guaranteed to
  know about both recog.h and insn-codes.h.

The patch only tackles files in gcc/ itself.  If it's OK, I'll do a
follow-up for the backends.

Bootstrapped & regression-tested on x86_64-linux-gnu.  OK to install?

Richard


gcc/
	* expr.h (prepare_operand): Move to optabs.h.
	* optabs.h (emit_unop_insn): Change insn code parameter from "int"
	to "enum insn_code".
	(maybe_emit_unop_insn): Likewise.
	(prepare_operand): Likewise.  Move from expr.h.
	(insn_operand_matches): Declare.
	(maybe_legitimize_insn_target, legitimize_insn_target): Likewise.
	(maybe_legitimize_insn_source, legitimize_insn_source): Likewise.
	* builtins.c (expand_builtin_prefetch): Call convert_memory_address
	unconditionally.  Use legitimize_insn_source.
	(expand_builtin_interclass_mathfn): Use legitimize_insn_target.
	(expand_movstr): Likewise.   Use GEN_FCN.
	(expand_builtin___clear_cache): Use legitimize_insn_source.
	(expand_builtin_strlen): Likewise.  Clean up if the expander FAILs.
	(expand_builtin_lock_release): Likewise.
	* explow.c (probe_stack_range): Use legitimize_insn_source.
	(allocate_dynamic_stack_space): Likewise.  Call convert_to_mode
	unconditionally beforehand.
	* expmed.c (check_predicate_volatile_ok): Remove mode parameter.
	Use insn_operand_matches.
	(store_bit_field_1): Use insn_operand_matches and
	maybe_legitimize_insn_source.  Don't generate a sequences when
	tentatively expanding an instruction; use delete_insns_since instead.
	Localise the second operand to movstrict.  Update calls to
	check_predicate_volatile_ok.  Use "enum insn_code" for instruction
	codes.
	(extract_bit_field_1): Likewise, except for movstrict.
	Use maybe_legitimize_insn_target.
	(emit_cstore): Use insn_operand_matches and
	maybe_legitimize_insn_target.  Clean up if the expansion fails.
	* expr.c (init_expr_target): Use insn_operand_matches.
	(compress_float_constant): Likewise.
	(emit_block_move_via_movmem): Use insn_operand_matches and
	maybe_legitimize_insn_source.
	(set_storage_via_setmem): Likewise.
	(emit_storent_insn): Likewise.  Clean if up the expansion fails.
	(emit_single_push_insn): Use legitimize_insn_source.
	(expand_assignment): Likewise.
	(try_casesi): Likewise.
	* function.c (safe_insn_predicate): Use insn_operand_matches.
	(assign_parm_setup_reg): Likewise.
	* optabs.c (insn_operand_matches): New function.
	(maybe_legitimize_insn_target, legitimize_insn_target): Likewise.
	(maybe_legitimize_insn_source, legitimize_insn_source): Likewise.
	(expand_vec_shift_expr): Use legitimize_insn_source and
	legitimize_insn_target.
	(expand_vec_cond_expr): Likewise.
	(expand_widen_pattern_expr): Likewise.  Change icode to an insn_code.
	(expand_ternary_op): Likewise.
	(expand_unop_direct): Likewise.
	(maybe_emit_unop_insn): Likewise.
	(expand_binop_directly): Likewise.  Clean up if the mode isn't
	acceptable.
	(expand_twoval_unop): Change icode to an insn_code.
	Use insn_operand_matches and maybe_legitimize_insn_source.
	(expand_twoval_binop): Likewise.
	(expand_copysign_absneg): Change icode to an insn_code.
	(emit_unop_insn): Likewise.
	(emit_unop): Likewise.  Use insn_operand_matches.
	(can_compare_p): Likewise.
	(prepare_operand): Likewise.
	(gen_add2_insn): Likewise.
	(gen_add3_insn): Likewise.
	(have_add2_insn): Likewise.
	(gen_sub2_insn): Likewise.
	(gen_sub3_insn): Likewise.
	(have_sub2_insn): Likewise.
	(prepare_cmp_insn): Use insn_operand_matches.
	(emit_cmp_and_jump_insn_1): Likewise.
	(gen_cond_trap): Likewise.
	(emit_indirect_jump): Use legitimize_insn_source.
	(vector_compare_rtx): Likewise.
	(emit_conditional_move): Use maybe_legitimize_insn_source
	and maybe_legitimize_insn_target.
	(emit_conditional_add): Likewise.
	(expand_val_compare_and_swap_1): Likewise.  Clean up on failure.
	(expand_sync_operation): Likewise.  Use local variables for the
	coerced operands.
	(expand_sync_fetch_operation): Likewise.
	(expand_sync_lock_test_and_set): Likewise.
	* reload.c (find_reloads_address_1): Change icode to an insn_code.
	Use insn_operand_matches.
	* reload1.c (gen_reload): Likewise.
	* targhooks.c (default_secondary_reload): Use insn_operand_matches.

Index: gcc/expr.h
===================================================================
--- gcc/expr.h	2011-03-04 14:15:06.000000000 +0000
+++ gcc/expr.h	2011-03-16 15:44:16.000000000 +0000
@@ -173,9 +173,6 @@ extern rtx expand_simple_unop (enum mach
    perform the operation described by CODE and MODE.  */
 extern int have_insn_for (enum rtx_code, enum machine_mode);
 
-extern rtx prepare_operand (int, rtx, int, enum machine_mode, enum machine_mode,
-			    int);
-
 /* Emit code to make a call to a constant function or a library call.  */
 extern void emit_libcall_block (rtx, rtx, rtx, rtx);
 
Index: gcc/optabs.h
===================================================================
--- gcc/optabs.h	2011-03-16 09:02:36.000000000 +0000
+++ gcc/optabs.h	2011-03-16 15:44:38.000000000 +0000
@@ -791,8 +791,8 @@ extern rtx expand_copysign (rtx, rtx, rt
 
 /* Generate an instruction with a given INSN_CODE with an output and
    an input.  */
-extern void emit_unop_insn (int, rtx, rtx, enum rtx_code);
-extern bool maybe_emit_unop_insn (int, rtx, rtx, enum rtx_code);
+extern void emit_unop_insn (enum insn_code, rtx, rtx, enum rtx_code);
+extern bool maybe_emit_unop_insn (enum insn_code, rtx, rtx, enum rtx_code);
 
 /* An extra flag to control optab_for_tree_code's behavior.  This is needed to
    distinguish between machines with a vector shift that takes a scalar for the
@@ -923,4 +923,21 @@ set_direct_optab_handler (direct_optab o
 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);
+
+extern bool insn_operand_matches (enum insn_code icode, unsigned int opno,
+				  rtx operand);
+extern rtx maybe_legitimize_insn_target (enum insn_code icode,
+					 unsigned int opno, rtx target,
+					 enum machine_mode mode);
+extern rtx legitimize_insn_target (enum insn_code icode, unsigned int opno,
+				   rtx target, enum machine_mode mode);
+extern rtx maybe_legitimize_insn_source (enum insn_code icode,
+					 unsigned int opno, rtx source,
+					 enum machine_mode mode);
+extern rtx legitimize_insn_source (enum insn_code icode, unsigned int opno,
+				   rtx source, enum machine_mode mode);
+
+extern rtx prepare_operand (enum insn_code, rtx, int, enum machine_mode,
+			    enum machine_mode, int);
+
 #endif /* GCC_OPTABS_H */
Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	2011-03-16 09:02:40.000000000 +0000
+++ gcc/builtins.c	2011-03-17 09:22:38.000000000 +0000
@@ -1143,14 +1143,8 @@ expand_builtin_prefetch (tree exp)
 #ifdef HAVE_prefetch
   if (HAVE_prefetch)
     {
-      if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate)
-	     (op0,
-	      insn_data[(int) CODE_FOR_prefetch].operand[0].mode))
-	  || (GET_MODE (op0) != Pmode))
-	{
-	  op0 = convert_memory_address (Pmode, op0);
-	  op0 = force_reg (Pmode, op0);
-	}
+      op0 = convert_memory_address (Pmode, op0);
+      op0 = legitimize_insn_source (CODE_FOR_prefetch, 0, op0, Pmode);
       emit_insn (gen_prefetch (op0, op1, op2));
     }
 #endif
@@ -2434,13 +2428,8 @@ expand_builtin_interclass_mathfn (tree e
       rtx last = get_last_insn ();
       tree orig_arg = arg;
       /* Make a suitable register to place result in.  */
-      if (!target
-	  || GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp))
-	  || !insn_data[icode].operand[0].predicate (target, GET_MODE (target)))
-         target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-
-      gcc_assert (insn_data[icode].operand[0].predicate
-		  (target, GET_MODE (target)));
+      target = legitimize_insn_target (icode, 0, target,
+				       TYPE_MODE (TREE_TYPE (exp)));
 
       /* Wrap the computation of the argument in a SAVE_EXPR, as we may
 	 need to expand the argument again.  This way, we will not perform
@@ -3366,7 +3355,7 @@ expand_builtin_strlen (tree exp, rtx tar
       tree len;
       tree src = CALL_EXPR_ARG (exp, 0);
       rtx result, src_reg, char_rtx, before_strlen;
-      enum machine_mode insn_mode = target_mode, char_mode;
+      enum machine_mode insn_mode = target_mode;
       enum insn_code icode = CODE_FOR_nothing;
       unsigned int align;
 
@@ -3422,16 +3411,14 @@ expand_builtin_strlen (tree exp, rtx tar
 	 source operand later.  */
       before_strlen = get_last_insn ();
 
-      char_rtx = const0_rtx;
-      char_mode = insn_data[(int) icode].operand[2].mode;
-      if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx,
-							    char_mode))
-	char_rtx = copy_to_mode_reg (char_mode, char_rtx);
-
-      pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
-			     char_rtx, GEN_INT (align));
-      if (! pat)
-	return NULL_RTX;
+      if (!(char_rtx = maybe_legitimize_insn_source (icode, 2, const0_rtx,
+						     VOIDmode))
+	  || !(pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
+				      char_rtx, GEN_INT (align))))
+	{
+	  delete_insns_since (before_strlen);
+	  return NULL_RTX;
+	}
       emit_insn (pat);
 
       /* Now that we are assured of success, expand the source.  */
@@ -3678,14 +3665,12 @@ expand_movstr (tree dest, tree src, rtx 
   rtx dest_mem;
   rtx src_mem;
   rtx insn;
-  const struct insn_data_d * data;
 
   if (!HAVE_movstr)
     return NULL_RTX;
 
   dest_mem = get_memory_rtx (dest, NULL);
   src_mem = get_memory_rtx (src, NULL);
-  data = insn_data + CODE_FOR_movstr;
   if (!endp)
     {
       target = force_reg (Pmode, XEXP (dest_mem, 0));
@@ -3694,22 +3679,12 @@ expand_movstr (tree dest, tree src, rtx 
     }
   else
     {
-      if (target == 0
-	  || target == const0_rtx
-	  || ! (*data->operand[0].predicate) (target, Pmode))
-	{
-	  end = gen_reg_rtx (Pmode);
-	  if (target != const0_rtx)
-	    target = end;
-	}
-      else
-	end = target;
+      end = legitimize_insn_target (CODE_FOR_movstr, 0, target, Pmode);
+      if (target != const0_rtx)
+	target = end;
     }
 
-  if (data->operand[0].mode != VOIDmode)
-    end = gen_lowpart (data->operand[0].mode, end);
-
-  insn = data->genfun (end, dest_mem, src_mem);
+  insn = GEN_FCN (CODE_FOR_movstr) (end, dest_mem, src_mem);
 
   gcc_assert (insn);
 
@@ -5241,14 +5216,12 @@ expand_builtin___clear_cache (tree exp A
       begin = CALL_EXPR_ARG (exp, 0);
       begin_rtx = expand_expr (begin, NULL_RTX, Pmode, EXPAND_NORMAL);
       begin_rtx = convert_memory_address (Pmode, begin_rtx);
-      if (!insn_data[icode].operand[0].predicate (begin_rtx, Pmode))
-	begin_rtx = copy_to_mode_reg (Pmode, begin_rtx);
+      begin_rtx = legitimize_insn_source (icode, 0, begin_rtx, Pmode);
 
       end = CALL_EXPR_ARG (exp, 1);
       end_rtx = expand_expr (end, NULL_RTX, Pmode, EXPAND_NORMAL);
       end_rtx = convert_memory_address (Pmode, end_rtx);
-      if (!insn_data[icode].operand[1].predicate (end_rtx, Pmode))
-	end_rtx = copy_to_mode_reg (Pmode, end_rtx);
+      end_rtx = legitimize_insn_source (icode, 1, end_rtx, Pmode);
 
       emit_insn (gen_clear_cache (begin_rtx, end_rtx));
     }
@@ -5749,8 +5722,7 @@ expand_builtin_synchronize (void)
 expand_builtin_lock_release (enum machine_mode mode, tree exp)
 {
   enum insn_code icode;
-  rtx mem, insn;
-  rtx val = const0_rtx;
+  rtx mem, insn, start, val;
 
   /* Expand the operands.  */
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
@@ -5759,21 +5731,20 @@ expand_builtin_lock_release (enum machin
   icode = direct_optab_handler (sync_lock_release_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (!insn_data[icode].operand[1].predicate (val, mode))
-	val = force_reg (mode, val);
-
-      insn = GEN_FCN (icode) (mem, val);
-      if (insn)
+      start = get_last_insn ();
+      if ((val = maybe_legitimize_insn_source (icode, 1, const0_rtx, mode))
+	  && (insn = GEN_FCN (icode) (mem, val)))
 	{
 	  emit_insn (insn);
 	  return;
 	}
+      delete_insns_since (start);
     }
 
   /* Otherwise we can implement this operation by emitting a barrier
      followed by a store of zero.  */
   expand_builtin_synchronize ();
-  emit_move_insn (mem, val);
+  emit_move_insn (mem, const0_rtx);
 }
 \f
 /* Expand an expression EXP that calls a built-in function,
Index: gcc/explow.c
===================================================================
--- gcc/explow.c	2011-03-04 14:15:06.000000000 +0000
+++ gcc/explow.c	2011-03-16 11:19:28.000000000 +0000
@@ -1380,7 +1380,6 @@ allocate_dynamic_stack_space (rtx size, 
   if (HAVE_allocate_stack)
     {
       enum machine_mode mode = STACK_SIZE_MODE;
-      insn_operand_predicate_fn pred;
 
       /* We don't have to check against the predicate for operand 0 since
 	 TARGET is known to be a pseudo of the proper mode, which must
@@ -1388,11 +1387,8 @@ allocate_dynamic_stack_space (rtx size, 
 	 proper mode and validate.  */
       if (mode == VOIDmode)
 	mode = insn_data[(int) CODE_FOR_allocate_stack].operand[1].mode;
-
-      pred = insn_data[(int) CODE_FOR_allocate_stack].operand[1].predicate;
-      if (pred && ! ((*pred) (size, mode)))
-	size = copy_to_mode_reg (mode, convert_to_mode (mode, size, 1));
-
+      size = convert_to_mode (mode, size, 1);
+      size = legitimize_insn_source (CODE_FOR_allocate_stack, 1, size, mode);
       emit_insn (gen_allocate_stack (target, size));
     }
   else
@@ -1554,11 +1550,7 @@ probe_stack_range (HOST_WIDE_INT first, 
 				 gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
 					         stack_pointer_rtx,
 					         plus_constant (size, first)));
-      insn_operand_predicate_fn pred
-	= insn_data[(int) CODE_FOR_check_stack].operand[0].predicate;
-      if (pred && !((*pred) (addr, Pmode)))
-	addr = copy_to_mode_reg (Pmode, addr);
-
+      addr = legitimize_insn_source (CODE_FOR_check_stack, 0, addr, Pmode);
       emit_insn (gen_check_stack (addr));
     }
 #endif
Index: gcc/expmed.c
===================================================================
--- gcc/expmed.c	2011-03-04 14:15:06.000000000 +0000
+++ gcc/expmed.c	2011-03-17 10:38:39.000000000 +0000
@@ -324,18 +324,17 @@ mode_for_extraction (enum extraction_pat
   return data->operand[opno].mode;
 }
 
-/* Return true if X, of mode MODE, matches the predicate for operand
-   OPNO of instruction ICODE.  Allow volatile memories, regardless of
+/* Return true if X matches the predicate for operand OPNO of
+   instruction ICODE.  Allow volatile memories, regardless of
    the ambient volatile_ok setting.  */
 
 static bool
-check_predicate_volatile_ok (enum insn_code icode, int opno,
-			     rtx x, enum machine_mode mode)
+check_predicate_volatile_ok (enum insn_code icode, int opno, rtx x)
 {
   bool save_volatile_ok, result;
 
   save_volatile_ok = volatile_ok;
-  result = insn_data[(int) icode].operand[opno].predicate (x, mode);
+  result = insn_operand_matches (icode, opno, x);
   volatile_ok = save_volatile_ok;
   return result;
 }
@@ -407,38 +406,25 @@ store_bit_field_1 (rtx str_rtx, unsigned
     {
       enum machine_mode outermode = GET_MODE (op0);
       enum machine_mode innermode = GET_MODE_INNER (outermode);
-      int icode = (int) optab_handler (vec_set_optab, outermode);
+      enum insn_code icode = optab_handler (vec_set_optab, outermode);
       int pos = bitnum / GET_MODE_BITSIZE (innermode);
-      rtx rtxpos = GEN_INT (pos);
-      rtx src = value;
       rtx dest = op0;
-      rtx pat, seq;
-      enum machine_mode mode0 = insn_data[icode].operand[0].mode;
-      enum machine_mode mode1 = insn_data[icode].operand[1].mode;
-      enum machine_mode mode2 = insn_data[icode].operand[2].mode;
+      rtx rtxpos, src, start, pat;
 
-      start_sequence ();
-
-      if (! (*insn_data[icode].operand[1].predicate) (src, mode1))
-	src = copy_to_mode_reg (mode1, src);
-
-      if (! (*insn_data[icode].operand[2].predicate) (rtxpos, mode2))
-	rtxpos = copy_to_mode_reg (mode1, rtxpos);
+      start = get_last_insn ();
 
       /* We could handle this, but we should always be called with a pseudo
 	 for our targets and all insns should take them as outputs.  */
-      gcc_assert ((*insn_data[icode].operand[0].predicate) (dest, mode0)
-		  && (*insn_data[icode].operand[1].predicate) (src, mode1)
-		  && (*insn_data[icode].operand[2].predicate) (rtxpos, mode2));
-      pat = GEN_FCN (icode) (dest, src, rtxpos);
-      seq = get_insns ();
-      end_sequence ();
-      if (pat)
+      gcc_assert (insn_operand_matches (icode, 0, dest));
+      if ((src = maybe_legitimize_insn_source (icode, 1, value, innermode))
+	  && (rtxpos = maybe_legitimize_insn_source (icode, 2, GEN_INT (pos),
+						     VOIDmode))
+	  && (pat = GEN_FCN (icode) (dest, src, rtxpos)))
 	{
-	  emit_insn (seq);
 	  emit_insn (pat);
 	  return true;
 	}
+      delete_insns_since (start);
     }
 
   /* If the target is a register, overwriting the entire object, or storing
@@ -515,39 +501,37 @@ store_bit_field_1 (rtx str_rtx, unsigned
       && bitsize == GET_MODE_BITSIZE (fieldmode)
       && optab_handler (movstrict_optab, fieldmode) != CODE_FOR_nothing)
     {
-      int icode = optab_handler (movstrict_optab, fieldmode);
+      enum insn_code icode = optab_handler (movstrict_optab, fieldmode);
       rtx insn;
       rtx start = get_last_insn ();
       rtx arg0 = op0;
+      rtx arg1 = value;
 
-      /* Get appropriate low part of the value being stored.  */
-      if (CONST_INT_P (value) || REG_P (value))
-	value = gen_lowpart (fieldmode, value);
-      else if (!(GET_CODE (value) == SYMBOL_REF
-		 || GET_CODE (value) == LABEL_REF
-		 || GET_CODE (value) == CONST))
-	value = convert_to_mode (fieldmode, value, 0);
+      /* Get appropriate low part of the arg1 being stored.  */
+      if (CONST_INT_P (arg1) || REG_P (arg1))
+	arg1 = gen_lowpart (fieldmode, arg1);
+      else if (!(GET_CODE (arg1) == SYMBOL_REF
+		 || GET_CODE (arg1) == LABEL_REF
+		 || GET_CODE (arg1) == CONST))
+	arg1 = convert_to_mode (fieldmode, arg1, 0);
 
-      if (! (*insn_data[icode].operand[1].predicate) (value, fieldmode))
-	value = copy_to_mode_reg (fieldmode, value);
-
-      if (GET_CODE (op0) == SUBREG)
+      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 (op0)) == fieldmode
+	  gcc_assert (GET_MODE (SUBREG_REG (arg0)) == fieldmode
 		      || GET_MODE_CLASS (fieldmode) == MODE_INT
 		      || GET_MODE_CLASS (fieldmode) == MODE_PARTIAL_INT);
-	  arg0 = SUBREG_REG (op0);
+	  arg0 = SUBREG_REG (arg0);
 	}
 
-      insn = (GEN_FCN (icode)
-		 (gen_rtx_SUBREG (fieldmode, arg0,
-				  (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
-				  + (offset * UNITS_PER_WORD)),
-				  value));
-      if (insn)
+      if ((arg1 = maybe_legitimize_insn_source (icode, 1, arg1, fieldmode))
+	  && (insn = (GEN_FCN (icode)
+		      (gen_rtx_SUBREG (fieldmode, arg0,
+				       (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
+				       + (offset * UNITS_PER_WORD)),
+		       arg1))))
 	{
 	  emit_insn (insn);
 	  return true;
@@ -654,9 +638,8 @@ store_bit_field_1 (rtx str_rtx, unsigned
       && GET_MODE_BITSIZE (op_mode) >= bitsize
       && ! ((REG_P (op0) || GET_CODE (op0) == SUBREG)
 	    && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode)))
-      && insn_data[CODE_FOR_insv].operand[1].predicate (GEN_INT (bitsize),
-							VOIDmode)
-      && check_predicate_volatile_ok (CODE_FOR_insv, 0, op0, VOIDmode))
+      && insn_operand_matches (CODE_FOR_insv, 1, GEN_INT (bitsize))
+      && check_predicate_volatile_ok (CODE_FOR_insv, 0, op0))
     {
       int xbitpos = bitpos;
       rtx value1;
@@ -745,12 +728,10 @@ store_bit_field_1 (rtx str_rtx, unsigned
 
       /* If this machine's insv insists on a register,
 	 get VALUE1 into a register.  */
-      if (! ((*insn_data[(int) CODE_FOR_insv].operand[3].predicate)
-	     (value1, op_mode)))
-	value1 = force_reg (op_mode, value1);
-
-      pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1);
-      if (pat)
+      if ((value1 = maybe_legitimize_insn_source (CODE_FOR_insv, 3,
+						  value1, op_mode))
+	  && (pat = gen_insv (xop0, GEN_INT (bitsize),
+			      GEN_INT (xbitpos), value1)))
 	{
 	  emit_insn (pat);
 
@@ -1237,49 +1218,23 @@ extract_bit_field_1 (rtx str_rtx, unsign
     {
       enum machine_mode outermode = GET_MODE (op0);
       enum machine_mode innermode = GET_MODE_INNER (outermode);
-      int icode = (int) optab_handler (vec_extract_optab, outermode);
+      enum insn_code icode = optab_handler (vec_extract_optab, outermode);
       unsigned HOST_WIDE_INT pos = bitnum / GET_MODE_BITSIZE (innermode);
-      rtx rtxpos = GEN_INT (pos);
-      rtx src = op0;
-      rtx dest = NULL, pat, seq;
-      enum machine_mode mode0 = insn_data[icode].operand[0].mode;
-      enum machine_mode mode1 = insn_data[icode].operand[1].mode;
-      enum machine_mode mode2 = insn_data[icode].operand[2].mode;
-
-      if (innermode == tmode || innermode == mode)
-	dest = target;
-
-      if (!dest)
-	dest = gen_reg_rtx (innermode);
-
-      start_sequence ();
-
-      if (! (*insn_data[icode].operand[0].predicate) (dest, mode0))
-	dest = copy_to_mode_reg (mode0, dest);
-
-      if (! (*insn_data[icode].operand[1].predicate) (src, mode1))
-	src = copy_to_mode_reg (mode1, src);
-
-      if (! (*insn_data[icode].operand[2].predicate) (rtxpos, mode2))
-	rtxpos = copy_to_mode_reg (mode1, rtxpos);
+      rtx dest, src, rtxpos, pat, last;
 
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert ((*insn_data[icode].operand[0].predicate) (dest, mode0)
-		  && (*insn_data[icode].operand[1].predicate) (src, mode1)
-		  && (*insn_data[icode].operand[2].predicate) (rtxpos, mode2));
-
-      pat = GEN_FCN (icode) (dest, src, rtxpos);
-      seq = get_insns ();
-      end_sequence ();
-      if (pat)
+      last = get_last_insn ();
+      if ((dest = maybe_legitimize_insn_target (icode, 0, target, innermode))
+	  && (src = maybe_legitimize_insn_source (icode, 1, op0, outermode))
+	  && (rtxpos = maybe_legitimize_insn_source (icode, 2, GEN_INT (pos),
+						     VOIDmode))
+	  && (pat = GEN_FCN (icode) (dest, src, rtxpos)))
 	{
-	  emit_insn (seq);
 	  emit_insn (pat);
-      	  if (mode0 != mode)
+      	  if (GET_MODE (dest) != mode)
 	    return gen_lowpart (tmode, dest);
 	  return dest;
 	}
+      delete_insns_since (last);
     }
 
   /* Make sure we are playing with integral modes.  Pun with subregs
@@ -1518,7 +1473,7 @@ extract_bit_field_1 (rtx str_rtx, unsign
       && !(GET_CODE (op0) == SUBREG && GET_MODE (op0) != ext_mode)
       && !((REG_P (op0) || GET_CODE (op0) == SUBREG)
 	   && (bitsize + bitpos > GET_MODE_BITSIZE (ext_mode)))
-      && check_predicate_volatile_ok (icode, 1, op0, GET_MODE (op0)))
+      && check_predicate_volatile_ok (icode, 1, op0))
     {
       unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
       rtx bitsize_rtx, bitpos_rtx;
@@ -1570,18 +1525,13 @@ extract_bit_field_1 (rtx str_rtx, unsign
 	    xtarget = gen_reg_rtx (ext_mode);
 	}
 
-      /* If this machine's ext(z)v insists on a register target,
-	 make sure we have one.  */
-      if (!insn_data[(int) icode].operand[0].predicate (xtarget, ext_mode))
-	xtarget = gen_reg_rtx (ext_mode);
-
       bitsize_rtx = GEN_INT (bitsize);
       bitpos_rtx = GEN_INT (xbitpos);
 
-      pat = (unsignedp
-	     ? gen_extzv (xtarget, xop0, bitsize_rtx, bitpos_rtx)
-	     : gen_extv (xtarget, xop0, bitsize_rtx, bitpos_rtx));
-      if (pat)
+      if ((xtarget = maybe_legitimize_insn_target (icode, 0, xtarget, ext_mode))
+	  && (pat = (unsignedp
+		     ? gen_extzv (xtarget, xop0, bitsize_rtx, bitpos_rtx)
+		     : gen_extv (xtarget, xop0, bitsize_rtx, bitpos_rtx))))
 	{
 	  emit_insn (pat);
 	  if (xtarget == xspec_target)
@@ -5109,11 +5059,9 @@ emit_cstore (rtx target, enum insn_code 
   y = prepare_operand (icode, y, 3, mode, compare_mode, unsignedp);
   comparison = gen_rtx_fmt_ee (code, result_mode, x, y);
   if (!x || !y
-      || !insn_data[icode].operand[2].predicate
-	  (x, insn_data[icode].operand[2].mode)
-      || !insn_data[icode].operand[3].predicate
-	  (y, insn_data[icode].operand[3].mode)
-      || !insn_data[icode].operand[1].predicate (comparison, VOIDmode))
+      || !insn_operand_matches (icode, 2, x)
+      || !insn_operand_matches (icode, 3, y)
+      || !insn_operand_matches (icode, 1, comparison))
     {
       delete_insns_since (last);
       return NULL_RTX;
@@ -5124,15 +5072,14 @@ emit_cstore (rtx target, enum insn_code 
   if (!target)
     target = gen_reg_rtx (target_mode);
 
-  if (optimize
-      || !(insn_data[(int) icode].operand[0].predicate (target, result_mode)))
-    subtarget = gen_reg_rtx (result_mode);
-  else
-    subtarget = target;
-
-  pattern = GEN_FCN (icode) (subtarget, comparison, x, y);
-  if (!pattern)
-    return NULL_RTX;
+  if (!(subtarget = maybe_legitimize_insn_target (icode, 0,
+						  optimize ? NULL_RTX : target,
+						  result_mode))
+      || !(pattern = GEN_FCN (icode) (subtarget, comparison, x, y)))
+    {
+      delete_insns_since (last);
+      return NULL_RTX;
+    }
   emit_insn (pattern);
 
   /* If we are converting to a wider mode, first convert to
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	2011-03-15 14:16:09.000000000 +0000
+++ gcc/expr.c	2011-03-16 16:57:21.000000000 +0000
@@ -286,7 +286,7 @@ init_expr_target (void)
 
 	  PUT_MODE (mem, srcmode);
 
-	  if ((*insn_data[ic].operand[1].predicate) (mem, srcmode))
+	  if (insn_operand_matches (ic, 1, mem))
 	    float_extend_from_mem[mode][srcmode] = true;
 	}
     }
@@ -1276,7 +1276,6 @@ emit_block_move_via_movmem (rtx x, rtx y
        mode = GET_MODE_WIDER_MODE (mode))
     {
       enum insn_code code = direct_optab_handler (movmem_optab, mode);
-      insn_operand_predicate_fn pred;
 
       if (code != CODE_FOR_nothing
 	  /* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT
@@ -1287,42 +1286,32 @@ emit_block_move_via_movmem (rtx x, rtx y
 	       && ((unsigned HOST_WIDE_INT) INTVAL (size)
 		   <= (GET_MODE_MASK (mode) >> 1)))
 	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
-	  && ((pred = insn_data[(int) code].operand[0].predicate) == 0
-	      || (*pred) (x, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[1].predicate) == 0
-	      || (*pred) (y, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[3].predicate) == 0
-	      || (*pred) (opalign, VOIDmode)))
+	  && insn_operand_matches (code, 0, x)
+	  && insn_operand_matches (code, 1, y)
+	  && insn_operand_matches (code, 3, opalign))
 	{
 	  rtx op2;
 	  rtx last = get_last_insn ();
 	  rtx pat;
 
-	  op2 = convert_to_mode (mode, size, 1);
-	  pred = insn_data[(int) code].operand[2].predicate;
-	  if (pred != 0 && ! (*pred) (op2, mode))
-	    op2 = copy_to_mode_reg (mode, op2);
-
 	  /* ??? When called via emit_block_move_for_call, it'd be
 	     nice if there were some way to inform the backend, so
 	     that it doesn't fail the expansion because it thinks
 	     emitting the libcall would be more efficient.  */
-
-	  if (insn_data[(int) code].n_operands == 4)
-	    pat = GEN_FCN ((int) code) (x, y, op2, opalign);
-	  else
-	    pat = GEN_FCN ((int) code) (x, y, op2, opalign,
-					GEN_INT (expected_align
-						 / BITS_PER_UNIT),
-					GEN_INT (expected_size));
-	  if (pat)
+	  op2 = convert_to_mode (mode, size, 1);
+	  if ((op2 = maybe_legitimize_insn_source (code, 2, op2, mode))
+	      && (pat = (insn_data[(int) code].n_operands == 4
+			 ? GEN_FCN ((int) code) (x, y, op2, opalign)
+			 : GEN_FCN ((int) code) (x, y, op2, opalign,
+						 GEN_INT (expected_align
+							  / BITS_PER_UNIT),
+						 GEN_INT (expected_size)))))
 	    {
 	      emit_insn (pat);
 	      volatile_ok = save_volatile_ok;
 	      return true;
 	    }
-	  else
-	    delete_insns_since (last);
+	  delete_insns_since (last);
 	}
     }
 
@@ -2715,7 +2704,6 @@ set_storage_via_setmem (rtx object, rtx 
        mode = GET_MODE_WIDER_MODE (mode))
     {
       enum insn_code code = direct_optab_handler (setmem_optab, mode);
-      insn_operand_predicate_fn pred;
 
       if (code != CODE_FOR_nothing
 	  /* We don't need MODE to be narrower than
@@ -2726,10 +2714,8 @@ set_storage_via_setmem (rtx object, rtx 
 	       && ((unsigned HOST_WIDE_INT) INTVAL (size)
 		   <= (GET_MODE_MASK (mode) >> 1)))
 	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
-	  && ((pred = insn_data[(int) code].operand[0].predicate) == 0
-	      || (*pred) (object, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[3].predicate) == 0
-	      || (*pred) (opalign, VOIDmode)))
+	  && insn_operand_matches (code, 0, object)
+	  && insn_operand_matches (code, 3, opalign))
 	{
 	  rtx opsize, opchar;
 	  enum machine_mode char_mode;
@@ -2737,34 +2723,28 @@ set_storage_via_setmem (rtx object, rtx 
 	  rtx pat;
 
 	  opsize = convert_to_mode (mode, size, 1);
-	  pred = insn_data[(int) code].operand[1].predicate;
-	  if (pred != 0 && ! (*pred) (opsize, mode))
-	    opsize = copy_to_mode_reg (mode, opsize);
 
 	  opchar = val;
 	  char_mode = insn_data[(int) code].operand[2].mode;
 	  if (char_mode != VOIDmode)
-	    {
-	      opchar = convert_to_mode (char_mode, opchar, 1);
-	      pred = insn_data[(int) code].operand[2].predicate;
-	      if (pred != 0 && ! (*pred) (opchar, char_mode))
-		opchar = copy_to_mode_reg (char_mode, opchar);
-	    }
+	    opchar = convert_to_mode (char_mode, opchar, 1);
 
-	  if (insn_data[(int) code].n_operands == 4)
-	    pat = GEN_FCN ((int) code) (object, opsize, opchar, opalign);
-	  else
-	    pat = GEN_FCN ((int) code) (object, opsize, opchar, opalign,
-					GEN_INT (expected_align
-						 / BITS_PER_UNIT),
-					GEN_INT (expected_size));
-	  if (pat)
+	  if ((opsize = maybe_legitimize_insn_source (code, 1, opsize, mode))
+	      && (opchar = maybe_legitimize_insn_source (code, 2, opchar,
+							 char_mode))
+	      && (pat = (insn_data[(int) code].n_operands == 4
+			 ? GEN_FCN ((int) code) (object, opsize,
+						 opchar, opalign)
+			 : GEN_FCN ((int) code) (object, opsize, opchar,
+						 opalign,
+						 GEN_INT (expected_align
+							  / BITS_PER_UNIT),
+						 GEN_INT (expected_size)))))
 	    {
 	      emit_insn (pat);
 	      return true;
 	    }
-	  else
-	    delete_insns_since (last);
+	  delete_insns_since (last);
 	}
     }
 
@@ -3446,7 +3426,7 @@ compress_float_constant (rtx x, rtx y)
 	{
 	  /* Skip if the target needs extra instructions to perform
 	     the extension.  */
-	  if (! (*insn_data[ic].operand[1].predicate) (trunc_y, srcmode))
+	  if (!insn_operand_matches (ic, 1, trunc_y))
 	    continue;
 	  /* This is valid, but may not be cheaper than the original. */
 	  newcost = rtx_cost (gen_rtx_FLOAT_EXTEND (dstmode, trunc_y), SET, speed);
@@ -3547,7 +3527,6 @@ emit_single_push_insn (enum machine_mode
   unsigned rounded_size = PUSH_ROUNDING (GET_MODE_SIZE (mode));
   rtx dest;
   enum insn_code icode;
-  insn_operand_predicate_fn pred;
 
   stack_pointer_delta += PUSH_ROUNDING (GET_MODE_SIZE (mode));
   /* If there is push pattern, use it.  Otherwise try old way of throwing
@@ -3555,9 +3534,7 @@ emit_single_push_insn (enum machine_mode
   icode = optab_handler (push_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (((pred = insn_data[(int) icode].operand[0].predicate)
-	   && !((*pred) (x, mode))))
-	x = force_reg (mode, x);
+      x = legitimize_insn_source (icode, 0, x, mode);
       emit_insn (GEN_FCN (icode) (x));
       return;
     }
@@ -4121,7 +4098,8 @@ expand_assignment (tree to, tree from, b
   rtx to_rtx = 0;
   rtx result;
   enum machine_mode mode;
-  int align, icode;
+  int align;
+  enum insn_code icode;
 
   /* Don't crash if the lhs of the assignment was erroneous.  */
   if (TREE_CODE (to) == ERROR_MARK)
@@ -4144,7 +4122,7 @@ expand_assignment (tree to, tree from, b
       && ((icode = optab_handler (movmisalign_optab, mode))
 	  != CODE_FOR_nothing))
     {
-      enum machine_mode address_mode, op_mode1;
+      enum machine_mode address_mode;
       rtx insn, reg, op0, mem;
 
       reg = expand_expr (from, NULL_RTX, VOIDmode, EXPAND_NORMAL);
@@ -4186,11 +4164,7 @@ expand_assignment (tree to, tree from, b
       if (TREE_THIS_VOLATILE (to))
 	MEM_VOLATILE_P (mem) = 1;
 
-      op_mode1 = insn_data[icode].operand[1].mode;
-      if (! (*insn_data[icode].operand[1].predicate) (reg, op_mode1)
-	  && op_mode1 != VOIDmode)
-	reg = copy_to_mode_reg (op_mode1, reg);
-
+      reg = legitimize_insn_source (icode, 1, reg, mode);
       insn = GEN_FCN (icode) (mem, reg);
       /* The movmisalign<mode> pattern cannot fail, else the assignment would
          silently be omitted.  */
@@ -4457,31 +4431,25 @@ expand_assignment (tree to, tree from, b
 bool
 emit_storent_insn (rtx to, rtx from)
 {
-  enum machine_mode mode = GET_MODE (to), imode;
+  enum machine_mode mode = GET_MODE (to);
   enum insn_code code = optab_handler (storent_optab, mode);
-  rtx pattern;
+  rtx pattern, last;
 
   if (code == CODE_FOR_nothing)
     return false;
 
-  imode = insn_data[code].operand[0].mode;
-  if (!insn_data[code].operand[0].predicate (to, imode))
+  if (!insn_operand_matches (code, 0, to))
     return false;
 
-  imode = insn_data[code].operand[1].mode;
-  if (!insn_data[code].operand[1].predicate (from, imode))
+  last = get_last_insn ();
+  if ((from = maybe_legitimize_insn_source (code, 1, from, mode))
+      && (pattern = GEN_FCN (code) (to, from)))
     {
-      from = copy_to_mode_reg (imode, from);
-      if (!insn_data[code].operand[1].predicate (from, imode))
-	return false;
+      emit_insn (pattern);
+      return true;
     }
-
-  pattern = GEN_FCN (code) (to, from);
-  if (pattern == NULL_RTX)
-    return false;
-
-  emit_insn (pattern);
-  return true;
+  delete_insns_since (last);
+  return false;
 }
 
 /* Generate code for computing expression EXP,
@@ -10133,28 +10101,21 @@ try_casesi (tree index_type, tree index_
 
   do_pending_stack_adjust ();
 
-  op_mode = insn_data[(int) CODE_FOR_casesi].operand[0].mode;
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[0].predicate)
-      (index, op_mode))
-    index = copy_to_mode_reg (op_mode, index);
+  index = legitimize_insn_source (CODE_FOR_casesi, 0, index, VOIDmode);
 
   op1 = expand_normal (minval);
 
   op_mode = insn_data[(int) CODE_FOR_casesi].operand[1].mode;
   op1 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (minval)),
 		       op1, TYPE_UNSIGNED (TREE_TYPE (minval)));
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[1].predicate)
-      (op1, op_mode))
-    op1 = copy_to_mode_reg (op_mode, op1);
+  op1 = legitimize_insn_source (CODE_FOR_casesi, 1, op1, op_mode);
 
   op2 = expand_normal (range);
 
   op_mode = insn_data[(int) CODE_FOR_casesi].operand[2].mode;
   op2 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (range)),
 		       op2, TYPE_UNSIGNED (TREE_TYPE (range)));
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[2].predicate)
-      (op2, op_mode))
-    op2 = copy_to_mode_reg (op_mode, op2);
+  op2 = legitimize_insn_source (CODE_FOR_casesi, 2, op2, op_mode);
 
   emit_jump_insn (gen_casesi (index, op1, op2,
 			      table_label, !default_label
Index: gcc/function.c
===================================================================
--- gcc/function.c	2011-03-14 11:55:40.000000000 +0000
+++ gcc/function.c	2011-03-16 15:29:53.000000000 +0000
@@ -1493,16 +1493,7 @@ instantiate_virtual_regs_in_rtx (rtx *lo
 static int
 safe_insn_predicate (int code, int operand, rtx x)
 {
-  const struct insn_operand_data *op_data;
-
-  if (code < 0)
-    return true;
-
-  op_data = &insn_data[code].operand[operand];
-  if (op_data->predicate == NULL)
-    return true;
-
-  return op_data->predicate (x, op_data->mode);
+  return code < 0 || insn_operand_matches ((enum insn_code) code, operand, x);
 }
 
 /* A subroutine of instantiate_virtual_regs.  Instantiate any virtual
@@ -3013,8 +3004,8 @@ assign_parm_setup_reg (struct assign_par
       op0 = parmreg;
       op1 = validated_mem;
       if (icode != CODE_FOR_nothing
-	  && insn_data[icode].operand[0].predicate (op0, promoted_nominal_mode)
-	  && insn_data[icode].operand[1].predicate (op1, data->passed_mode))
+	  && insn_operand_matches (icode, 0, op0)
+	  && insn_operand_matches (icode, 1, op1))
 	{
 	  enum rtx_code code = unsignedp ? ZERO_EXTEND : SIGN_EXTEND;
 	  rtx insn, insns;
Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	2011-03-04 14:15:06.000000000 +0000
+++ gcc/optabs.c	2011-03-17 12:25:28.000000000 +0000
@@ -157,6 +157,113 @@ optab_libfunc (optab optab, enum machine
 }
 
 \f
+/* Return true if OPERAND is suitable for operand number OPNO of
+   instruction ICODE.  */
+
+bool
+insn_operand_matches (enum insn_code icode, unsigned int opno, rtx operand)
+{
+  return (!insn_data[(int) icode].operand[opno].predicate
+	  || (insn_data[(int) icode].operand[opno].predicate
+	      (operand, insn_data[(int) icode].operand[opno].mode)));
+}
+
+/* Try to create an rtx that is suitable for output operand OPNO of
+   instruction ICODE.  MODE is the mode of target that the caller requires,
+   or VOIDmode if it doesn't care.  Return the rtx on success or null on
+   failure.
+
+   If TARGET is nonnull and not const0_rtx, try to use that rtx as the
+   target if it meets all the requirements.  It is this function's
+   responsibility to check whether TARGET is consistent with MODE,
+   not the caller's.  */
+
+rtx
+maybe_legitimize_insn_target (enum insn_code icode, unsigned int opno,
+			      rtx target, enum machine_mode mode)
+{
+  if (!target
+      || target == const0_rtx
+      || (mode != VOIDmode && GET_MODE (target) != mode)
+      || !insn_operand_matches (icode, opno, target))
+    {
+      if (mode == VOIDmode)
+	{
+	  mode = insn_data[(int) icode].operand[opno].mode;
+	  gcc_assert (mode != VOIDmode);
+	}
+      target = gen_reg_rtx (mode);
+    }
+  if (!insn_operand_matches (icode, opno, target))
+    return NULL_RTX;
+  return target;
+}
+
+/* Like maybe_legitimize_target, but require the operation to succeed.  */
+
+rtx
+legitimize_insn_target (enum insn_code icode, unsigned int opno,
+			rtx target, enum machine_mode mode)
+{
+  target = maybe_legitimize_insn_target (icode, opno, target, mode);
+  gcc_assert (target);
+  return target;
+}
+
+/* Try to coerce SOURCE into a form that is suitable for input operand
+   OPNO of instruction ICODE.  MODE is the mode of operand that the
+   caller requires, or VOIDmode if it doesn't care.  SOURCE must be
+   consistent with MODE.
+
+   Return the new form of SOURCE on success and null on failure.  */
+
+rtx
+maybe_legitimize_insn_source (enum insn_code icode, unsigned int opno,
+			      rtx source, enum machine_mode mode)
+{
+  /* This function should never need to do any mode conversion.  */
+  if (mode == VOIDmode)
+    mode = GET_MODE (source);
+  else
+    gcc_assert (GET_MODE (source) == VOIDmode || GET_MODE (source) == mode);
+
+  if (!insn_operand_matches (icode, opno, source))
+    {
+      rtx start;
+
+      start = get_last_insn ();
+      /* If the caller has given a CONST_INT without specifying a MODE.
+	 we can only go further if the instruction pattern itself
+	 nominates a suitable mode.  */
+      if (mode == VOIDmode)
+	{
+	  gcc_assert (CONST_INT_P (source));
+	  mode = insn_data[(int) icode].operand[opno].mode;
+	  if (mode == VOIDmode
+	      || trunc_int_for_mode (INTVAL (source), mode) != INTVAL (source))
+	    return NULL_RTX;
+	}
+      source = copy_to_mode_reg (mode, source);
+      if (!insn_operand_matches (icode, opno, source))
+	{
+	  delete_insns_since (start);
+	  return NULL_RTX;
+	}
+    }
+  return source;
+}
+
+/* Like maybe_legitimize_insn_source, but require the coercion to succeed.  */
+
+rtx
+legitimize_insn_source (enum insn_code icode, unsigned int opno,
+			rtx source, enum machine_mode mode)
+{
+  source = maybe_legitimize_insn_source (icode, opno, source, mode);
+  gcc_assert (source);
+  return source;
+}
+\f
 /* Add a REG_EQUAL note to the last insn in INSNS.  TARGET is being set to
    the result of operation CODE applied to OP0 (and OP1 if it is a binary
    operation).
@@ -504,7 +611,7 @@ expand_widen_pattern_expr (sepops ops, r
   tree oprnd0, oprnd1, oprnd2;
   enum machine_mode wmode = VOIDmode, tmode0, tmode1 = VOIDmode;
   optab widen_pattern_optab;
-  int icode;
+  enum insn_code icode;
   enum machine_mode xmode0, xmode1 = VOIDmode, wxmode = VOIDmode;
   rtx temp;
   rtx pat;
@@ -517,18 +624,18 @@ expand_widen_pattern_expr (sepops ops, r
     optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
   if (ops->code == WIDEN_MULT_PLUS_EXPR
       || ops->code == WIDEN_MULT_MINUS_EXPR)
-    icode = (int) optab_handler (widen_pattern_optab,
-				 TYPE_MODE (TREE_TYPE (ops->op2)));
+    icode = optab_handler (widen_pattern_optab,
+			   TYPE_MODE (TREE_TYPE (ops->op2)));
   else
-    icode = (int) optab_handler (widen_pattern_optab, tmode0);
+    icode = optab_handler (widen_pattern_optab, tmode0);
   gcc_assert (icode != CODE_FOR_nothing);
-  xmode0 = insn_data[icode].operand[1].mode;
+  xmode0 = insn_data[(int) icode].operand[1].mode;
 
   if (nops >= 2)
     {
       oprnd1 = ops->op1;
       tmode1 = TYPE_MODE (TREE_TYPE (oprnd1));
-      xmode1 = insn_data[icode].operand[2].mode;
+      xmode1 = insn_data[(int) icode].operand[2].mode;
     }
 
   /* The last operand is of a wider mode than the rest of the operands.  */
@@ -543,18 +650,13 @@ expand_widen_pattern_expr (sepops ops, r
       gcc_assert (op1);
       oprnd2 = ops->op2;
       wmode = TYPE_MODE (TREE_TYPE (oprnd2));
-      wxmode = insn_data[icode].operand[3].mode;
+      wxmode = insn_data[(int) icode].operand[3].mode;
     }
 
   if (!wide_op)
-    wmode = wxmode = insn_data[icode].operand[0].mode;
-
-  if (!target
-      || ! (*insn_data[icode].operand[0].predicate) (target, wmode))
-    temp = gen_reg_rtx (wmode);
-  else
-    temp = target;
+    wmode = wxmode = insn_data[(int) icode].operand[0].mode;
 
+  temp = legitimize_insn_target (icode, 0, target, wmode);
   xop0 = op0;
   xop1 = op1;
   wxop = wide_op;
@@ -591,22 +693,13 @@ expand_widen_pattern_expr (sepops ops, r
   /* Now, if insn's predicates don't allow our operands, put them into
      pseudo regs.  */
 
-  if (! (*insn_data[icode].operand[1].predicate) (xop0, xmode0)
-      && xmode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (xmode0, xop0);
-
+  xop0 = legitimize_insn_source (icode, 1, xop0, xmode0);
   if (op1)
     {
-      if (! (*insn_data[icode].operand[2].predicate) (xop1, xmode1)
-          && xmode1 != VOIDmode)
-        xop1 = copy_to_mode_reg (xmode1, xop1);
-
+      xop1 = legitimize_insn_source (icode, 2, xop1, xmode1);
       if (wide_op)
         {
-          if (! (*insn_data[icode].operand[3].predicate) (wxop, wxmode)
-              && wxmode != VOIDmode)
-            wxop = copy_to_mode_reg (wxmode, wxop);
-
+	  wxop = legitimize_insn_source (icode, 3, wxop, wxmode);
           pat = GEN_FCN (icode) (temp, xop0, xop1, wxop);
         }
       else
@@ -616,10 +709,7 @@ expand_widen_pattern_expr (sepops ops, r
     {
       if (wide_op)
         {
-          if (! (*insn_data[icode].operand[2].predicate) (wxop, wxmode)
-              && wxmode != VOIDmode)
-            wxop = copy_to_mode_reg (wxmode, wxop);
-
+	  wxop = legitimize_insn_source (icode, 2, wxop, wxmode);
           pat = GEN_FCN (icode) (temp, xop0, wxop);
         }
       else
@@ -645,20 +735,17 @@ expand_widen_pattern_expr (sepops ops, r
 expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0,
 		   rtx op1, rtx op2, rtx target, int unsignedp)
 {
-  int icode = (int) optab_handler (ternary_optab, mode);
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
-  enum machine_mode mode2 = insn_data[icode].operand[3].mode;
+  enum insn_code icode = optab_handler (ternary_optab, mode);
+  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
+  enum machine_mode mode2 = insn_data[(int) icode].operand[3].mode;
   rtx temp;
   rtx pat;
   rtx xop0 = op0, xop1 = op1, xop2 = op2;
 
   gcc_assert (optab_handler (ternary_optab, mode) != CODE_FOR_nothing);
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    temp = gen_reg_rtx (mode);
-  else
-    temp = target;
+  temp = legitimize_insn_target (icode, 0, target, mode);
 
   /* In case the insn wants input operands in modes different from
      those of the actual operands, convert the operands.  It would
@@ -690,18 +777,9 @@ expand_ternary_op (enum machine_mode mod
   /* Now, if insn's predicates don't allow our operands, put them into
      pseudo regs.  */
 
-  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
-      && mode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (mode0, xop0);
-
-  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
-      && mode1 != VOIDmode)
-    xop1 = copy_to_mode_reg (mode1, xop1);
-
-  if (!insn_data[icode].operand[3].predicate (xop2, mode2)
-      && mode2 != VOIDmode)
-    xop2 = copy_to_mode_reg (mode2, xop2);
-
+  xop0 = legitimize_insn_source (icode, 1, xop0, mode0);
+  xop1 = legitimize_insn_source (icode, 2, xop1, mode1);
+  xop2 = legitimize_insn_source (icode, 3, xop2, mode2);
   pat = GEN_FCN (icode) (temp, xop0, xop1, xop2);
 
   emit_insn (pat);
@@ -753,8 +831,6 @@ expand_vec_shift_expr (sepops ops, rtx t
 {
   enum insn_code icode;
   rtx rtx_op1, rtx_op2;
-  enum machine_mode mode1;
-  enum machine_mode mode2;
   enum machine_mode mode = TYPE_MODE (ops->type);
   tree vec_oprnd = ops->op0;
   tree shift_oprnd = ops->op1;
@@ -776,22 +852,13 @@ expand_vec_shift_expr (sepops ops, rtx t
   icode = optab_handler (shift_optab, mode);
   gcc_assert (icode != CODE_FOR_nothing);
 
-  mode1 = insn_data[icode].operand[1].mode;
-  mode2 = insn_data[icode].operand[2].mode;
-
   rtx_op1 = expand_normal (vec_oprnd);
-  if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode1)
-      && mode1 != VOIDmode)
-    rtx_op1 = force_reg (mode1, rtx_op1);
+  rtx_op1 = legitimize_insn_source (icode, 1, rtx_op1, VOIDmode);
 
   rtx_op2 = expand_normal (shift_oprnd);
-  if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode2)
-      && mode2 != VOIDmode)
-    rtx_op2 = force_reg (mode2, rtx_op2);
+  rtx_op2 = legitimize_insn_source (icode, 2, rtx_op2, VOIDmode);
 
-  if (!target
-      || ! (*insn_data[icode].operand[0].predicate) (target, mode))
-    target = gen_reg_rtx (mode);
+  target = legitimize_insn_target (icode, 0, target, mode);
 
   /* Emit instruction */
   pat = GEN_FCN (icode) (target, rtx_op1, rtx_op2);
@@ -1389,9 +1456,9 @@ expand_binop_directly (enum machine_mode
 		       rtx target, int unsignedp, enum optab_methods methods,
 		       rtx last)
 {
-  int icode = (int) optab_handler (binoptab, mode);
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+  enum insn_code icode = optab_handler (binoptab, mode);
+  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
   enum machine_mode tmp_mode;
   bool commutative_p;
   rtx pat;
@@ -1399,11 +1466,6 @@ expand_binop_directly (enum machine_mode
   rtx temp;
   rtx swap;
 
-  if (target)
-    temp = target;
-  else
-    temp = gen_reg_rtx (mode);
-
   /* If it is a commutative operator and the modes would match
      if we would swap the operands, we can save the conversions.  */
   commutative_p = commutative_optab_p (binoptab);
@@ -1456,14 +1518,6 @@ expand_binop_directly (enum machine_mode
   /* Now, if insn's predicates don't allow our operands, put them into
      pseudo regs.  */
 
-  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
-      && mode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (mode0, xop0);
-
-  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
-      && mode1 != VOIDmode)
-    xop1 = copy_to_mode_reg (mode1, xop1);
-
   if (binoptab == vec_pack_trunc_optab
       || binoptab == vec_pack_usat_optab
       || binoptab == vec_pack_ssat_optab
@@ -1472,18 +1526,20 @@ expand_binop_directly (enum machine_mode
     {
       /* The mode of the result is different then the mode of the
 	 arguments.  */
-      tmp_mode = insn_data[icode].operand[0].mode;
+      tmp_mode = insn_data[(int) icode].operand[0].mode;
       if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode))
-	return 0;
+	{
+	  delete_insns_since (last);
+	  return NULL_RTX;
+	}
     }
   else
     tmp_mode = mode;
 
-  if (!insn_data[icode].operand[0].predicate (temp, tmp_mode))
-    temp = gen_reg_rtx (tmp_mode);
-
-  pat = GEN_FCN (icode) (temp, xop0, xop1);
-  if (pat)
+  if ((temp = maybe_legitimize_insn_target (icode, 0, target, tmp_mode))
+      && (xop0 = maybe_legitimize_insn_source (icode, 1, xop0, mode0))
+      && (xop1 = maybe_legitimize_insn_source (icode, 2, xop1, mode1))
+      && (pat = GEN_FCN (icode) (temp, xop0, xop1)))
     {
       /* If PAT is composed of more than one insn, try to add an appropriate
 	 REG_EQUAL note to it.  If we can't because TEMP conflicts with an
@@ -2284,8 +2340,8 @@ expand_twoval_unop (optab unoptab, rtx o
 
   if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (unoptab, mode);
-      enum machine_mode mode0 = insn_data[icode].operand[2].mode;
+      enum insn_code icode = optab_handler (unoptab, mode);
+      enum machine_mode mode0 = insn_data[(int) icode].operand[2].mode;
       rtx pat;
       rtx xop0 = op0;
 
@@ -2293,23 +2349,20 @@ expand_twoval_unop (optab unoptab, rtx o
 	  && GET_MODE (xop0) != mode0)
 	xop0 = convert_to_mode (mode0, xop0, unsignedp);
 
-      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (!insn_data[icode].operand[2].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
-      gcc_assert (insn_data[icode].operand[1].predicate (targ1, mode));
+      /* We could handle this, but we should always be called with
+	 a pseudo for our targets and all insns should take them as
+	 outputs.  */
+      gcc_assert (insn_operand_matches (icode, 0, targ0));
+      gcc_assert (insn_operand_matches (icode, 1, targ1));
 
-      pat = GEN_FCN (icode) (targ0, targ1, xop0);
-      if (pat)
+      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
+      if ((xop0 = maybe_legitimize_insn_source (icode, 2, xop0, mode0))
+	  && (pat = GEN_FCN (icode) (targ0, targ1, xop0)))
 	{
 	  emit_insn (pat);
 	  return 1;
 	}
-      else
-	delete_insns_since (last);
+      delete_insns_since (last);
     }
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
@@ -2376,9 +2429,9 @@ expand_twoval_binop (optab binoptab, rtx
 
   if (optab_handler (binoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (binoptab, mode);
-      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-      enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+      enum insn_code icode = optab_handler (binoptab, mode);
+      enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
+      enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
       rtx pat;
       rtx xop0 = op0, xop1 = op1;
 
@@ -2406,26 +2459,20 @@ expand_twoval_binop (optab binoptab, rtx
 			      : mode,
 			      xop1, unsignedp);
 
-      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (!insn_data[icode].operand[1].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (!insn_data[icode].operand[2].predicate (xop1, mode1))
-	xop1 = copy_to_mode_reg (mode1, xop1);
-
       /* We could handle this, but we should always be called with a pseudo
 	 for our targets and all insns should take them as outputs.  */
-      gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
-      gcc_assert (insn_data[icode].operand[3].predicate (targ1, mode));
+      gcc_assert (insn_operand_matches (icode, 0, targ0));
+      gcc_assert (insn_operand_matches (icode, 3, targ1));
 
-      pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
-      if (pat)
+      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
+      if ((xop0 = maybe_legitimize_insn_source (icode, 1, xop0, mode0))
+	  && (xop1 = maybe_legitimize_insn_source (icode, 2, xop1, mode1))
+	  && (pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1)))
 	{
 	  emit_insn (pat);
 	  return 1;
 	}
-      else
-	delete_insns_since (last);
+      delete_insns_since (last);
     }
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
@@ -2985,31 +3032,20 @@ expand_unop_direct (enum machine_mode mo
 {
   if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (unoptab, mode);
-      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+      enum insn_code icode = optab_handler (unoptab, mode);
+      enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
       rtx xop0 = op0;
       rtx last = get_last_insn ();
       rtx pat, temp;
 
-      if (target)
-	temp = target;
-      else
-	temp = gen_reg_rtx (mode);
-
       if (GET_MODE (xop0) != VOIDmode
 	  && GET_MODE (xop0) != mode0)
 	xop0 = convert_to_mode (mode0, xop0, unsignedp);
 
       /* Now, if insn doesn't accept our operand, put it into a pseudo.  */
-
-      if (!insn_data[icode].operand[1].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (!insn_data[icode].operand[0].predicate (temp, mode))
-	temp = gen_reg_rtx (mode);
-
-      pat = GEN_FCN (icode) (temp, xop0);
-      if (pat)
+      if ((temp = maybe_legitimize_insn_target (icode, 0, target, mode))
+	  && (xop0 = maybe_legitimize_insn_source (icode, 1, xop0, mode0))
+	  && (pat = GEN_FCN (icode) (temp, xop0)))
 	{
 	  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
 	      && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX))
@@ -3022,8 +3058,7 @@ expand_unop_direct (enum machine_mode mo
 
 	  return temp;
 	}
-      else
-	delete_insns_since (last);
+      delete_insns_since (last);
     }
   return 0;
 }
@@ -3499,7 +3534,7 @@ expand_copysign_absneg (enum machine_mod
 		        int bitpos, bool op0_is_abs)
 {
   enum machine_mode imode;
-  int icode;
+  enum insn_code icode;
   rtx sign, label;
 
   if (target == op1)
@@ -3507,10 +3542,10 @@ expand_copysign_absneg (enum machine_mod
 
   /* Check if the back end provides an insn that handles signbit for the
      argument's mode. */
-  icode = (int) optab_handler (signbit_optab, mode);
+  icode = optab_handler (signbit_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      imode = insn_data[icode].operand[0].mode;
+      imode = insn_data[(int) icode].operand[0].mode;
       sign = gen_reg_rtx (imode);
       emit_unop_insn (icode, sign, op1, UNKNOWN);
     }
@@ -3731,38 +3766,30 @@ expand_copysign (rtx op0, rtx op1, rtx t
    Return false if expansion failed.  */
 
 bool
-maybe_emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+maybe_emit_unop_insn (enum insn_code icode, rtx target, rtx op0,
+		      enum rtx_code code)
 {
   rtx temp;
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
   rtx pat;
   rtx last = get_last_insn ();
 
-  temp = target;
-
   /* Now, if insn does not accept our operands, put them into pseudos.  */
-
-  if (!insn_data[icode].operand[1].predicate (op0, mode0))
-    op0 = copy_to_mode_reg (mode0, op0);
-
-  if (!insn_data[icode].operand[0].predicate (temp, GET_MODE (temp)))
-    temp = gen_reg_rtx (GET_MODE (temp));
-
-  pat = GEN_FCN (icode) (temp, op0);
-  if (!pat)
+  if ((temp = maybe_legitimize_insn_target (icode, 0, target,
+					    GET_MODE (target)))
+      && (op0 = maybe_legitimize_insn_source (icode, 1, op0, VOIDmode))
+      && (pat = GEN_FCN (icode) (temp, op0)))
     {
-      delete_insns_since (last);
-      return false;
-    }
+      if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN)
+	add_equal_note (pat, temp, code, op0, NULL_RTX);
 
-  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN)
-    add_equal_note (pat, temp, code, op0, NULL_RTX);
-
-  emit_insn (pat);
+      emit_insn (pat);
 
-  if (temp != target)
-    emit_move_insn (target, temp);
-  return true;
+      if (temp != target)
+	emit_move_insn (target, temp);
+      return true;
+    }
+  delete_insns_since (last);
+  return false;
 }
 /* Generate an instruction whose insn-code is INSN_CODE,
    with two operands: an output TARGET and an input OP0.
@@ -3771,7 +3798,7 @@ maybe_emit_unop_insn (int icode, rtx tar
    the value that is stored into TARGET.  */
 
 void
-emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+emit_unop_insn (enum insn_code icode, rtx target, rtx op0, enum rtx_code code)
 {
   bool ok = maybe_emit_unop_insn (icode, target, op0, code);
   gcc_assert (ok);
@@ -3943,15 +3970,15 @@ can_compare_p (enum rtx_code code, enum 
   test = gen_rtx_fmt_ee (code, mode, const0_rtx, const0_rtx);
   do
     {
-      int icode;
+      enum insn_code icode;
 
       if (purpose == ccp_jump
           && (icode = optab_handler (cbranch_optab, mode)) != CODE_FOR_nothing
-          && insn_data[icode].operand[0].predicate (test, mode))
+          && insn_operand_matches (icode, 0, test))
         return 1;
       if (purpose == ccp_store_flag
           && (icode = optab_handler (cstore_optab, mode)) != CODE_FOR_nothing
-          && insn_data[icode].operand[1].predicate (test, mode))
+          && insn_operand_matches (icode, 1, test))
         return 1;
       if (purpose == ccp_cmov
 	  && optab_handler (cmov_optab, mode) != CODE_FOR_nothing)
@@ -4112,16 +4139,14 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx
       enum insn_code icode;
       icode = optab_handler (cbranch_optab, cmp_mode);
       if (icode != CODE_FOR_nothing
-	  && insn_data[icode].operand[0].predicate (test, VOIDmode))
+	  && insn_operand_matches (icode, 0, test))
 	{
 	  rtx last = get_last_insn ();
 	  rtx op0 = prepare_operand (icode, x, 1, mode, cmp_mode, unsignedp);
 	  rtx op1 = prepare_operand (icode, y, 2, mode, cmp_mode, unsignedp);
 	  if (op0 && op1
-	      && insn_data[icode].operand[1].predicate
-		 (op0, insn_data[icode].operand[1].mode)
-	      && insn_data[icode].operand[2].predicate
-		 (op1, insn_data[icode].operand[2].mode))
+	      && insn_operand_matches (icode, 1, op0)
+	      && insn_operand_matches (icode, 2, op1))
 	    {
 	      XEXP (test, 0) = op0;
 	      XEXP (test, 1) = op1;
@@ -4200,18 +4225,17 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx
    that it is accepted by the operand predicate.  Return the new value.  */
 
 rtx
-prepare_operand (int icode, rtx x, int opnum, enum machine_mode mode,
+prepare_operand (enum insn_code icode, rtx x, int opnum, enum machine_mode mode,
 		 enum machine_mode wider_mode, int unsignedp)
 {
   if (mode != wider_mode)
     x = convert_modes (wider_mode, mode, x, unsignedp);
 
-  if (!insn_data[icode].operand[opnum].predicate
-      (x, insn_data[icode].operand[opnum].mode))
+  if (!insn_operand_matches (icode, opnum, x))
     {
       if (reload_completed)
 	return NULL_RTX;
-      x = copy_to_mode_reg (insn_data[icode].operand[opnum].mode, x);
+      x = copy_to_mode_reg (insn_data[(int) icode].operand[opnum].mode, x);
     }
 
   return x;
@@ -4232,7 +4256,7 @@ emit_cmp_and_jump_insn_1 (rtx test, enum
   icode = optab_handler (cbranch_optab, optab_mode);
 
   gcc_assert (icode != CODE_FOR_nothing);
-  gcc_assert (insn_data[icode].operand[0].predicate (test, VOIDmode));
+  gcc_assert (insn_operand_matches (icode, 0, test));
   emit_jump_insn (GEN_FCN (icode) (test, XEXP (test, 0), XEXP (test, 1), label));
 }
 
@@ -4421,10 +4445,7 @@ prepare_float_lib_cmp (rtx x, rtx y, enu
 void
 emit_indirect_jump (rtx loc)
 {
-  if (!insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate
-      (loc, Pmode))
-    loc = copy_to_mode_reg (Pmode, loc);
-
+  loc = legitimize_insn_source (CODE_FOR_indirect_jump, 0, loc, Pmode);
   emit_jump_insn (gen_indirect_jump (loc));
   emit_barrier ();
 }
@@ -4497,21 +4518,13 @@ emit_conditional_move (rtx target, enum 
   if (!target)
     target = gen_reg_rtx (mode);
 
-  subtarget = target;
-
   /* If the insn doesn't accept these operands, put them in pseudos.  */
 
-  if (!insn_data[icode].operand[0].predicate
-      (subtarget, insn_data[icode].operand[0].mode))
-    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
-
-  if (!insn_data[icode].operand[2].predicate
-      (op2, insn_data[icode].operand[2].mode))
-    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
-
-  if (!insn_data[icode].operand[3].predicate
-      (op3, insn_data[icode].operand[3].mode))
-    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
+  if (!(subtarget = maybe_legitimize_insn_target (icode, 0, target, mode))
+      || !(op2 = maybe_legitimize_insn_source (icode, 2, op2, mode))
+      || !(op3 = maybe_legitimize_insn_source (icode, 3, op3, mode)))
+    /* The caller cleans up.  */
+    return NULL_RTX;
 
   /* Everything should now be in the suitable form.  */
 
@@ -4638,19 +4651,11 @@ emit_conditional_add (rtx target, enum r
 
   /* If the insn doesn't accept these operands, put them in pseudos.  */
 
-  if (!insn_data[icode].operand[0].predicate
-      (target, insn_data[icode].operand[0].mode))
-    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
-  else
-    subtarget = target;
-
-  if (!insn_data[icode].operand[2].predicate
-      (op2, insn_data[icode].operand[2].mode))
-    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
-
-  if (!insn_data[icode].operand[3].predicate
-      (op3, insn_data[icode].operand[3].mode))
-    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
+  if (!(subtarget = maybe_legitimize_insn_target (icode, 0, target, mode))
+      && !(op2 = maybe_legitimize_insn_source (icode, 2, op2, mode))
+      && !(op3 = maybe_legitimize_insn_source (icode, 3, op3, mode)))
+    /* The caller cleans up.  */
+    return NULL_RTX;
 
   /* Everything should now be in the suitable form.  */
 
@@ -4699,14 +4704,11 @@ emit_conditional_add (rtx target, enum r
 rtx
 gen_add2_insn (rtx x, rtx y)
 {
-  int icode = (int) optab_handler (add_optab, GET_MODE (x));
+  enum insn_code icode = optab_handler (add_optab, GET_MODE (x));
 
-  gcc_assert (insn_data[icode].operand[0].predicate
-	      (x, insn_data[icode].operand[0].mode));
-  gcc_assert (insn_data[icode].operand[1].predicate
-	      (x, insn_data[icode].operand[1].mode));
-  gcc_assert (insn_data[icode].operand[2].predicate
-	      (y, insn_data[icode].operand[2].mode));
+  gcc_assert (insn_operand_matches (icode, 0, x));
+  gcc_assert (insn_operand_matches (icode, 1, x));
+  gcc_assert (insn_operand_matches (icode, 2, y));
 
   return GEN_FCN (icode) (x, x, y);
 }
@@ -4717,15 +4719,12 @@ gen_add2_insn (rtx x, rtx y)
 rtx
 gen_add3_insn (rtx r0, rtx r1, rtx c)
 {
-  int icode = (int) optab_handler (add_optab, GET_MODE (r0));
+  enum insn_code icode = optab_handler (add_optab, GET_MODE (r0));
 
   if (icode == CODE_FOR_nothing
-      || !(insn_data[icode].operand[0].predicate
-	   (r0, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (r1, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (c, insn_data[icode].operand[2].mode)))
+      || !insn_operand_matches (icode, 0, r0)
+      || !insn_operand_matches (icode, 1, r1)
+      || !insn_operand_matches (icode, 2, c))
     return NULL_RTX;
 
   return GEN_FCN (icode) (r0, r1, c);
@@ -4734,21 +4733,18 @@ gen_add3_insn (rtx r0, rtx r1, rtx c)
 int
 have_add2_insn (rtx x, rtx y)
 {
-  int icode;
+  enum insn_code icode;
 
   gcc_assert (GET_MODE (x) != VOIDmode);
 
-  icode = (int) optab_handler (add_optab, GET_MODE (x));
+  icode = optab_handler (add_optab, GET_MODE (x));
 
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!(insn_data[icode].operand[0].predicate
-	(x, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (x, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (y, insn_data[icode].operand[2].mode)))
+  if (!insn_operand_matches (icode, 0, x)
+      || !insn_operand_matches (icode, 1, x)
+      || !insn_operand_matches (icode, 2, y))
     return 0;
 
   return 1;
@@ -4759,14 +4755,11 @@ have_add2_insn (rtx x, rtx y)
 rtx
 gen_sub2_insn (rtx x, rtx y)
 {
-  int icode = (int) optab_handler (sub_optab, GET_MODE (x));
+  enum insn_code icode = optab_handler (sub_optab, GET_MODE (x));
 
-  gcc_assert (insn_data[icode].operand[0].predicate
-	      (x, insn_data[icode].operand[0].mode));
-  gcc_assert (insn_data[icode].operand[1].predicate
-	      (x, insn_data[icode].operand[1].mode));
-  gcc_assert  (insn_data[icode].operand[2].predicate
-	       (y, insn_data[icode].operand[2].mode));
+  gcc_assert (insn_operand_matches (icode, 0, x));
+  gcc_assert (insn_operand_matches (icode, 1, x));
+  gcc_assert (insn_operand_matches (icode, 2, y));
 
   return GEN_FCN (icode) (x, x, y);
 }
@@ -4777,15 +4770,12 @@ gen_sub2_insn (rtx x, rtx y)
 rtx
 gen_sub3_insn (rtx r0, rtx r1, rtx c)
 {
-  int icode = (int) optab_handler (sub_optab, GET_MODE (r0));
+  enum insn_code icode = optab_handler (sub_optab, GET_MODE (r0));
 
   if (icode == CODE_FOR_nothing
-      || !(insn_data[icode].operand[0].predicate
-	   (r0, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (r1, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (c, insn_data[icode].operand[2].mode)))
+      || !insn_operand_matches (icode, 0, r0)
+      || !insn_operand_matches (icode, 1, r1)
+      || !insn_operand_matches (icode, 2, c))
     return NULL_RTX;
 
   return GEN_FCN (icode) (r0, r1, c);
@@ -4794,21 +4784,18 @@ gen_sub3_insn (rtx r0, rtx r1, rtx c)
 int
 have_sub2_insn (rtx x, rtx y)
 {
-  int icode;
+  enum insn_code icode;
 
   gcc_assert (GET_MODE (x) != VOIDmode);
 
-  icode = (int) optab_handler (sub_optab, GET_MODE (x));
+  icode = optab_handler (sub_optab, GET_MODE (x));
 
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!(insn_data[icode].operand[0].predicate
-	(x, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (x, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (y, insn_data[icode].operand[2].mode)))
+  if (!insn_operand_matches (icode, 0, x)
+      || !insn_operand_matches (icode, 1, x)
+      || !insn_operand_matches (icode, 2, y))
     return 0;
 
   return 1;
@@ -6643,8 +6630,7 @@ gen_cond_trap (enum rtx_code code, rtx o
     return 0;
 
   /* Some targets only accept a zero trap code.  */
-  if (insn_data[icode].operand[3].predicate
-      && !insn_data[icode].operand[3].predicate (tcode, VOIDmode))
+  if (!insn_operand_matches (icode, 3, tcode))
     return 0;
 
   do_pending_stack_adjust ();
@@ -6753,14 +6739,8 @@ vector_compare_rtx (tree cond, bool unsi
   rtx_op1 = expand_expr (t_op1, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op1)),
 			 EXPAND_STACK_PARM);
 
-  if (!insn_data[icode].operand[4].predicate (rtx_op0, GET_MODE (rtx_op0))
-      && GET_MODE (rtx_op0) != VOIDmode)
-    rtx_op0 = force_reg (GET_MODE (rtx_op0), rtx_op0);
-
-  if (!insn_data[icode].operand[5].predicate (rtx_op1, GET_MODE (rtx_op1))
-      && GET_MODE (rtx_op1) != VOIDmode)
-    rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1);
-
+  rtx_op0 = legitimize_insn_source (icode, 4, rtx_op0, GET_MODE (rtx_op0));
+  rtx_op1 = legitimize_insn_source (icode, 5, rtx_op1, GET_MODE (rtx_op1));
   return gen_rtx_fmt_ee (rcode, VOIDmode, rtx_op0, rtx_op1);
 }
 
@@ -6805,8 +6785,7 @@ expand_vec_cond_expr (tree vec_cond_type
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    target = gen_reg_rtx (mode);
+  target = legitimize_insn_target (icode, 0, target, mode);
 
   /* Get comparison rtx.  First expand both cond expr operands.  */
   comparison = vector_compare_rtx (op0,
@@ -6814,15 +6793,8 @@ expand_vec_cond_expr (tree vec_cond_type
   cc_op0 = XEXP (comparison, 0);
   cc_op1 = XEXP (comparison, 1);
   /* Expand both operands and force them in reg, if required.  */
-  rtx_op1 = expand_normal (op1);
-  if (!insn_data[icode].operand[1].predicate (rtx_op1, mode)
-      && mode != VOIDmode)
-    rtx_op1 = force_reg (mode, rtx_op1);
-
-  rtx_op2 = expand_normal (op2);
-  if (!insn_data[icode].operand[2].predicate (rtx_op2, mode)
-      && mode != VOIDmode)
-    rtx_op2 = force_reg (mode, rtx_op2);
+  rtx_op1 = legitimize_insn_source (icode, 1, expand_normal (op1), mode);
+  rtx_op2 = legitimize_insn_source (icode, 2, expand_normal (op2), mode);
 
   /* Emit instruction! */
   emit_insn (GEN_FCN (icode) (target, rtx_op1, rtx_op2,
@@ -6843,27 +6815,26 @@ expand_val_compare_and_swap_1 (rtx mem, 
 			       rtx target, enum insn_code icode)
 {
   enum machine_mode mode = GET_MODE (mem);
-  rtx insn;
+  rtx pat, start;
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    target = gen_reg_rtx (mode);
+  start = get_last_insn ();
 
   if (GET_MODE (old_val) != VOIDmode && GET_MODE (old_val) != mode)
     old_val = convert_modes (mode, GET_MODE (old_val), old_val, 1);
-  if (!insn_data[icode].operand[2].predicate (old_val, mode))
-    old_val = force_reg (mode, old_val);
 
   if (GET_MODE (new_val) != VOIDmode && GET_MODE (new_val) != mode)
     new_val = convert_modes (mode, GET_MODE (new_val), new_val, 1);
-  if (!insn_data[icode].operand[3].predicate (new_val, mode))
-    new_val = force_reg (mode, new_val);
 
-  insn = GEN_FCN (icode) (target, mem, old_val, new_val);
-  if (insn == NULL_RTX)
-    return NULL_RTX;
-  emit_insn (insn);
-
-  return target;
+  if ((target = maybe_legitimize_insn_target (icode, 0, target, mode))
+      && (old_val = maybe_legitimize_insn_source (icode, 2, old_val, mode))
+      && (new_val = maybe_legitimize_insn_source (icode, 3, new_val, mode))
+      && (pat = GEN_FCN (icode) (target, mem, old_val, new_val)))
+    {
+      emit_insn (pat);
+      return target;
+    }
+  delete_insns_since (start);
+  return NULL_RTX;
 }
 
 /* Expand a compare-and-swap operation and return its value.  */
@@ -7068,17 +7039,19 @@ expand_sync_operation (rtx mem, rtx val,
   /* Generate the direct operation, if present.  */
   if (icode != CODE_FOR_nothing)
     {
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[1].predicate (val, mode))
-	val = force_reg (mode, val);
+      rtx start, op1;
 
-      insn = GEN_FCN (icode) (mem, val);
-      if (insn)
+      start = get_last_insn ();
+      op1 = val;
+      if (GET_MODE (op1) != VOIDmode && GET_MODE (op1) != mode)
+	op1 = convert_modes (mode, GET_MODE (op1), op1, 1);
+      if ((op1 = maybe_legitimize_insn_source (icode, 1, op1, mode))
+	  && (insn = GEN_FCN (icode) (mem, op1)))
 	{
 	  emit_insn (insn);
 	  return const0_rtx;
 	}
+      delete_insns_since (start);
     }
 
   /* Failing that, generate a compare-and-swap loop in which we perform the
@@ -7201,16 +7174,17 @@ expand_sync_fetch_operation (rtx mem, rt
   /* If we found something supported, great.  */
   if (icode != CODE_FOR_nothing)
     {
-      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-	target = gen_reg_rtx (mode);
+      rtx start, dest, op2;
 
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[2].predicate (val, mode))
-	val = force_reg (mode, val);
+      start = get_last_insn ();
+
+      op2 = val;
+      if (GET_MODE (op2) != VOIDmode && GET_MODE (op2) != mode)
+	op2 = convert_modes (mode, GET_MODE (op2), op2, 1);
 
-      insn = GEN_FCN (icode) (target, mem, val);
-      if (insn)
+      if ((dest = maybe_legitimize_insn_target (icode, 0, target, mode))
+	  && (op2 = maybe_legitimize_insn_source (icode, 2, op2, mode))
+	  && (insn = GEN_FCN (icode) (dest, mem, op2)))
 	{
 	  emit_insn (insn);
 
@@ -7228,20 +7202,18 @@ expand_sync_fetch_operation (rtx mem, rt
 
 	      if (code == NOT)
 		{
-		  target = expand_simple_binop (mode, AND, target, val,
-						NULL_RTX, true,
-						OPTAB_LIB_WIDEN);
-		  target = expand_simple_unop (mode, code, target,
-					       NULL_RTX, true);
+		  dest = expand_simple_binop (mode, AND, dest, op2,
+					      NULL_RTX, true, OPTAB_LIB_WIDEN);
+		  dest = expand_simple_unop (mode, code, dest, NULL_RTX, true);
 		}
 	      else
-		target = expand_simple_binop (mode, code, target, val,
-					      NULL_RTX, true,
-					      OPTAB_LIB_WIDEN);
+		dest = expand_simple_binop (mode, code, dest, op2,
+					    NULL_RTX, true, OPTAB_LIB_WIDEN);
 	    }
 
-	  return target;
+	  return dest;
 	}
+      delete_insns_since (start);
     }
 
   /* Failing that, generate a compare-and-swap loop in which we perform the
@@ -7299,20 +7271,22 @@ expand_sync_lock_test_and_set (rtx mem, 
   icode = direct_optab_handler (sync_lock_test_and_set_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-	target = gen_reg_rtx (mode);
+      rtx start, dest, op2;
 
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[2].predicate (val, mode))
-	val = force_reg (mode, val);
+      start = get_last_insn ();
 
-      insn = GEN_FCN (icode) (target, mem, val);
-      if (insn)
+      op2 = val;
+      if (GET_MODE (op2) != VOIDmode && GET_MODE (op2) != mode)
+	op2 = convert_modes (mode, GET_MODE (op2), op2, 1);
+
+      if ((dest = maybe_legitimize_insn_target (icode, 0, target, mode))
+	  && (op2 = maybe_legitimize_insn_source (icode, 2, op2, mode))
+	  && (insn = GEN_FCN (icode) (dest, mem, op2)))
 	{
 	  emit_insn (insn);
-	  return target;
+	  return dest;
 	}
+      delete_insns_since (start);
     }
 
   /* Otherwise, use a compare-and-swap loop for the exchange.  */
Index: gcc/reload.c
===================================================================
--- gcc/reload.c	2011-02-28 10:29:00.000000000 +0000
+++ gcc/reload.c	2011-03-16 15:56:56.000000000 +0000
@@ -5819,17 +5819,15 @@ #define REG_OK_FOR_CONTEXT(CONTEXT, REGN
 	      rtx equiv = (MEM_P (XEXP (x, 0))
 			   ? XEXP (x, 0)
 			   : reg_equiv_mem[regno]);
-	      int icode = (int) optab_handler (add_optab, GET_MODE (x));
+	      enum insn_code icode = optab_handler (add_optab, GET_MODE (x));
 	      if (insn && NONJUMP_INSN_P (insn) && equiv
 		  && memory_operand (equiv, GET_MODE (equiv))
 #ifdef HAVE_cc0
 		  && ! sets_cc0_p (PATTERN (insn))
 #endif
 		  && ! (icode != CODE_FOR_nothing
-			&& ((*insn_data[icode].operand[0].predicate)
-			    (equiv, GET_MODE (x)))
-			&& ((*insn_data[icode].operand[1].predicate)
-			    (equiv, GET_MODE (x)))))
+			&& insn_operand_matches (icode, 0, equiv)
+			&& insn_operand_matches (icode, 1, equiv)))
 		{
 		  /* We use the original pseudo for loc, so that
 		     emit_reload_insns() knows which pseudo this
Index: gcc/reload1.c
===================================================================
--- gcc/reload1.c	2011-01-28 11:27:01.000000000 +0000
+++ gcc/reload1.c	2011-03-16 15:56:16.000000000 +0000
@@ -8479,7 +8479,7 @@ gen_reload (rtx out, rtx in, int opnum, 
 	 not valid than to dummy things up.  */
 
       rtx op0, op1, tem, insn;
-      int code;
+      enum insn_code code;
 
       op0 = find_replacement (&XEXP (in, 0));
       op1 = find_replacement (&XEXP (in, 1));
@@ -8517,14 +8517,13 @@ gen_reload (rtx out, rtx in, int opnum, 
 	 DEFINE_PEEPHOLE should be specified that recognizes the sequence
 	 we emit below.  */
 
-      code = (int) optab_handler (add_optab, GET_MODE (out));
+      code = optab_handler (add_optab, GET_MODE (out));
 
       if (CONSTANT_P (op1) || MEM_P (op1) || GET_CODE (op1) == SUBREG
 	  || (REG_P (op1)
 	      && REGNO (op1) >= FIRST_PSEUDO_REGISTER)
 	  || (code != CODE_FOR_nothing
-	      && ! ((*insn_data[code].operand[2].predicate)
-		    (op1, insn_data[code].operand[2].mode))))
+	      && !insn_operand_matches (code, 2, op1)))
 	tem = op0, op0 = op1, op1 = tem;
 
       gen_reload (out, op0, opnum, type);
Index: gcc/targhooks.c
===================================================================
--- gcc/targhooks.c	2011-01-28 11:27:01.000000000 +0000
+++ gcc/targhooks.c	2011-03-16 10:55:16.000000000 +0000
@@ -893,8 +893,7 @@ default_secondary_reload (bool in_p ATTR
 				reload_mode);
 
       if (icode != CODE_FOR_nothing
-	  && insn_data[(int) icode].operand[in_p].predicate
-	  && ! insn_data[(int) icode].operand[in_p].predicate (x, reload_mode))
+	  && !insn_operand_matches (icode, in_p, x))
 	icode = CODE_FOR_nothing;
       else if (icode != CODE_FOR_nothing)
 	{

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-17 16:33 Cleaning up expand optabs code Richard Sandiford
@ 2011-03-17 19:20 ` Richard Henderson
  2011-03-19 19:53   ` Richard Sandiford
  0 siblings, 1 reply; 32+ messages in thread
From: Richard Henderson @ 2011-03-17 19:20 UTC (permalink / raw)
  To: gcc-patches, patches, richard.sandiford

On 03/17/2011 09:32 AM, Richard Sandiford wrote:
> This patch adds a few helper functions for dealing with optabs:
> 
> - insn_operand_matches (ICODE, OPNO, X)
> - (maybe_)legitimize_insn_target (ICODE, OPNO, TARGET, MODE)
> - (maybe_)legitimize_insn_source (ICODE, OPNO, SOURCE, MODE)

Cool.

Why pass in MODE in the later two cases?  Seems to me that your
argument for omitting it for insn_operand_matches is just as
compelling for the other functions...

> - I first tried to make insn_operand_matches an inline function,
>   but I can't think of a good header file that is guaranteed to
>   know about both recog.h and insn-codes.h.

Meh.  I can't imagine it mattering so much.  Anyway, an lto build
of gcc itself would fix that if needed, right?  ;-)

> -      char_rtx = const0_rtx;
> -      char_mode = insn_data[(int) icode].operand[2].mode;
> -      if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx,
> -							    char_mode))
> -	char_rtx = copy_to_mode_reg (char_mode, char_rtx);
> -
> -      pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
> -			     char_rtx, GEN_INT (align));
> -      if (! pat)
> -	return NULL_RTX;
> +      if (!(char_rtx = maybe_legitimize_insn_source (icode, 2, const0_rtx,
> +						     VOIDmode))
> +	  || !(pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
> +				      char_rtx, GEN_INT (align))))
> +	{
> +	  delete_insns_since (before_strlen);
> +	  return NULL_RTX;
> +	}
>        emit_insn (pat);

I'm not so fond of burying assignments into conditionals.  I know we
do it a lot in gcc, and it's a tad harder to avoid here than ...

> -      if (!insn_data[icode].operand[1].predicate (val, mode))
> -	val = force_reg (mode, val);
> -
> -      insn = GEN_FCN (icode) (mem, val);
> -      if (insn)
> +      start = get_last_insn ();
> +      if ((val = maybe_legitimize_insn_source (icode, 1, const0_rtx, mode))
> +	  && (insn = GEN_FCN (icode) (mem, val)))
>  	{
>  	  emit_insn (insn);
>  	  return;
>  	}
> +      delete_insns_since (start);

... here, where it's just as readable to write

	val = maybe_legitimize_insn_source (icode, 1, const0_rtx, mode);
	if (val)
	  {
	    insn = GEN_FCN (icode) (mem, val);
	    if (insn)
	      {
		emit_insn (insn);
		return;
	      }
	  }
	delete_insns_since (start);

> +  if ((temp = maybe_legitimize_insn_target (icode, 0, target, tmp_mode))
> +      && (xop0 = maybe_legitimize_insn_source (icode, 1, xop0, mode0))
> +      && (xop1 = maybe_legitimize_insn_source (icode, 2, xop1, mode1))
> +      && (pat = GEN_FCN (icode) (temp, xop0, xop1)))

Although, these patterns occur often enough that I wonder if there's a way
to tidy them up into a single function call.  We could either use a set of
functions like

    target = maybe_legitimize_emit_insn_ts (icode, target, src1);
    target = maybe_legitimize_emit_insn_tss (icode, target, src1, src2);

or have a single function with variadic source operands.  Probably the
separate functions is better for error checking within the compiler.  We
can validate the correct function was called for a given icode based on
the n_operands stored in the insn_data, and the compiler itself will make
sure that we didn't omit an argument for that function.

The interface I was considering here would validate the target and all
of the sources, emit the insn or delete_insns_since, and return the new
target or NULL if no insn was emitted.

All that said, I'm very much in favour of this cleanup.


r~

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-17 19:20 ` Richard Henderson
@ 2011-03-19 19:53   ` Richard Sandiford
  2011-03-21 19:27     ` Richard Henderson
  0 siblings, 1 reply; 32+ messages in thread
From: Richard Sandiford @ 2011-03-19 19:53 UTC (permalink / raw)
  To: Richard Henderson; +Cc: gcc-patches, patches

Richard Henderson <rth@redhat.com> writes:
> On 03/17/2011 09:32 AM, Richard Sandiford wrote:
>> This patch adds a few helper functions for dealing with optabs:
>> 
>> - insn_operand_matches (ICODE, OPNO, X)
>> - (maybe_)legitimize_insn_target (ICODE, OPNO, TARGET, MODE)
>> - (maybe_)legitimize_insn_source (ICODE, OPNO, SOURCE, MODE)
>
> Why pass in MODE in the later two cases?  Seems to me that your
> argument for omitting it for insn_operand_matches is just as
> compelling for the other functions...

The problem is that the insn_data mode isn't always going to tell us
what mode a new register should have.  match_operands without modes are
only a warning, not an error, and they're sometimes needed.  E.g. extv
and insv get passed word_mode values for registers and QImode values
for memories.  And even the register case is a problem, because word_mode
isn't constant on some targets.

We also have patterns like prefetch that take Pmode operands, and Pmode
is again not constant on some targets.  This case matters because we can
in principle have CONST_INT addresses that need to be forced into
registers.

Then there are special predicates, where the predicate mode in the
insn_data isn't necessarily the mode of the operand.  This again
matters for address operands, because they could be CONST_INTs
that need to be forced into registers.

I agree it'd be better if we could know from insn_data what the
right mode is for generation as well as matching, but it'd be
a lot more work.  Which I suppose is a long-winded way of saying
I bottled it.

>> - I first tried to make insn_operand_matches an inline function,
>>   but I can't think of a good header file that is guaranteed to
>>   know about both recog.h and insn-codes.h.
>
> Meh.  I can't imagine it mattering so much.  Anyway, an lto build
> of gcc itself would fix that if needed, right?  ;-)

Of course :-)

>> -      char_rtx = const0_rtx;
>> -      char_mode = insn_data[(int) icode].operand[2].mode;
>> -      if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx,
>> -							    char_mode))
>> -	char_rtx = copy_to_mode_reg (char_mode, char_rtx);
>> -
>> -      pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
>> -			     char_rtx, GEN_INT (align));
>> -      if (! pat)
>> -	return NULL_RTX;
>> +      if (!(char_rtx = maybe_legitimize_insn_source (icode, 2, const0_rtx,
>> +						     VOIDmode))
>> +	  || !(pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
>> +				      char_rtx, GEN_INT (align))))
>> +	{
>> +	  delete_insns_since (before_strlen);
>> +	  return NULL_RTX;
>> +	}
>>        emit_insn (pat);
>
> I'm not so fond of burying assignments into conditionals.  I know we
> do it a lot in gcc, and it's a tad harder to avoid here than ...

Yeah, I wasn't too fond of that either to be honest.

>> +  if ((temp = maybe_legitimize_insn_target (icode, 0, target, tmp_mode))
>> +      && (xop0 = maybe_legitimize_insn_source (icode, 1, xop0, mode0))
>> +      && (xop1 = maybe_legitimize_insn_source (icode, 2, xop1, mode1))
>> +      && (pat = GEN_FCN (icode) (temp, xop0, xop1)))
>
> Although, these patterns occur often enough that I wonder if there's a way
> to tidy them up into a single function call.  We could either use a set of
> functions like
>
>     target = maybe_legitimize_emit_insn_ts (icode, target, src1);
>     target = maybe_legitimize_emit_insn_tss (icode, target, src1, src2);
>
> or have a single function with variadic source operands.  Probably the
> separate functions is better for error checking within the compiler.  We
> can validate the correct function was called for a given icode based on
> the n_operands stored in the insn_data, and the compiler itself will make
> sure that we didn't omit an argument for that function.

OK, I gave that a go, but in the end, it seemed that there were
just too many variations for the _ts, _tss, etc.  It might not be
too bad if it was just "target" and "source", but many callers
want "fixed" as well.

> The interface I was considering here would validate the target and all
> of the sources, emit the insn or delete_insns_since, and return the new
> target or NULL if no insn was emitted.

Yeah, I agree a higher-level interface like that would be better.
In the end I went for a function that takes an array of light-weight
structures that describe each operand.  This also makes it easier to
handle other common idioms, like calling convert_modes beforehand.

Given the mode stuff above, I've tried to be quite draconian as far
as caller-provided modes go.  I think the caller really should know
what mode they're dealing with.  The one case where I had to hold
back a bit was create_convert_operand_from, which replaces things like:

  if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
    xop0 = convert_modes (mode0,
                          GET_MODE (op0) != VOIDmode
                          ? GET_MODE (op0)
                          : mode,
                          xop0, unsignedp);

It seems a little suspicious that we only trust "mode" for CONST_INT
op0s, and not for other cases, but I'd like to leave that for now.
Maybe a future "clean up"?

Also, I had to change the i386 setmem pattern in order to avoid
a regression in cold-attribute-1.c.  The current predicate for
the character operand is "const_int_operand", but the pattern
handles registers as well.  I'm sure there are going to be other
things like that, so sorry in advance if this patch goes in and
breaks a target...

Anyway, thanks for pushing back on this.  Looking back, the original
interface was a bit poor.  I hope the new version really is cleaner,
rather than just a token wiping over with suds.

Bootstrapped & regression-tested on x86_64-linux-gnu.

Richard


gcc/
	* expr.h (prepare_operand): Move to optabs.h.
	* optabs.h (emit_unop_insn): Change insn code parameter from "int"
	to "enum insn_code".
	(maybe_emit_unop_insn): Likewise.
	(prepare_operand): Likewise.  Move from expr.h.
	(MAX_EXPAND_OPERANDS): New macro.
	(expand_operand_type): New enum.
	(expand_operand): New structure.
	(create_expand_operand): New function.
	(create_fixed_operand, create_output_operand): Likewise
	(create_input_operand, create_convert_operand_to): Likewise.
	(create_convert_operand_from, create_address_operand): Likewise.
	(create_integer_operand, make_operand_commutative): Likewise.
	(create_convert_operand_from_type, insn_operand_matches)
	(maybe_legitimize_operands, maybe_gen_insn, maybe_expand_insn)
	(maybe_expand_jump_insn, expand_insn, expand_jump_insn): Declare.
	* builtins.c (expand_builtin_prefetch): Use the new interfaces.
	(expand_builtin_interclass_mathfn, expand_builtin_strlen): Likewise.
	(expand_movstr, expand_builtin___clear_cache): Likewise.
	(expand_builtin_lock_release): Likewise.
	* explow.c (allocate_dynamic_stack_space): Likewise.
	(probe_stack_range): Likewise.  Allow check_stack to FAIL,
	and use the default handling in that case.
	* expmed.c (check_predicate_volatile_ok): Delete.
	(store_bit_field_1, extract_bit_field_1): Use the new interfaces.
	(emit_cstore): Likewise.
	* expr.c (init_expr_target, emit_block_move_via_movmem): Likewise.
	(set_storage_via_setmem, compress_float_constant): Likewise.
	(expand_assignment, emit_storent_insn, try_casesi): Likewise.
	(emit_single_push_insn): Likewise.  Allow the expansion to fail.
	* function.c (safe_insn_predicate): Use the new interfaces.
	(assign_parm_setup_reg): Likewise.
	* optabs.c (expand_widen_pattern_expr, expand_ternary_op): Likewise.
	(expand_vec_shift_expr, expand_binop_directly): Likewise.
	(expand_twoval_unop, expand_twoval_binop): Likewise.
	(expand_unop_direct, prepare_cmp_insn): Likewise.
	(emit_cmp_and_jump_insn_1, emit_indirect_jump): Likewise.
	(emit_conditional_move, gen_add2_insn, gen_add3_insn): Likewise.
	(have_add2_insn, gen_sub2_insn, have_sub2_insn): Likewise.
	(gen_cond_trap, vector_compare_rtx, expand_vec_cond_expr): Likewise.
	(expand_val_compare_and_swap_1, expand_sync_operation): Likewise.
	(expand_sync_fetch_operation, expand_sync_lock_test_and_set): Likewise.
	(maybe_emit_unop_insn): Likewise.  Change icode to an insn_code.
	(emit_unop_insn): Likewise.
	(expand_copysign_absneg, prepare_operand): Change icode to an insn_code.
	(create_convert_operand_from_type): New function.
	(insn_operand_matches, maybe_legitimize_operand): Likewise.
	(maybe_legitimize_operands, maybe_gen_insn): Likewise.
	(maybe_expand_insn, maybe_expand_jump_insn): Likewise.
	(expand_insn, expand_jump_insn): Likewise.
	* reload.c (find_reloads_address_1): Use insn_operand_matches.
	* reload1.c (gen_reload): Likewise.
	* targhooks.c (default_secondary_reload): Likewise.
	* config/i386/i386.md (setmem<mode>): Use nonmemory_operand rather
	than const_int_operand for operand 2.

Index: gcc/expr.h
===================================================================
--- gcc/expr.h	2011-03-19 17:07:49.000000000 +0000
+++ gcc/expr.h	2011-03-19 17:11:42.000000000 +0000
@@ -173,9 +173,6 @@ extern rtx expand_simple_unop (enum mach
    perform the operation described by CODE and MODE.  */
 extern int have_insn_for (enum rtx_code, enum machine_mode);
 
-extern rtx prepare_operand (int, rtx, int, enum machine_mode, enum machine_mode,
-			    int);
-
 /* Emit code to make a call to a constant function or a library call.  */
 extern void emit_libcall_block (rtx, rtx, rtx, rtx);
 
Index: gcc/optabs.h
===================================================================
--- gcc/optabs.h	2011-03-19 17:07:49.000000000 +0000
+++ gcc/optabs.h	2011-03-19 17:11:42.000000000 +0000
@@ -791,8 +791,8 @@ extern rtx expand_copysign (rtx, rtx, rt
 
 /* Generate an instruction with a given INSN_CODE with an output and
    an input.  */
-extern void emit_unop_insn (int, rtx, rtx, enum rtx_code);
-extern bool maybe_emit_unop_insn (int, rtx, rtx, enum rtx_code);
+extern void emit_unop_insn (enum insn_code, rtx, rtx, enum rtx_code);
+extern bool maybe_emit_unop_insn (enum insn_code, rtx, rtx, enum rtx_code);
 
 /* An extra flag to control optab_for_tree_code's behavior.  This is needed to
    distinguish between machines with a vector shift that takes a scalar for the
@@ -923,4 +923,167 @@ set_direct_optab_handler (direct_optab o
 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);
+
+#define MAX_EXPAND_OPERANDS 6
+
+/* Describes the type of an expand_operand.  Each value is associated
+   with a create_*_operand function; see the comments above those
+   functions for details.  */
+enum expand_operand_type {
+  EXPAND_FIXED,
+  EXPAND_OUTPUT,
+  EXPAND_INPUT,
+  EXPAND_CONVERT_TO,
+  EXPAND_CONVERT_FROM,
+  EXPAND_ADDRESS,
+  EXPAND_INTEGER
+};
+
+/* Information about an operand for instruction expansion.  */
+struct expand_operand {
+  /* The type of operand.  */
+  ENUM_BITFIELD (expand_operand_type) type : 8;
+
+  /* True if any conversion should treat VALUE as being unsigned
+     rather than signed.  Only meaningful for certain types.  */
+  unsigned int unsigned_p : 1;
+
+  /* If this operand is the first of a commutative pair, this field
+     specifies the target operand that should be used when deciding
+     whether to swap the operands.  It is MAX_EXPAND_OPERANDS otherwise.
+
+     Shrink as necessary if new flags are needed.  */
+  unsigned int commutative : 7;
+
+  /* The mode passed to the convert_*_operand function.  It has a
+     type-dependent meaning.  */
+  ENUM_BITFIELD (machine_mode) mode : 16;
+
+  /* The value of the operand.  */
+  rtx value;
+};
+
+/* Initialize OP with the given fields.  Initialise the other fields
+   to their default values.  */
+
+static inline void
+create_expand_operand (struct expand_operand *op,
+		       enum expand_operand_type type,
+		       rtx value, enum machine_mode mode,
+		       bool unsigned_p)
+{
+  op->type = type;
+  op->unsigned_p = unsigned_p;
+  op->commutative = MAX_EXPAND_OPERANDS;
+  op->mode = mode;
+  op->value = value;
+}
+
+/* Make OP describe an operand that must use rtx X, even if X is volatile.  */
+
+static inline void
+create_fixed_operand (struct expand_operand *op, rtx x)
+{
+  create_expand_operand (op, EXPAND_FIXED, x, VOIDmode, false);
+}
+
+/* Make OP describe an output operand that must have mode MODE.
+   X, if nonnull, is a suggestion for where the output should be stored.
+   It is OK for VALUE to be inconsistent with MODE, although it will just
+   be ignored in that case.  */
+
+static inline void
+create_output_operand (struct expand_operand *op, rtx x,
+		       enum machine_mode mode)
+{
+  create_expand_operand (op, EXPAND_OUTPUT, x, mode, false);
+}
+
+/* Make OP describe an input operand that must have mode MODE and
+   value VALUE; MODE cannot be VOIDmode.  The backend may request that
+   VALUE be copied into a different kind of rtx before being passed
+   as an operand.  */
+
+static inline void
+create_input_operand (struct expand_operand *op, rtx value,
+		      enum machine_mode mode)
+{
+  create_expand_operand (op, EXPAND_INPUT, value, mode, false);
+}
+
+/* Like create_input_operand, except that VALUE must first be converted
+   to mode MODE.  UNSIGNED_P says whether VALUE is unsigned.  */
+
+static inline void
+create_convert_operand_to (struct expand_operand *op, rtx value,
+			   enum machine_mode mode, bool unsigned_p)
+{
+  create_expand_operand (op, EXPAND_CONVERT_TO, value, mode, unsigned_p);
+}
+
+/* Make OP describe an input operand that should have the same value
+   as VALUE, after any mode conversion that the backend might request.
+   If VALUE is a CONST_INT, it should be treated as having mode MODE.
+   UNSIGNED_P says whether VALUE is unsigned.  */
+
+static inline void
+create_convert_operand_from (struct expand_operand *op, rtx value,
+			     enum machine_mode mode, bool unsigned_p)
+{
+  create_expand_operand (op, EXPAND_CONVERT_FROM, value, mode, unsigned_p);
+}
+
+extern void create_convert_operand_from_type (struct expand_operand *op,
+					      rtx value, tree type);
+
+/* Make OP describe an input Pmode address operand.  VALUE is the value
+   of the address, but it may need to be converted to Pmode first.  */
+
+static inline void
+create_address_operand (struct expand_operand *op, rtx value)
+{
+  create_expand_operand (op, EXPAND_ADDRESS, value, Pmode, false);
+}
+
+/* Make OP describe an input operand that has value INTVAL and which has
+   no inherent mode.  This function should only be used for operands that
+   are always expand-time constants.  The backend may request that INTVAL
+   be copied into a different kind of rtx, but it must specify the mode
+   of that rtx if so.  */
+
+static inline void
+create_integer_operand (struct expand_operand *op, HOST_WIDE_INT intval)
+{
+  create_expand_operand (op, EXPAND_INTEGER, GEN_INT (intval), VOIDmode, false);
+}
+
+/* Record that OP and the following operand are commumative.  Use output
+   operand TARGET to decide whether the two commutative operands should
+   be swapped.  */
+
+static inline void
+make_operand_commutative (struct expand_operand *op, unsigned int target)
+{
+  op->commutative = target;
+}
+
+extern bool insn_operand_matches (enum insn_code icode, unsigned int opno,
+				  rtx operand);
+extern bool maybe_legitimize_operands (enum insn_code icode,
+				       unsigned int opno, unsigned int nops,
+				       struct expand_operand *ops);
+extern rtx maybe_gen_insn (enum insn_code icode, unsigned int nops,
+			   struct expand_operand *ops);
+extern bool maybe_expand_insn (enum insn_code icode, unsigned int nops,
+			       struct expand_operand *ops);
+extern bool maybe_expand_jump_insn (enum insn_code icode, unsigned int nops,
+				    struct expand_operand *ops);
+extern void expand_insn (enum insn_code icode, unsigned int nops,
+			 struct expand_operand *ops);
+extern void expand_jump_insn (enum insn_code icode, unsigned int nops,
+			      struct expand_operand *ops);
+
+extern rtx prepare_operand (enum insn_code, rtx, int, enum machine_mode,
+			    enum machine_mode, int);
+
 #endif /* GCC_OPTABS_H */
Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/builtins.c	2011-03-19 17:11:42.000000000 +0000
@@ -1143,15 +1143,13 @@ expand_builtin_prefetch (tree exp)
 #ifdef HAVE_prefetch
   if (HAVE_prefetch)
     {
-      if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate)
-	     (op0,
-	      insn_data[(int) CODE_FOR_prefetch].operand[0].mode))
-	  || (GET_MODE (op0) != Pmode))
-	{
-	  op0 = convert_memory_address (Pmode, op0);
-	  op0 = force_reg (Pmode, op0);
-	}
-      emit_insn (gen_prefetch (op0, op1, op2));
+      struct expand_operand ops[3];
+
+      create_address_operand (&ops[0], op0);
+      create_integer_operand (&ops[1], INTVAL (op1));
+      create_integer_operand (&ops[2], INTVAL (op2));
+      if (maybe_expand_insn (CODE_FOR_prefetch, 3, ops))
+	return;
     }
 #endif
 
@@ -2431,16 +2429,9 @@ expand_builtin_interclass_mathfn (tree e
 
   if (icode != CODE_FOR_nothing)
     {
+      struct expand_operand ops[1];
       rtx last = get_last_insn ();
       tree orig_arg = arg;
-      /* Make a suitable register to place result in.  */
-      if (!target
-	  || GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp))
-	  || !insn_data[icode].operand[0].predicate (target, GET_MODE (target)))
-         target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-
-      gcc_assert (insn_data[icode].operand[0].predicate
-		  (target, GET_MODE (target)));
 
       /* Wrap the computation of the argument in a SAVE_EXPR, as we may
 	 need to expand the argument again.  This way, we will not perform
@@ -2452,10 +2443,11 @@ expand_builtin_interclass_mathfn (tree e
       if (mode != GET_MODE (op0))
 	op0 = convert_to_mode (mode, op0, 0);
 
-      /* Compute into TARGET.
-	 Set TARGET to wherever the result comes back.  */
-      if (maybe_emit_unop_insn (icode, target, op0, UNKNOWN))
-	return target;
+      create_output_operand (&ops[0], target, TYPE_MODE (TREE_TYPE (exp)));
+      if (maybe_legitimize_operands (icode, 0, 1, ops)
+	  && maybe_emit_unop_insn (icode, ops[0].value, op0, UNKNOWN))
+	return ops[0].value;
+
       delete_insns_since (last);
       CALL_EXPR_ARG (exp, 0) = orig_arg;
     }
@@ -3362,11 +3354,12 @@ expand_builtin_strlen (tree exp, rtx tar
     return NULL_RTX;
   else
     {
+      struct expand_operand ops[4];
       rtx pat;
       tree len;
       tree src = CALL_EXPR_ARG (exp, 0);
-      rtx result, src_reg, char_rtx, before_strlen;
-      enum machine_mode insn_mode = target_mode, char_mode;
+      rtx src_reg, before_strlen;
+      enum machine_mode insn_mode = target_mode;
       enum insn_code icode = CODE_FOR_nothing;
       unsigned int align;
 
@@ -3405,14 +3398,6 @@ expand_builtin_strlen (tree exp, rtx tar
       if (insn_mode == VOIDmode)
 	return NULL_RTX;
 
-      /* Make a place to write the result of the instruction.  */
-      result = target;
-      if (! (result != 0
-	     && REG_P (result)
-	     && GET_MODE (result) == insn_mode
-	     && REGNO (result) >= FIRST_PSEUDO_REGISTER))
-	result = gen_reg_rtx (insn_mode);
-
       /* Make a place to hold the source address.  We will not expand
 	 the actual source until we are sure that the expansion will
 	 not fail -- there are trees that cannot be expanded twice.  */
@@ -3422,17 +3407,12 @@ expand_builtin_strlen (tree exp, rtx tar
 	 source operand later.  */
       before_strlen = get_last_insn ();
 
-      char_rtx = const0_rtx;
-      char_mode = insn_data[(int) icode].operand[2].mode;
-      if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx,
-							    char_mode))
-	char_rtx = copy_to_mode_reg (char_mode, char_rtx);
-
-      pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
-			     char_rtx, GEN_INT (align));
-      if (! pat)
+      create_output_operand (&ops[0], target, insn_mode);
+      create_fixed_operand (&ops[1], gen_rtx_MEM (BLKmode, src_reg));
+      create_integer_operand (&ops[2], 0);
+      create_integer_operand (&ops[3], align);
+      if (!maybe_expand_insn (icode, 4, ops))
 	return NULL_RTX;
-      emit_insn (pat);
 
       /* Now that we are assured of success, expand the source.  */
       start_sequence ();
@@ -3448,12 +3428,12 @@ expand_builtin_strlen (tree exp, rtx tar
 	emit_insn_before (pat, get_insns ());
 
       /* Return the value in the proper mode for this function.  */
-      if (GET_MODE (result) == target_mode)
-	target = result;
+      if (GET_MODE (ops[0].value) == target_mode)
+	target = ops[0].value;
       else if (target != 0)
-	convert_move (target, result, 0);
+	convert_move (target, ops[0].value, 0);
       else
-	target = convert_to_mode (target_mode, result, 0);
+	target = convert_to_mode (target_mode, ops[0].value, 0);
 
       return target;
     }
@@ -3674,56 +3654,39 @@ expand_builtin_mempcpy_args (tree dest,
 static rtx
 expand_movstr (tree dest, tree src, rtx target, int endp)
 {
+  struct expand_operand ops[3];
   rtx end;
   rtx dest_mem;
   rtx src_mem;
-  rtx insn;
-  const struct insn_data_d * data;
 
   if (!HAVE_movstr)
     return NULL_RTX;
 
   dest_mem = get_memory_rtx (dest, NULL);
   src_mem = get_memory_rtx (src, NULL);
-  data = insn_data + CODE_FOR_movstr;
   if (!endp)
     {
       target = force_reg (Pmode, XEXP (dest_mem, 0));
       dest_mem = replace_equiv_address (dest_mem, target);
-      end = gen_reg_rtx (Pmode);
     }
-  else
-    {
-      if (target == 0
-	  || target == const0_rtx
-	  || ! (*data->operand[0].predicate) (target, Pmode))
+
+  create_output_operand (&ops[0], endp ? target : NULL_RTX, Pmode);
+  create_fixed_operand (&ops[1], dest_mem);
+  create_fixed_operand (&ops[2], src_mem);
+  expand_insn (CODE_FOR_movstr, 3, ops);
+
+  if (endp && target != const0_rtx)
+    {
+      target = ops[0].value;
+      /* movstr is supposed to set end to the address of the NUL
+	 terminator.  If the caller requested a mempcpy-like return value,
+	 adjust it.  */
+      if (endp == 1)
 	{
-	  end = gen_reg_rtx (Pmode);
-	  if (target != const0_rtx)
-	    target = end;
+	  rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1);
+	  emit_move_insn (target, force_operand (tem, NULL_RTX));
 	}
-      else
-	end = target;
     }
-
-  if (data->operand[0].mode != VOIDmode)
-    end = gen_lowpart (data->operand[0].mode, end);
-
-  insn = data->genfun (end, dest_mem, src_mem);
-
-  gcc_assert (insn);
-
-  emit_insn (insn);
-
-  /* movstr is supposed to set end to the address of the NUL
-     terminator.  If the caller requested a mempcpy-like return value,
-     adjust it.  */
-  if (endp == 1 && target != const0_rtx)
-    {
-      rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1);
-      emit_move_insn (target, force_operand (tem, NULL_RTX));
-    }
-
   return target;
 }
 
@@ -5223,7 +5186,6 @@ expand_builtin___clear_cache (tree exp A
   /* We have a "clear_cache" insn, and it will handle everything.  */
   tree begin, end;
   rtx begin_rtx, end_rtx;
-  enum insn_code icode;
 
   /* We must not expand to a library call.  If we did, any
      fallback library function in libgcc that might contain a call to
@@ -5236,21 +5198,18 @@ expand_builtin___clear_cache (tree exp A
 
   if (HAVE_clear_cache)
     {
-      icode = CODE_FOR_clear_cache;
+      struct expand_operand ops[2];
 
       begin = CALL_EXPR_ARG (exp, 0);
       begin_rtx = expand_expr (begin, NULL_RTX, Pmode, EXPAND_NORMAL);
-      begin_rtx = convert_memory_address (Pmode, begin_rtx);
-      if (!insn_data[icode].operand[0].predicate (begin_rtx, Pmode))
-	begin_rtx = copy_to_mode_reg (Pmode, begin_rtx);
 
       end = CALL_EXPR_ARG (exp, 1);
       end_rtx = expand_expr (end, NULL_RTX, Pmode, EXPAND_NORMAL);
-      end_rtx = convert_memory_address (Pmode, end_rtx);
-      if (!insn_data[icode].operand[1].predicate (end_rtx, Pmode))
-	end_rtx = copy_to_mode_reg (Pmode, end_rtx);
 
-      emit_insn (gen_clear_cache (begin_rtx, end_rtx));
+      create_address_operand (&ops[0], begin_rtx);
+      create_address_operand (&ops[1], end_rtx);
+      if (maybe_expand_insn (CODE_FOR_clear_cache, 2, ops))
+	return const0_rtx;
     }
   return const0_rtx;
 #endif /* HAVE_clear_cache */
@@ -5748,9 +5707,9 @@ expand_builtin_synchronize (void)
 static void
 expand_builtin_lock_release (enum machine_mode mode, tree exp)
 {
+  struct expand_operand ops[2];
   enum insn_code icode;
-  rtx mem, insn;
-  rtx val = const0_rtx;
+  rtx mem;
 
   /* Expand the operands.  */
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
@@ -5759,21 +5718,16 @@ expand_builtin_lock_release (enum machin
   icode = direct_optab_handler (sync_lock_release_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (!insn_data[icode].operand[1].predicate (val, mode))
-	val = force_reg (mode, val);
-
-      insn = GEN_FCN (icode) (mem, val);
-      if (insn)
-	{
-	  emit_insn (insn);
-	  return;
-	}
+      create_fixed_operand (&ops[0], mem);
+      create_input_operand (&ops[1], const0_rtx, mode);
+      if (maybe_expand_insn (icode, 2, ops))
+	return;
     }
 
   /* Otherwise we can implement this operation by emitting a barrier
      followed by a store of zero.  */
   expand_builtin_synchronize ();
-  emit_move_insn (mem, val);
+  emit_move_insn (mem, const0_rtx);
 }
 \f
 /* Expand an expression EXP that calls a built-in function,
Index: gcc/explow.c
===================================================================
--- gcc/explow.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/explow.c	2011-03-19 17:11:42.000000000 +0000
@@ -1379,21 +1379,13 @@ allocate_dynamic_stack_space (rtx size,
 #ifdef HAVE_allocate_stack
   if (HAVE_allocate_stack)
     {
-      enum machine_mode mode = STACK_SIZE_MODE;
-      insn_operand_predicate_fn pred;
-
+      struct expand_operand ops[2];
       /* We don't have to check against the predicate for operand 0 since
 	 TARGET is known to be a pseudo of the proper mode, which must
-	 be valid for the operand.  For operand 1, convert to the
-	 proper mode and validate.  */
-      if (mode == VOIDmode)
-	mode = insn_data[(int) CODE_FOR_allocate_stack].operand[1].mode;
-
-      pred = insn_data[(int) CODE_FOR_allocate_stack].operand[1].predicate;
-      if (pred && ! ((*pred) (size, mode)))
-	size = copy_to_mode_reg (mode, convert_to_mode (mode, size, 1));
-
-      emit_insn (gen_allocate_stack (target, size));
+	 be valid for the operand.  */
+      create_fixed_operand (&ops[0], target);
+      create_convert_operand_to (&ops[1], size, STACK_SIZE_MODE, true);
+      expand_insn (CODE_FOR_allocate_stack, 2, ops);
     }
   else
 #endif
@@ -1544,22 +1536,22 @@ probe_stack_range (HOST_WIDE_INT first,
 					         plus_constant (size, first)));
       emit_library_call (stack_check_libfunc, LCT_NORMAL, VOIDmode, 1, addr,
 			 Pmode);
+      return;
     }
 
   /* Next see if we have an insn to check the stack.  */
 #ifdef HAVE_check_stack
-  else if (HAVE_check_stack)
+  if (HAVE_check_stack)
     {
+      struct expand_operand ops[1];
       rtx addr = memory_address (Pmode,
 				 gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
 					         stack_pointer_rtx,
 					         plus_constant (size, first)));
-      insn_operand_predicate_fn pred
-	= insn_data[(int) CODE_FOR_check_stack].operand[0].predicate;
-      if (pred && !((*pred) (addr, Pmode)))
-	addr = copy_to_mode_reg (Pmode, addr);
 
-      emit_insn (gen_check_stack (addr));
+      create_input_operand (&ops[0], addr, Pmode);
+      if (maybe_expand_insn (CODE_FOR_check_stack, 1, ops))
+	return;
     }
 #endif
 
Index: gcc/expmed.c
===================================================================
--- gcc/expmed.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/expmed.c	2011-03-19 17:11:42.000000000 +0000
@@ -323,22 +323,6 @@ mode_for_extraction (enum extraction_pat
     return word_mode;
   return data->operand[opno].mode;
 }
-
-/* Return true if X, of mode MODE, matches the predicate for operand
-   OPNO of instruction ICODE.  Allow volatile memories, regardless of
-   the ambient volatile_ok setting.  */
-
-static bool
-check_predicate_volatile_ok (enum insn_code icode, int opno,
-			     rtx x, enum machine_mode mode)
-{
-  bool save_volatile_ok, result;
-
-  save_volatile_ok = volatile_ok;
-  result = insn_data[(int) icode].operand[opno].predicate (x, mode);
-  volatile_ok = save_volatile_ok;
-  return result;
-}
 \f
 /* A subroutine of store_bit_field, with the same arguments.  Return true
    if the operation could be implemented.
@@ -405,40 +389,17 @@ store_bit_field_1 (rtx str_rtx, unsigned
       && 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];
       enum machine_mode outermode = GET_MODE (op0);
       enum machine_mode innermode = GET_MODE_INNER (outermode);
-      int icode = (int) optab_handler (vec_set_optab, outermode);
+      enum insn_code icode = optab_handler (vec_set_optab, outermode);
       int pos = bitnum / GET_MODE_BITSIZE (innermode);
-      rtx rtxpos = GEN_INT (pos);
-      rtx src = value;
-      rtx dest = op0;
-      rtx pat, seq;
-      enum machine_mode mode0 = insn_data[icode].operand[0].mode;
-      enum machine_mode mode1 = insn_data[icode].operand[1].mode;
-      enum machine_mode mode2 = insn_data[icode].operand[2].mode;
-
-      start_sequence ();
 
-      if (! (*insn_data[icode].operand[1].predicate) (src, mode1))
-	src = copy_to_mode_reg (mode1, src);
-
-      if (! (*insn_data[icode].operand[2].predicate) (rtxpos, mode2))
-	rtxpos = copy_to_mode_reg (mode1, rtxpos);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert ((*insn_data[icode].operand[0].predicate) (dest, mode0)
-		  && (*insn_data[icode].operand[1].predicate) (src, mode1)
-		  && (*insn_data[icode].operand[2].predicate) (rtxpos, mode2));
-      pat = GEN_FCN (icode) (dest, src, rtxpos);
-      seq = get_insns ();
-      end_sequence ();
-      if (pat)
-	{
-	  emit_insn (seq);
-	  emit_insn (pat);
-	  return true;
-	}
+      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
@@ -515,44 +476,30 @@ store_bit_field_1 (rtx str_rtx, unsigned
       && bitsize == GET_MODE_BITSIZE (fieldmode)
       && optab_handler (movstrict_optab, fieldmode) != CODE_FOR_nothing)
     {
-      int icode = optab_handler (movstrict_optab, fieldmode);
-      rtx insn;
-      rtx start = get_last_insn ();
+      struct expand_operand ops[2];
+      enum insn_code icode = optab_handler (movstrict_optab, fieldmode);
       rtx arg0 = op0;
 
-      /* Get appropriate low part of the value being stored.  */
-      if (CONST_INT_P (value) || REG_P (value))
-	value = gen_lowpart (fieldmode, value);
-      else if (!(GET_CODE (value) == SYMBOL_REF
-		 || GET_CODE (value) == LABEL_REF
-		 || GET_CODE (value) == CONST))
-	value = convert_to_mode (fieldmode, value, 0);
-
-      if (! (*insn_data[icode].operand[1].predicate) (value, fieldmode))
-	value = copy_to_mode_reg (fieldmode, value);
-
-      if (GET_CODE (op0) == SUBREG)
+      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 (op0)) == fieldmode
+	  gcc_assert (GET_MODE (SUBREG_REG (arg0)) == fieldmode
 		      || GET_MODE_CLASS (fieldmode) == MODE_INT
 		      || GET_MODE_CLASS (fieldmode) == MODE_PARTIAL_INT);
-	  arg0 = SUBREG_REG (op0);
+	  arg0 = SUBREG_REG (arg0);
 	}
 
-      insn = (GEN_FCN (icode)
-		 (gen_rtx_SUBREG (fieldmode, arg0,
-				  (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
-				  + (offset * UNITS_PER_WORD)),
-				  value));
-      if (insn)
-	{
-	  emit_insn (insn);
-	  return true;
-	}
-      delete_insns_since (start);
+      arg0 = gen_rtx_SUBREG (fieldmode, arg0,
+			     (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
+			     + (offset * UNITS_PER_WORD));
+
+      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.  */
@@ -653,16 +600,13 @@ store_bit_field_1 (rtx str_rtx, unsigned
       && bitsize > 0
       && GET_MODE_BITSIZE (op_mode) >= bitsize
       && ! ((REG_P (op0) || GET_CODE (op0) == SUBREG)
-	    && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode)))
-      && insn_data[CODE_FOR_insv].operand[1].predicate (GEN_INT (bitsize),
-							VOIDmode)
-      && check_predicate_volatile_ok (CODE_FOR_insv, 0, op0, VOIDmode))
+	    && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode))))
     {
+      struct expand_operand ops[4];
       int xbitpos = bitpos;
       rtx value1;
       rtx xop0 = op0;
       rtx last = get_last_insn ();
-      rtx pat;
       bool copy_back = false;
 
       /* Add OFFSET into OP0's address.  */
@@ -743,17 +687,12 @@ store_bit_field_1 (rtx str_rtx, unsigned
 	    gcc_assert (CONSTANT_P (value));
 	}
 
-      /* If this machine's insv insists on a register,
-	 get VALUE1 into a register.  */
-      if (! ((*insn_data[(int) CODE_FOR_insv].operand[3].predicate)
-	     (value1, op_mode)))
-	value1 = force_reg (op_mode, value1);
-
-      pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1);
-      if (pat)
+      create_fixed_operand (&ops[0], xop0);
+      create_integer_operand (&ops[1], bitsize);
+      create_integer_operand (&ops[2], xbitpos);
+      create_input_operand (&ops[3], value1, op_mode);
+      if (maybe_expand_insn (CODE_FOR_insv, 4, ops))
 	{
-	  emit_insn (pat);
-
 	  if (copy_back)
 	    convert_move (op0, xop0, true);
 	  return true;
@@ -1235,50 +1174,21 @@ extract_bit_field_1 (rtx str_rtx, unsign
       && ((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];
       enum machine_mode outermode = GET_MODE (op0);
       enum machine_mode innermode = GET_MODE_INNER (outermode);
-      int icode = (int) optab_handler (vec_extract_optab, outermode);
+      enum insn_code icode = optab_handler (vec_extract_optab, outermode);
       unsigned HOST_WIDE_INT pos = bitnum / GET_MODE_BITSIZE (innermode);
-      rtx rtxpos = GEN_INT (pos);
-      rtx src = op0;
-      rtx dest = NULL, pat, seq;
-      enum machine_mode mode0 = insn_data[icode].operand[0].mode;
-      enum machine_mode mode1 = insn_data[icode].operand[1].mode;
-      enum machine_mode mode2 = insn_data[icode].operand[2].mode;
-
-      if (innermode == tmode || innermode == mode)
-	dest = target;
-
-      if (!dest)
-	dest = gen_reg_rtx (innermode);
-
-      start_sequence ();
-
-      if (! (*insn_data[icode].operand[0].predicate) (dest, mode0))
-	dest = copy_to_mode_reg (mode0, dest);
 
-      if (! (*insn_data[icode].operand[1].predicate) (src, mode1))
-	src = copy_to_mode_reg (mode1, src);
-
-      if (! (*insn_data[icode].operand[2].predicate) (rtxpos, mode2))
-	rtxpos = copy_to_mode_reg (mode1, rtxpos);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert ((*insn_data[icode].operand[0].predicate) (dest, mode0)
-		  && (*insn_data[icode].operand[1].predicate) (src, mode1)
-		  && (*insn_data[icode].operand[2].predicate) (rtxpos, mode2));
-
-      pat = GEN_FCN (icode) (dest, src, rtxpos);
-      seq = get_insns ();
-      end_sequence ();
-      if (pat)
-	{
-	  emit_insn (seq);
-	  emit_insn (pat);
-      	  if (mode0 != mode)
-	    return gen_lowpart (tmode, dest);
-	  return dest;
+      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;
 	}
     }
 
@@ -1517,17 +1427,14 @@ extract_bit_field_1 (rtx str_rtx, unsign
 	 acceptable to the format of ext(z)v.  */
       && !(GET_CODE (op0) == SUBREG && GET_MODE (op0) != ext_mode)
       && !((REG_P (op0) || GET_CODE (op0) == SUBREG)
-	   && (bitsize + bitpos > GET_MODE_BITSIZE (ext_mode)))
-      && check_predicate_volatile_ok (icode, 1, op0, GET_MODE (op0)))
+	   && (bitsize + bitpos > GET_MODE_BITSIZE (ext_mode))))
     {
+      struct expand_operand ops[4];
       unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
-      rtx bitsize_rtx, bitpos_rtx;
-      rtx last = get_last_insn ();
       rtx xop0 = op0;
       rtx xtarget = target;
       rtx xspec_target = target;
       rtx xspec_target_subreg = 0;
-      rtx pat;
 
       /* If op0 is a register, we need it in EXT_MODE to make it
 	 acceptable to the format of ext(z)v.  */
@@ -1570,27 +1477,20 @@ extract_bit_field_1 (rtx str_rtx, unsign
 	    xtarget = gen_reg_rtx (ext_mode);
 	}
 
-      /* If this machine's ext(z)v insists on a register target,
-	 make sure we have one.  */
-      if (!insn_data[(int) icode].operand[0].predicate (xtarget, ext_mode))
-	xtarget = gen_reg_rtx (ext_mode);
-
-      bitsize_rtx = GEN_INT (bitsize);
-      bitpos_rtx = GEN_INT (xbitpos);
-
-      pat = (unsignedp
-	     ? gen_extzv (xtarget, xop0, bitsize_rtx, bitpos_rtx)
-	     : gen_extv (xtarget, xop0, bitsize_rtx, bitpos_rtx));
-      if (pat)
+      create_output_operand (&ops[0], xtarget, ext_mode);
+      create_fixed_operand (&ops[1], xop0);
+      create_integer_operand (&ops[2], bitsize);
+      create_integer_operand (&ops[3], xbitpos);
+      if (maybe_expand_insn (unsignedp ? CODE_FOR_extzv : CODE_FOR_extv,
+			     4, ops))
 	{
-	  emit_insn (pat);
+	  xtarget = ops[0].value;
 	  if (xtarget == xspec_target)
 	    return xtarget;
-	  if (xtarget == xspec_target_subreg)
+	  if (ops[0].value == xspec_target_subreg)
 	    return xspec_target;
 	  return convert_extracted_bit_field (xtarget, mode, tmode, unsignedp);
 	}
-      delete_insns_since (last);
     }
 
   /* If OP0 is a memory, try copying it to a register and seeing if a
@@ -5101,19 +5001,15 @@ emit_cstore (rtx target, enum insn_code
 	     int unsignedp, rtx x, rtx y, int normalizep,
 	     enum machine_mode target_mode)
 {
-  rtx op0, last, comparison, subtarget, pattern;
+  struct expand_operand ops[4];
+  rtx op0, last, comparison, subtarget;
   enum machine_mode result_mode = insn_data[(int) icode].operand[0].mode;
 
   last = get_last_insn ();
   x = prepare_operand (icode, x, 2, mode, compare_mode, unsignedp);
   y = prepare_operand (icode, y, 3, mode, compare_mode, unsignedp);
   comparison = gen_rtx_fmt_ee (code, result_mode, x, y);
-  if (!x || !y
-      || !insn_data[icode].operand[2].predicate
-	  (x, insn_data[icode].operand[2].mode)
-      || !insn_data[icode].operand[3].predicate
-	  (y, insn_data[icode].operand[3].mode)
-      || !insn_data[icode].operand[1].predicate (comparison, VOIDmode))
+  if (!x || !y)
     {
       delete_insns_since (last);
       return NULL_RTX;
@@ -5124,16 +5020,16 @@ emit_cstore (rtx target, enum insn_code
   if (!target)
     target = gen_reg_rtx (target_mode);
 
-  if (optimize
-      || !(insn_data[(int) icode].operand[0].predicate (target, result_mode)))
-    subtarget = gen_reg_rtx (result_mode);
-  else
-    subtarget = target;
-
-  pattern = GEN_FCN (icode) (subtarget, comparison, x, y);
-  if (!pattern)
-    return NULL_RTX;
-  emit_insn (pattern);
+  create_output_operand (&ops[0], optimize ? NULL_RTX : target, result_mode);
+  create_fixed_operand (&ops[1], comparison);
+  create_fixed_operand (&ops[2], x);
+  create_fixed_operand (&ops[3], y);
+  if (!maybe_expand_insn (icode, 4, ops))
+    {
+      delete_insns_since (last);
+      return NULL_RTX;
+    }
+  subtarget = ops[0].value;
 
   /* If we are converting to a wider mode, first convert to
      TARGET_MODE, then normalize.  This produces better combining
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/expr.c	2011-03-19 17:11:42.000000000 +0000
@@ -286,7 +286,7 @@ init_expr_target (void)
 
 	  PUT_MODE (mem, srcmode);
 
-	  if ((*insn_data[ic].operand[1].predicate) (mem, srcmode))
+	  if (insn_operand_matches (ic, 1, mem))
 	    float_extend_from_mem[mode][srcmode] = true;
 	}
     }
@@ -1258,7 +1258,6 @@ block_move_libcall_safe_for_call_parm (v
 emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align,
 			    unsigned int expected_align, HOST_WIDE_INT expected_size)
 {
-  rtx opalign = GEN_INT (align / BITS_PER_UNIT);
   int save_volatile_ok = volatile_ok;
   enum machine_mode mode;
 
@@ -1276,7 +1275,6 @@ emit_block_move_via_movmem (rtx x, rtx y
        mode = GET_MODE_WIDER_MODE (mode))
     {
       enum insn_code code = direct_optab_handler (movmem_optab, mode);
-      insn_operand_predicate_fn pred;
 
       if (code != CODE_FOR_nothing
 	  /* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT
@@ -1286,43 +1284,32 @@ emit_block_move_via_movmem (rtx x, rtx y
 	  && ((CONST_INT_P (size)
 	       && ((unsigned HOST_WIDE_INT) INTVAL (size)
 		   <= (GET_MODE_MASK (mode) >> 1)))
-	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
-	  && ((pred = insn_data[(int) code].operand[0].predicate) == 0
-	      || (*pred) (x, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[1].predicate) == 0
-	      || (*pred) (y, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[3].predicate) == 0
-	      || (*pred) (opalign, VOIDmode)))
-	{
-	  rtx op2;
-	  rtx last = get_last_insn ();
-	  rtx pat;
-
-	  op2 = convert_to_mode (mode, size, 1);
-	  pred = insn_data[(int) code].operand[2].predicate;
-	  if (pred != 0 && ! (*pred) (op2, mode))
-	    op2 = copy_to_mode_reg (mode, op2);
+	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD))
+	{
+	  struct expand_operand ops[6];
+	  unsigned int nops;
 
 	  /* ??? When called via emit_block_move_for_call, it'd be
 	     nice if there were some way to inform the backend, so
 	     that it doesn't fail the expansion because it thinks
 	     emitting the libcall would be more efficient.  */
-
-	  if (insn_data[(int) code].n_operands == 4)
-	    pat = GEN_FCN ((int) code) (x, y, op2, opalign);
-	  else
-	    pat = GEN_FCN ((int) code) (x, y, op2, opalign,
-					GEN_INT (expected_align
-						 / BITS_PER_UNIT),
-					GEN_INT (expected_size));
-	  if (pat)
+	  nops = insn_data[(int) code].n_operands;
+	  create_fixed_operand (&ops[0], x);
+	  create_fixed_operand (&ops[1], y);
+	  /* The check above guarantees that this size conversion is valid.  */
+	  create_convert_operand_to (&ops[2], size, mode, true);
+	  create_integer_operand (&ops[3], align / BITS_PER_UNIT);
+	  if (nops != 4)
+	    {
+	      create_integer_operand (&ops[4], expected_align / BITS_PER_UNIT);
+	      create_integer_operand (&ops[5], expected_size);
+	      nops = 6;
+	    }
+	  if (maybe_expand_insn (code, nops, ops))
 	    {
-	      emit_insn (pat);
 	      volatile_ok = save_volatile_ok;
 	      return true;
 	    }
-	  else
-	    delete_insns_since (last);
 	}
     }
 
@@ -2705,7 +2692,6 @@ set_storage_via_setmem (rtx object, rtx
      including more than one in the machine description unless
      the more limited one has some advantage.  */
 
-  rtx opalign = GEN_INT (align / BITS_PER_UNIT);
   enum machine_mode mode;
 
   if (expected_align < align)
@@ -2715,7 +2701,6 @@ set_storage_via_setmem (rtx object, rtx
        mode = GET_MODE_WIDER_MODE (mode))
     {
       enum insn_code code = direct_optab_handler (setmem_optab, mode);
-      insn_operand_predicate_fn pred;
 
       if (code != CODE_FOR_nothing
 	  /* We don't need MODE to be narrower than
@@ -2725,46 +2710,25 @@ set_storage_via_setmem (rtx object, rtx
 	  && ((CONST_INT_P (size)
 	       && ((unsigned HOST_WIDE_INT) INTVAL (size)
 		   <= (GET_MODE_MASK (mode) >> 1)))
-	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
-	  && ((pred = insn_data[(int) code].operand[0].predicate) == 0
-	      || (*pred) (object, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[3].predicate) == 0
-	      || (*pred) (opalign, VOIDmode)))
-	{
-	  rtx opsize, opchar;
-	  enum machine_mode char_mode;
-	  rtx last = get_last_insn ();
-	  rtx pat;
-
-	  opsize = convert_to_mode (mode, size, 1);
-	  pred = insn_data[(int) code].operand[1].predicate;
-	  if (pred != 0 && ! (*pred) (opsize, mode))
-	    opsize = copy_to_mode_reg (mode, opsize);
-
-	  opchar = val;
-	  char_mode = insn_data[(int) code].operand[2].mode;
-	  if (char_mode != VOIDmode)
-	    {
-	      opchar = convert_to_mode (char_mode, opchar, 1);
-	      pred = insn_data[(int) code].operand[2].predicate;
-	      if (pred != 0 && ! (*pred) (opchar, char_mode))
-		opchar = copy_to_mode_reg (char_mode, opchar);
-	    }
+	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD))
+	{
+	  struct expand_operand ops[6];
+	  unsigned int nops;
 
-	  if (insn_data[(int) code].n_operands == 4)
-	    pat = GEN_FCN ((int) code) (object, opsize, opchar, opalign);
-	  else
-	    pat = GEN_FCN ((int) code) (object, opsize, opchar, opalign,
-					GEN_INT (expected_align
-						 / BITS_PER_UNIT),
-					GEN_INT (expected_size));
-	  if (pat)
-	    {
-	      emit_insn (pat);
-	      return true;
+	  nops = insn_data[(int) code].n_operands;
+	  create_fixed_operand (&ops[0], object);
+	  /* The check above guarantees that this size conversion is valid.  */
+	  create_convert_operand_to (&ops[1], size, mode, true);
+	  create_convert_operand_from (&ops[2], val, byte_mode, true);
+	  create_integer_operand (&ops[3], align / BITS_PER_UNIT);
+	  if (nops != 4)
+	    {
+	      create_integer_operand (&ops[4], expected_align / BITS_PER_UNIT);
+	      create_integer_operand (&ops[5], expected_size);
+	      nops = 6;
 	    }
-	  else
-	    delete_insns_since (last);
+	  if (maybe_expand_insn (code, nops, ops))
+	    return true;
 	}
     }
 
@@ -3446,7 +3410,7 @@ compress_float_constant (rtx x, rtx y)
 	{
 	  /* Skip if the target needs extra instructions to perform
 	     the extension.  */
-	  if (! (*insn_data[ic].operand[1].predicate) (trunc_y, srcmode))
+	  if (!insn_operand_matches (ic, 1, trunc_y))
 	    continue;
 	  /* This is valid, but may not be cheaper than the original. */
 	  newcost = rtx_cost (gen_rtx_FLOAT_EXTEND (dstmode, trunc_y), SET, speed);
@@ -3547,7 +3511,6 @@ emit_single_push_insn (enum machine_mode
   unsigned rounded_size = PUSH_ROUNDING (GET_MODE_SIZE (mode));
   rtx dest;
   enum insn_code icode;
-  insn_operand_predicate_fn pred;
 
   stack_pointer_delta += PUSH_ROUNDING (GET_MODE_SIZE (mode));
   /* If there is push pattern, use it.  Otherwise try old way of throwing
@@ -3555,11 +3518,11 @@ emit_single_push_insn (enum machine_mode
   icode = optab_handler (push_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (((pred = insn_data[(int) icode].operand[0].predicate)
-	   && !((*pred) (x, mode))))
-	x = force_reg (mode, x);
-      emit_insn (GEN_FCN (icode) (x));
-      return;
+      struct expand_operand ops[1];
+
+      create_input_operand (&ops[0], x, mode);
+      if (maybe_expand_insn (icode, 1, ops))
+	return;
     }
   if (GET_MODE_SIZE (mode) == rounded_size)
     dest_addr = gen_rtx_fmt_e (STACK_PUSH_CODE, Pmode, stack_pointer_rtx);
@@ -4147,7 +4110,8 @@ expand_assignment (tree to, tree from, b
   rtx to_rtx = 0;
   rtx result;
   enum machine_mode mode;
-  int align, icode;
+  int align;
+  enum insn_code icode;
 
   /* Don't crash if the lhs of the assignment was erroneous.  */
   if (TREE_CODE (to) == ERROR_MARK)
@@ -4170,8 +4134,9 @@ expand_assignment (tree to, tree from, b
       && ((icode = optab_handler (movmisalign_optab, mode))
 	  != CODE_FOR_nothing))
     {
-      enum machine_mode address_mode, op_mode1;
-      rtx insn, reg, op0, mem;
+      struct expand_operand ops[2];
+      enum machine_mode address_mode;
+      rtx reg, op0, mem;
 
       reg = expand_expr (from, NULL_RTX, VOIDmode, EXPAND_NORMAL);
       reg = force_not_mem (reg);
@@ -4212,16 +4177,11 @@ expand_assignment (tree to, tree from, b
       if (TREE_THIS_VOLATILE (to))
 	MEM_VOLATILE_P (mem) = 1;
 
-      op_mode1 = insn_data[icode].operand[1].mode;
-      if (! (*insn_data[icode].operand[1].predicate) (reg, op_mode1)
-	  && op_mode1 != VOIDmode)
-	reg = copy_to_mode_reg (op_mode1, reg);
-
-      insn = GEN_FCN (icode) (mem, reg);
+      create_fixed_operand (&ops[0], mem);
+      create_input_operand (&ops[1], reg, mode);
       /* The movmisalign<mode> pattern cannot fail, else the assignment would
          silently be omitted.  */
-      gcc_assert (insn != NULL_RTX);
-      emit_insn (insn);
+      expand_insn (icode, 2, ops);
       return;
     }
 
@@ -4483,31 +4443,16 @@ expand_assignment (tree to, tree from, b
 bool
 emit_storent_insn (rtx to, rtx from)
 {
-  enum machine_mode mode = GET_MODE (to), imode;
+  struct expand_operand ops[2];
+  enum machine_mode mode = GET_MODE (to);
   enum insn_code code = optab_handler (storent_optab, mode);
-  rtx pattern;
 
   if (code == CODE_FOR_nothing)
     return false;
 
-  imode = insn_data[code].operand[0].mode;
-  if (!insn_data[code].operand[0].predicate (to, imode))
-    return false;
-
-  imode = insn_data[code].operand[1].mode;
-  if (!insn_data[code].operand[1].predicate (from, imode))
-    {
-      from = copy_to_mode_reg (imode, from);
-      if (!insn_data[code].operand[1].predicate (from, imode))
-	return false;
-    }
-
-  pattern = GEN_FCN (code) (to, from);
-  if (pattern == NULL_RTX)
-    return false;
-
-  emit_insn (pattern);
-  return true;
+  create_fixed_operand (&ops[0], to);
+  create_input_operand (&ops[1], from, mode);
+  return maybe_expand_insn (code, 2, ops);
 }
 
 /* Generate code for computing expression EXP,
@@ -10121,10 +10066,10 @@ try_casesi (tree index_type, tree index_
 	    rtx table_label ATTRIBUTE_UNUSED, rtx default_label,
 	    rtx fallback_label ATTRIBUTE_UNUSED)
 {
+  struct expand_operand ops[5];
   enum machine_mode index_mode = SImode;
   int index_bits = GET_MODE_BITSIZE (index_mode);
   rtx op1, op2, index;
-  enum machine_mode op_mode;
 
   if (! HAVE_casesi)
     return 0;
@@ -10159,32 +10104,17 @@ try_casesi (tree index_type, tree index_
 
   do_pending_stack_adjust ();
 
-  op_mode = insn_data[(int) CODE_FOR_casesi].operand[0].mode;
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[0].predicate)
-      (index, op_mode))
-    index = copy_to_mode_reg (op_mode, index);
-
   op1 = expand_normal (minval);
-
-  op_mode = insn_data[(int) CODE_FOR_casesi].operand[1].mode;
-  op1 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (minval)),
-		       op1, TYPE_UNSIGNED (TREE_TYPE (minval)));
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[1].predicate)
-      (op1, op_mode))
-    op1 = copy_to_mode_reg (op_mode, op1);
-
   op2 = expand_normal (range);
 
-  op_mode = insn_data[(int) CODE_FOR_casesi].operand[2].mode;
-  op2 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (range)),
-		       op2, TYPE_UNSIGNED (TREE_TYPE (range)));
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[2].predicate)
-      (op2, op_mode))
-    op2 = copy_to_mode_reg (op_mode, op2);
-
-  emit_jump_insn (gen_casesi (index, op1, op2,
-			      table_label, !default_label
-					   ? fallback_label : default_label));
+  create_input_operand (&ops[0], index, index_mode);
+  create_convert_operand_from_type (&ops[1], op1, TREE_TYPE (minval));
+  create_convert_operand_from_type (&ops[2], op2, TREE_TYPE (range));
+  create_fixed_operand (&ops[3], table_label);
+  create_fixed_operand (&ops[4], (default_label
+				  ? default_label
+				  : fallback_label));
+  expand_jump_insn (CODE_FOR_casesi, 5, ops);
   return 1;
 }
 
Index: gcc/function.c
===================================================================
--- gcc/function.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/function.c	2011-03-19 17:11:42.000000000 +0000
@@ -1493,16 +1493,7 @@ instantiate_virtual_regs_in_rtx (rtx *lo
 static int
 safe_insn_predicate (int code, int operand, rtx x)
 {
-  const struct insn_operand_data *op_data;
-
-  if (code < 0)
-    return true;
-
-  op_data = &insn_data[code].operand[operand];
-  if (op_data->predicate == NULL)
-    return true;
-
-  return op_data->predicate (x, op_data->mode);
+  return code < 0 || insn_operand_matches ((enum insn_code) code, operand, x);
 }
 
 /* A subroutine of instantiate_virtual_regs.  Instantiate any virtual
@@ -3013,8 +3004,8 @@ assign_parm_setup_reg (struct assign_par
       op0 = parmreg;
       op1 = validated_mem;
       if (icode != CODE_FOR_nothing
-	  && insn_data[icode].operand[0].predicate (op0, promoted_nominal_mode)
-	  && insn_data[icode].operand[1].predicate (op1, data->passed_mode))
+	  && insn_operand_matches (icode, 0, op0)
+	  && insn_operand_matches (icode, 1, op1))
 	{
 	  enum rtx_code code = unsignedp ? ZERO_EXTEND : SIGN_EXTEND;
 	  rtx insn, insns;
Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/optabs.c	2011-03-19 17:11:42.000000000 +0000
@@ -501,15 +501,13 @@ optab_for_tree_code (enum tree_code code
 expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
 			   rtx target, int unsignedp)
 {
+  struct expand_operand eops[4];
   tree oprnd0, oprnd1, oprnd2;
   enum machine_mode wmode = VOIDmode, tmode0, tmode1 = VOIDmode;
   optab widen_pattern_optab;
-  int icode;
-  enum machine_mode xmode0, xmode1 = VOIDmode, wxmode = VOIDmode;
-  rtx temp;
-  rtx pat;
-  rtx xop0, xop1, wxop;
+  enum insn_code icode;
   int nops = TREE_CODE_LENGTH (ops->code);
+  int op;
 
   oprnd0 = ops->op0;
   tmode0 = TYPE_MODE (TREE_TYPE (oprnd0));
@@ -517,117 +515,38 @@ expand_widen_pattern_expr (sepops ops, r
     optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
   if (ops->code == WIDEN_MULT_PLUS_EXPR
       || ops->code == WIDEN_MULT_MINUS_EXPR)
-    icode = (int) optab_handler (widen_pattern_optab,
-				 TYPE_MODE (TREE_TYPE (ops->op2)));
+    icode = optab_handler (widen_pattern_optab,
+			   TYPE_MODE (TREE_TYPE (ops->op2)));
   else
-    icode = (int) optab_handler (widen_pattern_optab, tmode0);
+    icode = optab_handler (widen_pattern_optab, tmode0);
   gcc_assert (icode != CODE_FOR_nothing);
-  xmode0 = insn_data[icode].operand[1].mode;
 
   if (nops >= 2)
     {
       oprnd1 = ops->op1;
       tmode1 = TYPE_MODE (TREE_TYPE (oprnd1));
-      xmode1 = insn_data[icode].operand[2].mode;
     }
 
   /* The last operand is of a wider mode than the rest of the operands.  */
   if (nops == 2)
-    {
-      wmode = tmode1;
-      wxmode = xmode1;
-    }
+    wmode = tmode1;
   else if (nops == 3)
     {
       gcc_assert (tmode1 == tmode0);
       gcc_assert (op1);
       oprnd2 = ops->op2;
       wmode = TYPE_MODE (TREE_TYPE (oprnd2));
-      wxmode = insn_data[icode].operand[3].mode;
     }
 
-  if (!wide_op)
-    wmode = wxmode = insn_data[icode].operand[0].mode;
-
-  if (!target
-      || ! (*insn_data[icode].operand[0].predicate) (target, wmode))
-    temp = gen_reg_rtx (wmode);
-  else
-    temp = target;
-
-  xop0 = op0;
-  xop1 = op1;
-  wxop = wide_op;
-
-  /* In case the insn wants input operands in modes different from
-     those of the actual operands, convert the operands.  It would
-     seem that we don't need to convert CONST_INTs, but we do, so
-     that they're properly zero-extended, sign-extended or truncated
-     for their mode.  */
-
-  if (GET_MODE (op0) != xmode0 && xmode0 != VOIDmode)
-    xop0 = convert_modes (xmode0,
-                          GET_MODE (op0) != VOIDmode
-                          ? GET_MODE (op0)
-                          : tmode0,
-                          xop0, unsignedp);
-
+  op = 0;
+  create_output_operand (&eops[op++], target, TYPE_MODE (ops->type));
+  create_convert_operand_from (&eops[op++], op0, tmode0, unsignedp);
   if (op1)
-    if (GET_MODE (op1) != xmode1 && xmode1 != VOIDmode)
-      xop1 = convert_modes (xmode1,
-                            GET_MODE (op1) != VOIDmode
-                            ? GET_MODE (op1)
-                            : tmode1,
-                            xop1, unsignedp);
-
+    create_convert_operand_from (&eops[op++], op1, tmode1, unsignedp);
   if (wide_op)
-    if (GET_MODE (wide_op) != wxmode && wxmode != VOIDmode)
-      wxop = convert_modes (wxmode,
-                            GET_MODE (wide_op) != VOIDmode
-                            ? GET_MODE (wide_op)
-                            : wmode,
-                            wxop, unsignedp);
-
-  /* Now, if insn's predicates don't allow our operands, put them into
-     pseudo regs.  */
-
-  if (! (*insn_data[icode].operand[1].predicate) (xop0, xmode0)
-      && xmode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (xmode0, xop0);
-
-  if (op1)
-    {
-      if (! (*insn_data[icode].operand[2].predicate) (xop1, xmode1)
-          && xmode1 != VOIDmode)
-        xop1 = copy_to_mode_reg (xmode1, xop1);
-
-      if (wide_op)
-        {
-          if (! (*insn_data[icode].operand[3].predicate) (wxop, wxmode)
-              && wxmode != VOIDmode)
-            wxop = copy_to_mode_reg (wxmode, wxop);
-
-          pat = GEN_FCN (icode) (temp, xop0, xop1, wxop);
-        }
-      else
-        pat = GEN_FCN (icode) (temp, xop0, xop1);
-    }
-  else
-    {
-      if (wide_op)
-        {
-          if (! (*insn_data[icode].operand[2].predicate) (wxop, wxmode)
-              && wxmode != VOIDmode)
-            wxop = copy_to_mode_reg (wxmode, wxop);
-
-          pat = GEN_FCN (icode) (temp, xop0, wxop);
-        }
-      else
-        pat = GEN_FCN (icode) (temp, xop0);
-    }
-
-  emit_insn (pat);
-  return temp;
+    create_convert_operand_from (&eops[op++], wide_op, wmode, unsignedp);
+  expand_insn (icode, op, eops);
+  return eops[0].value;
 }
 
 /* Generate code to perform an operation specified by TERNARY_OPTAB
@@ -645,67 +564,17 @@ expand_widen_pattern_expr (sepops ops, r
 expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0,
 		   rtx op1, rtx op2, rtx target, int unsignedp)
 {
-  int icode = (int) optab_handler (ternary_optab, mode);
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
-  enum machine_mode mode2 = insn_data[icode].operand[3].mode;
-  rtx temp;
-  rtx pat;
-  rtx xop0 = op0, xop1 = op1, xop2 = op2;
+  struct expand_operand ops[4];
+  enum insn_code icode = optab_handler (ternary_optab, mode);
 
   gcc_assert (optab_handler (ternary_optab, mode) != CODE_FOR_nothing);
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    temp = gen_reg_rtx (mode);
-  else
-    temp = target;
-
-  /* In case the insn wants input operands in modes different from
-     those of the actual operands, convert the operands.  It would
-     seem that we don't need to convert CONST_INTs, but we do, so
-     that they're properly zero-extended, sign-extended or truncated
-     for their mode.  */
-
-  if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
-    xop0 = convert_modes (mode0,
-                          GET_MODE (op0) != VOIDmode
-                          ? GET_MODE (op0)
-                          : mode,
-                          xop0, unsignedp);
-
-  if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
-    xop1 = convert_modes (mode1,
-                          GET_MODE (op1) != VOIDmode
-                          ? GET_MODE (op1)
-                          : mode,
-                          xop1, unsignedp);
-
-  if (GET_MODE (op2) != mode2 && mode2 != VOIDmode)
-    xop2 = convert_modes (mode2,
-                          GET_MODE (op2) != VOIDmode
-                          ? GET_MODE (op2)
-                          : mode,
-                          xop2, unsignedp);
-
-  /* Now, if insn's predicates don't allow our operands, put them into
-     pseudo regs.  */
-
-  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
-      && mode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (mode0, xop0);
-
-  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
-      && mode1 != VOIDmode)
-    xop1 = copy_to_mode_reg (mode1, xop1);
-
-  if (!insn_data[icode].operand[3].predicate (xop2, mode2)
-      && mode2 != VOIDmode)
-    xop2 = copy_to_mode_reg (mode2, xop2);
-
-  pat = GEN_FCN (icode) (temp, xop0, xop1, xop2);
-
-  emit_insn (pat);
-  return temp;
+  create_output_operand (&ops[0], target, mode);
+  create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+  create_convert_operand_from (&ops[2], op1, mode, unsignedp);
+  create_convert_operand_from (&ops[3], op2, mode, unsignedp);
+  expand_insn (icode, 4, ops);
+  return ops[0].value;
 }
 
 
@@ -751,15 +620,13 @@ force_expand_binop (enum machine_mode mo
 rtx
 expand_vec_shift_expr (sepops ops, rtx target)
 {
+  struct expand_operand eops[3];
   enum insn_code icode;
   rtx rtx_op1, rtx_op2;
-  enum machine_mode mode1;
-  enum machine_mode mode2;
   enum machine_mode mode = TYPE_MODE (ops->type);
   tree vec_oprnd = ops->op0;
   tree shift_oprnd = ops->op1;
   optab shift_optab;
-  rtx pat;
 
   switch (ops->code)
     {
@@ -776,29 +643,15 @@ expand_vec_shift_expr (sepops ops, rtx t
   icode = optab_handler (shift_optab, mode);
   gcc_assert (icode != CODE_FOR_nothing);
 
-  mode1 = insn_data[icode].operand[1].mode;
-  mode2 = insn_data[icode].operand[2].mode;
-
   rtx_op1 = expand_normal (vec_oprnd);
-  if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode1)
-      && mode1 != VOIDmode)
-    rtx_op1 = force_reg (mode1, rtx_op1);
-
   rtx_op2 = expand_normal (shift_oprnd);
-  if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode2)
-      && mode2 != VOIDmode)
-    rtx_op2 = force_reg (mode2, rtx_op2);
 
-  if (!target
-      || ! (*insn_data[icode].operand[0].predicate) (target, mode))
-    target = gen_reg_rtx (mode);
+  create_output_operand (&eops[0], target, mode);
+  create_input_operand (&eops[1], rtx_op1, GET_MODE (rtx_op1));
+  create_convert_operand_from_type (&eops[2], rtx_op2, TREE_TYPE (shift_oprnd));
+  expand_insn (icode, 3, eops);
 
-  /* Emit instruction */
-  pat = GEN_FCN (icode) (target, rtx_op1, rtx_op2);
-  gcc_assert (pat);
-  emit_insn (pat);
-
-  return target;
+  return eops[0].value;
 }
 
 /* This subroutine of expand_doubleword_shift handles the cases in which
@@ -1389,21 +1242,16 @@ expand_binop_directly (enum machine_mode
 		       rtx target, int unsignedp, enum optab_methods methods,
 		       rtx last)
 {
-  int icode = (int) optab_handler (binoptab, mode);
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+  enum insn_code icode = optab_handler (binoptab, mode);
+  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
   enum machine_mode tmp_mode;
+  struct expand_operand ops[3];
   bool commutative_p;
   rtx pat;
   rtx xop0 = op0, xop1 = op1;
-  rtx temp;
   rtx swap;
 
-  if (target)
-    temp = target;
-  else
-    temp = gen_reg_rtx (mode);
-
   /* If it is a commutative operator and the modes would match
      if we would swap the operands, we can save the conversions.  */
   commutative_p = commutative_optab_p (binoptab);
@@ -1421,49 +1269,9 @@ expand_binop_directly (enum machine_mode
   if (!shift_optab_p (binoptab))
     xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
 
-  /* In case the insn wants input operands in modes different from
-     those of the actual operands, convert the operands.  It would
-     seem that we don't need to convert CONST_INTs, but we do, so
-     that they're properly zero-extended, sign-extended or truncated
-     for their mode.  */
-
-  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
-    xop0 = convert_modes (mode0,
-			  GET_MODE (xop0) != VOIDmode
-			  ? GET_MODE (xop0)
-			  : mode,
-			  xop0, unsignedp);
-
-  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
-    xop1 = convert_modes (mode1,
-			  GET_MODE (xop1) != VOIDmode
-			  ? GET_MODE (xop1)
-			  : mode,
-			  xop1, unsignedp);
-
-  /* If operation is commutative,
-     try to make the first operand a register.
-     Even better, try to make it the same as the target.
-     Also try to make the last operand a constant.  */
-  if (commutative_p
-      && swap_commutative_operands_with_target (target, xop0, xop1))
-    {
-      swap = xop1;
-      xop1 = xop0;
-      xop0 = swap;
-    }
-
   /* Now, if insn's predicates don't allow our operands, put them into
      pseudo regs.  */
 
-  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
-      && mode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (mode0, xop0);
-
-  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
-      && mode1 != VOIDmode)
-    xop1 = copy_to_mode_reg (mode1, xop1);
-
   if (binoptab == vec_pack_trunc_optab
       || binoptab == vec_pack_usat_optab
       || binoptab == vec_pack_ssat_optab
@@ -1472,24 +1280,30 @@ expand_binop_directly (enum machine_mode
     {
       /* The mode of the result is different then the mode of the
 	 arguments.  */
-      tmp_mode = insn_data[icode].operand[0].mode;
+      tmp_mode = insn_data[(int) icode].operand[0].mode;
       if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode))
-	return 0;
+	{
+	  delete_insns_since (last);
+	  return NULL_RTX;
+	}
     }
   else
     tmp_mode = mode;
 
-  if (!insn_data[icode].operand[0].predicate (temp, tmp_mode))
-    temp = gen_reg_rtx (tmp_mode);
-
-  pat = GEN_FCN (icode) (temp, xop0, xop1);
+  create_output_operand (&ops[0], target, tmp_mode);
+  create_convert_operand_from (&ops[1], xop0, mode, unsignedp);
+  create_convert_operand_from (&ops[2], xop1, mode, unsignedp);
+  if (commutative_p)
+    make_operand_commutative (&ops[1], 0);
+  pat = maybe_gen_insn (icode, 3, ops);
   if (pat)
     {
       /* If PAT is composed of more than one insn, try to add an appropriate
 	 REG_EQUAL note to it.  If we can't because TEMP conflicts with an
 	 operand, call expand_binop again, this time without a target.  */
       if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-	  && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1))
+	  && ! add_equal_note (pat, ops[0].value, binoptab->code,
+			       ops[1].value, ops[2].value))
 	{
 	  delete_insns_since (last);
 	  return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
@@ -1497,7 +1311,7 @@ expand_binop_directly (enum machine_mode
 	}
 
       emit_insn (pat);
-      return temp;
+      return ops[0].value;
     }
 
   delete_insns_since (last);
@@ -2284,32 +2098,14 @@ expand_twoval_unop (optab unoptab, rtx o
 
   if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (unoptab, mode);
-      enum machine_mode mode0 = insn_data[icode].operand[2].mode;
-      rtx pat;
-      rtx xop0 = op0;
+      struct expand_operand ops[3];
+      enum insn_code icode = optab_handler (unoptab, mode);
 
-      if (GET_MODE (xop0) != VOIDmode
-	  && GET_MODE (xop0) != mode0)
-	xop0 = convert_to_mode (mode0, xop0, unsignedp);
-
-      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (!insn_data[icode].operand[2].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
-      gcc_assert (insn_data[icode].operand[1].predicate (targ1, mode));
-
-      pat = GEN_FCN (icode) (targ0, targ1, xop0);
-      if (pat)
-	{
-	  emit_insn (pat);
-	  return 1;
-	}
-      else
-	delete_insns_since (last);
+      create_fixed_operand (&ops[0], targ0);
+      create_fixed_operand (&ops[1], targ1);
+      create_convert_operand_from (&ops[2], op0, mode, unsignedp);
+      if (maybe_expand_insn (icode, 3, ops))
+	return 1;
     }
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
@@ -2376,56 +2172,23 @@ expand_twoval_binop (optab binoptab, rtx
 
   if (optab_handler (binoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (binoptab, mode);
+      struct expand_operand ops[4];
+      enum insn_code icode = optab_handler (binoptab, mode);
       enum machine_mode mode0 = insn_data[icode].operand[1].mode;
       enum machine_mode mode1 = insn_data[icode].operand[2].mode;
-      rtx pat;
       rtx xop0 = op0, xop1 = op1;
 
       /* If we are optimizing, force expensive constants into a register.  */
       xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp);
       xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
 
-      /* In case the insn wants input operands in modes different from
-	 those of the actual operands, convert the operands.  It would
-	 seem that we don't need to convert CONST_INTs, but we do, so
-	 that they're properly zero-extended, sign-extended or truncated
-	 for their mode.  */
-
-      if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
-	xop0 = convert_modes (mode0,
-			      GET_MODE (op0) != VOIDmode
-			      ? GET_MODE (op0)
-			      : mode,
-			      xop0, unsignedp);
-
-      if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
-	xop1 = convert_modes (mode1,
-			      GET_MODE (op1) != VOIDmode
-			      ? GET_MODE (op1)
-			      : mode,
-			      xop1, unsignedp);
-
-      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (!insn_data[icode].operand[1].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (!insn_data[icode].operand[2].predicate (xop1, mode1))
-	xop1 = copy_to_mode_reg (mode1, xop1);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
-      gcc_assert (insn_data[icode].operand[3].predicate (targ1, mode));
-
-      pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
-      if (pat)
-	{
-	  emit_insn (pat);
-	  return 1;
-	}
-      else
-	delete_insns_since (last);
+      create_fixed_operand (&ops[0], targ0);
+      create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+      create_convert_operand_from (&ops[2], op1, mode, unsignedp);
+      create_fixed_operand (&ops[3], targ1);
+      if (maybe_expand_insn (icode, 4, ops))
+	return 1;
+      delete_insns_since (last);
     }
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
@@ -2985,34 +2748,19 @@ expand_unop_direct (enum machine_mode mo
 {
   if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (unoptab, mode);
-      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-      rtx xop0 = op0;
+      struct expand_operand ops[2];
+      enum insn_code icode = optab_handler (unoptab, mode);
       rtx last = get_last_insn ();
-      rtx pat, temp;
-
-      if (target)
-	temp = target;
-      else
-	temp = gen_reg_rtx (mode);
-
-      if (GET_MODE (xop0) != VOIDmode
-	  && GET_MODE (xop0) != mode0)
-	xop0 = convert_to_mode (mode0, xop0, unsignedp);
-
-      /* Now, if insn doesn't accept our operand, put it into a pseudo.  */
-
-      if (!insn_data[icode].operand[1].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (!insn_data[icode].operand[0].predicate (temp, mode))
-	temp = gen_reg_rtx (mode);
+      rtx pat;
 
-      pat = GEN_FCN (icode) (temp, xop0);
+      create_output_operand (&ops[0], target, mode);
+      create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+      pat = maybe_gen_insn (icode, 2, ops);
       if (pat)
 	{
 	  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-	      && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX))
+	      && ! add_equal_note (pat, ops[0].value, unoptab->code,
+				   ops[1].value, NULL_RTX))
 	    {
 	      delete_insns_since (last);
 	      return expand_unop (mode, unoptab, op0, NULL_RTX, unsignedp);
@@ -3020,10 +2768,8 @@ expand_unop_direct (enum machine_mode mo
 
 	  emit_insn (pat);
 
-	  return temp;
+	  return ops[0].value;
 	}
-      else
-	delete_insns_since (last);
     }
   return 0;
 }
@@ -3499,7 +3245,7 @@ expand_copysign_absneg (enum machine_mod
 		        int bitpos, bool op0_is_abs)
 {
   enum machine_mode imode;
-  int icode;
+  enum insn_code icode;
   rtx sign, label;
 
   if (target == op1)
@@ -3507,10 +3253,10 @@ expand_copysign_absneg (enum machine_mod
 
   /* Check if the back end provides an insn that handles signbit for the
      argument's mode. */
-  icode = (int) optab_handler (signbit_optab, mode);
+  icode = optab_handler (signbit_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      imode = insn_data[icode].operand[0].mode;
+      imode = insn_data[(int) icode].operand[0].mode;
       sign = gen_reg_rtx (imode);
       emit_unop_insn (icode, sign, op1, UNKNOWN);
     }
@@ -3731,37 +3477,25 @@ expand_copysign (rtx op0, rtx op1, rtx t
    Return false if expansion failed.  */
 
 bool
-maybe_emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+maybe_emit_unop_insn (enum insn_code icode, rtx target, rtx op0,
+		      enum rtx_code code)
 {
-  rtx temp;
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  struct expand_operand ops[2];
   rtx pat;
-  rtx last = get_last_insn ();
-
-  temp = target;
-
-  /* Now, if insn does not accept our operands, put them into pseudos.  */
 
-  if (!insn_data[icode].operand[1].predicate (op0, mode0))
-    op0 = copy_to_mode_reg (mode0, op0);
-
-  if (!insn_data[icode].operand[0].predicate (temp, GET_MODE (temp)))
-    temp = gen_reg_rtx (GET_MODE (temp));
-
-  pat = GEN_FCN (icode) (temp, op0);
+  create_output_operand (&ops[0], target, GET_MODE (target));
+  create_input_operand (&ops[1], op0, GET_MODE (op0));
+  pat = maybe_gen_insn (icode, 2, ops);
   if (!pat)
-    {
-      delete_insns_since (last);
-      return false;
-    }
+    return false;
 
   if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN)
-    add_equal_note (pat, temp, code, op0, NULL_RTX);
+    add_equal_note (pat, ops[0].value, code, ops[1].value, NULL_RTX);
 
   emit_insn (pat);
 
-  if (temp != target)
-    emit_move_insn (target, temp);
+  if (ops[0].value != target)
+    emit_move_insn (target, ops[0].value);
   return true;
 }
 /* Generate an instruction whose insn-code is INSN_CODE,
@@ -3771,7 +3505,7 @@ maybe_emit_unop_insn (int icode, rtx tar
    the value that is stored into TARGET.  */
 
 void
-emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+emit_unop_insn (enum insn_code icode, rtx target, rtx op0, enum rtx_code code)
 {
   bool ok = maybe_emit_unop_insn (icode, target, op0, code);
   gcc_assert (ok);
@@ -3943,15 +3677,15 @@ can_compare_p (enum rtx_code code, enum
   test = gen_rtx_fmt_ee (code, mode, const0_rtx, const0_rtx);
   do
     {
-      int icode;
+      enum insn_code icode;
 
       if (purpose == ccp_jump
           && (icode = optab_handler (cbranch_optab, mode)) != CODE_FOR_nothing
-          && insn_data[icode].operand[0].predicate (test, mode))
+          && insn_operand_matches (icode, 0, test))
         return 1;
       if (purpose == ccp_store_flag
           && (icode = optab_handler (cstore_optab, mode)) != CODE_FOR_nothing
-          && insn_data[icode].operand[1].predicate (test, mode))
+          && insn_operand_matches (icode, 1, test))
         return 1;
       if (purpose == ccp_cmov
 	  && optab_handler (cmov_optab, mode) != CODE_FOR_nothing)
@@ -4112,16 +3846,14 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx
       enum insn_code icode;
       icode = optab_handler (cbranch_optab, cmp_mode);
       if (icode != CODE_FOR_nothing
-	  && insn_data[icode].operand[0].predicate (test, VOIDmode))
+	  && insn_operand_matches (icode, 0, test))
 	{
 	  rtx last = get_last_insn ();
 	  rtx op0 = prepare_operand (icode, x, 1, mode, cmp_mode, unsignedp);
 	  rtx op1 = prepare_operand (icode, y, 2, mode, cmp_mode, unsignedp);
 	  if (op0 && op1
-	      && insn_data[icode].operand[1].predicate
-		 (op0, insn_data[icode].operand[1].mode)
-	      && insn_data[icode].operand[2].predicate
-		 (op1, insn_data[icode].operand[2].mode))
+	      && insn_operand_matches (icode, 1, op0)
+	      && insn_operand_matches (icode, 2, op1))
 	    {
 	      XEXP (test, 0) = op0;
 	      XEXP (test, 1) = op1;
@@ -4200,18 +3932,17 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx
    that it is accepted by the operand predicate.  Return the new value.  */
 
 rtx
-prepare_operand (int icode, rtx x, int opnum, enum machine_mode mode,
+prepare_operand (enum insn_code icode, rtx x, int opnum, enum machine_mode mode,
 		 enum machine_mode wider_mode, int unsignedp)
 {
   if (mode != wider_mode)
     x = convert_modes (wider_mode, mode, x, unsignedp);
 
-  if (!insn_data[icode].operand[opnum].predicate
-      (x, insn_data[icode].operand[opnum].mode))
+  if (!insn_operand_matches (icode, opnum, x))
     {
       if (reload_completed)
 	return NULL_RTX;
-      x = copy_to_mode_reg (insn_data[icode].operand[opnum].mode, x);
+      x = copy_to_mode_reg (insn_data[(int) icode].operand[opnum].mode, x);
     }
 
   return x;
@@ -4232,7 +3963,7 @@ emit_cmp_and_jump_insn_1 (rtx test, enum
   icode = optab_handler (cbranch_optab, optab_mode);
 
   gcc_assert (icode != CODE_FOR_nothing);
-  gcc_assert (insn_data[icode].operand[0].predicate (test, VOIDmode));
+  gcc_assert (insn_operand_matches (icode, 0, test));
   emit_jump_insn (GEN_FCN (icode) (test, XEXP (test, 0), XEXP (test, 1), label));
 }
 
@@ -4421,11 +4152,10 @@ prepare_float_lib_cmp (rtx x, rtx y, enu
 void
 emit_indirect_jump (rtx loc)
 {
-  if (!insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate
-      (loc, Pmode))
-    loc = copy_to_mode_reg (Pmode, loc);
+  struct expand_operand ops[1];
 
-  emit_jump_insn (gen_indirect_jump (loc));
+  create_address_operand (&ops[0], loc);
+  expand_jump_insn (CODE_FOR_indirect_jump, 1, ops);
   emit_barrier ();
 }
 \f
@@ -4450,7 +4180,7 @@ emit_conditional_move (rtx target, enum
 		       enum machine_mode cmode, rtx op2, rtx op3,
 		       enum machine_mode mode, int unsignedp)
 {
-  rtx tem, subtarget, comparison, insn;
+  rtx tem, comparison, last;
   enum insn_code icode;
   enum rtx_code reversed;
 
@@ -4497,24 +4227,6 @@ emit_conditional_move (rtx target, enum
   if (!target)
     target = gen_reg_rtx (mode);
 
-  subtarget = target;
-
-  /* If the insn doesn't accept these operands, put them in pseudos.  */
-
-  if (!insn_data[icode].operand[0].predicate
-      (subtarget, insn_data[icode].operand[0].mode))
-    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
-
-  if (!insn_data[icode].operand[2].predicate
-      (op2, insn_data[icode].operand[2].mode))
-    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
-
-  if (!insn_data[icode].operand[3].predicate
-      (op3, insn_data[icode].operand[3].mode))
-    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
-
-  /* Everything should now be in the suitable form.  */
-
   code = unsignedp ? unsigned_condition (code) : code;
   comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1);
 
@@ -4525,30 +4237,27 @@ emit_conditional_move (rtx target, enum
     return NULL_RTX;
 
   do_pending_stack_adjust ();
-  start_sequence ();
+  last = get_last_insn ();
   prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
 		    GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN,
 		    &comparison, &cmode);
-  if (!comparison)
-    insn = NULL_RTX;
-  else
-    insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
-
-  /* If that failed, then give up.  */
-  if (insn == 0)
+  if (comparison)
     {
-      end_sequence ();
-      return 0;
-    }
+      struct expand_operand ops[4];
 
-  emit_insn (insn);
-  insn = get_insns ();
-  end_sequence ();
-  emit_insn (insn);
-  if (subtarget != target)
-    convert_move (target, subtarget, 0);
-
-  return target;
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], comparison);
+      create_input_operand (&ops[2], op2, mode);
+      create_input_operand (&ops[3], op3, mode);
+      if (maybe_expand_insn (icode, 4, ops))
+	{
+	  if (ops[0].value != target)
+	    convert_move (target, ops[0].value, false);
+	  return target;
+	}
+    }
+  delete_insns_since (last);
+  return NULL_RTX;
 }
 
 /* Return nonzero if a conditional move of mode MODE is supported.
@@ -4589,7 +4298,7 @@ emit_conditional_add (rtx target, enum r
 		      enum machine_mode cmode, rtx op2, rtx op3,
 		      enum machine_mode mode, int unsignedp)
 {
-  rtx tem, subtarget, comparison, insn;
+  rtx tem, comparison, last;
   enum insn_code icode;
   enum rtx_code reversed;
 
@@ -4636,24 +4345,6 @@ emit_conditional_add (rtx target, enum r
   if (!target)
     target = gen_reg_rtx (mode);
 
-  /* If the insn doesn't accept these operands, put them in pseudos.  */
-
-  if (!insn_data[icode].operand[0].predicate
-      (target, insn_data[icode].operand[0].mode))
-    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
-  else
-    subtarget = target;
-
-  if (!insn_data[icode].operand[2].predicate
-      (op2, insn_data[icode].operand[2].mode))
-    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
-
-  if (!insn_data[icode].operand[3].predicate
-      (op3, insn_data[icode].operand[3].mode))
-    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
-
-  /* Everything should now be in the suitable form.  */
-
   code = unsignedp ? unsigned_condition (code) : code;
   comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1);
 
@@ -4664,30 +4355,27 @@ emit_conditional_add (rtx target, enum r
     return NULL_RTX;
 
   do_pending_stack_adjust ();
-  start_sequence ();
+  last = get_last_insn ();
   prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
                     GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN,
                     &comparison, &cmode);
-  if (!comparison)
-    insn = NULL_RTX;
-  else
-    insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
-
-  /* If that failed, then give up.  */
-  if (insn == 0)
+  if (comparison)
     {
-      end_sequence ();
-      return 0;
-    }
-
-  emit_insn (insn);
-  insn = get_insns ();
-  end_sequence ();
-  emit_insn (insn);
-  if (subtarget != target)
-    convert_move (target, subtarget, 0);
+      struct expand_operand ops[4];
 
-  return target;
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], comparison);
+      create_input_operand (&ops[2], op2, mode);
+      create_input_operand (&ops[3], op3, mode);
+      if (maybe_expand_insn (icode, 4, ops))
+	{
+	  if (ops[0].value != target)
+	    convert_move (target, ops[0].value, false);
+	  return target;
+	}
+    }
+  delete_insns_since (last);
+  return NULL_RTX;
 }
 \f
 /* These functions attempt to generate an insn body, rather than
@@ -4699,14 +4387,11 @@ emit_conditional_add (rtx target, enum r
 rtx
 gen_add2_insn (rtx x, rtx y)
 {
-  int icode = (int) optab_handler (add_optab, GET_MODE (x));
+  enum insn_code icode = optab_handler (add_optab, GET_MODE (x));
 
-  gcc_assert (insn_data[icode].operand[0].predicate
-	      (x, insn_data[icode].operand[0].mode));
-  gcc_assert (insn_data[icode].operand[1].predicate
-	      (x, insn_data[icode].operand[1].mode));
-  gcc_assert (insn_data[icode].operand[2].predicate
-	      (y, insn_data[icode].operand[2].mode));
+  gcc_assert (insn_operand_matches (icode, 0, x));
+  gcc_assert (insn_operand_matches (icode, 1, x));
+  gcc_assert (insn_operand_matches (icode, 2, y));
 
   return GEN_FCN (icode) (x, x, y);
 }
@@ -4717,15 +4402,12 @@ gen_add2_insn (rtx x, rtx y)
 rtx
 gen_add3_insn (rtx r0, rtx r1, rtx c)
 {
-  int icode = (int) optab_handler (add_optab, GET_MODE (r0));
+  enum insn_code icode = optab_handler (add_optab, GET_MODE (r0));
 
   if (icode == CODE_FOR_nothing
-      || !(insn_data[icode].operand[0].predicate
-	   (r0, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (r1, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (c, insn_data[icode].operand[2].mode)))
+      || !insn_operand_matches (icode, 0, r0)
+      || !insn_operand_matches (icode, 1, r1)
+      || !insn_operand_matches (icode, 2, c))
     return NULL_RTX;
 
   return GEN_FCN (icode) (r0, r1, c);
@@ -4734,21 +4416,18 @@ gen_add3_insn (rtx r0, rtx r1, rtx c)
 int
 have_add2_insn (rtx x, rtx y)
 {
-  int icode;
+  enum insn_code icode;
 
   gcc_assert (GET_MODE (x) != VOIDmode);
 
-  icode = (int) optab_handler (add_optab, GET_MODE (x));
+  icode = optab_handler (add_optab, GET_MODE (x));
 
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!(insn_data[icode].operand[0].predicate
-	(x, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (x, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (y, insn_data[icode].operand[2].mode)))
+  if (!insn_operand_matches (icode, 0, x)
+      || !insn_operand_matches (icode, 1, x)
+      || !insn_operand_matches (icode, 2, y))
     return 0;
 
   return 1;
@@ -4759,14 +4438,11 @@ have_add2_insn (rtx x, rtx y)
 rtx
 gen_sub2_insn (rtx x, rtx y)
 {
-  int icode = (int) optab_handler (sub_optab, GET_MODE (x));
+  enum insn_code icode = optab_handler (sub_optab, GET_MODE (x));
 
-  gcc_assert (insn_data[icode].operand[0].predicate
-	      (x, insn_data[icode].operand[0].mode));
-  gcc_assert (insn_data[icode].operand[1].predicate
-	      (x, insn_data[icode].operand[1].mode));
-  gcc_assert  (insn_data[icode].operand[2].predicate
-	       (y, insn_data[icode].operand[2].mode));
+  gcc_assert (insn_operand_matches (icode, 0, x));
+  gcc_assert (insn_operand_matches (icode, 1, x));
+  gcc_assert (insn_operand_matches (icode, 2, y));
 
   return GEN_FCN (icode) (x, x, y);
 }
@@ -4777,15 +4453,12 @@ gen_sub2_insn (rtx x, rtx y)
 rtx
 gen_sub3_insn (rtx r0, rtx r1, rtx c)
 {
-  int icode = (int) optab_handler (sub_optab, GET_MODE (r0));
+  enum insn_code icode = optab_handler (sub_optab, GET_MODE (r0));
 
   if (icode == CODE_FOR_nothing
-      || !(insn_data[icode].operand[0].predicate
-	   (r0, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (r1, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (c, insn_data[icode].operand[2].mode)))
+      || !insn_operand_matches (icode, 0, r0)
+      || !insn_operand_matches (icode, 1, r1)
+      || !insn_operand_matches (icode, 2, c))
     return NULL_RTX;
 
   return GEN_FCN (icode) (r0, r1, c);
@@ -4794,21 +4467,18 @@ gen_sub3_insn (rtx r0, rtx r1, rtx c)
 int
 have_sub2_insn (rtx x, rtx y)
 {
-  int icode;
+  enum insn_code icode;
 
   gcc_assert (GET_MODE (x) != VOIDmode);
 
-  icode = (int) optab_handler (sub_optab, GET_MODE (x));
+  icode = optab_handler (sub_optab, GET_MODE (x));
 
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!(insn_data[icode].operand[0].predicate
-	(x, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (x, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (y, insn_data[icode].operand[2].mode)))
+  if (!insn_operand_matches (icode, 0, x)
+      || !insn_operand_matches (icode, 1, x)
+      || !insn_operand_matches (icode, 2, y))
     return 0;
 
   return 1;
@@ -6643,8 +6313,7 @@ gen_cond_trap (enum rtx_code code, rtx o
     return 0;
 
   /* Some targets only accept a zero trap code.  */
-  if (insn_data[icode].operand[3].predicate
-      && !insn_data[icode].operand[3].predicate (tcode, VOIDmode))
+  if (!insn_operand_matches (icode, 3, tcode))
     return 0;
 
   do_pending_stack_adjust ();
@@ -6735,6 +6404,7 @@ get_rtx_code (enum tree_code tcode, bool
 static rtx
 vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode)
 {
+  struct expand_operand ops[2];
   enum rtx_code rcode;
   tree t_op0, t_op1;
   rtx rtx_op0, rtx_op1;
@@ -6753,15 +6423,11 @@ vector_compare_rtx (tree cond, bool unsi
   rtx_op1 = expand_expr (t_op1, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op1)),
 			 EXPAND_STACK_PARM);
 
-  if (!insn_data[icode].operand[4].predicate (rtx_op0, GET_MODE (rtx_op0))
-      && GET_MODE (rtx_op0) != VOIDmode)
-    rtx_op0 = force_reg (GET_MODE (rtx_op0), rtx_op0);
-
-  if (!insn_data[icode].operand[5].predicate (rtx_op1, GET_MODE (rtx_op1))
-      && GET_MODE (rtx_op1) != VOIDmode)
-    rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1);
-
-  return gen_rtx_fmt_ee (rcode, VOIDmode, rtx_op0, rtx_op1);
+  create_input_operand (&ops[0], rtx_op0, GET_MODE (rtx_op0));
+  create_input_operand (&ops[1], rtx_op1, GET_MODE (rtx_op1));
+  if (!maybe_legitimize_operands (icode, 4, 2, ops))
+    gcc_unreachable ();
+  return gen_rtx_fmt_ee (rcode, VOIDmode, ops[0].value, ops[1].value);
 }
 
 /* Return insn code for TYPE, the type of a VEC_COND_EXPR.  */
@@ -6796,8 +6462,9 @@ expand_vec_cond_expr_p (tree type, enum
 expand_vec_cond_expr (tree vec_cond_type, tree op0, tree op1, tree op2,
 		      rtx target)
 {
+  struct expand_operand ops[6];
   enum insn_code icode;
-  rtx comparison, rtx_op1, rtx_op2, cc_op0, cc_op1;
+  rtx comparison, rtx_op1, rtx_op2;
   enum machine_mode mode = TYPE_MODE (vec_cond_type);
   bool unsignedp = TYPE_UNSIGNED (vec_cond_type);
 
@@ -6805,30 +6472,18 @@ expand_vec_cond_expr (tree vec_cond_type
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    target = gen_reg_rtx (mode);
-
-  /* Get comparison rtx.  First expand both cond expr operands.  */
-  comparison = vector_compare_rtx (op0,
-				   unsignedp, icode);
-  cc_op0 = XEXP (comparison, 0);
-  cc_op1 = XEXP (comparison, 1);
-  /* Expand both operands and force them in reg, if required.  */
+  comparison = vector_compare_rtx (op0, unsignedp, icode);
   rtx_op1 = expand_normal (op1);
-  if (!insn_data[icode].operand[1].predicate (rtx_op1, mode)
-      && mode != VOIDmode)
-    rtx_op1 = force_reg (mode, rtx_op1);
-
   rtx_op2 = expand_normal (op2);
-  if (!insn_data[icode].operand[2].predicate (rtx_op2, mode)
-      && mode != VOIDmode)
-    rtx_op2 = force_reg (mode, rtx_op2);
-
-  /* Emit instruction! */
-  emit_insn (GEN_FCN (icode) (target, rtx_op1, rtx_op2,
-			      comparison, cc_op0,  cc_op1));
 
-  return target;
+  create_output_operand (&ops[0], target, mode);
+  create_input_operand (&ops[1], rtx_op1, mode);
+  create_input_operand (&ops[2], rtx_op2, mode);
+  create_fixed_operand (&ops[3], comparison);
+  create_fixed_operand (&ops[4], XEXP (comparison, 0));
+  create_fixed_operand (&ops[5], XEXP (comparison, 1));
+  expand_insn (icode, 6, ops);
+  return ops[0].value;
 }
 
 \f
@@ -6842,28 +6497,18 @@ expand_vec_cond_expr (tree vec_cond_type
 expand_val_compare_and_swap_1 (rtx mem, rtx old_val, rtx new_val,
 			       rtx target, enum insn_code icode)
 {
+  struct expand_operand ops[4];
   enum machine_mode mode = GET_MODE (mem);
-  rtx insn;
-
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    target = gen_reg_rtx (mode);
-
-  if (GET_MODE (old_val) != VOIDmode && GET_MODE (old_val) != mode)
-    old_val = convert_modes (mode, GET_MODE (old_val), old_val, 1);
-  if (!insn_data[icode].operand[2].predicate (old_val, mode))
-    old_val = force_reg (mode, old_val);
-
-  if (GET_MODE (new_val) != VOIDmode && GET_MODE (new_val) != mode)
-    new_val = convert_modes (mode, GET_MODE (new_val), new_val, 1);
-  if (!insn_data[icode].operand[3].predicate (new_val, mode))
-    new_val = force_reg (mode, new_val);
 
-  insn = GEN_FCN (icode) (target, mem, old_val, new_val);
-  if (insn == NULL_RTX)
-    return NULL_RTX;
-  emit_insn (insn);
-
-  return target;
+  create_output_operand (&ops[0], target, mode);
+  create_fixed_operand (&ops[1], mem);
+  /* OLD_VAL and NEW_VAL may have been promoted to a wider mode.
+     Shrink them if so.  */
+  create_convert_operand_to (&ops[2], old_val, mode, true);
+  create_convert_operand_to (&ops[3], new_val, mode, true);
+  if (maybe_expand_insn (icode, 4, ops))
+    return ops[0].value;
+  return NULL_RTX;
 }
 
 /* Expand a compare-and-swap operation and return its value.  */
@@ -7068,17 +6713,13 @@ expand_sync_operation (rtx mem, rtx val,
   /* Generate the direct operation, if present.  */
   if (icode != CODE_FOR_nothing)
     {
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[1].predicate (val, mode))
-	val = force_reg (mode, val);
+      struct expand_operand ops[2];
 
-      insn = GEN_FCN (icode) (mem, val);
-      if (insn)
-	{
-	  emit_insn (insn);
-	  return const0_rtx;
-	}
+      create_fixed_operand (&ops[0], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[1], val, mode, true);
+      if (maybe_expand_insn (icode, 2, ops))
+	return const0_rtx;
     }
 
   /* Failing that, generate a compare-and-swap loop in which we perform the
@@ -7201,19 +6842,16 @@ expand_sync_fetch_operation (rtx mem, rt
   /* If we found something supported, great.  */
   if (icode != CODE_FOR_nothing)
     {
-      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-	target = gen_reg_rtx (mode);
-
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[2].predicate (val, mode))
-	val = force_reg (mode, val);
+      struct expand_operand ops[3];
 
-      insn = GEN_FCN (icode) (target, mem, val);
-      if (insn)
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[2], val, mode, true);
+      if (maybe_expand_insn (icode, 3, ops))
 	{
-	  emit_insn (insn);
-
+	  target = ops[0].value;
+	  val = ops[2].value;
 	  /* If we need to compensate for using an operation with the
 	     wrong return value, do so now.  */
 	  if (compensate)
@@ -7293,26 +6931,19 @@ expand_sync_lock_test_and_set (rtx mem,
 {
   enum machine_mode mode = GET_MODE (mem);
   enum insn_code icode;
-  rtx insn;
 
   /* If the target supports the test-and-set directly, great.  */
   icode = direct_optab_handler (sync_lock_test_and_set_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-	target = gen_reg_rtx (mode);
+      struct expand_operand ops[3];
 
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[2].predicate (val, mode))
-	val = force_reg (mode, val);
-
-      insn = GEN_FCN (icode) (target, mem, val);
-      if (insn)
-	{
-	  emit_insn (insn);
-	  return target;
-	}
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[2], val, mode, true);
+      if (maybe_expand_insn (icode, 3, ops))
+	return ops[0].value;
     }
 
   /* Otherwise, use a compare-and-swap loop for the exchange.  */
@@ -7329,5 +6960,224 @@ expand_sync_lock_test_and_set (rtx mem,
 
   return NULL_RTX;
 }
+\f
+/* Make OP describe an input operand that should have the same value
+   as VALUE, after any mode conversion that the target might request.
+   TYPE is the type of VALUE.  */
+
+void
+create_convert_operand_from_type (struct expand_operand *op,
+				  rtx value, tree type)
+{
+  create_convert_operand_from (op, value, TYPE_MODE (type),
+			       TYPE_UNSIGNED (type));
+}
+
+/* Return true if OPERAND is suitable for operand number OPNO of
+   instruction ICODE.  */
+
+bool
+insn_operand_matches (enum insn_code icode, unsigned int opno, rtx operand)
+{
+  return (!insn_data[(int) icode].operand[opno].predicate
+	  || (insn_data[(int) icode].operand[opno].predicate
+	      (operand, insn_data[(int) icode].operand[opno].mode)));
+}
+
+/* Try to make OP match operand OPNO of instruction ICODE.  Return true
+   on success, storing the new operand value back in OP.  */
+
+static bool
+maybe_legitimize_operand (enum insn_code icode, unsigned int opno,
+			  struct expand_operand *op)
+{
+  enum machine_mode mode, imode;
+  bool old_volatile_ok, result;
+
+  old_volatile_ok = volatile_ok;
+  mode = op->mode;
+  result = false;
+  switch (op->type)
+    {
+    case EXPAND_FIXED:
+      volatile_ok = true;
+      break;
+
+    case EXPAND_OUTPUT:
+      gcc_assert (mode != VOIDmode);
+      if (!op->value
+	  || op->value == const0_rtx
+	  || GET_MODE (op->value) != mode
+	  || !insn_operand_matches (icode, opno, op->value))
+	op->value = gen_reg_rtx (mode);
+      break;
+
+    case EXPAND_INPUT:
+    input:
+      gcc_assert (mode != VOIDmode);
+      gcc_assert (GET_MODE (op->value) == VOIDmode
+		  || GET_MODE (op->value) == mode);
+      result = insn_operand_matches (icode, opno, op->value);
+      if (!result)
+	op->value = copy_to_mode_reg (mode, op->value);
+      break;
+
+    case EXPAND_CONVERT_TO:
+      gcc_assert (mode != VOIDmode);
+      op->value = convert_to_mode (mode, op->value, op->unsigned_p);
+      goto input;
+
+    case EXPAND_CONVERT_FROM:
+      if (GET_MODE (op->value) != VOIDmode)
+	mode = GET_MODE (op->value);
+      else
+	/* The caller must tell us what mode this value has.  */
+	gcc_assert (mode != VOIDmode);
+
+      imode = insn_data[(int) icode].operand[opno].mode;
+      if (imode != VOIDmode && imode != mode)
+	{
+	  op->value = convert_modes (imode, mode, op->value, op->unsigned_p);
+	  mode = imode;
+	}
+      goto input;
+
+    case EXPAND_ADDRESS:
+      gcc_assert (mode != VOIDmode);
+      op->value = convert_memory_address (mode, op->value);
+      goto input;
+
+    case EXPAND_INTEGER:
+      mode = insn_data[(int) icode].operand[opno].mode;
+      if (mode != VOIDmode
+	  && (trunc_int_for_mode (INTVAL (op->value), mode)
+	      == INTVAL (op->value)))
+	goto input;
+      break;
+    }
+  if (!result)
+    result = insn_operand_matches (icode, opno, op->value);
+  volatile_ok = old_volatile_ok;
+  return result;
+}
+
+/* Try to make operands [OPS, OPS + NOPS) match operands [OPNO, OPNO + NOPS)
+   of instruction ICODE.  Return true on success, leaving the new operand
+   values in the OPS themselves.  Emit no code on failure.  */
+
+bool
+maybe_legitimize_operands (enum insn_code icode, unsigned int opno,
+			   unsigned int nops, struct expand_operand *ops)
+{
+  rtx last, tmp;
+  unsigned int i;
+
+  last = get_last_insn ();
+  for (i = 0; i < nops; i++)
+    if (!maybe_legitimize_operand (icode, opno + i, &ops[i]))
+      {
+	delete_insns_since (last);
+	return false;
+      }
+  for (i = 0; i + 1 < nops; i++)
+    if (ops[i].commutative < MAX_EXPAND_OPERANDS
+	&& swap_commutative_operands_with_target (ops[ops[i].commutative].value,
+						  ops[i].value,
+						  ops[i + 1].value))
+      {
+	tmp = ops[i].value;
+	ops[i].value = ops[i + 1].value;
+	ops[i + 1].value = tmp;
+      }
+  return true;
+}
+
+/* Try to generate instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  Return the instruction pattern on success,
+   and emit any necessary set-up code.  Return null and emit no
+   code on failure.  */
+
+rtx
+maybe_gen_insn (enum insn_code icode, unsigned int nops,
+		struct expand_operand *ops)
+{
+  /* n_operands includes any automatically-generated match_scratches,
+     so we can't check for equality here.  */
+  gcc_assert (nops <= (unsigned int) insn_data[(int) icode].n_operands);
+  if (!maybe_legitimize_operands (icode, 0, nops, ops))
+    return NULL_RTX;
+
+  switch (nops)
+    {
+    case 1:
+      return GEN_FCN (icode) (ops[0].value);
+    case 2:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value);
+    case 3:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value);
+    case 4:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+			      ops[3].value);
+    case 5:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+			      ops[3].value, ops[4].value);
+    case 6:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+			      ops[3].value, ops[4].value, ops[5].value);
+    }
+  gcc_unreachable ();
+}
+
+/* Try to emit instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  Return true on success and emit no code on failure.  */
+
+bool
+maybe_expand_insn (enum insn_code icode, unsigned int nops,
+		   struct expand_operand *ops)
+{
+  rtx pat = maybe_gen_insn (icode, nops, ops);
+  if (pat)
+    {
+      emit_insn (pat);
+      return true;
+    }
+  return false;
+}
+
+/* Like maybe_expand_insn, but for jumps.  */
+
+bool
+maybe_expand_jump_insn (enum insn_code icode, unsigned int nops,
+			struct expand_operand *ops)
+{
+  rtx pat = maybe_gen_insn (icode, nops, ops);
+  if (pat)
+    {
+      emit_jump_insn (pat);
+      return true;
+    }
+  return false;
+}
+
+/* Emit instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  */
+
+void
+expand_insn (enum insn_code icode, unsigned int nops,
+	     struct expand_operand *ops)
+{
+  if (!maybe_expand_insn (icode, nops, ops))
+    gcc_unreachable ();
+}
+
+/* Like expand_insn, but for jumps.  */
+
+void
+expand_jump_insn (enum insn_code icode, unsigned int nops,
+		  struct expand_operand *ops)
+{
+  if (!maybe_expand_jump_insn (icode, nops, ops))
+    gcc_unreachable ();
+}
 
 #include "gt-optabs.h"
Index: gcc/reload.c
===================================================================
--- gcc/reload.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/reload.c	2011-03-19 17:11:42.000000000 +0000
@@ -5819,17 +5819,15 @@ #define REG_OK_FOR_CONTEXT(CONTEXT, REGN
 	      rtx equiv = (MEM_P (XEXP (x, 0))
 			   ? XEXP (x, 0)
 			   : reg_equiv_mem[regno]);
-	      int icode = (int) optab_handler (add_optab, GET_MODE (x));
+	      enum insn_code icode = optab_handler (add_optab, GET_MODE (x));
 	      if (insn && NONJUMP_INSN_P (insn) && equiv
 		  && memory_operand (equiv, GET_MODE (equiv))
 #ifdef HAVE_cc0
 		  && ! sets_cc0_p (PATTERN (insn))
 #endif
 		  && ! (icode != CODE_FOR_nothing
-			&& ((*insn_data[icode].operand[0].predicate)
-			    (equiv, GET_MODE (x)))
-			&& ((*insn_data[icode].operand[1].predicate)
-			    (equiv, GET_MODE (x)))))
+			&& insn_operand_matches (icode, 0, equiv)
+			&& insn_operand_matches (icode, 1, equiv)))
 		{
 		  /* We use the original pseudo for loc, so that
 		     emit_reload_insns() knows which pseudo this
Index: gcc/reload1.c
===================================================================
--- gcc/reload1.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/reload1.c	2011-03-19 17:11:42.000000000 +0000
@@ -8479,7 +8479,7 @@ gen_reload (rtx out, rtx in, int opnum,
 	 not valid than to dummy things up.  */
 
       rtx op0, op1, tem, insn;
-      int code;
+      enum insn_code code;
 
       op0 = find_replacement (&XEXP (in, 0));
       op1 = find_replacement (&XEXP (in, 1));
@@ -8517,14 +8517,13 @@ gen_reload (rtx out, rtx in, int opnum,
 	 DEFINE_PEEPHOLE should be specified that recognizes the sequence
 	 we emit below.  */
 
-      code = (int) optab_handler (add_optab, GET_MODE (out));
+      code = optab_handler (add_optab, GET_MODE (out));
 
       if (CONSTANT_P (op1) || MEM_P (op1) || GET_CODE (op1) == SUBREG
 	  || (REG_P (op1)
 	      && REGNO (op1) >= FIRST_PSEUDO_REGISTER)
 	  || (code != CODE_FOR_nothing
-	      && ! ((*insn_data[code].operand[2].predicate)
-		    (op1, insn_data[code].operand[2].mode))))
+	      && !insn_operand_matches (code, 2, op1)))
 	tem = op0, op0 = op1, op1 = tem;
 
       gen_reload (out, op0, opnum, type);
Index: gcc/targhooks.c
===================================================================
--- gcc/targhooks.c	2011-03-19 17:07:49.000000000 +0000
+++ gcc/targhooks.c	2011-03-19 17:11:42.000000000 +0000
@@ -893,8 +893,7 @@ default_secondary_reload (bool in_p ATTR
 				reload_mode);
 
       if (icode != CODE_FOR_nothing
-	  && insn_data[(int) icode].operand[in_p].predicate
-	  && ! insn_data[(int) icode].operand[in_p].predicate (x, reload_mode))
+	  && !insn_operand_matches (icode, in_p, x))
 	icode = CODE_FOR_nothing;
       else if (icode != CODE_FOR_nothing)
 	{
Index: gcc/config/i386/i386.md
===================================================================
--- gcc/config/i386/i386.md	2011-03-19 17:12:02.000000000 +0000
+++ gcc/config/i386/i386.md	2011-03-19 17:12:15.000000000 +0000
@@ -15793,7 +15793,7 @@ (define_insn "*rep_movqi"
 (define_expand "setmem<mode>"
    [(use (match_operand:BLK 0 "memory_operand" ""))
     (use (match_operand:SWI48 1 "nonmemory_operand" ""))
-    (use (match_operand 2 "const_int_operand" ""))
+    (use (match_operand 2 "nonmemory_operand" ""))
     (use (match_operand 3 "const_int_operand" ""))
     (use (match_operand:SI 4 "const_int_operand" ""))
     (use (match_operand:SI 5 "const_int_operand" ""))]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-19 19:53   ` Richard Sandiford
@ 2011-03-21 19:27     ` Richard Henderson
  2011-03-21 21:39       ` Richard Sandiford
  2011-03-22 15:09       ` Richard Sandiford
  0 siblings, 2 replies; 32+ messages in thread
From: Richard Henderson @ 2011-03-21 19:27 UTC (permalink / raw)
  To: gcc-patches, patches, rdsandiford

On 03/19/2011 12:52 PM, Richard Sandiford wrote:
> Given the mode stuff above, I've tried to be quite draconian as far
> as caller-provided modes go.  I think the caller really should know
> what mode they're dealing with.  The one case where I had to hold
> back a bit was create_convert_operand_from, which replaces things like:
> 
>   if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
>     xop0 = convert_modes (mode0,
>                           GET_MODE (op0) != VOIDmode
>                           ? GET_MODE (op0)
>                           : mode,
>                           xop0, unsignedp);
> 
> It seems a little suspicious that we only trust "mode" for CONST_INT
> op0s, and not for other cases, but I'd like to leave that for now.
> Maybe a future "clean up"?

Sure.

> Also, I had to change the i386 setmem pattern in order to avoid
> a regression in cold-attribute-1.c.  The current predicate for
> the character operand is "const_int_operand", but the pattern
> handles registers as well.  I'm sure there are going to be other
> things like that, so sorry in advance if this patch goes in and
> breaks a target...

Sure.

> 	* reload.c (find_reloads_address_1): Use insn_operand_matches.
> 	* reload1.c (gen_reload): Likewise.

All the bits that just use insn_operand_matches are approved.
You can commit those first if you like to reduce the patch size.

>      {
> -      if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate)
> -	     (op0,
> -	      insn_data[(int) CODE_FOR_prefetch].operand[0].mode))
> -	  || (GET_MODE (op0) != Pmode))
> -	{
> -	  op0 = convert_memory_address (Pmode, op0);
> -	  op0 = force_reg (Pmode, op0);
> -	}
> -      emit_insn (gen_prefetch (op0, op1, op2));
> +      struct expand_operand ops[3];
> +
> +      create_address_operand (&ops[0], op0);
> +      create_integer_operand (&ops[1], INTVAL (op1));
> +      create_integer_operand (&ops[2], INTVAL (op2));
> +      if (maybe_expand_insn (CODE_FOR_prefetch, 3, ops))
> +	return;
>      }

Yep, this interface is a definite cleanup.

> @@ -2452,10 +2443,11 @@ expand_builtin_interclass_mathfn (tree e
>        if (mode != GET_MODE (op0))
>  	op0 = convert_to_mode (mode, op0, 0);
>  
> -      /* Compute into TARGET.
> -	 Set TARGET to wherever the result comes back.  */
> -      if (maybe_emit_unop_insn (icode, target, op0, UNKNOWN))
> -	return target;
> +      create_output_operand (&ops[0], target, TYPE_MODE (TREE_TYPE (exp)));
> +      if (maybe_legitimize_operands (icode, 0, 1, ops)
> +	  && maybe_emit_unop_insn (icode, ops[0].value, op0, UNKNOWN))
> +	return ops[0].value;

What are you doing here that maybe_emit_unop_insn doesn't?

> +      if (maybe_expand_insn (unsignedp ? CODE_FOR_extzv : CODE_FOR_extv,
> +			     4, ops))
>  	{
> -	  emit_insn (pat);
> +	  xtarget = ops[0].value;
>  	  if (xtarget == xspec_target)
>  	    return xtarget;
> -	  if (xtarget == xspec_target_subreg)
> +	  if (ops[0].value == xspec_target_subreg)
>  	    return xspec_target;

Why this last change?

>    x = prepare_operand (icode, x, 2, mode, compare_mode, unsignedp);
>    y = prepare_operand (icode, y, 3, mode, compare_mode, unsignedp);
>    comparison = gen_rtx_fmt_ee (code, result_mode, x, y);
> -  if (!x || !y
> -      || !insn_data[icode].operand[2].predicate
> -	  (x, insn_data[icode].operand[2].mode)
> -      || !insn_data[icode].operand[3].predicate
> -	  (y, insn_data[icode].operand[3].mode)
> -      || !insn_data[icode].operand[1].predicate (comparison, VOIDmode))
> +  if (!x || !y)
>      {
>        delete_insns_since (last);
>        return NULL_RTX;

Seems like we ought to push the IF above generating COMPARISON now.


>  expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
>  			   rtx target, int unsignedp)

Geezam.  I hope this one's correct -- the original code is impossible to
follow.  I suspect that it's trying to handle so many different cases at
once that it can't really validate anything at all.

> +  op = 0;
> +  create_output_operand (&eops[op++], target, TYPE_MODE (ops->type));
> +  create_convert_operand_from (&eops[op++], op0, tmode0, unsignedp);
>    if (op1)
> +    create_convert_operand_from (&eops[op++], op1, tmode1, unsignedp);
>    if (wide_op)
> +    create_convert_operand_from (&eops[op++], wide_op, wmode, unsignedp);
> +  expand_insn (icode, op, eops);
> +  return eops[0].value;

... the conversion is at least legible.

> +  if (commutative_p)
> +    make_operand_commutative (&ops[1], 0);

So this is used exactly once here in expand_binop_directly?

Honestly, I found the description of make_operand_commutative to
be rather weak, and the implementation,

> +  for (i = 0; i + 1 < nops; i++)
> +    if (ops[i].commutative < MAX_EXPAND_OPERANDS
> +       && swap_commutative_operands_with_target (ops[ops[i].commutative].value,
> +                                                 ops[i].value,
> +                                                 ops[i + 1].value))

with the assumption of i & i+1 being related, to be a pretty strong
assumption.

I think perhaps we should omit commutative operands as a feature of the
new interface -- at least until we have more than one user and can firm
up the semantics.  Instead you can simply use maybe_legitimize_operands
here directly, and do the commutative thing right here inline.

> +    case EXPAND_INTEGER:
> +      mode = insn_data[(int) icode].operand[opno].mode;
> +      if (mode != VOIDmode
> +	  && (trunc_int_for_mode (INTVAL (op->value), mode)
> +	      == INTVAL (op->value)))
> +	goto input;
> +      break;

Surely const_int_operand (op->value, mode).

> Index: gcc/config/i386/i386.md
> ===================================================================
> --- gcc/config/i386/i386.md	2011-03-19 17:12:02.000000000 +0000
> +++ gcc/config/i386/i386.md	2011-03-19 17:12:15.000000000 +0000
> @@ -15793,7 +15793,7 @@ (define_insn "*rep_movqi"
>  (define_expand "setmem<mode>"
>     [(use (match_operand:BLK 0 "memory_operand" ""))
>      (use (match_operand:SWI48 1 "nonmemory_operand" ""))
> -    (use (match_operand 2 "const_int_operand" ""))
> +    (use (match_operand 2 "nonmemory_operand" ""))

I do wonder if this operand ought to have QImode.  We do have

>   if (valmode != QImode)
>     val = gen_lowpart (QImode, val);

inside promote_duplicated_reg, but it does seem like leaving
the mode unspecified in the md file is a mistake.


r~

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-21 19:27     ` Richard Henderson
@ 2011-03-21 21:39       ` Richard Sandiford
  2011-03-22 15:09       ` Richard Sandiford
  1 sibling, 0 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-21 21:39 UTC (permalink / raw)
  To: Richard Henderson; +Cc: gcc-patches, patches

Richard Henderson <rth@redhat.com> writes:
>> 	* reload.c (find_reloads_address_1): Use insn_operand_matches.
>> 	* reload1.c (gen_reload): Likewise.
>
> All the bits that just use insn_operand_matches are approved.
> You can commit those first if you like to reduce the patch size.

OK, thanks, here's what I applied after testing on x86_64-linux-gnu.

Richard


gcc/
	* expr.h (prepare_operand): Move to...
	* optabs.h (prepare_operand): ...here and change the insn code
	parameter from "int" to "enum insn_code".
	(insn_operand_matches): Declare.
	* expr.c (init_expr_target): Use insn_operand_matches.
	(compress_float_constant): Likewise.
	* function.c (safe_insn_predicate, assign_parm_setup_reg): Likewise.
	* optabs.c (can_compare_p, prepare_cmp_insn): Likewise.
	(emit_cmp_and_jump_insn_1, gen_add2_insn, gen_add3_insn): Likewise.
	(have_add2_insn, gen_sub2_insn, gen_sub3_insn, have_sub2_insn): Likewise.
	(gen_cond_trap): Likewise.
	(prepare_operand): Likewise.  Change icode to an insn_code.
	(insn_operand_matches): New function.
	* reload.c (find_reloads_address_1): Use insn_operand_matches.
	* reload1.c (gen_reload): Likewise.
	* targhooks.c (default_secondary_reload): Likewise.

Index: gcc/expr.h
===================================================================
--- gcc/expr.h	2011-03-21 21:35:04.000000000 +0000
+++ gcc/expr.h	2011-03-21 21:36:40.000000000 +0000
@@ -173,9 +173,6 @@ extern rtx expand_simple_unop (enum mach
    perform the operation described by CODE and MODE.  */
 extern int have_insn_for (enum rtx_code, enum machine_mode);
 
-extern rtx prepare_operand (int, rtx, int, enum machine_mode, enum machine_mode,
-			    int);
-
 /* Emit code to make a call to a constant function or a library call.  */
 extern void emit_libcall_block (rtx, rtx, rtx, rtx);
 
Index: gcc/optabs.h
===================================================================
--- gcc/optabs.h	2011-03-21 21:35:04.000000000 +0000
+++ gcc/optabs.h	2011-03-21 21:36:40.000000000 +0000
@@ -923,4 +923,10 @@ set_direct_optab_handler (direct_optab o
 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);
+
+extern bool insn_operand_matches (enum insn_code icode, unsigned int opno,
+				  rtx operand);
+extern rtx prepare_operand (enum insn_code, rtx, int, enum machine_mode,
+			    enum machine_mode, int);
+
 #endif /* GCC_OPTABS_H */
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	2011-03-21 21:35:04.000000000 +0000
+++ gcc/expr.c	2011-03-21 21:36:40.000000000 +0000
@@ -286,7 +286,7 @@ init_expr_target (void)
 
 	  PUT_MODE (mem, srcmode);
 
-	  if ((*insn_data[ic].operand[1].predicate) (mem, srcmode))
+	  if (insn_operand_matches (ic, 1, mem))
 	    float_extend_from_mem[mode][srcmode] = true;
 	}
     }
@@ -3446,7 +3446,7 @@ compress_float_constant (rtx x, rtx y)
 	{
 	  /* Skip if the target needs extra instructions to perform
 	     the extension.  */
-	  if (! (*insn_data[ic].operand[1].predicate) (trunc_y, srcmode))
+	  if (!insn_operand_matches (ic, 1, trunc_y))
 	    continue;
 	  /* This is valid, but may not be cheaper than the original. */
 	  newcost = rtx_cost (gen_rtx_FLOAT_EXTEND (dstmode, trunc_y), SET, speed);
Index: gcc/function.c
===================================================================
--- gcc/function.c	2011-03-21 21:35:04.000000000 +0000
+++ gcc/function.c	2011-03-21 21:36:40.000000000 +0000
@@ -1493,16 +1493,7 @@ instantiate_virtual_regs_in_rtx (rtx *lo
 static int
 safe_insn_predicate (int code, int operand, rtx x)
 {
-  const struct insn_operand_data *op_data;
-
-  if (code < 0)
-    return true;
-
-  op_data = &insn_data[code].operand[operand];
-  if (op_data->predicate == NULL)
-    return true;
-
-  return op_data->predicate (x, op_data->mode);
+  return code < 0 || insn_operand_matches ((enum insn_code) code, operand, x);
 }
 
 /* A subroutine of instantiate_virtual_regs.  Instantiate any virtual
@@ -3013,8 +3004,8 @@ assign_parm_setup_reg (struct assign_par
       op0 = parmreg;
       op1 = validated_mem;
       if (icode != CODE_FOR_nothing
-	  && insn_data[icode].operand[0].predicate (op0, promoted_nominal_mode)
-	  && insn_data[icode].operand[1].predicate (op1, data->passed_mode))
+	  && insn_operand_matches (icode, 0, op0)
+	  && insn_operand_matches (icode, 1, op1))
 	{
 	  enum rtx_code code = unsignedp ? ZERO_EXTEND : SIGN_EXTEND;
 	  rtx insn, insns;
Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	2011-03-21 21:35:04.000000000 +0000
+++ gcc/optabs.c	2011-03-21 21:36:40.000000000 +0000
@@ -3943,15 +3943,15 @@ can_compare_p (enum rtx_code code, enum
   test = gen_rtx_fmt_ee (code, mode, const0_rtx, const0_rtx);
   do
     {
-      int icode;
+      enum insn_code icode;
 
       if (purpose == ccp_jump
           && (icode = optab_handler (cbranch_optab, mode)) != CODE_FOR_nothing
-          && insn_data[icode].operand[0].predicate (test, mode))
+          && insn_operand_matches (icode, 0, test))
         return 1;
       if (purpose == ccp_store_flag
           && (icode = optab_handler (cstore_optab, mode)) != CODE_FOR_nothing
-          && insn_data[icode].operand[1].predicate (test, mode))
+          && insn_operand_matches (icode, 1, test))
         return 1;
       if (purpose == ccp_cmov
 	  && optab_handler (cmov_optab, mode) != CODE_FOR_nothing)
@@ -4112,16 +4112,14 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx
       enum insn_code icode;
       icode = optab_handler (cbranch_optab, cmp_mode);
       if (icode != CODE_FOR_nothing
-	  && insn_data[icode].operand[0].predicate (test, VOIDmode))
+	  && insn_operand_matches (icode, 0, test))
 	{
 	  rtx last = get_last_insn ();
 	  rtx op0 = prepare_operand (icode, x, 1, mode, cmp_mode, unsignedp);
 	  rtx op1 = prepare_operand (icode, y, 2, mode, cmp_mode, unsignedp);
 	  if (op0 && op1
-	      && insn_data[icode].operand[1].predicate
-		 (op0, insn_data[icode].operand[1].mode)
-	      && insn_data[icode].operand[2].predicate
-		 (op1, insn_data[icode].operand[2].mode))
+	      && insn_operand_matches (icode, 1, op0)
+	      && insn_operand_matches (icode, 2, op1))
 	    {
 	      XEXP (test, 0) = op0;
 	      XEXP (test, 1) = op1;
@@ -4200,18 +4198,17 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx
    that it is accepted by the operand predicate.  Return the new value.  */
 
 rtx
-prepare_operand (int icode, rtx x, int opnum, enum machine_mode mode,
+prepare_operand (enum insn_code icode, rtx x, int opnum, enum machine_mode mode,
 		 enum machine_mode wider_mode, int unsignedp)
 {
   if (mode != wider_mode)
     x = convert_modes (wider_mode, mode, x, unsignedp);
 
-  if (!insn_data[icode].operand[opnum].predicate
-      (x, insn_data[icode].operand[opnum].mode))
+  if (!insn_operand_matches (icode, opnum, x))
     {
       if (reload_completed)
 	return NULL_RTX;
-      x = copy_to_mode_reg (insn_data[icode].operand[opnum].mode, x);
+      x = copy_to_mode_reg (insn_data[(int) icode].operand[opnum].mode, x);
     }
 
   return x;
@@ -4232,7 +4229,7 @@ emit_cmp_and_jump_insn_1 (rtx test, enum
   icode = optab_handler (cbranch_optab, optab_mode);
 
   gcc_assert (icode != CODE_FOR_nothing);
-  gcc_assert (insn_data[icode].operand[0].predicate (test, VOIDmode));
+  gcc_assert (insn_operand_matches (icode, 0, test));
   emit_jump_insn (GEN_FCN (icode) (test, XEXP (test, 0), XEXP (test, 1), label));
 }
 
@@ -4699,14 +4696,11 @@ emit_conditional_add (rtx target, enum r
 rtx
 gen_add2_insn (rtx x, rtx y)
 {
-  int icode = (int) optab_handler (add_optab, GET_MODE (x));
+  enum insn_code icode = optab_handler (add_optab, GET_MODE (x));
 
-  gcc_assert (insn_data[icode].operand[0].predicate
-	      (x, insn_data[icode].operand[0].mode));
-  gcc_assert (insn_data[icode].operand[1].predicate
-	      (x, insn_data[icode].operand[1].mode));
-  gcc_assert (insn_data[icode].operand[2].predicate
-	      (y, insn_data[icode].operand[2].mode));
+  gcc_assert (insn_operand_matches (icode, 0, x));
+  gcc_assert (insn_operand_matches (icode, 1, x));
+  gcc_assert (insn_operand_matches (icode, 2, y));
 
   return GEN_FCN (icode) (x, x, y);
 }
@@ -4717,15 +4711,12 @@ gen_add2_insn (rtx x, rtx y)
 rtx
 gen_add3_insn (rtx r0, rtx r1, rtx c)
 {
-  int icode = (int) optab_handler (add_optab, GET_MODE (r0));
+  enum insn_code icode = optab_handler (add_optab, GET_MODE (r0));
 
   if (icode == CODE_FOR_nothing
-      || !(insn_data[icode].operand[0].predicate
-	   (r0, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (r1, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (c, insn_data[icode].operand[2].mode)))
+      || !insn_operand_matches (icode, 0, r0)
+      || !insn_operand_matches (icode, 1, r1)
+      || !insn_operand_matches (icode, 2, c))
     return NULL_RTX;
 
   return GEN_FCN (icode) (r0, r1, c);
@@ -4734,21 +4725,18 @@ gen_add3_insn (rtx r0, rtx r1, rtx c)
 int
 have_add2_insn (rtx x, rtx y)
 {
-  int icode;
+  enum insn_code icode;
 
   gcc_assert (GET_MODE (x) != VOIDmode);
 
-  icode = (int) optab_handler (add_optab, GET_MODE (x));
+  icode = optab_handler (add_optab, GET_MODE (x));
 
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!(insn_data[icode].operand[0].predicate
-	(x, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (x, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (y, insn_data[icode].operand[2].mode)))
+  if (!insn_operand_matches (icode, 0, x)
+      || !insn_operand_matches (icode, 1, x)
+      || !insn_operand_matches (icode, 2, y))
     return 0;
 
   return 1;
@@ -4759,14 +4747,11 @@ have_add2_insn (rtx x, rtx y)
 rtx
 gen_sub2_insn (rtx x, rtx y)
 {
-  int icode = (int) optab_handler (sub_optab, GET_MODE (x));
+  enum insn_code icode = optab_handler (sub_optab, GET_MODE (x));
 
-  gcc_assert (insn_data[icode].operand[0].predicate
-	      (x, insn_data[icode].operand[0].mode));
-  gcc_assert (insn_data[icode].operand[1].predicate
-	      (x, insn_data[icode].operand[1].mode));
-  gcc_assert  (insn_data[icode].operand[2].predicate
-	       (y, insn_data[icode].operand[2].mode));
+  gcc_assert (insn_operand_matches (icode, 0, x));
+  gcc_assert (insn_operand_matches (icode, 1, x));
+  gcc_assert (insn_operand_matches (icode, 2, y));
 
   return GEN_FCN (icode) (x, x, y);
 }
@@ -4777,15 +4762,12 @@ gen_sub2_insn (rtx x, rtx y)
 rtx
 gen_sub3_insn (rtx r0, rtx r1, rtx c)
 {
-  int icode = (int) optab_handler (sub_optab, GET_MODE (r0));
+  enum insn_code icode = optab_handler (sub_optab, GET_MODE (r0));
 
   if (icode == CODE_FOR_nothing
-      || !(insn_data[icode].operand[0].predicate
-	   (r0, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (r1, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (c, insn_data[icode].operand[2].mode)))
+      || !insn_operand_matches (icode, 0, r0)
+      || !insn_operand_matches (icode, 1, r1)
+      || !insn_operand_matches (icode, 2, c))
     return NULL_RTX;
 
   return GEN_FCN (icode) (r0, r1, c);
@@ -4794,21 +4776,18 @@ gen_sub3_insn (rtx r0, rtx r1, rtx c)
 int
 have_sub2_insn (rtx x, rtx y)
 {
-  int icode;
+  enum insn_code icode;
 
   gcc_assert (GET_MODE (x) != VOIDmode);
 
-  icode = (int) optab_handler (sub_optab, GET_MODE (x));
+  icode = optab_handler (sub_optab, GET_MODE (x));
 
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!(insn_data[icode].operand[0].predicate
-	(x, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-	   (x, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-	   (y, insn_data[icode].operand[2].mode)))
+  if (!insn_operand_matches (icode, 0, x)
+      || !insn_operand_matches (icode, 1, x)
+      || !insn_operand_matches (icode, 2, y))
     return 0;
 
   return 1;
@@ -6643,8 +6622,7 @@ gen_cond_trap (enum rtx_code code, rtx o
     return 0;
 
   /* Some targets only accept a zero trap code.  */
-  if (insn_data[icode].operand[3].predicate
-      && !insn_data[icode].operand[3].predicate (tcode, VOIDmode))
+  if (!insn_operand_matches (icode, 3, tcode))
     return 0;
 
   do_pending_stack_adjust ();
@@ -7329,5 +7307,16 @@ expand_sync_lock_test_and_set (rtx mem,
 
   return NULL_RTX;
 }
+\f
+/* Return true if OPERAND is suitable for operand number OPNO of
+   instruction ICODE.  */
+
+bool
+insn_operand_matches (enum insn_code icode, unsigned int opno, rtx operand)
+{
+  return (!insn_data[(int) icode].operand[opno].predicate
+	  || (insn_data[(int) icode].operand[opno].predicate
+	      (operand, insn_data[(int) icode].operand[opno].mode)));
+}
 
 #include "gt-optabs.h"
Index: gcc/reload.c
===================================================================
--- gcc/reload.c	2011-03-21 21:35:04.000000000 +0000
+++ gcc/reload.c	2011-03-21 21:36:40.000000000 +0000
@@ -5819,17 +5819,15 @@ #define REG_OK_FOR_CONTEXT(CONTEXT, REGN
 	      rtx equiv = (MEM_P (XEXP (x, 0))
 			   ? XEXP (x, 0)
 			   : reg_equiv_mem[regno]);
-	      int icode = (int) optab_handler (add_optab, GET_MODE (x));
+	      enum insn_code icode = optab_handler (add_optab, GET_MODE (x));
 	      if (insn && NONJUMP_INSN_P (insn) && equiv
 		  && memory_operand (equiv, GET_MODE (equiv))
 #ifdef HAVE_cc0
 		  && ! sets_cc0_p (PATTERN (insn))
 #endif
 		  && ! (icode != CODE_FOR_nothing
-			&& ((*insn_data[icode].operand[0].predicate)
-			    (equiv, GET_MODE (x)))
-			&& ((*insn_data[icode].operand[1].predicate)
-			    (equiv, GET_MODE (x)))))
+			&& insn_operand_matches (icode, 0, equiv)
+			&& insn_operand_matches (icode, 1, equiv)))
 		{
 		  /* We use the original pseudo for loc, so that
 		     emit_reload_insns() knows which pseudo this
Index: gcc/reload1.c
===================================================================
--- gcc/reload1.c	2011-03-21 21:35:04.000000000 +0000
+++ gcc/reload1.c	2011-03-21 21:36:40.000000000 +0000
@@ -8479,7 +8479,7 @@ gen_reload (rtx out, rtx in, int opnum,
 	 not valid than to dummy things up.  */
 
       rtx op0, op1, tem, insn;
-      int code;
+      enum insn_code code;
 
       op0 = find_replacement (&XEXP (in, 0));
       op1 = find_replacement (&XEXP (in, 1));
@@ -8517,14 +8517,13 @@ gen_reload (rtx out, rtx in, int opnum,
 	 DEFINE_PEEPHOLE should be specified that recognizes the sequence
 	 we emit below.  */
 
-      code = (int) optab_handler (add_optab, GET_MODE (out));
+      code = optab_handler (add_optab, GET_MODE (out));
 
       if (CONSTANT_P (op1) || MEM_P (op1) || GET_CODE (op1) == SUBREG
 	  || (REG_P (op1)
 	      && REGNO (op1) >= FIRST_PSEUDO_REGISTER)
 	  || (code != CODE_FOR_nothing
-	      && ! ((*insn_data[code].operand[2].predicate)
-		    (op1, insn_data[code].operand[2].mode))))
+	      && !insn_operand_matches (code, 2, op1)))
 	tem = op0, op0 = op1, op1 = tem;
 
       gen_reload (out, op0, opnum, type);
Index: gcc/targhooks.c
===================================================================
--- gcc/targhooks.c	2011-03-21 21:35:04.000000000 +0000
+++ gcc/targhooks.c	2011-03-21 21:36:40.000000000 +0000
@@ -893,8 +893,7 @@ default_secondary_reload (bool in_p ATTR
 				reload_mode);
 
       if (icode != CODE_FOR_nothing
-	  && insn_data[(int) icode].operand[in_p].predicate
-	  && ! insn_data[(int) icode].operand[in_p].predicate (x, reload_mode))
+	  && !insn_operand_matches (icode, in_p, x))
 	icode = CODE_FOR_nothing;
       else if (icode != CODE_FOR_nothing)
 	{

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-21 19:27     ` Richard Henderson
  2011-03-21 21:39       ` Richard Sandiford
@ 2011-03-22 15:09       ` Richard Sandiford
  2011-03-22 17:49         ` Richard Henderson
  2011-03-24 15:14         ` Richard Sandiford
  1 sibling, 2 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-22 15:09 UTC (permalink / raw)
  To: Richard Henderson; +Cc: gcc-patches, patches

Richard Henderson <rth@redhat.com> writes:
> On 03/19/2011 12:52 PM, Richard Sandiford wrote:
>> @@ -2452,10 +2443,11 @@ expand_builtin_interclass_mathfn (tree e
>>        if (mode != GET_MODE (op0))
>>  	op0 = convert_to_mode (mode, op0, 0);
>>  
>> -      /* Compute into TARGET.
>> -	 Set TARGET to wherever the result comes back.  */
>> -      if (maybe_emit_unop_insn (icode, target, op0, UNKNOWN))
>> -	return target;
>> +      create_output_operand (&ops[0], target, TYPE_MODE (TREE_TYPE (exp)));
>> +      if (maybe_legitimize_operands (icode, 0, 1, ops)
>> +	  && maybe_emit_unop_insn (icode, ops[0].value, op0, UNKNOWN))
>> +	return ops[0].value;
>
> What are you doing here that maybe_emit_unop_insn doesn't?

maybe_emit_unop_insn needs a non-null target, and guarantees
that the result will be placed in that target.  So we need to
create one up-front.

>> +      if (maybe_expand_insn (unsignedp ? CODE_FOR_extzv : CODE_FOR_extv,
>> +			     4, ops))
>>  	{
>> -	  emit_insn (pat);
>> +	  xtarget = ops[0].value;
>>  	  if (xtarget == xspec_target)
>>  	    return xtarget;
>> -	  if (xtarget == xspec_target_subreg)
>> +	  if (ops[0].value == xspec_target_subreg)
>>  	    return xspec_target;
>
> Why this last change?

Oops.

>>    x = prepare_operand (icode, x, 2, mode, compare_mode, unsignedp);
>>    y = prepare_operand (icode, y, 3, mode, compare_mode, unsignedp);
>>    comparison = gen_rtx_fmt_ee (code, result_mode, x, y);
>> -  if (!x || !y
>> -      || !insn_data[icode].operand[2].predicate
>> -	  (x, insn_data[icode].operand[2].mode)
>> -      || !insn_data[icode].operand[3].predicate
>> -	  (y, insn_data[icode].operand[3].mode)
>> -      || !insn_data[icode].operand[1].predicate (comparison, VOIDmode))
>> +  if (!x || !y)
>>      {
>>        delete_insns_since (last);
>>        return NULL_RTX;
>
> Seems like we ought to push the IF above generating COMPARISON now.

Good catch, fixed.

>> +  if (commutative_p)
>> +    make_operand_commutative (&ops[1], 0);
>
> So this is used exactly once here in expand_binop_directly?
>
> Honestly, I found the description of make_operand_commutative to
> be rather weak, and the implementation,
>
>> +  for (i = 0; i + 1 < nops; i++)
>> +    if (ops[i].commutative < MAX_EXPAND_OPERANDS
>> +       && swap_commutative_operands_with_target (ops[ops[i].commutative].value,
>> +                                                 ops[i].value,
>> +                                                 ops[i + 1].value))
>
> with the assumption of i & i+1 being related, to be a pretty strong
> assumption.

OK.  I saw this as the expand equivalent of the "+" constraint, which
makes the same assumption.  I don't think we really support gaps between
commutative operands in any meaningful way.

> I think perhaps we should omit commutative operands as a feature of the
> new interface -- at least until we have more than one user and can firm
> up the semantics.  Instead you can simply use maybe_legitimize_operands
> here directly, and do the commutative thing right here inline.

OK, done.

>> +    case EXPAND_INTEGER:
>> +      mode = insn_data[(int) icode].operand[opno].mode;
>> +      if (mode != VOIDmode
>> +	  && (trunc_int_for_mode (INTVAL (op->value), mode)
>> +	      == INTVAL (op->value)))
>> +	goto input;
>> +      break;
>
> Surely const_int_operand (op->value, mode).

Doh, yes.

>> Index: gcc/config/i386/i386.md
>> ===================================================================
>> --- gcc/config/i386/i386.md	2011-03-19 17:12:02.000000000 +0000
>> +++ gcc/config/i386/i386.md	2011-03-19 17:12:15.000000000 +0000
>> @@ -15793,7 +15793,7 @@ (define_insn "*rep_movqi"
>>  (define_expand "setmem<mode>"
>>     [(use (match_operand:BLK 0 "memory_operand" ""))
>>      (use (match_operand:SWI48 1 "nonmemory_operand" ""))
>> -    (use (match_operand 2 "const_int_operand" ""))
>> +    (use (match_operand 2 "nonmemory_operand" ""))
>
> I do wonder if this operand ought to have QImode.  We do have
>
>>   if (valmode != QImode)
>>     val = gen_lowpart (QImode, val);
>
> inside promote_duplicated_reg, but it does seem like leaving
> the mode unspecified in the md file is a mistake.

Seems to work.

How does this version look?  Bootstrapped & regression-tested on
x86_64-linux-gnu.  I've also tested yesterday's version on
arm-linux-gnueabi and mipsisa64-elf.

Richard


gcc/
	* optabs.h (emit_unop_insn, maybe_emit_unop_insn): Change insn code
	parameter from "int" to "enum insn_code".
	(expand_operand_type): New enum.
	(expand_operand): New structure.
	(create_expand_operand): New function.
	(create_fixed_operand, create_output_operand): Likewise
	(create_input_operand, create_convert_operand_to): Likewise.
	(create_convert_operand_from, create_address_operand): Likewise.
	(create_integer_operand): Likewise.
	(create_convert_operand_from_type, maybe_legitimize_operands): Declare.
	(maybe_gen_insn, maybe_expand_insn, maybe_expand_jump_insn): Likewise.
	(expand_insn, expand_jump_insn): Likewise.
	* builtins.c (expand_builtin_prefetch): Use the new interfaces.
	(expand_builtin_interclass_mathfn, expand_builtin_strlen): Likewise.
	(expand_movstr, expand_builtin___clear_cache): Likewise.
	(expand_builtin_lock_release): Likewise.
	* explow.c (allocate_dynamic_stack_space): Likewise.
	(probe_stack_range): Likewise.  Allow check_stack to FAIL,
	and use the default handling in that case.
	* expmed.c (check_predicate_volatile_ok): Delete.
	(store_bit_field_1, extract_bit_field_1): Use the new interfaces.
	(emit_cstore): Likewise.
	* expr.c (emit_block_move_via_movmem): Likewise.
	(set_storage_via_setmem, expand_assignment): Likewise.
	(emit_storent_insn, try_casesi): Likewise.
	(emit_single_push_insn): Likewise.  Allow the expansion to fail.
	* optabs.c (expand_widen_pattern_expr, expand_ternary_op): Likewise.
	(expand_vec_shift_expr, expand_binop_directly): Likewise.
	(expand_twoval_unop, expand_twoval_binop): Likewise.
	(expand_unop_direct, emit_indirect_jump): Likewise.
	(emit_conditional_move, vector_compare_rtx): Likewise.
	(expand_vec_cond_expr, expand_val_compare_and_swap_1): Likewise.
	(expand_sync_operation, expand_sync_fetch_operation): Likewise.
	(expand_sync_lock_test_and_set): Likewise.
	(maybe_emit_unop_insn): Likewise.  Change icode to an insn_code.
	(emit_unop_insn): Likewise.
	(expand_copysign_absneg): Change icode to an insn_code.
	(create_convert_operand_from_type): New function.
	(maybe_legitimize_operand, maybe_legitimize_operands): Likewise.
	(maybe_gen_insn, maybe_expand_insn, maybe_expand_jump_insn): Likewise.
	(expand_insn, expand_jump_insn): Likewise.
	* config/i386/i386.md (setmem<mode>): Use nonmemory_operand rather
	than const_int_operand for operand 2.

Index: gcc/optabs.h
===================================================================
--- gcc/optabs.h	2011-03-22 08:48:40.000000000 +0000
+++ gcc/optabs.h	2011-03-22 09:54:46.000000000 +0000
@@ -791,8 +791,8 @@ extern rtx expand_copysign (rtx, rtx, rt
 
 /* Generate an instruction with a given INSN_CODE with an output and
    an input.  */
-extern void emit_unop_insn (int, rtx, rtx, enum rtx_code);
-extern bool maybe_emit_unop_insn (int, rtx, rtx, enum rtx_code);
+extern void emit_unop_insn (enum insn_code, rtx, rtx, enum rtx_code);
+extern bool maybe_emit_unop_insn (enum insn_code, rtx, rtx, enum rtx_code);
 
 /* An extra flag to control optab_for_tree_code's behavior.  This is needed to
    distinguish between machines with a vector shift that takes a scalar for the
@@ -926,6 +926,148 @@ extern rtx convert_optab_libfunc (conver
 
 extern bool insn_operand_matches (enum insn_code icode, unsigned int opno,
 				  rtx operand);
+
+/* Describes the type of an expand_operand.  Each value is associated
+   with a create_*_operand function; see the comments above those
+   functions for details.  */
+enum expand_operand_type {
+  EXPAND_FIXED,
+  EXPAND_OUTPUT,
+  EXPAND_INPUT,
+  EXPAND_CONVERT_TO,
+  EXPAND_CONVERT_FROM,
+  EXPAND_ADDRESS,
+  EXPAND_INTEGER
+};
+
+/* Information about an operand for instruction expansion.  */
+struct expand_operand {
+  /* The type of operand.  */
+  ENUM_BITFIELD (expand_operand_type) type : 8;
+
+  /* True if any conversion should treat VALUE as being unsigned
+     rather than signed.  Only meaningful for certain types.  */
+  unsigned int unsigned_p : 1;
+
+  /* Unused; available for future use.  */
+  unsigned int unused : 7;
+
+  /* The mode passed to the convert_*_operand function.  It has a
+     type-dependent meaning.  */
+  ENUM_BITFIELD (machine_mode) mode : 16;
+
+  /* The value of the operand.  */
+  rtx value;
+};
+
+/* Initialize OP with the given fields.  Initialise the other fields
+   to their default values.  */
+
+static inline void
+create_expand_operand (struct expand_operand *op,
+		       enum expand_operand_type type,
+		       rtx value, enum machine_mode mode,
+		       bool unsigned_p)
+{
+  op->type = type;
+  op->unsigned_p = unsigned_p;
+  op->unused = 0;
+  op->mode = mode;
+  op->value = value;
+}
+
+/* Make OP describe an operand that must use rtx X, even if X is volatile.  */
+
+static inline void
+create_fixed_operand (struct expand_operand *op, rtx x)
+{
+  create_expand_operand (op, EXPAND_FIXED, x, VOIDmode, false);
+}
+
+/* Make OP describe an output operand that must have mode MODE.
+   X, if nonnull, is a suggestion for where the output should be stored.
+   It is OK for VALUE to be inconsistent with MODE, although it will just
+   be ignored in that case.  */
+
+static inline void
+create_output_operand (struct expand_operand *op, rtx x,
+		       enum machine_mode mode)
+{
+  create_expand_operand (op, EXPAND_OUTPUT, x, mode, false);
+}
+
+/* Make OP describe an input operand that must have mode MODE and
+   value VALUE; MODE cannot be VOIDmode.  The backend may request that
+   VALUE be copied into a different kind of rtx before being passed
+   as an operand.  */
+
+static inline void
+create_input_operand (struct expand_operand *op, rtx value,
+		      enum machine_mode mode)
+{
+  create_expand_operand (op, EXPAND_INPUT, value, mode, false);
+}
+
+/* Like create_input_operand, except that VALUE must first be converted
+   to mode MODE.  UNSIGNED_P says whether VALUE is unsigned.  */
+
+static inline void
+create_convert_operand_to (struct expand_operand *op, rtx value,
+			   enum machine_mode mode, bool unsigned_p)
+{
+  create_expand_operand (op, EXPAND_CONVERT_TO, value, mode, unsigned_p);
+}
+
+/* Make OP describe an input operand that should have the same value
+   as VALUE, after any mode conversion that the backend might request.
+   If VALUE is a CONST_INT, it should be treated as having mode MODE.
+   UNSIGNED_P says whether VALUE is unsigned.  */
+
+static inline void
+create_convert_operand_from (struct expand_operand *op, rtx value,
+			     enum machine_mode mode, bool unsigned_p)
+{
+  create_expand_operand (op, EXPAND_CONVERT_FROM, value, mode, unsigned_p);
+}
+
+extern void create_convert_operand_from_type (struct expand_operand *op,
+					      rtx value, tree type);
+
+/* Make OP describe an input Pmode address operand.  VALUE is the value
+   of the address, but it may need to be converted to Pmode first.  */
+
+static inline void
+create_address_operand (struct expand_operand *op, rtx value)
+{
+  create_expand_operand (op, EXPAND_ADDRESS, value, Pmode, false);
+}
+
+/* Make OP describe an input operand that has value INTVAL and that has
+   no inherent mode.  This function should only be used for operands that
+   are always expand-time constants.  The backend may request that INTVAL
+   be copied into a different kind of rtx, but it must specify the mode
+   of that rtx if so.  */
+
+static inline void
+create_integer_operand (struct expand_operand *op, HOST_WIDE_INT intval)
+{
+  create_expand_operand (op, EXPAND_INTEGER, GEN_INT (intval), VOIDmode, false);
+}
+
+extern bool maybe_legitimize_operands (enum insn_code icode,
+				       unsigned int opno, unsigned int nops,
+				       struct expand_operand *ops);
+extern rtx maybe_gen_insn (enum insn_code icode, unsigned int nops,
+			   struct expand_operand *ops);
+extern bool maybe_expand_insn (enum insn_code icode, unsigned int nops,
+			       struct expand_operand *ops);
+extern bool maybe_expand_jump_insn (enum insn_code icode, unsigned int nops,
+				    struct expand_operand *ops);
+extern void expand_insn (enum insn_code icode, unsigned int nops,
+			 struct expand_operand *ops);
+extern void expand_jump_insn (enum insn_code icode, unsigned int nops,
+			      struct expand_operand *ops);
+
 extern rtx prepare_operand (enum insn_code, rtx, int, enum machine_mode,
 			    enum machine_mode, int);
 
Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	2011-03-22 08:48:40.000000000 +0000
+++ gcc/builtins.c	2011-03-22 08:51:13.000000000 +0000
@@ -1143,15 +1143,13 @@ expand_builtin_prefetch (tree exp)
 #ifdef HAVE_prefetch
   if (HAVE_prefetch)
     {
-      if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate)
-	     (op0,
-	      insn_data[(int) CODE_FOR_prefetch].operand[0].mode))
-	  || (GET_MODE (op0) != Pmode))
-	{
-	  op0 = convert_memory_address (Pmode, op0);
-	  op0 = force_reg (Pmode, op0);
-	}
-      emit_insn (gen_prefetch (op0, op1, op2));
+      struct expand_operand ops[3];
+
+      create_address_operand (&ops[0], op0);
+      create_integer_operand (&ops[1], INTVAL (op1));
+      create_integer_operand (&ops[2], INTVAL (op2));
+      if (maybe_expand_insn (CODE_FOR_prefetch, 3, ops))
+	return;
     }
 #endif
 
@@ -2431,16 +2429,9 @@ expand_builtin_interclass_mathfn (tree e
 
   if (icode != CODE_FOR_nothing)
     {
+      struct expand_operand ops[1];
       rtx last = get_last_insn ();
       tree orig_arg = arg;
-      /* Make a suitable register to place result in.  */
-      if (!target
-	  || GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp))
-	  || !insn_data[icode].operand[0].predicate (target, GET_MODE (target)))
-         target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-
-      gcc_assert (insn_data[icode].operand[0].predicate
-		  (target, GET_MODE (target)));
 
       /* Wrap the computation of the argument in a SAVE_EXPR, as we may
 	 need to expand the argument again.  This way, we will not perform
@@ -2452,10 +2443,11 @@ expand_builtin_interclass_mathfn (tree e
       if (mode != GET_MODE (op0))
 	op0 = convert_to_mode (mode, op0, 0);
 
-      /* Compute into TARGET.
-	 Set TARGET to wherever the result comes back.  */
-      if (maybe_emit_unop_insn (icode, target, op0, UNKNOWN))
-	return target;
+      create_output_operand (&ops[0], target, TYPE_MODE (TREE_TYPE (exp)));
+      if (maybe_legitimize_operands (icode, 0, 1, ops)
+	  && maybe_emit_unop_insn (icode, ops[0].value, op0, UNKNOWN))
+	return ops[0].value;
+
       delete_insns_since (last);
       CALL_EXPR_ARG (exp, 0) = orig_arg;
     }
@@ -3362,11 +3354,12 @@ expand_builtin_strlen (tree exp, rtx tar
     return NULL_RTX;
   else
     {
+      struct expand_operand ops[4];
       rtx pat;
       tree len;
       tree src = CALL_EXPR_ARG (exp, 0);
-      rtx result, src_reg, char_rtx, before_strlen;
-      enum machine_mode insn_mode = target_mode, char_mode;
+      rtx src_reg, before_strlen;
+      enum machine_mode insn_mode = target_mode;
       enum insn_code icode = CODE_FOR_nothing;
       unsigned int align;
 
@@ -3405,14 +3398,6 @@ expand_builtin_strlen (tree exp, rtx tar
       if (insn_mode == VOIDmode)
 	return NULL_RTX;
 
-      /* Make a place to write the result of the instruction.  */
-      result = target;
-      if (! (result != 0
-	     && REG_P (result)
-	     && GET_MODE (result) == insn_mode
-	     && REGNO (result) >= FIRST_PSEUDO_REGISTER))
-	result = gen_reg_rtx (insn_mode);
-
       /* Make a place to hold the source address.  We will not expand
 	 the actual source until we are sure that the expansion will
 	 not fail -- there are trees that cannot be expanded twice.  */
@@ -3422,17 +3407,12 @@ expand_builtin_strlen (tree exp, rtx tar
 	 source operand later.  */
       before_strlen = get_last_insn ();
 
-      char_rtx = const0_rtx;
-      char_mode = insn_data[(int) icode].operand[2].mode;
-      if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx,
-							    char_mode))
-	char_rtx = copy_to_mode_reg (char_mode, char_rtx);
-
-      pat = GEN_FCN (icode) (result, gen_rtx_MEM (BLKmode, src_reg),
-			     char_rtx, GEN_INT (align));
-      if (! pat)
+      create_output_operand (&ops[0], target, insn_mode);
+      create_fixed_operand (&ops[1], gen_rtx_MEM (BLKmode, src_reg));
+      create_integer_operand (&ops[2], 0);
+      create_integer_operand (&ops[3], align);
+      if (!maybe_expand_insn (icode, 4, ops))
 	return NULL_RTX;
-      emit_insn (pat);
 
       /* Now that we are assured of success, expand the source.  */
       start_sequence ();
@@ -3448,12 +3428,12 @@ expand_builtin_strlen (tree exp, rtx tar
 	emit_insn_before (pat, get_insns ());
 
       /* Return the value in the proper mode for this function.  */
-      if (GET_MODE (result) == target_mode)
-	target = result;
+      if (GET_MODE (ops[0].value) == target_mode)
+	target = ops[0].value;
       else if (target != 0)
-	convert_move (target, result, 0);
+	convert_move (target, ops[0].value, 0);
       else
-	target = convert_to_mode (target_mode, result, 0);
+	target = convert_to_mode (target_mode, ops[0].value, 0);
 
       return target;
     }
@@ -3674,56 +3654,39 @@ expand_builtin_mempcpy_args (tree dest, 
 static rtx
 expand_movstr (tree dest, tree src, rtx target, int endp)
 {
+  struct expand_operand ops[3];
   rtx end;
   rtx dest_mem;
   rtx src_mem;
-  rtx insn;
-  const struct insn_data_d * data;
 
   if (!HAVE_movstr)
     return NULL_RTX;
 
   dest_mem = get_memory_rtx (dest, NULL);
   src_mem = get_memory_rtx (src, NULL);
-  data = insn_data + CODE_FOR_movstr;
   if (!endp)
     {
       target = force_reg (Pmode, XEXP (dest_mem, 0));
       dest_mem = replace_equiv_address (dest_mem, target);
-      end = gen_reg_rtx (Pmode);
     }
-  else
-    {
-      if (target == 0
-	  || target == const0_rtx
-	  || ! (*data->operand[0].predicate) (target, Pmode))
+
+  create_output_operand (&ops[0], endp ? target : NULL_RTX, Pmode);
+  create_fixed_operand (&ops[1], dest_mem);
+  create_fixed_operand (&ops[2], src_mem);
+  expand_insn (CODE_FOR_movstr, 3, ops);
+
+  if (endp && target != const0_rtx)
+    {
+      target = ops[0].value;
+      /* movstr is supposed to set end to the address of the NUL
+	 terminator.  If the caller requested a mempcpy-like return value,
+	 adjust it.  */
+      if (endp == 1)
 	{
-	  end = gen_reg_rtx (Pmode);
-	  if (target != const0_rtx)
-	    target = end;
+	  rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1);
+	  emit_move_insn (target, force_operand (tem, NULL_RTX));
 	}
-      else
-	end = target;
-    }
-
-  if (data->operand[0].mode != VOIDmode)
-    end = gen_lowpart (data->operand[0].mode, end);
-
-  insn = data->genfun (end, dest_mem, src_mem);
-
-  gcc_assert (insn);
-
-  emit_insn (insn);
-
-  /* movstr is supposed to set end to the address of the NUL
-     terminator.  If the caller requested a mempcpy-like return value,
-     adjust it.  */
-  if (endp == 1 && target != const0_rtx)
-    {
-      rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1);
-      emit_move_insn (target, force_operand (tem, NULL_RTX));
     }
-
   return target;
 }
 
@@ -5223,7 +5186,6 @@ expand_builtin___clear_cache (tree exp A
   /* We have a "clear_cache" insn, and it will handle everything.  */
   tree begin, end;
   rtx begin_rtx, end_rtx;
-  enum insn_code icode;
 
   /* We must not expand to a library call.  If we did, any
      fallback library function in libgcc that might contain a call to
@@ -5236,21 +5198,18 @@ expand_builtin___clear_cache (tree exp A
 
   if (HAVE_clear_cache)
     {
-      icode = CODE_FOR_clear_cache;
+      struct expand_operand ops[2];
 
       begin = CALL_EXPR_ARG (exp, 0);
       begin_rtx = expand_expr (begin, NULL_RTX, Pmode, EXPAND_NORMAL);
-      begin_rtx = convert_memory_address (Pmode, begin_rtx);
-      if (!insn_data[icode].operand[0].predicate (begin_rtx, Pmode))
-	begin_rtx = copy_to_mode_reg (Pmode, begin_rtx);
 
       end = CALL_EXPR_ARG (exp, 1);
       end_rtx = expand_expr (end, NULL_RTX, Pmode, EXPAND_NORMAL);
-      end_rtx = convert_memory_address (Pmode, end_rtx);
-      if (!insn_data[icode].operand[1].predicate (end_rtx, Pmode))
-	end_rtx = copy_to_mode_reg (Pmode, end_rtx);
 
-      emit_insn (gen_clear_cache (begin_rtx, end_rtx));
+      create_address_operand (&ops[0], begin_rtx);
+      create_address_operand (&ops[1], end_rtx);
+      if (maybe_expand_insn (CODE_FOR_clear_cache, 2, ops))
+	return const0_rtx;
     }
   return const0_rtx;
 #endif /* HAVE_clear_cache */
@@ -5748,9 +5707,9 @@ expand_builtin_synchronize (void)
 static void
 expand_builtin_lock_release (enum machine_mode mode, tree exp)
 {
+  struct expand_operand ops[2];
   enum insn_code icode;
-  rtx mem, insn;
-  rtx val = const0_rtx;
+  rtx mem;
 
   /* Expand the operands.  */
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
@@ -5759,21 +5718,16 @@ expand_builtin_lock_release (enum machin
   icode = direct_optab_handler (sync_lock_release_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (!insn_data[icode].operand[1].predicate (val, mode))
-	val = force_reg (mode, val);
-
-      insn = GEN_FCN (icode) (mem, val);
-      if (insn)
-	{
-	  emit_insn (insn);
-	  return;
-	}
+      create_fixed_operand (&ops[0], mem);
+      create_input_operand (&ops[1], const0_rtx, mode);
+      if (maybe_expand_insn (icode, 2, ops))
+	return;
     }
 
   /* Otherwise we can implement this operation by emitting a barrier
      followed by a store of zero.  */
   expand_builtin_synchronize ();
-  emit_move_insn (mem, val);
+  emit_move_insn (mem, const0_rtx);
 }
 \f
 /* Expand an expression EXP that calls a built-in function,
Index: gcc/explow.c
===================================================================
--- gcc/explow.c	2011-03-21 19:36:17.000000000 +0000
+++ gcc/explow.c	2011-03-22 08:51:13.000000000 +0000
@@ -1379,21 +1379,13 @@ allocate_dynamic_stack_space (rtx size, 
 #ifdef HAVE_allocate_stack
   if (HAVE_allocate_stack)
     {
-      enum machine_mode mode = STACK_SIZE_MODE;
-      insn_operand_predicate_fn pred;
-
+      struct expand_operand ops[2];
       /* We don't have to check against the predicate for operand 0 since
 	 TARGET is known to be a pseudo of the proper mode, which must
-	 be valid for the operand.  For operand 1, convert to the
-	 proper mode and validate.  */
-      if (mode == VOIDmode)
-	mode = insn_data[(int) CODE_FOR_allocate_stack].operand[1].mode;
-
-      pred = insn_data[(int) CODE_FOR_allocate_stack].operand[1].predicate;
-      if (pred && ! ((*pred) (size, mode)))
-	size = copy_to_mode_reg (mode, convert_to_mode (mode, size, 1));
-
-      emit_insn (gen_allocate_stack (target, size));
+	 be valid for the operand.  */
+      create_fixed_operand (&ops[0], target);
+      create_convert_operand_to (&ops[1], size, STACK_SIZE_MODE, true);
+      expand_insn (CODE_FOR_allocate_stack, 2, ops);
     }
   else
 #endif
@@ -1544,22 +1536,22 @@ probe_stack_range (HOST_WIDE_INT first, 
 					         plus_constant (size, first)));
       emit_library_call (stack_check_libfunc, LCT_NORMAL, VOIDmode, 1, addr,
 			 Pmode);
+      return;
     }
 
   /* Next see if we have an insn to check the stack.  */
 #ifdef HAVE_check_stack
-  else if (HAVE_check_stack)
+  if (HAVE_check_stack)
     {
+      struct expand_operand ops[1];
       rtx addr = memory_address (Pmode,
 				 gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
 					         stack_pointer_rtx,
 					         plus_constant (size, first)));
-      insn_operand_predicate_fn pred
-	= insn_data[(int) CODE_FOR_check_stack].operand[0].predicate;
-      if (pred && !((*pred) (addr, Pmode)))
-	addr = copy_to_mode_reg (Pmode, addr);
 
-      emit_insn (gen_check_stack (addr));
+      create_input_operand (&ops[0], addr, Pmode);
+      if (maybe_expand_insn (CODE_FOR_check_stack, 1, ops))
+	return;
     }
 #endif
 
Index: gcc/expmed.c
===================================================================
--- gcc/expmed.c	2011-03-21 19:36:17.000000000 +0000
+++ gcc/expmed.c	2011-03-22 08:51:13.000000000 +0000
@@ -323,22 +323,6 @@ mode_for_extraction (enum extraction_pat
     return word_mode;
   return data->operand[opno].mode;
 }
-
-/* Return true if X, of mode MODE, matches the predicate for operand
-   OPNO of instruction ICODE.  Allow volatile memories, regardless of
-   the ambient volatile_ok setting.  */
-
-static bool
-check_predicate_volatile_ok (enum insn_code icode, int opno,
-			     rtx x, enum machine_mode mode)
-{
-  bool save_volatile_ok, result;
-
-  save_volatile_ok = volatile_ok;
-  result = insn_data[(int) icode].operand[opno].predicate (x, mode);
-  volatile_ok = save_volatile_ok;
-  return result;
-}
 \f
 /* A subroutine of store_bit_field, with the same arguments.  Return true
    if the operation could be implemented.
@@ -405,40 +389,17 @@ store_bit_field_1 (rtx str_rtx, unsigned
       && 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];
       enum machine_mode outermode = GET_MODE (op0);
       enum machine_mode innermode = GET_MODE_INNER (outermode);
-      int icode = (int) optab_handler (vec_set_optab, outermode);
+      enum insn_code icode = optab_handler (vec_set_optab, outermode);
       int pos = bitnum / GET_MODE_BITSIZE (innermode);
-      rtx rtxpos = GEN_INT (pos);
-      rtx src = value;
-      rtx dest = op0;
-      rtx pat, seq;
-      enum machine_mode mode0 = insn_data[icode].operand[0].mode;
-      enum machine_mode mode1 = insn_data[icode].operand[1].mode;
-      enum machine_mode mode2 = insn_data[icode].operand[2].mode;
-
-      start_sequence ();
 
-      if (! (*insn_data[icode].operand[1].predicate) (src, mode1))
-	src = copy_to_mode_reg (mode1, src);
-
-      if (! (*insn_data[icode].operand[2].predicate) (rtxpos, mode2))
-	rtxpos = copy_to_mode_reg (mode1, rtxpos);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert ((*insn_data[icode].operand[0].predicate) (dest, mode0)
-		  && (*insn_data[icode].operand[1].predicate) (src, mode1)
-		  && (*insn_data[icode].operand[2].predicate) (rtxpos, mode2));
-      pat = GEN_FCN (icode) (dest, src, rtxpos);
-      seq = get_insns ();
-      end_sequence ();
-      if (pat)
-	{
-	  emit_insn (seq);
-	  emit_insn (pat);
-	  return true;
-	}
+      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
@@ -515,44 +476,30 @@ store_bit_field_1 (rtx str_rtx, unsigned
       && bitsize == GET_MODE_BITSIZE (fieldmode)
       && optab_handler (movstrict_optab, fieldmode) != CODE_FOR_nothing)
     {
-      int icode = optab_handler (movstrict_optab, fieldmode);
-      rtx insn;
-      rtx start = get_last_insn ();
+      struct expand_operand ops[2];
+      enum insn_code icode = optab_handler (movstrict_optab, fieldmode);
       rtx arg0 = op0;
 
-      /* Get appropriate low part of the value being stored.  */
-      if (CONST_INT_P (value) || REG_P (value))
-	value = gen_lowpart (fieldmode, value);
-      else if (!(GET_CODE (value) == SYMBOL_REF
-		 || GET_CODE (value) == LABEL_REF
-		 || GET_CODE (value) == CONST))
-	value = convert_to_mode (fieldmode, value, 0);
-
-      if (! (*insn_data[icode].operand[1].predicate) (value, fieldmode))
-	value = copy_to_mode_reg (fieldmode, value);
-
-      if (GET_CODE (op0) == SUBREG)
+      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 (op0)) == fieldmode
+	  gcc_assert (GET_MODE (SUBREG_REG (arg0)) == fieldmode
 		      || GET_MODE_CLASS (fieldmode) == MODE_INT
 		      || GET_MODE_CLASS (fieldmode) == MODE_PARTIAL_INT);
-	  arg0 = SUBREG_REG (op0);
+	  arg0 = SUBREG_REG (arg0);
 	}
 
-      insn = (GEN_FCN (icode)
-		 (gen_rtx_SUBREG (fieldmode, arg0,
-				  (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
-				  + (offset * UNITS_PER_WORD)),
-				  value));
-      if (insn)
-	{
-	  emit_insn (insn);
-	  return true;
-	}
-      delete_insns_since (start);
+      arg0 = gen_rtx_SUBREG (fieldmode, arg0,
+			     (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
+			     + (offset * UNITS_PER_WORD));
+
+      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.  */
@@ -653,16 +600,13 @@ store_bit_field_1 (rtx str_rtx, unsigned
       && bitsize > 0
       && GET_MODE_BITSIZE (op_mode) >= bitsize
       && ! ((REG_P (op0) || GET_CODE (op0) == SUBREG)
-	    && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode)))
-      && insn_data[CODE_FOR_insv].operand[1].predicate (GEN_INT (bitsize),
-							VOIDmode)
-      && check_predicate_volatile_ok (CODE_FOR_insv, 0, op0, VOIDmode))
+	    && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode))))
     {
+      struct expand_operand ops[4];
       int xbitpos = bitpos;
       rtx value1;
       rtx xop0 = op0;
       rtx last = get_last_insn ();
-      rtx pat;
       bool copy_back = false;
 
       /* Add OFFSET into OP0's address.  */
@@ -743,17 +687,12 @@ store_bit_field_1 (rtx str_rtx, unsigned
 	    gcc_assert (CONSTANT_P (value));
 	}
 
-      /* If this machine's insv insists on a register,
-	 get VALUE1 into a register.  */
-      if (! ((*insn_data[(int) CODE_FOR_insv].operand[3].predicate)
-	     (value1, op_mode)))
-	value1 = force_reg (op_mode, value1);
-
-      pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1);
-      if (pat)
+      create_fixed_operand (&ops[0], xop0);
+      create_integer_operand (&ops[1], bitsize);
+      create_integer_operand (&ops[2], xbitpos);
+      create_input_operand (&ops[3], value1, op_mode);
+      if (maybe_expand_insn (CODE_FOR_insv, 4, ops))
 	{
-	  emit_insn (pat);
-
 	  if (copy_back)
 	    convert_move (op0, xop0, true);
 	  return true;
@@ -1235,50 +1174,21 @@ extract_bit_field_1 (rtx str_rtx, unsign
       && ((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];
       enum machine_mode outermode = GET_MODE (op0);
       enum machine_mode innermode = GET_MODE_INNER (outermode);
-      int icode = (int) optab_handler (vec_extract_optab, outermode);
+      enum insn_code icode = optab_handler (vec_extract_optab, outermode);
       unsigned HOST_WIDE_INT pos = bitnum / GET_MODE_BITSIZE (innermode);
-      rtx rtxpos = GEN_INT (pos);
-      rtx src = op0;
-      rtx dest = NULL, pat, seq;
-      enum machine_mode mode0 = insn_data[icode].operand[0].mode;
-      enum machine_mode mode1 = insn_data[icode].operand[1].mode;
-      enum machine_mode mode2 = insn_data[icode].operand[2].mode;
-
-      if (innermode == tmode || innermode == mode)
-	dest = target;
-
-      if (!dest)
-	dest = gen_reg_rtx (innermode);
-
-      start_sequence ();
-
-      if (! (*insn_data[icode].operand[0].predicate) (dest, mode0))
-	dest = copy_to_mode_reg (mode0, dest);
 
-      if (! (*insn_data[icode].operand[1].predicate) (src, mode1))
-	src = copy_to_mode_reg (mode1, src);
-
-      if (! (*insn_data[icode].operand[2].predicate) (rtxpos, mode2))
-	rtxpos = copy_to_mode_reg (mode1, rtxpos);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert ((*insn_data[icode].operand[0].predicate) (dest, mode0)
-		  && (*insn_data[icode].operand[1].predicate) (src, mode1)
-		  && (*insn_data[icode].operand[2].predicate) (rtxpos, mode2));
-
-      pat = GEN_FCN (icode) (dest, src, rtxpos);
-      seq = get_insns ();
-      end_sequence ();
-      if (pat)
-	{
-	  emit_insn (seq);
-	  emit_insn (pat);
-      	  if (mode0 != mode)
-	    return gen_lowpart (tmode, dest);
-	  return dest;
+      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;
 	}
     }
 
@@ -1517,17 +1427,14 @@ extract_bit_field_1 (rtx str_rtx, unsign
 	 acceptable to the format of ext(z)v.  */
       && !(GET_CODE (op0) == SUBREG && GET_MODE (op0) != ext_mode)
       && !((REG_P (op0) || GET_CODE (op0) == SUBREG)
-	   && (bitsize + bitpos > GET_MODE_BITSIZE (ext_mode)))
-      && check_predicate_volatile_ok (icode, 1, op0, GET_MODE (op0)))
+	   && (bitsize + bitpos > GET_MODE_BITSIZE (ext_mode))))
     {
+      struct expand_operand ops[4];
       unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
-      rtx bitsize_rtx, bitpos_rtx;
-      rtx last = get_last_insn ();
       rtx xop0 = op0;
       rtx xtarget = target;
       rtx xspec_target = target;
       rtx xspec_target_subreg = 0;
-      rtx pat;
 
       /* If op0 is a register, we need it in EXT_MODE to make it
 	 acceptable to the format of ext(z)v.  */
@@ -1570,27 +1477,20 @@ extract_bit_field_1 (rtx str_rtx, unsign
 	    xtarget = gen_reg_rtx (ext_mode);
 	}
 
-      /* If this machine's ext(z)v insists on a register target,
-	 make sure we have one.  */
-      if (!insn_data[(int) icode].operand[0].predicate (xtarget, ext_mode))
-	xtarget = gen_reg_rtx (ext_mode);
-
-      bitsize_rtx = GEN_INT (bitsize);
-      bitpos_rtx = GEN_INT (xbitpos);
-
-      pat = (unsignedp
-	     ? gen_extzv (xtarget, xop0, bitsize_rtx, bitpos_rtx)
-	     : gen_extv (xtarget, xop0, bitsize_rtx, bitpos_rtx));
-      if (pat)
+      create_output_operand (&ops[0], xtarget, ext_mode);
+      create_fixed_operand (&ops[1], xop0);
+      create_integer_operand (&ops[2], bitsize);
+      create_integer_operand (&ops[3], xbitpos);
+      if (maybe_expand_insn (unsignedp ? CODE_FOR_extzv : CODE_FOR_extv,
+			     4, ops))
 	{
-	  emit_insn (pat);
+	  xtarget = ops[0].value;
 	  if (xtarget == xspec_target)
 	    return xtarget;
 	  if (xtarget == xspec_target_subreg)
 	    return xspec_target;
 	  return convert_extracted_bit_field (xtarget, mode, tmode, unsignedp);
 	}
-      delete_insns_since (last);
     }
 
   /* If OP0 is a memory, try copying it to a register and seeing if a
@@ -5101,19 +5001,14 @@ emit_cstore (rtx target, enum insn_code 
 	     int unsignedp, rtx x, rtx y, int normalizep,
 	     enum machine_mode target_mode)
 {
-  rtx op0, last, comparison, subtarget, pattern;
+  struct expand_operand ops[4];
+  rtx op0, last, comparison, subtarget;
   enum machine_mode result_mode = insn_data[(int) icode].operand[0].mode;
 
   last = get_last_insn ();
   x = prepare_operand (icode, x, 2, mode, compare_mode, unsignedp);
   y = prepare_operand (icode, y, 3, mode, compare_mode, unsignedp);
-  comparison = gen_rtx_fmt_ee (code, result_mode, x, y);
-  if (!x || !y
-      || !insn_data[icode].operand[2].predicate
-	  (x, insn_data[icode].operand[2].mode)
-      || !insn_data[icode].operand[3].predicate
-	  (y, insn_data[icode].operand[3].mode)
-      || !insn_data[icode].operand[1].predicate (comparison, VOIDmode))
+  if (!x || !y)
     {
       delete_insns_since (last);
       return NULL_RTX;
@@ -5124,16 +5019,18 @@ emit_cstore (rtx target, enum insn_code 
   if (!target)
     target = gen_reg_rtx (target_mode);
 
-  if (optimize
-      || !(insn_data[(int) icode].operand[0].predicate (target, result_mode)))
-    subtarget = gen_reg_rtx (result_mode);
-  else
-    subtarget = target;
+  comparison = gen_rtx_fmt_ee (code, result_mode, x, y);
 
-  pattern = GEN_FCN (icode) (subtarget, comparison, x, y);
-  if (!pattern)
-    return NULL_RTX;
-  emit_insn (pattern);
+  create_output_operand (&ops[0], optimize ? NULL_RTX : target, result_mode);
+  create_fixed_operand (&ops[1], comparison);
+  create_fixed_operand (&ops[2], x);
+  create_fixed_operand (&ops[3], y);
+  if (!maybe_expand_insn (icode, 4, ops))
+    {
+      delete_insns_since (last);
+      return NULL_RTX;
+    }
+  subtarget = ops[0].value;
 
   /* If we are converting to a wider mode, first convert to
      TARGET_MODE, then normalize.  This produces better combining
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	2011-03-22 08:48:41.000000000 +0000
+++ gcc/expr.c	2011-03-22 08:51:13.000000000 +0000
@@ -1258,7 +1258,6 @@ block_move_libcall_safe_for_call_parm (v
 emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align,
 			    unsigned int expected_align, HOST_WIDE_INT expected_size)
 {
-  rtx opalign = GEN_INT (align / BITS_PER_UNIT);
   int save_volatile_ok = volatile_ok;
   enum machine_mode mode;
 
@@ -1276,7 +1275,6 @@ emit_block_move_via_movmem (rtx x, rtx y
        mode = GET_MODE_WIDER_MODE (mode))
     {
       enum insn_code code = direct_optab_handler (movmem_optab, mode);
-      insn_operand_predicate_fn pred;
 
       if (code != CODE_FOR_nothing
 	  /* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT
@@ -1286,43 +1284,32 @@ emit_block_move_via_movmem (rtx x, rtx y
 	  && ((CONST_INT_P (size)
 	       && ((unsigned HOST_WIDE_INT) INTVAL (size)
 		   <= (GET_MODE_MASK (mode) >> 1)))
-	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
-	  && ((pred = insn_data[(int) code].operand[0].predicate) == 0
-	      || (*pred) (x, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[1].predicate) == 0
-	      || (*pred) (y, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[3].predicate) == 0
-	      || (*pred) (opalign, VOIDmode)))
-	{
-	  rtx op2;
-	  rtx last = get_last_insn ();
-	  rtx pat;
-
-	  op2 = convert_to_mode (mode, size, 1);
-	  pred = insn_data[(int) code].operand[2].predicate;
-	  if (pred != 0 && ! (*pred) (op2, mode))
-	    op2 = copy_to_mode_reg (mode, op2);
+	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD))
+	{
+	  struct expand_operand ops[6];
+	  unsigned int nops;
 
 	  /* ??? When called via emit_block_move_for_call, it'd be
 	     nice if there were some way to inform the backend, so
 	     that it doesn't fail the expansion because it thinks
 	     emitting the libcall would be more efficient.  */
-
-	  if (insn_data[(int) code].n_operands == 4)
-	    pat = GEN_FCN ((int) code) (x, y, op2, opalign);
-	  else
-	    pat = GEN_FCN ((int) code) (x, y, op2, opalign,
-					GEN_INT (expected_align
-						 / BITS_PER_UNIT),
-					GEN_INT (expected_size));
-	  if (pat)
+	  nops = insn_data[(int) code].n_operands;
+	  create_fixed_operand (&ops[0], x);
+	  create_fixed_operand (&ops[1], y);
+	  /* The check above guarantees that this size conversion is valid.  */
+	  create_convert_operand_to (&ops[2], size, mode, true);
+	  create_integer_operand (&ops[3], align / BITS_PER_UNIT);
+	  if (nops != 4)
+	    {
+	      create_integer_operand (&ops[4], expected_align / BITS_PER_UNIT);
+	      create_integer_operand (&ops[5], expected_size);
+	      nops = 6;
+	    }
+	  if (maybe_expand_insn (code, nops, ops))
 	    {
-	      emit_insn (pat);
 	      volatile_ok = save_volatile_ok;
 	      return true;
 	    }
-	  else
-	    delete_insns_since (last);
 	}
     }
 
@@ -2705,7 +2692,6 @@ set_storage_via_setmem (rtx object, rtx 
      including more than one in the machine description unless
      the more limited one has some advantage.  */
 
-  rtx opalign = GEN_INT (align / BITS_PER_UNIT);
   enum machine_mode mode;
 
   if (expected_align < align)
@@ -2715,7 +2701,6 @@ set_storage_via_setmem (rtx object, rtx 
        mode = GET_MODE_WIDER_MODE (mode))
     {
       enum insn_code code = direct_optab_handler (setmem_optab, mode);
-      insn_operand_predicate_fn pred;
 
       if (code != CODE_FOR_nothing
 	  /* We don't need MODE to be narrower than
@@ -2725,46 +2710,25 @@ set_storage_via_setmem (rtx object, rtx 
 	  && ((CONST_INT_P (size)
 	       && ((unsigned HOST_WIDE_INT) INTVAL (size)
 		   <= (GET_MODE_MASK (mode) >> 1)))
-	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
-	  && ((pred = insn_data[(int) code].operand[0].predicate) == 0
-	      || (*pred) (object, BLKmode))
-	  && ((pred = insn_data[(int) code].operand[3].predicate) == 0
-	      || (*pred) (opalign, VOIDmode)))
-	{
-	  rtx opsize, opchar;
-	  enum machine_mode char_mode;
-	  rtx last = get_last_insn ();
-	  rtx pat;
-
-	  opsize = convert_to_mode (mode, size, 1);
-	  pred = insn_data[(int) code].operand[1].predicate;
-	  if (pred != 0 && ! (*pred) (opsize, mode))
-	    opsize = copy_to_mode_reg (mode, opsize);
-
-	  opchar = val;
-	  char_mode = insn_data[(int) code].operand[2].mode;
-	  if (char_mode != VOIDmode)
-	    {
-	      opchar = convert_to_mode (char_mode, opchar, 1);
-	      pred = insn_data[(int) code].operand[2].predicate;
-	      if (pred != 0 && ! (*pred) (opchar, char_mode))
-		opchar = copy_to_mode_reg (char_mode, opchar);
-	    }
+	      || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD))
+	{
+	  struct expand_operand ops[6];
+	  unsigned int nops;
 
-	  if (insn_data[(int) code].n_operands == 4)
-	    pat = GEN_FCN ((int) code) (object, opsize, opchar, opalign);
-	  else
-	    pat = GEN_FCN ((int) code) (object, opsize, opchar, opalign,
-					GEN_INT (expected_align
-						 / BITS_PER_UNIT),
-					GEN_INT (expected_size));
-	  if (pat)
-	    {
-	      emit_insn (pat);
-	      return true;
+	  nops = insn_data[(int) code].n_operands;
+	  create_fixed_operand (&ops[0], object);
+	  /* The check above guarantees that this size conversion is valid.  */
+	  create_convert_operand_to (&ops[1], size, mode, true);
+	  create_convert_operand_from (&ops[2], val, byte_mode, true);
+	  create_integer_operand (&ops[3], align / BITS_PER_UNIT);
+	  if (nops != 4)
+	    {
+	      create_integer_operand (&ops[4], expected_align / BITS_PER_UNIT);
+	      create_integer_operand (&ops[5], expected_size);
+	      nops = 6;
 	    }
-	  else
-	    delete_insns_since (last);
+	  if (maybe_expand_insn (code, nops, ops))
+	    return true;
 	}
     }
 
@@ -3547,7 +3511,6 @@ emit_single_push_insn (enum machine_mode
   unsigned rounded_size = PUSH_ROUNDING (GET_MODE_SIZE (mode));
   rtx dest;
   enum insn_code icode;
-  insn_operand_predicate_fn pred;
 
   stack_pointer_delta += PUSH_ROUNDING (GET_MODE_SIZE (mode));
   /* If there is push pattern, use it.  Otherwise try old way of throwing
@@ -3555,11 +3518,11 @@ emit_single_push_insn (enum machine_mode
   icode = optab_handler (push_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (((pred = insn_data[(int) icode].operand[0].predicate)
-	   && !((*pred) (x, mode))))
-	x = force_reg (mode, x);
-      emit_insn (GEN_FCN (icode) (x));
-      return;
+      struct expand_operand ops[1];
+
+      create_input_operand (&ops[0], x, mode);
+      if (maybe_expand_insn (icode, 1, ops))
+	return;
     }
   if (GET_MODE_SIZE (mode) == rounded_size)
     dest_addr = gen_rtx_fmt_e (STACK_PUSH_CODE, Pmode, stack_pointer_rtx);
@@ -4147,7 +4110,8 @@ expand_assignment (tree to, tree from, b
   rtx to_rtx = 0;
   rtx result;
   enum machine_mode mode;
-  int align, icode;
+  int align;
+  enum insn_code icode;
 
   /* Don't crash if the lhs of the assignment was erroneous.  */
   if (TREE_CODE (to) == ERROR_MARK)
@@ -4170,8 +4134,9 @@ expand_assignment (tree to, tree from, b
       && ((icode = optab_handler (movmisalign_optab, mode))
 	  != CODE_FOR_nothing))
     {
-      enum machine_mode address_mode, op_mode1;
-      rtx insn, reg, op0, mem;
+      struct expand_operand ops[2];
+      enum machine_mode address_mode;
+      rtx reg, op0, mem;
 
       reg = expand_expr (from, NULL_RTX, VOIDmode, EXPAND_NORMAL);
       reg = force_not_mem (reg);
@@ -4212,16 +4177,11 @@ expand_assignment (tree to, tree from, b
       if (TREE_THIS_VOLATILE (to))
 	MEM_VOLATILE_P (mem) = 1;
 
-      op_mode1 = insn_data[icode].operand[1].mode;
-      if (! (*insn_data[icode].operand[1].predicate) (reg, op_mode1)
-	  && op_mode1 != VOIDmode)
-	reg = copy_to_mode_reg (op_mode1, reg);
-
-      insn = GEN_FCN (icode) (mem, reg);
+      create_fixed_operand (&ops[0], mem);
+      create_input_operand (&ops[1], reg, mode);
       /* The movmisalign<mode> pattern cannot fail, else the assignment would
          silently be omitted.  */
-      gcc_assert (insn != NULL_RTX);
-      emit_insn (insn);
+      expand_insn (icode, 2, ops);
       return;
     }
 
@@ -4483,31 +4443,16 @@ expand_assignment (tree to, tree from, b
 bool
 emit_storent_insn (rtx to, rtx from)
 {
-  enum machine_mode mode = GET_MODE (to), imode;
+  struct expand_operand ops[2];
+  enum machine_mode mode = GET_MODE (to);
   enum insn_code code = optab_handler (storent_optab, mode);
-  rtx pattern;
 
   if (code == CODE_FOR_nothing)
     return false;
 
-  imode = insn_data[code].operand[0].mode;
-  if (!insn_data[code].operand[0].predicate (to, imode))
-    return false;
-
-  imode = insn_data[code].operand[1].mode;
-  if (!insn_data[code].operand[1].predicate (from, imode))
-    {
-      from = copy_to_mode_reg (imode, from);
-      if (!insn_data[code].operand[1].predicate (from, imode))
-	return false;
-    }
-
-  pattern = GEN_FCN (code) (to, from);
-  if (pattern == NULL_RTX)
-    return false;
-
-  emit_insn (pattern);
-  return true;
+  create_fixed_operand (&ops[0], to);
+  create_input_operand (&ops[1], from, mode);
+  return maybe_expand_insn (code, 2, ops);
 }
 
 /* Generate code for computing expression EXP,
@@ -10121,10 +10066,10 @@ try_casesi (tree index_type, tree index_
 	    rtx table_label ATTRIBUTE_UNUSED, rtx default_label,
 	    rtx fallback_label ATTRIBUTE_UNUSED)
 {
+  struct expand_operand ops[5];
   enum machine_mode index_mode = SImode;
   int index_bits = GET_MODE_BITSIZE (index_mode);
   rtx op1, op2, index;
-  enum machine_mode op_mode;
 
   if (! HAVE_casesi)
     return 0;
@@ -10159,32 +10104,17 @@ try_casesi (tree index_type, tree index_
 
   do_pending_stack_adjust ();
 
-  op_mode = insn_data[(int) CODE_FOR_casesi].operand[0].mode;
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[0].predicate)
-      (index, op_mode))
-    index = copy_to_mode_reg (op_mode, index);
-
   op1 = expand_normal (minval);
-
-  op_mode = insn_data[(int) CODE_FOR_casesi].operand[1].mode;
-  op1 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (minval)),
-		       op1, TYPE_UNSIGNED (TREE_TYPE (minval)));
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[1].predicate)
-      (op1, op_mode))
-    op1 = copy_to_mode_reg (op_mode, op1);
-
   op2 = expand_normal (range);
 
-  op_mode = insn_data[(int) CODE_FOR_casesi].operand[2].mode;
-  op2 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (range)),
-		       op2, TYPE_UNSIGNED (TREE_TYPE (range)));
-  if (! (*insn_data[(int) CODE_FOR_casesi].operand[2].predicate)
-      (op2, op_mode))
-    op2 = copy_to_mode_reg (op_mode, op2);
-
-  emit_jump_insn (gen_casesi (index, op1, op2,
-			      table_label, !default_label
-					   ? fallback_label : default_label));
+  create_input_operand (&ops[0], index, index_mode);
+  create_convert_operand_from_type (&ops[1], op1, TREE_TYPE (minval));
+  create_convert_operand_from_type (&ops[2], op2, TREE_TYPE (range));
+  create_fixed_operand (&ops[3], table_label);
+  create_fixed_operand (&ops[4], (default_label
+				  ? default_label
+				  : fallback_label));
+  expand_jump_insn (CODE_FOR_casesi, 5, ops);
   return 1;
 }
 
Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	2011-03-22 08:48:40.000000000 +0000
+++ gcc/optabs.c	2011-03-22 10:18:22.000000000 +0000
@@ -501,15 +501,13 @@ optab_for_tree_code (enum tree_code code
 expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
 			   rtx target, int unsignedp)
 {
+  struct expand_operand eops[4];
   tree oprnd0, oprnd1, oprnd2;
   enum machine_mode wmode = VOIDmode, tmode0, tmode1 = VOIDmode;
   optab widen_pattern_optab;
-  int icode;
-  enum machine_mode xmode0, xmode1 = VOIDmode, wxmode = VOIDmode;
-  rtx temp;
-  rtx pat;
-  rtx xop0, xop1, wxop;
+  enum insn_code icode;
   int nops = TREE_CODE_LENGTH (ops->code);
+  int op;
 
   oprnd0 = ops->op0;
   tmode0 = TYPE_MODE (TREE_TYPE (oprnd0));
@@ -517,117 +515,38 @@ expand_widen_pattern_expr (sepops ops, r
     optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
   if (ops->code == WIDEN_MULT_PLUS_EXPR
       || ops->code == WIDEN_MULT_MINUS_EXPR)
-    icode = (int) optab_handler (widen_pattern_optab,
-				 TYPE_MODE (TREE_TYPE (ops->op2)));
+    icode = optab_handler (widen_pattern_optab,
+			   TYPE_MODE (TREE_TYPE (ops->op2)));
   else
-    icode = (int) optab_handler (widen_pattern_optab, tmode0);
+    icode = optab_handler (widen_pattern_optab, tmode0);
   gcc_assert (icode != CODE_FOR_nothing);
-  xmode0 = insn_data[icode].operand[1].mode;
 
   if (nops >= 2)
     {
       oprnd1 = ops->op1;
       tmode1 = TYPE_MODE (TREE_TYPE (oprnd1));
-      xmode1 = insn_data[icode].operand[2].mode;
     }
 
   /* The last operand is of a wider mode than the rest of the operands.  */
   if (nops == 2)
-    {
-      wmode = tmode1;
-      wxmode = xmode1;
-    }
+    wmode = tmode1;
   else if (nops == 3)
     {
       gcc_assert (tmode1 == tmode0);
       gcc_assert (op1);
       oprnd2 = ops->op2;
       wmode = TYPE_MODE (TREE_TYPE (oprnd2));
-      wxmode = insn_data[icode].operand[3].mode;
     }
 
-  if (!wide_op)
-    wmode = wxmode = insn_data[icode].operand[0].mode;
-
-  if (!target
-      || ! (*insn_data[icode].operand[0].predicate) (target, wmode))
-    temp = gen_reg_rtx (wmode);
-  else
-    temp = target;
-
-  xop0 = op0;
-  xop1 = op1;
-  wxop = wide_op;
-
-  /* In case the insn wants input operands in modes different from
-     those of the actual operands, convert the operands.  It would
-     seem that we don't need to convert CONST_INTs, but we do, so
-     that they're properly zero-extended, sign-extended or truncated
-     for their mode.  */
-
-  if (GET_MODE (op0) != xmode0 && xmode0 != VOIDmode)
-    xop0 = convert_modes (xmode0,
-                          GET_MODE (op0) != VOIDmode
-                          ? GET_MODE (op0)
-                          : tmode0,
-                          xop0, unsignedp);
-
+  op = 0;
+  create_output_operand (&eops[op++], target, TYPE_MODE (ops->type));
+  create_convert_operand_from (&eops[op++], op0, tmode0, unsignedp);
   if (op1)
-    if (GET_MODE (op1) != xmode1 && xmode1 != VOIDmode)
-      xop1 = convert_modes (xmode1,
-                            GET_MODE (op1) != VOIDmode
-                            ? GET_MODE (op1)
-                            : tmode1,
-                            xop1, unsignedp);
-
+    create_convert_operand_from (&eops[op++], op1, tmode1, unsignedp);
   if (wide_op)
-    if (GET_MODE (wide_op) != wxmode && wxmode != VOIDmode)
-      wxop = convert_modes (wxmode,
-                            GET_MODE (wide_op) != VOIDmode
-                            ? GET_MODE (wide_op)
-                            : wmode,
-                            wxop, unsignedp);
-
-  /* Now, if insn's predicates don't allow our operands, put them into
-     pseudo regs.  */
-
-  if (! (*insn_data[icode].operand[1].predicate) (xop0, xmode0)
-      && xmode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (xmode0, xop0);
-
-  if (op1)
-    {
-      if (! (*insn_data[icode].operand[2].predicate) (xop1, xmode1)
-          && xmode1 != VOIDmode)
-        xop1 = copy_to_mode_reg (xmode1, xop1);
-
-      if (wide_op)
-        {
-          if (! (*insn_data[icode].operand[3].predicate) (wxop, wxmode)
-              && wxmode != VOIDmode)
-            wxop = copy_to_mode_reg (wxmode, wxop);
-
-          pat = GEN_FCN (icode) (temp, xop0, xop1, wxop);
-        }
-      else
-        pat = GEN_FCN (icode) (temp, xop0, xop1);
-    }
-  else
-    {
-      if (wide_op)
-        {
-          if (! (*insn_data[icode].operand[2].predicate) (wxop, wxmode)
-              && wxmode != VOIDmode)
-            wxop = copy_to_mode_reg (wxmode, wxop);
-
-          pat = GEN_FCN (icode) (temp, xop0, wxop);
-        }
-      else
-        pat = GEN_FCN (icode) (temp, xop0);
-    }
-
-  emit_insn (pat);
-  return temp;
+    create_convert_operand_from (&eops[op++], wide_op, wmode, unsignedp);
+  expand_insn (icode, op, eops);
+  return eops[0].value;
 }
 
 /* Generate code to perform an operation specified by TERNARY_OPTAB
@@ -645,67 +564,17 @@ expand_widen_pattern_expr (sepops ops, r
 expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0,
 		   rtx op1, rtx op2, rtx target, int unsignedp)
 {
-  int icode = (int) optab_handler (ternary_optab, mode);
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
-  enum machine_mode mode2 = insn_data[icode].operand[3].mode;
-  rtx temp;
-  rtx pat;
-  rtx xop0 = op0, xop1 = op1, xop2 = op2;
+  struct expand_operand ops[4];
+  enum insn_code icode = optab_handler (ternary_optab, mode);
 
   gcc_assert (optab_handler (ternary_optab, mode) != CODE_FOR_nothing);
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    temp = gen_reg_rtx (mode);
-  else
-    temp = target;
-
-  /* In case the insn wants input operands in modes different from
-     those of the actual operands, convert the operands.  It would
-     seem that we don't need to convert CONST_INTs, but we do, so
-     that they're properly zero-extended, sign-extended or truncated
-     for their mode.  */
-
-  if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
-    xop0 = convert_modes (mode0,
-                          GET_MODE (op0) != VOIDmode
-                          ? GET_MODE (op0)
-                          : mode,
-                          xop0, unsignedp);
-
-  if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
-    xop1 = convert_modes (mode1,
-                          GET_MODE (op1) != VOIDmode
-                          ? GET_MODE (op1)
-                          : mode,
-                          xop1, unsignedp);
-
-  if (GET_MODE (op2) != mode2 && mode2 != VOIDmode)
-    xop2 = convert_modes (mode2,
-                          GET_MODE (op2) != VOIDmode
-                          ? GET_MODE (op2)
-                          : mode,
-                          xop2, unsignedp);
-
-  /* Now, if insn's predicates don't allow our operands, put them into
-     pseudo regs.  */
-
-  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
-      && mode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (mode0, xop0);
-
-  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
-      && mode1 != VOIDmode)
-    xop1 = copy_to_mode_reg (mode1, xop1);
-
-  if (!insn_data[icode].operand[3].predicate (xop2, mode2)
-      && mode2 != VOIDmode)
-    xop2 = copy_to_mode_reg (mode2, xop2);
-
-  pat = GEN_FCN (icode) (temp, xop0, xop1, xop2);
-
-  emit_insn (pat);
-  return temp;
+  create_output_operand (&ops[0], target, mode);
+  create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+  create_convert_operand_from (&ops[2], op1, mode, unsignedp);
+  create_convert_operand_from (&ops[3], op2, mode, unsignedp);
+  expand_insn (icode, 4, ops);
+  return ops[0].value;
 }
 
 
@@ -751,15 +620,13 @@ force_expand_binop (enum machine_mode mo
 rtx
 expand_vec_shift_expr (sepops ops, rtx target)
 {
+  struct expand_operand eops[3];
   enum insn_code icode;
   rtx rtx_op1, rtx_op2;
-  enum machine_mode mode1;
-  enum machine_mode mode2;
   enum machine_mode mode = TYPE_MODE (ops->type);
   tree vec_oprnd = ops->op0;
   tree shift_oprnd = ops->op1;
   optab shift_optab;
-  rtx pat;
 
   switch (ops->code)
     {
@@ -776,29 +643,15 @@ expand_vec_shift_expr (sepops ops, rtx t
   icode = optab_handler (shift_optab, mode);
   gcc_assert (icode != CODE_FOR_nothing);
 
-  mode1 = insn_data[icode].operand[1].mode;
-  mode2 = insn_data[icode].operand[2].mode;
-
   rtx_op1 = expand_normal (vec_oprnd);
-  if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode1)
-      && mode1 != VOIDmode)
-    rtx_op1 = force_reg (mode1, rtx_op1);
-
   rtx_op2 = expand_normal (shift_oprnd);
-  if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode2)
-      && mode2 != VOIDmode)
-    rtx_op2 = force_reg (mode2, rtx_op2);
 
-  if (!target
-      || ! (*insn_data[icode].operand[0].predicate) (target, mode))
-    target = gen_reg_rtx (mode);
-
-  /* Emit instruction */
-  pat = GEN_FCN (icode) (target, rtx_op1, rtx_op2);
-  gcc_assert (pat);
-  emit_insn (pat);
+  create_output_operand (&eops[0], target, mode);
+  create_input_operand (&eops[1], rtx_op1, GET_MODE (rtx_op1));
+  create_convert_operand_from_type (&eops[2], rtx_op2, TREE_TYPE (shift_oprnd));
+  expand_insn (icode, 3, eops);
 
-  return target;
+  return eops[0].value;
 }
 
 /* This subroutine of expand_doubleword_shift handles the cases in which
@@ -1389,21 +1242,16 @@ expand_binop_directly (enum machine_mode
 		       rtx target, int unsignedp, enum optab_methods methods,
 		       rtx last)
 {
-  int icode = (int) optab_handler (binoptab, mode);
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+  enum insn_code icode = optab_handler (binoptab, mode);
+  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
   enum machine_mode tmp_mode;
+  struct expand_operand ops[3];
   bool commutative_p;
   rtx pat;
   rtx xop0 = op0, xop1 = op1;
-  rtx temp;
   rtx swap;
 
-  if (target)
-    temp = target;
-  else
-    temp = gen_reg_rtx (mode);
-
   /* If it is a commutative operator and the modes would match
      if we would swap the operands, we can save the conversions.  */
   commutative_p = commutative_optab_p (binoptab);
@@ -1421,49 +1269,9 @@ expand_binop_directly (enum machine_mode
   if (!shift_optab_p (binoptab))
     xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
 
-  /* In case the insn wants input operands in modes different from
-     those of the actual operands, convert the operands.  It would
-     seem that we don't need to convert CONST_INTs, but we do, so
-     that they're properly zero-extended, sign-extended or truncated
-     for their mode.  */
-
-  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
-    xop0 = convert_modes (mode0,
-			  GET_MODE (xop0) != VOIDmode
-			  ? GET_MODE (xop0)
-			  : mode,
-			  xop0, unsignedp);
-
-  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
-    xop1 = convert_modes (mode1,
-			  GET_MODE (xop1) != VOIDmode
-			  ? GET_MODE (xop1)
-			  : mode,
-			  xop1, unsignedp);
-
-  /* If operation is commutative,
-     try to make the first operand a register.
-     Even better, try to make it the same as the target.
-     Also try to make the last operand a constant.  */
-  if (commutative_p
-      && swap_commutative_operands_with_target (target, xop0, xop1))
-    {
-      swap = xop1;
-      xop1 = xop0;
-      xop0 = swap;
-    }
-
   /* Now, if insn's predicates don't allow our operands, put them into
      pseudo regs.  */
 
-  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
-      && mode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (mode0, xop0);
-
-  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
-      && mode1 != VOIDmode)
-    xop1 = copy_to_mode_reg (mode1, xop1);
-
   if (binoptab == vec_pack_trunc_optab
       || binoptab == vec_pack_usat_optab
       || binoptab == vec_pack_ssat_optab
@@ -1472,34 +1280,53 @@ expand_binop_directly (enum machine_mode
     {
       /* The mode of the result is different then the mode of the
 	 arguments.  */
-      tmp_mode = insn_data[icode].operand[0].mode;
+      tmp_mode = insn_data[(int) icode].operand[0].mode;
       if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode))
-	return 0;
+	{
+	  delete_insns_since (last);
+	  return NULL_RTX;
+	}
     }
   else
     tmp_mode = mode;
 
-  if (!insn_data[icode].operand[0].predicate (temp, tmp_mode))
-    temp = gen_reg_rtx (tmp_mode);
+  create_output_operand (&ops[0], target, tmp_mode);
+  create_convert_operand_from (&ops[1], xop0, mode, unsignedp);
+  create_convert_operand_from (&ops[2], xop1, mode, unsignedp);
+  if (maybe_legitimize_operands (icode, 0, 3, ops))
+    {
+      /* If operation is commutative,
+	 try to make the first operand a register.
+	 Even better, try to make it the same as the target.
+	 Also try to make the last operand a constant.  */
+      if (commutative_p
+	  && swap_commutative_operands_with_target (ops[0].value, ops[1].value,
+						    ops[2].value))
+	{
+	  swap = ops[2].value;
+	  ops[2].value = ops[1].value;
+	  ops[1].value = swap;
+	}
 
-  pat = GEN_FCN (icode) (temp, xop0, xop1);
-  if (pat)
-    {
-      /* If PAT is composed of more than one insn, try to add an appropriate
-	 REG_EQUAL note to it.  If we can't because TEMP conflicts with an
-	 operand, call expand_binop again, this time without a target.  */
-      if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-	  && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1))
+      pat = GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value);
+      if (pat)
 	{
-	  delete_insns_since (last);
-	  return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
-			       unsignedp, methods);
-	}
+	  /* If PAT is composed of more than one insn, try to add an appropriate
+	     REG_EQUAL note to it.  If we can't because TEMP conflicts with an
+	     operand, call expand_binop again, this time without a target.  */
+	  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
+	      && ! add_equal_note (pat, ops[0].value, binoptab->code,
+				   ops[1].value, ops[2].value))
+	    {
+	      delete_insns_since (last);
+	      return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
+				   unsignedp, methods);
+	    }
 
-      emit_insn (pat);
-      return temp;
+	  emit_insn (pat);
+	  return ops[0].value;
+	}
     }
-
   delete_insns_since (last);
   return NULL_RTX;
 }
@@ -2284,32 +2111,14 @@ expand_twoval_unop (optab unoptab, rtx o
 
   if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (unoptab, mode);
-      enum machine_mode mode0 = insn_data[icode].operand[2].mode;
-      rtx pat;
-      rtx xop0 = op0;
+      struct expand_operand ops[3];
+      enum insn_code icode = optab_handler (unoptab, mode);
 
-      if (GET_MODE (xop0) != VOIDmode
-	  && GET_MODE (xop0) != mode0)
-	xop0 = convert_to_mode (mode0, xop0, unsignedp);
-
-      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (!insn_data[icode].operand[2].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
-      gcc_assert (insn_data[icode].operand[1].predicate (targ1, mode));
-
-      pat = GEN_FCN (icode) (targ0, targ1, xop0);
-      if (pat)
-	{
-	  emit_insn (pat);
-	  return 1;
-	}
-      else
-	delete_insns_since (last);
+      create_fixed_operand (&ops[0], targ0);
+      create_fixed_operand (&ops[1], targ1);
+      create_convert_operand_from (&ops[2], op0, mode, unsignedp);
+      if (maybe_expand_insn (icode, 3, ops))
+	return 1;
     }
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
@@ -2376,56 +2185,23 @@ expand_twoval_binop (optab binoptab, rtx
 
   if (optab_handler (binoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (binoptab, mode);
+      struct expand_operand ops[4];
+      enum insn_code icode = optab_handler (binoptab, mode);
       enum machine_mode mode0 = insn_data[icode].operand[1].mode;
       enum machine_mode mode1 = insn_data[icode].operand[2].mode;
-      rtx pat;
       rtx xop0 = op0, xop1 = op1;
 
       /* If we are optimizing, force expensive constants into a register.  */
       xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp);
       xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
 
-      /* In case the insn wants input operands in modes different from
-	 those of the actual operands, convert the operands.  It would
-	 seem that we don't need to convert CONST_INTs, but we do, so
-	 that they're properly zero-extended, sign-extended or truncated
-	 for their mode.  */
-
-      if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
-	xop0 = convert_modes (mode0,
-			      GET_MODE (op0) != VOIDmode
-			      ? GET_MODE (op0)
-			      : mode,
-			      xop0, unsignedp);
-
-      if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
-	xop1 = convert_modes (mode1,
-			      GET_MODE (op1) != VOIDmode
-			      ? GET_MODE (op1)
-			      : mode,
-			      xop1, unsignedp);
-
-      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (!insn_data[icode].operand[1].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (!insn_data[icode].operand[2].predicate (xop1, mode1))
-	xop1 = copy_to_mode_reg (mode1, xop1);
-
-      /* We could handle this, but we should always be called with a pseudo
-	 for our targets and all insns should take them as outputs.  */
-      gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
-      gcc_assert (insn_data[icode].operand[3].predicate (targ1, mode));
-
-      pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
-      if (pat)
-	{
-	  emit_insn (pat);
-	  return 1;
-	}
-      else
-	delete_insns_since (last);
+      create_fixed_operand (&ops[0], targ0);
+      create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+      create_convert_operand_from (&ops[2], op1, mode, unsignedp);
+      create_fixed_operand (&ops[3], targ1);
+      if (maybe_expand_insn (icode, 4, ops))
+	return 1;
+      delete_insns_since (last);
     }
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
@@ -2985,34 +2761,19 @@ expand_unop_direct (enum machine_mode mo
 {
   if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (unoptab, mode);
-      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-      rtx xop0 = op0;
+      struct expand_operand ops[2];
+      enum insn_code icode = optab_handler (unoptab, mode);
       rtx last = get_last_insn ();
-      rtx pat, temp;
-
-      if (target)
-	temp = target;
-      else
-	temp = gen_reg_rtx (mode);
-
-      if (GET_MODE (xop0) != VOIDmode
-	  && GET_MODE (xop0) != mode0)
-	xop0 = convert_to_mode (mode0, xop0, unsignedp);
-
-      /* Now, if insn doesn't accept our operand, put it into a pseudo.  */
-
-      if (!insn_data[icode].operand[1].predicate (xop0, mode0))
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (!insn_data[icode].operand[0].predicate (temp, mode))
-	temp = gen_reg_rtx (mode);
+      rtx pat;
 
-      pat = GEN_FCN (icode) (temp, xop0);
+      create_output_operand (&ops[0], target, mode);
+      create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+      pat = maybe_gen_insn (icode, 2, ops);
       if (pat)
 	{
 	  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-	      && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX))
+	      && ! add_equal_note (pat, ops[0].value, unoptab->code,
+				   ops[1].value, NULL_RTX))
 	    {
 	      delete_insns_since (last);
 	      return expand_unop (mode, unoptab, op0, NULL_RTX, unsignedp);
@@ -3020,10 +2781,8 @@ expand_unop_direct (enum machine_mode mo
 
 	  emit_insn (pat);
 
-	  return temp;
+	  return ops[0].value;
 	}
-      else
-	delete_insns_since (last);
     }
   return 0;
 }
@@ -3499,7 +3258,7 @@ expand_copysign_absneg (enum machine_mod
 		        int bitpos, bool op0_is_abs)
 {
   enum machine_mode imode;
-  int icode;
+  enum insn_code icode;
   rtx sign, label;
 
   if (target == op1)
@@ -3507,10 +3266,10 @@ expand_copysign_absneg (enum machine_mod
 
   /* Check if the back end provides an insn that handles signbit for the
      argument's mode. */
-  icode = (int) optab_handler (signbit_optab, mode);
+  icode = optab_handler (signbit_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      imode = insn_data[icode].operand[0].mode;
+      imode = insn_data[(int) icode].operand[0].mode;
       sign = gen_reg_rtx (imode);
       emit_unop_insn (icode, sign, op1, UNKNOWN);
     }
@@ -3731,37 +3490,25 @@ expand_copysign (rtx op0, rtx op1, rtx t
    Return false if expansion failed.  */
 
 bool
-maybe_emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+maybe_emit_unop_insn (enum insn_code icode, rtx target, rtx op0,
+		      enum rtx_code code)
 {
-  rtx temp;
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  struct expand_operand ops[2];
   rtx pat;
-  rtx last = get_last_insn ();
-
-  temp = target;
-
-  /* Now, if insn does not accept our operands, put them into pseudos.  */
-
-  if (!insn_data[icode].operand[1].predicate (op0, mode0))
-    op0 = copy_to_mode_reg (mode0, op0);
 
-  if (!insn_data[icode].operand[0].predicate (temp, GET_MODE (temp)))
-    temp = gen_reg_rtx (GET_MODE (temp));
-
-  pat = GEN_FCN (icode) (temp, op0);
+  create_output_operand (&ops[0], target, GET_MODE (target));
+  create_input_operand (&ops[1], op0, GET_MODE (op0));
+  pat = maybe_gen_insn (icode, 2, ops);
   if (!pat)
-    {
-      delete_insns_since (last);
-      return false;
-    }
+    return false;
 
   if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN)
-    add_equal_note (pat, temp, code, op0, NULL_RTX);
+    add_equal_note (pat, ops[0].value, code, ops[1].value, NULL_RTX);
 
   emit_insn (pat);
 
-  if (temp != target)
-    emit_move_insn (target, temp);
+  if (ops[0].value != target)
+    emit_move_insn (target, ops[0].value);
   return true;
 }
 /* Generate an instruction whose insn-code is INSN_CODE,
@@ -3771,7 +3518,7 @@ maybe_emit_unop_insn (int icode, rtx tar
    the value that is stored into TARGET.  */
 
 void
-emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+emit_unop_insn (enum insn_code icode, rtx target, rtx op0, enum rtx_code code)
 {
   bool ok = maybe_emit_unop_insn (icode, target, op0, code);
   gcc_assert (ok);
@@ -4418,11 +4165,10 @@ prepare_float_lib_cmp (rtx x, rtx y, enu
 void
 emit_indirect_jump (rtx loc)
 {
-  if (!insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate
-      (loc, Pmode))
-    loc = copy_to_mode_reg (Pmode, loc);
+  struct expand_operand ops[1];
 
-  emit_jump_insn (gen_indirect_jump (loc));
+  create_address_operand (&ops[0], loc);
+  expand_jump_insn (CODE_FOR_indirect_jump, 1, ops);
   emit_barrier ();
 }
 \f
@@ -4447,7 +4193,7 @@ emit_conditional_move (rtx target, enum 
 		       enum machine_mode cmode, rtx op2, rtx op3,
 		       enum machine_mode mode, int unsignedp)
 {
-  rtx tem, subtarget, comparison, insn;
+  rtx tem, comparison, last;
   enum insn_code icode;
   enum rtx_code reversed;
 
@@ -4494,24 +4240,6 @@ emit_conditional_move (rtx target, enum 
   if (!target)
     target = gen_reg_rtx (mode);
 
-  subtarget = target;
-
-  /* If the insn doesn't accept these operands, put them in pseudos.  */
-
-  if (!insn_data[icode].operand[0].predicate
-      (subtarget, insn_data[icode].operand[0].mode))
-    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
-
-  if (!insn_data[icode].operand[2].predicate
-      (op2, insn_data[icode].operand[2].mode))
-    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
-
-  if (!insn_data[icode].operand[3].predicate
-      (op3, insn_data[icode].operand[3].mode))
-    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
-
-  /* Everything should now be in the suitable form.  */
-
   code = unsignedp ? unsigned_condition (code) : code;
   comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1);
 
@@ -4522,30 +4250,27 @@ emit_conditional_move (rtx target, enum 
     return NULL_RTX;
 
   do_pending_stack_adjust ();
-  start_sequence ();
+  last = get_last_insn ();
   prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
 		    GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN,
 		    &comparison, &cmode);
-  if (!comparison)
-    insn = NULL_RTX;
-  else
-    insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
-
-  /* If that failed, then give up.  */
-  if (insn == 0)
+  if (comparison)
     {
-      end_sequence ();
-      return 0;
-    }
-
-  emit_insn (insn);
-  insn = get_insns ();
-  end_sequence ();
-  emit_insn (insn);
-  if (subtarget != target)
-    convert_move (target, subtarget, 0);
+      struct expand_operand ops[4];
 
-  return target;
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], comparison);
+      create_input_operand (&ops[2], op2, mode);
+      create_input_operand (&ops[3], op3, mode);
+      if (maybe_expand_insn (icode, 4, ops))
+	{
+	  if (ops[0].value != target)
+	    convert_move (target, ops[0].value, false);
+	  return target;
+	}
+    }
+  delete_insns_since (last);
+  return NULL_RTX;
 }
 
 /* Return nonzero if a conditional move of mode MODE is supported.
@@ -4586,7 +4311,7 @@ emit_conditional_add (rtx target, enum r
 		      enum machine_mode cmode, rtx op2, rtx op3,
 		      enum machine_mode mode, int unsignedp)
 {
-  rtx tem, subtarget, comparison, insn;
+  rtx tem, comparison, last;
   enum insn_code icode;
   enum rtx_code reversed;
 
@@ -4633,24 +4358,6 @@ emit_conditional_add (rtx target, enum r
   if (!target)
     target = gen_reg_rtx (mode);
 
-  /* If the insn doesn't accept these operands, put them in pseudos.  */
-
-  if (!insn_data[icode].operand[0].predicate
-      (target, insn_data[icode].operand[0].mode))
-    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
-  else
-    subtarget = target;
-
-  if (!insn_data[icode].operand[2].predicate
-      (op2, insn_data[icode].operand[2].mode))
-    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
-
-  if (!insn_data[icode].operand[3].predicate
-      (op3, insn_data[icode].operand[3].mode))
-    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
-
-  /* Everything should now be in the suitable form.  */
-
   code = unsignedp ? unsigned_condition (code) : code;
   comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1);
 
@@ -4661,30 +4368,27 @@ emit_conditional_add (rtx target, enum r
     return NULL_RTX;
 
   do_pending_stack_adjust ();
-  start_sequence ();
+  last = get_last_insn ();
   prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
                     GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN,
                     &comparison, &cmode);
-  if (!comparison)
-    insn = NULL_RTX;
-  else
-    insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
-
-  /* If that failed, then give up.  */
-  if (insn == 0)
+  if (comparison)
     {
-      end_sequence ();
-      return 0;
-    }
-
-  emit_insn (insn);
-  insn = get_insns ();
-  end_sequence ();
-  emit_insn (insn);
-  if (subtarget != target)
-    convert_move (target, subtarget, 0);
+      struct expand_operand ops[4];
 
-  return target;
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], comparison);
+      create_input_operand (&ops[2], op2, mode);
+      create_input_operand (&ops[3], op3, mode);
+      if (maybe_expand_insn (icode, 4, ops))
+	{
+	  if (ops[0].value != target)
+	    convert_move (target, ops[0].value, false);
+	  return target;
+	}
+    }
+  delete_insns_since (last);
+  return NULL_RTX;
 }
 \f
 /* These functions attempt to generate an insn body, rather than
@@ -6713,6 +6417,7 @@ get_rtx_code (enum tree_code tcode, bool
 static rtx
 vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode)
 {
+  struct expand_operand ops[2];
   enum rtx_code rcode;
   tree t_op0, t_op1;
   rtx rtx_op0, rtx_op1;
@@ -6731,15 +6436,11 @@ vector_compare_rtx (tree cond, bool unsi
   rtx_op1 = expand_expr (t_op1, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op1)),
 			 EXPAND_STACK_PARM);
 
-  if (!insn_data[icode].operand[4].predicate (rtx_op0, GET_MODE (rtx_op0))
-      && GET_MODE (rtx_op0) != VOIDmode)
-    rtx_op0 = force_reg (GET_MODE (rtx_op0), rtx_op0);
-
-  if (!insn_data[icode].operand[5].predicate (rtx_op1, GET_MODE (rtx_op1))
-      && GET_MODE (rtx_op1) != VOIDmode)
-    rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1);
-
-  return gen_rtx_fmt_ee (rcode, VOIDmode, rtx_op0, rtx_op1);
+  create_input_operand (&ops[0], rtx_op0, GET_MODE (rtx_op0));
+  create_input_operand (&ops[1], rtx_op1, GET_MODE (rtx_op1));
+  if (!maybe_legitimize_operands (icode, 4, 2, ops))
+    gcc_unreachable ();
+  return gen_rtx_fmt_ee (rcode, VOIDmode, ops[0].value, ops[1].value);
 }
 
 /* Return insn code for TYPE, the type of a VEC_COND_EXPR.  */
@@ -6774,8 +6475,9 @@ expand_vec_cond_expr_p (tree type, enum 
 expand_vec_cond_expr (tree vec_cond_type, tree op0, tree op1, tree op2,
 		      rtx target)
 {
+  struct expand_operand ops[6];
   enum insn_code icode;
-  rtx comparison, rtx_op1, rtx_op2, cc_op0, cc_op1;
+  rtx comparison, rtx_op1, rtx_op2;
   enum machine_mode mode = TYPE_MODE (vec_cond_type);
   bool unsignedp = TYPE_UNSIGNED (vec_cond_type);
 
@@ -6783,30 +6485,18 @@ expand_vec_cond_expr (tree vec_cond_type
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    target = gen_reg_rtx (mode);
-
-  /* Get comparison rtx.  First expand both cond expr operands.  */
-  comparison = vector_compare_rtx (op0,
-				   unsignedp, icode);
-  cc_op0 = XEXP (comparison, 0);
-  cc_op1 = XEXP (comparison, 1);
-  /* Expand both operands and force them in reg, if required.  */
+  comparison = vector_compare_rtx (op0, unsignedp, icode);
   rtx_op1 = expand_normal (op1);
-  if (!insn_data[icode].operand[1].predicate (rtx_op1, mode)
-      && mode != VOIDmode)
-    rtx_op1 = force_reg (mode, rtx_op1);
-
   rtx_op2 = expand_normal (op2);
-  if (!insn_data[icode].operand[2].predicate (rtx_op2, mode)
-      && mode != VOIDmode)
-    rtx_op2 = force_reg (mode, rtx_op2);
-
-  /* Emit instruction! */
-  emit_insn (GEN_FCN (icode) (target, rtx_op1, rtx_op2,
-			      comparison, cc_op0,  cc_op1));
 
-  return target;
+  create_output_operand (&ops[0], target, mode);
+  create_input_operand (&ops[1], rtx_op1, mode);
+  create_input_operand (&ops[2], rtx_op2, mode);
+  create_fixed_operand (&ops[3], comparison);
+  create_fixed_operand (&ops[4], XEXP (comparison, 0));
+  create_fixed_operand (&ops[5], XEXP (comparison, 1));
+  expand_insn (icode, 6, ops);
+  return ops[0].value;
 }
 
 \f
@@ -6820,28 +6510,18 @@ expand_vec_cond_expr (tree vec_cond_type
 expand_val_compare_and_swap_1 (rtx mem, rtx old_val, rtx new_val,
 			       rtx target, enum insn_code icode)
 {
+  struct expand_operand ops[4];
   enum machine_mode mode = GET_MODE (mem);
-  rtx insn;
-
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    target = gen_reg_rtx (mode);
-
-  if (GET_MODE (old_val) != VOIDmode && GET_MODE (old_val) != mode)
-    old_val = convert_modes (mode, GET_MODE (old_val), old_val, 1);
-  if (!insn_data[icode].operand[2].predicate (old_val, mode))
-    old_val = force_reg (mode, old_val);
-
-  if (GET_MODE (new_val) != VOIDmode && GET_MODE (new_val) != mode)
-    new_val = convert_modes (mode, GET_MODE (new_val), new_val, 1);
-  if (!insn_data[icode].operand[3].predicate (new_val, mode))
-    new_val = force_reg (mode, new_val);
-
-  insn = GEN_FCN (icode) (target, mem, old_val, new_val);
-  if (insn == NULL_RTX)
-    return NULL_RTX;
-  emit_insn (insn);
 
-  return target;
+  create_output_operand (&ops[0], target, mode);
+  create_fixed_operand (&ops[1], mem);
+  /* OLD_VAL and NEW_VAL may have been promoted to a wider mode.
+     Shrink them if so.  */
+  create_convert_operand_to (&ops[2], old_val, mode, true);
+  create_convert_operand_to (&ops[3], new_val, mode, true);
+  if (maybe_expand_insn (icode, 4, ops))
+    return ops[0].value;
+  return NULL_RTX;
 }
 
 /* Expand a compare-and-swap operation and return its value.  */
@@ -7046,17 +6726,13 @@ expand_sync_operation (rtx mem, rtx val,
   /* Generate the direct operation, if present.  */
   if (icode != CODE_FOR_nothing)
     {
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[1].predicate (val, mode))
-	val = force_reg (mode, val);
+      struct expand_operand ops[2];
 
-      insn = GEN_FCN (icode) (mem, val);
-      if (insn)
-	{
-	  emit_insn (insn);
-	  return const0_rtx;
-	}
+      create_fixed_operand (&ops[0], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[1], val, mode, true);
+      if (maybe_expand_insn (icode, 2, ops))
+	return const0_rtx;
     }
 
   /* Failing that, generate a compare-and-swap loop in which we perform the
@@ -7179,19 +6855,16 @@ expand_sync_fetch_operation (rtx mem, rt
   /* If we found something supported, great.  */
   if (icode != CODE_FOR_nothing)
     {
-      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-	target = gen_reg_rtx (mode);
+      struct expand_operand ops[3];
 
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[2].predicate (val, mode))
-	val = force_reg (mode, val);
-
-      insn = GEN_FCN (icode) (target, mem, val);
-      if (insn)
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[2], val, mode, true);
+      if (maybe_expand_insn (icode, 3, ops))
 	{
-	  emit_insn (insn);
-
+	  target = ops[0].value;
+	  val = ops[2].value;
 	  /* If we need to compensate for using an operation with the
 	     wrong return value, do so now.  */
 	  if (compensate)
@@ -7271,26 +6944,19 @@ expand_sync_lock_test_and_set (rtx mem, 
 {
   enum machine_mode mode = GET_MODE (mem);
   enum insn_code icode;
-  rtx insn;
 
   /* If the target supports the test-and-set directly, great.  */
   icode = direct_optab_handler (sync_lock_test_and_set_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-	target = gen_reg_rtx (mode);
-
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-	val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[2].predicate (val, mode))
-	val = force_reg (mode, val);
+      struct expand_operand ops[3];
 
-      insn = GEN_FCN (icode) (target, mem, val);
-      if (insn)
-	{
-	  emit_insn (insn);
-	  return target;
-	}
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[2], val, mode, true);
+      if (maybe_expand_insn (icode, 3, ops))
+	return ops[0].value;
     }
 
   /* Otherwise, use a compare-and-swap loop for the exchange.  */
@@ -7318,5 +6984,201 @@ insn_operand_matches (enum insn_code ico
 	  || (insn_data[(int) icode].operand[opno].predicate
 	      (operand, insn_data[(int) icode].operand[opno].mode)));
 }
+\f
+/* Try to make OP match operand OPNO of instruction ICODE.  Return true
+   on success, storing the new operand value back in OP.  */
+
+static bool
+maybe_legitimize_operand (enum insn_code icode, unsigned int opno,
+			  struct expand_operand *op)
+{
+  enum machine_mode mode, imode;
+  bool old_volatile_ok, result;
+
+  old_volatile_ok = volatile_ok;
+  mode = op->mode;
+  result = false;
+  switch (op->type)
+    {
+    case EXPAND_FIXED:
+      volatile_ok = true;
+      break;
+
+    case EXPAND_OUTPUT:
+      gcc_assert (mode != VOIDmode);
+      if (!op->value
+	  || op->value == const0_rtx
+	  || GET_MODE (op->value) != mode
+	  || !insn_operand_matches (icode, opno, op->value))
+	op->value = gen_reg_rtx (mode);
+      break;
+
+    case EXPAND_INPUT:
+    input:
+      gcc_assert (mode != VOIDmode);
+      gcc_assert (GET_MODE (op->value) == VOIDmode
+		  || GET_MODE (op->value) == mode);
+      result = insn_operand_matches (icode, opno, op->value);
+      if (!result)
+	op->value = copy_to_mode_reg (mode, op->value);
+      break;
+
+    case EXPAND_CONVERT_TO:
+      gcc_assert (mode != VOIDmode);
+      op->value = convert_to_mode (mode, op->value, op->unsigned_p);
+      goto input;
+
+    case EXPAND_CONVERT_FROM:
+      if (GET_MODE (op->value) != VOIDmode)
+	mode = GET_MODE (op->value);
+      else
+	/* The caller must tell us what mode this value has.  */
+	gcc_assert (mode != VOIDmode);
+
+      imode = insn_data[(int) icode].operand[opno].mode;
+      if (imode != VOIDmode && imode != mode)
+	{
+	  op->value = convert_modes (imode, mode, op->value, op->unsigned_p);
+	  mode = imode;
+	}
+      goto input;
+
+    case EXPAND_ADDRESS:
+      gcc_assert (mode != VOIDmode);
+      op->value = convert_memory_address (mode, op->value);
+      goto input;
+
+    case EXPAND_INTEGER:
+      mode = insn_data[(int) icode].operand[opno].mode;
+      if (mode != VOIDmode && const_int_operand (op->value, mode))
+	goto input;
+      break;
+    }
+  if (!result)
+    result = insn_operand_matches (icode, opno, op->value);
+  volatile_ok = old_volatile_ok;
+  return result;
+}
+
+/* Make OP describe an input operand that should have the same value
+   as VALUE, after any mode conversion that the target might request.
+   TYPE is the type of VALUE.  */
+
+void
+create_convert_operand_from_type (struct expand_operand *op,
+				  rtx value, tree type)
+{
+  create_convert_operand_from (op, value, TYPE_MODE (type),
+			       TYPE_UNSIGNED (type));
+}
+
+/* Try to make operands [OPS, OPS + NOPS) match operands [OPNO, OPNO + NOPS)
+   of instruction ICODE.  Return true on success, leaving the new operand
+   values in the OPS themselves.  Emit no code on failure.  */
+
+bool
+maybe_legitimize_operands (enum insn_code icode, unsigned int opno,
+			   unsigned int nops, struct expand_operand *ops)
+{
+  rtx last;
+  unsigned int i;
+
+  last = get_last_insn ();
+  for (i = 0; i < nops; i++)
+    if (!maybe_legitimize_operand (icode, opno + i, &ops[i]))
+      {
+	delete_insns_since (last);
+	return false;
+      }
+  return true;
+}
+
+/* Try to generate instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  Return the instruction pattern on success,
+   and emit any necessary set-up code.  Return null and emit no
+   code on failure.  */
+
+rtx
+maybe_gen_insn (enum insn_code icode, unsigned int nops,
+		struct expand_operand *ops)
+{
+  /* n_operands includes any automatically-generated match_scratches,
+     so we can't check for equality here.  */
+  gcc_assert (nops <= (unsigned int) insn_data[(int) icode].n_operands);
+  if (!maybe_legitimize_operands (icode, 0, nops, ops))
+    return NULL_RTX;
+
+  switch (nops)
+    {
+    case 1:
+      return GEN_FCN (icode) (ops[0].value);
+    case 2:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value);
+    case 3:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value);
+    case 4:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+			      ops[3].value);
+    case 5:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+			      ops[3].value, ops[4].value);
+    case 6:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+			      ops[3].value, ops[4].value, ops[5].value);
+    }
+  gcc_unreachable ();
+}
+
+/* Try to emit instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  Return true on success and emit no code on failure.  */
+
+bool
+maybe_expand_insn (enum insn_code icode, unsigned int nops,
+		   struct expand_operand *ops)
+{
+  rtx pat = maybe_gen_insn (icode, nops, ops);
+  if (pat)
+    {
+      emit_insn (pat);
+      return true;
+    }
+  return false;
+}
+
+/* Like maybe_expand_insn, but for jumps.  */
+
+bool
+maybe_expand_jump_insn (enum insn_code icode, unsigned int nops,
+			struct expand_operand *ops)
+{
+  rtx pat = maybe_gen_insn (icode, nops, ops);
+  if (pat)
+    {
+      emit_jump_insn (pat);
+      return true;
+    }
+  return false;
+}
+
+/* Emit instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  */
+
+void
+expand_insn (enum insn_code icode, unsigned int nops,
+	     struct expand_operand *ops)
+{
+  if (!maybe_expand_insn (icode, nops, ops))
+    gcc_unreachable ();
+}
+
+/* Like expand_insn, but for jumps.  */
+
+void
+expand_jump_insn (enum insn_code icode, unsigned int nops,
+		  struct expand_operand *ops)
+{
+  if (!maybe_expand_jump_insn (icode, nops, ops))
+    gcc_unreachable ();
+}
 
 #include "gt-optabs.h"
Index: gcc/config/i386/i386.md
===================================================================
--- gcc/config/i386/i386.md	2011-03-22 08:48:37.000000000 +0000
+++ gcc/config/i386/i386.md	2011-03-22 08:51:13.000000000 +0000
@@ -15793,7 +15793,7 @@ (define_insn "*rep_movqi"
 (define_expand "setmem<mode>"
    [(use (match_operand:BLK 0 "memory_operand" ""))
     (use (match_operand:SWI48 1 "nonmemory_operand" ""))
-    (use (match_operand 2 "const_int_operand" ""))
+    (use (match_operand:QI 2 "nonmemory_operand" ""))
     (use (match_operand 3 "const_int_operand" ""))
     (use (match_operand:SI 4 "const_int_operand" ""))
     (use (match_operand:SI 5 "const_int_operand" ""))]

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-22 15:09       ` Richard Sandiford
@ 2011-03-22 17:49         ` Richard Henderson
  2011-03-23 18:01           ` Anatoly Sokolov
  2011-03-24 12:01           ` Andreas Krebbel
  2011-03-24 15:14         ` Richard Sandiford
  1 sibling, 2 replies; 32+ messages in thread
From: Richard Henderson @ 2011-03-22 17:49 UTC (permalink / raw)
  To: gcc-patches, patches, richard.sandiford

On 03/22/2011 08:08 AM, Richard Sandiford wrote:
>>> +  for (i = 0; i + 1 < nops; i++)
>>> +    if (ops[i].commutative < MAX_EXPAND_OPERANDS
>>> +       && swap_commutative_operands_with_target (ops[ops[i].commutative].value,
>>> +                                                 ops[i].value,
>>> +                                                 ops[i + 1].value))
>>
>> with the assumption of i & i+1 being related, to be a pretty strong
>> assumption.
> 
> OK.  I saw this as the expand equivalent of the "+" constraint, which
> makes the same assumption.  I don't think we really support gaps between
> commutative operands in any meaningful way.

You mean "%" I assume.  Yes, it does concern operands N and N+1.  But it's
documented that way, whereas you have .commutative and documented that as
the the operand number that gets swapped.  Which begs the question of what
is that third operand above, and how it would get specified.

Not that it matters now...

> 	* optabs.h (emit_unop_insn, maybe_emit_unop_insn): Change insn code
> 	parameter from "int" to "enum insn_code".
> 	(expand_operand_type): New enum.
> 	(expand_operand): New structure.
> 	(create_expand_operand): New function.
> 	(create_fixed_operand, create_output_operand): Likewise
> 	(create_input_operand, create_convert_operand_to): Likewise.
> 	(create_convert_operand_from, create_address_operand): Likewise.
> 	(create_integer_operand): Likewise.
> 	(create_convert_operand_from_type, maybe_legitimize_operands): Declare.
> 	(maybe_gen_insn, maybe_expand_insn, maybe_expand_jump_insn): Likewise.
> 	(expand_insn, expand_jump_insn): Likewise.
> 	* builtins.c (expand_builtin_prefetch): Use the new interfaces.
> 	(expand_builtin_interclass_mathfn, expand_builtin_strlen): Likewise.
> 	(expand_movstr, expand_builtin___clear_cache): Likewise.
> 	(expand_builtin_lock_release): Likewise.
> 	* explow.c (allocate_dynamic_stack_space): Likewise.
> 	(probe_stack_range): Likewise.  Allow check_stack to FAIL,
> 	and use the default handling in that case.
> 	* expmed.c (check_predicate_volatile_ok): Delete.
> 	(store_bit_field_1, extract_bit_field_1): Use the new interfaces.
> 	(emit_cstore): Likewise.
> 	* expr.c (emit_block_move_via_movmem): Likewise.
> 	(set_storage_via_setmem, expand_assignment): Likewise.
> 	(emit_storent_insn, try_casesi): Likewise.
> 	(emit_single_push_insn): Likewise.  Allow the expansion to fail.
> 	* optabs.c (expand_widen_pattern_expr, expand_ternary_op): Likewise.
> 	(expand_vec_shift_expr, expand_binop_directly): Likewise.
> 	(expand_twoval_unop, expand_twoval_binop): Likewise.
> 	(expand_unop_direct, emit_indirect_jump): Likewise.
> 	(emit_conditional_move, vector_compare_rtx): Likewise.
> 	(expand_vec_cond_expr, expand_val_compare_and_swap_1): Likewise.
> 	(expand_sync_operation, expand_sync_fetch_operation): Likewise.
> 	(expand_sync_lock_test_and_set): Likewise.
> 	(maybe_emit_unop_insn): Likewise.  Change icode to an insn_code.
> 	(emit_unop_insn): Likewise.
> 	(expand_copysign_absneg): Change icode to an insn_code.
> 	(create_convert_operand_from_type): New function.
> 	(maybe_legitimize_operand, maybe_legitimize_operands): Likewise.
> 	(maybe_gen_insn, maybe_expand_insn, maybe_expand_jump_insn): Likewise.
> 	(expand_insn, expand_jump_insn): Likewise.
> 	* config/i386/i386.md (setmem<mode>): Use nonmemory_operand rather
> 	than const_int_operand for operand 2.

Ok.  Watch out for other target problems this week.


r~

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-22 17:49         ` Richard Henderson
@ 2011-03-23 18:01           ` Anatoly Sokolov
  2011-03-24 10:04             ` Richard Sandiford
  2011-03-24 12:01           ` Andreas Krebbel
  1 sibling, 1 reply; 32+ messages in thread
From: Anatoly Sokolov @ 2011-03-23 18:01 UTC (permalink / raw)
  To: Richard Henderson, gcc-patches, patches, richard.sandiford; +Cc: Jeff Law, kazu

Hi.


From: "Richard Henderson" <rth@redhat.com>
Sent: Tuesday, March 22, 2011 8:48 PM
>
> Ok.  Watch out for other target problems this week.
>
>


This patch casue ICE on H8300 target:

make[4]: Entering directory 
`/home/aesok/h83001/build/h8300-elf/h8300h/libgcc'
# If this is the top-level multilib, build all the other
# multilibs.
/home/aesok/h83001/build/./gcc/xgcc -B/home/aesok/h83001/build/./gcc/ -nostdinc
-B/home/aesok/h83001/build/h8300-elf/newlib/ -isystem 
/home/aesok/h83001/build/h
8300-elf/newlib/targ-include -isystem 
/home/aesok/h83001/combined/newlib/libc/in
clude -B/home/aesok/cross-local/h8300-elf/h8300-elf/bin/ -B/home/aesok/cross-loc
al/h8300-elf/h8300-elf/lib/ -isystem 
/home/aesok/cross-local/h8300-elf/h8300-elf
/include -isystem 
/home/aesok/cross-local/h8300-elf/h8300-elf/sys-include -L/hom
e/aesok/h83001/build/./ld    -g -O2 -mh -O2  -g -O2 -DIN_GCC -DCROSS_DIRECTORY_S
TRUCTURE  -W -Wall -Wwrite-strings -Wcast-qual -Wstrict-prototypes -Wmissing-pro
totypes -Wold-style-definition  -isystem 
./include  -DDF=SF -g  -DIN_LIBGCC2 -D_
_GCC_FLOAT_NOT_NEEDED -fno-stack-protector -Dinhibit_libc  -I. -I. -I../../.././
gcc -I../../../../combined/libgcc -I../../../../combined/libgcc/. -I../../../../
combined/libgcc/../gcc -I../../../../combined/libgcc/../include  -DHAVE_CC_TLS 
 -
DUSE_EMUTLS -o unwind-sjlj.o -MT unwind-sjlj.o -MD -MP -MF 
unwind-sjlj.dep -fexc
eptions -c ../../../../combined/libgcc/../gcc/unwind-sjlj.c
../../../../combined/libgcc/../gcc/unwind-sjlj.c: In function 
'uw_install_contex
t.isra___1':
../../../../combined/libgcc/../gcc/unwind-sjlj.c:306:11: internal compiler 
error
: in expand_jump_insn, at optabs.c:7181
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://gcc.gnu.org/bugs.html> for instructions.
make[4]: *** [unwind-sjlj.o] Error 1
make[4]: Leaving directory 
`/home/aesok/h83001/build/h8300-elf/h8300h/libgcc'
make[3]: *** [multi-do] Error 1
make[3]: Leaving directory `/home/aesok/h83001/build/h8300-elf/libgcc'
make[2]: *** [all-multi] Error 2
make[2]: Leaving directory `/home/aesok/h83001/build/h8300-elf/libgcc'
make[1]: *** [all-target-libgcc] Error 2
make[1]: Leaving directory `/home/aesok/h83001/build'

Anatoly. 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-23 18:01           ` Anatoly Sokolov
@ 2011-03-24 10:04             ` Richard Sandiford
  0 siblings, 0 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-24 10:04 UTC (permalink / raw)
  To: Anatoly Sokolov; +Cc: Richard Henderson, gcc-patches, patches, Jeff Law, kazu

"Anatoly Sokolov" <aesok@post.ru> writes:
> This patch casue ICE on H8300 target:

This is due to jump_address_operand checking the wrong mode.
The predicate is:

(define_predicate "jump_address_operand"
  (match_code "reg,mem")
{
  if (GET_CODE (op) == REG)
    return mode == Pmode;
  [...]
}

but "mode" is the mode passed to the predicate, not the mode of OP.
The indirect_jump pattern is:

(define_expand "indirect_jump"
  [(set (pc) (match_operand 0 "jump_address_operand" ""))]
  ""
  "")

which says that VOIDmode should be passed to jump_address_operand,
so all registers end up being rejected.

I've applied the following as the obvious fix.  You then hit bug
48263 ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48263 );
I'm testing a fix for that now.

Richard


gcc/
	* config/h8300/predicates.md (jump_address_operand): Fix register
	mode check.

Index: gcc/config/h8300/predicates.md
===================================================================
--- gcc/config/h8300/predicates.md	2011-01-05 15:12:08.000000000 +0000
+++ gcc/config/h8300/predicates.md	2011-03-24 09:20:15.000000000 +0000
@@ -259,7 +259,7 @@ (define_predicate "jump_address_operand"
   (match_code "reg,mem")
 {
   if (GET_CODE (op) == REG)
-    return mode == Pmode;
+    return GET_MODE (op) == Pmode;
 
   if (GET_CODE (op) == MEM)
     {

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-22 17:49         ` Richard Henderson
  2011-03-23 18:01           ` Anatoly Sokolov
@ 2011-03-24 12:01           ` Andreas Krebbel
  2011-03-24 12:13             ` Richard Sandiford
  2011-03-25 12:58             ` Georg-Johann Lay
  1 sibling, 2 replies; 32+ messages in thread
From: Andreas Krebbel @ 2011-03-24 12:01 UTC (permalink / raw)
  Cc: gcc-patches, richard.sandiford

On 03/22/2011 06:48 PM, Richard Henderson wrote:

> Ok.  Watch out for other target problems this week.

This unfortunately broke bootstrap on s390.

An unrecognizable insns is generated:

(insn 22 21 23 4 (set (reg/v:DI 44 [ end])
   (mult:DI (sign_extend:DI (mem/s/j:SI (plus:SI (reg/v/f:SI 47 [ foo])
                        (const_int 4 [0x4])) [0 foo_5(D)->incr_ull+4 S4 A32]))
            (sign_extend:DI (subreg:SI (reg:DI 49) 4)))) t.c:12 -1
     (nil))

The problem is that expand_binop_directly swaps the operands without rechecking the
predicates afterwards.  The old code did:

  /* Now, if insn's predicates don't allow our operands, put them into
     pseudo regs.  */

  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
      && mode0 != VOIDmode)
    xop0 = copy_to_mode_reg (mode0, xop0);

  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
      && mode1 != VOIDmode)
    xop1 = copy_to_mode_reg (mode1, xop1);

Right after swapping the operands.

Unfortunately it cannot be simply fixed by swapping the operands in the back end pattern.
 Since subreg and reg have different operand precedences. A subreg usually will be second
after a mem while a reg (having same precedence as mem) might be first operand before a mem.

Just copying the pre-patch behaviour fixes the problem for me:

Index: gcc/optabs.c
===================================================================
*** gcc/optabs.c.orig   2011-03-24 12:54:31.000000000 +0100
--- gcc/optabs.c        2011-03-24 12:54:43.000000000 +0100
*************** expand_binop_directly (enum machine_mode
*** 1308,1313 ****
--- 1308,1322 ----
          ops[1].value = swap;
        }

+       /* Now, if insn's predicates don't allow our operands, put them into
+        pseudo regs.  */
+
+       if (!insn_operand_matches (icode, 1, ops[1].value))
+       ops[1].value = copy_to_mode_reg (mode0, ops[1].value);
+
+       if (!insn_operand_matches (icode, 2, ops[2].value))
+       ops[2].value = copy_to_mode_reg (mode1, ops[2].value);
+
        pat = GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value);
        if (pat)
        {

Ok?

Bye,

-Andreas-

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-24 12:01           ` Andreas Krebbel
@ 2011-03-24 12:13             ` Richard Sandiford
  2011-03-24 15:09               ` Richard Sandiford
                                 ` (2 more replies)
  2011-03-25 12:58             ` Georg-Johann Lay
  1 sibling, 3 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-24 12:13 UTC (permalink / raw)
  To: Andreas Krebbel; +Cc: gcc-patches, rth

Andreas Krebbel <krebbel@linux.vnet.ibm.com> writes:
> On 03/22/2011 06:48 PM, Richard Henderson wrote:
>
>> Ok.  Watch out for other target problems this week.
>
> This unfortunately broke bootstrap on s390.

This is PR 48263.  Since it seems to be affecting several targets,
and since my bootstrap seems to be taking a looong time, I'll post
the patch here before testing has finished.

> Just copying the pre-patch behaviour fixes the problem for me:

I think we need to undo more of the patch, and leave the conversion
outside of the new interface.

Sorry for the breakage.

Richard


gcc/
	PR rtl-optimization/48263
	* optabs.c (expand_binop_directly): Reinstate convert_modes code
	and original commutative_p handling.  Use maybe_gen_insn.

Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	2011-03-24 09:18:00.000000000 +0000
+++ gcc/optabs.c	2011-03-24 09:36:46.000000000 +0000
@@ -1269,6 +1269,38 @@ expand_binop_directly (enum machine_mode
   if (!shift_optab_p (binoptab))
     xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
 
+  /* In case the insn wants input operands in modes different from
+     those of the actual operands, convert the operands.  It would
+     seem that we don't need to convert CONST_INTs, but we do, so
+     that they're properly zero-extended, sign-extended or truncated
+     for their mode.  */
+
+  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
+    xop0 = convert_modes (mode0,
+			  GET_MODE (xop0) != VOIDmode
+			  ? GET_MODE (xop0)
+			  : mode,
+			  xop0, unsignedp);
+
+  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
+    xop1 = convert_modes (mode1,
+			  GET_MODE (xop1) != VOIDmode
+			  ? GET_MODE (xop1)
+			  : mode,
+			  xop1, unsignedp);
+
+  /* If operation is commutative,
+     try to make the first operand a register.
+     Even better, try to make it the same as the target.
+     Also try to make the last operand a constant.  */
+  if (commutative_p
+      && swap_commutative_operands_with_target (target, xop0, xop1))
+    {
+      swap = xop1;
+      xop1 = xop0;
+      xop0 = swap;
+    }
+
   /* Now, if insn's predicates don't allow our operands, put them into
      pseudo regs.  */
 
@@ -1291,41 +1323,25 @@ expand_binop_directly (enum machine_mode
     tmp_mode = mode;
 
   create_output_operand (&ops[0], target, tmp_mode);
-  create_convert_operand_from (&ops[1], xop0, mode, unsignedp);
-  create_convert_operand_from (&ops[2], xop1, mode, unsignedp);
-  if (maybe_legitimize_operands (icode, 0, 3, ops))
-    {
-      /* If operation is commutative,
-	 try to make the first operand a register.
-	 Even better, try to make it the same as the target.
-	 Also try to make the last operand a constant.  */
-      if (commutative_p
-	  && swap_commutative_operands_with_target (ops[0].value, ops[1].value,
-						    ops[2].value))
-	{
-	  swap = ops[2].value;
-	  ops[2].value = ops[1].value;
-	  ops[1].value = swap;
-	}
-
-      pat = GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value);
-      if (pat)
+  create_input_operand (&ops[1], xop0, mode0);
+  create_input_operand (&ops[2], xop1, mode1);
+  pat = maybe_gen_insn (icode, 3, ops);
+  if (pat)
+    {
+      /* If PAT is composed of more than one insn, try to add an appropriate
+	 REG_EQUAL note to it.  If we can't because TEMP conflicts with an
+	 operand, call expand_binop again, this time without a target.  */
+      if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
+	  && ! add_equal_note (pat, ops[0].value, binoptab->code,
+			       ops[1].value, ops[2].value))
 	{
-	  /* If PAT is composed of more than one insn, try to add an appropriate
-	     REG_EQUAL note to it.  If we can't because TEMP conflicts with an
-	     operand, call expand_binop again, this time without a target.  */
-	  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-	      && ! add_equal_note (pat, ops[0].value, binoptab->code,
-				   ops[1].value, ops[2].value))
-	    {
-	      delete_insns_since (last);
-	      return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
-				   unsignedp, methods);
-	    }
-
-	  emit_insn (pat);
-	  return ops[0].value;
+	  delete_insns_since (last);
+	  return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
+			       unsignedp, methods);
 	}
+
+      emit_insn (pat);
+      return ops[0].value;
     }
   delete_insns_since (last);
   return NULL_RTX;

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-24 12:13             ` Richard Sandiford
@ 2011-03-24 15:09               ` Richard Sandiford
  2011-03-24 17:09               ` Richard Henderson
  2011-03-29 12:26               ` Mikael Pettersson
  2 siblings, 0 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-24 15:09 UTC (permalink / raw)
  To: Andreas Krebbel; +Cc: gcc-patches, rth

Richard Sandiford <richard.sandiford@linaro.org> writes:
> Andreas Krebbel <krebbel@linux.vnet.ibm.com> writes:
>> On 03/22/2011 06:48 PM, Richard Henderson wrote:
>>
>>> Ok.  Watch out for other target problems this week.
>>
>> This unfortunately broke bootstrap on s390.
>
> This is PR 48263.  Since it seems to be affecting several targets,
> and since my bootstrap seems to be taking a looong time, I'll post
> the patch here before testing has finished.

Bootstrap & regression-test on x86_64-linux-gnu now finished.  OK to install?

>> Just copying the pre-patch behaviour fixes the problem for me:
>
> I think we need to undo more of the patch, and leave the conversion
> outside of the new interface.
>
> Sorry for the breakage.
>
> Richard
>
>
> gcc/
> 	PR rtl-optimization/48263
> 	* optabs.c (expand_binop_directly): Reinstate convert_modes code
> 	and original commutative_p handling.  Use maybe_gen_insn.
>
> Index: gcc/optabs.c
> ===================================================================
> --- gcc/optabs.c	2011-03-24 09:18:00.000000000 +0000
> +++ gcc/optabs.c	2011-03-24 09:36:46.000000000 +0000
> @@ -1269,6 +1269,38 @@ expand_binop_directly (enum machine_mode
>    if (!shift_optab_p (binoptab))
>      xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
>  
> +  /* In case the insn wants input operands in modes different from
> +     those of the actual operands, convert the operands.  It would
> +     seem that we don't need to convert CONST_INTs, but we do, so
> +     that they're properly zero-extended, sign-extended or truncated
> +     for their mode.  */
> +
> +  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
> +    xop0 = convert_modes (mode0,
> +			  GET_MODE (xop0) != VOIDmode
> +			  ? GET_MODE (xop0)
> +			  : mode,
> +			  xop0, unsignedp);
> +
> +  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
> +    xop1 = convert_modes (mode1,
> +			  GET_MODE (xop1) != VOIDmode
> +			  ? GET_MODE (xop1)
> +			  : mode,
> +			  xop1, unsignedp);
> +
> +  /* If operation is commutative,
> +     try to make the first operand a register.
> +     Even better, try to make it the same as the target.
> +     Also try to make the last operand a constant.  */
> +  if (commutative_p
> +      && swap_commutative_operands_with_target (target, xop0, xop1))
> +    {
> +      swap = xop1;
> +      xop1 = xop0;
> +      xop0 = swap;
> +    }
> +
>    /* Now, if insn's predicates don't allow our operands, put them into
>       pseudo regs.  */
>  
> @@ -1291,41 +1323,25 @@ expand_binop_directly (enum machine_mode
>      tmp_mode = mode;
>  
>    create_output_operand (&ops[0], target, tmp_mode);
> -  create_convert_operand_from (&ops[1], xop0, mode, unsignedp);
> -  create_convert_operand_from (&ops[2], xop1, mode, unsignedp);
> -  if (maybe_legitimize_operands (icode, 0, 3, ops))
> -    {
> -      /* If operation is commutative,
> -	 try to make the first operand a register.
> -	 Even better, try to make it the same as the target.
> -	 Also try to make the last operand a constant.  */
> -      if (commutative_p
> -	  && swap_commutative_operands_with_target (ops[0].value, ops[1].value,
> -						    ops[2].value))
> -	{
> -	  swap = ops[2].value;
> -	  ops[2].value = ops[1].value;
> -	  ops[1].value = swap;
> -	}
> -
> -      pat = GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value);
> -      if (pat)
> +  create_input_operand (&ops[1], xop0, mode0);
> +  create_input_operand (&ops[2], xop1, mode1);
> +  pat = maybe_gen_insn (icode, 3, ops);
> +  if (pat)
> +    {
> +      /* If PAT is composed of more than one insn, try to add an appropriate
> +	 REG_EQUAL note to it.  If we can't because TEMP conflicts with an
> +	 operand, call expand_binop again, this time without a target.  */
> +      if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
> +	  && ! add_equal_note (pat, ops[0].value, binoptab->code,
> +			       ops[1].value, ops[2].value))
>  	{
> -	  /* If PAT is composed of more than one insn, try to add an appropriate
> -	     REG_EQUAL note to it.  If we can't because TEMP conflicts with an
> -	     operand, call expand_binop again, this time without a target.  */
> -	  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
> -	      && ! add_equal_note (pat, ops[0].value, binoptab->code,
> -				   ops[1].value, ops[2].value))
> -	    {
> -	      delete_insns_since (last);
> -	      return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
> -				   unsignedp, methods);
> -	    }
> -
> -	  emit_insn (pat);
> -	  return ops[0].value;
> +	  delete_insns_since (last);
> +	  return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
> +			       unsignedp, methods);
>  	}
> +
> +      emit_insn (pat);
> +      return ops[0].value;
>      }
>    delete_insns_since (last);
>    return NULL_RTX;

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-22 15:09       ` Richard Sandiford
  2011-03-22 17:49         ` Richard Henderson
@ 2011-03-24 15:14         ` Richard Sandiford
  1 sibling, 0 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-24 15:14 UTC (permalink / raw)
  To: gcc-patches

This showed up as a warning during the h8300 build I did this morning.
I've no idea why it wasn't caught during the x86_64, ARM and MIPS testing.

Bootstrapped & regression-tested on x86_64-linux-gnu.  Installed as obvious.

Richard


gcc/
	* builtins.c (expand_movstr): Fix endp == 1 adjustment after
	last commit.

Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	2011-03-23 09:30:17.000000000 +0000
+++ gcc/builtins.c	2011-03-24 09:08:05.000000000 +0000
@@ -3655,7 +3655,6 @@ expand_builtin_mempcpy_args (tree dest, 
 expand_movstr (tree dest, tree src, rtx target, int endp)
 {
   struct expand_operand ops[3];
-  rtx end;
   rtx dest_mem;
   rtx src_mem;
 
@@ -3683,7 +3682,7 @@ expand_movstr (tree dest, tree src, rtx 
 	 adjust it.  */
       if (endp == 1)
 	{
-	  rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1);
+	  rtx tem = plus_constant (gen_lowpart (GET_MODE (target), target), 1);
 	  emit_move_insn (target, force_operand (tem, NULL_RTX));
 	}
     }

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-24 12:13             ` Richard Sandiford
  2011-03-24 15:09               ` Richard Sandiford
@ 2011-03-24 17:09               ` Richard Henderson
  2011-03-29 12:26               ` Mikael Pettersson
  2 siblings, 0 replies; 32+ messages in thread
From: Richard Henderson @ 2011-03-24 17:09 UTC (permalink / raw)
  To: Andreas Krebbel, gcc-patches, richard.sandiford

On 03/24/2011 05:13 AM, Richard Sandiford wrote:
> gcc/
> 	PR rtl-optimization/48263
> 	* optabs.c (expand_binop_directly): Reinstate convert_modes code
> 	and original commutative_p handling.  Use maybe_gen_insn.

Ok.


r~

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-24 12:01           ` Andreas Krebbel
  2011-03-24 12:13             ` Richard Sandiford
@ 2011-03-25 12:58             ` Georg-Johann Lay
  2011-03-25 13:00               ` Georg-Johann Lay
  2011-03-25 17:35               ` Richard Henderson
  1 sibling, 2 replies; 32+ messages in thread
From: Georg-Johann Lay @ 2011-03-25 12:58 UTC (permalink / raw)
  To: Andreas Krebbel; +Cc: gcc-patches, richard.sandiford, Richard Henderson

[-- Attachment #1: Type: text/plain, Size: 721 bytes --]

Andreas Krebbel schrieb:
> On 03/22/2011 06:48 PM, Richard Henderson wrote:
> 
>> Ok.  Watch out for other target problems this week.

libgcc fails to build for avr (SVN 171446)

../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c: In function
'__negdi2':
../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c:68:17:
internal compiler error: in maybe_gen_insn, at  optabs.c:7123

Target: avr
Configured with: ../../gcc.gnu.org/trunk/configure --target=avr
--prefix=/local/gnu/install/gcc-4.6 --enable-languages=c,c++
--disable-libssp --disable-libada --disable-nls --disable-shared

Attached the source where it crashes; compile with -S -Os

Building last 4.7 snapshot + avr backend from HEAD works fine.

Johann

[-- Attachment #2: libgcc2-negdi2.c --]
[-- Type: text/x-csrc, Size: 28479 bytes --]























typedef int ptrdiff_t;

typedef unsigned int size_t;

typedef int wchar_t;







extern void *malloc (size_t);



extern void free (void *);



extern int atexit (void (*)(void));



extern void abort (void) __attribute__ ((__noreturn__));



extern size_t strlen (const char *);



extern void *memcpy (void *, const void *, size_t);



extern void *memset (void *, int, size_t);



extern int filename_cmp (const char *s1, const char *s2);


extern int filename_ncmp (const char *s1, const char *s2,
     size_t n);




struct _dont_use_rtx_here_;
struct _dont_use_rtvec_here_;
union _dont_use_tree_here_;











enum debug_info_type
{
  NO_DEBUG,
  DBX_DEBUG,
  SDB_DEBUG,
  DWARF2_DEBUG,
  XCOFF_DEBUG,
  VMS_DEBUG,
  VMS_AND_DWARF2_DEBUG

};

enum debug_info_levels
{
  DINFO_LEVEL_NONE,
  DINFO_LEVEL_TERSE,
  DINFO_LEVEL_NORMAL,
  DINFO_LEVEL_VERBOSE
};

enum debug_info_usage
{
  DINFO_USAGE_DFN,
  DINFO_USAGE_DIR_USE,
  DINFO_USAGE_IND_USE,
  DINFO_USAGE_NUM_ENUMS
};

enum debug_struct_file
{
  DINFO_STRUCT_FILE_NONE,
  DINFO_STRUCT_FILE_BASE,

  DINFO_STRUCT_FILE_SYS,

  DINFO_STRUCT_FILE_ANY
};





enum symbol_visibility
{
  VISIBILITY_DEFAULT,
  VISIBILITY_PROTECTED,
  VISIBILITY_HIDDEN,
  VISIBILITY_INTERNAL
};



enum ira_algorithm
{
  IRA_ALGORITHM_CB,
  IRA_ALGORITHM_PRIORITY
};


enum ira_region
{
  IRA_REGION_ONE,
  IRA_REGION_ALL,
  IRA_REGION_MIXED
};


enum excess_precision
{
  EXCESS_PRECISION_DEFAULT,
  EXCESS_PRECISION_FAST,
  EXCESS_PRECISION_STANDARD
};


enum graph_dump_types
{
  no_graph = 0,
  vcg
};


enum stack_check_type
{

  NO_STACK_CHECK = 0,



  GENERIC_STACK_CHECK,




  STATIC_BUILTIN_STACK_CHECK,



  FULL_BUILTIN_STACK_CHECK
};




enum warn_strict_overflow_code
{



  WARN_STRICT_OVERFLOW_ALL = 1,




  WARN_STRICT_OVERFLOW_CONDITIONAL = 2,


  WARN_STRICT_OVERFLOW_COMPARISON = 3,


  WARN_STRICT_OVERFLOW_MISC = 4,


  WARN_STRICT_OVERFLOW_MAGNITUDE = 5
};


enum fp_contract_mode {
  FP_CONTRACT_OFF = 0,
  FP_CONTRACT_ON = 1,
  FP_CONTRACT_FAST = 2
};


enum vect_verbosity_levels {
  REPORT_NONE,
  REPORT_VECTORIZED_LOCATIONS,
  REPORT_UNVECTORIZED_LOCATIONS,
  REPORT_COST,
  REPORT_ALIGNMENT,
  REPORT_DR_DETAILS,
  REPORT_BAD_FORM_LOOPS,
  REPORT_OUTER_LOOPS,
  REPORT_SLP,
  REPORT_DETAILS,

  MAX_VERBOSITY_LEVEL
};


enum opt_code
{
  OPT____ = 0,

  OPT__help = 27,
  OPT__help_ = 28,

  OPT__output_pch_ = 58,

  OPT__param = 60,

  OPT__sysroot_ = 90,
  OPT__target_help = 91,

  OPT__version = 101,


  OPT_A = 104,
  OPT_B = 105,
  OPT_C = 106,
  OPT_CC = 107,
  OPT_D = 108,
  OPT_E = 109,
  OPT_F = 110,
  OPT_H = 111,
  OPT_I = 112,
  OPT_J = 113,
  OPT_L = 114,
  OPT_M = 115,
  OPT_MD = 116,
  OPT_MF = 117,
  OPT_MG = 118,
  OPT_MM = 119,
  OPT_MMD = 120,
  OPT_MP = 121,
  OPT_MQ = 122,
  OPT_MT = 123,
  OPT_N = 124,
  OPT_O = 125,
  OPT_Ofast = 126,
  OPT_Os = 127,
  OPT_P = 128,
  OPT_Q = 129,
  OPT_Qn = 130,
  OPT_Qy = 131,
  OPT_R = 132,
  OPT_S = 133,
  OPT_T = 134,
  OPT_Tbss = 135,
  OPT_Tdata = 136,
  OPT_Ttext = 137,
  OPT_U = 138,

  OPT_Wa_ = 140,
  OPT_Wabi = 141,
  OPT_Waddress = 142,
  OPT_Waggregate_return = 143,
  OPT_Waliasing = 144,
  OPT_Walign_commons = 145,
  OPT_Wall = 146,
  OPT_Wampersand = 147,
  OPT_Warray_bounds = 148,
  OPT_Warray_temporaries = 149,
  OPT_Wassign_intercept = 150,
  OPT_Wattributes = 151,
  OPT_Wbad_function_cast = 152,
  OPT_Wbuiltin_macro_redefined = 153,
  OPT_Wc___compat = 154,
  OPT_Wc__0x_compat = 155,
  OPT_Wcast_align = 156,
  OPT_Wcast_qual = 157,
  OPT_Wchar_subscripts = 158,
  OPT_Wcharacter_truncation = 159,
  OPT_Wclobbered = 160,
  OPT_Wcomment = 161,

  OPT_Wconversion = 163,
  OPT_Wconversion_extra = 164,
  OPT_Wconversion_null = 165,
  OPT_Wcoverage_mismatch = 166,
  OPT_Wcpp = 167,
  OPT_Wctor_dtor_privacy = 168,
  OPT_Wdeclaration_after_statement = 169,
  OPT_Wdeprecated = 170,
  OPT_Wdeprecated_declarations = 171,
  OPT_Wdisabled_optimization = 172,
  OPT_Wdiv_by_zero = 173,
  OPT_Wdouble_promotion = 174,
  OPT_Weffc__ = 175,
  OPT_Wempty_body = 176,
  OPT_Wendif_labels = 177,
  OPT_Wenum_compare = 178,
  OPT_Werror = 179,

  OPT_Werror_ = 181,
  OPT_Wextra = 182,
  OPT_Wfatal_errors = 183,
  OPT_Wfloat_equal = 184,
  OPT_Wformat = 185,
  OPT_Wformat_contains_nul = 186,
  OPT_Wformat_extra_args = 187,
  OPT_Wformat_nonliteral = 188,
  OPT_Wformat_security = 189,
  OPT_Wformat_y2k = 190,
  OPT_Wformat_zero_length = 191,
  OPT_Wformat_ = 192,
  OPT_Wframe_larger_than_ = 193,
  OPT_Wignored_qualifiers = 194,
  OPT_Wimplicit = 195,
  OPT_Wimplicit_function_declaration = 196,
  OPT_Wimplicit_int = 197,
  OPT_Wimplicit_interface = 198,
  OPT_Wimplicit_procedure = 199,

  OPT_Winit_self = 201,
  OPT_Winline = 202,
  OPT_Wint_to_pointer_cast = 203,
  OPT_Wintrinsic_shadow = 204,
  OPT_Wintrinsics_std = 205,
  OPT_Winvalid_offsetof = 206,
  OPT_Winvalid_pch = 207,
  OPT_Wjump_misses_init = 208,
  OPT_Wl_ = 209,

  OPT_Wlarger_than_ = 211,
  OPT_Wline_truncation = 212,
  OPT_Wlogical_op = 213,
  OPT_Wlong_long = 214,
  OPT_Wmain = 215,
  OPT_Wmissing_braces = 216,
  OPT_Wmissing_declarations = 217,
  OPT_Wmissing_field_initializers = 218,
  OPT_Wmissing_format_attribute = 219,
  OPT_Wmissing_include_dirs = 220,
  OPT_Wmissing_noreturn = 221,
  OPT_Wmissing_parameter_type = 222,
  OPT_Wmissing_prototypes = 223,
  OPT_Wmudflap = 224,
  OPT_Wmultichar = 225,
  OPT_Wnested_externs = 226,
  OPT_Wnoexcept = 227,
  OPT_Wnon_template_friend = 228,
  OPT_Wnon_virtual_dtor = 229,
  OPT_Wnonnull = 230,
  OPT_Wnormalized_ = 231,
  OPT_Wold_style_cast = 232,
  OPT_Wold_style_declaration = 233,
  OPT_Wold_style_definition = 234,
  OPT_Woverflow = 235,
  OPT_Woverlength_strings = 236,
  OPT_Woverloaded_virtual = 237,
  OPT_Woverride_init = 238,
  OPT_Wp_ = 239,
  OPT_Wpacked = 240,
  OPT_Wpacked_bitfield_compat = 241,
  OPT_Wpadded = 242,
  OPT_Wparentheses = 243,
  OPT_Wpmf_conversions = 244,
  OPT_Wpointer_arith = 245,
  OPT_Wpointer_sign = 246,
  OPT_Wpointer_to_int_cast = 247,
  OPT_Wpragmas = 248,
  OPT_Wproperty_assign_default = 249,
  OPT_Wprotocol = 250,
  OPT_Wpsabi = 251,
  OPT_Wredundant_decls = 252,
  OPT_Wreorder = 253,
  OPT_Wreturn_type = 254,
  OPT_Wselector = 255,
  OPT_Wsequence_point = 256,
  OPT_Wshadow = 257,
  OPT_Wsign_compare = 258,
  OPT_Wsign_conversion = 259,
  OPT_Wsign_promo = 260,
  OPT_Wstack_protector = 261,
  OPT_Wstrict_aliasing = 262,
  OPT_Wstrict_aliasing_ = 263,
  OPT_Wstrict_null_sentinel = 264,
  OPT_Wstrict_overflow = 265,
  OPT_Wstrict_overflow_ = 266,
  OPT_Wstrict_prototypes = 267,
  OPT_Wstrict_selector_match = 268,
  OPT_Wsuggest_attribute_const = 269,
  OPT_Wsuggest_attribute_noreturn = 270,
  OPT_Wsuggest_attribute_pure = 271,
  OPT_Wsurprising = 272,
  OPT_Wswitch = 273,
  OPT_Wswitch_default = 274,
  OPT_Wswitch_enum = 275,
  OPT_Wsync_nand = 276,
  OPT_Wsynth = 277,
  OPT_Wsystem_headers = 278,
  OPT_Wtabs = 279,
  OPT_Wtraditional = 280,
  OPT_Wtraditional_conversion = 281,
  OPT_Wtrampolines = 282,
  OPT_Wtrigraphs = 283,
  OPT_Wtype_limits = 284,
  OPT_Wundeclared_selector = 285,
  OPT_Wundef = 286,
  OPT_Wunderflow = 287,
  OPT_Wuninitialized = 288,
  OPT_Wunknown_pragmas = 289,

  OPT_Wunsafe_loop_optimizations = 291,
  OPT_Wunsuffixed_float_constants = 292,
  OPT_Wunused = 293,
  OPT_Wunused_but_set_parameter = 294,
  OPT_Wunused_but_set_variable = 295,
  OPT_Wunused_dummy_argument = 296,
  OPT_Wunused_function = 297,
  OPT_Wunused_label = 298,
  OPT_Wunused_macros = 299,
  OPT_Wunused_parameter = 300,
  OPT_Wunused_result = 301,
  OPT_Wunused_value = 302,
  OPT_Wunused_variable = 303,
  OPT_Wvariadic_macros = 304,
  OPT_Wvla = 305,
  OPT_Wvolatile_register_var = 306,
  OPT_Wwrite_strings = 307,
  OPT_Xassembler = 308,
  OPT_Xlinker = 309,
  OPT_Xpreprocessor = 310,
  OPT_Z = 311,
  OPT_ansi = 312,
  OPT_aux_info = 313,

  OPT_auxbase = 315,
  OPT_auxbase_strip = 316,
  OPT_c = 317,
  OPT_coverage = 318,
  OPT_cpp = 319,
  OPT_cpp_ = 320,
  OPT_d = 321,
  OPT_dumpbase = 322,
  OPT_dumpdir = 323,
  OPT_dumpmachine = 324,
  OPT_dumpspecs = 325,
  OPT_dumpversion = 326,
  OPT_e = 327,
  OPT_export_dynamic = 328,
  OPT_fPIC = 329,
  OPT_fPIE = 330,
  OPT_fabi_version_ = 331,
  OPT_faccess_control = 332,
  OPT_faggressive_function_elimination = 333,
  OPT_falign_commons = 334,
  OPT_falign_functions = 335,
  OPT_falign_functions_ = 336,
  OPT_falign_jumps = 337,
  OPT_falign_jumps_ = 338,
  OPT_falign_labels = 339,
  OPT_falign_labels_ = 340,
  OPT_falign_loops = 341,
  OPT_falign_loops_ = 342,
  OPT_fall_intrinsics = 343,

  OPT_fallow_leading_underscore = 345,





  OPT_fasm = 351,
  OPT_fassociative_math = 352,
  OPT_fasynchronous_unwind_tables = 353,
  OPT_fauto_inc_dec = 354,
  OPT_fautomatic = 355,
  OPT_fbackslash = 356,
  OPT_fbacktrace = 357,
  OPT_fblas_matmul_limit_ = 358,
  OPT_fbounds_check = 359,
  OPT_fbranch_count_reg = 360,
  OPT_fbranch_probabilities = 361,
  OPT_fbranch_target_load_optimize = 362,
  OPT_fbranch_target_load_optimize2 = 363,
  OPT_fbtr_bb_exclusive = 364,
  OPT_fbuiltin = 365,
  OPT_fbuiltin_ = 366,
  OPT_fcall_saved_ = 367,
  OPT_fcall_used_ = 368,
  OPT_fcaller_saves = 369,
  OPT_fcheck_array_temporaries = 370,
  OPT_fcheck_data_deps = 371,
  OPT_fcheck_new = 372,
  OPT_fcheck_ = 373,
  OPT_fcoarray_ = 374,
  OPT_fcombine_stack_adjustments = 375,
  OPT_fcommon = 376,
  OPT_fcompare_debug = 377,
  OPT_fcompare_debug_second = 378,
  OPT_fcompare_debug_ = 379,
  OPT_fcompare_elim = 380,
  OPT_fcond_mismatch = 381,
  OPT_fconserve_space = 382,
  OPT_fconserve_stack = 383,
  OPT_fconstant_string_class_ = 384,
  OPT_fconstexpr_depth_ = 385,
  OPT_fconvert_big_endian = 386,
  OPT_fconvert_little_endian = 387,
  OPT_fconvert_native = 388,
  OPT_fconvert_swap = 389,
  OPT_fcprop_registers = 390,
  OPT_fcray_pointer = 391,
  OPT_fcrossjumping = 392,
  OPT_fcse_follow_jumps = 393,

  OPT_fcx_fortran_rules = 395,
  OPT_fcx_limited_range = 396,
  OPT_fd_lines_as_code = 397,
  OPT_fd_lines_as_comments = 398,
  OPT_fdata_sections = 399,
  OPT_fdbg_cnt_list = 400,
  OPT_fdbg_cnt_ = 401,
  OPT_fdce = 402,
  OPT_fdebug_prefix_map_ = 403,
  OPT_fdeduce_init_list = 404,
  OPT_fdefault_double_8 = 405,

  OPT_fdefault_integer_8 = 407,
  OPT_fdefault_real_8 = 408,
  OPT_fdefer_pop = 409,
  OPT_fdelayed_branch = 410,
  OPT_fdelete_null_pointer_checks = 411,
  OPT_fdevirtualize = 412,
  OPT_fdiagnostics_show_location_ = 413,
  OPT_fdiagnostics_show_option = 414,
  OPT_fdirectives_only = 415,
  OPT_fdollar_ok = 416,
  OPT_fdollars_in_identifiers = 417,
  OPT_fdse = 418,
  OPT_fdump_ = 419,
  OPT_fdump_core = 420,
  OPT_fdump_final_insns = 421,
  OPT_fdump_final_insns_ = 422,
  OPT_fdump_fortran_optimized = 423,
  OPT_fdump_fortran_original = 424,
  OPT_fdump_go_spec_ = 425,
  OPT_fdump_noaddr = 426,
  OPT_fdump_parse_tree = 427,
  OPT_fdump_unnumbered = 428,
  OPT_fdump_unnumbered_links = 429,
  OPT_fdwarf2_cfi_asm = 430,
  OPT_fearly_inlining = 431,
  OPT_felide_constructors = 432,
  OPT_feliminate_dwarf2_dups = 433,
  OPT_feliminate_unused_debug_symbols = 434,
  OPT_feliminate_unused_debug_types = 435,
  OPT_femit_class_debug_always = 436,
  OPT_femit_struct_debug_baseonly = 437,
  OPT_femit_struct_debug_detailed_ = 438,
  OPT_femit_struct_debug_reduced = 439,
  OPT_fenforce_eh_specs = 440,

  OPT_fexceptions = 442,
  OPT_fexcess_precision_ = 443,
  OPT_fexec_charset_ = 444,
  OPT_fexpensive_optimizations = 445,
  OPT_fextended_identifiers = 446,
  OPT_fexternal_blas = 447,

  OPT_ff2c = 449,
  OPT_ffast_math = 450,
  OPT_ffinite_math_only = 451,
  OPT_ffixed_ = 452,
  OPT_ffixed_form = 453,
  OPT_ffixed_line_length_ = 454,
  OPT_ffixed_line_length_none = 455,
  OPT_ffloat_store = 456,
  OPT_ffor_scope = 457,

  OPT_fforward_propagate = 459,
  OPT_ffp_contract_ = 460,
  OPT_ffpe_trap_ = 461,
  OPT_ffree_form = 462,
  OPT_ffree_line_length_ = 463,
  OPT_ffree_line_length_none = 464,
  OPT_ffreestanding = 465,
  OPT_ffriend_injection = 466,
  OPT_ffunction_cse = 467,
  OPT_ffunction_sections = 468,
  OPT_fgcse = 469,
  OPT_fgcse_after_reload = 470,
  OPT_fgcse_las = 471,
  OPT_fgcse_lm = 472,
  OPT_fgcse_sm = 473,
  OPT_fgnu_keywords = 474,
  OPT_fgnu_runtime = 475,
  OPT_fgnu89_inline = 476,
  OPT_fgraphite = 477,
  OPT_fgraphite_identity = 478,
  OPT_fguess_branch_probability = 479,





  OPT_fhosted = 485,

  OPT_fident = 487,
  OPT_fif_conversion = 488,
  OPT_fif_conversion2 = 489,
  OPT_fimplement_inlines = 490,
  OPT_fimplicit_inline_templates = 491,
  OPT_fimplicit_none = 492,
  OPT_fimplicit_templates = 493,
  OPT_findirect_inlining = 494,
  OPT_finhibit_size_directive = 495,
  OPT_finit_character_ = 496,
  OPT_finit_integer_ = 497,
  OPT_finit_local_zero = 498,
  OPT_finit_logical_ = 499,
  OPT_finit_real_ = 500,
  OPT_finline = 501,
  OPT_finline_functions = 502,
  OPT_finline_functions_called_once = 503,

  OPT_finline_limit_ = 505,
  OPT_finline_small_functions = 506,
  OPT_finput_charset_ = 507,
  OPT_finstrument_functions = 508,
  OPT_finstrument_functions_exclude_file_list_ = 509,
  OPT_finstrument_functions_exclude_function_list_ = 510,
  OPT_fintrinsic_modules_path = 511,
  OPT_fipa_cp = 512,
  OPT_fipa_cp_clone = 513,
  OPT_fipa_matrix_reorg = 514,
  OPT_fipa_profile = 515,
  OPT_fipa_pta = 516,
  OPT_fipa_pure_const = 517,
  OPT_fipa_reference = 518,
  OPT_fipa_sra = 519,

  OPT_fira_algorithm_ = 521,
  OPT_fira_loop_pressure = 522,
  OPT_fira_region_ = 523,
  OPT_fira_share_save_slots = 524,
  OPT_fira_share_spill_slots = 525,
  OPT_fira_verbose_ = 526,
  OPT_fivopts = 527,
  OPT_fjump_tables = 528,
  OPT_fkeep_inline_dllexport = 529,
  OPT_fkeep_inline_functions = 530,
  OPT_fkeep_static_consts = 531,

  OPT_flax_vector_conversions = 533,
  OPT_fleading_underscore = 534,
  OPT_floop_block = 535,
  OPT_floop_flatten = 536,
  OPT_floop_interchange = 537,

  OPT_floop_parallelize_all = 539,
  OPT_floop_strip_mine = 540,
  OPT_flto = 541,
  OPT_flto_compression_level_ = 542,
  OPT_flto_partition_1to1 = 543,
  OPT_flto_partition_balanced = 544,
  OPT_flto_partition_none = 545,
  OPT_flto_report = 546,
  OPT_flto_ = 547,
  OPT_fltrans = 548,
  OPT_fltrans_output_list_ = 549,
  OPT_fmath_errno = 550,
  OPT_fmax_array_constructor_ = 551,
  OPT_fmax_errors_ = 552,
  OPT_fmax_identifier_length_ = 553,
  OPT_fmax_stack_var_size_ = 554,
  OPT_fmax_subrecord_length_ = 555,
  OPT_fmem_report = 556,
  OPT_fmerge_all_constants = 557,
  OPT_fmerge_constants = 558,
  OPT_fmerge_debug_strings = 559,
  OPT_fmessage_length_ = 560,
  OPT_fmodule_private = 561,
  OPT_fmodulo_sched = 562,
  OPT_fmodulo_sched_allow_regmoves = 563,
  OPT_fmove_loop_invariants = 564,
  OPT_fms_extensions = 565,
  OPT_fmudflap = 566,
  OPT_fmudflapir = 567,
  OPT_fmudflapth = 568,


  OPT_fnext_runtime = 571,
  OPT_fnil_receivers = 572,
  OPT_fnon_call_exceptions = 573,
  OPT_fnonansi_builtins = 574,

  OPT_fnothrow_opt = 576,
  OPT_fobjc_abi_version_ = 577,
  OPT_fobjc_call_cxx_cdtors = 578,
  OPT_fobjc_direct_dispatch = 579,
  OPT_fobjc_exceptions = 580,
  OPT_fobjc_gc = 581,
  OPT_fobjc_nilcheck = 582,
  OPT_fobjc_sjlj_exceptions = 583,
  OPT_fobjc_std_objc1 = 584,
  OPT_fomit_frame_pointer = 585,
  OPT_fopenmp = 586,
  OPT_foperator_names = 587,
  OPT_foptimize_register_move = 588,
  OPT_foptimize_sibling_calls = 589,

  OPT_fpack_derived = 591,
  OPT_fpack_struct = 592,
  OPT_fpack_struct_ = 593,
  OPT_fpartial_inlining = 594,
  OPT_fpcc_struct_return = 595,
  OPT_fpch_deps = 596,
  OPT_fpch_preprocess = 597,
  OPT_fpeel_loops = 598,
  OPT_fpeephole = 599,
  OPT_fpeephole2 = 600,
  OPT_fpermissive = 601,
  OPT_fpic = 602,
  OPT_fpie = 603,
  OPT_fplan9_extensions = 604,
  OPT_fplugin_arg_ = 605,
  OPT_fplugin_ = 606,
  OPT_fpost_ipa_mem_report = 607,
  OPT_fpre_ipa_mem_report = 608,
  OPT_fpredictive_commoning = 609,
  OPT_fprefetch_loop_arrays = 610,
  OPT_fpreprocessed = 611,
  OPT_fpretty_templates = 612,
  OPT_fprofile = 613,
  OPT_fprofile_arcs = 614,
  OPT_fprofile_correction = 615,
  OPT_fprofile_dir_ = 616,
  OPT_fprofile_generate = 617,
  OPT_fprofile_generate_ = 618,
  OPT_fprofile_use = 619,
  OPT_fprofile_use_ = 620,
  OPT_fprofile_values = 621,
  OPT_fprotect_parens = 622,
  OPT_frandom_seed = 623,
  OPT_frandom_seed_ = 624,
  OPT_frange_check = 625,
  OPT_frealloc_lhs = 626,
  OPT_freciprocal_math = 627,
  OPT_frecord_gcc_switches = 628,
  OPT_frecord_marker_4 = 629,
  OPT_frecord_marker_8 = 630,
  OPT_frecursive = 631,
  OPT_freg_struct_return = 632,
  OPT_fregmove = 633,
  OPT_frename_registers = 634,
  OPT_freorder_blocks = 635,
  OPT_freorder_blocks_and_partition = 636,
  OPT_freorder_functions = 637,
  OPT_frepack_arrays = 638,
  OPT_freplace_objc_classes = 639,
  OPT_frepo = 640,
  OPT_frerun_cse_after_loop = 641,

  OPT_freschedule_modulo_scheduled_loops = 643,
  OPT_fresolution_ = 644,
  OPT_frounding_math = 645,
  OPT_frtti = 646,
  OPT_fsched_critical_path_heuristic = 647,
  OPT_fsched_dep_count_heuristic = 648,
  OPT_fsched_group_heuristic = 649,
  OPT_fsched_interblock = 650,
  OPT_fsched_last_insn_heuristic = 651,
  OPT_fsched_pressure = 652,
  OPT_fsched_rank_heuristic = 653,
  OPT_fsched_spec = 654,
  OPT_fsched_spec_insn_heuristic = 655,
  OPT_fsched_spec_load = 656,
  OPT_fsched_spec_load_dangerous = 657,
  OPT_fsched_stalled_insns = 658,
  OPT_fsched_stalled_insns_dep = 659,
  OPT_fsched_stalled_insns_dep_ = 660,
  OPT_fsched_stalled_insns_ = 661,
  OPT_fsched_verbose_ = 662,
  OPT_fsched2_use_superblocks = 663,

  OPT_fschedule_insns = 665,
  OPT_fschedule_insns2 = 666,
  OPT_fsecond_underscore = 667,
  OPT_fsection_anchors = 668,

  OPT_fsel_sched_pipelining = 670,
  OPT_fsel_sched_pipelining_outer_loops = 671,
  OPT_fsel_sched_reschedule_pipelined = 672,
  OPT_fselective_scheduling = 673,
  OPT_fselective_scheduling2 = 674,
  OPT_fshort_double = 675,
  OPT_fshort_enums = 676,
  OPT_fshort_wchar = 677,
  OPT_fshow_column = 678,
  OPT_fsign_zero = 679,
  OPT_fsignaling_nans = 680,
  OPT_fsigned_bitfields = 681,
  OPT_fsigned_char = 682,
  OPT_fsigned_zeros = 683,
  OPT_fsingle_precision_constant = 684,
  OPT_fsplit_ivs_in_unroller = 685,
  OPT_fsplit_stack = 686,
  OPT_fsplit_wide_types = 687,


  OPT_fstack_check_ = 690,
  OPT_fstack_limit = 691,
  OPT_fstack_limit_register_ = 692,
  OPT_fstack_limit_symbol_ = 693,
  OPT_fstack_protector = 694,
  OPT_fstack_protector_all = 695,
  OPT_fstack_usage = 696,
  OPT_fstats = 697,

  OPT_fstrict_aliasing = 699,
  OPT_fstrict_enums = 700,
  OPT_fstrict_overflow = 701,

  OPT_fstrict_volatile_bitfields = 703,
  OPT_fsyntax_only = 704,
  OPT_ftabstop_ = 705,


  OPT_ftemplate_depth_ = 708,
  OPT_ftest_coverage = 709,

  OPT_fthread_jumps = 711,
  OPT_fthreadsafe_statics = 712,
  OPT_ftime_report = 713,
  OPT_ftls_model_ = 714,
  OPT_ftoplevel_reorder = 715,
  OPT_ftracer = 716,
  OPT_ftrapping_math = 717,
  OPT_ftrapv = 718,
  OPT_ftree_bit_ccp = 719,
  OPT_ftree_builtin_call_dce = 720,
  OPT_ftree_ccp = 721,
  OPT_ftree_ch = 722,
  OPT_ftree_copy_prop = 723,
  OPT_ftree_copyrename = 724,
  OPT_ftree_cselim = 725,
  OPT_ftree_dce = 726,
  OPT_ftree_dominator_opts = 727,
  OPT_ftree_dse = 728,
  OPT_ftree_forwprop = 729,
  OPT_ftree_fre = 730,
  OPT_ftree_loop_distribute_patterns = 731,
  OPT_ftree_loop_distribution = 732,
  OPT_ftree_loop_if_convert = 733,
  OPT_ftree_loop_if_convert_stores = 734,
  OPT_ftree_loop_im = 735,
  OPT_ftree_loop_ivcanon = 736,

  OPT_ftree_loop_optimize = 738,
  OPT_ftree_lrs = 739,
  OPT_ftree_parallelize_loops_ = 740,
  OPT_ftree_phiprop = 741,
  OPT_ftree_pre = 742,
  OPT_ftree_pta = 743,
  OPT_ftree_reassoc = 744,

  OPT_ftree_scev_cprop = 746,
  OPT_ftree_sink = 747,
  OPT_ftree_slp_vectorize = 748,
  OPT_ftree_sra = 749,


  OPT_ftree_switch_conversion = 752,
  OPT_ftree_ter = 753,
  OPT_ftree_vect_loop_version = 754,
  OPT_ftree_vectorize = 755,
  OPT_ftree_vectorizer_verbose_ = 756,
  OPT_ftree_vrp = 757,
  OPT_funderscoring = 758,
  OPT_funit_at_a_time = 759,
  OPT_funroll_all_loops = 760,
  OPT_funroll_loops = 761,
  OPT_funsafe_loop_optimizations = 762,
  OPT_funsafe_math_optimizations = 763,
  OPT_funsigned_bitfields = 764,
  OPT_funsigned_char = 765,
  OPT_funswitch_loops = 766,
  OPT_funwind_tables = 767,
  OPT_fuse_cxa_atexit = 768,
  OPT_fuse_cxa_get_exception_ptr = 769,
  OPT_fuse_linker_plugin = 770,
  OPT_fvar_tracking = 771,
  OPT_fvar_tracking_assignments = 772,
  OPT_fvar_tracking_assignments_toggle = 773,
  OPT_fvar_tracking_uninit = 774,
  OPT_fvariable_expansion_in_unroller = 775,
  OPT_fvect_cost_model = 776,
  OPT_fverbose_asm = 777,

  OPT_fvisibility_inlines_hidden = 779,
  OPT_fvisibility_ms_compat = 780,
  OPT_fvisibility_ = 781,
  OPT_fvpt = 782,


  OPT_fweak = 785,
  OPT_fweb = 786,
  OPT_fwhole_file = 787,
  OPT_fwhole_program = 788,
  OPT_fwide_exec_charset_ = 789,
  OPT_fworking_directory = 790,
  OPT_fwpa = 791,
  OPT_fwrapv = 792,

  OPT_fzee = 794,
  OPT_fzero_initialized_in_bss = 795,
  OPT_fzero_link = 796,
  OPT_g = 797,
  OPT_gcoff = 798,
  OPT_gdwarf_ = 799,
  OPT_gen_decls = 800,
  OPT_ggdb = 801,
  OPT_gno_strict_dwarf = 802,
  OPT_gstabs = 803,
  OPT_gstabs_ = 804,
  OPT_gstrict_dwarf = 805,
  OPT_gtoggle = 806,
  OPT_gvms = 807,
  OPT_gxcoff = 808,
  OPT_gxcoff_ = 809,
  OPT_h = 810,
  OPT_idirafter = 811,
  OPT_imacros = 812,
  OPT_imultilib = 813,
  OPT_include = 814,
  OPT_iplugindir_ = 815,
  OPT_iprefix = 816,
  OPT_iquote = 817,
  OPT_isysroot = 818,
  OPT_isystem = 819,
  OPT_iwithprefix = 820,
  OPT_iwithprefixbefore = 821,
  OPT_l = 822,
  OPT_lang_asm = 823,
  OPT_mcall_prologues = 824,
  OPT_mdeb = 825,
  OPT_mint8 = 826,
  OPT_mmcu_ = 827,
  OPT_mno_interrupts = 828,
  OPT_morder1 = 829,
  OPT_morder2 = 830,
  OPT_mpmem_wrap_around = 831,
  OPT_mrelax = 832,
  OPT_mshort_calls = 833,
  OPT_mtiny_stack = 834,
  OPT_n = 835,
  OPT_no_canonical_prefixes = 836,
  OPT_no_integrated_cpp = 837,
  OPT_nocpp = 838,
  OPT_nodefaultlibs = 839,
  OPT_nostartfiles = 840,
  OPT_nostdinc = 841,
  OPT_nostdinc__ = 842,
  OPT_nostdlib = 843,
  OPT_o = 844,
  OPT_p = 845,
  OPT_pass_exit_codes = 846,
  OPT_pedantic = 847,
  OPT_pedantic_errors = 848,
  OPT_pg = 849,
  OPT_pie = 850,
  OPT_pipe = 851,
  OPT_print_file_name_ = 852,
  OPT_print_libgcc_file_name = 853,
  OPT_print_multi_directory = 854,
  OPT_print_multi_lib = 855,
  OPT_print_multi_os_directory = 856,
  OPT_print_objc_runtime_info = 857,
  OPT_print_prog_name_ = 858,
  OPT_print_search_dirs = 859,
  OPT_print_sysroot = 860,
  OPT_print_sysroot_headers_suffix = 861,
  OPT_quiet = 862,
  OPT_r = 863,
  OPT_remap = 864,
  OPT_s = 865,
  OPT_save_temps = 866,
  OPT_save_temps_ = 867,
  OPT_shared = 868,
  OPT_shared_libgcc = 869,

  OPT_specs_ = 871,
  OPT_static = 872,
  OPT_static_libgcc = 873,
  OPT_static_libgfortran = 874,
  OPT_static_libgo = 875,
  OPT_static_libstdc__ = 876,
  OPT_std_c__0x = 877,
  OPT_std_c__98 = 878,
  OPT_std_c1x = 879,

  OPT_std_c90 = 881,
  OPT_std_c99 = 882,

  OPT_std_f2003 = 884,
  OPT_std_f2008 = 885,
  OPT_std_f95 = 886,
  OPT_std_gnu = 887,
  OPT_std_gnu__0x = 888,
  OPT_std_gnu__98 = 889,
  OPT_std_gnu1x = 890,

  OPT_std_gnu90 = 892,
  OPT_std_gnu99 = 893,


  OPT_std_iso9899_199409 = 896,


  OPT_std_legacy = 899,
  OPT_symbolic = 900,
  OPT_t = 901,
  OPT_time = 902,
  OPT_time_ = 903,
  OPT_traditional = 904,
  OPT_traditional_cpp = 905,
  OPT_trigraphs = 906,
  OPT_u = 907,
  OPT_undef = 908,
  OPT_v = 909,
  OPT_version = 910,
  OPT_w = 911,
  OPT_wrapper = 912,
  OPT_x = 913,
  OPT_z = 914,
  N_OPTS,
  OPT_SPECIAL_unknown,
  OPT_SPECIAL_ignore,
  OPT_SPECIAL_program_name,
  OPT_SPECIAL_input_file
};





struct base_arch_s {

  int asm_only;


  int have_mul;


  int have_jmp_call;


  int have_movw_lpmx;


  int have_elpm;


  int have_elpmx;


  int have_eijmp_eicall;


  int reserved;


  int reserved2;


  int default_data_section_start;

  const char *const macro;


  const char *const arch_name;
};




enum avr_arch
{
  ARCH_UNKNOWN,
  ARCH_AVR1,
  ARCH_AVR2,
  ARCH_AVR25,
  ARCH_AVR3,
  ARCH_AVR31,
  ARCH_AVR35,
  ARCH_AVR4,
  ARCH_AVR5,
  ARCH_AVR51,
  ARCH_AVR6
};

struct mcu_type_s {

  const char *const name;


  int arch;


  const char *const macro;


  int short_sp;


  int data_section_start;


  const char *const library_name;
};


extern const char *avr_extra_arch_macro;
extern const struct base_arch_s *avr_current_arch;
extern const struct mcu_type_s *avr_current_device;
extern const struct mcu_type_s avr_mcu_types[];
extern const struct base_arch_s avr_arch_types[];

enum reg_class {
  NO_REGS,
  R0_REG,
  POINTER_X_REGS,
  POINTER_Y_REGS,
  POINTER_Z_REGS,
  STACK_REG,
  BASE_POINTER_REGS,
  POINTER_REGS,
  ADDW_REGS,
  SIMPLE_LD_REGS,
  LD_REGS,
  NO_LD_REGS,
  GENERAL_REGS,
  ALL_REGS, LIM_REG_CLASSES
};

typedef struct avr_args {
  int nregs;
  int regno;
} CUMULATIVE_ARGS;






extern int avr_reg_order[];

extern const char *avr_device_to_arch (int argc, const char **argv);
extern const char *avr_device_to_data_start (int argc, const char **argv);
extern const char *avr_device_to_startfiles (int argc, const char **argv);
extern const char *avr_device_to_devicelib (int argc, const char **argv);

struct machine_function
{

  int is_naked;



  int is_interrupt;



  int is_signal;



  int is_OS_task;



  int is_OS_main;


  int stack_usage;


  int sibcall_fails;
};











       








extern int __gcc_bcmp (const unsigned char *, const unsigned char *, size_t);
extern void __clear_cache (char *, char *);
extern void __eprintf (const char *, const char *, unsigned int, const char *)
  __attribute__ ((__noreturn__));

typedef int QItype __attribute__ ((mode (QI)));
typedef unsigned int UQItype __attribute__ ((mode (QI)));
typedef int HItype __attribute__ ((mode (HI)));
typedef unsigned int UHItype __attribute__ ((mode (HI)));


typedef int SItype __attribute__ ((mode (SI)));
typedef unsigned int USItype __attribute__ ((mode (SI)));


typedef int DItype __attribute__ ((mode (DI)));
typedef unsigned int UDItype __attribute__ ((mode (DI)));

typedef float SFtype __attribute__ ((mode (SF)));
typedef _Complex float SCtype __attribute__ ((mode (SC)));

typedef int cmp_return_type __attribute__((mode (__libgcc_cmp_return__)));
typedef int shift_count_type __attribute__((mode (__libgcc_shift_count__)));

extern DItype __muldi3 (DItype, DItype);
extern DItype __divdi3 (DItype, DItype);
extern UDItype __udivdi3 (UDItype, UDItype);
extern UDItype __umoddi3 (UDItype, UDItype);
extern DItype __moddi3 (DItype, DItype);




extern UDItype __udivmoddi4 (UDItype, UDItype, UDItype *);




extern DItype __negdi2 (DItype);


extern DItype __lshrdi3 (DItype, shift_count_type);
extern DItype __ashldi3 (DItype, shift_count_type);
extern DItype __ashrdi3 (DItype, shift_count_type);




extern USItype __udiv_w_sdiv (USItype *, USItype, USItype, USItype);


extern cmp_return_type __cmpdi2 (DItype, DItype);
extern cmp_return_type __ucmpdi2 (DItype, DItype);


extern SItype __bswapsi2 (SItype);


extern DItype __bswapdi2 (DItype);


extern SItype __absvsi2 (SItype);
extern SItype __addvsi3 (SItype, SItype);
extern SItype __subvsi3 (SItype, SItype);
extern SItype __mulvsi3 (SItype, SItype);
extern SItype __negvsi2 (SItype);
extern DItype __absvdi2 (DItype);
extern DItype __addvdi3 (DItype, DItype);
extern DItype __subvdi3 (DItype, DItype);
extern DItype __mulvdi3 (DItype, DItype);
extern DItype __negvdi2 (DItype);

extern DItype __fixsfdi (SFtype);
extern SFtype __floatdisf (DItype);
extern SFtype __floatundisf (UDItype);
extern USItype __fixunssfsi (SFtype);
extern UDItype __fixunssfdi (SFtype);
extern SFtype __powisf2 (SFtype, int);
extern SCtype __divsc3 (SFtype, SFtype, SFtype, SFtype);
extern SCtype __mulsc3 (SFtype, SFtype, SFtype, SFtype);

  struct DWstruct {SItype low, high;};






typedef union
{
  struct DWstruct s;
  DItype ll;
} DWunion;



extern const UQItype __popcount_tab[256];





extern const UQItype __clz_tab[256];



extern const UQItype __clz_tab[256] ;



extern int __clzdi2 (UDItype);
extern int __clzsi2 (USItype);
extern int __ctzsi2 (USItype);
extern int __ffssi2 (USItype);
extern int __ffsdi2 (DItype);
extern int __ctzdi2 (UDItype);
extern int __popcountsi2 (USItype);
extern int __popcountdi2 (UDItype);
extern int __paritysi2 (USItype);
extern int __paritydi2 (UDItype);


extern void __enable_execute_stack (void *);










DItype
__negdi2 (DItype u)
{
  const DWunion uu = {.ll = u};
  const DWunion w = { {.low = -uu.s.low,
         .high = -uu.s.high - ((USItype) -uu.s.low > 0) } };

  return w.ll;
}

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-25 12:58             ` Georg-Johann Lay
@ 2011-03-25 13:00               ` Georg-Johann Lay
  2011-03-25 17:35               ` Richard Henderson
  1 sibling, 0 replies; 32+ messages in thread
From: Georg-Johann Lay @ 2011-03-25 13:00 UTC (permalink / raw)
  To: Andreas Krebbel; +Cc: gcc-patches, richard.sandiford, Richard Henderson

Georg-Johann Lay schrieb:
> Andreas Krebbel schrieb:
>> On 03/22/2011 06:48 PM, Richard Henderson wrote:
>>
>>> Ok.  Watch out for other target problems this week.
> 
> libgcc fails to build for avr (SVN 171446)
> 
> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c: In function
> '__negdi2':
> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c:68:17:
> internal compiler error: in maybe_gen_insn, at  optabs.c:7123

p.s.: some additional info

(gdb) set args -quiet -v -iprefix
/local/gnu/build/gcc-4.6-avr/gcc/../lib/gcc/avr/4.7.0/ -isystem
/mnt/nfs/home/georg/gnu/build/gcc-4.6-avr/gcc/include -isystem
/mnt/nfs/home/georg/gnu/build/gcc-4.6-avr/gcc/include-fixed
libgcc2-negdi2.c -quiet -dumpbase libgcc2-negdi2.c -auxbase
libgcc2-negdi2 -Os -version -o libgcc2-negdi2.s
(gdb) cd ~/test
(gdb) r
GNU C (GCC) version 4.7.0 20110325 (experimental) (avr)
	compiled by GNU C version 4.3.2 [gcc-4_3-branch revision 141291], GMP
version 5.0.1, MPFR version 3.0.0-p8, MPC version 0.8.2
GGC heuristics: --param ggc-min-expand=30 --param ggc-min-heapsize=4096

Breakpoint 1, fancy_abort (file=0x87ee230
"../../../gcc.gnu.org/trunk/gcc/optabs.c", line=7123,
function=0x87ee585 "maybe_gen_insn") at
../../../gcc.gnu.org/trunk/gcc/diagnostic.c:893
(gdb) bt
#0  fancy_abort (file=0x87ee230
"../../../gcc.gnu.org/trunk/gcc/optabs.c", line=7123,
function=0x87ee585 "maybe_gen_insn") at
../../../gcc.gnu.org/trunk/gcc/diagnostic.c:893
#1  0x0837a86a in maybe_gen_insn (icode=CODE_FOR_setmemhi, nops=6,
ops=0xbfffdfcc) at ../../../gcc.gnu.org/trunk/gcc/optabs.c:7123
#2  0x0837a91f in maybe_expand_insn (icode=CODE_FOR_setmemhi, nops=6,
ops=0xbfffdfcc) at ../../../gcc.gnu.org/trunk/gcc/optabs.c:7155
#3  0x08241122 in set_storage_via_setmem (object=0xb7dfcc60,
size=0xb7d682f8, val=0xb7d682b8, align=<value optimized out>,
expected_align=<value optimized out>, expected_size=-1) at
../../../gcc.gnu.org/trunk/gcc/expr.c:2730
#4  0x082567d5 in clear_storage_hints (object=0xb7dfcc60,
size=0xb7d682f8, method=BLOCK_OP_NORMAL, expected_align=0,
expected_size=-1) at ../../../gcc.gnu.org/trunk/gcc/expr.c:2575
#5  0x0825691f in clear_storage (object=0xb7dfcc60, size=0xb7d682f8,
method=BLOCK_OP_NORMAL) at ../../../gcc.gnu.org/trunk/gcc/expr.c:2590
#6  0x0825aa83 in store_constructor (exp=0xb7dfaca8,
target=0xb7dfcc60, cleared=0, size=8) at
../../../gcc.gnu.org/trunk/gcc/expr.c:5188
#7  0x0825b6fe in expand_constructor (exp=0xb7dfaca8,
target=0xb7dfcc60, modifier=EXPAND_NORMAL, avoid_temp_mem=0 '\0') at
../../../gcc.gnu.org/trunk/gcc/expr.c:7092
#8  0x0824dade in expand_expr_real_1 (exp=0xb7dfaca8,
target=0xb7dfcc60, tmode=BLKmode, modifier=EXPAND_NORMAL,
alt_rtl=0xbfffe35c) at ../../../gcc.gnu.org/trunk/gcc/expr.c:8655
#9  0x08245d52 in store_expr (exp=0xb7dfaca8, target=0xb7dfcc60,
call_param_p=0, nontemporal=0 '\0') at
../../../gcc.gnu.org/trunk/gcc/expr.c:4645
#10 0x08257949 in expand_assignment (to=0xb7e2a1e0, from=0xb7dfaca8,
nontemporal=5 '\005') at ../../../gcc.gnu.org/trunk/gcc/expr.c:4433
#11 0x081a61aa in expand_gimple_stmt (stmt=0xb7e237e0) at
../../../gcc.gnu.org/trunk/gcc/cfgexpand.c:1971
#12 0x081a7200 in expand_gimple_basic_block (bb=0xb7ddf3c0) at
../../../gcc.gnu.org/trunk/gcc/cfgexpand.c:3645
#13 0x081a8a65 in gimple_expand_cfg () at
../../../gcc.gnu.org/trunk/gcc/cfgexpand.c:4128
#14 0x08395334 in execute_one_pass (pass=0x88742e0) at
../../../gcc.gnu.org/trunk/gcc/passes.c:1554
#15 0x0839562d in execute_pass_list (pass=0x88742e0) at
../../../gcc.gnu.org/trunk/gcc/passes.c:1609
#16 0x08475baa in tree_rest_of_compilation (fndecl=0xb7e21180) at
../../../gcc.gnu.org/trunk/gcc/tree-optimize.c:422
#17 0x08615bc6 in cgraph_expand_function (node=0xb7e2c000) at
../../../gcc.gnu.org/trunk/gcc/cgraphunit.c:1576
#18 0x08618c59 in cgraph_optimize () at
../../../gcc.gnu.org/trunk/gcc/cgraphunit.c:1635
#19 0x0861915d in cgraph_finalize_compilation_unit () at
../../../gcc.gnu.org/trunk/gcc/cgraphunit.c:1096
#20 0x080b6590 in c_write_global_declarations () at
../../../gcc.gnu.org/trunk/gcc/c-decl.c:9879
#21 0x08411fae in toplev_main (argc=19, argv=0xbfffe874) at
../../../gcc.gnu.org/trunk/gcc/toplev.c:591
#22 0x08152372 in main (argc=Cannot access memory at address 0x1
) at ../../../gcc.gnu.org/trunk/gcc/main.c:36
(gdb) frame 1
#1  0x0837a86a in maybe_gen_insn (icode=CODE_FOR_setmemhi, nops=6,
ops=0xbfffdfcc) at ../../../gcc.gnu.org/trunk/gcc/optabs.c:7123
(gdb) p nops
$1 = 6
(gdb) p insn_data[(int) icode].n_operands
$2 = 5 '\005'
(gdb) p *ops
$3 = {type = EXPAND_FIXED, unsigned_p = 0, unused = 0, mode =
VOIDmode, value = 0xb7dfcc60}
(gdb) p ops -> value
$4 = (rtx) 0xb7dfcc60
(gdb) pr
(mem/s/c:BLK (reg/f:HI 37 virtual-stack-vars) [2 w+0 S8 A8])
(gdb) p icode
$5 = CODE_FOR_setmemhi
(gdb) Quit
(gdb)

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-25 12:58             ` Georg-Johann Lay
  2011-03-25 13:00               ` Georg-Johann Lay
@ 2011-03-25 17:35               ` Richard Henderson
  2011-03-25 17:56                 ` Richard Sandiford
  2011-03-31 10:00                 ` Georg-Johann Lay
  1 sibling, 2 replies; 32+ messages in thread
From: Richard Henderson @ 2011-03-25 17:35 UTC (permalink / raw)
  To: Georg-Johann Lay; +Cc: gcc-patches, richard.sandiford

[-- Attachment #1: Type: text/plain, Size: 1403 bytes --]

On 03/25/2011 05:41 AM, Georg-Johann Lay wrote:
>> On 03/22/2011 06:48 PM, Richard Henderson wrote:
>>
>>> Ok.  Watch out for other target problems this week.
> 
> libgcc fails to build for avr (SVN 171446)
> 
> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c: In function
> '__negdi2':
> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c:68:17:
> internal compiler error: in maybe_gen_insn, at  optabs.c:7123

This is due to a miscommunication between the middle-end and the backend
about how many arguments the setmemhi pattern takes.

(define_expand "setmemhi"
  [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
                   (match_operand 2 "const_int_operand" ""))
              (use (match_operand:HI 1 "const_int_operand" ""))
              (use (match_operand:HI 3 "const_int_operand" "n"))
              (clobber (match_scratch:HI 4 ""))
              (clobber (match_dup 5))])]

The match_scratch is counted in .n_operands, which makes the count of
operands not equal 4, so we assume 6 operands are necessary.  We can
fix this for the special case of avr by only assuming 6 operands when
there are in fact 6 operands, but of course this could fail just as
easily if there were two scratches.

All of which suggests that optional arguments to a named optab is a
mistake that ought to be rectified.

I plan to commit the following after bootstrap and check.


r~

[-- Attachment #2: z --]
[-- Type: text/plain, Size: 1198 bytes --]

diff --git a/gcc/expr.c b/gcc/expr.c
index 4db1c77..0a95aa7 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1299,11 +1299,10 @@ emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align,
 	  /* The check above guarantees that this size conversion is valid.  */
 	  create_convert_operand_to (&ops[2], size, mode, true);
 	  create_integer_operand (&ops[3], align / BITS_PER_UNIT);
-	  if (nops != 4)
+	  if (nops == 6)
 	    {
 	      create_integer_operand (&ops[4], expected_align / BITS_PER_UNIT);
 	      create_integer_operand (&ops[5], expected_size);
-	      nops = 6;
 	    }
 	  if (maybe_expand_insn (code, nops, ops))
 	    {
@@ -2721,11 +2720,10 @@ set_storage_via_setmem (rtx object, rtx size, rtx val, unsigned int align,
 	  create_convert_operand_to (&ops[1], size, mode, true);
 	  create_convert_operand_from (&ops[2], val, byte_mode, true);
 	  create_integer_operand (&ops[3], align / BITS_PER_UNIT);
-	  if (nops != 4)
+	  if (nops == 6)
 	    {
 	      create_integer_operand (&ops[4], expected_align / BITS_PER_UNIT);
 	      create_integer_operand (&ops[5], expected_size);
-	      nops = 6;
 	    }
 	  if (maybe_expand_insn (code, nops, ops))
 	    return true;

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-25 17:35               ` Richard Henderson
@ 2011-03-25 17:56                 ` Richard Sandiford
  2011-03-25 18:30                   ` Richard Sandiford
  2011-03-25 19:23                   ` Richard Henderson
  2011-03-31 10:00                 ` Georg-Johann Lay
  1 sibling, 2 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-25 17:56 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Georg-Johann Lay, gcc-patches

Richard Henderson <rth@redhat.com> writes:
> This is due to a miscommunication between the middle-end and the backend
> about how many arguments the setmemhi pattern takes.
>
> (define_expand "setmemhi"
>   [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
>                    (match_operand 2 "const_int_operand" ""))
>               (use (match_operand:HI 1 "const_int_operand" ""))
>               (use (match_operand:HI 3 "const_int_operand" "n"))
>               (clobber (match_scratch:HI 4 ""))
>               (clobber (match_dup 5))])]
>
> The match_scratch is counted in .n_operands, which makes the count of
> operands not equal 4, so we assume 6 operands are necessary.  We can
> fix this for the special case of avr by only assuming 6 operands when
> there are in fact 6 operands, but of course this could fail just as
> easily if there were two scratches.
>
> All of which suggests that optional arguments to a named optab is a
> mistake that ought to be rectified.
>
> I plan to commit the following after bootstrap and check.

Thanks.  I think it needs to be s/!= 4/>= 6/ though, so that
match_scratches still work when 6 operands really are passed in.

Fully agreed on the optional args thing.  Or maybe insn_data should
have a separate "num_args" field.

Richard

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-25 17:56                 ` Richard Sandiford
@ 2011-03-25 18:30                   ` Richard Sandiford
  2011-03-25 18:31                     ` Richard Sandiford
  2011-03-25 19:23                   ` Richard Henderson
  1 sibling, 1 reply; 32+ messages in thread
From: Richard Sandiford @ 2011-03-25 18:30 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Georg-Johann Lay, gcc-patches

Richard Sandiford <richard.sandiford@linaro.org> writes:
> Richard Henderson <rth@redhat.com> writes:
>> This is due to a miscommunication between the middle-end and the backend
>> about how many arguments the setmemhi pattern takes.
>>
>> (define_expand "setmemhi"
>>   [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
>>                    (match_operand 2 "const_int_operand" ""))
>>               (use (match_operand:HI 1 "const_int_operand" ""))
>>               (use (match_operand:HI 3 "const_int_operand" "n"))
>>               (clobber (match_scratch:HI 4 ""))
>>               (clobber (match_dup 5))])]
>>
>> The match_scratch is counted in .n_operands, which makes the count of
>> operands not equal 4, so we assume 6 operands are necessary.  We can
>> fix this for the special case of avr by only assuming 6 operands when
>> there are in fact 6 operands, but of course this could fail just as
>> easily if there were two scratches.
>>
>> All of which suggests that optional arguments to a named optab is a
>> mistake that ought to be rectified.
>>
>> I plan to commit the following after bootstrap and check.
>
> Thanks.  I think it needs to be s/!= 4/>= 6/ though, so that
> match_scratches still work when 6 operands really are passed in.

Er, >= 4 even...

Richard

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-25 18:30                   ` Richard Sandiford
@ 2011-03-25 18:31                     ` Richard Sandiford
  0 siblings, 0 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-25 18:31 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Georg-Johann Lay, gcc-patches

Richard Sandiford <richard.sandiford@linaro.org> writes:
> Er, >= 4 even...

Or not,  *sigh*.  Time I went home...

Richard


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-25 17:56                 ` Richard Sandiford
  2011-03-25 18:30                   ` Richard Sandiford
@ 2011-03-25 19:23                   ` Richard Henderson
  2011-03-26  2:51                     ` Richard Henderson
  1 sibling, 1 reply; 32+ messages in thread
From: Richard Henderson @ 2011-03-25 19:23 UTC (permalink / raw)
  To: Georg-Johann Lay, gcc-patches, richard.sandiford

On 03/25/2011 10:51 AM, Richard Sandiford wrote:
> Thanks.  I think it needs to be s/!= 4/>= 6/ though, so that
> match_scratches still work when 6 operands really are passed in.

For the record, I audited all setmem and movmem patterns.

There are is only one that uses 6 operands: i386.
There are two that use 4 operands, but have 1 scratch: avr, pdp11.
All the rest have exactly 4 operands.

I'll leave the test as == 6 for now.

> Fully agreed on the optional args thing.  Or maybe insn_data should
> have a separate "num_args" field.

This is sounds like a good thing.

It's probably worth doing some checking in some genfoo (opinit?) that
the named patterns have exactly the number of operands expected.


r~

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-25 19:23                   ` Richard Henderson
@ 2011-03-26  2:51                     ` Richard Henderson
  0 siblings, 0 replies; 32+ messages in thread
From: Richard Henderson @ 2011-03-26  2:51 UTC (permalink / raw)
  To: Georg-Johann Lay, gcc-patches, richard.sandiford

[-- Attachment #1: Type: text/plain, Size: 593 bytes --]

On 03/25/2011 11:49 AM, Richard Henderson wrote:
> On 03/25/2011 10:51 AM, Richard Sandiford wrote:
>> Thanks.  I think it needs to be s/!= 4/>= 6/ though, so that
>> match_scratches still work when 6 operands really are passed in.
> 
> For the record, I audited all setmem and movmem patterns.
> 
> There are is only one that uses 6 operands: i386.
> There are two that use 4 operands, but have 1 scratch: avr, pdp11.
> All the rest have exactly 4 operands.
> 
> I'll leave the test as == 6 for now.

I added a check to make sure that we don't get a 7 unexpectedly.

Committed as below.


r~

[-- Attachment #2: commit-b52cb71 --]
[-- Type: text/plain, Size: 2888 bytes --]

commit b52cb7196addd0e2bc281787a43dd9ce2b9b8cdc
Author: rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Fri Mar 25 23:17:26 2011 +0000

            * expr.c (emit_block_move_via_movmem): Only use 6 operand variant
            if there are exactly 6 operands.
            (set_storage_via_setmem): Similarly.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@171532 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e7983a2..040a83c 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,9 @@
+2011-03-25  Richard Henderson  <rth@redhat.com>
+
+	* expr.c (emit_block_move_via_movmem): Only use 6 operand variant
+	if there are exactly 6 operands.
+	(set_storage_via_setmem): Similarly.
+
 2011-03-25  Kai Tietz  <ktietz@redhat.com>
 
 	* collect2.c (write_c_file_stat): Handle backslash
diff --git a/gcc/expr.c b/gcc/expr.c
index 4db1c77..076b8d2 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1294,16 +1294,20 @@ emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align,
 	     that it doesn't fail the expansion because it thinks
 	     emitting the libcall would be more efficient.  */
 	  nops = insn_data[(int) code].n_operands;
+	  /* ??? n_operands includes match_scratches; find some other
+	     way to select the 6 operand variant, or force all targets
+	     to have exactly 6 operands.  */
+	  gcc_assert (nops >= 4 && nops <= 6);
+
 	  create_fixed_operand (&ops[0], x);
 	  create_fixed_operand (&ops[1], y);
 	  /* The check above guarantees that this size conversion is valid.  */
 	  create_convert_operand_to (&ops[2], size, mode, true);
 	  create_integer_operand (&ops[3], align / BITS_PER_UNIT);
-	  if (nops != 4)
+	  if (nops == 6)
 	    {
 	      create_integer_operand (&ops[4], expected_align / BITS_PER_UNIT);
 	      create_integer_operand (&ops[5], expected_size);
-	      nops = 6;
 	    }
 	  if (maybe_expand_insn (code, nops, ops))
 	    {
@@ -2716,16 +2720,20 @@ set_storage_via_setmem (rtx object, rtx size, rtx val, unsigned int align,
 	  unsigned int nops;
 
 	  nops = insn_data[(int) code].n_operands;
+	  /* ??? n_operands includes match_scratches; find some other
+	     way to select the 6 operand variant, or force all targets
+	     to have exactly 6 operands.  */
+	  gcc_assert (nops >= 4 && nops <= 6);
+
 	  create_fixed_operand (&ops[0], object);
 	  /* The check above guarantees that this size conversion is valid.  */
 	  create_convert_operand_to (&ops[1], size, mode, true);
 	  create_convert_operand_from (&ops[2], val, byte_mode, true);
 	  create_integer_operand (&ops[3], align / BITS_PER_UNIT);
-	  if (nops != 4)
+	  if (nops == 6)
 	    {
 	      create_integer_operand (&ops[4], expected_align / BITS_PER_UNIT);
 	      create_integer_operand (&ops[5], expected_size);
-	      nops = 6;
 	    }
 	  if (maybe_expand_insn (code, nops, ops))
 	    return true;

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-24 12:13             ` Richard Sandiford
  2011-03-24 15:09               ` Richard Sandiford
  2011-03-24 17:09               ` Richard Henderson
@ 2011-03-29 12:26               ` Mikael Pettersson
  2011-03-29 13:27                 ` Richard Sandiford
  2 siblings, 1 reply; 32+ messages in thread
From: Mikael Pettersson @ 2011-03-29 12:26 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Andreas Krebbel, gcc-patches, rth

Richard Sandiford writes:
 > Andreas Krebbel <krebbel@linux.vnet.ibm.com> writes:
 > > On 03/22/2011 06:48 PM, Richard Henderson wrote:
 > >
 > >> Ok.  Watch out for other target problems this week.
 > >
 > > This unfortunately broke bootstrap on s390.
 > 
 > This is PR 48263.  Since it seems to be affecting several targets,
 > and since my bootstrap seems to be taking a looong time, I'll post
 > the patch here before testing has finished.
 > 
 > > Just copying the pre-patch behaviour fixes the problem for me:
 > 
 > I think we need to undo more of the patch, and leave the conversion
 > outside of the new interface.
 > 
 > Sorry for the breakage.
 > 
 > Richard
 > 
 > 
 > gcc/
 > 	PR rtl-optimization/48263
 > 	* optabs.c (expand_binop_directly): Reinstate convert_modes code
 > 	and original commutative_p handling.  Use maybe_gen_insn.

Unfortunately this fix (r171418) broke m68k-linux, see
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48332

/Mikael

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-29 12:26               ` Mikael Pettersson
@ 2011-03-29 13:27                 ` Richard Sandiford
  2011-03-29 14:27                   ` Mikael Pettersson
  2011-03-29 22:14                   ` Richard Henderson
  0 siblings, 2 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-29 13:27 UTC (permalink / raw)
  To: Mikael Pettersson; +Cc: Andreas Krebbel, gcc-patches, rth

Mikael Pettersson <mikpe@it.uu.se> writes:
> Richard Sandiford writes:
>  > Andreas Krebbel <krebbel@linux.vnet.ibm.com> writes:
>  > > On 03/22/2011 06:48 PM, Richard Henderson wrote:
>  > >
>  > >> Ok.  Watch out for other target problems this week.
>  > >
>  > > This unfortunately broke bootstrap on s390.
>  > 
>  > This is PR 48263.  Since it seems to be affecting several targets,
>  > and since my bootstrap seems to be taking a looong time, I'll post
>  > the patch here before testing has finished.
>  > 
>  > > Just copying the pre-patch behaviour fixes the problem for me:
>  > 
>  > I think we need to undo more of the patch, and leave the conversion
>  > outside of the new interface.
>  > 
>  > Sorry for the breakage.
>  > 
>  > Richard
>  > 
>  > 
>  > gcc/
>  > 	PR rtl-optimization/48263
>  > 	* optabs.c (expand_binop_directly): Reinstate convert_modes code
>  > 	and original commutative_p handling.  Use maybe_gen_insn.
>
> Unfortunately this fix (r171418) broke m68k-linux, see
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48332

Please try the attached

Richard


gcc/
	PR rtl-optimization/48332
	* optabs.c (expand_binop_directly): Set xmodeN to the target-mandated
	mode of input operand N and modeN to its actual mode.

Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	2011-03-24 17:23:05.000000000 +0000
+++ gcc/optabs.c	2011-03-29 14:18:02.000000000 +0100
@@ -1243,9 +1243,9 @@ expand_binop_directly (enum machine_mode
 		       rtx last)
 {
   enum insn_code icode = optab_handler (binoptab, mode);
-  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
-  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
-  enum machine_mode tmp_mode;
+  enum machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
+  enum machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
+  enum machine_mode mode0, mode1, tmp_mode;
   struct expand_operand ops[3];
   bool commutative_p;
   rtx pat;
@@ -1256,8 +1256,8 @@ expand_binop_directly (enum machine_mode
      if we would swap the operands, we can save the conversions.  */
   commutative_p = commutative_optab_p (binoptab);
   if (commutative_p
-      && GET_MODE (xop0) != mode0 && GET_MODE (xop1) != mode1
-      && GET_MODE (xop0) == mode1 && GET_MODE (xop1) == mode1)
+      && GET_MODE (xop0) != xmode0 && GET_MODE (xop1) != xmode1
+      && GET_MODE (xop0) == xmode1 && GET_MODE (xop1) == xmode1)
     {
       swap = xop0;
       xop0 = xop1;
@@ -1265,9 +1265,9 @@ expand_binop_directly (enum machine_mode
     }
 
   /* If we are optimizing, force expensive constants into a register.  */
-  xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp);
+  xop0 = avoid_expensive_constant (xmode0, binoptab, xop0, unsignedp);
   if (!shift_optab_p (binoptab))
-    xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
+    xop1 = avoid_expensive_constant (xmode1, binoptab, xop1, unsignedp);
 
   /* In case the insn wants input operands in modes different from
      those of the actual operands, convert the operands.  It would
@@ -1275,19 +1275,19 @@ expand_binop_directly (enum machine_mode
      that they're properly zero-extended, sign-extended or truncated
      for their mode.  */
 
-  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
-    xop0 = convert_modes (mode0,
-			  GET_MODE (xop0) != VOIDmode
-			  ? GET_MODE (xop0)
-			  : mode,
-			  xop0, unsignedp);
-
-  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
-    xop1 = convert_modes (mode1,
-			  GET_MODE (xop1) != VOIDmode
-			  ? GET_MODE (xop1)
-			  : mode,
-			  xop1, unsignedp);
+  mode0 = GET_MODE (xop0) != VOIDmode ? GET_MODE (xop0) : mode;
+  if (xmode0 != VOIDmode && xmode0 != mode0)
+    {
+      xop0 = convert_modes (xmode0, mode0, xop0, unsignedp);
+      mode0 = xmode0;
+    }
+
+  mode1 = GET_MODE (xop1) != VOIDmode ? GET_MODE (xop1) : mode;
+  if (xmode1 != VOIDmode && xmode1 != mode1)
+    {
+      xop1 = convert_modes (xmode1, mode1, xop1, unsignedp);
+      mode1 = xmode1;
+    }
 
   /* If operation is commutative,
      try to make the first operand a register.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-29 13:27                 ` Richard Sandiford
@ 2011-03-29 14:27                   ` Mikael Pettersson
  2011-03-29 22:01                     ` Richard Sandiford
  2011-03-29 22:14                   ` Richard Henderson
  1 sibling, 1 reply; 32+ messages in thread
From: Mikael Pettersson @ 2011-03-29 14:27 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Mikael Pettersson, Andreas Krebbel, gcc-patches, rth

Richard Sandiford writes:
 > Mikael Pettersson <mikpe@it.uu.se> writes:
 > > Richard Sandiford writes:
 > >  > Andreas Krebbel <krebbel@linux.vnet.ibm.com> writes:
 > >  > > On 03/22/2011 06:48 PM, Richard Henderson wrote:
 > >  > >
 > >  > >> Ok.  Watch out for other target problems this week.
 > >  > >
 > >  > > This unfortunately broke bootstrap on s390.
 > >  > 
 > >  > This is PR 48263.  Since it seems to be affecting several targets,
 > >  > and since my bootstrap seems to be taking a looong time, I'll post
 > >  > the patch here before testing has finished.
 > >  > 
 > >  > > Just copying the pre-patch behaviour fixes the problem for me:
 > >  > 
 > >  > I think we need to undo more of the patch, and leave the conversion
 > >  > outside of the new interface.
 > >  > 
 > >  > Sorry for the breakage.
 > >  > 
 > >  > Richard
 > >  > 
 > >  > 
 > >  > gcc/
 > >  > 	PR rtl-optimization/48263
 > >  > 	* optabs.c (expand_binop_directly): Reinstate convert_modes code
 > >  > 	and original commutative_p handling.  Use maybe_gen_insn.
 > >
 > > Unfortunately this fix (r171418) broke m68k-linux, see
 > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48332
 > 
 > Please try the attached
 > 
 > Richard
 > 
 > 
 > gcc/
 > 	PR rtl-optimization/48332
 > 	* optabs.c (expand_binop_directly): Set xmodeN to the target-mandated
 > 	mode of input operand N and modeN to its actual mode.

Thanks, this allowed me to build gcc-4.7 as a cross to m68k-linux again.

I'm starting a native bootstrap of gcc-4.7 head + this fix on m68k, but
that will take about 5 days to complete...

/Mikael

 > 
 > Index: gcc/optabs.c
 > ===================================================================
 > --- gcc/optabs.c	2011-03-24 17:23:05.000000000 +0000
 > +++ gcc/optabs.c	2011-03-29 14:18:02.000000000 +0100
 > @@ -1243,9 +1243,9 @@ expand_binop_directly (enum machine_mode
 >  		       rtx last)
 >  {
 >    enum insn_code icode = optab_handler (binoptab, mode);
 > -  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
 > -  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
 > -  enum machine_mode tmp_mode;
 > +  enum machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
 > +  enum machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
 > +  enum machine_mode mode0, mode1, tmp_mode;
 >    struct expand_operand ops[3];
 >    bool commutative_p;
 >    rtx pat;
 > @@ -1256,8 +1256,8 @@ expand_binop_directly (enum machine_mode
 >       if we would swap the operands, we can save the conversions.  */
 >    commutative_p = commutative_optab_p (binoptab);
 >    if (commutative_p
 > -      && GET_MODE (xop0) != mode0 && GET_MODE (xop1) != mode1
 > -      && GET_MODE (xop0) == mode1 && GET_MODE (xop1) == mode1)
 > +      && GET_MODE (xop0) != xmode0 && GET_MODE (xop1) != xmode1
 > +      && GET_MODE (xop0) == xmode1 && GET_MODE (xop1) == xmode1)
 >      {
 >        swap = xop0;
 >        xop0 = xop1;
 > @@ -1265,9 +1265,9 @@ expand_binop_directly (enum machine_mode
 >      }
 >  
 >    /* If we are optimizing, force expensive constants into a register.  */
 > -  xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp);
 > +  xop0 = avoid_expensive_constant (xmode0, binoptab, xop0, unsignedp);
 >    if (!shift_optab_p (binoptab))
 > -    xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
 > +    xop1 = avoid_expensive_constant (xmode1, binoptab, xop1, unsignedp);
 >  
 >    /* In case the insn wants input operands in modes different from
 >       those of the actual operands, convert the operands.  It would
 > @@ -1275,19 +1275,19 @@ expand_binop_directly (enum machine_mode
 >       that they're properly zero-extended, sign-extended or truncated
 >       for their mode.  */
 >  
 > -  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
 > -    xop0 = convert_modes (mode0,
 > -			  GET_MODE (xop0) != VOIDmode
 > -			  ? GET_MODE (xop0)
 > -			  : mode,
 > -			  xop0, unsignedp);
 > -
 > -  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
 > -    xop1 = convert_modes (mode1,
 > -			  GET_MODE (xop1) != VOIDmode
 > -			  ? GET_MODE (xop1)
 > -			  : mode,
 > -			  xop1, unsignedp);
 > +  mode0 = GET_MODE (xop0) != VOIDmode ? GET_MODE (xop0) : mode;
 > +  if (xmode0 != VOIDmode && xmode0 != mode0)
 > +    {
 > +      xop0 = convert_modes (xmode0, mode0, xop0, unsignedp);
 > +      mode0 = xmode0;
 > +    }
 > +
 > +  mode1 = GET_MODE (xop1) != VOIDmode ? GET_MODE (xop1) : mode;
 > +  if (xmode1 != VOIDmode && xmode1 != mode1)
 > +    {
 > +      xop1 = convert_modes (xmode1, mode1, xop1, unsignedp);
 > +      mode1 = xmode1;
 > +    }
 >  
 >    /* If operation is commutative,
 >       try to make the first operand a register.
 > 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-29 14:27                   ` Mikael Pettersson
@ 2011-03-29 22:01                     ` Richard Sandiford
  0 siblings, 0 replies; 32+ messages in thread
From: Richard Sandiford @ 2011-03-29 22:01 UTC (permalink / raw)
  To: Mikael Pettersson; +Cc: Richard Sandiford, Andreas Krebbel, gcc-patches, rth

Mikael Pettersson <mikpe@it.uu.se> writes:
>  > gcc/
>  > 	PR rtl-optimization/48332
>  > 	* optabs.c (expand_binop_directly): Set xmodeN to the target-mandated
>  > 	mode of input operand N and modeN to its actual mode.
>
> Thanks, this allowed me to build gcc-4.7 as a cross to m68k-linux again.

Good to hear, thanks for testing.

> I'm starting a native bootstrap of gcc-4.7 head + this fix on m68k, but
> that will take about 5 days to complete...

Ouch.

Hopefully this time I've finally got it right.  Bootstrapped &
regression-tested on x86_64-linux-gnu.  OK to install?

Richard

>  > 
>  > Index: gcc/optabs.c
>  > ===================================================================
>  > --- gcc/optabs.c	2011-03-24 17:23:05.000000000 +0000
>  > +++ gcc/optabs.c	2011-03-29 14:18:02.000000000 +0100
>  > @@ -1243,9 +1243,9 @@ expand_binop_directly (enum machine_mode
>  >  		       rtx last)
>  >  {
>  >    enum insn_code icode = optab_handler (binoptab, mode);
>  > -  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
>  > -  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
>  > -  enum machine_mode tmp_mode;
>  > +  enum machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
>  > +  enum machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
>  > +  enum machine_mode mode0, mode1, tmp_mode;
>  >    struct expand_operand ops[3];
>  >    bool commutative_p;
>  >    rtx pat;
>  > @@ -1256,8 +1256,8 @@ expand_binop_directly (enum machine_mode
>  >       if we would swap the operands, we can save the conversions.  */
>  >    commutative_p = commutative_optab_p (binoptab);
>  >    if (commutative_p
>  > -      && GET_MODE (xop0) != mode0 && GET_MODE (xop1) != mode1
>  > -      && GET_MODE (xop0) == mode1 && GET_MODE (xop1) == mode1)
>  > +      && GET_MODE (xop0) != xmode0 && GET_MODE (xop1) != xmode1
>  > +      && GET_MODE (xop0) == xmode1 && GET_MODE (xop1) == xmode1)
>  >      {
>  >        swap = xop0;
>  >        xop0 = xop1;
>  > @@ -1265,9 +1265,9 @@ expand_binop_directly (enum machine_mode
>  >      }
>  >  
>  >    /* If we are optimizing, force expensive constants into a register.  */
>  > -  xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp);
>  > +  xop0 = avoid_expensive_constant (xmode0, binoptab, xop0, unsignedp);
>  >    if (!shift_optab_p (binoptab))
>  > -    xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
>  > +    xop1 = avoid_expensive_constant (xmode1, binoptab, xop1, unsignedp);
>  >  
>  >    /* In case the insn wants input operands in modes different from
>  >       those of the actual operands, convert the operands.  It would
>  > @@ -1275,19 +1275,19 @@ expand_binop_directly (enum machine_mode
>  >       that they're properly zero-extended, sign-extended or truncated
>  >       for their mode.  */
>  >  
>  > -  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
>  > -    xop0 = convert_modes (mode0,
>  > -			  GET_MODE (xop0) != VOIDmode
>  > -			  ? GET_MODE (xop0)
>  > -			  : mode,
>  > -			  xop0, unsignedp);
>  > -
>  > -  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
>  > -    xop1 = convert_modes (mode1,
>  > -			  GET_MODE (xop1) != VOIDmode
>  > -			  ? GET_MODE (xop1)
>  > -			  : mode,
>  > -			  xop1, unsignedp);
>  > +  mode0 = GET_MODE (xop0) != VOIDmode ? GET_MODE (xop0) : mode;
>  > +  if (xmode0 != VOIDmode && xmode0 != mode0)
>  > +    {
>  > +      xop0 = convert_modes (xmode0, mode0, xop0, unsignedp);
>  > +      mode0 = xmode0;
>  > +    }
>  > +
>  > +  mode1 = GET_MODE (xop1) != VOIDmode ? GET_MODE (xop1) : mode;
>  > +  if (xmode1 != VOIDmode && xmode1 != mode1)
>  > +    {
>  > +      xop1 = convert_modes (xmode1, mode1, xop1, unsignedp);
>  > +      mode1 = xmode1;
>  > +    }
>  >  
>  >    /* If operation is commutative,
>  >       try to make the first operand a register.
>  > 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-29 13:27                 ` Richard Sandiford
  2011-03-29 14:27                   ` Mikael Pettersson
@ 2011-03-29 22:14                   ` Richard Henderson
  2011-03-30  9:13                     ` Richard Sandiford
  1 sibling, 1 reply; 32+ messages in thread
From: Richard Henderson @ 2011-03-29 22:14 UTC (permalink / raw)
  To: Mikael Pettersson, Andreas Krebbel, gcc-patches, richard.sandiford

On 03/29/2011 06:21 AM, Richard Sandiford wrote:
> -  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
> -  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
> -  enum machine_mode tmp_mode;
> +  enum machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
> +  enum machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
> +  enum machine_mode mode0, mode1, tmp_mode;
...
> -  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
> -    xop0 = convert_modes (mode0,
> -			  GET_MODE (xop0) != VOIDmode
> -			  ? GET_MODE (xop0)
> -			  : mode,
> -			  xop0, unsignedp);
> -
> -  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
> -    xop1 = convert_modes (mode1,
> -			  GET_MODE (xop1) != VOIDmode
> -			  ? GET_MODE (xop1)
> -			  : mode,
> -			  xop1, unsignedp);
> +  mode0 = GET_MODE (xop0) != VOIDmode ? GET_MODE (xop0) : mode;
> +  if (xmode0 != VOIDmode && xmode0 != mode0)
> +    {
> +      xop0 = convert_modes (xmode0, mode0, xop0, unsignedp);
> +      mode0 = xmode0;
> +    }
> +
> +  mode1 = GET_MODE (xop1) != VOIDmode ? GET_MODE (xop1) : mode;
> +  if (xmode1 != VOIDmode && xmode1 != mode1)
> +    {
> +      xop1 = convert_modes (xmode1, mode1, xop1, unsignedp);
> +      mode1 = xmode1;
> +    }

This smells like a target bug, but I can't quite put my finger
on exactly what's wrong, and I haven't debugged the original.

The backend has said xmode[01] is what it expects.  The only
reason you wouldn't write xmode[01] in the create_input_operand
line is if xmode[01] are VOIDmode.

That seems like mistake number one, particularly for a binop,
but I'll accept that for the nonce.  Which pattern triggered
the problem in the target?

Then we've got the conditionalization in the init of mode[01],
which is presumably to handle CONST_INT as an input.

What about something like

	xmode0 = insn_data...
	if (xmode0 == VOIDmode)
	  xmode0 = mode;

	mode0 = GET_MODE (xop0);
	if (mode0 == VOIDmode)
	  mode0 = mode;

	if (xmode0 != mode0)
	  convert_modes

	create_input_operand(..., xmode0)

?  That seems more obvious than what you have.  And I *think*
it should produce the same results.  If it doesn't, then this
whole block of code needs a lot more explanation.


r~

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-29 22:14                   ` Richard Henderson
@ 2011-03-30  9:13                     ` Richard Sandiford
  2011-03-30 15:23                       ` Richard Henderson
  0 siblings, 1 reply; 32+ messages in thread
From: Richard Sandiford @ 2011-03-30  9:13 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Mikael Pettersson, Andreas Krebbel, gcc-patches

Richard Henderson <rth@redhat.com> writes:
> On 03/29/2011 06:21 AM, Richard Sandiford wrote:
>> -  enum machine_mode mode0 = insn_data[(int) icode].operand[1].mode;
>> -  enum machine_mode mode1 = insn_data[(int) icode].operand[2].mode;
>> -  enum machine_mode tmp_mode;
>> +  enum machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
>> +  enum machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
>> +  enum machine_mode mode0, mode1, tmp_mode;
> ...
>> -  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
>> -    xop0 = convert_modes (mode0,
>> -			  GET_MODE (xop0) != VOIDmode
>> -			  ? GET_MODE (xop0)
>> -			  : mode,
>> -			  xop0, unsignedp);
>> -
>> -  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
>> -    xop1 = convert_modes (mode1,
>> -			  GET_MODE (xop1) != VOIDmode
>> -			  ? GET_MODE (xop1)
>> -			  : mode,
>> -			  xop1, unsignedp);
>> +  mode0 = GET_MODE (xop0) != VOIDmode ? GET_MODE (xop0) : mode;
>> +  if (xmode0 != VOIDmode && xmode0 != mode0)
>> +    {
>> +      xop0 = convert_modes (xmode0, mode0, xop0, unsignedp);
>> +      mode0 = xmode0;
>> +    }
>> +
>> +  mode1 = GET_MODE (xop1) != VOIDmode ? GET_MODE (xop1) : mode;
>> +  if (xmode1 != VOIDmode && xmode1 != mode1)
>> +    {
>> +      xop1 = convert_modes (xmode1, mode1, xop1, unsignedp);
>> +      mode1 = xmode1;
>> +    }
>
> This smells like a target bug, but I can't quite put my finger
> on exactly what's wrong, and I haven't debugged the original.
>
> The backend has said xmode[01] is what it expects.  The only
> reason you wouldn't write xmode[01] in the create_input_operand
> line is if xmode[01] are VOIDmode.

Right.  Sorry, I should have said, but the failure was actually from:

    case EXPAND_INPUT:
    input:
      gcc_assert (mode != VOIDmode);

expand_binop_direct passed the match_operand's mode to create_input_operand,
which was really the opposite of the intention.  The caller should be
passing the mode of the rtx, which it should always "know".

> That seems like mistake number one, particularly for a binop,
> but I'll accept that for the nonce.  Which pattern triggered
> the problem in the target?

It was ashrdi3:

(define_expand "ashrdi3"
  [(set (match_operand:DI 0 "register_operand" "")
	(ashiftrt:DI (match_operand:DI 1 "register_operand" "")
		     (match_operand 2 "const_int_operand" "")))]
  "!TARGET_COLDFIRE"

Which seems reasonable in this context, since the shift count isn't
really DImode.

> Then we've got the conditionalization in the init of mode[01],
> which is presumably to handle CONST_INT as an input.
>
> What about something like
>
> 	xmode0 = insn_data...
> 	if (xmode0 == VOIDmode)
> 	  xmode0 = mode;
>
> 	mode0 = GET_MODE (xop0);
> 	if (mode0 == VOIDmode)
> 	  mode0 = mode;
>
> 	if (xmode0 != mode0)
> 	  convert_modes
>
> 	create_input_operand(..., xmode0)
>
> ?  That seems more obvious than what you have.  And I *think*
> it should produce the same results.  If it doesn't, then this
> whole block of code needs a lot more explanation.

The problem is that a VOIDmode match_operand doesn't necessary imply
that "mode" is the right mode.  VOIDmode register-accepting operands
are only a warning, not an error, and are sometimes needed for
flag-specific variations.  They've traditionally not forced a conversion
to "mode".  E.g. if you have something like this on a 32-bit target:

  unsigned long long foo (unsigned long long x, int y)
  {
    return x >>= y;
  }

then op1 will be (reg:SI y).  Having a VOIDmode match_operand shouldn't
force that to be converted to (reg:DI y), whereas I think the sequence
above would.

Or to put it another way: as things stand, "mode" is only trustworthy
for CONST_INT opNs.  A VOIDmode match_operand doesn't imply that the
opN argument to expand_binop_directly is a CONST_INT, or even that
only CONST_INTs are acceptable to the target.

This comes back to the point that we really should know up-front what
modes op0 and op1 actually have.  (The thing I left as a future clean-up.)
Until then, the process implemented by yesterday's patch was supposed to be:

   - work out what mode opN actually has at this point in time
   - see if the target has specifically asked for a different mode
   - if so, convert the operand

This is directly equivalent to what create_convert_operand_from does:

    case EXPAND_CONVERT_FROM:
      if (GET_MODE (op->value) != VOIDmode)
	mode = GET_MODE (op->value);
      else
	/* The caller must tell us what mode this value has.  */
	gcc_assert (mode != VOIDmode);

      imode = insn_data[(int) icode].operand[opno].mode;
      if (imode != VOIDmode && imode != mode)
	{
	  op->value = convert_modes (imode, mode, op->value, op->unsigned_p);
	  mode = imode;
	}

But we have to break the flow there (rather than go on to coerce the operands)
because of the commutative thing.

Richard

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-30  9:13                     ` Richard Sandiford
@ 2011-03-30 15:23                       ` Richard Henderson
  0 siblings, 0 replies; 32+ messages in thread
From: Richard Henderson @ 2011-03-30 15:23 UTC (permalink / raw)
  To: Mikael Pettersson, Andreas Krebbel, gcc-patches, richard.sandiford

On 03/30/2011 01:53 AM, Richard Sandiford wrote:
> This comes back to the point that we really should know up-front what
> modes op0 and op1 actually have.  (The thing I left as a future clean-up.)
> Until then, the process implemented by yesterday's patch was supposed to be:
> 
>    - work out what mode opN actually has at this point in time
>    - see if the target has specifically asked for a different mode
>    - if so, convert the operand

Ok, I get it.  The patch is ok as-is.


r~

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-25 17:35               ` Richard Henderson
  2011-03-25 17:56                 ` Richard Sandiford
@ 2011-03-31 10:00                 ` Georg-Johann Lay
  2011-03-31 12:57                   ` Richard Sandiford
  1 sibling, 1 reply; 32+ messages in thread
From: Georg-Johann Lay @ 2011-03-31 10:00 UTC (permalink / raw)
  To: Richard Henderson; +Cc: gcc-patches, richard.sandiford

Richard Henderson schrieb:
> On 03/25/2011 05:41 AM, Georg-Johann Lay wrote:
>>> On 03/22/2011 06:48 PM, Richard Henderson wrote:
>>>
>>>> Ok.  Watch out for other target problems this week.
>> libgcc fails to build for avr (SVN 171446)
>>
>> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c: In function
>> '__negdi2':
>> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c:68:17:
>> internal compiler error: in maybe_gen_insn, at  optabs.c:7123
> 
> This is due to a miscommunication between the middle-end and the backend
> about how many arguments the setmemhi pattern takes.
> 
> (define_expand "setmemhi"
>   [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
>                    (match_operand 2 "const_int_operand" ""))
>               (use (match_operand:HI 1 "const_int_operand" ""))
>               (use (match_operand:HI 3 "const_int_operand" "n"))
>               (clobber (match_scratch:HI 4 ""))
>               (clobber (match_dup 5))])]
> 
> The match_scratch is counted in .n_operands, which makes the count of
> operands not equal 4, so we assume 6 operands are necessary.  We can
> fix this for the special case of avr by only assuming 6 operands when
> there are in fact 6 operands, but of course this could fail just as
> easily if there were two scratches.
> 
> All of which suggests that optional arguments to a named optab is a
> mistake that ought to be rectified.
> 
> I plan to commit the following after bootstrap and check.
> 
> 

Hi, there is still trouble with "setmemhi" on avr, again for the
negdi2 from libgcc:

#1  0x0839ccd7 in maybe_legitimize_operand (icode=CODE_FOR_setmemhi,
opno=4, op=0xbfffd22c) at ../../../gcc.gnu.org/trunk/gcc/optabs.c:7024
(gdb) p *op
$11 = {type = EXPAND_OUTPUT, unsigned_p = 0, unused = 0, mode =
VOIDmode, value = 0xb7d63360}
(gdb) p op->value
$12 = (rtx) 0xb7d63360
(gdb) pr
(use:CC (nil))


i.e. there is garbage in op->value

(gdb)
(gdb) frame 0
#0  fancy_abort (file=0x88099f0
"../../../gcc.gnu.org/trunk/gcc/optabs.c", line=7024,
function=0x880a280 "maybe_legitimize_operand") at
../../../gcc.gnu.org/trunk/gcc/diagnostic.c:893
(gdb)

Sources are as of svn trunk 4.7 (SVN 171773)

Reading specs from /mnt/nfs/home/georg/gnu/build/gcc-4.6-avr/gcc/specs
COLLECT_GCC=/mnt/nfs/home/georg/gnu/build/gcc-4.6-avr/gcc/xgcc
COLLECT_LTO_WRAPPER=/mnt/nfs/home/georg/gnu/build/gcc-4.6-avr/gcc/lto-wrapper
Target: avr
Configured with: ../../gcc.gnu.org/trunk/configure --target=avr
--prefix=/local/gnu/install/gcc-4.6 --enable-languages=c,c++
--disable-libssp --disable-libada --disable-nls --disable-shared

Johann

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-31 10:00                 ` Georg-Johann Lay
@ 2011-03-31 12:57                   ` Richard Sandiford
  2011-04-01 12:54                     ` Georg-Johann Lay
  0 siblings, 1 reply; 32+ messages in thread
From: Richard Sandiford @ 2011-03-31 12:57 UTC (permalink / raw)
  To: Georg-Johann Lay; +Cc: Richard Henderson, gcc-patches

Georg-Johann Lay <avr@gjlay.de> writes:
> Richard Henderson schrieb:
>> On 03/25/2011 05:41 AM, Georg-Johann Lay wrote:
>>>> On 03/22/2011 06:48 PM, Richard Henderson wrote:
>>>>
>>>>> Ok.  Watch out for other target problems this week.
>>> libgcc fails to build for avr (SVN 171446)
>>>
>>> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c: In function
>>> '__negdi2':
>>> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c:68:17:
>>> internal compiler error: in maybe_gen_insn, at  optabs.c:7123
>> 
>> This is due to a miscommunication between the middle-end and the backend
>> about how many arguments the setmemhi pattern takes.
>> 
>> (define_expand "setmemhi"
>>   [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
>>                    (match_operand 2 "const_int_operand" ""))
>>               (use (match_operand:HI 1 "const_int_operand" ""))
>>               (use (match_operand:HI 3 "const_int_operand" "n"))
>>               (clobber (match_scratch:HI 4 ""))
>>               (clobber (match_dup 5))])]
>> 
>> The match_scratch is counted in .n_operands, which makes the count of
>> operands not equal 4, so we assume 6 operands are necessary.  We can
>> fix this for the special case of avr by only assuming 6 operands when
>> there are in fact 6 operands, but of course this could fail just as
>> easily if there were two scratches.
>> 
>> All of which suggests that optional arguments to a named optab is a
>> mistake that ought to be rectified.
>> 
>> I plan to commit the following after bootstrap and check.
>> 
>> 
>
> Hi, there is still trouble with "setmemhi" on avr, again for the
> negdi2 from libgcc:
>
> #1  0x0839ccd7 in maybe_legitimize_operand (icode=CODE_FOR_setmemhi,
> opno=4, op=0xbfffd22c) at ../../../gcc.gnu.org/trunk/gcc/optabs.c:7024
> (gdb) p *op
> $11 = {type = EXPAND_OUTPUT, unsigned_p = 0, unused = 0, mode =
> VOIDmode, value = 0xb7d63360}
> (gdb) p op->value
> $12 = (rtx) 0xb7d63360
> (gdb) pr
> (use:CC (nil))
>
>
> i.e. there is garbage in op->value
>
> (gdb)
> (gdb) frame 0
> #0  fancy_abort (file=0x88099f0
> "../../../gcc.gnu.org/trunk/gcc/optabs.c", line=7024,
> function=0x880a280 "maybe_legitimize_operand") at
> ../../../gcc.gnu.org/trunk/gcc/diagnostic.c:893
> (gdb)

Yeah, as things stand, we need to set nops to 4 if was originally 5.

I'm testing a series of patches to make the number of generator
arguments available in insn_data.  I'll post them once they pass
(hopefully later today).

Richard

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Cleaning up expand optabs code
  2011-03-31 12:57                   ` Richard Sandiford
@ 2011-04-01 12:54                     ` Georg-Johann Lay
  0 siblings, 0 replies; 32+ messages in thread
From: Georg-Johann Lay @ 2011-04-01 12:54 UTC (permalink / raw)
  To: Georg-Johann Lay, Richard Henderson, gcc-patches, richard.sandiford

Richard Sandiford schrieb:
> Georg-Johann Lay <avr@gjlay.de> writes:
>> Richard Henderson schrieb:
>>> On 03/25/2011 05:41 AM, Georg-Johann Lay wrote:
>>>>> On 03/22/2011 06:48 PM, Richard Henderson wrote:
>>>>>
>>>>>> Ok.  Watch out for other target problems this week.
>>>> libgcc fails to build for avr (SVN 171446)
>>>>
>>>> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c: In function
>>>> '__negdi2':
>>>> ../../../../../gcc.gnu.org/trunk/libgcc/../gcc/libgcc2.c:68:17:
>>>> internal compiler error: in maybe_gen_insn, at  optabs.c:7123
>>> This is due to a miscommunication between the middle-end and the backend
>>> about how many arguments the setmemhi pattern takes.
>>>
>>> (define_expand "setmemhi"
>>>   [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
>>>                    (match_operand 2 "const_int_operand" ""))
>>>               (use (match_operand:HI 1 "const_int_operand" ""))
>>>               (use (match_operand:HI 3 "const_int_operand" "n"))
>>>               (clobber (match_scratch:HI 4 ""))
>>>               (clobber (match_dup 5))])]
>>>
>>> The match_scratch is counted in .n_operands, which makes the count of
>>> operands not equal 4, so we assume 6 operands are necessary.  We can
>>> fix this for the special case of avr by only assuming 6 operands when
>>> there are in fact 6 operands, but of course this could fail just as
>>> easily if there were two scratches.
>>>
>>> All of which suggests that optional arguments to a named optab is a
>>> mistake that ought to be rectified.
>>>
>>> I plan to commit the following after bootstrap and check.
>>>
>>>
>> Hi, there is still trouble with "setmemhi" on avr, again for the
>> negdi2 from libgcc:
>>
>> #1  0x0839ccd7 in maybe_legitimize_operand (icode=CODE_FOR_setmemhi,
>> opno=4, op=0xbfffd22c) at ../../../gcc.gnu.org/trunk/gcc/optabs.c:7024
>> (gdb) p *op
>> $11 = {type = EXPAND_OUTPUT, unsigned_p = 0, unused = 0, mode =
>> VOIDmode, value = 0xb7d63360}
>> (gdb) p op->value
>> $12 = (rtx) 0xb7d63360
>> (gdb) pr
>> (use:CC (nil))
>>
>>
>> i.e. there is garbage in op->value
>>
>> (gdb)
>> (gdb) frame 0
>> #0  fancy_abort (file=0x88099f0
>> "../../../gcc.gnu.org/trunk/gcc/optabs.c", line=7024,
>> function=0x880a280 "maybe_legitimize_operand") at
>> ../../../gcc.gnu.org/trunk/gcc/diagnostic.c:893
>> (gdb)
> 
> Yeah, as things stand, we need to set nops to 4 if was originally 5.
> 
> I'm testing a series of patches to make the number of generator
> arguments available in insn_data.  I'll post them once they pass
> (hopefully later today).
> 
> Richard

Thanks, I can build avr-gcc again.

Johann



^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2011-04-01 12:54 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-17 16:33 Cleaning up expand optabs code Richard Sandiford
2011-03-17 19:20 ` Richard Henderson
2011-03-19 19:53   ` Richard Sandiford
2011-03-21 19:27     ` Richard Henderson
2011-03-21 21:39       ` Richard Sandiford
2011-03-22 15:09       ` Richard Sandiford
2011-03-22 17:49         ` Richard Henderson
2011-03-23 18:01           ` Anatoly Sokolov
2011-03-24 10:04             ` Richard Sandiford
2011-03-24 12:01           ` Andreas Krebbel
2011-03-24 12:13             ` Richard Sandiford
2011-03-24 15:09               ` Richard Sandiford
2011-03-24 17:09               ` Richard Henderson
2011-03-29 12:26               ` Mikael Pettersson
2011-03-29 13:27                 ` Richard Sandiford
2011-03-29 14:27                   ` Mikael Pettersson
2011-03-29 22:01                     ` Richard Sandiford
2011-03-29 22:14                   ` Richard Henderson
2011-03-30  9:13                     ` Richard Sandiford
2011-03-30 15:23                       ` Richard Henderson
2011-03-25 12:58             ` Georg-Johann Lay
2011-03-25 13:00               ` Georg-Johann Lay
2011-03-25 17:35               ` Richard Henderson
2011-03-25 17:56                 ` Richard Sandiford
2011-03-25 18:30                   ` Richard Sandiford
2011-03-25 18:31                     ` Richard Sandiford
2011-03-25 19:23                   ` Richard Henderson
2011-03-26  2:51                     ` Richard Henderson
2011-03-31 10:00                 ` Georg-Johann Lay
2011-03-31 12:57                   ` Richard Sandiford
2011-04-01 12:54                     ` Georg-Johann Lay
2011-03-24 15:14         ` Richard Sandiford

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).