OSDN Git Service

* config/s390/s390.c (s390_split_branches): Add return
authoruweigand <uweigand@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 15 Oct 2002 16:27:34 +0000 (16:27 +0000)
committeruweigand <uweigand@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 15 Oct 2002 16:27:34 +0000 (16:27 +0000)
value.  Add parameters TEMP_REG and TEMP_USED.  Use unspec 104.

(find_base_register_in_addr): New function.
(find_base_register_ref): New function.
(replace_base_register_ref): New function.

(struct constant_pool): Add members pool_insn, insns, and anchor.
Remove member last_insn.
(s390_start_pool): Initialize them.
(s390_end_pool): Emit pool placeholder insn.
(s390_add_pool_insn): New function.
(s390_find_pool): Use insns bitmap instead of addresses.
(s390_dump_pool): Replace placeholder insn.  Emit anchor.
Replace unspec 104 by local-pool-relative references.
(s390_output_constant_pool): Output anchor label if required.
(s390_output_symbolic_const): Handle unspec 104 and 105.
(s390_add_pool): Remove, replace by ...
(s390_add_constant, s390_find_constant): ... these new functions.
(s390_add_anchor): New function.

(s390_chunkify_pool): Delete, replace by ...
(s390_chunkify_start, s390_chunkify_finish,
s390_chunkify_cancel): ... these new functions.
(s390_optimize_prolog): Add parameter TEMP_REGNO.
Recompute register live data for special registers.
(s390_fixup_clobbered_return_reg): New function.
(s390_machine_dependent_reorg): Rewrite to use new
s390_chunkify_... routines.

config/s390/s390.md ("reload_base"): Rename to ...
("reload_base_31"): ... this.
("reload_base_64"): New insn.
("reload_base2"): Remove.
("reload_anchor"): New insn.
("pool"): New insn.

s390.c (s390_pool_overflow): Remove.
s390.h (s390_pool_overflow): Likewise.
s390.md ("cjump", "icjump", "doloop_si"): Remove s390_pool_overflow.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@58168 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/s390/s390.c
gcc/config/s390/s390.h
gcc/config/s390/s390.md

index 00f190e..b7b6667 100644 (file)
@@ -1,3 +1,46 @@
+2002-10-15  Ulrich Weigand  <uweigand@de.ibm.com>
+
+       * config/s390/s390.c (s390_split_branches): Add return
+       value.  Add parameters TEMP_REG and TEMP_USED.  Use unspec 104.
+
+       (find_base_register_in_addr): New function.
+       (find_base_register_ref): New function.
+       (replace_base_register_ref): New function.
+
+       (struct constant_pool): Add members pool_insn, insns, and anchor.
+       Remove member last_insn.
+       (s390_start_pool): Initialize them.
+       (s390_end_pool): Emit pool placeholder insn. 
+       (s390_add_pool_insn): New function.
+       (s390_find_pool): Use insns bitmap instead of addresses.
+       (s390_dump_pool): Replace placeholder insn.  Emit anchor.
+       Replace unspec 104 by local-pool-relative references.
+       (s390_output_constant_pool): Output anchor label if required.
+       (s390_output_symbolic_const): Handle unspec 104 and 105.
+       (s390_add_pool): Remove, replace by ...
+       (s390_add_constant, s390_find_constant): ... these new functions.
+       (s390_add_anchor): New function.
+
+       (s390_chunkify_pool): Delete, replace by ...
+       (s390_chunkify_start, s390_chunkify_finish, 
+       s390_chunkify_cancel): ... these new functions.
+       (s390_optimize_prolog): Add parameter TEMP_REGNO.
+       Recompute register live data for special registers.
+       (s390_fixup_clobbered_return_reg): New function.
+       (s390_machine_dependent_reorg): Rewrite to use new
+       s390_chunkify_... routines.
+
+       config/s390/s390.md ("reload_base"): Rename to ...
+       ("reload_base_31"): ... this.
+       ("reload_base_64"): New insn.
+       ("reload_base2"): Remove.
+       ("reload_anchor"): New insn.
+       ("pool"): New insn.
+
+       s390.c (s390_pool_overflow): Remove.
+       s390.h (s390_pool_overflow): Likewise.
+       s390.md ("cjump", "icjump", "doloop_si"): Remove s390_pool_overflow.
+
 Tue Oct 15 16:51:04 2002  J"orn Rennecke <joern.rennecke@superh.com>
 
        * sh.md (movv8qi_i+2): Don't split if source is -1.
index 50dbc36..d84b727 100644 (file)
@@ -130,11 +130,14 @@ static int general_s_operand PARAMS ((rtx, enum machine_mode, int));
 static int s390_decompose_address PARAMS ((rtx, struct s390_address *));
 static int reg_used_in_mem_p PARAMS ((int, rtx));
 static int addr_generation_dependency_p PARAMS ((rtx, rtx));
-static void s390_split_branches PARAMS ((void));
+static int s390_split_branches PARAMS ((rtx, bool *));
 static void find_constant_pool_ref PARAMS ((rtx, rtx *));
 static void replace_constant_pool_ref PARAMS ((rtx *, rtx, rtx));
-static void s390_chunkify_pool PARAMS ((void));
-static void s390_optimize_prolog PARAMS ((void));
+static int find_base_register_in_addr PARAMS ((struct s390_address *));
+static bool find_base_register_ref PARAMS ((rtx));
+static void replace_base_register_ref PARAMS ((rtx *, rtx));
+static void s390_optimize_prolog PARAMS ((int));
+static bool s390_fixup_clobbered_return_reg PARAMS ((rtx));
 static int find_unused_clobbered_reg PARAMS ((void));
 static void s390_frame_info PARAMS ((struct s390_frame *));
 static rtx save_fpr PARAMS ((rtx, int, int));
