summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/config/rs6000/rs6000.c1509
1 files changed, 1509 insertions, 0 deletions
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
new file mode 100644
index 00000000000..47e9342a63a
--- /dev/null
+++ b/gcc/config/rs6000/rs6000.c
@@ -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;
+
+/* 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);
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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 ();
+ }
+}
+
+/* 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");
+ }
+}
+
+/* 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 ();
+}
+
+/* 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");
+ }
+}
+
+/* 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");
+}
+
+/* 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);
+}
+
+/* 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';
+}