#include "expr.h"
#include "obstack.h"
#include "tree.h"
+#include "except.h"
+#include "function.h"
+
+/* External data. */
+extern char *version_string;
+extern int rtx_equal_function_value_matters;
/* Specify which cpu to schedule for. */
+
enum processor_type alpha_cpu;
/* Specify how accurate floating-point traps need to be. */
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] */
int alpha_function_needs_gp;
-extern char *version_string;
-extern int rtx_equal_function_value_matters;
+/* If non-null, this rtx holds the return address for the function. */
+
+static rtx alpha_return_addr_rtx;
/* Declarations of static functions. */
static void alpha_set_memflags_1 PROTO((rtx, 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)
+
+/* Some helpful register info. */
+#define REG_PV 27
+#define REG_RA 26
+
\f
/* Parse target option strings. */
/* Otherwise, return the default cost. */
return cost;
}
+\f
+/* Functions to save and restore alpha_return_addr_rtx. */
+
+struct machine_function
+{
+ rtx ra_rtx;
+};
+
+static void
+alpha_save_machine_status (p)
+ struct function *p;
+{
+ struct machine_function *machine =
+ (struct machine_function *) xmalloc (sizeof (struct machine_function));
+
+ p->machine = machine;
+ machine->ra_rtx = alpha_return_addr_rtx;
+}
+
+static void
+alpha_restore_machine_status (p)
+ struct function *p;
+{
+ struct machine_function *machine = p->machine;
+
+ alpha_return_addr_rtx = machine->ra_rtx;
+
+ free (machine);
+ p->machine = (struct machine_function *)0;
+}
+
+/* Do anything needed before RTL is emitted for each function. */
+
+void
+alpha_init_expanders ()
+{
+ alpha_return_addr_rtx = NULL_RTX;
+
+ /* Arrange to save and restore machine status around nested functions. */
+ save_machine_status = alpha_save_machine_status;
+ restore_machine_status = alpha_restore_machine_status;
+}
+
+/* Start the ball rolling with RETURN_ADDR_RTX. */
+
+rtx
+alpha_return_addr (count, frame)
+ int count;
+ rtx frame;
+{
+ rtx init, first;
+
+ if (count != 0)
+ return const0_rtx;
+
+ if (alpha_return_addr_rtx)
+ return alpha_return_addr_rtx;
+
+ /* No rtx yet. Invent one, and initialize it from $26 in the prologue. */
+ alpha_return_addr_rtx = gen_reg_rtx (Pmode);
+ init = gen_rtx (SET, Pmode, alpha_return_addr_rtx, gen_rtx (REG, Pmode, 26));
+
+ /* Emit the insn to the prologue with the other argument copies. */
+ push_topmost_sequence ();
+ emit_insn_after (init, get_insns ());
+ pop_topmost_sequence ();
+
+ return alpha_return_addr_rtx;
+}
+
+static int
+alpha_ra_ever_killed ()
+{
+ rtx i, ra;
+
+ if (!alpha_return_addr_rtx)
+ return regs_ever_live[REG_RA];
+
+ return reg_set_between_p (gen_rtx (REG, REG_RA), get_insns(), NULL_RTX);
+}
+
\f
/* Print an operand. Recognize special options, documented below. */
#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
/* 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 (! fixed_regs[i] && ! call_used_regs[i]
+ && regs_ever_live[i] && i != REG_RA)
{
if (i < 32)
imask |= (1L << i);
else
fmask |= (1L << (i - 32));
}
+ if (alpha_ra_ever_killed ())
+ imask |= (1L << REG_RA);
*imaskP = imask;
*fmaskP = fmask;
/* 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 (! fixed_regs[i] && ! call_used_regs[i]
+ && regs_ever_live[i] && i != REG_RA)
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;
+ is_stack_procedure = sa_size != 0 || alpha_ra_ever_killed ();
/* 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
int i;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (! fixed_regs[i] && ! call_used_regs[i] && regs_ever_live[i])
+ if (! fixed_regs[i] && ! call_used_regs[i]
+ && regs_ever_live[i] && i != REG_RA)
size++;
/* If some registers were saved but not reg 26, reg 26 must also
be saved, so leave space for it. */
- if (size != 0 && ! regs_ever_live[26])
+ if (size != 0 || alpha_ra_ever_killed ())
size++;
/* Our size must be even (multiple of 16 bytes). */
#else /* !OPEN_VMS */
+static int
+alpha_does_function_need_gp ()
+{
+ rtx insn;
+
+ /* We never need a GP for Windows/NT. */
+ if (TARGET_WINDOWS_NT)
+ return 0;
+
+#ifdef TARGET_PROFILING_NEEDS_GP
+ if (profile_flag)
+ return 1;
+#endif
+
+ /* If we need a GP (we have a LDSYM insn or a CALL_INSN), load it first.
+ Even if we are a static function, we still need to do this in case
+ our address is taken and passed to something like qsort. */
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER)
+ {
+ enum attr_type type = get_attr_type (insn);
+ if (type == TYPE_LDSYM || type == TYPE_JSR)
+ return 1;
+ }
+
+ return 0;
+}
+
void
output_prolog (file, size)
FILE *file;
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, sa_reg;
alpha_auto_offset = -frame_size + current_function_pretend_args_size;
alpha_arg_offset = -frame_size + 48;
- /* If we need a GP (we have a LDSYM insn or a CALL_INSN), load it first.
- Even if we are a static function, we still need to do this in case
- our address is taken and passed to something like qsort.
-
- 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'
- && GET_CODE (PATTERN (insn)) != USE
- && GET_CODE (PATTERN (insn)) != CLOBBER
- && (get_attr_type (insn) == TYPE_LDSYM
- || get_attr_type (insn) == TYPE_ISUBR)))
- {
- alpha_function_needs_gp = 1;
- break;
- }
+ alpha_function_needs_gp = alpha_does_function_need_gp ();
if (TARGET_WINDOWS_NT == 0)
{
sa_reg = 24;
}
- /* Save register 26 if any other register needs to be saved. */
+ /* Save register RA if any other register needs to be saved. */
if (sa_size != 0)
{
- reg_mask |= 1 << 26;
+ reg_mask |= 1 << REG_RA;
fprintf (file, "\tstq $26,%d($%d)\n", reg_offset, sa_reg);
reg_offset += 8;
int_reg_save_area_size += 8;
/* Now save any other used integer registers required to be saved. */
for (i = 0; i < 32; i++)
- if (! fixed_regs[i] && ! call_used_regs[i] && regs_ever_live[i] && i != 26)
+ if (! fixed_regs[i] && ! call_used_regs[i]
+ && regs_ever_live[i] && i != REG_RA)
{
reg_mask |= 1 << i;
fprintf (file, "\tstq $%d,%d($%d)\n", i, reg_offset, sa_reg);
;; separately.
(define_attr "type"
- "ld,st,ibr,fbr,jsr,iadd,ilog,shift,cmov,icmp,imull,imulq,imulh,fadd,fmul,fcpys,fdivs,fdivt,ldsym,isubr,misc"
+ "ld,st,ibr,fbr,jsr,iadd,ilog,shift,cmov,icmp,imull,imulq,imulh,fadd,fmul,fcpys,fdivs,fdivt,ldsym,misc"
(const_string "iadd"))
;; The TRAP_TYPE attribute marks instructions that may generate traps
; (clobber (reg:DI 28))]
; "!TARGET_OPEN_VMS"
; "%E1 $24,$25,$27"
-; [(set_attr "type" "isubr")])
+; [(set_attr "type" "jsr")])
(define_insn ""
[(set (reg:DI 27)
(clobber (reg:DI 28))]
"!TARGET_OPEN_VMS"
"%E1 $24,$25,$27"
- [(set_attr "type" "isubr")])
+ [(set_attr "type" "jsr")])
\f
;; Next are the basic logical operations. These only exist in DImode.
jsr $26,($27),0\;ldgp $29,0($26)
bsr $26,%0..ng
jsr $26,%0\;ldgp $29,0($26)"
- [(set_attr "type" "jsr,jsr,ibr")])
+ [(set_attr "type" "jsr,ibr,jsr")])
(define_insn ""
[(call (mem:DI (match_operand:DI 0 "call_operand" "r,i"))
"@
jsr $26,(%0)
bsr $26,%0"
- [(set_attr "type" "jsr")])
+ [(set_attr "type" "jsr,ibr")])
(define_insn ""
[(call (mem:DI (match_operand:DI 0 "call_operand" "r,i"))
jsr $26,($27),0\;ldgp $29,0($26)
bsr $26,%1..ng
jsr $26,%1\;ldgp $29,0($26)"
- [(set_attr "type" "jsr,jsr,ibr")])
+ [(set_attr "type" "jsr,ibr,jsr")])
(define_insn ""
[(set (match_operand 0 "register_operand" "=rf,rf")
"@
jsr $26,(%1)
bsr $26,%1"
- [(set_attr "type" "jsr")])
+ [(set_attr "type" "jsr,ibr")])
(define_insn ""
[(set (match_operand 0 "register_operand" "")
;; Cache flush. Used by INITIALIZE_TRAMPOLINE. 0x86 is PAL_imb, but we don't
;; want to have to include pal.h in our .s file.
+;;
+;; Technically the type for call_pal is jsr, but we use that for determining
+;; if we need a GP. Use ibr instead since it has the same scheduling
+;; characteristics.
(define_insn ""
[(unspec_volatile [(const_int 0)] 0)]
""
"call_pal 0x86"
- [(set_attr "type" "isubr")])
+ [(set_attr "type" "ibr")])
\f
;; Finally, we have the basic data motion insns. The byte and word insns
;; are done via define_expand. Start with the floating-point insns, since