@@ -2569,9 +2572,14 @@ s390_output_symbolic_const (file, x)
       switch (XINT (x, 1))
         {
         case 100:
+        case 104:
          s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
           fprintf (file, "-.LT%d", current_function_funcdef_no);
          break;
+        case 105:
+          fprintf (file, ".LT%d-", current_function_funcdef_no);
+         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
+         break;
        case 110:
          s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
          fprintf (file, "@GOT12");
@@ -2985,12 +2993,19 @@ s390_adjust_priority (insn, priority)
 }
 
 
-/* Split all branches that exceed the maximum distance.  */
+/* Split all branches that exceed the maximum distance.  
+   Returns true if this created a new literal pool entry.  
+
+   Code generated by this routine is allowed to use
+   TEMP_REG as temporary scratch register.  If this is
+   done, TEMP_USED is set to true.  */
 
-static void 
-s390_split_branches ()
+static int 
+s390_split_branches (temp_reg, temp_used)
+     rtx temp_reg;
+     bool *temp_used;
 {
-  rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
+  int new_literal = 0;
   rtx insn, pat, tmp, target;
   rtx *label;
 
@@ -3030,7 +3045,7 @@ s390_split_branches ()
       if (get_attr_length (insn) <= (TARGET_64BIT ? 6 : 4))
        continue;
 
-      regs_ever_live[RETURN_REGNUM] = 1;
+      *temp_used = 1;
 
       if (TARGET_64BIT)
        {
@@ -3041,6 +3056,7 @@ s390_split_branches ()
        }
       else if (!flag_pic)
        {
+         new_literal = 1;
          tmp = force_const_mem (Pmode, *label);
          tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
          INSN_ADDRESSES_NEW (tmp, -1);
@@ -3049,7 +3065,8 @@ s390_split_branches ()
        }
       else
        {
-         tmp = gen_rtx_UNSPEC (SImode, gen_rtvec (1, *label), 100);
+         new_literal = 1;
+         tmp = gen_rtx_UNSPEC (SImode, gen_rtvec (1, *label), 104);
          tmp = gen_rtx_CONST (SImode, tmp);
          tmp = force_const_mem (SImode, tmp);
          tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
@@ -3062,6 +3079,8 @@ s390_split_branches ()
       if (!validate_change (insn, label, target, 0))
        abort ();
     }
+
+  return new_literal;
 }
 
 
@@ -3179,6 +3198,153 @@ replace_constant_pool_ref (x, ref, addr)
     }
 }
 
+/* Check whether ADDR is an address that uses the base register, 
+   without actually constituting a literal pool access.  (This happens
+   in 31-bit PIC mode, where the base register is used as anchor for
+   relative addressing of local symbols.) 
+
+   Returns 1 if the base register occupies the base slot,
+   returns 2 if the base register occupies the index slot,
+   returns 0 if the address is not of this form.  */
+
+static int
+find_base_register_in_addr (addr)
+     struct s390_address *addr;
+{
+  /* If DISP is complex, we might have a literal pool reference.  */
+  if (addr->disp && GET_CODE (addr->disp) != CONST_INT)
+    return 0;
+
+  if (addr->base && REG_P (addr->base) && REGNO (addr->base) == BASE_REGISTER)
+    return 1;
+
+  if (addr->indx && REG_P (addr->indx) && REGNO (addr->indx) == BASE_REGISTER)
+    return 2;
+
+  return 0;
+}
+
+/* Return true if X contains an address that uses the base register, 
+   without actually constituting a literal pool access.  */
+
+static bool
+find_base_register_ref (x)
+     rtx x;
+{
+  bool retv = FALSE;
+  struct s390_address addr;
+  int i, j;
+  const char *fmt;
+
+  /* Addresses can only occur inside a MEM ...  */
+  if (GET_CODE (x) == MEM)
+    {
+      if (s390_decompose_address (XEXP (x, 0), &addr)
+         && find_base_register_in_addr (&addr))
+       return TRUE;
+    }
+
+  /* ... or a load-address type pattern.  */
+  if (GET_CODE (x) == SET && GET_CODE (SET_DEST (x)) == REG)
+    {
+      if (s390_decompose_address (SET_SRC (x), &addr)
+         && find_base_register_in_addr (&addr))
+       return TRUE;
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (x));
+  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+        {
+          retv |= find_base_register_ref (XEXP (x, i));
+        }
+      else if (fmt[i] == 'E')
+        {
+          for (j = 0; j < XVECLEN (x, i); j++)
+            retv |= find_base_register_ref (XVECEXP (x, i, j));
+        }
+    }
+
+  return retv;
+}
+
+/* If X contains an address that uses the base register,
+   without actually constituting a literal pool access,
+   replace the base register with REPL in all such cases.
+
+   Handles both MEMs and load address patterns.  */
+
+static void
+replace_base_register_ref (x, repl)
+     rtx *x;
+     rtx repl;
+{
+  struct s390_address addr;
+  rtx new_addr;
+  int i, j, pos;
+  const char *fmt;
+
+  /* Addresses can only occur inside a MEM ...  */
+  if (GET_CODE (*x) == MEM)
+    {
+      if (s390_decompose_address (XEXP (*x, 0), &addr)
+         && (pos = find_base_register_in_addr (&addr)))
+       {
+         if (pos == 1)
+           addr.base = repl;
+         else
+           addr.indx = repl;
+
+         new_addr = addr.base;
+         if (addr.indx)
+           new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx);
+         if (addr.disp)
+           new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp);
+
+         *x = replace_equiv_address (*x, new_addr);
+         return;
+       }
+    }
+
+  /* ... or a load-address type pattern.  */
+  if (GET_CODE (*x) == SET && GET_CODE (SET_DEST (*x)) == REG)
+    {
+      if (s390_decompose_address (SET_SRC (*x), &addr)
+         && (pos = find_base_register_in_addr (&addr)))
+       {
+         if (pos == 1)
+           addr.base = repl;
+         else
+           addr.indx = repl;
+
+         new_addr = addr.base;
+         if (addr.indx)
+           new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx);
+         if (addr.disp)
+           new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp);
+
+         SET_SRC (*x) = new_addr;
+         return;
+       }
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (*x));
+  for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+        {
+          replace_base_register_ref (&XEXP (*x, i), repl);
+        }
+      else if (fmt[i] == 'E')
+        {
+          for (j = 0; j < XVECLEN (*x, i); j++)
+            replace_base_register_ref (&XVECEXP (*x, i, j), repl);
+        }
+    }
+}
+
+
 /* We keep a list of constants we which we have to add to internal
    constant tables in the middle of large functions.  */
 
