OSDN Git Service

* rs6000.c (direct_return): Check if we are saving altivec
authoraldyh <aldyh@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 21 Nov 2001 01:04:44 +0000 (01:04 +0000)
committeraldyh <aldyh@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 21 Nov 2001 01:04:44 +0000 (01:04 +0000)
registers.
(first_altivec_reg_to_save): New.
Updated stack frame layout comments.
(rs6000_stack_info): Calculate altivec register save size.
Save link register if we saved some altivec registers.
(rs6000_stack_info): Align save size to 16 if altivec abi or
abi_darwin.
(rs6000_stack_info): Calculate altivec register offsets.
(rs6000_stack_info): Add altivec info to save_size.
(debug_stack_info): Add altivec debug info.
(rs6000_emit_prologue): Save altivec registers and vrsave.
(compute_vrsave_mask): New.
(altivec_expand_builtin): Remove unused variables.
(rs6000_parse_abi_options): Add static qualifier.
(rs6000_expand_builtin): Remove unused parameters.
(altivec_expand_builtin): Cast bdesc_2arg to get rid of warning.
(altivec_init_builtins): Same.
(is_altivec_return_reg): New.
(vrsave_operation): New.
(ALTIVEC_REG_BIT): New.
(generate_set_vrsave): New.

* rs6000.md (get_vrsave): New.
(set_vrsave): New.
(*set_vrsave_internal): New.

* rs6000.h (rs6000_stack): Add first_altivec_reg_save,
altivec_save_offset, vrsave_save_offset, altive_size, vrsave_size,
altivec_padding_size, vrsave_mask.
(TOTAL_ALTIVEC_REGS): New.
(EPILOGUE_USES): Add VRSAVE_REGNO.

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

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

index 57c1677..417b2f9 100644 (file)
@@ -1,3 +1,38 @@
+2001-11-15  Aldy Hernandez  <aldyh@redhat.com>
+
+       * rs6000.c (direct_return): Check if we are saving altivec
+       registers.
+       (first_altivec_reg_to_save): New.
+       Updated stack frame layout comments.
+       (rs6000_stack_info): Calculate altivec register save size.
+       Save link register if we saved some altivec registers.
+       (rs6000_stack_info): Align save size to 16 if altivec abi or
+       abi_darwin.
+       (rs6000_stack_info): Calculate altivec register offsets.
+       (rs6000_stack_info): Add altivec info to save_size.
+       (debug_stack_info): Add altivec debug info.
+       (rs6000_emit_prologue): Save altivec registers and vrsave.
+       (compute_vrsave_mask): New.
+       (altivec_expand_builtin): Remove unused variables.
+       (rs6000_parse_abi_options): Add static qualifier.
+       (rs6000_expand_builtin): Remove unused parameters.
+       (altivec_expand_builtin): Cast bdesc_2arg to get rid of warning.
+       (altivec_init_builtins): Same.
+       (is_altivec_return_reg): New.
+       (vrsave_operation): New.
+       (ALTIVEC_REG_BIT): New.
+       (generate_set_vrsave): New.
+
+       * rs6000.md (get_vrsave): New.
+       (set_vrsave): New.
+       (*set_vrsave_internal): New.
+
+       * rs6000.h (rs6000_stack): Add first_altivec_reg_save,
+       altivec_save_offset, vrsave_save_offset, altive_size, vrsave_size,
+       altivec_padding_size, vrsave_mask.
+       (TOTAL_ALTIVEC_REGS): New.
+       (EPILOGUE_USES): Add VRSAVE_REGNO.
+
 2001-11-20  Jeff Law <law@redhat.com>
 
        * unroll.c (copy_loop_body): Update LABEL_NUSES for the
index b2bb934..6ed9a49 100644 (file)
@@ -153,10 +153,15 @@ static int rs6000_issue_rate PARAMS ((void));
 static void rs6000_init_builtins PARAMS ((tree));
 static void altivec_init_builtins PARAMS ((void));
 static rtx rs6000_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int));
-static rtx altivec_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int));
+static rtx altivec_expand_builtin PARAMS ((tree, rtx));
 static rtx altivec_expand_binop_builtin PARAMS ((enum insn_code, tree, rtx));
 
 static void rs6000_parse_abi_options PARAMS ((void));
