/* Subroutines used for code generation on the DEC Alpha.
- Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+ Copyright (C) 1992, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
This file is part of GNU CC.
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. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
#include <stdio.h>
#include "obstack.h"
#include "tree.h"
+/* Specify which cpu to schedule for. */
+ enum processor_type alpha_cpu;
+
+/* Specify how accurate floating-point traps need to be. */
+
+enum alpha_trap_precision alpha_tp;
+
+/* Specify the floating-point rounding mode. */
+
+enum alpha_fp_rounding_mode alpha_fprm;
+
+/* Specify which things cause traps. */
+
+enum alpha_fp_trap_mode alpha_fptm;
+
+/* Strings decoded into the above options. */
+char *alpha_cpu_string; /* -mcpu=ev[4|5] */
+char *alpha_tp_string; /* -mtrap-precision=[p|s|i] */
+char *alpha_fprm_string; /* -mfp-rounding-mode=[n|m|c|d] */
+char *alpha_fptm_string; /* -mfp-trap-mode=[n|u|su|sui] */
+
/* Save information from a "cmpxx" operation until the branch or scc is
emitted. */
static int inside_function = FALSE;
+/* Non-zero if an instruction that may cause a trap is pending. */
+
+static int trap_pending = 0;
+
/* Nonzero if the current function needs gp. */
int alpha_function_needs_gp;
/* Declarations of static functions. */
static void alpha_set_memflags_1 PROTO((rtx, int, int, int));
+static rtx alpha_emit_set_const_1 PROTO((rtx, enum machine_mode,
+ HOST_WIDE_INT, int));
static void add_long_const PROTO((FILE *, HOST_WIDE_INT, int, int, int));
+
+/* Compute the size of the save area in the stack. */
+static void alpha_sa_mask PROTO((unsigned long *imaskP,
+ unsigned long *fmaskP));
+/* Strip type information. */
+#define CURRENT_FUNCTION_ARGS_INFO \
+(TARGET_OPEN_VMS ? current_function_args_info & 0xff \
+ : current_function_args_info)
+\f
+/* Parse target option strings. */
+
+void
+override_options ()
+{
+ alpha_cpu = PROCESSOR_EV4;
+
+ if (alpha_cpu_string)
+ {
+ if (! strcmp (alpha_cpu_string, "ev4")
+ || ! strcmp (alpha_cpu_string, "21064"))
+ alpha_cpu = PROCESSOR_EV4;
+ else if (! strcmp (alpha_cpu_string, "ev5")
+ || ! strcmp (alpha_cpu_string, "21164"))
+ alpha_cpu = PROCESSOR_EV5;
+ else
+ error ("bad value `%s' for -mcpu switch", alpha_cpu_string);
+ }
+
+ alpha_tp = ALPHA_TP_PROG;
+ alpha_fprm = ALPHA_FPRM_NORM;
+ alpha_fptm = ALPHA_FPTM_N;
+
+ if (TARGET_IEEE)
+ {
+ alpha_tp = ALPHA_TP_INSN;
+ alpha_fptm = ALPHA_FPTM_SU;
+ }
+
+ if (TARGET_IEEE_WITH_INEXACT)
+ {
+ alpha_tp = ALPHA_TP_INSN;
+ alpha_fptm = ALPHA_FPTM_SUI;
+ }
+
+ if (alpha_tp_string)
+ {
+ if (! strcmp (alpha_tp_string, "p"))
+ alpha_tp = ALPHA_TP_PROG;
+ else if (! strcmp (alpha_tp_string, "f"))
+ alpha_tp = ALPHA_TP_FUNC;
+ else if (! strcmp (alpha_tp_string, "i"))
+ alpha_tp = ALPHA_TP_INSN;
+ else
+ error ("bad value `%s' for -mtrap-precision switch", alpha_tp_string);
+ }
+
+ if (alpha_fprm_string)
+ {
+ if (! strcmp (alpha_fprm_string, "n"))
+ alpha_fprm = ALPHA_FPRM_NORM;
+ else if (! strcmp (alpha_fprm_string, "m"))
+ alpha_fprm = ALPHA_FPRM_MINF;
+ else if (! strcmp (alpha_fprm_string, "c"))
+ alpha_fprm = ALPHA_FPRM_CHOP;
+ else if (! strcmp (alpha_fprm_string,"d"))
+ alpha_fprm = ALPHA_FPRM_DYN;
+ else
+ error ("bad value `%s' for -mfp-rounding-mode switch",
+ alpha_fprm_string);
+ }
+
+ if (alpha_fptm_string)
+ {
+ if (strcmp (alpha_fptm_string, "n") == 0)
+ alpha_fptm = ALPHA_FPTM_N;
+ else if (strcmp (alpha_fptm_string, "u") == 0)
+ alpha_fptm = ALPHA_FPTM_U;
+ else if (strcmp (alpha_fptm_string, "su") == 0)
+ alpha_fptm = ALPHA_FPTM_SU;
+ else if (strcmp (alpha_fptm_string, "sui") == 0)
+ alpha_fptm = ALPHA_FPTM_SUI;
+ else
+ error ("bad value `%s' for -mfp-trap-mode switch", alpha_fptm_string);
+ }
+
+ /* Do some sanity checks on the above option. */
+
+ if ((alpha_fptm == ALPHA_FPTM_SU || alpha_fptm == ALPHA_FPTM_SUI)
+ && alpha_tp != ALPHA_TP_INSN)
+ {
+ warning ("fp software completion requires -mtrap-precision=i");
+ alpha_tp = ALPHA_TP_INSN;
+ }
+
+ if (TARGET_FLOAT_VAX)
+ {
+ if (alpha_fprm == ALPHA_FPRM_MINF || alpha_fprm == ALPHA_FPRM_DYN)
+ {
+ warning ("rounding mode not supported for VAX floats");
+ alpha_fprm = ALPHA_FPRM_NORM;
+ }
+ if (alpha_fptm == ALPHA_FPTM_SUI)
+ {
+ warning ("trap mode not supported for VAX floats");
+ alpha_fptm = ALPHA_FPTM_SU;
+ }
+ }
+}
\f
/* Returns 1 if VALUE is a mask that contains full bytes of zero or ones. */
return 1;
/* ... fall through ... */
case MEM:
- return mode != HImode && mode != QImode && general_operand (op, mode);
+ return (TARGET_BYTE_OPS || (mode != HImode && mode != QImode)
+ && general_operand (op, mode));
case CONST_DOUBLE:
return GET_MODE_CLASS (mode) == MODE_FLOAT && op == CONST0_RTX (mode);
return 0;
return (GET_CODE (op) == SYMBOL_REF
- || (GET_CODE (op) == REG
- && (REGNO (op) == 27 || WINDOWS_NT)));
+ || (GET_CODE (op) == REG && (TARGET_OPEN_VMS || REGNO (op) == 27)));
}
/* Return 1 if OP is a valid Alpha comparison operator. Here we know which
|| (mode == DImode && (code == LEU || code == LTU)));
}
+/* Return 1 if OP is a valid Alpha swapped comparison operator. */
+
+int
+alpha_swapped_comparison_operator (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ enum rtx_code code = GET_CODE (op);
+
+ if (mode != GET_MODE (op) || GET_RTX_CLASS (code) != '<')
+ return 0;
+
+ code = swap_condition (code);
+ return (code == EQ || code == LE || code == LT
+ || (mode == DImode && (code == LEU || code == LTU)));
+}
+
/* Return 1 if OP is a signed comparison operation. */
int
op = XEXP (op, 0);
return (GET_CODE (op) == REG
- && (REGNO (op) == STACK_POINTER_REGNUM
- || op == hard_frame_pointer_rtx
- || (REGNO (op) >= FIRST_VIRTUAL_REGISTER
- && REGNO (op) <= LAST_VIRTUAL_REGISTER)));
+ && REGNO_POINTER_ALIGN (REGNO (op)) >= 4);
}
/* Similar, but return 1 if OP is a MEM which is not alignable. */
op = XEXP (op, 0);
return (GET_CODE (op) != REG
- || (REGNO (op) != STACK_POINTER_REGNUM
- && op != hard_frame_pointer_rtx
- && (REGNO (op) < FIRST_VIRTUAL_REGISTER
- || REGNO (op) > LAST_VIRTUAL_REGISTER)));
+ || REGNO_POINTER_ALIGN (REGNO (op)) < 4);
+}
+
+/* Return 1 if OP is either a register or an unaligned memory location. */
+
+int
+reg_or_unaligned_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return register_operand (op, mode) || unaligned_memory_operand (op, mode);
}
/* Return 1 if OP is any memory location. During reload a pseudo matches. */
*pbitnum = GEN_INT ((offset & 3) * 8);
}
-/* Similar, but just get the address. Handle the two reload cases. */
+/* Similar, but just get the address. Handle the two reload cases.
+ Add EXTRA_OFFSET to the address we return. */
rtx
-get_unaligned_address (ref)
+get_unaligned_address (ref, extra_offset)
rtx ref;
+ int extra_offset;
{
rtx base;
HOST_WIDE_INT offset = 0;
if (GET_CODE (base) == PLUS)
offset += INTVAL (XEXP (base, 1)), base = XEXP (base, 0);
- return plus_constant (base, offset);
+ return plus_constant (base, offset + extra_offset);
}
\f
/* Subfunction of the following function. Update the flags of any MEM
enum machine_mode mode;
HOST_WIDE_INT c;
int n;
+{
+ rtx pat;
+ int i;
+
+ /* Try 1 insn, then 2, then up to N. */
+ for (i = 1; i <= n; i++)
+ if ((pat = alpha_emit_set_const_1 (target, mode, c, i)) != 0)
+ return pat;
+
+ return 0;
+}
+
+/* Internal routine for the above to check for N or below insns. */
+
+static rtx
+alpha_emit_set_const_1 (target, mode, c, n)
+ rtx target;
+ enum machine_mode mode;
+ HOST_WIDE_INT c;
+ int n;
{
HOST_WIDE_INT new = c;
int i, bits;
if ((HOST_BITS_PER_WIDE_INT != 64
|| c >> 31 == -1 || c >> 31 == 0)
- && c != 0x80000000u)
+ && c != 0x80000000U)
{
HOST_WIDE_INT low = (c & 0xffff) - 2 * (c & 0x8000);
HOST_WIDE_INT tmp1 = c - low;
if ((new & ((HOST_WIDE_INT) 0xff << i)) == 0)
new |= (HOST_WIDE_INT) 0xff << i;
- if ((temp = alpha_emit_set_const (subtarget, mode, new, n - 1)) != 0)
+ /* We are only called for SImode and DImode. If this is SImode, ensure that
+ we are sign extended to a full word. */
+
+ if (mode == SImode)
+ new = (new & 0xffffffff) - 2 * (new & 0x80000000);
+
+ if (new != c
+ && (temp = alpha_emit_set_const (subtarget, mode, new, n - 1)) != 0)
return expand_binop (mode, and_optab, temp, GEN_INT (c | ~ new),
target, 0, OPTAB_WIDEN);
#endif
target, 0, OPTAB_WIDEN);
/* Now try high-order zero bits. Here we try the shifted-in bits as
- all zero and all ones. */
+ all zero and all ones. Be careful to avoid shifting outside the
+ mode and to avoid shifting outside the host wide int size. */
- if ((bits = HOST_BITS_PER_WIDE_INT - floor_log2 (c) - 1) > 0)
+ if ((bits = (MIN (HOST_BITS_PER_WIDE_INT, GET_MODE_SIZE (mode) * 8)
+ - floor_log2 (c) - 1)) > 0)
for (; bits > 0; bits--)
if ((temp = alpha_emit_set_const (subtarget, mode,
c << bits, i)) != 0
i)))
!= 0))
return expand_binop (mode, lshr_optab, temp, GEN_INT (bits),
- target, 0, OPTAB_WIDEN);
+ target, 1, OPTAB_WIDEN);
/* Now try high-order 1 bits. We get that with a sign-extension.
- But one bit isn't enough here. */
+ But one bit isn't enough here. Be careful to avoid shifting outside
+ the mode and to avoid shifting outside the host wide int size. */
- if ((bits = HOST_BITS_PER_WIDE_INT - floor_log2 (~ c) - 2) > 0)
+ if ((bits = (MIN (HOST_BITS_PER_WIDE_INT, GET_MODE_SIZE (mode) * 8)
+ - floor_log2 (~ c) - 2)) > 0)
for (; bits > 0; bits--)
if ((temp = alpha_emit_set_const (subtarget, mode,
c << bits, i)) != 0
return 0;
}
+
+#if HOST_BITS_PER_WIDE_INT == 64
+/* Having failed to find a 3 insn sequence in alpha_emit_set_const,
+ fall back to a straight forward decomposition. We do this to avoid
+ exponential run times encountered when looking for longer sequences
+ with alpha_emit_set_const. */
+
+rtx
+alpha_emit_set_long_const (target, c)
+ rtx target;
+ HOST_WIDE_INT c;
+{
+ /* Use a pseudo if highly optimizing and still generating RTL. */
+ rtx subtarget
+ = (flag_expensive_optimizations && rtx_equal_function_value_matters
+ ? 0 : target);
+ HOST_WIDE_INT d1, d2, d3, d4;
+ rtx r1, r2;
+
+ /* Decompose the entire word */
+ d1 = ((c & 0xffff) ^ 0x8000) - 0x8000;
+ c -= d1;
+ d2 = ((c & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ c = (c - d2) >> 32;
+ d3 = ((c & 0xffff) ^ 0x8000) - 0x8000;
+ c -= d3;
+ d4 = ((c & 0xffffffff) ^ 0x80000000) - 0x80000000;
+
+ if (c - d4 != 0)
+ abort();
+
+ /* Construct the high word */
+ if (d3 == 0)
+ r1 = copy_to_suggested_reg (GEN_INT (d4), subtarget, DImode);
+ else if (d4 == 0)
+ r1 = copy_to_suggested_reg (GEN_INT (d3), subtarget, DImode);
+ else
+ r1 = expand_binop (DImode, add_optab, GEN_INT (d3), GEN_INT (d4),
+ subtarget, 0, OPTAB_WIDEN);
+
+ /* Shift it into place */
+ r2 = expand_binop (DImode, ashl_optab, r1, GEN_INT (32),
+ subtarget, 0, OPTAB_WIDEN);
+
+ if (subtarget == 0 && d1 == d3 && d2 == d4)
+ r1 = expand_binop (DImode, add_optab, r1, r2, subtarget, 0, OPTAB_WIDEN);
+ else
+ {
+ r1 = r2;
+
+ /* Add in the low word */
+ if (d2 != 0)
+ r1 = expand_binop (DImode, add_optab, r1, GEN_INT (d2),
+ subtarget, 0, OPTAB_WIDEN);
+ if (d1 != 0)
+ r1 = expand_binop (DImode, add_optab, r1, GEN_INT (d1),
+ subtarget, 0, OPTAB_WIDEN);
+ }
+
+ if (subtarget == 0)
+ r1 = copy_to_suggested_reg(r1, target, DImode);
+
+ return r1;
+}
+#endif /* HOST_BITS_PER_WIDE_INT == 64 */
+
+/* Rewrite a comparison against zero CMP of the form
+ (CODE (cc0) (const_int 0)) so it can be written validly in
+ a conditional move (if_then_else CMP ...).
+ If both of the operands that set cc0 are non-zero we must emit
+ an insn to perform the compare (it can't be done within
+ the conditional move). */
+rtx
+alpha_emit_conditional_move (cmp, mode)
+ rtx cmp;
+ enum machine_mode mode;
+{
+ enum rtx_code code = GET_CODE (cmp);
+ enum rtx_code cmov_code = NE;
+ rtx op0 = alpha_compare_op0;
+ rtx op1 = alpha_compare_op1;
+ enum machine_mode cmp_mode
+ = (GET_MODE (op0) == VOIDmode ? DImode : GET_MODE (op0));
+ enum machine_mode cmp_op_mode = alpha_compare_fp_p ? DFmode : DImode;
+ rtx tem;
+
+ if (alpha_compare_fp_p != FLOAT_MODE_P (mode))
+ return 0;
+
+ /* We may be able to use a conditional move directly.
+ This avoids emitting spurious compares. */
+ if (signed_comparison_operator (cmp, cmp_op_mode)
+ && (op0 == CONST0_RTX (cmp_mode) || op1 == CONST0_RTX (cmp_mode)))
+ return gen_rtx (code, VOIDmode, op0, op1);
+
+ /* We can't put the comparison insides a conditional move;
+ emit a compare instruction and put that inside the
+ conditional move. Make sure we emit only comparisons we have;
+ swap or reverse as necessary. */
+
+ switch (code)
+ {
+ case EQ: case LE: case LT: case LEU: case LTU:
+ /* We have these compares: */
+ break;
+
+ case NE:
+ /* This must be reversed. */
+ code = reverse_condition (code);
+ cmov_code = EQ;
+ break;
+
+ case GE: case GT: case GEU: case GTU:
+ /* These must be swapped. Make sure the new first operand is in
+ a register. */
+ code = swap_condition (code);
+ tem = op0, op0 = op1, op1 = tem;
+ op0 = force_reg (cmp_mode, op0);
+ break;
+
+ default:
+ abort ();
+ }
+
+ tem = gen_reg_rtx (cmp_op_mode);
+ emit_move_insn (tem, gen_rtx (code, cmp_op_mode, op0, op1));
+ return gen_rtx (cmov_code, VOIDmode, tem, CONST0_RTX (cmp_op_mode));
+}
\f
/* Adjust the cost of a scheduling dependency. Return the new cost of
a dependency LINK or INSN on DEP_INSN. COST is the current cost. */
if (REG_NOTE_KIND (link) != 0)
return 0;
+ /* EV5 costs are as given in alpha.md; exceptions are given here. */
+ if (alpha_cpu == PROCESSOR_EV5)
+ {
+ /* And the lord DEC sayeth: "A special bypass provides an effective
+ latency of 0 cycles for an ICMP or ILOG insn producing the test
+ operand of an IBR or CMOV insn." */
+ if (recog_memoized (dep_insn) >= 0
+ && (get_attr_type (dep_insn) == TYPE_ICMP
+ || get_attr_type (dep_insn) == TYPE_ILOG)
+ && recog_memoized (insn) >= 0
+ && (get_attr_type (insn) == TYPE_IBR
+ || (get_attr_type (insn) == TYPE_CMOV
+ && !((set = single_set (dep_insn)) != 0
+ && GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE
+ && (rtx_equal_p (SET_DEST (set),
+ XEXP (SET_SRC (PATTERN (insn)), 1))
+ || rtx_equal_p (SET_DEST (set),
+ XEXP (SET_SRC (PATTERN (insn)), 2)))))))
+ return 1;
+ return cost;
+ }
+
/* If INSN is a store insn and DEP_INSN is setting the data being stored,
we can sometimes lower the cost. */
for the address in loads and stores. */
if (recog_memoized (dep_insn) >= 0
- && get_attr_type (dep_insn) == TYPE_IADDLOG)
+ && (get_attr_type (dep_insn) == TYPE_IADD
+ || get_attr_type (dep_insn) == TYPE_ILOG))
switch (get_attr_type (insn))
{
case TYPE_LD:
switch (code)
{
+ case '&':
+ /* Generates fp-rounding mode suffix: nothing for normal, 'c' for
+ chopped, 'm' for minus-infinity, and 'd' for dynamic rounding
+ mode. alpha_fprm controls which suffix is generated. */
+ switch (alpha_fprm)
+ {
+ case ALPHA_FPRM_NORM:
+ break;
+ case ALPHA_FPRM_MINF:
+ fputc ('m', file);
+ break;
+ case ALPHA_FPRM_CHOP:
+ fputc ('c', file);
+ break;
+ case ALPHA_FPRM_DYN:
+ fputc ('d', file);
+ break;
+ }
+ break;
+
+ case '\'':
+ /* Generates trap-mode suffix for instructions that accept the su
+ suffix only (cmpt et al). */
+ if (alpha_tp == ALPHA_TP_INSN)
+ fputs ("su", file);
+ break;
+
+ case ')':
+ /* Generates trap-mode suffix for instructions that accept the u, su,
+ and sui suffix. This is the bulk of the IEEE floating point
+ instructions (addt et al). */
+ switch (alpha_fptm)
+ {
+ case ALPHA_FPTM_N:
+ break;
+ case ALPHA_FPTM_U:
+ fputc ('u', file);
+ break;
+ case ALPHA_FPTM_SU:
+ fputs ("su", file);
+ break;
+ case ALPHA_FPTM_SUI:
+ fputs ("sui", file);
+ break;
+ }
+ break;
+
+ case '+':
+ /* Generates trap-mode suffix for instructions that accept the sui
+ suffix (cvtqt and cvtqs). */
+ switch (alpha_fptm)
+ {
+ case ALPHA_FPTM_N: case ALPHA_FPTM_U:
+ case ALPHA_FPTM_SU: /* cvtqt/cvtqs can't cause underflow */
+ break;
+ case ALPHA_FPTM_SUI:
+ fputs ("sui", file);
+ break;
+ }
+ break;
+
+ case ',':
+ /* Generates single precision instruction suffix. */
+ fprintf (file, "%c", (TARGET_FLOAT_VAX?'f':'s'));
+ break;
+
+ case '-':
+ /* Generates double precision instruction suffix. */
+ fprintf (file, "%c", (TARGET_FLOAT_VAX?'g':'t'));
+ break;
+
case 'r':
/* If this operand is the constant zero, write it as "$31". */
if (GET_CODE (x) == REG)
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%N value");
- fprintf (file, "%ld", ~ INTVAL (x));
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~ INTVAL (x));
break;
case 'P':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%P value");
- fprintf (file, "%ld", (HOST_WIDE_INT) 1 << INTVAL (x));
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, (HOST_WIDE_INT) 1 << INTVAL (x));
break;
case 'h':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%h value");
- fprintf (file, "%ld", INTVAL (x) >> 16);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) >> 16);
break;
case 'L':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%L value");
- fprintf (file, "%ld", (INTVAL (x) & 0xffff) - 2 * (INTVAL (x) & 0x8000));
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+ (INTVAL (x) & 0xffff) - 2 * (INTVAL (x) & 0x8000));
break;
case 'm':
if (value & 0xff)
mask |= (1 << (i + sizeof (int)));
- fprintf (file, "%ld", mask & 0xff);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, mask & 0xff);
}
else if (GET_CODE (x) == CONST_INT)
if (value & 0xff)
mask |= (1 << i);
- fprintf (file, "%ld", mask);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, mask);
}
else
output_operand_lossage ("invalid %%m value");
&& (INTVAL (x) & 7) != 8)
output_operand_lossage ("invalid %%s value");
- fprintf (file, "%ld", INTVAL (x) / 8);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) / 8);
break;
case 'S':
&& (INTVAL (x) & 7) != 8)
output_operand_lossage ("invalid %%s value");
- fprintf (file, "%ld", (64 - INTVAL (x)) / 8);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, (64 - INTVAL (x)) / 8);
break;
- case 'C':
+ case 'C': case 'D': case 'c': case 'd':
/* Write out comparison name. */
- if (GET_RTX_CLASS (GET_CODE (x)) != '<')
- output_operand_lossage ("invalid %%C value");
-
- if (GET_CODE (x) == LEU)
- fprintf (file, "ule");
- else if (GET_CODE (x) == LTU)
- fprintf (file, "ult");
- else
- fprintf (file, "%s", GET_RTX_NAME (GET_CODE (x)));
- break;
-
- case 'D':
- /* Similar, but write reversed code. We can't get an unsigned code
- here. */
- if (GET_RTX_CLASS (GET_CODE (x)) != '<')
- output_operand_lossage ("invalid %%D value");
-
- fprintf (file, "%s", GET_RTX_NAME (reverse_condition (GET_CODE (x))));
- break;
-
- case 'c':
- /* Similar to `c', but swap. We can't get unsigned here either. */
- if (GET_RTX_CLASS (GET_CODE (x)) != '<')
- output_operand_lossage ("invalid %%D value");
-
- fprintf (file, "%s", GET_RTX_NAME (swap_condition (GET_CODE (x))));
- break;
-
- case 'd':
- /* Similar, but reverse and swap. We can't get unsigned here either. */
- if (GET_RTX_CLASS (GET_CODE (x)) != '<')
- output_operand_lossage ("invalid %%D value");
-
- fprintf (file, "%s",
- GET_RTX_NAME (swap_condition (reverse_condition ((GET_CODE (x))))));
+ {
+ enum rtx_code c = GET_CODE (x);
+
+ if (GET_RTX_CLASS (c) != '<')
+ output_operand_lossage ("invalid %%C value");
+
+ if (code == 'D')
+ c = reverse_condition (c);
+ else if (code == 'c')
+ c = swap_condition (c);
+ else if (code == 'd')
+ c = swap_condition (reverse_condition (c));
+
+ if (c == LEU)
+ fprintf (file, "ule");
+ else if (c == LTU)
+ fprintf (file, "ult");
+ else
+ fprintf (file, "%s", GET_RTX_NAME (c));
+ }
break;
case 'E':
both registers and memory. Both of these are already included in
current_function_args_info. */
- argsize = GEN_INT (current_function_args_info * UNITS_PER_WORD);
+ argsize = GEN_INT (CURRENT_FUNCTION_ARGS_INFO * UNITS_PER_WORD);
- /* SETUP_INCOMING_VARARGS moves the starting address base up by 48,
+ /* For Unix, SETUP_INCOMING_VARARGS moves the starting address base up by 48,
storing fp arg registers in the first 48 bytes, and the integer arg
registers in the next 48 bytes. This is only done, however, if any
integer registers need to be stored.
order to account for the integer arg registers which are counted in
argsize above, but which are not actually stored on the stack. */
- addr = (current_function_args_info <= 5 + stdarg
- ? plus_constant (virtual_incoming_args_rtx, 6 * UNITS_PER_WORD)
- : plus_constant (virtual_incoming_args_rtx, - (6 * UNITS_PER_WORD)));
-
- /* Allocate the va_list constructor */
- block = assign_stack_local (BLKmode, 2 * UNITS_PER_WORD, BITS_PER_WORD);
- RTX_UNCHANGING_P (block) = 1;
- RTX_UNCHANGING_P (XEXP (block, 0)) = 1;
-
- /* Store the address of the first integer register in the
- __va_base member. */
- emit_move_insn (change_address (block, Pmode, XEXP (block, 0)),
- force_operand (addr, NULL_RTX));
-
- /* Store the argsize as the __va_offset member. */
- emit_move_insn (change_address (block, Pmode,
- plus_constant (XEXP (block, 0),
- UNITS_PER_WORD)),
- force_operand (argsize, NULL_RTX));
-
- /* Return the address of the va_list constructor, but don't put it in a
- register. Doing so would fail when not optimizing and produce worse
- code when optimizing. */
- return XEXP (block, 0);
+ if (TARGET_OPEN_VMS)
+ addr = plus_constant (virtual_incoming_args_rtx,
+ CURRENT_FUNCTION_ARGS_INFO <= 5 + stdarg
+ ? UNITS_PER_WORD : - 6 * UNITS_PER_WORD);
+ else
+ addr = (CURRENT_FUNCTION_ARGS_INFO <= 5 + stdarg
+ ? plus_constant (virtual_incoming_args_rtx,
+ 6 * UNITS_PER_WORD)
+ : plus_constant (virtual_incoming_args_rtx,
+ - (6 * UNITS_PER_WORD)));
+
+ /* For VMS, we include the argsize, while on Unix, it's handled as
+ a separate field. */
+ if (TARGET_OPEN_VMS)
+ addr = plus_constant (addr, INTVAL (argsize));
+
+ addr = force_operand (addr, NULL_RTX);
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+ addr = convert_memory_address (ptr_mode, addr);
+#endif
+
+ if (TARGET_OPEN_VMS)
+ return addr;
+ else
+ {
+ /* Allocate the va_list constructor */
+ block = assign_stack_local (BLKmode, 2 * UNITS_PER_WORD, BITS_PER_WORD);
+ RTX_UNCHANGING_P (block) = 1;
+ RTX_UNCHANGING_P (XEXP (block, 0)) = 1;
+
+ /* Store the address of the first integer register in the __base
+ member. */
+
+ emit_move_insn (change_address (block, ptr_mode, XEXP (block, 0)), addr);
+
+ /* Store the argsize as the __va_offset member. */
+ emit_move_insn
+ (change_address (block, TYPE_MODE (integer_type_node),
+ plus_constant (XEXP (block, 0),
+ POINTER_SIZE/BITS_PER_UNIT)),
+ argsize);
+
+ /* Return the address of the va_list constructor, but don't put it in a
+ register. Doing so would fail when not optimizing and produce worse
+ code when optimizing. */
+ return XEXP (block, 0);
+ }
}
\f
/* This page contains routines that are used to determine what the function
/* Compute the size of the save area in the stack. */
+#if OPEN_VMS
+
+#define REG_PV 27
+#define REG_RA 26
+
+/* These variables are used for communication between the following functions.
+ They indicate various things about the current function being compiled
+ that are used to tell what kind of prologue, epilogue and procedure
+ descriptior to generate. */
+
+/* Nonzero if we need a stack procedure. */
+static int is_stack_procedure;
+
+/* Register number (either FP or SP) that is used to unwind the frame. */
+static int unwind_regno;
+
+/* Register number used to save FP. We need not have one for RA since
+ we don't modify it for register procedures. This is only defined
+ for register frame procedures. */
+static int save_fp_regno;
+
+/* Register number used to reference objects off our PV. */
+static int base_regno;
+
+/* Compute register masks for saved registers. */
+
+static void
+alpha_sa_mask (imaskP, fmaskP)
+ unsigned long *imaskP;
+ unsigned long *fmaskP;
+{
+ unsigned long imask = 0;
+ unsigned long fmask = 0;
+ int i;
+
+ if (is_stack_procedure)
+ imask |= (1L << HARD_FRAME_POINTER_REGNUM);
+
+ /* One for every register we have to save. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (! fixed_regs[i] && ! call_used_regs[i] && regs_ever_live[i])
+ {
+ if (i < 32)
+ imask |= (1L << i);
+ else
+ fmask |= (1L << (i - 32));
+ }
+
+ *imaskP = imask;
+ *fmaskP = fmask;
+
+ return;
+}
+
+int
+alpha_sa_size ()
+{
+ int sa_size = 0;
+ HOST_WIDE_INT stack_needed;
+ int i;
+
+ /* One for every register we have to save. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (! fixed_regs[i] && ! call_used_regs[i] && regs_ever_live[i])
+ sa_size++;
+
+ /* Start by assuming we can use a register procedure if we don't make any
+ calls (REG_RA not used) or need to save any registers and a stack
+ procedure if we do. */
+ is_stack_procedure = regs_ever_live[REG_RA] || sa_size != 0;
+
+ /* Decide whether to refer to objects off our PV via FP or PV.
+ If we need need FP for something else or if we receive a nonlocal
+ goto (which expects PV to contain the value), we must use PV.
+ Otherwise, start by assuming we can use FP. */
+ base_regno = (frame_pointer_needed || current_function_has_nonlocal_label
+ || is_stack_procedure
+ || current_function_outgoing_args_size
+ ? REG_PV : HARD_FRAME_POINTER_REGNUM);
+
+ /* If we want to copy PV into FP, we need to find some register in which to
+ save FP. */
+
+ save_fp_regno = -1;
+
+ if (base_regno == HARD_FRAME_POINTER_REGNUM)
+ for (i = 0; i < 32; i++)
+ if (! fixed_regs[i] && call_used_regs[i] && ! regs_ever_live[i])
+ save_fp_regno = i;
+
+ if (save_fp_regno == -1)
+ base_regno = REG_PV, is_stack_procedure = 1;
+
+ /* Stack unwinding should be done via FP unless we use it for PV. */
+ unwind_regno
+ = base_regno == REG_PV ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM;
+
+ /* If this is a stack procedure, allow space for saving FP and RA. */
+ if (is_stack_procedure)
+ sa_size += 2;
+
+ return sa_size * 8;
+}
+
+int
+alpha_pv_save_size ()
+{
+ alpha_sa_size ();
+ return is_stack_procedure ? 8 : 0;
+}
+
+int
+alpha_using_fp ()
+{
+ alpha_sa_size ();
+ return unwind_regno == HARD_FRAME_POINTER_REGNUM;
+}
+
+#else /* ! OPEN_VMS */
+
int
alpha_sa_size ()
{
return size * 8;
}
+#endif /* ! OPEN_VMS */
+
/* Return 1 if this function can directly return via $26. */
int
direct_return ()
{
- return (reload_completed && alpha_sa_size () == 0
+ return (! TARGET_OPEN_VMS && reload_completed && alpha_sa_size () == 0
&& get_frame_size () == 0
&& current_function_outgoing_args_size == 0
&& current_function_pretend_args_size == 0);
/* Write a version stamp. Don't write anything if we are running as a
cross-compiler. Otherwise, use the versions in /usr/include/stamp.h. */
-#if !defined(CROSS_COMPILE) && !defined(_WIN32)
+#if !defined(CROSS_COMPILE) && !defined(_WIN32) && !defined(__linux__) && !defined(VMS)
#include <stamp.h>
#endif
FILE *file;
{
#ifdef MS_STAMP
- char *p;
-
- fprintf (file, "\t.verstamp %d %d ", MS_STAMP, LS_STAMP);
- for (p = version_string; *p != ' ' && *p != 0; p++)
- fprintf (file, "%c", *p == '.' ? ' ' : *p);
- fprintf (file, "\n");
+ fprintf (file, "\t.verstamp %d %d\n", MS_STAMP, LS_STAMP);
#endif
}
\f
/* Write function prologue. */
-void
-output_prolog (file, size)
- FILE *file;
- int size;
+#if OPEN_VMS
+
+/*
+ Quick and dirty vmskrunch routine to ensure symbols are within the
+ 64 bytes limit imposed by VMS.
+
+ This is written specifically for GNAT, and may not work for C++.
+
+ This routine duplicates every symbol passed to it whether it is too
+ long or not, which is a waste of space, fix later.
+*/
+#include <string.h>
+char*
+vmskrunch (name)
+ char *name;
{
- HOST_WIDE_INT out_args_size
- = ALPHA_ROUND (current_function_outgoing_args_size);
- HOST_WIDE_INT sa_size = alpha_sa_size ();
- HOST_WIDE_INT frame_size
- = (out_args_size + sa_size
- + ALPHA_ROUND (size + current_function_pretend_args_size));
- HOST_WIDE_INT reg_offset = out_args_size;
- HOST_WIDE_INT start_reg_offset = reg_offset;
- HOST_WIDE_INT actual_start_reg_offset = start_reg_offset;
- int int_reg_save_area_size = 0;
- rtx insn;
- unsigned reg_mask = 0;
- int i;
+ char *foo;
+ int max = 60; /* Allow for the ..xx extension */
+ int len, tlen;
+
+ if (name[0] == '*')
+ return (&name[1]);
+
+ len = tlen = strlen (name);
+ foo = xstrdup (name);
+
+ /* Don't muck with the ..xx extenstion */
+ if ((foo [tlen-4] == '.') && (foo [tlen-3] == '.'))
+ {
+ max = max + 4;
+ if (tlen > max)
+ {
+ foo [tlen-4] = 0;
+ len = len - 4;
+ max = max - 4;
+ }
+ }
+
+ if (len > max)
+ {
+ char *bar;
+ int i, j, slen, nlen, xlen, chopchar;
+
+ nlen = len;
+
+ /* Change all _ and . characters to spaces, if thats enough then quit.
+ For example: "foobar__foo__bar" becomes "foobar foo bar" */
+
+ for (i = 0; bar = index (foo, '_'); i++)
+ *bar = ' ';
+ nlen = nlen - i;
+
+ for (i = 0; bar = index (foo, '.'); i++)
+ *bar = ' ';
+ nlen = nlen - i;
+
+ for (i = 0; bar = index (foo, '$'); i++)
+ *bar = ' ';
+ nlen = nlen - i;
+
+ /* Iteratively make blank the rightmost non-blank character on the
+ longest leftmost substring delmited by blanks, until it's short
+ enough. For example: "foobar foo bar" becomes, successively:
+ "fooba foo bar"
+ "foob foo bar"
+ "foo foo bar"
+ "fo foo bar"
+ "fo fo bar"
+ "fo fo ba "
+ "f fo ba "
+ "f f ba "
+ "f f b "
+ etc. */
+
+ while (nlen > max)
+ {
+ j = 0;
+ xlen = 0;
+
+ while (foo[j])
+ {
+ /* Find first non-blank */
+ if (foo[j])
+ for (i = j; foo[i]==' ' && foo[i]; i++)
+ ;
+
+ /* Find the first blank */
+ j = i;
+ if (foo[j])
+ for (i = j + 1; foo[i] != ' ' && foo[i]; i++)
+ ;
+
+ /* If this substring is the longest so far, remember the
+ position of the character to chop off. */
+ slen = i - j;
+ if (slen > xlen)
+ {
+ chopchar = i - 1;
+ xlen = slen;
+ }
+
+ j = i;
+ }
+
+ /* Try to avoid chopping uppercase suffix letters */
+ if (isupper (foo [chopchar]))
+ {
+ for (i = chopchar;
+ isupper (foo[i]) && foo[i] != ' ' && i >= 0;
+ i--)
+ ;
+ if (islower (foo[i]))
+ chopchar = i;
+ }
+ foo [chopchar] = ' ';
+ nlen--;
+ }
+
+ /* Put the ..xx extension back */
+ if (len != tlen)
+ {
+ foo [len] = '.';
+ len = len + 4;
+ }
+
+ /* Collapse all the blanks */
+ j = 0;
+ for (i = 0; foo[i]; i++)
+ if (foo[i] != ' ')
+ foo[j++] = foo[i];
+ foo[j] = 0;
+
+ return foo;
+ }
+
+ /* Put back the ..xx extension */
+ if (len != tlen)
+ {
+ foo [len] = '.';
+ len = len + 4;
+ }
+
+ free (foo);
+ return name;
+}
+
+/* On vms we have two kinds of functions:
+
+ - stack frame (PROC_STACK)
+ these are 'normal' functions with local vars and which are
+ calling other functions
+ - register frame (PROC_REGISTER)
+ keeps all data in registers, needs no stack
+
+ We must pass this to the assembler so it can generate the
+ proper pdsc (procedure descriptor)
+ This is done with the '.pdesc' command.
+
+ size is the stack size needed for local variables. */
+
+void
+output_prolog (file, size)
+ FILE *file;
+ HOST_WIDE_INT size;
+{
+ unsigned long imask = 0;
+ unsigned long fmask = 0;
+ /* Stack space needed for pushing registers clobbered by us. */
+ HOST_WIDE_INT sa_size;
+ /* Complete stack size needed. */
+ HOST_WIDE_INT frame_size;
+ /* Offset from base reg to register save area. */
+ int rsa_offset = 8;
+ /* Offset during register save. */
+ int reg_offset;
+ /* Label for the procedure entry. */
+ char entry_label[70];
+ int i;
+
+ sa_size = alpha_sa_size ();
+ frame_size
+ = ALPHA_ROUND (sa_size
+ + (is_stack_procedure ? 8 : 0)
+ + size + current_function_pretend_args_size);
+
+ /* Issue function start and label. */
+ fprintf (file, "\t.ent ");
+ assemble_name (file, alpha_function_name);
+ fprintf (file, "\n");
+ sprintf (entry_label, "%.64s..en", alpha_function_name);
+ ASM_OUTPUT_LABEL (file, entry_label);
+ inside_function = TRUE;
+
+ fprintf (file, "\t.base $%d\n", base_regno);
+
+ /* Calculate register masks for clobbered registers. */
+
+ if (is_stack_procedure)
+ alpha_sa_mask (&imask, &fmask);
+
+ /* Adjust the stack by the frame size. If the frame size is > 4096
+ bytes, we need to be sure we probe somewhere in the first and last
+ 4096 bytes (we can probably get away without the latter test) and
+ every 8192 bytes in between. If the frame size is > 32768, we
+ do this in a loop. Otherwise, we generate the explicit probe
+ instructions.
+
+ Note that we are only allowed to adjust sp once in the prologue. */
+
+ if (frame_size < 32768)
+ {
+ if (frame_size > 4096)
+ {
+ int probed = 4096;
+
+ fprintf (file, "\tstq $31,-%d($30)\n", probed);
+
+ while (probed + 8192 < frame_size)
+ fprintf (file, "\tstq $31,-%d($30)\n", probed += 8192);
+
+ /* We only have to do this probe if we aren't saving registers. */
+ if (sa_size == 0 && probed + 4096 < frame_size)
+ fprintf (file, "\tstq $31,-%d($30)\n", frame_size);
+ }
+
+ if (frame_size != 0)
+ fprintf (file, "\tlda $30,-%d($30)\n", frame_size);
+ }
+ else
+ {
+ /* Here we generate code to set R4 to SP + 4096 and set R23 to the
+ number of 8192 byte blocks to probe. We then probe each block
+ in the loop and then set SP to the proper location. If the
+ amount remaining is > 4096, we have to do one more probe if we
+ are not saving any registers. */
+
+ HOST_WIDE_INT blocks = (frame_size + 4096) / 8192;
+ HOST_WIDE_INT leftover = frame_size + 4096 - blocks * 8192;
+
+ add_long_const (file, blocks, 31, 23, 23);
+
+ fprintf (file, "\tlda $22,4096($30)\n");
+
+ assemble_name (file, alpha_function_name);
+ fprintf (file, "..sc:\n");
+
+ fprintf (file, "\tstq $31,-8192($22)\n");
+ fprintf (file, "\tsubq $23,1,$23\n");
+ fprintf (file, "\tlda $22,-8192($22)\n");
+
+ fprintf (file, "\tbne $23,");
+ assemble_name (file, alpha_function_name);
+ fprintf (file, "..sc\n");
+
+ if (leftover > 4096 && sa_size == 0)
+ fprintf (file, "\tstq $31,-%d($22)\n", leftover);
+
+ fprintf (file, "\tlda $30,-%d($22)\n", leftover);
+ }
+
+ if (is_stack_procedure)
+ {
+ int reg_offset = rsa_offset;
+
+ /* Store R26 (RA) first. */
+ fprintf (file, "\tstq $26,%d($30)\n", reg_offset);
+ reg_offset += 8;
+
+ /* Store integer regs. according to mask. */
+ for (i = 0; i < 32; i++)
+ if (imask & (1L<<i))
+ {
+ fprintf (file, "\tstq $%d,%d($30)\n", i, reg_offset);
+ reg_offset += 8;
+ }
+
+ /* Print the register mask and do floating-point saves. */
+
+ if (imask)
+ fprintf (file, "\t.mask 0x%x,0\n", imask);
+
+ for (i = 0; i < 32; i++)
+ {
+ if (fmask & (1L << i))
+ {
+ fprintf (file, "\tstt $f%d,%d($30)\n", i, reg_offset);
+ reg_offset += 8;
+ }
+ }
+
+ /* Print the floating-point mask, if we've saved any fp register. */
+ if (fmask)
+ fprintf (file, "\t.fmask 0x%x,0\n", fmask);
+
+ fprintf (file, "\tstq $27,0($30)\n");
+ }
+ else
+ {
+ fprintf (file, "\t.fp_save $%d\n", save_fp_regno);
+ fprintf (file, "\tbis $%d,$%d,$%d\n", HARD_FRAME_POINTER_REGNUM,
+ HARD_FRAME_POINTER_REGNUM, save_fp_regno);
+ }
+
+ if (base_regno != REG_PV)
+ fprintf (file, "\tbis $%d,$%d,$%d\n", REG_PV, REG_PV, base_regno);
+
+ if (unwind_regno == HARD_FRAME_POINTER_REGNUM)
+ fprintf (file, "\tbis $%d,$%d,$%d\n", STACK_POINTER_REGNUM,
+ STACK_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM);
+
+ /* Describe our frame. */
+ fprintf (file, "\t.frame $%d,%d,$26,%d\n",
+ unwind_regno, frame_size, rsa_offset);
+
+ /* If we have to allocate space for outgoing args, do it now. */
+ if (current_function_outgoing_args_size != 0)
+ fprintf (file, "\tlda $%d,%d($%d)\n", STACK_POINTER_REGNUM,
+ - ALPHA_ROUND (current_function_outgoing_args_size),
+ HARD_FRAME_POINTER_REGNUM);
+
+ fprintf (file, "\t.prologue\n");
+
+ link_section ();
+ fprintf (file, "\t.align 3\n");
+ ASM_OUTPUT_LABEL (file, alpha_function_name);
+ fprintf (file, "\t.pdesc ");
+ assemble_name (file, alpha_function_name);
+ fprintf (file, "..en,%s\n", is_stack_procedure ? "stack" : "reg");
+ alpha_need_linkage (alpha_function_name, 1);
+ text_section ();
+
+ return;
+}
+
+/* Write function epilogue. */
+
+void
+output_epilog (file, size)
+ FILE *file;
+ int size;
+{
+ unsigned long imask = 0;
+ unsigned long fmask = 0;
+ /* Stack space needed for pushing registers clobbered by us. */
+ HOST_WIDE_INT sa_size = alpha_sa_size ();
+ /* Complete stack size needed. */
+ HOST_WIDE_INT frame_size
+ = ALPHA_ROUND (sa_size
+ + (is_stack_procedure ? 8 : 0)
+ + size + current_function_pretend_args_size);
+ int i;
+ rtx insn = get_last_insn ();
+
+ /* If the last insn was a BARRIER, we don't have to write anything except
+ the .end pseudo-op. */
+
+ if (GET_CODE (insn) == NOTE)
+ insn = prev_nonnote_insn (insn);
+
+ if (insn == 0 || GET_CODE (insn) != BARRIER)
+ {
+ /* Restore clobbered registers, load FP last. */
+
+ if (is_stack_procedure)
+ {
+ int rsa_offset = 8;
+ int reg_offset;
+ int fp_offset;
+
+ if (unwind_regno == HARD_FRAME_POINTER_REGNUM)
+ fprintf (file, "\tbis $%d,$%d,$%d\n", HARD_FRAME_POINTER_REGNUM,
+ HARD_FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM);
+
+ alpha_sa_mask (&imask, &fmask);
+
+ /* Start reloading registers after RA. */
+ reg_offset = rsa_offset + 8;
+
+ for (i = 0; i < 32; i++)
+ if (imask & (1L<<i))
+ {
+ if (i == HARD_FRAME_POINTER_REGNUM)
+ fp_offset = reg_offset;
+ else
+ fprintf (file, "\tldq $%d,%d($30)\n",
+ i, reg_offset);
+ reg_offset += 8;
+ }
+
+ for (i = 0; i < 32; i++)
+ if (fmask & (1L << i))
+ {
+ fprintf (file, "\tldt $f%d,%d($30)\n", i, reg_offset);
+ reg_offset += 8;
+ }
+
+ /* Restore R26 (RA). */
+ fprintf (file, "\tldq $26,%d($30)\n", rsa_offset);
+
+ /* Restore R29 (FP). */
+ fprintf (file, "\tldq $29,%d($30)\n", fp_offset);
+ }
+ else
+ fprintf (file, "\tbis $%d,$%d,$%d\n", save_fp_regno, save_fp_regno,
+ HARD_FRAME_POINTER_REGNUM);
+
+ if (frame_size != 0)
+ {
+ if (frame_size < 32768)
+ fprintf (file, "\tlda $30,%d($30)\n", frame_size);
+ else
+ {
+ long high = frame_size >> 16;
+ long low = frame_size & 0xffff;
+ if (low & 0x8000)
+ {
+ high++;
+ low = -32768 + (low & 0x7fff);
+ }
+ fprintf (file, "\tldah $2,%ld($31)\n", high);
+ fprintf (file, "\tlda $2,%ld($2)\n", low);
+ fprintf (file, "\taddq $30,$2,$30\n");
+ }
+ }
+
+ /* Finally return to the caller. */
+ fprintf (file, "\tret $31,($26),1\n");
+ }
+
+ /* End the function. */
+ fprintf (file, "\t.end ");
+ assemble_name (file, alpha_function_name);
+ fprintf (file, "\n");
+ inside_function = FALSE;
+
+ /* Show that we know this function if it is called again. */
+ SYMBOL_REF_FLAG (XEXP (DECL_RTL (current_function_decl), 0)) = 1;
+}
+
+#else /* !OPEN_VMS */
+
+void
+output_prolog (file, size)
+ FILE *file;
+ int size;
+{
+ HOST_WIDE_INT out_args_size
+ = ALPHA_ROUND (current_function_outgoing_args_size);
+ HOST_WIDE_INT sa_size = alpha_sa_size ();
+ HOST_WIDE_INT frame_size
+ = (out_args_size + sa_size
+ + ALPHA_ROUND (size + current_function_pretend_args_size));
+ HOST_WIDE_INT reg_offset = out_args_size;
+ HOST_WIDE_INT start_reg_offset = reg_offset;
+ HOST_WIDE_INT actual_start_reg_offset = start_reg_offset;
+ int int_reg_save_area_size = 0;
+ rtx insn;
+ unsigned reg_mask = 0;
+ int i;
/* Ecoff can handle multiple .file directives, so put out file and lineno.
We have to do that before the .ent directive as we cannot switch
to the .ent directive, the lex_level, is ignored by the assembler,
so we might as well omit it. */
- fprintf (file, "\t.ent ");
- assemble_name (file, alpha_function_name);
- fprintf (file, "\n");
+ if (!flag_inhibit_size_directive)
+ {
+ fprintf (file, "\t.ent ");
+ assemble_name (file, alpha_function_name);
+ fprintf (file, "\n");
+ }
ASM_OUTPUT_LABEL (file, alpha_function_name);
inside_function = TRUE;
+ if (TARGET_IEEE_CONFORMANT && !flag_inhibit_size_directive)
+ /* Set flags in procedure descriptor to request IEEE-conformant
+ math-library routines. The value we set it to is PDSC_EXC_IEEE
+ (/usr/include/pdsc.h). */
+ fprintf (file, "\t.eflag 48\n");
+
/* Set up offsets to alpha virtual arg/local debugging pointer. */
alpha_auto_offset = -frame_size + current_function_pretend_args_size;
We never need a GP for Windows/NT. */
alpha_function_needs_gp = 0;
+
+#ifdef TARGET_PROFILING_NEEDS_GP
+ if (profile_flag)
+ alpha_function_needs_gp = 1;
+#endif
+
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if ((GET_CODE (insn) == CALL_INSN)
|| (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
break;
}
- if (WINDOWS_NT == 0)
+ if (TARGET_WINDOWS_NT == 0)
{
if (alpha_function_needs_gp)
fprintf (file, "\tldgp $29,0($27)\n");
}
/* Describe our frame. */
- fprintf (file, "\t.frame $%d,%d,$26,%d\n",
- (frame_pointer_needed
- ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM),
- frame_size, current_function_pretend_args_size);
+ if (!flag_inhibit_size_directive)
+ {
+ fprintf (file, "\t.frame $%d,%d,$26,%d\n",
+ (frame_pointer_needed
+ ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM),
+ frame_size, current_function_pretend_args_size);
+ }
/* Save register 26 if any other register needs to be saved. */
if (sa_size != 0)
}
/* Print the register mask and do floating-point saves. */
- if (reg_mask)
+ if (reg_mask && !flag_inhibit_size_directive)
fprintf (file, "\t.mask 0x%x,%d\n", reg_mask,
actual_start_reg_offset - frame_size);
}
/* Print the floating-point mask, if we've saved any fp register. */
- if (reg_mask)
+ if (reg_mask && !flag_inhibit_size_directive)
fprintf (file, "\t.fmask 0x%x,%d\n", reg_mask,
actual_start_reg_offset - frame_size + int_reg_save_area_size);
fprintf (file, "\tbis $30,$30,$15\n");
/* End the prologue and say if we used gp. */
- fprintf (file, "\t.prologue %d\n", alpha_function_needs_gp);
+ if (!flag_inhibit_size_directive)
+ fprintf (file, "\t.prologue %d\n", alpha_function_needs_gp);
}
/* Write function epilogue. */
{
int fp_offset = 0;
+ final_prescan_insn (NULL_RTX, NULL_PTR, 0);
+
/* If we have a frame pointer, restore SP from it. */
if (frame_pointer_needed)
fprintf (file, "\tbis $15,$15,$30\n");
}
/* End the function. */
- fprintf (file, "\t.end ");
- assemble_name (file, alpha_function_name);
- fprintf (file, "\n");
+ if (!flag_inhibit_size_directive)
+ {
+ fprintf (file, "\t.end ");
+ assemble_name (file, alpha_function_name);
+ fprintf (file, "\n");
+ }
inside_function = FALSE;
/* Show that we know this function if it is called again. */
SYMBOL_REF_FLAG (XEXP (DECL_RTL (current_function_decl), 0)) = 1;
}
+#endif /* !OPEN_VMS */
\f
/* Debugging support. */
fprintf (stream, "\t#@stabs\n");
}
- else if (!TARGET_GAS && write_symbols == DBX_DEBUG)
+ else if (write_symbols == DBX_DEBUG)
{
ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
fprintf (stream, "%s ", ASM_STABS_OP);
FILE *stream;
int line;
{
- if (! TARGET_GAS && write_symbols == DBX_DEBUG)
+ if (write_symbols == DBX_DEBUG)
{
/* mips-tfile doesn't understand .stabd directives. */
++sym_lineno;
else
fprintf (stream, "\n\t.loc\t%d %d\n", num_source_filenames, line);
}
+\f
+/* Structure to show the current status of registers and memory. */
+
+struct shadow_summary
+{
+ struct {
+ unsigned long i : 32; /* Mask of int regs */
+ unsigned long fp : 32; /* Mask of fp regs */
+ unsigned long mem : 1; /* mem == imem | fpmem */
+ } used, defd;
+};
+
+/* Summary the effects of expression X on the machine. Update SUM, a pointer
+ to the summary structure. SET is nonzero if the insn is setting the
+ object, otherwise zero. */
+
+static void
+summarize_insn (x, sum, set)
+ rtx x;
+ struct shadow_summary *sum;
+ int set;
+{
+ char *format_ptr;
+ int i, j;
+
+ if (x == 0)
+ return;
+
+ switch (GET_CODE (x))
+ {
+ /* ??? Note that this case would be incorrect if the Alpha had a
+ ZERO_EXTRACT in SET_DEST. */
+ case SET:
+ summarize_insn (SET_SRC (x), sum, 0);
+ summarize_insn (SET_DEST (x), sum, 1);
+ break;
+
+ case CLOBBER:
+ summarize_insn (XEXP (x, 0), sum, 1);
+ break;
+
+ case USE:
+ summarize_insn (XEXP (x, 0), sum, 0);
+ break;
+
+ case PARALLEL:
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ summarize_insn (XVECEXP (x, 0, i), sum, 0);
+ break;
+
+ case REG:
+ {
+ int regno = REGNO (x);
+ unsigned long mask = 1UL << (regno % 32);
+
+ if (regno == 31 || regno == 63)
+ break;
+
+ if (set)
+ {
+ if (regno < 32)
+ sum->defd.i |= mask;
+ else
+ sum->defd.fp |= mask;
+ }
+ else
+ {
+ if (regno < 32)
+ sum->used.i |= mask;
+ else
+ sum->used.fp |= mask;
+ }
+ }
+ break;
+
+ case MEM:
+ if (set)
+ sum->defd.mem = 1;
+ else
+ sum->used.mem = 1;
+
+ /* Find the regs used in memory address computation: */
+ summarize_insn (XEXP (x, 0), sum, 0);
+ break;
+
+ case SUBREG:
+ summarize_insn (SUBREG_REG (x), sum, 0);
+ break;
+
+ case CONST_INT: case CONST_DOUBLE:
+ case SYMBOL_REF: case LABEL_REF: case CONST:
+ break;
+
+ /* Handle common unary and binary ops for efficiency. */
+ case COMPARE: case PLUS: case MINUS: case MULT: case DIV:
+ case MOD: case UDIV: case UMOD: case AND: case IOR:
+ case XOR: case ASHIFT: case ROTATE: case ASHIFTRT: case LSHIFTRT:
+ case ROTATERT: case SMIN: case SMAX: case UMIN: case UMAX:
+ case NE: case EQ: case GE: case GT: case LE:
+ case LT: case GEU: case GTU: case LEU: case LTU:
+ summarize_insn (XEXP (x, 0), sum, 0);
+ summarize_insn (XEXP (x, 1), sum, 0);
+ break;
+
+ case NEG: case NOT: case SIGN_EXTEND: case ZERO_EXTEND:
+ case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE: case FLOAT:
+ case FIX: case UNSIGNED_FLOAT: case UNSIGNED_FIX: case ABS:
+ case SQRT: case FFS:
+ summarize_insn (XEXP (x, 0), sum, 0);
+ break;
+
+ default:
+ format_ptr = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ switch (format_ptr[i])
+ {
+ case 'e':
+ summarize_insn (XEXP (x, i), sum, 0);
+ break;
+
+ case 'E':
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ summarize_insn (XVECEXP (x, i, j), sum, 0);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+}
+\f
+/* This function is executed just prior to the output of assembler code for
+ INSN to modify the extracted operands so they will be output differently.
+
+ OPVEC is the vector containing the operands extracted from INSN, and
+ NOPERANDS is the number of elements of the vector which contain meaningful
+ data for this insn. The contents of this vector are what will be used to
+ convert the insn template into assembler code, so you can change the
+ assembler output by changing the contents of the vector.
+
+ We use this function to ensure a sufficient number of `trapb' instructions
+ are in the code when the user requests code with a trap precision of
+ functions or instructions.
+
+ In naive mode, when the user requests a trap-precision of "instruction", a
+ trapb is needed after every instruction that may generate a trap (and after
+ jsr/bsr instructions, because called functions may import a trap from the
+ caller). This ensures that the code is resumption safe but it is also slow.
+
+ When optimizations are turned on, we delay issuing a trapb as long as
+ possible. In this context, a trap shadow is the sequence of instructions
+ that starts with a (potentially) trap generating instruction and extends to
+ the next trapb or call_pal instruction (but GCC never generates call_pal by
+ itself). We can delay (and therefore sometimes omit) a trapb subject to the
+ following conditions:
+
+ (a) On entry to the trap shadow, if any Alpha register or memory location
+ contains a value that is used as an operand value by some instruction in
+ the trap shadow (live on entry), then no instruction in the trap shadow
+ may modify the register or memory location.
+
+ (b) Within the trap shadow, the computation of the base register for a
+ memory load or store instruction may not involve using the result
+ of an instruction that might generate an UNPREDICTABLE result.
+
+ (c) Within the trap shadow, no register may be used more than once as a
+ destination register. (This is to make life easier for the trap-handler.)
+
+ (d) The trap shadow may not include any branch instructions.
+
+ */
+
+void
+final_prescan_insn (insn, opvec, noperands)
+ rtx insn;
+ rtx *opvec;
+ int noperands;
+{
+ static struct shadow_summary shadow = {0, 0, 0, 0, 0};
+
+#define CLOSE_SHADOW \
+ do \
+ { \
+ fputs ("\ttrapb\n", asm_out_file); \
+ trap_pending = 0; \
+ bzero ((char *) &shadow, sizeof shadow); \
+ } \
+ while (0)
+
+ if (alpha_tp == ALPHA_TP_PROG)
+ return;
+
+ if (trap_pending)
+ switch (alpha_tp)
+ {
+ case ALPHA_TP_FUNC:
+ /* Generate one trapb before epilogue (indicated by INSN==0) */
+ if (insn == 0)
+ CLOSE_SHADOW;
+ break;
+
+ case ALPHA_TP_INSN:
+ if (optimize && insn != 0)
+ {
+ struct shadow_summary sum = {0, 0, 0};
+
+ switch (GET_CODE(insn))
+ {
+ case INSN:
+ summarize_insn (PATTERN (insn), &sum, 0);
+
+ if ((sum.defd.i & shadow.defd.i)
+ || (sum.defd.fp & shadow.defd.fp))
+ {
+ /* (c) would be violated */
+ CLOSE_SHADOW;
+ break;
+ }
+
+ /* Combine shadow with summary of current insn: */
+ shadow.used.i |= sum.used.i;
+ shadow.used.fp |= sum.used.fp;
+ shadow.used.mem |= sum.used.mem;
+ shadow.defd.i |= sum.defd.i;
+ shadow.defd.fp |= sum.defd.fp;
+ shadow.defd.mem |= sum.defd.mem;
+
+ if ((sum.defd.i & shadow.used.i)
+ || (sum.defd.fp & shadow.used.fp)
+ || (sum.defd.mem & shadow.used.mem))
+ {
+ /* (a) would be violated (also takes care of (b)). */
+ if (get_attr_trap (insn) == TRAP_YES
+ && ((sum.defd.i & sum.used.i)
+ || (sum.defd.fp & sum.used.fp)))
+ abort ();
+
+ CLOSE_SHADOW;
+ break;
+ }
+ break;
+
+ case JUMP_INSN:
+ case CALL_INSN:
+ case CODE_LABEL:
+ CLOSE_SHADOW;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ else
+ CLOSE_SHADOW;
+ break;
+ }
+
+ if (insn != 0 && get_attr_trap (insn) == TRAP_YES)
+ {
+ if (optimize && !trap_pending && GET_CODE (insn) == INSN)
+ summarize_insn (PATTERN (insn), &shadow, 0);
+ trap_pending = 1;
+ }
+}
+
+/* Check a floating-point value for validity for a particular machine mode. */
+
+static char *float_strings[] =
+{
+ "1.70141173319264430e+38", /* 2^127 (2^24 - 1) / 2^24 */
+ "-1.70141173319264430e+38",
+ "2.93873587705571877e-39", /* 2^-128 */
+ "-2.93873587705571877e-39"
+};
+
+static REAL_VALUE_TYPE float_values[4];
+static int inited_float_values = 0;
+
+int
+check_float_value (mode, d, overflow)
+ enum machine_mode mode;
+ REAL_VALUE_TYPE *d;
+ int overflow;
+{
+
+ if (TARGET_IEEE || TARGET_IEEE_CONFORMANT || TARGET_IEEE_WITH_INEXACT)
+ return 0;
+
+ if (TARGET_FLOAT_VAX)
+ return 0;
+
+ if (inited_float_values == 0)
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ float_values[i] = REAL_VALUE_ATOF (float_strings[i], DFmode);
+
+ inited_float_values = 1;
+ }
+
+ if (mode == SFmode)
+ {
+ REAL_VALUE_TYPE r;
+
+ bcopy ((char *) d, (char *) &r, sizeof (REAL_VALUE_TYPE));
+ if (REAL_VALUES_LESS (float_values[0], r))
+ {
+ bcopy ((char *) &float_values[0], (char *) d,
+ sizeof (REAL_VALUE_TYPE));
+ return 1;
+ }
+ else if (REAL_VALUES_LESS (r, float_values[1]))
+ {
+ bcopy ((char *) &float_values[1], (char *) d,
+ sizeof (REAL_VALUE_TYPE));
+ return 1;
+ }
+ else if (REAL_VALUES_LESS (dconst0, r)
+ && REAL_VALUES_LESS (r, float_values[2]))
+ {
+ bcopy ((char *) &dconst0, (char *) d, sizeof (REAL_VALUE_TYPE));
+ return 1;
+ }
+ else if (REAL_VALUES_LESS (r, dconst0)
+ && REAL_VALUES_LESS (float_values[3], r))
+ {
+ bcopy ((char *) &dconst0, (char *) d, sizeof (REAL_VALUE_TYPE));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#if OPEN_VMS
+
+void *
+function_arg (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int named;
+{
+ int arg;
+
+ if (mode == VOIDmode) /* final call, return argument information */
+ {
+ return GEN_INT (*cum);
+ }
+
+ arg = *cum & 0xff;
+
+ switch (mode)
+ {
+ case SFmode:
+ *cum |= (((TARGET_FLOAT_VAX)?1:4) << ((arg * 3)+8)); /* 4 = AI$K_AR_FS, IEEE single */
+ break;
+ case DFmode:
+ *cum |= (((TARGET_FLOAT_VAX)?3:5) << ((arg * 3)+8)); /* 5 = AI$K_AR_FT, IEEE double */
+ break;
+ case TFmode:
+ *cum |= (7 << ((arg * 3)+8)); /* 5 = AI$K_AR_FT, IEEE double */
+ break;
+ default:
+ break;
+ }
+
+ return (arg < 6 && ! MUST_PASS_IN_STACK (mode, type)
+ ? gen_rtx(REG, mode,
+ (*cum & 0xff) + 16 + ((TARGET_FPREGS
+ && (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_FLOAT))
+ * 32))
+ : 0);
+}
+
+/* Structure to collect function names for final output
+ in link section. */
+
+enum links_kind {KIND_UNUSED, KIND_LOCAL, KIND_EXTERN};
+
+
+struct alpha_links {
+ struct alpha_links *next;
+ char *name;
+ enum links_kind kind;
+};
+
+static struct alpha_links *alpha_links_base = 0;
+
+/* Make (or fake) .linkage entry for function call.
+
+ IS_LOCAL is 0 if name is used in call, 1 if name is used in definition. */
+
+void
+alpha_need_linkage (name, is_local)
+ char *name;
+ int is_local;
+{
+ rtx x;
+ struct alpha_links *lptr, *nptr;
+
+ if (name[0] == '*')
+ name++;
+
+ /* Is this name already defined ? */
+
+ for (lptr = alpha_links_base; lptr; lptr = lptr->next)
+ if (strcmp (lptr->name, name) == 0)
+ {
+ if (is_local)
+ {
+ /* Defined here but external assumed. */
+ if (lptr->kind == KIND_EXTERN)
+ lptr->kind = KIND_LOCAL;
+ }
+ else
+ {
+ /* Used here but unused assumed. */
+ if (lptr->kind == KIND_UNUSED)
+ lptr->kind = KIND_LOCAL;
+ }
+ return;
+ }
+
+ nptr = (struct alpha_links *) xmalloc (sizeof (struct alpha_links));
+ nptr->next = alpha_links_base;
+ nptr->name = xstrdup (name);
+
+ /* Assume external if no definition. */
+ nptr->kind = (is_local ? KIND_UNUSED : KIND_EXTERN);
+
+ /* Ensure we have an IDENTIFIER so assemble_name can mark is used. */
+ get_identifier (name);
+
+ alpha_links_base = nptr;
+
+ return;
+}
+
+
+void
+alpha_write_linkage (stream)
+ FILE *stream;
+{
+ struct alpha_links *lptr, *nptr;
+
+ readonly_section ();
+
+ fprintf (stream, "\t.align 3\n");
+
+ for (lptr = alpha_links_base; lptr; lptr = nptr)
+ {
+ nptr = lptr->next;
+
+ if (lptr->kind == KIND_UNUSED
+ || ! TREE_SYMBOL_REFERENCED (get_identifier (lptr->name)))
+ continue;
+
+ fprintf (stream, "%s..lk:\n", lptr->name);
+ if (lptr->kind == KIND_LOCAL)
+ {
+ /* Local and used, build linkage pair. */
+ fprintf (stream, "\t.quad %s..en\n", lptr->name);
+ fprintf (stream, "\t.quad %s\n", lptr->name);
+ }
+ else
+ /* External and used, request linkage pair. */
+ fprintf (stream, "\t.linkage %s\n", lptr->name);
+ }
+}
+
+#else
+
+void
+alpha_need_linkage (name, is_local)
+ char *name;
+ int is_local;
+{
+}
+
+#endif /* OPEN_VMS */
+