OSDN Git Service

Add reference to PR middle-end/48660
[pf3gnuchains/gcc-fork.git] / gcc / caller-save.c
index af9b2da..39bccc4 100644 (file)
@@ -1,6 +1,6 @@
 /* Save and restore call-clobbered registers which are live across a call.
    Copyright (C) 1989, 1992, 1994, 1995, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -35,7 +35,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "function.h"
 #include "expr.h"
 #include "diagnostic-core.h"
-#include "toplev.h"
 #include "tm_p.h"
 #include "addresses.h"
 #include "output.h"
@@ -83,7 +82,7 @@ static int reg_restore_code (int, enum machine_mode);
 
 struct saved_hard_reg;
 static void initiate_saved_hard_regs (void);
-static struct saved_hard_reg *new_saved_hard_reg (int, int);
+static void new_saved_hard_reg (int, int);
 static void finish_saved_hard_regs (void);
 static int saved_hard_reg_compare_func (const void *, const void *);
 
@@ -116,15 +115,19 @@ reg_save_code (int reg, enum machine_mode mode)
   if (cached_reg_save_code[reg][mode])
      return cached_reg_save_code[reg][mode];
   if (!HARD_REGNO_MODE_OK (reg, mode))
-     {
-       cached_reg_save_code[reg][mode] = -1;
-       cached_reg_restore_code[reg][mode] = -1;
-       return -1;
-     }
+    {
+      /* Depending on how HARD_REGNO_MODE_OK is defined, range propagation
+        might deduce here that reg >= FIRST_PSEUDO_REGISTER.  So the assert
+        below silences a warning.  */
+      gcc_assert (reg < FIRST_PSEUDO_REGISTER);
+      cached_reg_save_code[reg][mode] = -1;
+      cached_reg_restore_code[reg][mode] = -1;
+      return -1;
+    }
 
   /* Update the register number and modes of the register
      and memory operand.  */
-  SET_REGNO (test_reg, reg);
+  SET_REGNO_RAW (test_reg, reg);
   PUT_MODE (test_reg, mode);
   PUT_MODE (test_mem, mode);
 
@@ -228,7 +231,8 @@ init_caller_save (void)
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     if (TEST_HARD_REG_BIT
        (reg_class_contents
-        [(int) base_reg_class (regno_save_mode[i][1], PLUS, CONST_INT)], i))
+        [(int) base_reg_class (regno_save_mode[i][1], ADDR_SPACE_GENERIC,
+                               PLUS, CONST_INT)], i))
       break;
 
   gcc_assert (i < FIRST_PSEUDO_REGISTER);
@@ -343,7 +347,7 @@ initiate_saved_hard_regs (void)
 
 /* Allocate and return new saved hard register with given REGNO and
    CALL_FREQ.  */
-static struct saved_hard_reg *
+static void
 new_saved_hard_reg (int regno, int call_freq)
 {
   struct saved_hard_reg *saved_reg;
@@ -356,7 +360,6 @@ new_saved_hard_reg (int regno, int call_freq)
   saved_reg->call_freq = call_freq;
   saved_reg->first_p = FALSE;
   saved_reg->next = -1;
-  return saved_reg;
 }
 
 /* Free memory allocated for the saved hard registers.  */