+static int first_altivec_reg_to_save PARAMS ((void));
+static unsigned int compute_vrsave_mask PARAMS ((void));
+static void is_altivec_return_reg PARAMS ((rtx, void *));
+int vrsave_operation PARAMS ((rtx, enum machine_mode));
+static rtx generate_set_vrsave PARAMS ((rtx, rs6000_stack_t *));
 \f
 /* Default register names.  */
 char rs6000_reg_names[][8] =
@@ -234,6 +239,9 @@ static const char alt_reg_names[][8] =
 #undef TARGET_EXPAND_BUILTIN
 #define TARGET_EXPAND_BUILTIN rs6000_expand_builtin
 
+/* The VRSAVE bitmask puts bit %v0 as the most significant bit.  */
+#define ALTIVEC_REG_BIT(REGNO) (0x80000000 >> ((REGNO) - FIRST_ALTIVEC_REGNO))
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Override command line options.  Mostly we process the processor
@@ -496,7 +504,8 @@ rs6000_override_options (default_cpu)
 }
 
 /* Handle -mabi= options.  */
-void rs6000_parse_abi_options ()
+static void
+rs6000_parse_abi_options ()
 {
   if (rs6000_abi_string == 0)
     return;
@@ -585,8 +594,10 @@ direct_return ()
 
       if (info->first_gp_reg_save == 32
          && info->first_fp_reg_save == 64
+         && info->first_altivec_reg_save == LAST_ALTIVEC_REGNO + 1
          && ! info->lr_save_p
          && ! info->cr_save_p
+         && info->vrsave_mask == 0
          && ! info->push_p)
        return 1;
     }
@@ -3108,21 +3119,18 @@ altivec_expand_binop_builtin (icode, arglist, target)
 }
 
 static rtx
-altivec_expand_builtin (exp, target, subtarget, mode, ignore)
+altivec_expand_builtin (exp, target)
      tree exp;
      rtx target;
-     rtx subtarget;
-     enum machine_mode mode;
-     int ignore;
 {
   struct builtin_description *d;
   size_t i;
   enum insn_code icode;
   tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
   tree arglist = TREE_OPERAND (exp, 1);
-  tree arg0, arg1, arg2, arg3;
-  rtx op0, op1, op2, pat;
-  enum machine_mode tmode, mode0, mode1, mode2;
+  tree arg0, arg1;
+  rtx op0, op1, pat;
+  enum machine_mode tmode, mode0, mode1;
   unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
   
   switch (fcode)
@@ -3170,7 +3178,8 @@ altivec_expand_builtin (exp, target, subtarget, mode, ignore)
     }
 
   /* Handle simple binary operations.  */
-  for (i = 0, d = bdesc_2arg; i < sizeof (bdesc_2arg) / sizeof *d; i++, d++)
+  d = (struct builtin_description *) bdesc_2arg;
+  for (i = 0; i < sizeof (bdesc_2arg) / sizeof *d; i++, d++)
     if (d->code == fcode)
       return altivec_expand_binop_builtin (d->icode, arglist, target);
 
@@ -3188,12 +3197,12 @@ static rtx
 rs6000_expand_builtin (exp, target, subtarget, mode, ignore)
      tree exp;
      rtx target;
-     rtx subtarget;
-     enum machine_mode mode;
-     int ignore;
+     rtx subtarget ATTRIBUTE_UNUSED;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     int ignore ATTRIBUTE_UNUSED;
 {
   if (TARGET_ALTIVEC)
-    return altivec_expand_builtin (exp, target, subtarget, mode, ignore);
+    return altivec_expand_builtin (exp, target);
 
   abort ();
 }
@@ -3322,7 +3331,8 @@ altivec_init_builtins (void)
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_st_internal", void_ftype_pint_v4si, ALTIVEC_BUILTIN_ST_INTERNAL);
 
   /* Add the simple binary operators.  */
-  for (i = 0, d = bdesc_2arg; i < sizeof (bdesc_2arg) / sizeof *d; i++, d++)
+  d = (struct builtin_description *) bdesc_2arg;
+  for (i = 0; i < sizeof (bdesc_2arg) / sizeof *d; i++, d++)
     {
       enum machine_mode mode0, mode1, mode2;
       tree type;
@@ -3831,6 +3841,41 @@ store_multiple_operation (op, mode)
   return 1;
 }
 
