OSDN Git Service

2009-10-19 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / s390 / s390.c
index 1431bfd..c13170d 100644 (file)
@@ -205,13 +205,13 @@ struct processor_costs z10_cost =
   COSTS_N_INSNS (10),    /* MSGFR */
   COSTS_N_INSNS (10),    /* MSGR  */
   COSTS_N_INSNS (10),    /* MSR   */
-  COSTS_N_INSNS (10),    /* multiplication in DFmode */
+  COSTS_N_INSNS (1,    /* multiplication in DFmode */
   COSTS_N_INSNS (50),    /* MXBR */
   COSTS_N_INSNS (120),   /* SQXBR */
   COSTS_N_INSNS (52),    /* SQDBR */
   COSTS_N_INSNS (38),    /* SQEBR */
-  COSTS_N_INSNS (10),    /* MADBR */
-  COSTS_N_INSNS (10),    /* MAEBR */
+  COSTS_N_INSNS (1),     /* MADBR */
+  COSTS_N_INSNS (1),     /* MAEBR */
   COSTS_N_INSNS (111),   /* DXBR */
   COSTS_N_INSNS (39),    /* DDBR */
   COSTS_N_INSNS (32),    /* DEBR */
@@ -345,6 +345,10 @@ struct GTY(()) machine_function
 #define REGNO_PAIR_OK(REGNO, MODE)                               \
   (HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
 
+/* That's the read ahead of the dynamic branch prediction unit in
+   bytes on a z10 CPU.  */
+#define Z10_PREDICT_DISTANCE 384
+
 static enum machine_mode
 s390_libgcc_cmp_return_mode (void)
 {
@@ -362,7 +366,7 @@ static bool
 s390_scalar_mode_supported_p (enum machine_mode mode)
 {
   if (DECIMAL_FLOAT_MODE_P (mode))
-    return true;
+    return default_decimal_float_supported_p ();
   else
     return default_scalar_mode_supported_p (mode);
 }
@@ -5293,6 +5297,7 @@ s390_agen_dep_p (rtx dep_insn, rtx insn)
    A STD instruction should be scheduled earlier,
    in order to use the bypass.  */
 
+
 static int
 s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
 {
@@ -5300,7 +5305,8 @@ s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
     return priority;
 
   if (s390_tune != PROCESSOR_2084_Z990
-      && s390_tune != PROCESSOR_2094_Z9_109)
+      && s390_tune != PROCESSOR_2094_Z9_109
+      && s390_tune != PROCESSOR_2097_Z10)
     return priority;
 
   switch (s390_safe_attr_type (insn))
@@ -5319,6 +5325,7 @@ s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
   return priority;
 }
 
+
 /* The number of instructions that can be issued per cycle.  */
 
 static int
@@ -7331,8 +7338,8 @@ s390_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
 
 /* Return true if register FROM can be eliminated via register TO.  */
 
-bool
-s390_can_eliminate (int from, int to)
+static bool
+s390_can_eliminate (const int from, const int to)
 {
   /* On zSeries machines, we have not marked the base register as fixed.
      Instead, we have an elimination rule BASE_REGNUM -> BASE_REGNUM.
@@ -8856,8 +8863,8 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
    On S/390, we use gpr 1 internally in the trampoline code;
    gpr 0 is used to hold the static chain.  */
 
-void
-s390_trampoline_template (FILE *file)
+static void
+s390_asm_trampoline_template (FILE *file)
 {
   rtx op[2];
   op[0] = gen_rtx_REG (Pmode, 0);
@@ -8883,15 +8890,19 @@ s390_trampoline_template (FILE *file)
    FNADDR is an RTX for the address of the function's pure code.
    CXT is an RTX for the static chain value for the function.  */
 
-void
-s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
+static void
+s390_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
 {
-  emit_move_insn (gen_rtx_MEM (Pmode,
-                  memory_address (Pmode,
-                  plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt);
-  emit_move_insn (gen_rtx_MEM (Pmode,
-                  memory_address (Pmode,
-                  plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr);
+  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+  rtx mem;
+  
+  emit_block_move (m_tramp, assemble_trampoline_template (),
+                  GEN_INT (2*UNITS_PER_WORD), BLOCK_OP_NORMAL);
+
+  mem = adjust_address (m_tramp, Pmode, 2*UNITS_PER_WORD);
+  emit_move_insn (mem, cxt);
+  mem = adjust_address (m_tramp, Pmode, 3*UNITS_PER_WORD);
+  emit_move_insn (mem, fnaddr);
 }
 
 /* Output assembler code to FILE to increment profiler label # LABELNO
@@ -8992,6 +9003,7 @@ s390_encode_section_info (tree decl, rtx rtl, int first)
       && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
       && TREE_CONSTANT_POOL_ADDRESS_P (XEXP (rtl, 0))
       && (MEM_ALIGN (rtl) == 0
+         || GET_MODE_BITSIZE (GET_MODE (rtl)) == 0
          || MEM_ALIGN (rtl) < GET_MODE_BITSIZE (GET_MODE (rtl))))
     SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_NOT_NATURALLY_ALIGNED;
 }
@@ -9661,6 +9673,66 @@ s390_optimize_prologue (void)
     }
 }
 
+/* On z10 the dynamic branch prediction must see the backward jump in
+   a window of 384 bytes. If not it falls back to the static
+   prediction.  This function rearranges the loop backward branch in a
+   way which makes the static prediction always correct.  The function
+   returns true if it added an instruction.  */
+static bool
+s390_z10_fix_long_loop_prediction (rtx insn)
+{
+  rtx set = single_set (insn);
+  rtx code_label, label_ref, new_label;
+  rtx uncond_jump;
+  rtx cur_insn;
+  rtx tmp;
+  int distance;
+
+  /* This will exclude branch on count and branch on index patterns
+     since these are correctly statically predicted.  */
+  if (!set
+      || SET_DEST (set) != pc_rtx
+      || GET_CODE (SET_SRC(set)) != IF_THEN_ELSE)
+    return false;
+
+  label_ref = (GET_CODE (XEXP (SET_SRC (set), 1)) == LABEL_REF ?
+              XEXP (SET_SRC (set), 1) : XEXP (SET_SRC (set), 2));
+
+  gcc_assert (GET_CODE (label_ref) == LABEL_REF);
+
+  code_label = XEXP (label_ref, 0);
+
+  if (INSN_ADDRESSES (INSN_UID (code_label)) == -1
+      || INSN_ADDRESSES (INSN_UID (insn)) == -1
+      || (INSN_ADDRESSES (INSN_UID (insn))
+         - INSN_ADDRESSES (INSN_UID (code_label)) < Z10_PREDICT_DISTANCE))
+    return false;
+
+  for (distance = 0, cur_insn = PREV_INSN (insn);
+       distance < Z10_PREDICT_DISTANCE - 6;
+       distance += get_attr_length (cur_insn), cur_insn = PREV_INSN (cur_insn))
+    if (!cur_insn || JUMP_P (cur_insn) || LABEL_P (cur_insn))
+      return false;
+
+  new_label = gen_label_rtx ();
+  uncond_jump = emit_jump_insn_after (
+                 gen_rtx_SET (VOIDmode, pc_rtx,
+                              gen_rtx_LABEL_REF (VOIDmode, code_label)),
+                 insn);
+  emit_label_after (new_label, uncond_jump);
+
+  tmp = XEXP (SET_SRC (set), 1);
+  XEXP (SET_SRC (set), 1) = XEXP (SET_SRC (set), 2);
+  XEXP (SET_SRC (set), 2) = tmp;
+  INSN_CODE (insn) = -1;
+
+  XEXP (label_ref, 0) = new_label;
+  JUMP_LABEL (insn) = new_label;
+  JUMP_LABEL (uncond_jump) = code_label;
+
+  return true;
+}
+
 /* Returns 1 if INSN reads the value of REG for purposes not related
    to addressing of memory, and 0 otherwise.  */
 static int
@@ -9743,97 +9815,87 @@ s390_swap_cmp (rtx cond, rtx *op0, rtx *op1, rtx insn)
    if that register's value is delivered via a bypass, then the
    pipeline recycles, thereby causing significant performance decline.
    This function locates such situations and exchanges the two
-   operands of the compare.  */
-static void
-s390_z10_optimize_cmp (void)
+   operands of the compare.  The function return true whenever it
+   added an insn.  */
+static bool
+s390_z10_optimize_cmp (rtx insn)
 {
-  rtx insn, prev_insn, next_insn;
-  int added_NOPs = 0;
+  rtx prev_insn, next_insn;
+  bool insn_added_p = false;
+  rtx cond, *op0, *op1;
 
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+  if (GET_CODE (PATTERN (insn)) == PARALLEL)
     {
-      rtx cond, *op0, *op1;
-
-      if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
-       continue;
-
-      if (GET_CODE (PATTERN (insn)) == PARALLEL)
-       {
-         /* Handle compare and branch and branch on count
-            instructions.  */
-         rtx pattern = single_set (insn);
+      /* Handle compare and branch and branch on count
+        instructions.  */
+      rtx pattern = single_set (insn);
 
-         if (!pattern
-             || SET_DEST (pattern) != pc_rtx
-             || GET_CODE (SET_SRC (pattern)) != IF_THEN_ELSE)
-           continue;
+      if (!pattern
+         || SET_DEST (pattern) != pc_rtx
+         || GET_CODE (SET_SRC (pattern)) != IF_THEN_ELSE)
+       return false;
 
-         cond = XEXP (SET_SRC (pattern), 0);
-         op0 = &XEXP (cond, 0);
-         op1 = &XEXP (cond, 1);
-       }
-      else if (GET_CODE (PATTERN (insn)) == SET)
-       {
-         rtx src, dest;
+      cond = XEXP (SET_SRC (pattern), 0);
+      op0 = &XEXP (cond, 0);
+      op1 = &XEXP (cond, 1);
+    }
+  else if (GET_CODE (PATTERN (insn)) == SET)
+    {
+      rtx src, dest;
 
-         /* Handle normal compare instructions.  */
-         src = SET_SRC (PATTERN (insn));
-         dest = SET_DEST (PATTERN (insn));
+      /* Handle normal compare instructions.  */
+      src = SET_SRC (PATTERN (insn));
+      dest = SET_DEST (PATTERN (insn));
 
-         if (!REG_P (dest)
-             || !CC_REGNO_P (REGNO (dest))
-             || GET_CODE (src) != COMPARE)
-           continue;
+      if (!REG_P (dest)
+         || !CC_REGNO_P (REGNO (dest))
+         || GET_CODE (src) != COMPARE)
+       return false;
 
-         /* s390_swap_cmp will try to find the conditional
-            jump when passing NULL_RTX as condition.  */
-         cond = NULL_RTX;
-         op0 = &XEXP (src, 0);
-         op1 = &XEXP (src, 1);
-       }
-      else
-       continue;
+      /* s390_swap_cmp will try to find the conditional
+        jump when passing NULL_RTX as condition.  */
+      cond = NULL_RTX;
+      op0 = &XEXP (src, 0);
+      op1 = &XEXP (src, 1);
+    }
+  else
+    return false;
 
-      if (!REG_P (*op0) || !REG_P (*op1))
-       continue;
+  if (!REG_P (*op0) || !REG_P (*op1))
+    return false;
 
-      /* Swap the COMPARE arguments and its mask if there is a
-        conflicting access in the previous insn.  */
-      prev_insn = PREV_INSN (insn);
+  /* Swap the COMPARE arguments and its mask if there is a
+     conflicting access in the previous insn.  */
+  prev_insn = prev_active_insn (insn);
+  if (prev_insn != NULL_RTX && INSN_P (prev_insn)
+      && reg_referenced_p (*op1, PATTERN (prev_insn)))
+    s390_swap_cmp (cond, op0, op1, insn);
+
+  /* Check if there is a conflict with the next insn. If there
+     was no conflict with the previous insn, then swap the
+     COMPARE arguments and its mask.  If we already swapped
+     the operands, or if swapping them would cause a conflict
+     with the previous insn, issue a NOP after the COMPARE in
+     order to separate the two instuctions.  */
+  next_insn = next_active_insn (insn);
+  if (next_insn != NULL_RTX && INSN_P (next_insn)
+      && s390_non_addr_reg_read_p (*op1, next_insn))
+    {
       if (prev_insn != NULL_RTX && INSN_P (prev_insn)
-         && reg_referenced_p (*op1, PATTERN (prev_insn)))
-       s390_swap_cmp (cond, op0, op1, insn);
-
-      /* Check if there is a conflict with the next insn. If there
-        was no conflict with the previous insn, then swap the
-        COMPARE arguments and its mask.  If we already swapped
-        the operands, or if swapping them would cause a conflict
-        with the previous insn, issue a NOP after the COMPARE in
-        order to separate the two instuctions.  */
-      next_insn = NEXT_INSN (insn);
-      if (next_insn != NULL_RTX && INSN_P (next_insn)
-         && s390_non_addr_reg_read_p (*op1, next_insn))
+         && s390_non_addr_reg_read_p (*op0, prev_insn))
        {
-         if (prev_insn != NULL_RTX && INSN_P (prev_insn)
-             && s390_non_addr_reg_read_p (*op0, prev_insn))
-           {
-             if (REGNO (*op1) == 0)
-               emit_insn_after (gen_nop1 (), insn);
-             else
-               emit_insn_after (gen_nop (), insn);
-             added_NOPs = 1;
-           }
+         if (REGNO (*op1) == 0)
+           emit_insn_after (gen_nop1 (), insn);
          else
-           s390_swap_cmp (cond, op0, op1, insn);
+           emit_insn_after (gen_nop (), insn);
+         insn_added_p = true;
        }
+      else
+       s390_swap_cmp (cond, op0, op1, insn);
     }
-
-  /* Adjust branches if we added new instructions.  */
-  if (added_NOPs)
-    shorten_branches (get_insns ());
+  return insn_added_p;
 }
 
-
 /* Perform machine-dependent processing.  */
 
 static void
@@ -9944,10 +10006,33 @@ s390_reorg (void)
   /* Try to optimize prologue and epilogue further.  */
   s390_optimize_prologue ();
 
-  /* Eliminate z10-specific pipeline recycles related to some compare
-     instructions.  */
+  /* Walk over the insns and do some z10 specific changes.  */
   if (s390_tune == PROCESSOR_2097_Z10)
-    s390_z10_optimize_cmp ();
+    {
+      rtx insn;
+      bool insn_added_p = false;
+
+      /* The insn lengths and addresses have to be up to date for the
+        following manipulations.  */
+      shorten_branches (get_insns ());
+
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       {
+         if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
+           continue;
+
+         if (JUMP_P (insn))
+           insn_added_p |= s390_z10_fix_long_loop_prediction (insn);
+
+         if (GET_CODE (PATTERN (insn)) == PARALLEL
+             || GET_CODE (PATTERN (insn)) == SET)
+           insn_added_p |= s390_z10_optimize_cmp (insn);
+       }
+
+      /* Adjust branches if we added new instructions.  */
+      if (insn_added_p)
+       shorten_branches (get_insns ());
+    }
 }
 
 
@@ -10069,6 +10154,14 @@ s390_reorg (void)
 #undef TARGET_LEGITIMATE_ADDRESS_P
 #define TARGET_LEGITIMATE_ADDRESS_P s390_legitimate_address_p
 
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE s390_can_eliminate
+
+#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
+#define TARGET_ASM_TRAMPOLINE_TEMPLATE s390_asm_trampoline_template
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT s390_trampoline_init
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-s390.h"