OSDN Git Service

PR rtl-optimization/50249
[pf3gnuchains/gcc-fork.git] / gcc / recog.c
index 1a4824a..d3ecb73 100644 (file)
@@ -42,7 +42,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "timevar.h"
 #include "tree-pass.h"
 #include "df.h"
-#include "toplev.h" /* exact_log2 may be used by targets */
 
 #ifndef STACK_PUSH_CODE
 #ifdef STACK_GROWS_DOWNWARD
@@ -119,6 +118,25 @@ init_recog (void)
 }
 
 \f
+/* Return true if labels in asm operands BODY are LABEL_REFs.  */
+
+static bool
+asm_labels_ok (rtx body)
+{
+  rtx asmop;
+  int i;
+
+  asmop = extract_asm_operands (body);
+  if (asmop == NULL_RTX)
+    return true;
+
+  for (i = 0; i < ASM_OPERANDS_LABEL_LENGTH (asmop); i++)
+    if (GET_CODE (ASM_OPERANDS_LABEL (asmop, i)) != LABEL_REF)
+      return false;
+
+  return true;
+}
+
 /* Check that X is an insn-body for an `asm' with operands
    and that the operands mentioned in it are legitimate.  */
 
@@ -130,6 +148,9 @@ check_asm_operands (rtx x)
   const char **constraints;
   int i;
 
+  if (!asm_labels_ok (x))
+    return 0;
+
   /* Post-reload, be more strict with things.  */
   if (reload_completed)
     {
@@ -278,8 +299,8 @@ canonicalize_change_group (rtx insn, rtx x)
       /* Oops, the caller has made X no longer canonical.
         Let's redo the changes in the correct order.  */
       rtx tem = XEXP (x, 0);
-      validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
-      validate_change (insn, &XEXP (x, 1), tem, 1);
+      validate_unshare_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
+      validate_unshare_change (insn, &XEXP (x, 1), tem, 1);
       return true;
     }
   else
@@ -639,6 +660,8 @@ simplify_while_replacing (rtx *loc, rtx to, rtx object,
                  (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode) -
                   offset);
 
+             gcc_assert (GET_MODE_PRECISION (wanted_mode)
+                         == GET_MODE_BITSIZE (wanted_mode));
              pos %= GET_MODE_BITSIZE (wanted_mode);
 
              newmem = adjust_address_nv (XEXP (x, 0), wanted_mode, offset);
@@ -931,7 +954,9 @@ general_operand (rtx op, enum machine_mode mode)
     return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode
             || mode == VOIDmode)
            && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
-           && LEGITIMATE_CONSTANT_P (op));
+           && targetm.legitimate_constant_p (mode == VOIDmode
+                                             ? GET_MODE (op)
+                                             : mode, op));
 
   /* Except for certain constants with VOIDmode, already checked for,
      OP's mode must match MODE if MODE specifies a mode.  */
@@ -1108,7 +1133,9 @@ immediate_operand (rtx op, enum machine_mode mode)
          && (GET_MODE (op) == mode || mode == VOIDmode
              || GET_MODE (op) == VOIDmode)
          && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
-         && LEGITIMATE_CONSTANT_P (op));
+         && targetm.legitimate_constant_p (mode == VOIDmode
+                                           ? GET_MODE (op)
+                                           : mode, op));
 }
 
 /* Returns 1 if OP is an operand that is a CONST_INT.  */