@@ -3210,17 +3376,26 @@ struct constant_pool
 {
   struct constant_pool *next;
   rtx first_insn;
-  rtx last_insn;
+  rtx pool_insn;
+  bitmap insns;
 
   struct constant *constants[NR_C_MODES];
   rtx label;
   int size;
+  bool anchor;
 };
 
+static struct constant_pool * s390_chunkify_start PARAMS ((rtx, bool *));
+static void s390_chunkify_finish PARAMS ((struct constant_pool *, rtx));
+static void s390_chunkify_cancel PARAMS ((struct constant_pool *));
+
 static struct constant_pool *s390_start_pool PARAMS ((struct constant_pool **, rtx));
 static void s390_end_pool PARAMS ((struct constant_pool *, rtx));
+static void s390_add_pool_insn PARAMS ((struct constant_pool *, rtx));
 static struct constant_pool *s390_find_pool PARAMS ((struct constant_pool *, rtx));
-static rtx s390_add_pool PARAMS ((struct constant_pool *, rtx, enum machine_mode));
+static void s390_add_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode));
+static rtx s390_find_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode));
+static void s390_add_anchor PARAMS ((struct constant_pool *));
 static rtx s390_dump_pool PARAMS ((struct constant_pool *));
 static void s390_free_pool PARAMS ((struct constant_pool *));
 
@@ -3242,9 +3417,11 @@ s390_start_pool (pool_list, insn)
 
   pool->label = gen_label_rtx ();
   pool->first_insn = insn;
-  pool->last_insn = NULL_RTX;
+  pool->pool_insn = NULL_RTX;
+  pool->insns = BITMAP_XMALLOC ();
   pool->size = 0;
+  pool->anchor = FALSE;
+
   for (prev = pool_list; *prev; prev = &(*prev)->next)
     ;
   *prev = pool;
@@ -3252,14 +3429,31 @@ s390_start_pool (pool_list, insn)
   return pool;
 }
 
-/* End range of instructions covered by POOL at INSN.  */
+/* End range of instructions covered by POOL at INSN and emit
+   placeholder insn representing the pool.  */
 
 static void
 s390_end_pool (pool, insn)
      struct constant_pool *pool;
      rtx insn;
 {
-  pool->last_insn = insn;
+  rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */);
+
+  if (!insn)
+    insn = get_last_insn ();
+
+  pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn);
+  INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+}
+
+/* Add INSN to the list of insns covered by POOL.  */
+
+static void
+s390_add_pool_insn (pool, insn)
+     struct constant_pool *pool;
+     rtx insn;
+{
+  bitmap_set_bit (pool->insns, INSN_UID (insn));
 }
 
 /* Return pool out of POOL_LIST that covers INSN.  */
@@ -3269,33 +3463,24 @@ s390_find_pool (pool_list, insn)
      struct constant_pool *pool_list;
      rtx insn;
 {
-  int addr = INSN_ADDRESSES (INSN_UID (insn));
   struct constant_pool *pool;
 
-  if (addr == -1)
-    return NULL;
-
   for (pool = pool_list; pool; pool = pool->next)
-    if (INSN_ADDRESSES (INSN_UID (pool->first_insn)) <= addr
-        && (pool->last_insn == NULL_RTX
-            || INSN_ADDRESSES (INSN_UID (pool->last_insn)) > addr))
+    if (bitmap_bit_p (pool->insns, INSN_UID (insn)))
       break;
 
   return pool;
 }
 
-/* Add constant VAL of mode MODE to the constant pool POOL.
-   Return an RTX describing the distance from the start of
-   the pool to the location of the new constant.  */
+/* Add constant VAL of mode MODE to the constant pool POOL.  */
 
-static rtx
-s390_add_pool (pool, val, mode)
+static void
+s390_add_constant (pool, val, mode)
      struct constant_pool *pool;
      rtx val;
      enum machine_mode mode;
 {
   struct constant *c;
-  rtx offset;
   int i;
 
   for (i = 0; i < NR_C_MODES; i++)
@@ -3317,13 +3502,54 @@ s390_add_pool (pool, val, mode)
       pool->constants[i] = c;
       pool->size += GET_MODE_SIZE (mode);
     }
+}
 