@@ -413,101 +416,93 @@ saved_hard_reg_compare_func (const void *v1p, const void *v2p)
 void
 setup_save_areas (void)
 {
-  int i, j, k;
-  unsigned int r;
+  int i, j, k, freq;
   HARD_REG_SET hard_regs_used;
+  struct saved_hard_reg *saved_reg;
+  rtx insn;
+  struct insn_chain *chain, *next;
+  unsigned int regno;
+  HARD_REG_SET hard_regs_to_save, used_regs, this_insn_sets;
+  reg_set_iterator rsi;
 
-  /* Allocate space in the save area for the largest multi-register
-     pseudos first, then work backwards to single register
-     pseudos.  */
-
-  /* Find and record all call-used hard-registers in this function.  */
   CLEAR_HARD_REG_SET (hard_regs_used);
-  for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
-    if (reg_renumber[i] >= 0 && REG_N_CALLS_CROSSED (i) > 0)
-      {
-       unsigned int regno = reg_renumber[i];
-       unsigned int endregno
-         = end_hard_regno (GET_MODE (regno_reg_rtx[i]), regno);
-       for (r = regno; r < endregno; r++)
-         if (call_used_regs[r])
-           SET_HARD_REG_BIT (hard_regs_used, r);
-      }
 
-  if (optimize && flag_ira_share_save_slots)
+  /* Find every CALL_INSN and record which hard regs are live across the
+     call into HARD_REG_MAP and HARD_REGS_USED.  */
+  initiate_saved_hard_regs ();
+  /* Create hard reg saved regs.  */
+  for (chain = reload_insn_chain; chain != 0; chain = next)
     {
-      rtx insn, slot;
-      struct insn_chain *chain, *next;
-      char *saved_reg_conflicts;
-      unsigned int regno;
-      int next_k, freq;
-      struct saved_hard_reg *saved_reg, *saved_reg2, *saved_reg3;
-      int call_saved_regs_num;
-      struct saved_hard_reg *call_saved_regs[FIRST_PSEUDO_REGISTER];
-      HARD_REG_SET hard_regs_to_save, used_regs, this_insn_sets;
-      reg_set_iterator rsi;
-      int best_slot_num;
-      int prev_save_slots_num;
-      rtx prev_save_slots[FIRST_PSEUDO_REGISTER];
-
-      initiate_saved_hard_regs ();
-      /* Create hard reg saved regs.  */
-      for (chain = reload_insn_chain; chain != 0; chain = next)
+      insn = chain->insn;
+      next = chain->next;
+      if (!CALL_P (insn)
+         || find_reg_note (insn, REG_NORETURN, NULL))
+       continue;
+      freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn));
+      REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
+                              &chain->live_throughout);
+      COPY_HARD_REG_SET (used_regs, call_used_reg_set);
+
+      /* Record all registers set in this call insn.  These don't
+        need to be saved.  N.B. the call insn might set a subreg
+        of a multi-hard-reg pseudo; then the pseudo is considered
+        live during the call, but the subreg that is set
+        isn't.  */
+      CLEAR_HARD_REG_SET (this_insn_sets);
+      note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets);
+      /* Sibcalls are considered to set the return value.  */
+      if (SIBLING_CALL_P (insn) && crtl->return_rtx)
+       mark_set_regs (crtl->return_rtx, NULL_RTX, &this_insn_sets);
+
+      AND_COMPL_HARD_REG_SET (used_regs, call_fixed_reg_set);
+      AND_COMPL_HARD_REG_SET (used_regs, this_insn_sets);
+      AND_HARD_REG_SET (hard_regs_to_save, used_regs);
+      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+       if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+         {
+           if (hard_reg_map[regno] != NULL)
+             hard_reg_map[regno]->call_freq += freq;
+           else
+             new_saved_hard_reg (regno, freq);
+           SET_HARD_REG_BIT (hard_regs_used, regno);
+         }
+      /* Look through all live pseudos, mark their hard registers.  */
+      EXECUTE_IF_SET_IN_REG_SET
+       (&chain->live_throughout, FIRST_PSEUDO_REGISTER, regno, rsi)
        {
-         insn = chain->insn;
-         next = chain->next;
-         if (!CALL_P (insn)
-             || find_reg_note (insn, REG_NORETURN, NULL))
-           continue;
-         freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn));
-         REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
-                                  &chain->live_throughout);
-         COPY_HARD_REG_SET (used_regs, call_used_reg_set);
+         int r = reg_renumber[regno];
+         int bound;
 
