Initial revision
authorRichard Kenner <kenner@gcc.gnu.org>
Sun, 9 Feb 1992 19:26:21 +0000 (14:26 -0500)
committerRichard Kenner <kenner@gcc.gnu.org>
Sun, 9 Feb 1992 19:26:21 +0000 (14:26 -0500)
From-SVN: r296

gcc/config/rs6000/rs6000.c [new file with mode: 0644]

diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
new file mode 100644 (file)
index 0000000..47e9342
--- /dev/null
@@ -0,0 +1,1509 @@
+/* Subroutines used for code generation on IBM RS/6000.
+   Copyright (C) 1991 Free Software Foundation, Inc.
+   Contributed by Richard Kenner (kenner@nyu.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "recog.h"
+#include "expr.h"
+#include "obstack.h"
+
+#define min(A,B)       ((A) < (B) ? (A) : (B))
+#define max(A,B)       ((A) > (B) ? (A) : (B))
+
+/* Names of bss and data sections.  These should be unique names for each
+   compilation unit.  */
+
+char *rs6000_bss_section_name;
+char *rs6000_private_data_section_name;
+char *rs6000_read_only_section_name;
+
+/* Set to non-zero by "fix" operation to indicate that itrunc and
+   uitrunc must be defined.  */
+
+int rs6000_trunc_used;
+
+/* Set to non-zero once they have been defined.  */
+
+static int trunc_defined;
+
+/* Save information from a "cmpxx" operation until the branch or scc is
+   emitted.  */
+
+rtx rs6000_compare_op0, rs6000_compare_op1;
+int rs6000_compare_fp_p;
+\f
+/* Return non-zero if this function is known to have a null epilogue.  */
+
+int
+direct_return ()
+{
+  return (reload_completed
+         && first_reg_to_save () == 32
+         && first_fp_reg_to_save () == 64
+         && ! regs_ever_live[65]
+         && ! rs6000_pushes_stack ());
+}
+
+/* Returns 1 always.  */
+
+int
+any_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return 1;
+}
+
+/* Return 1 if OP is a constant that can fit in a D field.  */
+
+int
+short_cint_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return (GET_CODE (op) == CONST_INT
+         && (unsigned) (INTVAL (op) + 0x8000) < 0x10000);
+}
+
+/* Similar for a unsigned D field.  */
+
+int
+u_short_cint_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) == 0);
+}
+
+/* Returns 1 if OP is a register that is not special (i.e., not MQ,
+   ctr, or lr).  */
+
+int
+gen_reg_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return (register_operand (op, mode)
+         && (GET_CODE (op) != REG || REGNO (op) >= 67 || REGNO (op) < 64));
+}
+
+/* Returns 1 if OP is either a pseudo-register or a register denoting a
+   CR field.  */
+
+int
+cc_reg_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return (register_operand (op, mode)
+         && (GET_CODE (op) != REG
+             || REGNO (op) >= FIRST_PSEUDO_REGISTER
+             || CR_REGNO_P (REGNO (op))));
+}
+
+/* Returns 1 if OP is either a constant integer valid for a D-field or a
+   non-special register.  If a register, it must be in the proper mode unless
+   MODE is VOIDmode.  */
+
+int
+reg_or_short_operand (op, mode)
+      register rtx op;
+      enum machine_mode mode;
+{
+  if (GET_CODE (op) == CONST_INT)
+    return short_cint_operand (op, mode);
+
+  return gen_reg_operand (op, mode);
+}
+
+/* Similar, except check if the negation of the constant would be valid for
+   a D-field.  */
+
+int
+reg_or_neg_short_operand (op, mode)
+      register rtx op;
+      enum machine_mode mode;
+{
+  if (GET_CODE (op) == CONST_INT)
+    return CONST_OK_FOR_LETTER_P (INTVAL (op), 'P');
+
+  return gen_reg_operand (op, mode);
+}
+
+/* Return 1 if the operand is either a register or an integer whose high-order
+   16 bits are zero.  */
+
+int
+reg_or_u_short_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) == CONST_INT
+      && (INTVAL (op) & 0xffff0000) == 0)
+    return 1;
+
+  return gen_reg_operand (op, mode);
+}
+
+/* Return 1 is the operand is either a non-special register or ANY
+   constant integer.  */
+
+int
+reg_or_cint_operand (op, mode)
+    register rtx op;
+    enum machine_mode mode;
+{
+     return GET_CODE (op) == CONST_INT || gen_reg_operand (op, mode);
+}
+
+/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a
+   register with one instruction per word.  For SFmode, this means  that
+   the low 16-bits are zero.  For DFmode, it means the low 16-bits of
+   the first word are zero and the high 16 bits of the second word
+   are zero (usually all bits in the low-order word will be zero).
+
+   We only do this if we can safely read CONST_DOUBLE_{LOW,HIGH}.  */
+
+int
+easy_fp_constant (op, mode)
+     register rtx op;
+     register enum machine_mode mode;
+{
+  rtx low, high;
+
+  if (GET_CODE (op) != CONST_DOUBLE
+      || GET_MODE (op) != mode
+      || GET_MODE_CLASS (mode) != MODE_FLOAT)
+    return 0;
+
+  high = operand_subword (op, 0, 0, mode);
+  low = operand_subword (op, 1, 0, mode);
+
+  if (high == 0 || GET_CODE (high) != CONST_INT || (INTVAL (high) & 0xffff))
+    return 0;
+
+  return (mode == SFmode
+         || (low != 0 && GET_CODE (low) == CONST_INT
+             && (INTVAL (low) & 0xffff0000) == 0));
+}
+      
+/* Return 1 if the operand is either a floating-point register, a pseudo
+   register, or memory.  */
+
+int
+fp_reg_or_mem_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return (memory_operand (op, mode)
+         || (register_operand (op, mode)
+             && (GET_CODE (op) != REG
+                 || REGNO (op) >= FIRST_PSEUDO_REGISTER
+                 || FP_REGNO_P (REGNO (op)))));
+}
+
+/* Return 1 if the operand is either an easy FP constant (see above) or
+   memory.  */
+
+int
+mem_or_easy_const_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return memory_operand (op, mode) || easy_fp_constant (op, mode);
+}
+
+/* Return 1 if the operand is either a non-special register or an item
+   that can be used as the operand of an SI add insn.  */
+
+int
+add_operand (op, mode)
+    register rtx op;
+    enum machine_mode mode;
+{
+  return (reg_or_short_operand (op, mode)
+         || (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff) == 0));
+}
+
+/* Return 1 if the operand is a non-special register or a constant that
+   can be used as the operand of an OR or XOR insn on the RS/6000.  */
+
+int
+logical_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return (gen_reg_operand (op, mode)
+         || (GET_CODE (op) == CONST_INT
+             && ((INTVAL (op) & 0xffff0000) == 0
+                 || (INTVAL (op) & 0xffff) == 0)));
+}
+
+/* Return 1 if C is a constant that can be encoded in a mask on the
+   RS/6000.  It is if there are no more than two 1->0 or 0->1 transitions.
+   Reject all ones and all zeros, since these should have been optimized
+   away and confuse the making of MB and ME.  */
+
+int
+mask_constant (c)
+     register int c;
+{
+  int i;
+  int last_bit_value;
+  int transitions = 0;
+
+  if (c == 0 || c == ~0)
+    return 0;
+
+  last_bit_value = c & 1;
+
+  for (i = 1; i < 32; i++)
+    if (((c >>= 1) & 1) != last_bit_value)
+      last_bit_value ^= 1, transitions++;
+
+  return transitions <= 2;
+}
+
+/* Return 1 if the operand is a constant that is a mask on the RS/6000. */
+
+int
+mask_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return GET_CODE (op) == CONST_INT && mask_constant (INTVAL (op));
+}
+
+/* Return 1 if the operand is either a non-special register or a
+   constant that can be used as the operand of an RS/6000 logical AND insn.  */
+
+int
+and_operand (op, mode)
+    register rtx op;
+    enum machine_mode mode;
+{
+  return (reg_or_short_operand (op, mode)
+         || logical_operand (op, mode)
+         || mask_operand (op, mode));
+}
+
+/* Return 1 if the operand is a general register or memory operand.  */
+
+int
+reg_or_mem_operand (op, mode)
+     register rtx op;
+     register enum machine_mode mode;
+{
+  return gen_reg_operand (op, mode) || memory_operand (op, mode);
+}
+
+/* Return 1 if the operand, used inside a MEM, is a valid first argument
+   to CALL.  This is a SYMBOL_REF or a pseudo-register, which will be
+   forced to lr.  */
+
+int
+call_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  if (mode != VOIDmode && GET_MODE (op) != mode)
+    return 0;
+
+  return (GET_CODE (op) == SYMBOL_REF
+         || (GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER));
+}
+
+/* Return 1 if this operand is a valid input for a move insn.  */
+
+int
+input_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  if (memory_operand (op, mode))
+    return 1;
+
+  /* For floating-point or multi-word mode, only register or memory
+     is valid.  */
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT
+      || GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+    return gen_reg_operand (op, mode);
+
+  /* For SImode, we can also load from a special register, so any register
+     is valid.  */
+  if (mode == SImode && register_operand (op, mode))
+    return 1;
+
+  /* For HImode and QImode, any constant is valid along with any
+     non-special register.  */
+  if (mode == HImode || mode == QImode)
+    return register_operand (op, mode) || GET_CODE (op) == CONST_INT;
+
+  /* Otherwise, we will be doing this SET with an add, so anything valid
+     for an add will be valid.  */
+  return add_operand (op, mode);
+}
+\f
+/* Return 1 if OP is a load multiple operation.  It is known to be a
+   PARALLEL and the first section will be tested.  */
+
+int
+load_multiple_operation (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  int count = XVECLEN (op, 0);
+  int dest_regno;
+  rtx src_addr;
+  int i;
+
+  /* Perform a quick check so we don't blow up below.  */
+  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))) != MEM)
+    return 0;
+
+  dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
+  src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
+
+  for (i = 1; i < count; i++)
+    {
+      rtx elt = XVECEXP (op, 0, i);
+
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_DEST (elt)) != REG
+         || GET_MODE (SET_DEST (elt)) != SImode
+         || REGNO (SET_DEST (elt)) != dest_regno + i
+         || GET_CODE (SET_SRC (elt)) != MEM
+         || GET_MODE (SET_SRC (elt)) != SImode
+         || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
+         || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
+         || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
+         || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
+       return 0;
+    }
+
+  return 1;
+}
+
+/* Similar, but tests for store multiple.  Here, the second vector element
+   is a CLOBBER.  It will be tested later.  */
+
+int
+store_multiple_operation (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  int count = XVECLEN (op, 0) - 1;
+  int src_regno;
+  rtx dest_addr;
+  int i;
+
+  /* Perform a quick check so we don't blow up below.  */
+  if (count <= 1
+      || GET_CODE (XVECEXP (op, 0, 0)) != SET
+      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
+      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
+    return 0;
+
+  src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
+  dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
+
+  for (i = 1; i < count; i++)
+    {
+      rtx elt = XVECEXP (op, 0, i + 1);
+
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_SRC (elt)) != REG
+         || GET_MODE (SET_SRC (elt)) != SImode
+         || REGNO (SET_SRC (elt)) != src_regno + i
+         || GET_CODE (SET_DEST (elt)) != MEM
+         || GET_MODE (SET_DEST (elt)) != SImode
+         || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
+         || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
+         || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
+         || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
+       return 0;
+    }
+
+  return 1;
+}
+\f
+/* Return 1 if OP is a comparison operation that is valid for a branch insn.
+   We only check the opcode against the mode of the CC value here.  */
+
+int
+branch_comparison_operator (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  enum rtx_code code = GET_CODE (op);
+  enum machine_mode cc_mode;
+
+  if (GET_RTX_CLASS (code) != '<')
+    return 0;
+
+  cc_mode = GET_MODE (XEXP (op, 0));
+  if (GET_MODE_CLASS (cc_mode) != MODE_CC)
+    return 0;
+
+  if ((code == GT || code == LT || code == GE || code == LE)
+      && cc_mode == CCUNSmode)
+    return 0;
+
+  if ((code == GTU || code == LTU || code == GEU || code == LEU)
+      && (cc_mode != CCUNSmode))
+    return 0;
+
+  return 1;
+}
+
+/* Return 1 if OP is a comparison operation that is valid for an scc insn.
+   We check the opcode against the mode of the CC value and disallow EQ or
+   NE comparisons for integers.  */
+
+int
+scc_comparison_operator (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  enum rtx_code code = GET_CODE (op);
+  enum machine_mode cc_mode;
+
+  if (GET_MODE (op) != mode && mode != VOIDmode)
+    return 0;
+
+  if (GET_RTX_CLASS (code) != '<')
+    return 0;
+
+  cc_mode = GET_MODE (XEXP (op, 0));
+  if (GET_MODE_CLASS (cc_mode) != MODE_CC)
+    return 0;
+
+  if (code == NE && cc_mode != CCFPmode)
+    return 0;
+
+  if ((code == GT || code == LT || code == GE || code == LE)
+      && cc_mode == CCUNSmode)
+    return 0;
+
+  if ((code == GTU || code == LTU || code == GEU || code == LEU)
+      && (cc_mode != CCUNSmode))
+    return 0;
+
+  return 1;
+}
+\f
+/* Return 1 if ANDOP is a mask that has no bits on that are not in the
+   mask required to convert the result of a rotate insn into a shift
+   left insn of SHIFTOP bits.  Both are known to be CONST_INT.  */
+
+int
+includes_lshift_p (shiftop, andop)
+     register rtx shiftop;
+     register rtx andop;
+{
+  int shift_mask = (~0 << INTVAL (shiftop));
+
+  return (INTVAL (andop) & ~shift_mask) == 0;
+}
+
+/* Similar, but for right shift.  */
+
+int
+includes_rshift_p (shiftop, andop)
+     register rtx shiftop;
+     register rtx andop;
+{
+  unsigned shift_mask = ~0;
+
+  shift_mask >>= INTVAL (shiftop);
+
+  return (INTVAL (andop) & ~ shift_mask) == 0;
+}
+\f
+/* Return the register class of a scratch register needed to copy IN into
+   or out of a register in CLASS in MODE.  If it can be done directly,
+   NO_REGS is returned.  */
+
+enum reg_class
+secondary_reload_class (class, mode, in)
+     enum reg_class class;
+     enum machine_mode mode;
+     rtx in;
+{
+  int regno = true_regnum (in);
+
+  if (regno >= FIRST_PSEUDO_REGISTER)
+    regno = -1;
+
+  /* We can place anything into GENERAL_REGS and can put GENERAL_REGS
+     into anything.  */
+  if (class == GENERAL_REGS || class == BASE_REGS
+      || (regno >= 0 && INT_REGNO_P (regno)))
+    return NO_REGS;
+
+  /* Constants, memory, and FP registers can go into FP registers.  */
+  if ((regno == -1 || FP_REGNO_P (regno))
+      && (class == FLOAT_REGS || class == NON_SPECIAL_REGS))
+    return NO_REGS;
+
+  /* We can copy among the CR registers.  */
+  if ((class == CR_REGS || class == CR0_REGS)
+      && regno >= 0 && CR_REGNO_P (regno))
+    return NO_REGS;
+
+  /* Otherwise, we need GENERAL_REGS.  */
+  return GENERAL_REGS;
+}
+\f
+/* Given a comparison operation, return the bit number in CCR to test.  We
+   know this is a valid comparison.  
+
+   SCC_P is 1 if this is for an scc.  That means that %D will have been
+   used instead of %C, so the bits will be in different places.
+
+   Return -1 if OP isn't a valid compaison for some reason.  */
+
+int
+ccr_bit (op, scc_p)
+     register rtx op;
+     int scc_p;
+{
+  enum rtx_code code = GET_CODE (op);
+  enum machine_mode cc_mode;
+  int cc_regnum;
+  int base_bit;
+
+  if (GET_RTX_CLASS (code) != '<')
+    return -1;
+
+  cc_mode = GET_MODE (XEXP (op, 0));
+  cc_regnum = REGNO (XEXP (op, 0));
+  base_bit = 4 * (cc_regnum - 68);
+
+  switch (code)
+    {
+    case NE:
+      return scc_p ? base_bit + 3 : base_bit + 2;
+    case EQ:
+      return base_bit + 2;
+    case GT:  case GTU:
+      return base_bit + 1;
+    case LT:  case LTU:
+      return base_bit;
+
+    case GE:  case GEU:
+      /* If floating-point, we will have done a cror to put the bit in the
+        unordered position.  So test that bit.  For integer, this is ! LT
+        unless this is an scc insn.  */
+      return cc_mode == CCFPmode || scc_p ? base_bit + 3 : base_bit;
+
+    case LE:  case LEU:
+      return cc_mode == CCFPmode || scc_p ? base_bit + 3 : base_bit + 1;
+
+    default:
+      abort ();
+    }
+}
+\f
+/* Print an operand.  Recognize special options, documented below.  */
+
+void
+print_operand (file, x, code)
+    FILE *file;
+    rtx x;
+    char code;
+{
+  int i;
+  int val;
+
+  /* These macros test for integers and extract the low-order bits.  */
+#define INT_P(X)  \
+((GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE)   \
+ && GET_MODE (X) == VOIDmode)
+
+#define INT_LOWPART(X) \
+  (GET_CODE (X) == CONST_INT ? INTVAL (X) : CONST_DOUBLE_LOW (X))
+
+  switch (code)
+    {
+    case 'h':
+      /* If constant, output low-order six bits.  Otherwise, write normally. */
+      if (INT_P (x))
+       fprintf (file, "%d", INT_LOWPART (x) & 31);
+      else
+       print_operand (file, x, 0);
+      return;
+
+    case 'H':
+      /* X must be a constant.  Output the low order 6 bits plus 24.  */
+      if (! INT_P (x))
+       output_operand_lossage ("invalid %%H value");
+
+      fprintf (file, "%d", (INT_LOWPART (x) + 24) & 31);
+      return;
+
+    case 'b':
+      /* Low-order 16 bits of constant, unsigned.  */
+      if (! INT_P (x))
+       output_operand_lossage ("invalid %%b value");
+
+      fprintf (file, "%d", INT_LOWPART (x) & 0xffff);
+      return;
+
+    case 'w':
+      /* If constant, low-order 16 bits of constant, signed.  Otherwise, write
+        normally.  */
+      if (INT_P (x))
+       fprintf (file, "%d", (INT_LOWPART (x) << 16) >> 16);
+      else
+       print_operand (file, x, 0);
+      return;
+
+    case 'W':
+      /* If constant, low-order 16 bits of constant, unsigned.
+        Otherwise, write normally.  */
+      if (INT_P (x))
+       fprintf (file, "%d", INT_LOWPART (x) & 0xffff);
+      else
+       print_operand (file, x, 0);
+      return;
+
+    case 'u':
+      /* High-order 16 bits of constant.  */
+      if (! INT_P (x))
+       output_operand_lossage ("invalid %%u value");
+
+      fprintf (file, "%d", (INT_LOWPART (x) >> 16) & 0xffff);
+      return;
+
+    case 's':
+      /* Low 5 bits of 32 - value */
+      if (! INT_P (x))
+       output_operand_lossage ("invalid %%s value");
+
+      fprintf (file, "%d", (32 - INT_LOWPART (x)) & 31);
+      return;
+
+    case 'S':
+      /* Low 5 bits of 31 - value */
+      if (! INT_P (x))
+       output_operand_lossage ("invalid %%S value");
+
+      fprintf (file, "%d", (31 - INT_LOWPART (x)) & 31);
+      return;
+
+    case 'p':
+      /* X is a CONST_INT that is a power of two.  Output the logarithm.  */
+      if (! INT_P (x)
+         || (i = exact_log2 (INT_LOWPART (x))) < 0)
+       output_operand_lossage ("invalid %%p value");
+
+      fprintf (file, "%d", i);
+      return;
+
+    case 'm':
+      /* MB value for a mask operand.  */
+      if (! mask_operand (x, VOIDmode))
+       output_operand_lossage ("invalid %%m value");
+
+      val = INT_LOWPART (x);
+
+      /* If the high bit is set and the low bit is not, the value is zero.
+        If the high bit is zero, the value is the first 1 bit we find from
+        the left.  */
+      if (val < 0 && (val & 1) == 0)
+       {
+         fprintf (file, "0");
+         return;
+       }
+      else if (val >= 0)
+       {
+         for (i = 1; i < 32; i++)
+           if ((val <<= 1) < 0)
+             break;
+         fprintf (file, "%d", i);
+         return;
+       }
+         
+      /* Otherwise, look for the first 0 bit from the right.  The result is its
+        number plus 1. We know the low-order bit is one.  */
+      for (i = 0; i < 32; i++)
+       if (((val >>= 1) & 1) == 0)
+         break;
+
+      /* If we ended in ...01, I would be 0.  The correct value is 31, so
+        we want 31 - i.  */
+      fprintf (file, "%d", 31 - i);
+      return;
+
+    case 'M':
+      /* ME value for a mask operand.  */
+      if (! mask_operand (x, VOIDmode))
+       output_operand_lossage ("invalid %%m value");
+
+      val = INT_LOWPART (x);
+
+      /* If the low bit is set and the high bit is not, the value is 31.
+        If the low bit is zero, the value is the first 1 bit we find from
+        the right.  */
+      if ((val & 1) && val >= 0)
+       {
+         fprintf (file, "31");
+         return;
+       }
+      else if ((val & 1) == 0)
+       {
+         for (i = 0; i < 32; i++)
+           if ((val >>= 1) & 1)
+             break;
+
+         /* If we had ....10, I would be 0.  The result should be
+            30, so we need 30 - i.  */
+         fprintf (file, "%d", 30 - i);
+         return;
+       }
+         
+      /* Otherwise, look for the first 0 bit from the left.  The result is its
+        number minus 1. We know the high-order bit is one.  */
+      for (i = 0; i < 32; i++)
+       if ((val <<= 1) >= 0)
+         break;
+
+      fprintf (file, "%d", i);
+      return;
+
+    case 'f':
+      /* X is a CR register.  Print the shift count needed to move it
+        to the high-order four bits.  */
+      if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+       output_operand_lossage ("invalid %%f value");
+      else
+       fprintf (file, "%d", 4 * (REGNO (x) - 68));
+      return;
+
+    case 'F':
+      /* Similar, but print the count for the rotate in the opposite
+        direction.  */
+      if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+       output_operand_lossage ("invalid %%F value");
+      else
+       fprintf (file, "%d", 32 - 4 * (REGNO (x) - 68));
+      return;
+
+    case 'R':
+      /* X is a CR register.  Print the mask for `mtcrf'.  */
+      if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+       output_operand_lossage ("invalid %%R value");
+      else
+       fprintf (file, "%d", 128 >> (REGNO (x) - 68));
+      return;
+
+    case 'X':
+      if (GET_CODE (x) == MEM
+         && LEGITIMATE_INDEXED_ADDRESS_P (XEXP (x, 0)))
+       fprintf (file, "x");
+      return;
+
+    case 'U':
+      /* Print `u' is this has an auto-increment or auto-decremement.  */
+      if (GET_CODE (x) == MEM
+         && (GET_CODE (XEXP (x, 0)) == PRE_INC
+             || GET_CODE (XEXP (x, 0)) == PRE_DEC))
+       fprintf (file, "u");
+      return;
+
+    case 'I':
+      /* Print `i' is this is a constant, else nothing.  */
+      if (INT_P (x))
+       fprintf (file, "i");
+      return;
+
+    case 'N':
+      /* Write the number of elements in the vector times 4.  */
+      if (GET_CODE (x) != PARALLEL)
+       output_operand_lossage ("invalid %%N value");
+
+      fprintf (file, "%d", XVECLEN (x, 0) * 4);
+      return;
+
+    case 'O':
+      /* Similar, but subtract 1 first.  */
+      if (GET_CODE (x) != PARALLEL)
+       output_operand_lossage ("invalid %%N value");
+
+      fprintf (file, "%d", (XVECLEN (x, 0) - 1) * 4);
+      return;
+
+    case 'P':
+      /* The operand must be an indirect memory reference.  The result
+        is the register number. */
+      if (GET_CODE (x) != MEM || GET_CODE (XEXP (x, 0)) != REG
+         || REGNO (XEXP (x, 0)) >= 32)
+       output_operand_lossage ("invalid %%P value");
+
+      fprintf (file, "%d", REGNO (XEXP (x, 0)));
+      return;
+
+    case 'L':
+      /* Write second word of DImode or DFmode reference.  Works on register
+        or non-indexed memory only.  */
+      if (GET_CODE (x) == REG)
+       fprintf (file, "%d", REGNO (x) + 1);
+      else if (GET_CODE (x) == MEM)
+       {
+         /* Handle possible auto-increment.  Since it is pre-increment and
+            we have already done it, we can just use an offset of four.  */
+         if (GET_CODE (XEXP (x, 0)) == PRE_INC
+             || GET_CODE (XEXP (x, 0)) == PRE_DEC)
+           output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4));
+         else
+           output_address (plus_constant (XEXP (x, 0), 4));
+       }
+      return;
+                           
+    case 'Y':
+      /* Similar, for third word of TImode  */
+      if (GET_CODE (x) == REG)
+       fprintf (file, "%d", REGNO (x) + 2);
+      else if (GET_CODE (x) == MEM)
+       {
+         if (GET_CODE (XEXP (x, 0)) == PRE_INC
+             || GET_CODE (XEXP (x, 0)) == PRE_DEC)
+           output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8));
+         else
+           output_address (plus_constant (XEXP (x, 0), 8));
+       }
+      return;
+                           
+    case 'Z':
+      /* Similar, for last word of TImode.  */
+      if (GET_CODE (x) == REG)
+       fprintf (file, "%d", REGNO (x) + 3);
+      else if (GET_CODE (x) == MEM)
+       {
+         if (GET_CODE (XEXP (x, 0)) == PRE_INC
+             || GET_CODE (XEXP (x, 0)) == PRE_DEC)
+           output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12));
+         else
+           output_address (plus_constant (XEXP (x, 0), 12));
+       }
+      return;
+                           
+    case 't':
+      /* Write 12 if this jump operation will branch if true, 4 otherwise. 
+        All floating-point operations except NE branch true and integer
+        EQ, LT, GT, LTU and GTU also branch true.  */
+      if (GET_RTX_CLASS (GET_CODE (x)) != '<')
+       output_operand_lossage ("invalid %%t value");
+
+      else if ((GET_MODE (XEXP (x, 0)) == CCFPmode
+               && GET_CODE (x) != NE)
+              || GET_CODE (x) == EQ
+              || GET_CODE (x) == LT || GET_CODE (x) == GT
+              || GET_CODE (x) == LTU || GET_CODE (x) == GTU)
+       fprintf (file, "12");
+      else
+       fprintf (file, "4");
+      return;
+      
+    case 'T':
+      /* Opposite of 't': write 4 if this jump operation will branch if true,
+        12 otherwise.   */
+      if (GET_RTX_CLASS (GET_CODE (x)) != '<')
+       output_operand_lossage ("invalid %%t value");
+
+      else if ((GET_MODE (XEXP (x, 0)) == CCFPmode
+               && GET_CODE (x) != NE)
+              || GET_CODE (x) == EQ
+              || GET_CODE (x) == LT || GET_CODE (x) == GT
+              || GET_CODE (x) == LTU || GET_CODE (x) == GTU)
+       fprintf (file, "4");
+      else
+       fprintf (file, "12");
+      return;
+      
+    case 'j':
+      /* Write the bit number in CCR for jump.  */
+      i = ccr_bit (x, 0);
+      if (i == -1)
+       output_operand_lossage ("invalid %%j code");
+      else
+       fprintf (file, "%d", i);
+      return;
+
+    case 'J':
+      /* Similar, but add one for shift count in rlinm for scc and pass
+        scc flag to `ccr_bit'.  */
+      i = ccr_bit (x, 1);
+      if (i == -1)
+       output_operand_lossage ("invalid %%J code");
+      else
+       fprintf (file, "%d", i + 1);
+      return;
+
+    case 'C':
+      /* This is an optional cror needed for LE or GE floating-point
+        comparisons.  Otherwise write nothing.  */
+      if ((GET_CODE (x) == LE || GET_CODE (x) == GE)
+         && GET_MODE (XEXP (x, 0)) == CCFPmode)
+       {
+         int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68);
+
+         fprintf (file, "cror %d,%d,%d\n\t", base_bit + 3,
+                  base_bit + 2, base_bit + (GET_CODE (x) == GE));
+       }
+      return;
+
+    case 'D':
+      /* Similar, except that this is for an scc, so we must be able to
+        encode the test in a single bit that is one.  We do the above
+        for any LE, GE, GEU, or LEU and invert the bit for NE.  */
+      if (GET_CODE (x) == LE || GET_CODE (x) == GE
+         || GET_CODE (x) == LEU || GET_CODE (x) == GEU)
+       {
+         int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68);
+
+         fprintf (file, "cror %d,%d,%d\n\t", base_bit + 3,
+                  base_bit + 2,
+                  base_bit + (GET_CODE (x) == GE || GET_CODE (x) == GEU));
+       }
+
+      else if (GET_CODE (x) == NE)
+       {
+         int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68);
+
+         fprintf (file, "crnor %d,%d,%d\n\t", base_bit + 3,
+                  base_bit + 2, base_bit + 2);
+       }
+      return;
+
+    case 'z':
+      /* X is a SYMBOL_REF.  Write out the name preceeded by a
+        period and without any trailing data in backets.  Used for function
+        names.  */
+      if (GET_CODE (x) != SYMBOL_REF)
+       abort ();
+
+      fprintf (file, ".");
+      RS6000_OUTPUT_BASENAME (file, XSTR (x, 0));
+      return;
+
+    case 0:
+      if (GET_CODE (x) == REG)
+       fprintf (file, "%s", reg_names[REGNO (x)]);
+      else if (GET_CODE (x) == MEM)
+       {
+         /* We need to handle PRE_INC and PRE_DEC here, since we need to
+            know the width from the mode.  */
+         if (GET_CODE (XEXP (x, 0)) == PRE_INC)
+           fprintf (file, "%d(%d)", GET_MODE_SIZE (GET_MODE (x)),
+                    REGNO (XEXP (XEXP (x, 0), 0)));
+         else if (GET_CODE (XEXP (x, 0)) == PRE_DEC)
+           fprintf (file, "%d(%d)", - GET_MODE_SIZE (GET_MODE (x)),
+                    REGNO (XEXP (XEXP (x, 0), 0)));
+         else
+           output_address (XEXP (x, 0));
+       }
+      else
+       output_addr_const (file, x);
+      break;
+
+    default:
+      output_operand_lossage ("invalid %%xn code");
+    }
+}
+\f
+/* Print the address of an operand.  */
+
+void
+print_operand_address (file, x)
+     FILE *file;
+     register rtx x;
+{
+  if (GET_CODE (x) == REG)
+    fprintf (file, "0(%d)", REGNO (x));
+  else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST)
+    {
+      output_addr_const (file, x);
+      fprintf (file, "(2)");
+    }
+  else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG)
+    {
+      if (REGNO (XEXP (x, 0)) == 0)
+       fprintf (file, "%d,%d", REGNO (XEXP (x, 1)), REGNO (XEXP (x, 0)));
+      else
+       fprintf (file, "%d,%d", REGNO (XEXP (x, 0)), REGNO (XEXP (x, 1)));
+    }
+  else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+    fprintf (file, "%d(%d)", INTVAL (XEXP (x, 1)), REGNO (XEXP (x, 0)));
+  else
+    abort ();
+}
+\f
+/* This page contains routines that are used to determine what the function
+   prologue and epilogue code will do and write them out.  */
+
+/*  Return the first fixed-point register that is required to be saved. 32 if
+    none.  */
+
+int
+first_reg_to_save ()
+{
+  int first_reg;
+
+  /* Find lowest numbered live register.  */
+  for (first_reg = 13; first_reg <= 31; first_reg++)
+    if (regs_ever_live[first_reg])
+      break;
+
+  return first_reg;
+}
+
+/* Similar, for FP regs.  */
+
+int
+first_fp_reg_to_save ()
+{
+  int first_reg;
+
+  /* Find lowest numbered live register.  */
+  for (first_reg = 14 + 32; first_reg <= 63; first_reg++)
+    if (regs_ever_live[first_reg])
+      break;
+
+  return first_reg;
+}
+
+/* Return 1 if we need to save CR.  */
+
+int
+must_save_cr ()
+{
+  return regs_ever_live[70] || regs_ever_live[71] || regs_ever_live[72];
+}
+
+/* Compute the size of the save area in the stack, including the space for
+   the fixed area.  */
+
+int
+rs6000_sa_size ()
+{
+  int size;
+  int i;
+
+  /* We have the six fixed words, plus the size of the register save 
+     areas, rounded to a double-word.  */
+  size = 6 + (32 - first_reg_to_save ()) + (64 - first_fp_reg_to_save ()) * 2;
+  if (size & 1)
+    size++;
+
+  return size * 4;
+}
+
+/* Return non-zero if this function makes calls.  */
+
+int
+rs6000_makes_calls ()
+{
+  rtx insn;
+
+  for (insn = get_insns (); insn; insn = next_insn (insn))
+    if (GET_CODE (insn) == CALL_INSN)
+      return 1;
+
+  return 0;
+}
+
+/* Return non-zero if this function needs to push space on the stack.  */
+
+int
+rs6000_pushes_stack ()
+{
+  int total_size = (rs6000_sa_size () + get_frame_size ()
+                   + current_function_outgoing_args_size);
+
+  /* We need to push the stack if a frame pointer is needed (because the
+     stack might be dynamically adjusted), if we are debugging, if the
+     total stack size is more than 220 bytes, or if we make calls.  */
+
+  return (frame_pointer_needed || write_symbols != NO_DEBUG
+         || total_size > 220
+         || rs6000_makes_calls ());
+}
+
+/* Write function prologue.  */
+
+void
+output_prolog (file, size)
+     FILE *file;
+     int size;
+{
+  int first_reg = first_reg_to_save ();
+  int must_push = rs6000_pushes_stack ();
+  int first_fp_reg = first_fp_reg_to_save ();
+  int basic_size = rs6000_sa_size ();
+  int total_size = (basic_size + size + current_function_outgoing_args_size);
+
+  /* Round size to multiple of 8 bytes.  */
+  total_size = (total_size + 7) & ~7;
+
+  /* Write .extern for any function we will call to save and restore fp
+     values.  */
+  if (first_fp_reg < 62)
+    fprintf (file, "\t.extern ._savef%d\n\t.extern ._restf%d\n",
+            first_fp_reg - 32, first_fp_reg - 32);
+
+  /* Write .extern for truncation routines, if needed.  */
+  if (rs6000_trunc_used && ! trunc_defined)
+    {
+      fprintf (file, "\t.extern .itrunc\n\t.extern .uitrunc\n");
+      trunc_defined = 1;
+    }
+
+  /* If we have to call a function to save fpr's, we will be using LR.  */
+  if (first_fp_reg < 62)
+    regs_ever_live[65] = 1;
+
+  /* If we use the link register, get it into r0.  */
+  if (regs_ever_live[65])
+    fprintf (file, "\tmflr 0\n");
+
+  /* If we need to save CR, put it into r12.  */
+  if (must_save_cr ())
+    fprintf (file, "\tmfcr 12\n");
+
+  /* Do any required saving of fpr's.  If only one or two to save, do it
+     ourself.  Otherwise, call function.  */
+  if (first_fp_reg == 62)
+    fprintf (file, "\tstfd 30,-16(1)\n\tstfd 31,-8(1)\n");
+  else if (first_fp_reg == 63)
+    fprintf (file, "\tstfd 31,-8(1)\n");
+  else if (first_fp_reg != 64)
+    fprintf (file, "\tbl ._savef%d\n\tcror 15,15,15\n", first_fp_reg - 32);
+
+  /* Now save gpr's.  */
+  if (first_reg == 31)
+    fprintf (file, "\tst 31,%d(1)\n", -4 - (64 - first_fp_reg) * 8);
+  else if (first_reg != 32)
+    fprintf (file, "\tstm %d,%d(1)\n", first_reg,
+            - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8);
+
+  /* Save lr if we used it.  */
+  if (regs_ever_live[65])
+    fprintf (file, "\tst 0,8(1)\n");
+
+  /* Save CR if we use any that must be preserved.  */
+  if (must_save_cr ())
+    fprintf (file, "\tst 12,4(1)\n");
+
+  /* Update stack and set back pointer.  */
+  if (must_push)
+    {
+      if (total_size < 32767)
+       fprintf (file, "\tstu 1,%d(1)\n", - total_size);
+      else
+       {
+         fprintf (file, "\tcau 0,0,%d\n\toril 0,0,%d\n",
+                  (total_size >> 16) & 0xffff, total_size & 0xffff);
+         fprintf (file, "\tsf 12,0,1\n\tst 1,0(12)\n\toril 1,12,0\n");
+       }
+    }
+
+  /* Set frame pointer, if needed.  */
+  if (frame_pointer_needed)
+    fprintf (file, "\toril 31,1,0\n");
+}
+
+/* Write function epilogue.  */
+
+void
+output_epilog (file, size)
+     FILE *file;
+     int size;
+{
+  int first_reg = first_reg_to_save ();
+  int must_push = rs6000_pushes_stack ();
+  int first_fp_reg = first_fp_reg_to_save ();
+  int basic_size = rs6000_sa_size ();
+  int total_size = (basic_size + size + current_function_outgoing_args_size);
+  rtx insn = get_last_insn ();
+
+  /* Round size to multiple of 8 bytes.  */
+  total_size = (total_size + 7) & ~7;
+
+  /* If the last insn was a BARRIER, we don't have to write anything except
+     the trace table.  */
+  if (GET_CODE (insn) == NOTE)
+    insn = prev_nonnote_insn (insn);
+  if (insn == 0 ||  GET_CODE (insn) != BARRIER)
+    {
+      /* If we have a frame pointer, a call to alloca,  or a large stack
+        frame, restore the old stack pointer using the backchain.  Otherwise,
+        we know what size to update it with.  */
+      if (frame_pointer_needed || current_function_calls_alloca
+         || total_size > 32767)
+       fprintf (file, "\tl 1,0(1)\n");
+      else if (must_push)
+       fprintf (file, "\tai 1,1,%d\n", total_size);
+
+      /* Get the old lr if we saved it.  To speed things up, copy it into
+        lr here if we don't have to save more than 2 fp regs.  */
+      if (regs_ever_live[65])
+       {
+         fprintf (file, "\tl 0,8(1)\n");
+         if (first_fp_reg >= 62)
+           fprintf (file, "\tmtlr 0\n");
+       }
+
+      /* Get the old cr if we saved it.  */
+      if (must_save_cr ())
+       fprintf (file, "\tl 12,4(1)\n");
+
+      /* Restore gpr's.  */
+      if (first_reg == 31)
+       fprintf (file, "\tl 31,%d(1)\n", -4 - (64 - first_fp_reg) * 8);
+      else if (first_reg != 32)
+       fprintf (file, "\tlm %d,%d(1)\n", first_reg,
+                - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8);
+
+      /* Restore fpr's.  */
+      if (first_fp_reg == 62)
+       fprintf (file, "\tlfd 30,-16(1)\n\tlfd 31,-8(1)\n");
+      else if (first_fp_reg == 63)
+       fprintf (file, "\tlfd 31,-8(1)\n");
+      else if (first_fp_reg != 64)
+       fprintf (file, "\tbl ._restf%d\n\tcror 15,15,15\n", first_fp_reg - 32);
+
+      /* If we used the link register, get it from r0 if we haven't
+        already.  */
+      if (regs_ever_live[65] && first_fp_reg < 62)
+       fprintf (file, "\tmtlr 0\n");
+
+      /* If we saved cr, restore it here.  Just set cr2, cr3, and cr4.  */
+      if (must_save_cr ())
+       fprintf (file, "\tmtcrf 0x38,12\n");
+
+      fprintf (file, "\tbr\n");
+    }
+}
+\f
+/* Output a TOC entry.  We derive the entry name from what is
+   being written.  */
+
+void
+output_toc (file, x, labelno)
+     FILE *file;
+     rtx x;
+     int labelno;
+{
+  char buf[256];
+  char *name = buf;
+  rtx base = x;
+  int offset = 0;
+
+  ASM_OUTPUT_INTERNAL_LABEL (file, "LC", labelno);
+
+  /* Handle FP constants specially.  */
+  if (GET_CODE (x) == CONST_DOUBLE
+      && GET_MODE (x) == DFmode
+      && TARGET_FLOAT_FORMAT == HOST_FLOAT_FORMAT
+      && BITS_PER_WORD == HOST_BITS_PER_INT
+      && TARGET_FP_IN_TOC)
+    {
+      fprintf (file, "\t.tc FD_%x_%x[TC],%d,%d\n",
+              CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x),
+              CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x));
+      return;
+    }
+  else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode
+          && TARGET_FP_IN_TOC)
+    {
+      rtx val = operand_subword (x, 0, 0, SFmode);
+
+      if (val == 0 || GET_CODE (val) != CONST_INT)
+       abort ();
+
+      fprintf (file, "\t.tc FS_%x[TC],%d\n", INTVAL (val), INTVAL (val));
+      return;
+    }
+
+  if (GET_CODE (x) == CONST)
+    {
+      base = XEXP (XEXP (x, 0), 0);
+      offset = INTVAL (XEXP (XEXP (x, 0), 1));
+    }
+  
+  if (GET_CODE (base) == SYMBOL_REF)
+    name = XSTR (base, 0);
+  else if (GET_CODE (base) == LABEL_REF)
+    ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (base, 0)));
+  else if (GET_CODE (base) == CODE_LABEL)
+    ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (base));
+  else
+    abort ();
+
+  fprintf (file, "\t.tc ");
+  RS6000_OUTPUT_BASENAME (file, name);
+
+  if (offset < 0)
+    fprintf (file, "P.N.%d", - offset);
+  else if (offset)
+    fprintf (file, ".P.%d", offset);
+
+  fprintf (file, "[TC],");
+  output_addr_const (file, x);
+  fprintf (file, "\n");
+}
+\f
+/* Output an assembler pseudo-op to write an ASCII string of N characters
+   starting at P to FILE.
+
+   On the RS/6000, we have to do this using the .byte operation and
+   write out special characters outside the quoted string.
+   Also, the assembler is broken; very long strings are truncated,
+   so we must artificially break them up early. */
+
+void
+output_ascii (file, p, n)
+     FILE *file;
+     char *p;
+     int n;
+{
+  char c;
+  int i, count_string;
+  char *for_string = "\t.byte \"";
+  char *for_decimal = "\t.byte ";
+  char *to_close = NULL;
+
+  count_string = 0;
+  for (i = 0; i < n; i++)
+    {
+      c = *p++;
+      if (c >= ' ' && c < 0177)
+       {
+         if (for_string)
+           fputs (for_string, file);
+         putc (c, file);
+
+         /* Write two quotes to get one.  */
+         if (c == '"')
+           {
+             putc (c, file);
+             ++count_string;
+           }
+
+         for_string = NULL;
+         for_decimal = "\"\n\t.byte ";
+         to_close = "\"\n";
+         ++count_string;
+
+         if (count_string >= 512)
+           {
+             fputs (to_close, file);
+
+             for_string = "\t.byte \"";
+             for_decimal = "\t.byte ";
+             to_close = NULL;
+             count_string = 0;
+           }
+       }
+      else
+       {
+         if (for_decimal)
+           fputs (for_decimal, file);
+         fprintf (file, "%d", c);
+
+         for_string = "\n\t.byte \"";
+         for_decimal = ", ";
+         to_close = "\n";
+         count_string = 0;
+       }
+    }
+
+  /* Now close the string if we have written one.  Then end the line.  */
+  if (to_close)
+    fprintf (file, to_close);
+}
+\f
+/* Generate a unique section name for FILENAME for a section type
+   represented by SECTION_DESC.  Output goes into BUF.
+
+   SECTION_DESC can be any string, as long as it is different for each
+   possible section type.
+
+   We name the section in the same manner as xlc.  The name begins with an
+   underscore followed by the filename (after stripping any leading directory
+   names) with the period replaced by the string SECTION_DESC.  If FILENAME
+   does not contain a period, SECTION_DESC is appended at the end of the
+   name.  */
+
+void
+rs6000_gen_section_name (buf, filename, section_desc)
+     char **buf;
+     char *filename;
+     char *section_desc;
+{
+  char *q, *after_last_slash;
+  char *p;
+  int len;
+  int used_desc = 0;
+
+  after_last_slash = filename;
+  for (q = filename; *q; q++)
+    if (*q == '/')
+      after_last_slash = q + 1;
+
+  len = strlen (filename) + strlen (section_desc) + 2;
+  *buf = (char *) permalloc (len);
+
+  p = *buf;
+  *p++ = '_';
+
+  for (q = after_last_slash; *q; q++)
+    {
+      if (*q == '.')
+        {
+         strcpy (p, section_desc);
+         p += strlen (section_desc);
+         used_desc = 1;
+        }
+
+      else if (isalnum (*q))
+        *p++ = *q;
+    }
+
+  if (! used_desc)
+    strcpy (p, section_desc);
+  else
+    *p = '\0';
+}