-  offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label), 
-                                gen_rtx_LABEL_REF (Pmode, pool->label));
+/* Find constant VAL of mode MODE in the constant pool POOL.
+   Return an RTX describing the distance from the start of
+   the pool to the location of the new constant.  */
+static rtx
+s390_find_constant (pool, val, mode)
+     struct constant_pool *pool;
+     rtx val;
+     enum machine_mode mode;
+{
+  struct constant *c;
+  rtx offset;
+  int i;
+  for (i = 0; i < NR_C_MODES; i++)
+    if (constant_modes[i] == mode)
+      break;
+  if (i == NR_C_MODES)
+    abort ();
+  for (c = pool->constants[i]; c != NULL; c = c->next)
+    if (rtx_equal_p (val, c->value))
+      break;
+  if (c == NULL)
+    abort ();
+  offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
+                                 gen_rtx_LABEL_REF (Pmode, pool->label));
   offset = gen_rtx_CONST (Pmode, offset);
   return offset;
 }
 
+/* Set 'anchor' flag in POOL.  */
+
+static void
+s390_add_anchor (pool)
+     struct constant_pool *pool;
+{
+  if (!pool->anchor)
+    {
+      pool->anchor = TRUE;
+      pool->size += 4;
+    }
+}
+
 /* Dump out the constants in POOL.  */
 
 static rtx
@@ -3334,31 +3560,47 @@ s390_dump_pool (pool)
   rtx insn;
   int i;
 
-  /* Select location to put literal pool.  */
-  if (TARGET_64BIT)
-    insn = get_last_insn ();
-  else
-    insn = pool->last_insn? pool->last_insn : get_last_insn ();
-
   /* Pool start insn switches to proper section 
      and guarantees necessary alignment.  */
   if (TARGET_64BIT)
-    insn = emit_insn_after (gen_pool_start_64 (), insn);
+    insn = emit_insn_after (gen_pool_start_64 (), pool->pool_insn);
   else
-    insn = emit_insn_after (gen_pool_start_31 (), insn);
+    insn = emit_insn_after (gen_pool_start_31 (), pool->pool_insn);
   INSN_ADDRESSES_NEW (insn, -1);
 
   insn = emit_label_after (pool->label, insn);
   INSN_ADDRESSES_NEW (insn, -1);
 
+  /* Emit anchor if we need one.  */
+  if (pool->anchor)
+    {
+      rtx anchor = gen_rtx_LABEL_REF (VOIDmode, pool->label);
+      anchor = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, anchor), 105);
+      anchor = gen_rtx_CONST (VOIDmode, anchor);
+      insn = emit_insn_after (gen_consttable_si (anchor), insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+    }
+
   /* Dump constants in descending alignment requirement order,
      ensuring proper alignment for every constant.  */
   for (i = 0; i < NR_C_MODES; i++)
     for (c = pool->constants[i]; c; c = c->next)
       {
+       /* Convert 104 unspecs to pool-relative references.  */
+       rtx value = c->value;
+       if (GET_CODE (value) == CONST
+           && GET_CODE (XEXP (value, 0)) == UNSPEC
+           && XINT (XEXP (value, 0), 1) == 104
+           && XVECLEN (XEXP (value, 0), 0) == 1)
+         {
+           value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0),
+                                  gen_rtx_LABEL_REF (VOIDmode, pool->label));
+           value = gen_rtx_CONST (VOIDmode, value);
+         }
+
        insn = emit_label_after (c->label, insn);
        INSN_ADDRESSES_NEW (insn, -1);
-       insn = emit_insn_after (gen_consttable[i] (c->value), insn);
+       insn = emit_insn_after (gen_consttable[i] (value), insn);
        INSN_ADDRESSES_NEW (insn, -1);
       }
 
@@ -3373,6 +3615,9 @@ s390_dump_pool (pool)
   insn = emit_barrier_after (insn);
   INSN_ADDRESSES_NEW (insn, -1);
 
+  /* Remove placeholder insn.  */
+  remove_insn (pool->pool_insn);
+
   return insn;
 }
 
@@ -3395,65 +3640,86 @@ s390_free_pool (pool)
        }
     }
 
+  BITMAP_XFREE (pool->insns);
   free (pool);
 } 
 
-/* Used in s390.md for branch length calculation.  */
-int s390_pool_overflow = 0;
 
-/* Chunkify the literal pool if required.  */
+/* Chunkify the literal pool if required.
+
+   Code generated by this routine is allowed to use
+   TEMP_REG as temporary scratch register.  If this is
+   done, TEMP_USED is set to true.  */
 
 #define S390_POOL_CHUNK_MIN    0xc00
 #define S390_POOL_CHUNK_MAX    0xe00
 