-         /* Record all registers set in this call insn.  These don't
-            need to be saved.  N.B. the call insn might set a subreg
-            of a multi-hard-reg pseudo; then the pseudo is considered
-            live during the call, but the subreg that is set
-            isn't.  */
-         CLEAR_HARD_REG_SET (this_insn_sets);
-         note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets);
-         /* Sibcalls are considered to set the return value.  */
-         if (SIBLING_CALL_P (insn) && crtl->return_rtx)
-           mark_set_regs (crtl->return_rtx, NULL_RTX, &this_insn_sets);
+         if (r < 0)
+           continue;
 
-         AND_COMPL_HARD_REG_SET (used_regs, call_fixed_reg_set);
-         AND_COMPL_HARD_REG_SET (used_regs, this_insn_sets);
-         AND_HARD_REG_SET (hard_regs_to_save, used_regs);
-         for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-           if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+         bound = r + hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+         for (; r < bound; r++)
+           if (TEST_HARD_REG_BIT (used_regs, r))
              {
-               if (hard_reg_map[regno] != NULL)
-                 hard_reg_map[regno]->call_freq += freq;
+               if (hard_reg_map[r] != NULL)
+                 hard_reg_map[r]->call_freq += freq;
                else
-                 saved_reg = new_saved_hard_reg (regno, freq);
+                 new_saved_hard_reg (r, freq);
+                SET_HARD_REG_BIT (hard_regs_to_save, r);
+                SET_HARD_REG_BIT (hard_regs_used, r);
              }
-         /* Look through all live pseudos, mark their hard registers.  */
-         EXECUTE_IF_SET_IN_REG_SET
-           (&chain->live_throughout, FIRST_PSEUDO_REGISTER, regno, rsi)
-           {
-             int r = reg_renumber[regno];
-             int bound;
+       }
+    }
 
-             if (r < 0)
-               continue;
+  /* If requested, figure out which hard regs can share save slots.  */
+  if (optimize && flag_ira_share_save_slots)
+    {
+      rtx slot;
+      char *saved_reg_conflicts;
+      int next_k;
+      struct saved_hard_reg *saved_reg2, *saved_reg3;
+      int call_saved_regs_num;
+      struct saved_hard_reg *call_saved_regs[FIRST_PSEUDO_REGISTER];
+      int best_slot_num;
+      int prev_save_slots_num;
+      rtx prev_save_slots[FIRST_PSEUDO_REGISTER];
 
-             bound = r + hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
-             for (; r < bound; r++)
-               if (TEST_HARD_REG_BIT (used_regs, r))
-                 {
-                   if (hard_reg_map[r] != NULL)
-                     hard_reg_map[r]->call_freq += freq;
-                   else
-                     saved_reg = new_saved_hard_reg (r, freq);
-                   SET_HARD_REG_BIT (hard_regs_to_save, r);
-                 }
-           }
-       }
       /* Find saved hard register conflicts.  */
       saved_reg_conflicts = (char *) xmalloc (saved_regs_num * saved_regs_num);
       memset (saved_reg_conflicts, 0, saved_regs_num * saved_regs_num);
@@ -652,7 +647,8 @@ setup_save_areas (void)
                  saved_reg->slot
                    = assign_stack_local_1
                      (regno_save_mode[regno][1],
-                      GET_MODE_SIZE (regno_save_mode[regno][1]), 0, true);
+                      GET_MODE_SIZE (regno_save_mode[regno][1]), 0,
+                      ASLK_REDUCE_ALIGN);
                  if (dump_file != NULL)
                    fprintf (dump_file, "%d uses a new slot\n", regno);
                }