+/* Return 1 for a parallel vrsave operation.  */
+
+int
+vrsave_operation (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  int count = XVECLEN (op, 0);
+  unsigned int dest_regno, src_regno;
+  int i;
+
+  if (count <= 1
+      || GET_CODE (XVECEXP (op, 0, 0)) != SET
+      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
+      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC)
+    return 0;
+
+  dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
+  src_regno  = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
+
+  if (dest_regno != VRSAVE_REGNO
+      && src_regno != VRSAVE_REGNO)
+    return 0;
+
+  for (i = 1; i < count; i++)
+    {
+      rtx elt = XVECEXP (op, 0, i);
+
+      if (GET_CODE (elt) != CLOBBER)
+       return 0;
+    }
+
+  return 1;
+}
+
 /* Return 1 for an PARALLEL suitable for mtcrf.  */
 
 int
@@ -5926,6 +5971,85 @@ first_fp_reg_to_save ()
 
   return first_reg;
 }
+
+/* Similar, for AltiVec regs.  */
+
+static int
+first_altivec_reg_to_save ()
+{
+  int i;
+
+  /* Stack frame remains as is unless we are in AltiVec ABI.  */
+  if (! TARGET_ALTIVEC_ABI)
+    return LAST_ALTIVEC_REGNO + 1;
+
+  /* Find lowest numbered live register.  */
+  for (i = FIRST_ALTIVEC_REGNO + 20; i <= LAST_ALTIVEC_REGNO; ++i)
+    if (regs_ever_live[i])
+      break;
+
+  return i;
+}
+
+/* Return a 32-bit mask of the AltiVec registers we need to set in
+   VRSAVE.  Bit n of the return value is 1 if Vn is live.  The MSB in
+   the 32-bit word is 0.  */
+
+static unsigned int
+compute_vrsave_mask ()
+{
+  unsigned int i, mask = 0;
+
+  /* First, find out if we use _any_ altivec registers.  */
+  for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i)
+    if (regs_ever_live[i])
+      mask |= ALTIVEC_REG_BIT (i);
+
+  if (mask == 0)
+    return mask;
+
+  /* Next, add all registers that are call-clobbered.  We do this
+     because post-reload register optimizers such as regrename_optimize
+     may choose to use them.  They never change the register class
+     chosen by reload, so cannot create new uses of altivec registers
+     if there were none before, so the early exit above is safe.  */
+  /* ??? Alternately, we could define HARD_REGNO_RENAME_OK to disallow
+     altivec registers not saved in the mask, which might well make the
+     adjustments below more effective in eliding the save/restore of
+     VRSAVE in small functions.  */
+  for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i)
+    if (call_used_regs[i])
+      mask |= ALTIVEC_REG_BIT (i);
+
+  /* Next, remove the argument registers from the set.  These must
+     be in the VRSAVE mask set by the caller, so we don't need to add
+     them in again.  More importantly, the mask we compute here is
+     used to generate CLOBBERs in the set_vrsave insn, and we do not
+     wish the argument registers to die.  */
+  for (i = cfun->args_info.vregno; i >= ALTIVEC_ARG_MIN_REG; --i)
+    mask &= ~ALTIVEC_REG_BIT (i);
+
+  /* Similarly, remove the return value from the set.  */
+  {
+    bool yes = false;
+    diddle_return_value (is_altivec_return_reg, &yes);
+    if (yes)
+      mask &= ~ALTIVEC_REG_BIT (ALTIVEC_ARG_RETURN);
+  }
+
+  return mask;
+}
+
+static void
+is_altivec_return_reg (reg, xyes)
+     rtx reg;
+     void *xyes;
+{
+  bool *yes = (bool *) xyes;
+  if (REGNO (reg) == ALTIVEC_ARG_RETURN)
+    *yes = true;
+}
+
 \f
 /* Calculate the stack information for the current function.  This is
    complicated by having two separate calling sequences, the AIX calling
@@ -5954,9 +6078,15 @@ first_fp_reg_to_save ()
                +---------------------------------------+
                | Float/int conversion temporary (X)    | 24+P+A+L
                +---------------------------------------+
-               | Save area for GP registers (G)        | 24+P+A+X+L
+               | Save area for AltiVec registers (W)   | 24+P+A+L+X
+               +---------------------------------------+
+               | AltiVec alignment padding (Y)         | 24+P+A+L+X+W
                +---------------------------------------+
-               | Save area for FP registers (F)        | 24+P+A+X+L+G
+               | Save area for VRSAVE register (Z)     | 24+P+A+L+X+W+Y
+               +---------------------------------------+
+               | Save area for GP registers (G)        | 24+P+A+X+L+X+W+Y+Z
+               +---------------------------------------+
+               | Save area for FP registers (F)        | 24+P+A+X+L+X+W+Y+Z+G
                +---------------------------------------+
        old SP->| back chain to caller's caller         |
                +---------------------------------------+
@@ -5982,11 +6112,17 @@ first_fp_reg_to_save ()
                +---------------------------------------+    
                | Float/int conversion temporary (X)    | 8+P+A+V+L
                +---------------------------------------+
-               | saved CR (C)                          | 8+P+A+V+L+X
+               | Save area for AltiVec registers (W)   | 8+P+A+V+L+X
+               +---------------------------------------+
+               | AltiVec alignment padding (Y)         | 8+P+A+V+L+X+W
+               +---------------------------------------+
+               | Save area for VRSAVE register (Z)     | 8+P+A+V+L+X+W+Y
+               +---------------------------------------+
+               | saved CR (C)                          | 8+P+A+V+L+X+W+Y+Z
                +---------------------------------------+    
-               | Save area for GP registers (G)        | 8+P+A+V+L+X+C
+               | Save area for GP registers (G)        | 8+P+A+V+L+X+W+Y+Z+C
                +---------------------------------------+    
-               | Save area for FP registers (F)        | 8+P+A+V+L+X+C+G
+               | Save area for FP registers (F)        | 8+P+A+V+L+X+W+Y+Z+C+G
                +---------------------------------------+
        old SP->| back chain to caller's caller         |
                +---------------------------------------+
@@ -6042,6 +6178,10 @@ rs6000_stack_info ()
   info_ptr->first_fp_reg_save = first_fp_reg_to_save ();
   info_ptr->fp_size = 8 * (64 - info_ptr->first_fp_reg_save);
 
+  info_ptr->first_altivec_reg_save = first_altivec_reg_to_save ();
+  info_ptr->altivec_size = 16 * (LAST_ALTIVEC_REGNO + 1
+                                - info_ptr->first_altivec_reg_save);
+
   /* Does this function call anything?  */
   info_ptr->calls_p = (! current_function_is_leaf
                       || cfun->machine->ra_needs_full_frame);