@@ -3134,22 +3161,105 @@ peep2_reinit_state (regset live)
 
 /* While scanning basic block BB, we found a match of length MATCH_LEN,
    starting at INSN.  Perform the replacement, removing the old insns and
-   replacing them with ATTEMPT.  Returns the last insn emitted.  */
+   replacing them with ATTEMPT.  Returns the last insn emitted, or NULL
+   if the replacement is rejected.  */
 
 static rtx
 peep2_attempt (basic_block bb, rtx insn, int match_len, rtx attempt)
 {
   int i;
-  rtx last, note, before_try, x;
+  rtx last, eh_note, as_note, before_try, x;
+  rtx old_insn, new_insn;
   bool was_call = false;
 
+  /* If we are splitting an RTX_FRAME_RELATED_P insn, do not allow it to
+     match more than one insn, or to be split into more than one insn.  */
+  old_insn = peep2_insn_data[peep2_current].insn;
+  if (RTX_FRAME_RELATED_P (old_insn))
+    {
+      bool any_note = false;
+      rtx note;
+
+      if (match_len != 0)
+       return NULL;
+
+      /* Look for one "active" insn.  I.e. ignore any "clobber" insns that
+        may be in the stream for the purpose of register allocation.  */
+      if (active_insn_p (attempt))
+       new_insn = attempt;
+      else
+       new_insn = next_active_insn (attempt);
+      if (next_active_insn (new_insn))
+       return NULL;
+
+      /* We have a 1-1 replacement.  Copy over any frame-related info.  */
+      RTX_FRAME_RELATED_P (new_insn) = 1;
+
+      /* Allow the backend to fill in a note during the split.  */
+      for (note = REG_NOTES (new_insn); note ; note = XEXP (note, 1))
+       switch (REG_NOTE_KIND (note))
+         {
+         case REG_FRAME_RELATED_EXPR:
+         case REG_CFA_DEF_CFA:
+         case REG_CFA_ADJUST_CFA:
+         case REG_CFA_OFFSET:
+         case REG_CFA_REGISTER:
+         case REG_CFA_EXPRESSION:
+         case REG_CFA_RESTORE:
+         case REG_CFA_SET_VDRAP:
+           any_note = true;
+           break;
+         default:
+           break;
+         }
+
+      /* If the backend didn't supply a note, copy one over.  */
+      if (!any_note)
+        for (note = REG_NOTES (old_insn); note ; note = XEXP (note, 1))
+         switch (REG_NOTE_KIND (note))
+           {
+           case REG_FRAME_RELATED_EXPR:
+           case REG_CFA_DEF_CFA:
+           case REG_CFA_ADJUST_CFA:
+           case REG_CFA_OFFSET:
+           case REG_CFA_REGISTER:
+           case REG_CFA_EXPRESSION:
+           case REG_CFA_RESTORE:
+           case REG_CFA_SET_VDRAP:
+             add_reg_note (new_insn, REG_NOTE_KIND (note), XEXP (note, 0));
+             any_note = true;
+             break;
+           default:
+             break;
+           }
+
+      /* If there still isn't a note, make sure the unwind info sees the
+        same expression as before the split.  */
+      if (!any_note)
+       {
+         rtx old_set, new_set;
+
+         /* The old insn had better have been simple, or annotated.  */
+         old_set = single_set (old_insn);
+         gcc_assert (old_set != NULL);
+
+         new_set = single_set (new_insn);
+         if (!new_set || !rtx_equal_p (new_set, old_set))
+           add_reg_note (new_insn, REG_FRAME_RELATED_EXPR, old_set);
+       }
+
+      /* Copy prologue/epilogue status.  This is required in order to keep
+        proper placement of EPILOGUE_BEG and the DW_CFA_remember_state.  */
+      maybe_copy_prologue_epilogue_insn (old_insn, new_insn);
+    }
+
   /* If we are splitting a CALL_INSN, look for the CALL_INSN
      in SEQ and copy our CALL_INSN_FUNCTION_USAGE and other
      cfg-related call notes.  */
   for (i = 0; i <= match_len; ++i)
     {
       int j;
-      rtx old_insn, new_insn, note;
+      rtx note;
 
       j = peep2_buf_position (peep2_current + i);
       old_insn = peep2_insn_data[j].insn;
@@ -3195,9 +3305,21 @@ peep2_attempt (basic_block bb, rtx insn, int match_len, rtx attempt)
       break;
     }
 
-  i = peep2_buf_position (peep2_current + match_len);
+  /* If we matched any instruction that had a REG_ARGS_SIZE, then
+     move those notes over to the new sequence.  */
+  as_note = NULL;
+  for (i = match_len; i >= 0; --i)
+    {
+      int j = peep2_buf_position (peep2_current + i);
+      old_insn = peep2_insn_data[j].insn;
 
-  note = find_reg_note (peep2_insn_data[i].insn, REG_EH_REGION, NULL_RTX);
+      as_note = find_reg_note (old_insn, REG_ARGS_SIZE, NULL);
+      if (as_note)
+       break;
+    }
+
+  i = peep2_buf_position (peep2_current + match_len);
+  eh_note = find_reg_note (peep2_insn_data[i].insn, REG_EH_REGION, NULL_RTX);
 
   /* Replace the old sequence with the new.  */
   last = emit_insn_after_setloc (attempt,
@@ -3207,7 +3329,7 @@ peep2_attempt (basic_block bb, rtx insn, int match_len, rtx attempt)
   delete_insn_chain (insn, peep2_insn_data[i].insn, false);
 
   /* Re-insert the EH_REGION notes.  */
-  if (note || (was_call && nonlocal_goto_handler_labels))
+  if (eh_note || (was_call && nonlocal_goto_handler_labels))
     {
       edge eh_edge;
       edge_iterator ei;
@@ -3216,8 +3338,8 @@ peep2_attempt (basic_block bb, rtx insn, int match_len, rtx attempt)
        if (eh_edge->flags & (EDGE_EH | EDGE_ABNORMAL_CALL))
          break;
 
-      if (note)
-       copy_reg_eh_region_note_backward (note, last, before_try);
+      if (eh_note)
+       copy_reg_eh_region_note_backward (eh_note, last, before_try);
 
       if (eh_edge)
        for (x = last; x != before_try; x = PREV_INSN (x))
@@ -3250,6 +3372,10 @@ peep2_attempt (basic_block bb, rtx insn, int match_len, rtx attempt)
       peep2_do_cleanup_cfg |= purge_dead_edges (bb);
     }
 
+  /* Re-insert the ARGS_SIZE notes.  */
+  if (as_note)
+    fixup_args_size_notes (before_try, last, INTVAL (XEXP (as_note, 0)));
+
   /* If we generated a jump instruction, it won't have
      JUMP_LABEL set.  Recompute after we're done.  */
   for (x = last; x != before_try; x = PREV_INSN (x))
@@ -3322,18 +3448,14 @@ peep2_fill_buffer (basic_block bb, rtx insn, regset live)
   if (peep2_current_count == MAX_INSNS_PER_PEEP2)
     return false;
 
-  /* If an insn has RTX_FRAME_RELATED_P set, peephole substitution would lose
-     the REG_FRAME_RELATED_EXPR that is attached.  */
+  /* If an insn has RTX_FRAME_RELATED_P set, do not allow it to be matched with
+     any other pattern, lest it change the semantics of the frame info.  */
   if (RTX_FRAME_RELATED_P (insn))
     {
       /* Let the buffer drain first.  */
       if (peep2_current_count > 0)
        return false;
-      /* Step over the insn then return true without adding the insn
-        to the buffer; this will cause us to process the next
-        insn.  */
-      df_simulate_one_insn_forwards (bb, insn, live);
-      return true;
+      /* Now the insn will be the only thing in the buffer.  */
     }
 
   pos = peep2_buf_position (peep2_current + peep2_current_count);
@@ -3412,16 +3534,17 @@ peephole2_optimize (void)
          attempt = peephole2_insns (PATTERN (head), head, &match_len);
          if (attempt != NULL)
            {
-             rtx last;
-             last = peep2_attempt (bb, head, match_len, attempt);
-             peep2_update_life (bb, match_len, last, PREV_INSN (attempt));
-           }
-         else
-           {
-             /* If no match, advance the buffer by one insn.  */
-             peep2_current = peep2_buf_position (peep2_current + 1);
-             peep2_current_count--;
+             rtx last = peep2_attempt (bb, head, match_len, attempt);
+             if (last)
+               {
+                 peep2_update_life (bb, match_len, last, PREV_INSN (attempt));
+                 continue;
+               }
            }
+
+         /* No match: advance the buffer by one insn.  */
+         peep2_current = peep2_buf_position (peep2_current + 1);
+         peep2_current_count--;
        }
     }
 