@@ -665,8 +661,10 @@ setup_save_areas (void)
     }
   else
     {
-      /* Now run through all the call-used hard-registers and allocate
-        space for them in the caller-save area.  Try to allocate space
+      /* We are not sharing slots. 
+
+        Run through all the call-used hard-registers and allocate
+        space for each in the caller-save area.  Try to allocate space
         in a manner which allows multi-register saves/restores to be done.  */
 
       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
@@ -708,7 +706,7 @@ setup_save_areas (void)
            regno_save_mem[i][j]
              = assign_stack_local_1 (regno_save_mode[i][j],
                                      GET_MODE_SIZE (regno_save_mode[i][j]),
-                                     0, true);
+                                     0, ASLK_REDUCE_ALIGN);
 
            /* Setup single word save area just in case...  */
            for (k = 0; k < j; k++)
@@ -763,6 +761,7 @@ save_call_clobbered_regs (void)
          if (n_regs_saved)
            {
              int regno;
+             HARD_REG_SET this_insn_sets;
 
              if (code == JUMP_INSN)
                /* Restore all registers if this is a JUMP_INSN.  */
@@ -777,7 +776,17 @@ save_call_clobbered_regs (void)
 
              for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
                if (TEST_HARD_REG_BIT (referenced_regs, regno))
-                 regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS, save_mode);
+                 regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS,
+                                          save_mode);
+             /* If a saved register is set after the call, this means we no
+                longer should restore it.  This can happen when parts of a
+                multi-word pseudo do not conflict with other pseudos, so
+                IRA may allocate the same hard register for both.  One may
+                be live across the call, while the other is set
+                afterwards.  */
+             CLEAR_HARD_REG_SET (this_insn_sets);
+             note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets);
+             AND_COMPL_HARD_REG_SET (hard_regs_saved, this_insn_sets);
            }
 
          if (code == CALL_INSN
@@ -857,7 +866,10 @@ save_call_clobbered_regs (void)
             remain saved.  If the last insn in the block is a JUMP_INSN, put
             the restore before the insn, otherwise, put it after the insn.  */
 
-         if (DEBUG_INSN_P (insn) && last && last->block == chain->block)
+         if (n_regs_saved
+             && DEBUG_INSN_P (insn)
+             && last
+             && last->block == chain->block)
            {
              rtx ins, prev;
              basic_block bb = BLOCK_FOR_INSN (insn);
@@ -1015,10 +1027,10 @@ mark_referenced_regs (rtx *loc, refmarker_fn *mark, void *arg)
       /* If this is a pseudo that did not get a hard register, scan its
         memory location, since it might involve the use of another
         register, which might be saved.  */
-      else if (reg_equiv_mem[regno] != 0)
-       mark_referenced_regs (&XEXP (reg_equiv_mem[regno], 0), mark, arg);
-      else if (reg_equiv_address[regno] != 0)
-       mark_referenced_regs (&reg_equiv_address[regno], mark, arg);
+      else if (reg_equiv_mem (regno) != 0)
+       mark_referenced_regs (&XEXP (reg_equiv_mem (regno), 0), mark, arg);
+      else if (reg_equiv_address (regno) != 0)
+       mark_referenced_regs (&reg_equiv_address (regno), mark, arg);
       return;
     }
 
@@ -1300,7 +1312,7 @@ insert_save (struct insn_chain *chain, int before_p, int regno,
 static int
 add_used_regs_1 (rtx *loc, void *data)
 {
-  int regno, i;
+  unsigned int regno;
   regset live;
   rtx x;
 
@@ -1309,11 +1321,10 @@ add_used_regs_1 (rtx *loc, void *data)
   if (REG_P (x))
     {
       regno = REGNO (x);
-      if (!HARD_REGISTER_NUM_P (regno))
+      if (HARD_REGISTER_NUM_P (regno))
+       bitmap_set_range (live, regno, hard_regno_nregs[regno][GET_MODE (x)]);
+      else
        regno = reg_renumber[regno];
-      if (regno >= 0)
-       for (i = hard_regno_nregs[regno][GET_MODE (x)] - 1; i >= 0; i--)
-         SET_REGNO_REG_SET (live, regno + i);
     }
   return 0;
 }