@@ -6054,6 +6194,7 @@ rs6000_stack_info ()
 #endif
       || (info_ptr->first_fp_reg_save != 64
          && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save))
+      || info_ptr->first_altivec_reg_save <= LAST_ALTIVEC_REGNO
       || (abi == ABI_V4 && current_function_calls_alloca)
       || (abi == ABI_SOLARIS && current_function_calls_alloca)
       || (DEFAULT_ABI == ABI_DARWIN
@@ -6095,14 +6236,17 @@ rs6000_stack_info ()
   info_ptr->vars_size    = RS6000_ALIGN (get_frame_size (), 8);
   info_ptr->parm_size    = RS6000_ALIGN (current_function_outgoing_args_size,
                                         8);
-  info_ptr->save_size    = RS6000_ALIGN (info_ptr->fp_size
-                                        + info_ptr->gp_size
-                                        + ehrd_size
-                                        + info_ptr->cr_size
-                                        + info_ptr->lr_size
-                                        + info_ptr->toc_size, 8);
-  if (DEFAULT_ABI == ABI_DARWIN)
-    info_ptr->save_size = RS6000_ALIGN (info_ptr->save_size, 16);
+
+  if (TARGET_ALTIVEC_ABI)
+    {
+      info_ptr->vrsave_mask = compute_vrsave_mask ();
+      info_ptr->vrsave_size  = info_ptr->vrsave_mask ? 4 : 0;
+    }
+  else
+    {
+      info_ptr->vrsave_mask = 0;
+      info_ptr->vrsave_size = 0;
+    }
 
   /* Calculate the offsets.  */
   switch (abi)
@@ -6116,7 +6260,29 @@ rs6000_stack_info ()
     case ABI_DARWIN:
       info_ptr->fp_save_offset   = - info_ptr->fp_size;
       info_ptr->gp_save_offset   = info_ptr->fp_save_offset - info_ptr->gp_size;
-      info_ptr->ehrd_offset      = info_ptr->gp_save_offset - ehrd_size;
+
+      if (TARGET_ALTIVEC_ABI)
+       {
+         info_ptr->vrsave_save_offset
+           = info_ptr->gp_save_offset - info_ptr->vrsave_size;
+
+         /* Align stack so vector save area is on a quadword boundary.  */
+         if (info_ptr->altivec_size != 0)
+           info_ptr->altivec_padding_size
+             = 16 - (-info_ptr->vrsave_save_offset % 16);
+         else
+           info_ptr->altivec_padding_size = 0;
+
+         info_ptr->altivec_save_offset
+           = info_ptr->vrsave_save_offset
+           - info_ptr->altivec_padding_size
+           - info_ptr->altivec_size;
+
+         /* Adjust for AltiVec case.  */
+         info_ptr->ehrd_offset = info_ptr->altivec_save_offset - ehrd_size;
+       }
+      else
+       info_ptr->ehrd_offset      = info_ptr->gp_save_offset - ehrd_size;
       info_ptr->cr_save_offset   = reg_size; /* first word when 64-bit.  */
       info_ptr->lr_save_offset   = 2*reg_size;
       break;
@@ -6126,12 +6292,48 @@ rs6000_stack_info ()
       info_ptr->fp_save_offset   = - info_ptr->fp_size;
       info_ptr->gp_save_offset   = info_ptr->fp_save_offset - info_ptr->gp_size;
       info_ptr->cr_save_offset   = info_ptr->gp_save_offset - info_ptr->cr_size;
-      info_ptr->toc_save_offset  = info_ptr->cr_save_offset - info_ptr->toc_size;
+
+      if (TARGET_ALTIVEC_ABI)
+       {
+         info_ptr->vrsave_save_offset
+           = info_ptr->cr_save_offset - info_ptr->vrsave_size;
+
+         /* Align stack so vector save area is on a quadword boundary.  */
+         if (info_ptr->altivec_size != 0)
+           info_ptr->altivec_padding_size
+             = 16 - (-info_ptr->vrsave_save_offset % 16);
+         else
+           info_ptr->altivec_padding_size = 0;
+
+         info_ptr->altivec_save_offset
+           = info_ptr->vrsave_save_offset
+           - info_ptr->altivec_padding_size
+           - info_ptr->altivec_size;
+
+         /* Adjust for AltiVec case.  */
+         info_ptr->toc_save_offset
+           = info_ptr->altivec_save_offset - info_ptr->toc_size;
+       }
+      else
+       info_ptr->toc_save_offset  = info_ptr->cr_save_offset - info_ptr->toc_size;
       info_ptr->ehrd_offset      = info_ptr->toc_save_offset - ehrd_size;
       info_ptr->lr_save_offset   = reg_size;
       break;
     }
 