-static void 
-s390_chunkify_pool ()
+static struct constant_pool * 
+s390_chunkify_start (temp_reg, temp_used)
+     rtx temp_reg;
+     bool *temp_used;
 {
-  rtx base_reg = gen_rtx_REG (Pmode, 
-                             TARGET_64BIT? BASE_REGISTER : RETURN_REGNUM);
+  rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
 
   struct constant_pool *curr_pool = NULL, *pool_list = NULL;
   int extra_size = 0;
   bitmap far_labels;
   rtx insn;
 
+  rtx (*gen_reload_base) PARAMS ((rtx, rtx)) =
+    TARGET_64BIT? gen_reload_base_64 : gen_reload_base_31;
+
+
   /* Do we need to chunkify the literal pool?  */
 
   if (get_pool_size () < S390_POOL_CHUNK_MAX)
-    return;
-
-  if (!TARGET_64BIT)
-    regs_ever_live[RETURN_REGNUM] = 1;
+    return NULL;
 
   /* We need correct insn addresses.  */
 
   shorten_branches (get_insns ());
 
-
   /* Scan all insns and move literals to pool chunks.
-     Replace all occurrances of literal pool references
-     by explicit references to pool chunk entries.  */
+     Also, emit anchor reload insns before every insn that uses 
+     the literal pool base register as anchor pointer.  */
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
-      if (GET_CODE (insn) == INSN)
+      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
        {
-         rtx addr, pool_ref = NULL_RTX;
+         rtx pool_ref = NULL_RTX;
          find_constant_pool_ref (PATTERN (insn), &pool_ref);
          if (pool_ref)
            {
              if (!curr_pool)
                curr_pool = s390_start_pool (&pool_list, insn);
 
-             addr = s390_add_pool (curr_pool, get_pool_constant (pool_ref), 
-                                              get_pool_mode (pool_ref));
+             s390_add_constant (curr_pool, get_pool_constant (pool_ref), 
+                                           get_pool_mode (pool_ref));
+             s390_add_pool_insn (curr_pool, insn);
+           }
+
+         else if (!TARGET_64BIT && flag_pic
+                   && find_base_register_ref (PATTERN (insn)))
+           {
+             rtx new = gen_reload_anchor (temp_reg, base_reg);
+             new = emit_insn_before (new, insn);
+             INSN_ADDRESSES_NEW (new, INSN_ADDRESSES (INSN_UID (insn)));
+             extra_size += 8;
+             *temp_used = 1;
+             
+             if (!curr_pool)
+               curr_pool = s390_start_pool (&pool_list, new);
 
-             addr = gen_rtx_PLUS (Pmode, base_reg, addr);
-             replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
-             INSN_CODE (insn) = -1;
+             s390_add_anchor (curr_pool);
+             s390_add_pool_insn (curr_pool, insn);
            }
        }
 
+      if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL)
+       if (curr_pool)
+         s390_add_pool_insn (curr_pool, insn);
+
       if (!curr_pool 
          || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
           || INSN_ADDRESSES (INSN_UID (insn)) == -1)
@@ -3464,7 +3730,7 @@ s390_chunkify_pool ()
          if (curr_pool->size < S390_POOL_CHUNK_MAX)
            continue;
 
-         s390_end_pool (curr_pool, insn);
+         s390_end_pool (curr_pool, NULL_RTX);
          curr_pool = NULL;
        }
       else
@@ -3477,11 +3743,8 @@ s390_chunkify_pool ()
             Those will have an effect on code size, which we need to
             consider here.  This calculation makes rather pessimistic
             worst-case assumptions.  */
-         if (GET_CODE (insn) == CODE_LABEL
-             || GET_CODE (insn) == JUMP_INSN)
+         if (GET_CODE (insn) == CODE_LABEL)
            extra_size += 6;
-         else if (GET_CODE (insn) == CALL_INSN)
-           extra_size += 4;
 
          if (chunk_size < S390_POOL_CHUNK_MIN
              && curr_pool->size < S390_POOL_CHUNK_MIN)
@@ -3497,12 +3760,22 @@ s390_chunkify_pool ()
 
          /* ... so if we don't find one in time, create one.  */
           else if ((chunk_size > S390_POOL_CHUNK_MAX
-                  || curr_pool->size > S390_POOL_CHUNK_MAX)
-                 && (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN))
+                  || curr_pool->size > S390_POOL_CHUNK_MAX))
            {
-             int addr = INSN_ADDRESSES (INSN_UID (insn));
               rtx label, jump, barrier;
 
+             /* We can insert the barrier only after a 'real' insn.  */
+             if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
+               continue;
+             if (get_attr_length (insn) == 0)
+               continue;
+
+             /* Don't separate insns created by s390_split_branches.  */
+             if (GET_CODE (insn) == INSN 
+                 && GET_CODE (PATTERN (insn)) == SET
+                 && rtx_equal_p (SET_DEST (PATTERN (insn)), temp_reg))
+               continue;
+
              label = gen_label_rtx ();
              jump = emit_jump_insn_after (gen_jump (label), insn);
              barrier = emit_barrier_after (jump);
@@ -3510,8 +3783,8 @@ s390_chunkify_pool ()
              JUMP_LABEL (jump) = label;
              LABEL_NUSES (label) = 1;
 
-             INSN_ADDRESSES_NEW (jump, addr+1);
-             INSN_ADDRESSES_NEW (barrier, addr+1);
+             INSN_ADDRESSES_NEW (jump, -1);
+             INSN_ADDRESSES_NEW (barrier, -1);
              INSN_ADDRESSES_NEW (insn, -1);
 
              s390_end_pool (curr_pool, barrier);
@@ -3521,10 +3794,8 @@ s390_chunkify_pool ()
        }
     }
 
-  /* Dump out all literal pools.  */
-
-  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
-    s390_dump_pool (curr_pool);
+  if (curr_pool)
+    s390_end_pool (curr_pool, NULL_RTX);
 
 
   /* Find all labels that are branched into 
@@ -3563,20 +3834,7 @@ s390_chunkify_pool ()
 
           if (GET_CODE (pat) == SET) 
             {
-             rtx label = 0;
-
-              if (GET_CODE (SET_SRC (pat)) == LABEL_REF) 
-               {
-                 label = XEXP (SET_SRC (pat), 0);
-               } 
-              else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) 
-               {
-                 if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) 
-                   label = XEXP (XEXP (SET_SRC (pat), 1), 0);
-                 else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) 
-                   label = XEXP (XEXP (SET_SRC (pat), 2), 0);
-               }
-
+             rtx label = JUMP_LABEL (insn);
              if (label)
                {
                  if (s390_find_pool (pool_list, label) 
@@ -3617,19 +3875,11 @@ s390_chunkify_pool ()
   /* Insert base register reload insns before every pool.  */
 
   for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