@@ -3613,7 +3736,7 @@ struct rtl_opt_pass pass_peephole2 =
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   TODO_df_finish | TODO_verify_rtl_sharing |
-  TODO_dump_func                       /* todo_flags_finish */
+  0                                    /* todo_flags_finish */
  }
 };
 
@@ -3639,7 +3762,7 @@ struct rtl_opt_pass pass_split_all_insns =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func                        /* todo_flags_finish */
+  0                                     /* todo_flags_finish */
  }
 };
 
@@ -3669,7 +3792,7 @@ struct rtl_opt_pass pass_split_after_reload =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func                        /* todo_flags_finish */
+  0                                     /* todo_flags_finish */
  }
 };
 
@@ -3713,7 +3836,7 @@ struct rtl_opt_pass pass_split_before_regstack =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func                        /* todo_flags_finish */
+  0                                     /* todo_flags_finish */
  }
 };
 
@@ -3751,8 +3874,7 @@ struct rtl_opt_pass pass_split_before_sched2 =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_verify_flow |
-  TODO_dump_func                        /* todo_flags_finish */
+  TODO_verify_flow                      /* todo_flags_finish */
  }
 };
 
@@ -3783,6 +3905,6 @@ struct rtl_opt_pass pass_split_for_shorten_branches =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func | TODO_verify_rtl_sharing /* todo_flags_finish */
+  TODO_verify_rtl_sharing               /* todo_flags_finish */
  }
 };