+  info_ptr->save_size    = RS6000_ALIGN (info_ptr->fp_size
+                                        + info_ptr->gp_size
+                                        + info_ptr->altivec_size
+                                        + info_ptr->altivec_padding_size
+                                        + info_ptr->vrsave_size
+                                        + ehrd_size
+                                        + info_ptr->cr_size
+                                        + info_ptr->lr_size
+                                        + info_ptr->vrsave_size
+                                        + info_ptr->toc_size,
+                                        (TARGET_ALTIVEC_ABI || ABI_DARWIN)
+                                        ? 16 : 8);
+
   total_raw_size        = (info_ptr->vars_size
                            + info_ptr->parm_size
                            + info_ptr->save_size
@@ -6173,6 +6375,12 @@ rs6000_stack_info ()
   if (info_ptr->gp_size == 0)
     info_ptr->gp_save_offset = 0;
 
+  if (! TARGET_ALTIVEC_ABI || info_ptr->altivec_size == 0)
+    info_ptr->altivec_save_offset = 0;
+
+  if (! TARGET_ALTIVEC_ABI || info_ptr->vrsave_mask == 0)
+    info_ptr->vrsave_save_offset = 0;
+
   if (! info_ptr->lr_save_p)
     info_ptr->lr_save_offset = 0;
 
@@ -6212,12 +6420,19 @@ debug_stack_info (info)
 
   fprintf (stderr, "\tABI                 = %5s\n", abi_string);
 
+  if (TARGET_ALTIVEC_ABI)
+    fprintf (stderr, "\tALTIVEC ABI extensions enabled.\n");
+
   if (info->first_gp_reg_save != 32)
     fprintf (stderr, "\tfirst_gp_reg_save   = %5d\n", info->first_gp_reg_save);
 
   if (info->first_fp_reg_save != 64)
     fprintf (stderr, "\tfirst_fp_reg_save   = %5d\n", info->first_fp_reg_save);
 
+  if (info->first_altivec_reg_save <= LAST_ALTIVEC_REGNO)
+    fprintf (stderr, "\tfirst_altivec_reg_save = %5d\n",
+            info->first_altivec_reg_save);
+
   if (info->lr_save_p)
     fprintf (stderr, "\tlr_save_p           = %5d\n", info->lr_save_p);
 
@@ -6227,6 +6442,9 @@ debug_stack_info (info)
   if (info->toc_save_p)
     fprintf (stderr, "\ttoc_save_p          = %5d\n", info->toc_save_p);
 
+  if (info->vrsave_mask)
+    fprintf (stderr, "\tvrsave_mask         = 0x%x\n", info->vrsave_mask);
+
   if (info->push_p)
     fprintf (stderr, "\tpush_p              = %5d\n", info->push_p);
 
@@ -6239,6 +6457,14 @@ debug_stack_info (info)
   if (info->fp_save_offset)
     fprintf (stderr, "\tfp_save_offset      = %5d\n", info->fp_save_offset);
 
+  if (info->altivec_save_offset)
+    fprintf (stderr, "\taltivec_save_offset = %5d\n",
+            info->altivec_save_offset);
+
+  if (info->vrsave_save_offset)
+    fprintf (stderr, "\tvrsave_save_offset  = %5d\n",
+            info->vrsave_save_offset);
+
   if (info->lr_save_offset)
     fprintf (stderr, "\tlr_save_offset      = %5d\n", info->lr_save_offset);
 
@@ -6272,6 +6498,16 @@ debug_stack_info (info)
   if (info->fp_size)
     fprintf (stderr, "\tfp_size             = %5d\n", info->fp_size);
 
+  if (info->altivec_size)
+    fprintf (stderr, "\taltivec_size        = %5d\n", info->altivec_size);
+
+  if (info->vrsave_size)
+    fprintf (stderr, "\tvrsave_size         = %5d\n", info->vrsave_size);
+
+  if (info->altivec_padding_size)
+    fprintf (stderr, "\taltivec_padding_size= %5d\n",
+            info->altivec_padding_size);
+
   if (info->lr_size)
     fprintf (stderr, "\tlr_size             = %5d\n", info->lr_size);
 
@@ -6806,6 +7042,36 @@ rs6000_frame_related (insn, reg, val, reg2, rreg)
                                        REG_NOTES (insn));
 }
 