-    if (TARGET_64BIT)
-      {
-       rtx pool_ref = gen_rtx_LABEL_REF (Pmode, curr_pool->label);
-       rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref);
-       rtx insn = curr_pool->first_insn;
-        INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
-      }
-    else
-      {
-       rtx new_insn = gen_reload_base (base_reg, curr_pool->label);
-       rtx insn = curr_pool->first_insn;
-        INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
-      }
+    {
+      rtx new_insn = gen_reload_base (base_reg, curr_pool->label);
+      rtx insn = curr_pool->first_insn;
+      INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
+    }
 
   /* Insert base register reload insns at every far label.  */
 
@@ -3640,60 +3890,137 @@ s390_chunkify_pool ()
        struct constant_pool *pool = s390_find_pool (pool_list, insn);
        if (pool)
          {
-           if (TARGET_64BIT)
-             {
-               rtx pool_ref = gen_rtx_LABEL_REF (Pmode, pool->label);
-               rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref);
-               INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
-             }
-           else
-             {
-               rtx new_insn = gen_reload_base (base_reg, pool->label);
-               INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
-             }
+           rtx new_insn = gen_reload_base (base_reg, pool->label);
+           INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
          }
       }
 
-  /* Insert base register reload insns after every call if necessary.  */
-
-  if (REGNO (base_reg) == RETURN_REGNUM)
-    for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-      if (GET_CODE (insn) == CALL_INSN)
-        {
-          struct constant_pool *pool = s390_find_pool (pool_list, insn);
-          if (pool)
-            {
-              rtx new_insn = gen_reload_base2 (base_reg, pool->label);
-              INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
-            }
-         }
+
+  BITMAP_XFREE (far_labels);
 
 
   /* Recompute insn addresses.  */
 
-  s390_pool_overflow = 1;
   init_insn_lengths ();
   shorten_branches (get_insns ());
-  s390_pool_overflow = 0;
 
-  /* Insert base register reload insns after far branches.  */
+  return pool_list;
+}
 
-  if (!TARGET_64BIT)
-    for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-      if (GET_CODE (insn) == JUMP_INSN
-         && GET_CODE (PATTERN (insn)) == SET
-         && get_attr_length (insn) >= 12)
-       {
-         struct constant_pool *pool = s390_find_pool (pool_list, insn);
-         if (pool)
+/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
+   After we have decided to use this list, finish implementing 
+   all changes to the current function as required.
+
+   Code generated by this routine is allowed to use
+   TEMP_REG as temporary scratch register.  */
+static void
+s390_chunkify_finish (pool_list, temp_reg)
+     struct constant_pool *pool_list;
+     rtx temp_reg;
+{
+  rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
+  struct constant_pool *curr_pool = NULL;
+  rtx insn;
+  /* Replace all literal pool references.  */
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 
+    {
+      curr_pool = s390_find_pool (pool_list, insn);
+      if (!curr_pool)
+       continue;
+
+      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+        {
+          rtx addr, pool_ref = NULL_RTX;
+          find_constant_pool_ref (PATTERN (insn), &pool_ref);
+          if (pool_ref)
+            {
+              addr = s390_find_constant (curr_pool, get_pool_constant (pool_ref),
+                                                    get_pool_mode (pool_ref));
+              addr = gen_rtx_PLUS (Pmode, base_reg, addr);
+              replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
+              INSN_CODE (insn) = -1;
+            }
+
+         else if (!TARGET_64BIT && flag_pic
+                   && find_base_register_ref (PATTERN (insn)))
            {
-             rtx new_insn = gen_reload_base (base_reg, pool->label);
-             INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
+             replace_base_register_ref (&PATTERN (insn), temp_reg);
            }
+        }
+    }
+
+  /* Dump out all literal pools.  */
+  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
+    s390_dump_pool (curr_pool);
+  /* Free pool list.  */
+
+  while (pool_list)
+    {
+      struct constant_pool *next = pool_list->next;
+      s390_free_pool (pool_list);
+      pool_list = next;
+    }
+}
+
+/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
+   We have decided we cannot use this list, so revert all changes
+   to the current function that were done by s390_chunkify_start.  */
+static void
+s390_chunkify_cancel (pool_list)
+     struct constant_pool *pool_list;
+{
+  struct constant_pool *curr_pool = NULL;
+  rtx insn;
+
+  /* Remove all pool placeholder insns.  */
+
+  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
+    {
+      /* Did we insert an extra barrier?  Remove it.  */
+      rtx barrier = PREV_INSN (curr_pool->pool_insn);
+      rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX;
+      rtx label = NEXT_INSN (curr_pool->pool_insn);
+
+      if (jump && GET_CODE (jump) == JUMP_INSN
+         && barrier && GET_CODE (barrier) == BARRIER
+         && label && GET_CODE (label) == CODE_LABEL
+         && GET_CODE (PATTERN (jump)) == SET
+         && SET_DEST (PATTERN (jump)) == pc_rtx
+         && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
+         && XEXP (SET_SRC (PATTERN (jump)), 0) == label)
+       {
+         remove_insn (jump);
+         remove_insn (barrier);
+         remove_insn (label);
        }
 
+      remove_insn (curr_pool->pool_insn);
+    }
+
+  /* Remove all base/anchor register reload insns.  */
+
+  for (insn = get_insns (); insn; )
+    {
+      rtx next_insn = NEXT_INSN (insn);
+
+      if (GET_CODE (insn) == INSN
+         && GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
+         && (XINT (SET_SRC (PATTERN (insn)), 1) == 210
+             || XINT (SET_SRC (PATTERN (insn)), 1) == 211))
+       remove_insn (insn);
 
-  /* Free all memory.  */
+      insn = next_insn;
+    }
+
+  /* Free pool list.  */
 
   while (pool_list)
     {
@@ -3701,8 +4028,6 @@ s390_chunkify_pool ()
       s390_free_pool (pool_list);
       pool_list = next;
     }
-
-  BITMAP_XFREE (far_labels);
 }
 
 
@@ -3745,19 +4070,51 @@ s390_output_constant_pool (file)
       else
         fprintf (file, ".LTN%d:\n", current_function_funcdef_no);
     }
