/* Subroutines used for code generation on the DEC Alpha.
- Copyright (C) 1992, 1993, 1994, 1995, 1996 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.
#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;
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] */
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_string = "i";
- alpha_fptm_string = "su";
- target_flags |= MASK_IEEE_CONFORMANT;
+ alpha_tp = ALPHA_TP_INSN;
+ alpha_fptm = ALPHA_FPTM_SU;
}
if (TARGET_IEEE_WITH_INEXACT)
{
- alpha_tp_string = "i";
- alpha_fptm_string = "sui";
- target_flags |= MASK_IEEE_CONFORMANT;
+ alpha_tp = ALPHA_TP_INSN;
+ alpha_fptm = ALPHA_FPTM_SUI;
}
if (alpha_tp_string)
- switch (alpha_tp_string[0])
- {
- case 'p':
+ {
+ if (! strcmp (alpha_tp_string, "p"))
alpha_tp = ALPHA_TP_PROG;
- break;
-
- case 'f':
+ else if (! strcmp (alpha_tp_string, "f"))
alpha_tp = ALPHA_TP_FUNC;
- break;
-
- case 'i':
+ else if (! strcmp (alpha_tp_string, "i"))
alpha_tp = ALPHA_TP_INSN;
- break;
-
- default:
- error ("bad value (%s) for -mtrap-precision switch",
- alpha_tp_string);
- break;
- }
+ else
+ error ("bad value `%s' for -mtrap-precision switch", alpha_tp_string);
+ }
if (alpha_fprm_string)
- switch (alpha_fprm_string[0])
- {
- case 'n':
+ {
+ if (! strcmp (alpha_fprm_string, "n"))
alpha_fprm = ALPHA_FPRM_NORM;
- break;
-
- case 'm':
+ else if (! strcmp (alpha_fprm_string, "m"))
alpha_fprm = ALPHA_FPRM_MINF;
- break;
-
- case 'c':
+ else if (! strcmp (alpha_fprm_string, "c"))
alpha_fprm = ALPHA_FPRM_CHOP;
- break;
-
- case 'd':
+ else if (! strcmp (alpha_fprm_string,"d"))
alpha_fprm = ALPHA_FPRM_DYN;
- break;
-
- default:
- error ("bad value (%s) for -mfp-rounding-mode switch",
+ else
+ error ("bad value `%s' for -mfp-rounding-mode switch",
alpha_fprm_string);
- break;
- }
+ }
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);
+ {
+ 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_tp != ALPHA_TP_INSN)
+ if ((alpha_fptm == ALPHA_FPTM_SU || alpha_fptm == ALPHA_FPTM_SUI)
+ && alpha_tp != ALPHA_TP_INSN)
{
- error ("fp software completion requires -mtrap-precision=i");
+ 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));
+ || (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
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 ...).
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
case NE:
/* This must be reversed. */
code = reverse_condition (code);
+ cmov_code = EQ;
break;
case GE: case GT: case GEU: case GTU:
tem = gen_reg_rtx (cmp_op_mode);
emit_move_insn (tem, gen_rtx (code, cmp_op_mode, op0, op1));
- return gen_rtx (code == NE ? EQ : NE, VOIDmode, tem,
- CONST0_RTX (cmp_op_mode));
+ 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
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:
}
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)));
-
- addr = force_operand (addr, NULL_RTX);
+ 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)));
- /* 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;
+ /* 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));
- /* Store the address of the first integer register in the __base member. */
+ addr = force_operand (addr, NULL_RTX);
#ifdef POINTERS_EXTEND_UNSIGNED
addr = convert_memory_address (ptr_mode, addr);
#endif
- 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);
+ 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) && !defined(__linux__)
+#if !defined(CROSS_COMPILE) && !defined(_WIN32) && !defined(__linux__) && !defined(VMS)
#include <stamp.h>
#endif
/* Write function prologue. */
+#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;
+{
+ 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;
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)
+ 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). */
}
/* 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. */
}
/* 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;
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;
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 */
+