+/* Returns an insn that has a vrsave set operation with the
+   appropriate CLOBBERs.  */
+
+static rtx
+generate_set_vrsave (reg, info)
+     rtx reg;
+     rs6000_stack_t *info;
+{
+  int nclobs, i;
+  rtx insn, clobs[TOTAL_ALTIVEC_REGS + 1];
+
+  clobs[0] = gen_set_vrsave (reg);
+
+  nclobs = 1;
+
+  /* CLOBBER the registers in the mask.  */
+
+  for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i)
+    if (info->vrsave_mask != 0 && ALTIVEC_REG_BIT (i) != 0)
+      clobs[nclobs++] = gen_rtx_CLOBBER (VOIDmode,
+                                        gen_rtx_REG (V4SImode, i));
+
+  insn = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (nclobs));
+
+  for (i = 0; i < nclobs; ++i)
+    XVECEXP (insn, 0, i) = clobs[i];
+
+  return insn;
+}
+
 /* Emit function prologue as insns.  */
 
 void
@@ -7042,6 +7308,70 @@ rs6000_emit_prologue ()
   if (info->push_p && DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS)
     rs6000_emit_allocate_stack (info->total_size, FALSE);
 
+  /* Save AltiVec registers if needed.  */
+  if (TARGET_ALTIVEC_ABI && info->altivec_size != 0)
+    {
+      int i;
+
+      /* There should be a non inline version of this, for when we
+        are saving lots of vector registers.  */
+      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+       if (regs_ever_live[i] && ! call_used_regs[i])
+         {
+           rtx addr, areg, savereg, mem;
+
+           savereg = gen_rtx_REG (V4SImode, i);
+
+           areg = gen_rtx_REG (Pmode, 0);
+           emit_move_insn
+             (areg, GEN_INT (info->altivec_save_offset
+                             + sp_offset
+                             + 16 * (i - info->first_altivec_reg_save)));
+
+           /* AltiVec addressing mode is [reg+reg].  */
+           addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
+           mem = gen_rtx_MEM (V4SImode, addr);
+           set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+           insn = emit_move_insn (mem, savereg);
+           rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, 
+                                 NULL_RTX, NULL_RTX);
+         }
+    }
+
+  /* VRSAVE is a bit vector representing which AltiVec registers
+     are used.  The OS uses this to determine which vector
+     registers to save on a context switch.  We need to save
+     VRSAVE on the stack frame, add whatever AltiVec registers we
+     used in this function, and do the corresponding magic in the
+     epilogue.  */
+
+  if (TARGET_ALTIVEC && info->vrsave_mask != 0)
+    {
+      rtx reg, addr, mem;
+
+      /* Get VRSAVE onto a GPR.  */
+      reg = gen_rtx_REG (SImode, 12);
+      emit_insn (gen_get_vrsave (reg));
+
+      /* Save VRSAVE.  */
+      addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                          GEN_INT (info->vrsave_save_offset + sp_offset));
+      mem = gen_rtx_MEM (SImode, addr);
+      set_mem_alias_set (mem, rs6000_sr_alias_set);
+      insn = emit_move_insn (mem, reg);
+      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+                           NULL_RTX, NULL_RTX);
+
+      /* Include the registers in the mask.  */
+      emit_insn (gen_iorsi3 (reg, reg, GEN_INT ((int) info->vrsave_mask)));
+
+      insn = emit_insn (generate_set_vrsave (reg, info));
+
+      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+                           NULL_RTX, NULL_RTX);
+    }
+
   /* Set frame pointer, if needed.  */
   if (frame_pointer_needed)
     {
@@ -7321,6 +7651,46 @@ rs6000_emit_epilogue (sibcall)
                          mem);
        }
 