+
+  /* If no pool required, at least output the anchor label.  */
+  else if (!TARGET_64BIT && flag_pic)
+    fprintf (file, ".LT%d:\n", current_function_funcdef_no);
 }
 
 
 /* Rework the prolog/epilog to avoid saving/restoring
-   registers unnecessarily.  */
+   registers unnecessarily.  If TEMP_REGNO is nonnegative,
+   it specifies the number of a caller-saved register used 
+   as temporary scratch register by code emitted during 
+   machine dependent reorg.  */
 
 static void
-s390_optimize_prolog ()
+s390_optimize_prolog (temp_regno)
+     int temp_regno;
 {
   int save_first, save_last, restore_first, restore_last;
   int i, j;
   rtx insn, new_insn, next_insn;
 
+  struct s390_frame frame;
+  s390_frame_info (&frame);
+
+  /* Recompute regs_ever_live data for special registers.  */
+  regs_ever_live[BASE_REGISTER] = 0;
+  regs_ever_live[RETURN_REGNUM] = 0;
+  regs_ever_live[STACK_POINTER_REGNUM] = frame.frame_size > 0;
+
+  /* If there is (possibly) any pool entry, we need to
+     load the base register.  
+     ??? FIXME: this should be more precise.  */
+  if (get_pool_size ())
+    regs_ever_live[BASE_REGISTER] = 1;
+
+  /* In non-leaf functions, the prolog/epilog code relies 
+     on RETURN_REGNUM being saved in any case.  */
+  if (!current_function_is_leaf)
+    regs_ever_live[RETURN_REGNUM] = 1;
+
+  /* We need to save/restore the temporary register.  */
+  if (temp_regno >= 0)
+    regs_ever_live[temp_regno] = 1;
+
+
   /* Find first and last gpr to be saved.  */
   
   for (i = 6; i < 16; i++)
@@ -3865,34 +4222,146 @@ s390_optimize_prolog ()
     }
 }
 
+/* Check whether any insn in the function makes use of the original
+   value of RETURN_REG (e.g. for __builtin_return_address).
+   If so, insert an insn reloading that value.
+
+   Return true if any such insn was found.  */
+
+static bool
+s390_fixup_clobbered_return_reg (return_reg)
+    rtx return_reg;
+{
+  bool replacement_done = 0;
+  rtx insn;
+
+  struct s390_frame frame;
+  s390_frame_info (&frame);
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      rtx reg, off, new_insn;
+
+      if (GET_CODE (insn) != INSN)
+       continue;
+      if (!reg_referenced_p (return_reg, PATTERN (insn)))
+       continue;
+      if (GET_CODE (PATTERN (insn)) == PARALLEL
+         && store_multiple_operation (PATTERN (insn), VOIDmode))
+       continue;
+
+      if (frame.frame_pointer_p)
+       reg = hard_frame_pointer_rtx;
+      else
+       reg = stack_pointer_rtx;
+
+      off = GEN_INT (frame.frame_size + REGNO (return_reg) * UNITS_PER_WORD);
+      if (INTVAL (off) >= 4096)
+       {
+         off = force_const_mem (Pmode, off);
+         new_insn = gen_rtx_SET (Pmode, return_reg, off);
+         new_insn = emit_insn_before (new_insn, insn);
+         INSN_ADDRESSES_NEW (new_insn, -1);
+         off = return_reg;
+       }
+
+      new_insn = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, reg, off));
+      new_insn = gen_rtx_SET (Pmode, return_reg, new_insn);
+      new_insn = emit_insn_before (new_insn, insn);
+      INSN_ADDRESSES_NEW (new_insn, -1);
+
+      replacement_done = 1;
+    }
+
+  return replacement_done;
+}
+
 /* Perform machine-dependent processing.  */
 
 void
 s390_machine_dependent_reorg (first)
      rtx first ATTRIBUTE_UNUSED;
 {
-  struct s390_frame frame;
-  s390_frame_info (&frame);
+  bool fixed_up_clobbered_return_reg = 0;
+  rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
+  bool temp_used = 0;
 
-  /* Recompute regs_ever_live data for special registers.  */
-  regs_ever_live[BASE_REGISTER] = 0;
-  regs_ever_live[RETURN_REGNUM] = 0;
-  regs_ever_live[STACK_POINTER_REGNUM] = frame.frame_size > 0;
+  /* Make sure all splits have been performed; splits after
+     machine_dependent_reorg might confuse insn length counts.  */
+  split_all_insns_noflow ();
 
-  /* If there is (possibly) any pool entry, we need to
-     load the base register.  
-     ??? FIXME: this should be more precise.  */
-  if (get_pool_size ())
-    regs_ever_live[BASE_REGISTER] = 1;
 
-  /* In non-leaf functions, the prolog/epilog code relies 
-     on RETURN_REGNUM being saved in any case.  */
-  if (!current_function_is_leaf)
-    regs_ever_live[RETURN_REGNUM] = 1;
+  /* There are two problematic situations we need to correct:
+     - the literal pool might be > 4096 bytes in size, so that
+       some of its elements cannot be directly accessed
+     - a branch target might be > 64K away from the branch, so that
+       it is not possible to use a PC-relative instruction.
+     To fix those, we split the single literal pool into multiple
+     pool chunks, reloading the pool base register at various
+     points throughout the function to ensure it always points to
+     the pool chunk the following code expects, and / or replace
+     PC-relative branches by absolute branches.
+     However, the two problems are interdependent: splitting the
+     literal pool can move a branch further away from its target,
+     causing the 64K limit to overflow, and on the other hand,
+     replacing a PC-relative branch by an absolute branch means
+     we need to put the branch target address into the literal
+     pool, possibly causing it to overflow.
+     So, we loop trying to fix up both problems until we manage
+     to satisfy both conditions at the same time.  Note that the
+     loop is guaranteed to terminate as every pass of the loop
+     strictly decreases the total number of PC-relative branches
+     in the function.  (This is not completely true as there
+     might be branch-over-pool insns introduced by chunkify_start.
+     Those never need to be split however.)  */
+  for (;;)
+    {
+      struct constant_pool *pool_list;
+      /* Try to chunkify the literal pool.  */
+      pool_list = s390_chunkify_start (temp_reg, &temp_used);
+
+      /* Split out-of-range branches.  If this has created new
+        literal pool entries, cancel current chunk list and
+        recompute it.  */
+      if (s390_split_branches (temp_reg, &temp_used))
+        {
+          if (pool_list)
+            s390_chunkify_cancel (pool_list);
+          continue;
+        }
+
+      /* Check whether we have clobbered a use of the return
+        register (e.g. for __builtin_return_address).  If so,
+        add insns reloading the register where necessary.  */
+      if (temp_used && !fixed_up_clobbered_return_reg
+         && s390_fixup_clobbered_return_reg (temp_reg))
+       {
+         fixed_up_clobbered_return_reg = 1;
 
-  s390_chunkify_pool ();
-  s390_split_branches ();
-  s390_optimize_prolog ();
+         /* The fixup insns might have caused a jump to overflow.  */
+         if (pool_list)
+           s390_chunkify_cancel (pool_list);
+
+         continue;
+       }
+      /* If we made it up to here, both conditions are satisfied.
+        Finish up pool chunkification if required.  */
+      if (pool_list)
+       s390_chunkify_finish (pool_list, temp_reg);
+      break;
+    }
+  s390_optimize_prolog (temp_used? RETURN_REGNUM : -1);
 }
 
 