+  /* Restore AltiVec registers if needed.  */
+  if (TARGET_ALTIVEC_ABI && info->altivec_size != 0)
+    {
+      int i;
+
+      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+       if (regs_ever_live[i] && ! call_used_regs[i])
+         {
+           rtx addr, areg, mem;
+
+           areg = gen_rtx_REG (Pmode, 0);
+           emit_move_insn
+             (areg, GEN_INT (info->altivec_save_offset
+                             + sp_offset
+                             + 16 * (i - info->first_altivec_reg_save)));
+
+           /* AltiVec addressing mode is [reg+reg].  */
+           addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
+           mem = gen_rtx_MEM (V4SImode, addr);
+           set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+           emit_move_insn (gen_rtx_REG (V4SImode, i), mem);
+         }
+    }
+
+  /* Restore VRSAVE if needed.  */
+  if (TARGET_ALTIVEC_ABI && info->vrsave_mask != 0)
+    {
+      rtx addr, mem, reg;
+
+      addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                          GEN_INT (info->vrsave_save_offset + sp_offset));
+      mem = gen_rtx_MEM (SImode, addr);
+      set_mem_alias_set (mem, rs6000_sr_alias_set);
+      reg = gen_rtx_REG (SImode, 12);
+      emit_move_insn (reg, mem);
+
+      emit_insn (generate_set_vrsave (reg, info));
+    }
+
   /* If we saved cr, restore it here.  Just those that were used.  */
   if (info->cr_save_p)
     {
index b2401d9..71e41d2 100644 (file)
@@ -736,6 +736,7 @@ extern int rs6000_debug_arg;                /* debug argument handling */
 #define XER_REGNO    76
 #define FIRST_ALTIVEC_REGNO    77
 #define LAST_ALTIVEC_REGNO     108
+#define TOTAL_ALTIVEC_REGS     (LAST_ALTIVEC_REGNO - FIRST_ALTIVEC_REGNO)
 #define VRSAVE_REGNO           109
 
 /* List the order in which to allocate registers.  Each register must be
@@ -1265,16 +1266,20 @@ extern enum rs6000_abi rs6000_current_abi;      /* available for use by subtarget */
 typedef struct rs6000_stack {
   int first_gp_reg_save;       /* first callee saved GP register used */
   int first_fp_reg_save;       /* first callee saved FP register used */
+  int first_altivec_reg_save;  /* first callee saved AltiVec register used */
   int lr_save_p;               /* true if the link reg needs to be saved */
   int cr_save_p;               /* true if the CR reg needs to be saved */
+  unsigned int vrsave_mask;    /* mask of vec registers to save */
   int toc_save_p;              /* true if the TOC needs to be saved */
   int push_p;                  /* true if we need to allocate stack space */
   int calls_p;                 /* true if the function makes any calls */
   enum rs6000_abi abi;         /* which ABI to use */
   int gp_save_offset;          /* offset to save GP regs from initial SP */
   int fp_save_offset;          /* offset to save FP regs from initial SP */
+  int altivec_save_offset;     /* offset to save AltiVec regs from inital SP */
   int lr_save_offset;          /* offset to save LR from initial SP */
   int cr_save_offset;          /* offset to save CR from initial SP */
+  int vrsave_save_offset;      /* offset to save VRSAVE from initial SP */
   int toc_save_offset;         /* offset to save the TOC pointer */
   int varargs_save_offset;     /* offset to save the varargs registers */
   int ehrd_offset;             /* offset to EH return data */
@@ -1286,8 +1291,12 @@ typedef struct rs6000_stack {
   int fixed_size;              /* fixed size of stack frame */
   int gp_size;                 /* size of saved GP registers */
   int fp_size;                 /* size of saved FP registers */
+  int altivec_size;            /* size of saved AltiVec registers */
   int cr_size;                 /* size to hold CR if not in save_size */
   int lr_size;                 /* size to hold LR if not in save_size */
+  int vrsave_size;             /* size to hold VRSAVE if not in save_size */
+  int altivec_padding_size;    /* size of altivec alignment padding if
+                                  not in save_size */
   int toc_size;                        /* size to hold TOC if not in save_size */
   int total_size;              /* total bytes allocated for stack */
 } rs6000_stack_t;
@@ -1685,6 +1694,7 @@ typedef struct rs6000_args
 
 #define        EPILOGUE_USES(REGNO)                                    \
   ((reload_completed && (REGNO) == LINK_REGISTER_REGNUM)       \
+   || (REGNO) == VRSAVE_REGNO                                  \
    || (current_function_calls_eh_return                                \
        && TARGET_AIX                                           \
        && (REGNO) == TOC_REGISTER))
@@ -2792,6 +2802,7 @@ do {                                                                      \
                     CONST_DOUBLE, SYMBOL_REF}},                           \
   {"load_multiple_operation", {PARALLEL}},                                \
   {"store_multiple_operation", {PARALLEL}},                               \
+  {"vrsave_operation", {PARALLEL}},                                       \
   {"branch_comparison_operator", {EQ, NE, LE, LT, GE,                     \
                                  GT, LEU, LTU, GEU, GTU,                  \
                                  UNORDERED, ORDERED,                      \
index 0292c57..174d3e6 100644 (file)
    vor %0,%1,%1"
   [(set_attr "type" "altivec")])
 
+;; Copy VRSAVE into a GPR.
+(define_insn "get_vrsave"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (unspec:SI [(reg:SI 109)] 28))]
+  "TARGET_ALTIVEC"
+  "mfvrsave %0"
+  [(set_attr "type" "altivec")])
+
+(define_insn "*set_vrsave_internal"
+  [(match_parallel 0 "vrsave_operation"
+     [(set (reg:SI 109)
+          (unspec:SI [(match_operand:SI 1 "register_operand" "r")
+                      (reg:SI 109)] 30))])]
+  "TARGET_ALTIVEC"
+  "mtvrsave %1"
+  [(set_attr "type" "altivec")])
+
+(define_insn "set_vrsave"
+  [(set (reg:SI 109)
+       (unspec:SI [(match_operand:SI 0 "register_operand" "r")
+                   (reg:SI 109)] 30))]
+  "TARGET_ALTIVEC"
+  "mtvrsave %0"
+  [(set_attr "type" "altivec")])
+
 ;; Simple binary operations.
 
 (define_insn "altivec_vaddubm"