index 58ad2e7..4b992c3 100644 (file)
@@ -1299,7 +1299,6 @@ extern struct rtx_def *s390_compare_op0, *s390_compare_op1;
 
 extern int s390_pool_count;
 extern int s390_nr_constants;
-extern int s390_pool_overflow;
 
 #define ASM_OUTPUT_POOL_PROLOGUE(FILE, FUNNAME, fndecl, size)                  \
 {                                                                      \
index 40a7807..e998c4d 100644 (file)
                 (const_int 4)
                (ne (symbol_ref "TARGET_64BIT") (const_int 0))
                  (const_int 6)
-              (ne (symbol_ref "s390_pool_overflow") (const_int 0))
-                 (if_then_else (eq (symbol_ref "flag_pic") (const_int 0))
-                               (const_int 12) (const_int 14))
                (eq (symbol_ref "flag_pic") (const_int 0))
                  (const_int 6)] (const_int 8)))])
 
                 (const_int 4)
                (ne (symbol_ref "TARGET_64BIT") (const_int 0))
                  (const_int 6)
-              (ne (symbol_ref "s390_pool_overflow") (const_int 0))
-                 (if_then_else (eq (symbol_ref "flag_pic") (const_int 0))
-                               (const_int 12) (const_int 14))
                (eq (symbol_ref "flag_pic") (const_int 0))
                  (const_int 6)] (const_int 8)))])
 
                 (const_int 4)
                (ne (symbol_ref "TARGET_64BIT") (const_int 0))
                  (const_int 10)
-              (ne (symbol_ref "s390_pool_overflow") (const_int 0))
-                 (if_then_else (eq (symbol_ref "flag_pic") (const_int 0))
-                               (const_int 12) (const_int 14))
                (eq (symbol_ref "flag_pic") (const_int 0))
                  (const_int 6)] (const_int 8)))])
 
   [(set_attr "op_type"  "NN")
    (set_attr "length"   "0")])
 
-(define_insn "reload_base"
+(define_insn "reload_base_31"
   [(set (match_operand:SI 0 "register_operand" "=a")
         (unspec:SI [(label_ref (match_operand 1 "" ""))] 210))]
   "!TARGET_64BIT"
    (set_attr "type"    "la")
    (set_attr "length"  "6")])
 
-(define_insn "reload_base2"
+(define_insn "reload_base_64"
+  [(set (match_operand:DI 0 "register_operand" "=a")
+        (unspec:DI [(label_ref (match_operand 1 "" ""))] 210))]
+  "TARGET_64BIT"
+  "larl\\t%0,%1"
+  [(set_attr "op_type" "RIL")
+   (set_attr "type"    "la")])
+
+(define_insn "reload_anchor"
   [(set (match_operand:SI 0 "register_operand" "=a")
-        (unspec:SI [(label_ref (match_operand 1 "" ""))] 211))]
+        (unspec:SI [(match_operand:SI 1 "register_operand" "a")] 211))]
   "!TARGET_64BIT"
-  "la\\t%0,%1-.(%0)"
+  "l\\t%0,0(%1)\;la\\t%0,0(%0,%1)"
   [(set_attr "op_type" "NN")
    (set_attr "type"    "la")
-   (set_attr "length"  "4")])
+   (set_attr "length"  "8")])
 
+(define_insn "pool"
+  [(unspec_volatile [(match_operand 0 "const_int_operand" "n")] 220)]
+  ""
+  "* abort ();"
+  [(set_attr "op_type" "NN")
+   (set (attr "length") (symbol_ref "INTVAL (operands[0])"))])
 
 ;;
 ;; Insns related to generating the function prologue and epilogue.