/* Subroutines used for code generation on the DEC Alpha.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
This file is part of GNU CC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "regs.h"
#include "target-def.h"
#include "debug.h"
#include "langhooks.h"
+#include <splay-tree.h>
/* Specify which cpu to schedule for. */
enum alpha_fp_trap_mode alpha_fptm;
+/* Specify bit size of immediate TLS offsets. */
+
+int alpha_tls_size = 32;
+
/* Strings decoded into the above options. */
const char *alpha_cpu_string; /* -mcpu= */
const char *alpha_fprm_string; /* -mfp-rounding-mode=[n|m|c|d] */
const char *alpha_fptm_string; /* -mfp-trap-mode=[n|u|su|sui] */
const char *alpha_mlat_string; /* -mmemory-latency= */
+const char *alpha_tls_size_string; /* -mtls-size=[16|32|64] */
/* Save information from a "cmpxx" operation until the branch or scc is
emitted. */
struct alpha_compare alpha_compare;
-/* Non-zero if inside of a function, because the Alpha asm can't
+/* Nonzero if inside of a function, because the Alpha asm can't
handle .files inside of functions. */
static int inside_function = FALSE;
/* The alias set for prologue/epilogue register save/restore. */
-static int alpha_sr_alias_set;
+static GTY(()) int alpha_sr_alias_set;
/* The assembler name of the current function. */
static const char *alpha_fnname;
/* The next explicit relocation sequence number. */
+extern GTY(()) int alpha_next_sequence_number;
int alpha_next_sequence_number = 1;
/* The literal and gpdisp sequence numbers for this insn, as printed
by %# and %* respectively. */
+extern GTY(()) int alpha_this_literal_sequence_number;
+extern GTY(()) int alpha_this_gpdisp_sequence_number;
int alpha_this_literal_sequence_number;
int alpha_this_gpdisp_sequence_number;
+/* Costs of various operations on the different architectures. */
+
+struct alpha_rtx_cost_data
+{
+ unsigned char fp_add;
+ unsigned char fp_mult;
+ unsigned char fp_div_sf;
+ unsigned char fp_div_df;
+ unsigned char int_mult_si;
+ unsigned char int_mult_di;
+ unsigned char int_shift;
+ unsigned char int_cmov;
+};
+
+static struct alpha_rtx_cost_data const alpha_rtx_cost_data[PROCESSOR_MAX] =
+{
+ { /* EV4 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (6), /* fp_mult */
+ COSTS_N_INSNS (34), /* fp_div_sf */
+ COSTS_N_INSNS (63), /* fp_div_df */
+ COSTS_N_INSNS (23), /* int_mult_si */
+ COSTS_N_INSNS (23), /* int_mult_di */
+ COSTS_N_INSNS (2), /* int_shift */
+ COSTS_N_INSNS (2), /* int_cmov */
+ },
+ { /* EV5 */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult */
+ COSTS_N_INSNS (15), /* fp_div_sf */
+ COSTS_N_INSNS (22), /* fp_div_df */
+ COSTS_N_INSNS (8), /* int_mult_si */
+ COSTS_N_INSNS (12), /* int_mult_di */
+ COSTS_N_INSNS (1) + 1, /* int_shift */
+ COSTS_N_INSNS (1), /* int_cmov */
+ },
+ { /* EV6 */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult */
+ COSTS_N_INSNS (12), /* fp_div_sf */
+ COSTS_N_INSNS (15), /* fp_div_df */
+ COSTS_N_INSNS (7), /* int_mult_si */
+ COSTS_N_INSNS (7), /* int_mult_di */
+ COSTS_N_INSNS (1), /* int_shift */
+ COSTS_N_INSNS (2), /* int_cmov */
+ },
+};
+
/* Declarations of static functions. */
-static bool decl_in_text_section
+static bool alpha_function_ok_for_sibcall
+ PARAMS ((tree, tree));
+static int tls_symbolic_operand_1
+ PARAMS ((rtx, enum machine_mode, int, int));
+static enum tls_model tls_symbolic_operand_type
+ PARAMS ((rtx));
+static bool decl_has_samegp
PARAMS ((tree));
+static bool alpha_in_small_data_p
+ PARAMS ((tree));
+static rtx get_tls_get_addr
+ PARAMS ((void));
static int some_small_symbolic_operand_1
PARAMS ((rtx *, void *));
static int split_small_symbolic_operand_1
PARAMS ((rtx *, void *));
-static bool local_symbol_p
+static bool alpha_cannot_copy_insn_p
PARAMS ((rtx));
+static bool alpha_rtx_costs
+ PARAMS ((rtx, int, int, int *));
static void alpha_set_memflags_1
PARAMS ((rtx, int, int, int));
static rtx alpha_emit_set_const_1
PARAMS ((rtx *out_regs, rtx smem, HOST_WIDE_INT words, HOST_WIDE_INT ofs));
static void alpha_expand_unaligned_store_words
PARAMS ((rtx *out_regs, rtx smem, HOST_WIDE_INT words, HOST_WIDE_INT ofs));
+static void alpha_init_builtins
+ PARAMS ((void));
+static rtx alpha_expand_builtin
+ PARAMS ((tree, rtx, rtx, enum machine_mode, int));
static void alpha_sa_mask
PARAMS ((unsigned long *imaskP, unsigned long *fmaskP));
-static int find_lo_sum
+static int find_lo_sum_using_gp
PARAMS ((rtx *, void *));
static int alpha_does_function_need_gp
PARAMS ((void));
PARAMS ((void));
static const char *get_round_mode_suffix
PARAMS ((void));
+static const char *get_some_local_dynamic_name
+ PARAMS ((void));
+static int get_some_local_dynamic_name_1
+ PARAMS ((rtx *, void *));
static rtx set_frame_related_p
PARAMS ((void));
static const char *alpha_lookup_xfloating_lib_func
PARAMS ((rtx, rtx, rtx, int));
static int alpha_issue_rate
PARAMS ((void));
-static int alpha_variable_issue
- PARAMS ((FILE *, int, rtx, int));
+static int alpha_use_dfa_pipeline_interface
+ PARAMS ((void));
+static int alpha_multipass_dfa_lookahead
+ PARAMS ((void));
+static void alpha_reorg
+ PARAMS ((void));
-#if TARGET_ABI_UNICOSMK
-static void alpha_init_machine_status
- PARAMS ((struct function *p));
-static void alpha_mark_machine_status
- PARAMS ((struct function *p));
-static void alpha_free_machine_status
- PARAMS ((struct function *p));
+#ifdef OBJECT_FORMAT_ELF
+static void alpha_elf_select_rtx_section
+ PARAMS ((enum machine_mode, rtx, unsigned HOST_WIDE_INT));
+#endif
+
+#if TARGET_ABI_OPEN_VMS
+static bool alpha_linkage_symbol_p
+ PARAMS ((const char *symname));
+static int alpha_write_one_linkage
+ PARAMS ((splay_tree_node, void *));
+static void alpha_write_linkage
+ PARAMS ((FILE *, const char *, tree));
+#endif
+
+#if TARGET_ABI_OSF
+static void alpha_output_mi_thunk_osf
+ PARAMS ((FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree));
#endif
+static struct machine_function * alpha_init_machine_status
+ PARAMS ((void));
+
static void unicosmk_output_deferred_case_vectors PARAMS ((FILE *));
static void unicosmk_gen_dsib PARAMS ((unsigned long *imaskP));
static void unicosmk_output_ssib PARAMS ((FILE *, const char *));
# define TARGET_SECTION_TYPE_FLAGS vms_section_type_flags
#endif
+#undef TARGET_IN_SMALL_DATA_P
+#define TARGET_IN_SMALL_DATA_P alpha_in_small_data_p
+
#if TARGET_ABI_UNICOSMK
static void unicosmk_asm_named_section PARAMS ((const char *, unsigned int));
static void unicosmk_insert_attributes PARAMS ((tree, tree *));
static unsigned int unicosmk_section_type_flags PARAMS ((tree, const char *,
int));
+static void unicosmk_unique_section PARAMS ((tree, int));
# undef TARGET_INSERT_ATTRIBUTES
# define TARGET_INSERT_ATTRIBUTES unicosmk_insert_attributes
# undef TARGET_SECTION_TYPE_FLAGS
# define TARGET_SECTION_TYPE_FLAGS unicosmk_section_type_flags
+# undef TARGET_ASM_UNIQUE_SECTION
+# define TARGET_ASM_UNIQUE_SECTION unicosmk_unique_section
+# undef TARGET_ASM_GLOBALIZE_LABEL
+# define TARGET_ASM_GLOBALIZE_LABEL hook_FILEptr_constcharptr_void
#endif
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_DI_OP "\t.align 0\n\t.quad\t"
#endif
+#ifdef OBJECT_FORMAT_ELF
+#undef TARGET_ASM_SELECT_RTX_SECTION
+#define TARGET_ASM_SELECT_RTX_SECTION alpha_elf_select_rtx_section
+#endif
+
#undef TARGET_ASM_FUNCTION_END_PROLOGUE
#define TARGET_ASM_FUNCTION_END_PROLOGUE alpha_output_function_end_prologue
#define TARGET_SCHED_ADJUST_COST alpha_adjust_cost
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE alpha_issue_rate
-#undef TARGET_SCHED_VARIABLE_ISSUE
-#define TARGET_SCHED_VARIABLE_ISSUE alpha_variable_issue
+#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
+#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE \
+ alpha_use_dfa_pipeline_interface
+#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
+ alpha_multipass_dfa_lookahead
+
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS HAVE_AS_TLS
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS alpha_init_builtins
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN alpha_expand_builtin
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL alpha_function_ok_for_sibcall
+#undef TARGET_CANNOT_COPY_INSN_P
+#define TARGET_CANNOT_COPY_INSN_P alpha_cannot_copy_insn_p
+
+#if TARGET_ABI_OSF
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK alpha_output_mi_thunk_osf
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
+#endif
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS alpha_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST hook_int_rtx_0
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG alpha_reorg
struct gcc_target targetm = TARGET_INITIALIZER;
\f
error ("bad value `%s' for -mfp-trap-mode switch", alpha_fptm_string);
}
+ if (alpha_tls_size_string)
+ {
+ if (strcmp (alpha_tls_size_string, "16") == 0)
+ alpha_tls_size = 16;
+ else if (strcmp (alpha_tls_size_string, "32") == 0)
+ alpha_tls_size = 32;
+ else if (strcmp (alpha_tls_size_string, "64") == 0)
+ alpha_tls_size = 64;
+ else
+ error ("bad value `%s' for -mtls-size switch", alpha_tls_size_string);
+ }
+
alpha_cpu
= TARGET_CPU_DEFAULT & MASK_CPU_EV6 ? PROCESSOR_EV6
: (TARGET_CPU_DEFAULT & MASK_CPU_EV5 ? PROCESSOR_EV5 : PROCESSOR_EV4);
/* Register variables and functions with the garbage collector. */
-#if TARGET_ABI_UNICOSMK
/* Set up function hooks. */
init_machine_status = alpha_init_machine_status;
- mark_machine_status = alpha_mark_machine_status;
- free_machine_status = alpha_free_machine_status;
-#endif
+
+ /* Tell the compiler when we're using VAX floating point. */
+ if (TARGET_FLOAT_VAX)
+ {
+ real_format_for_mode[SFmode - QFmode] = &vax_f_format;
+ real_format_for_mode[DFmode - QFmode] = &vax_g_format;
+ real_format_for_mode[TFmode - QFmode] = NULL;
+ }
}
\f
/* Returns 1 if VALUE is a mask that contains full bytes of zero or ones. */
register rtx op;
enum machine_mode mode;
{
- return op == const0_rtx || register_operand (op, mode);
+ return op == CONST0_RTX (mode) || register_operand (op, mode);
}
/* Return 1 if OP is a constant in the range of 0-63 (for a shift) or
|| register_operand (op, mode));
}
+/* Return 1 if OP is a constant or any register. */
+
+int
+reg_or_const_int_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return GET_CODE (op) == CONST_INT || register_operand (op, mode);
+}
+
/* Return 1 if OP is an 8-bit constant. */
int
register rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
-#if HOST_BITS_PER_WIDE_INT == 32
- if (GET_CODE (op) == CONST_DOUBLE)
- return (CONST_DOUBLE_LOW (op) == -1
- && (CONST_DOUBLE_HIGH (op) == -1
- || CONST_DOUBLE_HIGH (op) == 0));
-#else
- if (GET_CODE (op) == CONST_DOUBLE)
- return (CONST_DOUBLE_LOW (op) == -1 && CONST_DOUBLE_HIGH (op) == 0);
-#endif
+ if (GET_CODE (op) == CONST_INT)
+ {
+ HOST_WIDE_INT value = INTVAL (op);
- return (GET_CODE (op) == CONST_INT
- && (INTVAL (op) == 0xff
- || INTVAL (op) == 0xffff
- || INTVAL (op) == (HOST_WIDE_INT)0xffffffff
-#if HOST_BITS_PER_WIDE_INT == 64
- || INTVAL (op) == -1
-#endif
- ));
+ if (value == 0xff)
+ return 1;
+ if (value == 0xffff)
+ return 1;
+ if (value == 0xffffffff)
+ return 1;
+ if (value == -1)
+ return 1;
+ }
+ else if (HOST_BITS_PER_WIDE_INT == 32 && GET_CODE (op) == CONST_DOUBLE)
+ {
+ if (CONST_DOUBLE_LOW (op) == 0xffffffff && CONST_DOUBLE_HIGH (op) == 0)
+ return 1;
+ }
+
+ return 0;
}
/* Return 1 if OP is a multiple of 8 less than 64. */
&& (INTVAL (op) & 7) == 0);
}
-/* Return 1 if OP is the constant zero in floating-point. */
-
-int
-fp0_operand (op, mode)
- register rtx op;
- enum machine_mode mode;
-{
- return (GET_MODE (op) == mode
- && GET_MODE_CLASS (mode) == MODE_FLOAT && op == CONST0_RTX (mode));
-}
-
-/* Return 1 if OP is the floating-point constant zero or a register. */
+/* Return 1 if OP is the zero constant for MODE. */
int
-reg_or_fp0_operand (op, mode)
+const0_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
- return fp0_operand (op, mode) || register_operand (op, mode);
+ return op == CONST0_RTX (mode);
}
/* Return 1 if OP is a hard floating-point register. */
switch (GET_CODE (op))
{
- case REG: case MEM: case CONST_DOUBLE: case CONST_INT: case LABEL_REF:
- case SYMBOL_REF: case CONST: case HIGH:
+ case REG:
+ case MEM:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST_VECTOR:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST:
+ case HIGH:
return 1;
case SUBREG:
symbolic operands to be reconstructed from their high/lo_sum
form. */
return (small_symbolic_operand (op, mode)
- || global_symbolic_operand (op, mode));
+ || global_symbolic_operand (op, mode)
+ || gotdtp_symbolic_operand (op, mode)
+ || gottp_symbolic_operand (op, mode));
}
/* This handles both the Windows/NT and OSF cases. */
&& general_operand (op, mode));
case CONST_DOUBLE:
- return GET_MODE_CLASS (mode) == MODE_FLOAT && op == CONST0_RTX (mode);
+ case CONST_VECTOR:
+ return op == CONST0_RTX (mode);
case CONST_INT:
return mode == QImode || mode == HImode || add_operand (op, mode);
file, and in the same section as the current function. */
int
-current_file_function_operand (op, mode)
+samegp_function_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) != SYMBOL_REF)
- return 0;
+ return false;
/* Easy test for recursion. */
if (op == XEXP (DECL_RTL (current_function_decl), 0))
- return 1;
+ return true;
- /* Otherwise, we need the DECL for the SYMBOL_REF, which we can't get.
- So SYMBOL_REF_FLAG has been declared to imply that the function is
- in the default text section. So we must also check that the current
- function is also in the text section. */
- if (SYMBOL_REF_FLAG (op) && decl_in_text_section (current_function_decl))
- return 1;
+ /* Functions that are not local can be overridden, and thus may
+ not share the same gp. */
+ if (! SYMBOL_REF_LOCAL_P (op))
+ return false;
- return 0;
+ /* If -msmall-data is in effect, assume that there is only one GP
+ for the module, and so any local symbol has this property. We
+ need explicit relocations to be able to enforce this for symbols
+ not defined in this unit of translation, however. */
+ if (TARGET_EXPLICIT_RELOCS && TARGET_SMALL_DATA)
+ return true;
+
+ /* Functions that are not external are defined in this UoT,
+ and thus must share the same gp. */
+ return ! SYMBOL_REF_EXTERNAL_P (op);
}
/* Return 1 if OP is a SYMBOL_REF for which we can make a call via bsr. */
rtx op;
enum machine_mode mode;
{
- /* Must be defined in this file. */
- if (! current_file_function_operand (op, mode))
- return 0;
+ tree op_decl, cfun_sec, op_sec;
+
+ /* Must share the same GP. */
+ if (!samegp_function_operand (op, mode))
+ return false;
/* If profiling is implemented via linker tricks, we can't jump
- to the nogp alternate entry point. */
+ to the nogp alternate entry point. Note that current_function_profile
+ would not be correct, since that doesn't indicate if the target
+ function uses profiling. */
/* ??? TARGET_PROFILING_NEEDS_GP isn't really the right test,
but is approximately correct for the OSF ABIs. Don't know
what to do for VMS, NT, or UMK. */
- if (! TARGET_PROFILING_NEEDS_GP
- && ! current_function_profile)
- return 0;
-
- return 1;
-}
+ if (!TARGET_PROFILING_NEEDS_GP && profile_flag)
+ return false;
-/* Return true if OP is a LABEL_REF, or SYMBOL_REF or CONST referencing
- a variable known to be defined in this file. */
+ /* Must be a function. In some cases folks create thunks in static
+ data structures and then make calls to them. If we allow the
+ direct call, we'll get an error from the linker about !samegp reloc
+ against a symbol without a .prologue directive. */
+ if (!SYMBOL_REF_FUNCTION_P (op))
+ return false;
+
+ /* Must be "near" so that the branch is assumed to reach. With
+ -msmall-text, this is assumed true of all local symbols. Since
+ we've already checked samegp, locality is already assured. */
+ if (TARGET_SMALL_TEXT)
+ return true;
-static bool
-local_symbol_p (op)
- rtx op;
-{
- const char *str = XSTR (op, 0);
+ /* Otherwise, a decl is "near" if it is defined in the same section. */
+ if (flag_function_sections)
+ return false;
- /* ??? SYMBOL_REF_FLAG is set for local function symbols, but we
- run into problems with the rtl inliner in that the symbol was
- once external, but is local after inlining, which results in
- unrecognizable insns. */
+ op_decl = SYMBOL_REF_DECL (op);
+ if (DECL_ONE_ONLY (current_function_decl)
+ || (op_decl && DECL_ONE_ONLY (op_decl)))
+ return false;
- return (CONSTANT_POOL_ADDRESS_P (op)
- /* If @, then ENCODE_SECTION_INFO sez it's local. */
- || str[0] == '@'
- /* If *$, then ASM_GENERATE_INTERNAL_LABEL sez it's local. */
- || (str[0] == '*' && str[1] == '$'));
+ cfun_sec = DECL_SECTION_NAME (current_function_decl);
+ op_sec = op_decl ? DECL_SECTION_NAME (op_decl) : NULL;
+ return ((!cfun_sec && !op_sec)
+ || (cfun_sec && op_sec
+ && strcmp (TREE_STRING_POINTER (cfun_sec),
+ TREE_STRING_POINTER (op_sec)) == 0));
}
+/* Return true if OP is a LABEL_REF, or SYMBOL_REF or CONST referencing
+ a (non-tls) variable known to be defined in this file. */
+
int
local_symbolic_operand (op, mode)
rtx op;
if (GET_CODE (op) != SYMBOL_REF)
return 0;
- return local_symbol_p (op);
+ return SYMBOL_REF_LOCAL_P (op) && !SYMBOL_REF_TLS_MODEL (op);
}
/* Return true if OP is a SYMBOL_REF or CONST referencing a variable
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
- const char *str;
-
if (! TARGET_SMALL_DATA)
return 0;
if (GET_CODE (op) != SYMBOL_REF)
return 0;
+ /* ??? There's no encode_section_info equivalent for the rtl
+ constant pool, so SYMBOL_FLAG_SMALL never gets set. */
if (CONSTANT_POOL_ADDRESS_P (op))
- return GET_MODE_SIZE (get_pool_mode (op)) <= (unsigned) g_switch_value;
- else
- {
- str = XSTR (op, 0);
- return str[0] == '@' && str[1] == 's';
- }
+ return GET_MODE_SIZE (get_pool_mode (op)) <= g_switch_value;
+
+ return (SYMBOL_REF_LOCAL_P (op)
+ && SYMBOL_REF_SMALL_P (op)
+ && SYMBOL_REF_TLS_MODEL (op) == 0);
}
/* Return true if OP is a SYMBOL_REF or CONST referencing a variable
if (GET_CODE (op) != SYMBOL_REF)
return 0;
- return ! local_symbol_p (op);
+ return !SYMBOL_REF_LOCAL_P (op) && !SYMBOL_REF_TLS_MODEL (op);
}
/* Return 1 if OP is a valid operand for the MEM of a CALL insn. */
return 0;
}
+/* Return true if OP is valid for a particular TLS relocation. */
+
+static int
+tls_symbolic_operand_1 (op, mode, size, unspec)
+ rtx op;
+ enum machine_mode mode;
+ int size, unspec;
+{
+ if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ if (GET_CODE (op) != CONST)
+ return 0;
+ op = XEXP (op, 0);
+
+ if (GET_CODE (op) != UNSPEC || XINT (op, 1) != unspec)
+ return 0;
+ op = XVECEXP (op, 0, 0);
+
+ if (GET_CODE (op) != SYMBOL_REF)
+ return 0;
+
+ if (SYMBOL_REF_LOCAL_P (op))
+ {
+ if (alpha_tls_size > size)
+ return 0;
+ }
+ else
+ {
+ if (size != 64)
+ return 0;
+ }
+
+ switch (SYMBOL_REF_TLS_MODEL (op))
+ {
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ return unspec == UNSPEC_DTPREL;
+ case TLS_MODEL_INITIAL_EXEC:
+ return unspec == UNSPEC_TPREL && size == 64;
+ case TLS_MODEL_LOCAL_EXEC:
+ return unspec == UNSPEC_TPREL;
+ default:
+ abort ();
+ }
+}
+
+/* Return true if OP is valid for 16-bit DTP relative relocations. */
+
+int
+dtp16_symbolic_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return tls_symbolic_operand_1 (op, mode, 16, UNSPEC_DTPREL);
+}
+
+/* Return true if OP is valid for 32-bit DTP relative relocations. */
+
+int
+dtp32_symbolic_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return tls_symbolic_operand_1 (op, mode, 32, UNSPEC_DTPREL);
+}
+
+/* Return true if OP is valid for 64-bit DTP relative relocations. */
+
+int
+gotdtp_symbolic_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return tls_symbolic_operand_1 (op, mode, 64, UNSPEC_DTPREL);
+}
+
+/* Return true if OP is valid for 16-bit TP relative relocations. */
+
+int
+tp16_symbolic_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return tls_symbolic_operand_1 (op, mode, 16, UNSPEC_TPREL);
+}
+
+/* Return true if OP is valid for 32-bit TP relative relocations. */
+
+int
+tp32_symbolic_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return tls_symbolic_operand_1 (op, mode, 32, UNSPEC_TPREL);
+}
+
+/* Return true if OP is valid for 64-bit TP relative relocations. */
+
+int
+gottp_symbolic_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return tls_symbolic_operand_1 (op, mode, 64, UNSPEC_TPREL);
+}
+
/* Return 1 if OP is a valid Alpha comparison operator. Here we know which
comparisons are valid in which insn. */
return GET_CODE (value) == HIGH;
case 'U':
return TARGET_ABI_UNICOSMK && symbolic_operand (value, VOIDmode);
-
+ case 'W':
+ return (GET_CODE (value) == CONST_VECTOR
+ && value == CONST0_RTX (GET_MODE (value)));
default:
return false;
}
return best_label ? best_label : const0_rtx;
}
-\f
-/* Return true if the function DECL will be placed in the default text
- section. */
-/* ??? Ideally we'd be able to always move from a SYMBOL_REF back to the
- decl, as that would allow us to determine if two functions are in the
- same section, which is what we really want to know. */
-static bool
-decl_in_text_section (decl)
- tree decl;
+/* Return the TLS model to use for SYMBOL. */
+
+static enum tls_model
+tls_symbolic_operand_type (symbol)
+ rtx symbol;
{
- return (DECL_SECTION_NAME (decl) == NULL_TREE
- && ! (flag_function_sections
- || (targetm.have_named_sections
- && DECL_ONE_ONLY (decl))));
-}
+ enum tls_model model;
-/* If we are referencing a function that is static, make the SYMBOL_REF
- special. We use this to see indicate we can branch to this function
- without setting PV or restoring GP.
+ if (GET_CODE (symbol) != SYMBOL_REF)
+ return 0;
+ model = SYMBOL_REF_TLS_MODEL (symbol);
- If this is a variable that is known to be defined locally, add "@v"
- to the name. If in addition the variable is to go in .sdata/.sbss,
- then add "@s" instead. */
+ /* Local-exec with a 64-bit size is the same code as initial-exec. */
+ if (model == TLS_MODEL_LOCAL_EXEC && alpha_tls_size == 64)
+ model = TLS_MODEL_INITIAL_EXEC;
-void
-alpha_encode_section_info (decl, first)
+ return model;
+}
+\f
+/* Return true if the function DECL will share the same GP as any
+ function in the current unit of translation. */
+
+static bool
+decl_has_samegp (decl)
tree decl;
- int first ATTRIBUTE_UNUSED;
{
- const char *symbol_str;
- bool is_local, is_small;
-
- if (TREE_CODE (decl) == FUNCTION_DECL)
- {
- /* We mark public functions once they are emitted; otherwise we
- don't know that they exist in this unit of translation. */
- if (TREE_PUBLIC (decl))
- return;
-
- /* Do not mark functions that are not in .text; otherwise we
- don't know that they are near enough for a direct branch. */
- if (! decl_in_text_section (decl))
- return;
+ /* Functions that are not local can be overridden, and thus may
+ not share the same gp. */
+ if (!(*targetm.binds_local_p) (decl))
+ return false;
- SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
- return;
- }
+ /* If -msmall-data is in effect, assume that there is only one GP
+ for the module, and so any local symbol has this property. We
+ need explicit relocations to be able to enforce this for symbols
+ not defined in this unit of translation, however. */
+ if (TARGET_EXPLICIT_RELOCS && TARGET_SMALL_DATA)
+ return true;
- /* Early out if we're not going to do anything with this data. */
- if (! TARGET_EXPLICIT_RELOCS)
- return;
+ /* Functions that are not external are defined in this UoT. */
+ /* ??? Irritatingly, static functions not yet emitted are still
+ marked "external". Apply this to non-static functions only. */
+ return !TREE_PUBLIC (decl) || !DECL_EXTERNAL (decl);
+}
- /* Careful not to prod global register variables. */
- if (TREE_CODE (decl) != VAR_DECL
- || GET_CODE (DECL_RTL (decl)) != MEM
- || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF)
- return;
-
- symbol_str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
-
- /* A variable is considered "local" if it is defined in this module. */
-
- /* Local binding occurs for any non-default visibility. */
- if (MODULE_LOCAL_P (decl))
- is_local = true;
- /* Otherwise, variables defined outside this object may not be local. */
- else if (DECL_EXTERNAL (decl))
- is_local = false;
- /* Linkonce and weak data is never local. */
- else if (DECL_ONE_ONLY (decl) || DECL_WEAK (decl))
- is_local = false;
- /* Static variables are always local. */
- else if (! TREE_PUBLIC (decl))
- is_local = true;
- /* If PIC, then assume that any global name can be overridden by
- symbols resolved from other modules. */
- else if (flag_pic)
- is_local = false;
- /* Uninitialized COMMON variable may be unified with symbols
- resolved from other modules. */
- else if (DECL_COMMON (decl)
- && (DECL_INITIAL (decl) == NULL
- || DECL_INITIAL (decl) == error_mark_node))
- is_local = false;
- /* Otherwise we're left with initialized (or non-common) global data
- which is of necessity defined locally. */
- else
- is_local = true;
+/* Return true if EXP should be placed in the small data section. */
- /* Determine if DECL will wind up in .sdata/.sbss. */
+static bool
+alpha_in_small_data_p (exp)
+ tree exp;
+{
+ /* We want to merge strings, so we never consider them small data. */
+ if (TREE_CODE (exp) == STRING_CST)
+ return false;
- is_small = false;
- if (DECL_SECTION_NAME (decl))
+ if (TREE_CODE (exp) == VAR_DECL && DECL_SECTION_NAME (exp))
{
- const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+ const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (exp));
if (strcmp (section, ".sdata") == 0
|| strcmp (section, ".sbss") == 0)
- is_small = true;
+ return true;
}
else
{
- HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl));
+ HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp));
- /* If the variable has already been defined in the output file, then it
- is too late to put it in sdata if it wasn't put there in the first
- place. The test is here rather than above, because if it is already
- in sdata, then it can stay there. */
-
- if (TREE_ASM_WRITTEN (decl))
- ;
-
- /* If this is an incomplete type with size 0, then we can't put it in
- sdata because it might be too big when completed. */
- else if (size > 0 && size <= g_switch_value)
- is_small = true;
+ /* If this is an incomplete type with size 0, then we can't put it
+ in sdata because it might be too big when completed. */
+ if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value)
+ return true;
}
- /* Finally, encode this into the symbol string. */
- if (is_local)
- {
- const char *string;
- char *newstr;
- size_t len;
+ return false;
+}
- if (symbol_str[0] == '@')
- {
- if (symbol_str[1] == (is_small ? 's' : 'v'))
- return;
- symbol_str += 2;
- }
+#if TARGET_ABI_OPEN_VMS
+static bool
+alpha_linkage_symbol_p (symname)
+ const char *symname;
+{
+ int symlen = strlen (symname);
- len = strlen (symbol_str) + 1;
- newstr = alloca (len + 2);
+ if (symlen > 4)
+ return strcmp (&symname [symlen - 4], "..lk") == 0;
- newstr[0] = '@';
- newstr[1] = (is_small ? 's' : 'v');
- memcpy (newstr + 2, symbol_str, len);
-
- string = ggc_alloc_string (newstr, len + 2 - 1);
- XSTR (XEXP (DECL_RTL (decl), 0), 0) = string;
- }
- else if (symbol_str[0] == '@')
- abort ();
+ return false;
}
+#define LINKAGE_SYMBOL_REF_P(X) \
+ ((GET_CODE (X) == SYMBOL_REF \
+ && alpha_linkage_symbol_p (XSTR (X, 0))) \
+ || (GET_CODE (X) == CONST \
+ && GET_CODE (XEXP (X, 0)) == PLUS \
+ && GET_CODE (XEXP (XEXP (X, 0), 0)) == SYMBOL_REF \
+ && alpha_linkage_symbol_p (XSTR (XEXP (XEXP (X, 0), 0), 0))))
+#endif
+
/* legitimate_address_p recognizes an RTL expression that is a valid
memory address for an instruction. The MODE argument is the
machine mode for the MEM expression that wants to use this address.
if (CONSTANT_ADDRESS_P (x))
return true;
+#if TARGET_ABI_OPEN_VMS
+ if (LINKAGE_SYMBOL_REF_P (x))
+ return true;
+#endif
+
/* Register plus a small constant offset is valid. */
if (GET_CODE (x) == PLUS)
{
return false;
/* The symbol must be local. */
- if (local_symbolic_operand (ofs, Pmode))
+ if (local_symbolic_operand (ofs, Pmode)
+ || dtp32_symbolic_operand (ofs, Pmode)
+ || tp32_symbolic_operand (ofs, Pmode))
return true;
}
}
return false;
}
+/* Build the SYMBOL_REF for __tls_get_addr. */
+
+static GTY(()) rtx tls_get_addr_libfunc;
+
+static rtx
+get_tls_get_addr ()
+{
+ if (!tls_get_addr_libfunc)
+ tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr");
+ return tls_get_addr_libfunc;
+}
+
/* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address. */
/* If this is a local symbol, split the address into HIGH/LO_SUM parts. */
if (TARGET_EXPLICIT_RELOCS && symbolic_operand (x, Pmode))
{
- if (local_symbolic_operand (x, Pmode))
+ rtx r0, r16, eqv, tga, tp, insn, dest, seq;
+
+ switch (tls_symbolic_operand_type (x))
{
- if (small_symbolic_operand (x, Pmode))
- return x;
- else
- {
- if (!no_new_pseudos)
- scratch = gen_reg_rtx (Pmode);
- emit_insn (gen_rtx_SET (VOIDmode, scratch,
- gen_rtx_HIGH (Pmode, x)));
- return gen_rtx_LO_SUM (Pmode, scratch, x);
- }
- }
- }
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ start_sequence ();
+
+ r0 = gen_rtx_REG (Pmode, 0);
+ r16 = gen_rtx_REG (Pmode, 16);
+ tga = get_tls_get_addr ();
+ dest = gen_reg_rtx (Pmode);
+ seq = GEN_INT (alpha_next_sequence_number++);
+
+ emit_insn (gen_movdi_er_tlsgd (r16, pic_offset_table_rtx, x, seq));
+ insn = gen_call_value_osf_tlsgd (r0, tga, seq);
+ insn = emit_call_insn (insn);
+ CONST_OR_PURE_CALL_P (insn) = 1;
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16);
- return NULL;
+ insn = get_insns ();
+ end_sequence ();
+
+ emit_libcall_block (insn, dest, r0, x);
+ return dest;
+
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ start_sequence ();
+
+ r0 = gen_rtx_REG (Pmode, 0);
+ r16 = gen_rtx_REG (Pmode, 16);
+ tga = get_tls_get_addr ();
+ scratch = gen_reg_rtx (Pmode);
+ seq = GEN_INT (alpha_next_sequence_number++);
+
+ emit_insn (gen_movdi_er_tlsldm (r16, pic_offset_table_rtx, seq));
+ insn = gen_call_value_osf_tlsldm (r0, tga, seq);
+ insn = emit_call_insn (insn);
+ CONST_OR_PURE_CALL_P (insn) = 1;
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16);
+
+ insn = get_insns ();
+ end_sequence ();
+
+ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
+ UNSPEC_TLSLDM_CALL);
+ emit_libcall_block (insn, scratch, r0, eqv);
+
+ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_DTPREL);
+ eqv = gen_rtx_CONST (Pmode, eqv);
+
+ if (alpha_tls_size == 64)
+ {
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_rtx_SET (VOIDmode, dest, eqv));
+ emit_insn (gen_adddi3 (dest, dest, scratch));
+ return dest;
+ }
+ if (alpha_tls_size == 32)
+ {
+ insn = gen_rtx_HIGH (Pmode, eqv);
+ insn = gen_rtx_PLUS (Pmode, scratch, insn);
+ scratch = gen_reg_rtx (Pmode);
+ emit_insn (gen_rtx_SET (VOIDmode, scratch, insn));
+ }
+ return gen_rtx_LO_SUM (Pmode, scratch, eqv);
+
+ case TLS_MODEL_INITIAL_EXEC:
+ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPREL);
+ eqv = gen_rtx_CONST (Pmode, eqv);
+ tp = gen_reg_rtx (Pmode);
+ scratch = gen_reg_rtx (Pmode);
+ dest = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_load_tp (tp));
+ emit_insn (gen_rtx_SET (VOIDmode, scratch, eqv));
+ emit_insn (gen_adddi3 (dest, tp, scratch));
+ return dest;
+
+ case TLS_MODEL_LOCAL_EXEC:
+ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPREL);
+ eqv = gen_rtx_CONST (Pmode, eqv);
+ tp = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_load_tp (tp));
+ if (alpha_tls_size == 32)
+ {
+ insn = gen_rtx_HIGH (Pmode, eqv);
+ insn = gen_rtx_PLUS (Pmode, tp, insn);
+ tp = gen_reg_rtx (Pmode);
+ emit_insn (gen_rtx_SET (VOIDmode, tp, insn));
+ }
+ return gen_rtx_LO_SUM (Pmode, tp, eqv);
+ }
+
+ if (local_symbolic_operand (x, Pmode))
+ {
+ if (small_symbolic_operand (x, Pmode))
+ return x;
+ else
+ {
+ if (!no_new_pseudos)
+ scratch = gen_reg_rtx (Pmode);
+ emit_insn (gen_rtx_SET (VOIDmode, scratch,
+ gen_rtx_HIGH (Pmode, x)));
+ return gen_rtx_LO_SUM (Pmode, scratch, x);
+ }
+ }
+ }
+
+ return NULL;
split_addend:
{
}
}
+/* We do not allow indirect calls to be optimized into sibling calls, nor
+ can we allow a call to a function with a different GP to be optimized
+ into a sibcall. */
+
+static bool
+alpha_function_ok_for_sibcall (decl, exp)
+ tree decl;
+ tree exp ATTRIBUTE_UNUSED;
+{
+ /* Can't do indirect tail calls, since we don't know if the target
+ uses the same GP. */
+ if (!decl)
+ return false;
+
+ /* Otherwise, we can make a tail call if the target function shares
+ the same GP. */
+ return decl_has_samegp (decl);
+}
+
/* For TARGET_EXPLICIT_RELOCS, we don't obfuscate a SYMBOL_REF to a
small symbolic operand until after reload. At which point we need
to replace (mem (symbol_ref)) with (mem (lo_sum $29 symbol_ref))
return 0;
}
+/* Indicate that INSN cannot be duplicated. This is true for any insn
+ that we've marked with gpdisp relocs, since those have to stay in
+ 1-1 correspondence with one another.
+
+ Techinically we could copy them if we could set up a mapping from one
+ sequence number to another, across the set of insns to be duplicated.
+ This seems overly complicated and error-prone since interblock motion
+ from sched-ebb could move one of the pair of insns to a different block. */
+
+static bool
+alpha_cannot_copy_insn_p (insn)
+ rtx insn;
+{
+ rtx pat;
+
+ if (!reload_completed || !TARGET_EXPLICIT_RELOCS)
+ return false;
+
+ if (GET_CODE (insn) != INSN)
+ return false;
+ if (asm_noperands (insn) >= 0)
+ return false;
+
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) != SET)
+ return false;
+ pat = SET_SRC (pat);
+ if (GET_CODE (pat) == UNSPEC_VOLATILE)
+ {
+ if (XINT (pat, 1) == UNSPECV_LDGP1
+ || XINT (pat, 1) == UNSPECV_PLDGP2)
+ return true;
+ }
+ else if (GET_CODE (pat) == UNSPEC)
+ {
+ if (XINT (pat, 1) == UNSPEC_LDGP2)
+ return true;
+ }
+
+ return false;
+}
+
+
/* Try a machine-dependent way of reloading an illegitimate address
operand. If we find one, push the reload and return the new rtx. */
return NULL_RTX;
}
\f
+/* Compute a (partial) cost for rtx X. Return true if the complete
+ cost has been computed, and false if subexpressions should be
+ scanned. In either case, *TOTAL contains the cost result. */
+
+static bool
+alpha_rtx_costs (x, code, outer_code, total)
+ rtx x;
+ int code, outer_code;
+ int *total;
+{
+ enum machine_mode mode = GET_MODE (x);
+ bool float_mode_p = FLOAT_MODE_P (mode);
+
+ switch (code)
+ {
+ /* If this is an 8-bit constant, return zero since it can be used
+ nearly anywhere with no cost. If it is a valid operand for an
+ ADD or AND, likewise return 0 if we know it will be used in that
+ context. Otherwise, return 2 since it might be used there later.
+ All other constants take at least two insns. */
+ case CONST_INT:
+ if (INTVAL (x) >= 0 && INTVAL (x) < 256)
+ {
+ *total = 0;
+ return true;
+ }
+ /* FALLTHRU */
+
+ case CONST_DOUBLE:
+ if (x == CONST0_RTX (mode))
+ *total = 0;
+ else if ((outer_code == PLUS && add_operand (x, VOIDmode))
+ || (outer_code == AND && and_operand (x, VOIDmode)))
+ *total = 0;
+ else if (add_operand (x, VOIDmode) || and_operand (x, VOIDmode))
+ *total = 2;
+ else
+ *total = COSTS_N_INSNS (2);
+ return true;
+
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ if (TARGET_EXPLICIT_RELOCS && small_symbolic_operand (x, VOIDmode))
+ *total = COSTS_N_INSNS (outer_code != MEM);
+ else if (TARGET_EXPLICIT_RELOCS && local_symbolic_operand (x, VOIDmode))
+ *total = COSTS_N_INSNS (1 + (outer_code != MEM));
+ else if (tls_symbolic_operand_type (x))
+ /* Estimate of cost for call_pal rduniq. */
+ *total = COSTS_N_INSNS (15);
+ else
+ /* Otherwise we do a load from the GOT. */
+ *total = COSTS_N_INSNS (alpha_memory_latency);
+ return true;
+
+ case PLUS:
+ case MINUS:
+ if (float_mode_p)
+ *total = alpha_rtx_cost_data[alpha_cpu].fp_add;
+ else if (GET_CODE (XEXP (x, 0)) == MULT
+ && const48_operand (XEXP (XEXP (x, 0), 1), VOIDmode))
+ {
+ *total = (rtx_cost (XEXP (XEXP (x, 0), 0), outer_code)
+ + rtx_cost (XEXP (x, 1), outer_code) + 2);
+ return true;
+ }
+ return false;
+
+ case MULT:
+ if (float_mode_p)
+ *total = alpha_rtx_cost_data[alpha_cpu].fp_mult;
+ else if (mode == DImode)
+ *total = alpha_rtx_cost_data[alpha_cpu].int_mult_di;
+ else
+ *total = alpha_rtx_cost_data[alpha_cpu].int_mult_si;
+ return false;
+
+ case ASHIFT:
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) <= 3)
+ {
+ *total = COSTS_N_INSNS (1);
+ return false;
+ }
+ /* FALLTHRU */
+
+ case ASHIFTRT:
+ case LSHIFTRT:
+ *total = alpha_rtx_cost_data[alpha_cpu].int_shift;
+ return false;
+
+ case IF_THEN_ELSE:
+ if (float_mode_p)
+ *total = alpha_rtx_cost_data[alpha_cpu].fp_add;
+ else
+ *total = alpha_rtx_cost_data[alpha_cpu].int_cmov;
+ return false;
+
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ if (!float_mode_p)
+ *total = COSTS_N_INSNS (70); /* ??? */
+ else if (mode == SFmode)
+ *total = alpha_rtx_cost_data[alpha_cpu].fp_div_sf;
+ else
+ *total = alpha_rtx_cost_data[alpha_cpu].fp_div_df;
+ return false;
+
+ case MEM:
+ *total = COSTS_N_INSNS (alpha_memory_latency);
+ return true;
+
+ case NEG:
+ if (! float_mode_p)
+ {
+ *total = COSTS_N_INSNS (1);
+ return false;
+ }
+ /* FALLTHRU */
+
+ case ABS:
+ if (! float_mode_p)
+ {
+ *total = COSTS_N_INSNS (1) + alpha_rtx_cost_data[alpha_cpu].int_cmov;
+ return false;
+ }
+ /* FALLTHRU */
+
+ case FLOAT:
+ case UNSIGNED_FLOAT:
+ case FIX:
+ case UNSIGNED_FIX:
+ case FLOAT_EXTEND:
+ case FLOAT_TRUNCATE:
+ *total = alpha_rtx_cost_data[alpha_cpu].fp_add;
+ return false;
+
+ default:
+ return false;
+ }
+}
+\f
/* REF is an alignable memory location. Place an aligned SImode
reference into *PALIGNED_MEM and the number of bits to shift into
*PBITNUM. SCRATCH is a free register for use in reloading out
switch (GET_CODE (x))
{
case SEQUENCE:
+ abort ();
+
case PARALLEL:
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
alpha_set_memflags_1 (XVECEXP (x, 0, i), in_struct_p, volatile_p,
}
}
-/* Given INSN, which is either an INSN or a SEQUENCE generated to
- perform a memory operation, look for any MEMs in either a SET_DEST or
- a SET_SRC and copy the in-struct, unchanging, and volatile flags from
- REF into each of the MEMs found. If REF is not a MEM, don't do
- anything. */
+/* Given INSN, which is an INSN list or the PATTERN of a single insn
+ generated to perform a memory operation, look for any MEMs in either
+ a SET_DEST or a SET_SRC and copy the in-struct, unchanging, and
+ volatile flags from REF into each of the MEMs found. If REF is not
+ a MEM, don't do anything. */
void
alpha_set_memflags (insn, ref)
/* Allow legitimize_address to perform some simplifications. */
if (mode == Pmode && symbolic_operand (operands[1], mode))
{
- rtx tmp = alpha_legitimize_address (operands[1], operands[0], mode);
+ rtx tmp;
+
+ /* With RTL inlining, at -O3, rtl is generated, stored, then actually
+ compiled at the end of compilation. In the meantime, someone can
+ re-encode-section-info on some symbol changing it e.g. from global
+ to local-not-small. If this happens, we'd have emitted a plain
+ load rather than a high+losum load and not recognize the insn.
+
+ So if rtl inlining is in effect, we delay the global/not-global
+ decision until rest_of_compilation by wrapping it in an
+ UNSPEC_SYMBOL. */
+ if (TARGET_EXPLICIT_RELOCS && flag_inline_functions
+ && rtx_equal_function_value_matters
+ && global_symbolic_operand (operands[1], mode))
+ {
+ emit_insn (gen_movdi_er_maybe_g (operands[0], operands[1]));
+ return true;
+ }
+
+ tmp = alpha_legitimize_address (operands[1], operands[0], mode);
if (tmp)
{
+ if (tmp == operands[0])
+ return true;
operands[1] = tmp;
return false;
}
}
/* Otherwise we've nothing left but to drop the thing to memory. */
- operands[1] = force_const_mem (DImode, operands[1]);
+ operands[1] = force_const_mem (mode, operands[1]);
if (reload_in_progress)
{
emit_move_insn (operands[0], XEXP (operands[1], 0));
}
else
{
- /* ??? We mark the the branch mode to be CCmode to prevent the
+ /* ??? We mark the branch mode to be CCmode to prevent the
compare and branch from being combined, since the compare
insn follows IEEE rules that the branch does not. */
branch_mode = CCmode;
/* 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
+ If both of the operands that set cc0 are nonzero we must emit
an insn to perform the compare (it can't be done within
the conditional move). */
rtx
/* If we have fp<->int register move instructions, do a cmov by
performing the comparison in fp registers, and move the
- zero/non-zero value to integer registers, where we can then
+ zero/nonzero value to integer registers, where we can then
use a normal cmov, or vice-versa. */
switch (code)
abort ();
}
- tmp = gen_rtx_MEM (QImode, gen_rtx_SYMBOL_REF (Pmode, (char *) func));
+ tmp = gen_rtx_MEM (QImode, init_one_libfunc (func));
tmp = emit_call_insn (GEN_CALL_VALUE (reg, tmp, const0_rtx,
const0_rtx, const0_rtx));
CALL_INSN_FUNCTION_USAGE (tmp) = usage;
alpha_split_tfmode_pair (operands);
- /* Detect three flavours of operand overlap. */
+ /* Detect three flavors of operand overlap. */
move = 1;
if (rtx_equal_p (operands[0], operands[2]))
move = 0;
emit_insn (gen_mskxl_be (dsth, dsth, GEN_INT (0xffff), addr));
break;
case 4:
- emit_insn (gen_mskxl_be (dsth, dsth, GEN_INT (0xffffffff), addr));
- break;
- case 8:
{
-#if HOST_BITS_PER_WIDE_INT == 32
- rtx msk = immed_double_const (0xffffffff, 0xffffffff, DImode);
-#else
- rtx msk = constm1_rtx;
-#endif
+ rtx msk = immed_double_const (0xffffffff, 0, DImode);
emit_insn (gen_mskxl_be (dsth, dsth, msk, addr));
+ break;
}
+ case 8:
+ emit_insn (gen_mskxl_be (dsth, dsth, constm1_rtx, addr));
break;
}
emit_insn (gen_mskxl_le (dstl, dstl, GEN_INT (0xffff), addr));
break;
case 4:
- emit_insn (gen_mskxl_le (dstl, dstl, GEN_INT (0xffffffff), addr));
- break;
- case 8:
{
-#if HOST_BITS_PER_WIDE_INT == 32
- rtx msk = immed_double_const (0xffffffff, 0xffffffff, DImode);
-#else
- rtx msk = constm1_rtx;
-#endif
+ rtx msk = immed_double_const (0xffffffff, 0, DImode);
emit_insn (gen_mskxl_le (dstl, dstl, msk, addr));
+ break;
}
+ case 8:
+ emit_insn (gen_mskxl_le (dstl, dstl, constm1_rtx, addr));
break;
}
}
{
rtx const im8 = GEN_INT (-8);
rtx const i64 = GEN_INT (64);
-#if HOST_BITS_PER_WIDE_INT == 32
- rtx const im1 = immed_double_const (0xffffffff, 0xffffffff, DImode);
-#else
- rtx const im1 = constm1_rtx;
-#endif
rtx ins_tmps[MAX_MOVE_WORDS];
rtx st_tmp_1, st_tmp_2, dreg;
rtx st_addr_1, st_addr_2, dmema;
/* Split and merge the ends with the destination data. */
if (WORDS_BIG_ENDIAN)
{
- emit_insn (gen_mskxl_be (st_tmp_2, st_tmp_2, im1, dreg));
+ emit_insn (gen_mskxl_be (st_tmp_2, st_tmp_2, constm1_rtx, dreg));
emit_insn (gen_mskxh (st_tmp_1, st_tmp_1, i64, dreg));
}
else
{
emit_insn (gen_mskxh (st_tmp_2, st_tmp_2, i64, dreg));
- emit_insn (gen_mskxl_le (st_tmp_1, st_tmp_1, im1, dreg));
+ emit_insn (gen_mskxl_le (st_tmp_1, st_tmp_1, constm1_rtx, dreg));
}
if (data_regs != NULL)
return 1;
}
+
+/* Returns a mask so that zap(x, value) == x & mask. */
+
+rtx
+alpha_expand_zap_mask (value)
+ HOST_WIDE_INT value;
+{
+ rtx result;
+ int i;
+
+ if (HOST_BITS_PER_WIDE_INT >= 64)
+ {
+ HOST_WIDE_INT mask = 0;
+
+ for (i = 7; i >= 0; --i)
+ {
+ mask <<= 8;
+ if (!((value >> i) & 1))
+ mask |= 0xff;
+ }
+
+ result = gen_int_mode (mask, DImode);
+ }
+ else if (HOST_BITS_PER_WIDE_INT == 32)
+ {
+ HOST_WIDE_INT mask_lo = 0, mask_hi = 0;
+
+ for (i = 7; i >= 4; --i)
+ {
+ mask_hi <<= 8;
+ if (!((value >> i) & 1))
+ mask_hi |= 0xff;
+ }
+
+ for (i = 3; i >= 0; --i)
+ {
+ mask_lo <<= 8;
+ if (!((value >> i) & 1))
+ mask_lo |= 0xff;
+ }
+
+ result = immed_double_const (mask_lo, mask_hi, DImode);
+ }
+ else
+ abort ();
+
+ return result;
+}
+
+void
+alpha_expand_builtin_vector_binop (gen, mode, op0, op1, op2)
+ rtx (*gen) PARAMS ((rtx, rtx, rtx));
+ enum machine_mode mode;
+ rtx op0, op1, op2;
+{
+ op0 = gen_lowpart (mode, op0);
+
+ if (op1 == const0_rtx)
+ op1 = CONST0_RTX (mode);
+ else
+ op1 = gen_lowpart (mode, op1);
+
+ if (op2 == const0_rtx)
+ op2 = CONST0_RTX (mode);
+ else
+ op2 = gen_lowpart (mode, op2);
+
+ emit_insn ((*gen) (op0, op1, op2));
+}
\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. */
rtx dep_insn;
int cost;
{
- rtx set, set_src;
enum attr_type insn_type, dep_insn_type;
/* If the dependence is an anti-dependence, there is no cost. For an
output dependence, there is sometimes a cost, but it doesn't seem
worth handling those few cases. */
-
if (REG_NOTE_KIND (link) != 0)
- return 0;
+ return cost;
/* If we can't recognize the insns, we can't really do anything. */
if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
|| dep_insn_type == TYPE_LDSYM)
cost += alpha_memory_latency-1;
- switch (alpha_cpu)
- {
- case PROCESSOR_EV4:
- /* On EV4, if INSN is a store insn and DEP_INSN is setting the data
- being stored, we can sometimes lower the cost. */
-
- if ((insn_type == TYPE_IST || insn_type == TYPE_FST)
- && (set = single_set (dep_insn)) != 0
- && GET_CODE (PATTERN (insn)) == SET
- && rtx_equal_p (SET_DEST (set), SET_SRC (PATTERN (insn))))
- {
- switch (dep_insn_type)
- {
- case TYPE_ILD:
- case TYPE_FLD:
- /* No savings here. */
- return cost;
-
- case TYPE_IMUL:
- /* In these cases, we save one cycle. */
- return cost - 1;
-
- default:
- /* In all other cases, we save two cycles. */
- return MAX (0, cost - 2);
- }
- }
-
- /* Another case that needs adjustment is an arithmetic or logical
- operation. It's cost is usually one cycle, but we default it to
- two in the MD file. The only case that it is actually two is
- for the address in loads, stores, and jumps. */
-
- if (dep_insn_type == TYPE_IADD || dep_insn_type == TYPE_ILOG)
- {
- switch (insn_type)
- {
- case TYPE_ILD:
- case TYPE_IST:
- case TYPE_FLD:
- case TYPE_FST:
- case TYPE_JSR:
- return cost;
- default:
- return 1;
- }
- }
-
- /* The final case is when a compare feeds into an integer branch;
- the cost is only one cycle in that case. */
-
- if (dep_insn_type == TYPE_ICMP && insn_type == TYPE_IBR)
- return 1;
- break;
-
- case PROCESSOR_EV5:
- /* And the lord DEC saith: "A special bypass provides an effective
- latency of 0 cycles for an ICMP or ILOG insn producing the test
- operand of an IBR or ICMOV insn." */
-
- if ((dep_insn_type == TYPE_ICMP || dep_insn_type == TYPE_ILOG)
- && (set = single_set (dep_insn)) != 0)
- {
- /* A branch only has one input. This must be it. */
- if (insn_type == TYPE_IBR)
- return 0;
- /* A conditional move has three, make sure it is the test. */
- if (insn_type == TYPE_ICMOV
- && GET_CODE (set_src = PATTERN (insn)) == SET
- && GET_CODE (set_src = SET_SRC (set_src)) == IF_THEN_ELSE
- && rtx_equal_p (SET_DEST (set), XEXP (set_src, 0)))
- return 0;
- }
-
- /* "The multiplier is unable to receive data from IEU bypass paths.
- The instruction issues at the expected time, but its latency is
- increased by the time it takes for the input data to become
- available to the multiplier" -- which happens in pipeline stage
- six, when results are comitted to the register file. */
-
- if (insn_type == TYPE_IMUL)
- {
- switch (dep_insn_type)
- {
- /* These insns produce their results in pipeline stage five. */
- case TYPE_ILD:
- case TYPE_ICMOV:
- case TYPE_IMUL:
- case TYPE_MVI:
- return cost + 1;
-
- /* Other integer insns produce results in pipeline stage four. */
- default:
- return cost + 2;
- }
- }
- break;
-
- case PROCESSOR_EV6:
- /* There is additional latency to move the result of (most) FP
- operations anywhere but the FP register file. */
-
- if ((insn_type == TYPE_FST || insn_type == TYPE_FTOI)
- && (dep_insn_type == TYPE_FADD ||
- dep_insn_type == TYPE_FMUL ||
- dep_insn_type == TYPE_FCMOV))
- return cost + 2;
+ /* Everything else handled in DFA bypasses now. */
- break;
- }
-
- /* Otherwise, return the default cost. */
return cost;
}
-/* Function to initialize the issue rate used by the scheduler. */
+/* The number of instructions that can be issued per cycle. */
+
static int
alpha_issue_rate ()
{
}
static int
-alpha_variable_issue (dump, verbose, insn, cim)
- FILE *dump ATTRIBUTE_UNUSED;
- int verbose ATTRIBUTE_UNUSED;
- rtx insn;
- int cim;
+alpha_use_dfa_pipeline_interface ()
{
- if (recog_memoized (insn) < 0 || get_attr_type (insn) == TYPE_MULTI)
- return 0;
-
- return cim - 1;
+ return true;
}
-\f
-/* Register global variables and machine-specific functions with the
- garbage collector. */
+/* How many alternative schedules to try. This should be as wide as the
+ scheduling freedom in the DFA, but no wider. Making this value too
+ large results extra work for the scheduler.
-#if TARGET_ABI_UNICOSMK
-static void
-alpha_init_machine_status (p)
- struct function *p;
-{
- p->machine =
- (struct machine_function *) xcalloc (1, sizeof (struct machine_function));
+ For EV4, loads can be issued to either IB0 or IB1, thus we have 2
+ alternative schedules. For EV5, we can choose between E0/E1 and
+ FA/FM. For EV6, an arithmatic insn can be issued to U0/U1/L0/L1. */
- p->machine->first_ciw = NULL_RTX;
- p->machine->last_ciw = NULL_RTX;
- p->machine->ciw_count = 0;
- p->machine->addr_list = NULL_RTX;
+static int
+alpha_multipass_dfa_lookahead ()
+{
+ return (alpha_cpu == PROCESSOR_EV6 ? 4 : 2);
}
+\f
+/* Machine-specific function data. */
-static void
-alpha_mark_machine_status (p)
- struct function *p;
+struct machine_function GTY(())
{
- struct machine_function *machine = p->machine;
+ /* For unicosmk. */
+ /* List of call information words for calls from this function. */
+ struct rtx_def *first_ciw;
+ struct rtx_def *last_ciw;
+ int ciw_count;
- if (machine)
- {
- ggc_mark_rtx (machine->first_ciw);
- ggc_mark_rtx (machine->addr_list);
- }
-}
+ /* List of deferred case vectors. */
+ struct rtx_def *addr_list;
-static void
-alpha_free_machine_status (p)
- struct function *p;
+ /* For OSF. */
+ const char *some_ld_name;
+};
+
+/* How to allocate a 'struct machine_function'. */
+
+static struct machine_function *
+alpha_init_machine_status ()
{
- free (p->machine);
- p->machine = NULL;
+ return ((struct machine_function *)
+ ggc_alloc_cleared (sizeof (struct machine_function)));
}
-#endif /* TARGET_ABI_UNICOSMK */
/* Functions to save and restore alpha_return_addr_rtx. */
{
rtx r = get_hard_reg_initial_val (DImode, 29);
if (GET_CODE (r) != MEM)
- r = gen_mem_addressof (r, NULL_TREE);
+ r = gen_mem_addressof (r, NULL_TREE, /*rescan=*/true);
return r;
}
abort ();
}
+/* Locate some local-dynamic symbol still in use by this function
+ so that we can print its name in some movdi_er_tlsldm pattern. */
+
+static const char *
+get_some_local_dynamic_name ()
+{
+ rtx insn;
+
+ if (cfun->machine->some_ld_name)
+ return cfun->machine->some_ld_name;
+
+ for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
+ if (INSN_P (insn)
+ && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
+ return cfun->machine->some_ld_name;
+
+ abort ();
+}
+
+static int
+get_some_local_dynamic_name_1 (px, data)
+ rtx *px;
+ void *data ATTRIBUTE_UNUSED;
+{
+ rtx x = *px;
+
+ if (GET_CODE (x) == SYMBOL_REF
+ && SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC)
+ {
+ cfun->machine->some_ld_name = XSTR (x, 0);
+ return 1;
+ }
+
+ return 0;
+}
+
/* Print an operand. Recognize special options, documented below. */
void
assemble_name (file, alpha_fnname);
break;
+ case '&':
+ assemble_name (file, get_some_local_dynamic_name ());
+ break;
+
case '/':
{
const char *trap = get_trap_mode_suffix ();
fputc ((TARGET_FLOAT_VAX ? 'g' : 't'), file);
break;
+ case '+':
+ /* Generates a nop after a noreturn call at the very end of the
+ function. */
+ if (next_real_insn (current_output_insn) == 0)
+ fprintf (file, "\n\tnop");
+ break;
+
case '#':
if (alpha_this_literal_sequence_number == 0)
alpha_this_literal_sequence_number = alpha_next_sequence_number++;
break;
case 'J':
- if (GET_CODE (x) == CONST_INT)
- {
- if (INTVAL (x) != 0)
- fprintf (file, "\t\t!lituse_jsr!%d", (int) INTVAL (x));
- }
- else
- output_operand_lossage ("invalid %%J value");
+ {
+ const char *lituse;
+
+ if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD_CALL)
+ {
+ x = XVECEXP (x, 0, 0);
+ lituse = "lituse_tlsgd";
+ }
+ else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM_CALL)
+ {
+ x = XVECEXP (x, 0, 0);
+ lituse = "lituse_tlsldm";
+ }
+ else if (GET_CODE (x) == CONST_INT)
+ lituse = "lituse_jsr";
+ else
+ {
+ output_operand_lossage ("invalid %%J value");
+ break;
+ }
+
+ if (x != const0_rtx)
+ fprintf (file, "\t\t!%s!%d", lituse, (int) INTVAL (x));
+ }
break;
case 'r':
case 'U':
/* Similar, except do it from the mask. */
- if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0xff)
- fprintf (file, "b");
- else if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0xffff)
- fprintf (file, "w");
- else if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0xffffffff)
- fprintf (file, "l");
-#if HOST_BITS_PER_WIDE_INT == 32
- else if (GET_CODE (x) == CONST_DOUBLE
- && CONST_DOUBLE_HIGH (x) == 0
- && CONST_DOUBLE_LOW (x) == -1)
- fprintf (file, "l");
- else if (GET_CODE (x) == CONST_DOUBLE
- && CONST_DOUBLE_HIGH (x) == -1
- && CONST_DOUBLE_LOW (x) == -1)
- fprintf (file, "q");
-#else
- else if (GET_CODE (x) == CONST_INT && INTVAL (x) == -1)
- fprintf (file, "q");
- else if (GET_CODE (x) == CONST_DOUBLE
- && CONST_DOUBLE_HIGH (x) == 0
- && CONST_DOUBLE_LOW (x) == -1)
- fprintf (file, "q");
-#endif
- else
- output_operand_lossage ("invalid %%U value");
+ if (GET_CODE (x) == CONST_INT)
+ {
+ HOST_WIDE_INT value = INTVAL (x);
+
+ if (value == 0xff)
+ {
+ fputc ('b', file);
+ break;
+ }
+ if (value == 0xffff)
+ {
+ fputc ('w', file);
+ break;
+ }
+ if (value == 0xffffffff)
+ {
+ fputc ('l', file);
+ break;
+ }
+ if (value == -1)
+ {
+ fputc ('q', file);
+ break;
+ }
+ }
+ else if (HOST_BITS_PER_WIDE_INT == 32
+ && GET_CODE (x) == CONST_DOUBLE
+ && CONST_DOUBLE_LOW (x) == 0xffffffff
+ && CONST_DOUBLE_HIGH (x) == 0)
+ {
+ fputc ('l', file);
+ break;
+ }
+ output_operand_lossage ("invalid %%U value");
break;
case 's':
fprintf (file, "%s", reg_names[REGNO (x)]);
else if (GET_CODE (x) == MEM)
output_address (XEXP (x, 0));
+ else if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == UNSPEC)
+ {
+ switch (XINT (XEXP (x, 0), 1))
+ {
+ case UNSPEC_DTPREL:
+ case UNSPEC_TPREL:
+ output_addr_const (file, XVECEXP (XEXP (x, 0), 0, 0));
+ break;
+ default:
+ output_operand_lossage ("unknown relocation unspec");
+ break;
+ }
+ }
else
output_addr_const (file, x);
break;
if (GET_CODE (addr) == LO_SUM)
{
- output_addr_const (file, XEXP (addr, 1));
- if (offset)
+ const char *reloc16, *reloclo;
+ rtx op1 = XEXP (addr, 1);
+
+ if (GET_CODE (op1) == CONST && GET_CODE (XEXP (op1, 0)) == UNSPEC)
+ {
+ op1 = XEXP (op1, 0);
+ switch (XINT (op1, 1))
+ {
+ case UNSPEC_DTPREL:
+ reloc16 = NULL;
+ reloclo = (alpha_tls_size == 16 ? "dtprel" : "dtprello");
+ break;
+ case UNSPEC_TPREL:
+ reloc16 = NULL;
+ reloclo = (alpha_tls_size == 16 ? "tprel" : "tprello");
+ break;
+ default:
+ output_operand_lossage ("unknown relocation unspec");
+ return;
+ }
+
+ output_addr_const (file, XVECEXP (op1, 0, 0));
+ }
+ else
{
- fputc ('+', file);
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
+ reloc16 = "gprel";
+ reloclo = "gprellow";
+ output_addr_const (file, op1);
}
+
+ if (offset)
+ fprintf (file, "+" HOST_WIDE_INT_PRINT_DEC, offset);
addr = XEXP (addr, 0);
if (GET_CODE (addr) == REG)
abort ();
fprintf (file, "($%d)\t\t!%s", basereg,
- (basereg == 29 ? "gprel" : "gprellow"));
+ (basereg == 29 ? reloc16 : reloclo));
return;
}
basereg = subreg_regno (addr);
else if (GET_CODE (addr) == CONST_INT)
offset = INTVAL (addr);
+
+#if TARGET_ABI_OPEN_VMS
+ else if (GET_CODE (addr) == SYMBOL_REF)
+ {
+ fprintf (file, "%s", XSTR (addr, 0));
+ return;
+ }
+ else if (GET_CODE (addr) == CONST
+ && GET_CODE (XEXP (addr, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (addr, 0), 0)) == SYMBOL_REF)
+ {
+ fprintf (file, "%s+%d",
+ XSTR (XEXP (XEXP (addr, 0), 0), 0),
+ INTVAL (XEXP (XEXP (addr, 0), 1)));
+ return;
+ }
+#endif
+
else
abort ();
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
- fprintf (file, "($%d)", basereg);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC "($%d)", offset, basereg);
}
\f
/* Emit RTL insns to initialize the variable parts of a trampoline at
}
#ifdef TRANSFER_FROM_TRAMPOLINE
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
- 0, VOIDmode, 1, addr, Pmode);
+ emit_library_call (init_one_libfunc ("__enable_execute_stack"),
+ 0, VOIDmode, 1, tramp, Pmode);
#endif
if (jmpofs >= 0)
return record;
}
+/* Perform any needed actions needed for a function that is receiving a
+ variable number of arguments.
+
+ On the Alpha, we allocate space for all 12 arg registers, but only
+ push those that are remaining. However, if NO registers need to be
+ saved, don't allocate any space. This is not only because we won't
+ need the space, but because AP includes the current_pretend_args_size
+ and we don't want to mess up any ap-relative addresses already made.
+
+ If we are not to use the floating-point registers, save the integer
+ registers where we would put the floating-point registers. This is
+ not the most efficient way to implement varargs with just one register
+ class, but it isn't worth doing anything more efficient in this rare
+ case. */
+
+void
+alpha_setup_incoming_varargs(cum, mode, type, pretend_size, no_rtl)
+ CUMULATIVE_ARGS cum;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ tree type ATTRIBUTE_UNUSED;
+ int *pretend_size;
+ int no_rtl;
+{
+ if (cum >= 6)
+ return;
+
+ if (!no_rtl)
+ {
+ int set = get_varargs_alias_set ();
+ rtx tmp;
+
+ tmp = gen_rtx_MEM (BLKmode,
+ plus_constant (virtual_incoming_args_rtx,
+ (cum + 6) * UNITS_PER_WORD));
+ set_mem_alias_set (tmp, set);
+ move_block_from_reg (16 + cum, tmp, 6 - cum);
+
+ tmp = gen_rtx_MEM (BLKmode,
+ plus_constant (virtual_incoming_args_rtx,
+ cum * UNITS_PER_WORD));
+ set_mem_alias_set (tmp, set);
+ move_block_from_reg (16 + (TARGET_FPREGS ? 32 : 0) + cum, tmp,
+ 6 - cum);
+ }
+ *pretend_size = 12 * UNITS_PER_WORD;
+}
+
void
-alpha_va_start (stdarg_p, valist, nextarg)
- int stdarg_p;
+alpha_va_start (valist, nextarg)
tree valist;
rtx nextarg ATTRIBUTE_UNUSED;
{
return;
if (TARGET_ABI_UNICOSMK)
- std_expand_builtin_va_start (stdarg_p, valist, nextarg);
+ std_expand_builtin_va_start (valist, nextarg);
/* For Unix, SETUP_INCOMING_VARARGS moves the starting address base
up by 48, storing fp arg registers in the first 48 bytes, and the
If no integer registers need be stored, then we must subtract 48
in order to account for the integer arg registers which are counted
- in argsize above, but which are not actually stored on the stack. */
+ in argsize above, but which are not actually stored on the stack.
+ Must further be careful here about structures straddling the last
+ integer argument register; that futzes with pretend_args_size,
+ which changes the meaning of AP. */
- if (NUM_ARGS <= 5 + stdarg_p)
+ if (NUM_ARGS <= 6)
offset = TARGET_ABI_OPEN_VMS ? UNITS_PER_WORD : 6 * UNITS_PER_WORD;
else
- offset = -6 * UNITS_PER_WORD;
+ offset = -6 * UNITS_PER_WORD + current_function_pretend_args_size;
if (TARGET_ABI_OPEN_VMS)
{
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
- wide_type = make_signed_type (64);
- wide_ofs = save_expr (build1 (CONVERT_EXPR, wide_type, offset_field));
+ wide_type = make_signed_type (64);
+ wide_ofs = save_expr (build1 (CONVERT_EXPR, wide_type, offset_field));
+
+ addend = wide_ofs;
+
+ if (TYPE_MODE (type) == TFmode || TYPE_MODE (type) == TCmode)
+ {
+ indirect = 1;
+ rounded_size = size_int (UNITS_PER_WORD);
+ }
+ else if (FLOAT_TYPE_P (type))
+ {
+ tree fpaddend, cond;
+
+ fpaddend = fold (build (PLUS_EXPR, TREE_TYPE (addend),
+ addend, build_int_2 (-6*8, 0)));
+
+ cond = fold (build (LT_EXPR, integer_type_node,
+ wide_ofs, build_int_2 (6*8, 0)));
+
+ addend = fold (build (COND_EXPR, TREE_TYPE (addend), cond,
+ fpaddend, addend));
+ }
+
+ addr_tree = build (PLUS_EXPR, TREE_TYPE (base_field),
+ base_field, addend);
+
+ addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
+ addr = copy_to_reg (addr);
+
+ t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field,
+ build (PLUS_EXPR, TREE_TYPE (offset_field),
+ offset_field, rounded_size));
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ if (indirect)
+ {
+ addr = force_reg (Pmode, addr);
+ addr = gen_rtx_MEM (Pmode, addr);
+ }
+
+ return addr;
+}
+\f
+/* Builtins. */
+
+enum alpha_builtin
+{
+ ALPHA_BUILTIN_CMPBGE,
+ ALPHA_BUILTIN_EXTBL,
+ ALPHA_BUILTIN_EXTWL,
+ ALPHA_BUILTIN_EXTLL,
+ ALPHA_BUILTIN_EXTQL,
+ ALPHA_BUILTIN_EXTWH,
+ ALPHA_BUILTIN_EXTLH,
+ ALPHA_BUILTIN_EXTQH,
+ ALPHA_BUILTIN_INSBL,
+ ALPHA_BUILTIN_INSWL,
+ ALPHA_BUILTIN_INSLL,
+ ALPHA_BUILTIN_INSQL,
+ ALPHA_BUILTIN_INSWH,
+ ALPHA_BUILTIN_INSLH,
+ ALPHA_BUILTIN_INSQH,
+ ALPHA_BUILTIN_MSKBL,
+ ALPHA_BUILTIN_MSKWL,
+ ALPHA_BUILTIN_MSKLL,
+ ALPHA_BUILTIN_MSKQL,
+ ALPHA_BUILTIN_MSKWH,
+ ALPHA_BUILTIN_MSKLH,
+ ALPHA_BUILTIN_MSKQH,
+ ALPHA_BUILTIN_UMULH,
+ ALPHA_BUILTIN_ZAP,
+ ALPHA_BUILTIN_ZAPNOT,
+ ALPHA_BUILTIN_AMASK,
+ ALPHA_BUILTIN_IMPLVER,
+ ALPHA_BUILTIN_RPCC,
+ ALPHA_BUILTIN_THREAD_POINTER,
+ ALPHA_BUILTIN_SET_THREAD_POINTER,
+
+ /* TARGET_MAX */
+ ALPHA_BUILTIN_MINUB8,
+ ALPHA_BUILTIN_MINSB8,
+ ALPHA_BUILTIN_MINUW4,
+ ALPHA_BUILTIN_MINSW4,
+ ALPHA_BUILTIN_MAXUB8,
+ ALPHA_BUILTIN_MAXSB8,
+ ALPHA_BUILTIN_MAXUW4,
+ ALPHA_BUILTIN_MAXSW4,
+ ALPHA_BUILTIN_PERR,
+ ALPHA_BUILTIN_PKLB,
+ ALPHA_BUILTIN_PKWB,
+ ALPHA_BUILTIN_UNPKBL,
+ ALPHA_BUILTIN_UNPKBW,
+
+ /* TARGET_CIX */
+ ALPHA_BUILTIN_CTTZ,
+ ALPHA_BUILTIN_CTLZ,
+ ALPHA_BUILTIN_CTPOP,
+
+ ALPHA_BUILTIN_max
+};
+
+static unsigned int const code_for_builtin[ALPHA_BUILTIN_max] = {
+ CODE_FOR_builtin_cmpbge,
+ CODE_FOR_builtin_extbl,
+ CODE_FOR_builtin_extwl,
+ CODE_FOR_builtin_extll,
+ CODE_FOR_builtin_extql,
+ CODE_FOR_builtin_extwh,
+ CODE_FOR_builtin_extlh,
+ CODE_FOR_builtin_extqh,
+ CODE_FOR_builtin_insbl,
+ CODE_FOR_builtin_inswl,
+ CODE_FOR_builtin_insll,
+ CODE_FOR_builtin_insql,
+ CODE_FOR_builtin_inswh,
+ CODE_FOR_builtin_inslh,
+ CODE_FOR_builtin_insqh,
+ CODE_FOR_builtin_mskbl,
+ CODE_FOR_builtin_mskwl,
+ CODE_FOR_builtin_mskll,
+ CODE_FOR_builtin_mskql,
+ CODE_FOR_builtin_mskwh,
+ CODE_FOR_builtin_msklh,
+ CODE_FOR_builtin_mskqh,
+ CODE_FOR_umuldi3_highpart,
+ CODE_FOR_builtin_zap,
+ CODE_FOR_builtin_zapnot,
+ CODE_FOR_builtin_amask,
+ CODE_FOR_builtin_implver,
+ CODE_FOR_builtin_rpcc,
+ CODE_FOR_load_tp,
+ CODE_FOR_set_tp,
+
+ /* TARGET_MAX */
+ CODE_FOR_builtin_minub8,
+ CODE_FOR_builtin_minsb8,
+ CODE_FOR_builtin_minuw4,
+ CODE_FOR_builtin_minsw4,
+ CODE_FOR_builtin_maxub8,
+ CODE_FOR_builtin_maxsb8,
+ CODE_FOR_builtin_maxuw4,
+ CODE_FOR_builtin_maxsw4,
+ CODE_FOR_builtin_perr,
+ CODE_FOR_builtin_pklb,
+ CODE_FOR_builtin_pkwb,
+ CODE_FOR_builtin_unpkbl,
+ CODE_FOR_builtin_unpkbw,
+
+ /* TARGET_CIX */
+ CODE_FOR_builtin_cttz,
+ CODE_FOR_builtin_ctlz,
+ CODE_FOR_builtin_ctpop
+};
+
+struct alpha_builtin_def
+{
+ const char *name;
+ enum alpha_builtin code;
+ unsigned int target_mask;
+};
+
+static struct alpha_builtin_def const zero_arg_builtins[] = {
+ { "__builtin_alpha_implver", ALPHA_BUILTIN_IMPLVER, 0 },
+ { "__builtin_alpha_rpcc", ALPHA_BUILTIN_RPCC, 0 }
+};
+
+static struct alpha_builtin_def const one_arg_builtins[] = {
+ { "__builtin_alpha_amask", ALPHA_BUILTIN_AMASK, 0 },
+ { "__builtin_alpha_pklb", ALPHA_BUILTIN_PKLB, MASK_MAX },
+ { "__builtin_alpha_pkwb", ALPHA_BUILTIN_PKWB, MASK_MAX },
+ { "__builtin_alpha_unpkbl", ALPHA_BUILTIN_UNPKBL, MASK_MAX },
+ { "__builtin_alpha_unpkbw", ALPHA_BUILTIN_UNPKBW, MASK_MAX },
+ { "__builtin_alpha_cttz", ALPHA_BUILTIN_CTTZ, MASK_CIX },
+ { "__builtin_alpha_ctlz", ALPHA_BUILTIN_CTLZ, MASK_CIX },
+ { "__builtin_alpha_ctpop", ALPHA_BUILTIN_CTPOP, MASK_CIX }
+};
+
+static struct alpha_builtin_def const two_arg_builtins[] = {
+ { "__builtin_alpha_cmpbge", ALPHA_BUILTIN_CMPBGE, 0 },
+ { "__builtin_alpha_extbl", ALPHA_BUILTIN_EXTBL, 0 },
+ { "__builtin_alpha_extwl", ALPHA_BUILTIN_EXTWL, 0 },
+ { "__builtin_alpha_extll", ALPHA_BUILTIN_EXTLL, 0 },
+ { "__builtin_alpha_extql", ALPHA_BUILTIN_EXTQL, 0 },
+ { "__builtin_alpha_extwh", ALPHA_BUILTIN_EXTWH, 0 },
+ { "__builtin_alpha_extlh", ALPHA_BUILTIN_EXTLH, 0 },
+ { "__builtin_alpha_extqh", ALPHA_BUILTIN_EXTQH, 0 },
+ { "__builtin_alpha_insbl", ALPHA_BUILTIN_INSBL, 0 },
+ { "__builtin_alpha_inswl", ALPHA_BUILTIN_INSWL, 0 },
+ { "__builtin_alpha_insll", ALPHA_BUILTIN_INSLL, 0 },
+ { "__builtin_alpha_insql", ALPHA_BUILTIN_INSQL, 0 },
+ { "__builtin_alpha_inswh", ALPHA_BUILTIN_INSWH, 0 },
+ { "__builtin_alpha_inslh", ALPHA_BUILTIN_INSLH, 0 },
+ { "__builtin_alpha_insqh", ALPHA_BUILTIN_INSQH, 0 },
+ { "__builtin_alpha_mskbl", ALPHA_BUILTIN_MSKBL, 0 },
+ { "__builtin_alpha_mskwl", ALPHA_BUILTIN_MSKWL, 0 },
+ { "__builtin_alpha_mskll", ALPHA_BUILTIN_MSKLL, 0 },
+ { "__builtin_alpha_mskql", ALPHA_BUILTIN_MSKQL, 0 },
+ { "__builtin_alpha_mskwh", ALPHA_BUILTIN_MSKWH, 0 },
+ { "__builtin_alpha_msklh", ALPHA_BUILTIN_MSKLH, 0 },
+ { "__builtin_alpha_mskqh", ALPHA_BUILTIN_MSKQH, 0 },
+ { "__builtin_alpha_umulh", ALPHA_BUILTIN_UMULH, 0 },
+ { "__builtin_alpha_zap", ALPHA_BUILTIN_ZAP, 0 },
+ { "__builtin_alpha_zapnot", ALPHA_BUILTIN_ZAPNOT, 0 },
+ { "__builtin_alpha_minub8", ALPHA_BUILTIN_MINUB8, MASK_MAX },
+ { "__builtin_alpha_minsb8", ALPHA_BUILTIN_MINSB8, MASK_MAX },
+ { "__builtin_alpha_minuw4", ALPHA_BUILTIN_MINUW4, MASK_MAX },
+ { "__builtin_alpha_minsw4", ALPHA_BUILTIN_MINSW4, MASK_MAX },
+ { "__builtin_alpha_maxub8", ALPHA_BUILTIN_MAXUB8, MASK_MAX },
+ { "__builtin_alpha_maxsb8", ALPHA_BUILTIN_MAXSB8, MASK_MAX },
+ { "__builtin_alpha_maxuw4", ALPHA_BUILTIN_MAXUW4, MASK_MAX },
+ { "__builtin_alpha_maxsw4", ALPHA_BUILTIN_MAXSW4, MASK_MAX },
+ { "__builtin_alpha_perr", ALPHA_BUILTIN_PERR, MASK_MAX }
+};
+
+static void
+alpha_init_builtins ()
+{
+ const struct alpha_builtin_def *p;
+ tree ftype;
+ size_t i;
+
+ ftype = build_function_type (long_integer_type_node, void_list_node);
+
+ p = zero_arg_builtins;
+ for (i = 0; i < ARRAY_SIZE (zero_arg_builtins); ++i, ++p)
+ if ((target_flags & p->target_mask) == p->target_mask)
+ builtin_function (p->name, ftype, p->code, BUILT_IN_MD,
+ NULL, NULL_TREE);
+
+ ftype = build_function_type_list (long_integer_type_node,
+ long_integer_type_node, NULL_TREE);
+
+ p = one_arg_builtins;
+ for (i = 0; i < ARRAY_SIZE (one_arg_builtins); ++i, ++p)
+ if ((target_flags & p->target_mask) == p->target_mask)
+ builtin_function (p->name, ftype, p->code, BUILT_IN_MD,
+ NULL, NULL_TREE);
+
+ ftype = build_function_type_list (long_integer_type_node,
+ long_integer_type_node,
+ long_integer_type_node, NULL_TREE);
+
+ p = two_arg_builtins;
+ for (i = 0; i < ARRAY_SIZE (two_arg_builtins); ++i, ++p)
+ if ((target_flags & p->target_mask) == p->target_mask)
+ builtin_function (p->name, ftype, p->code, BUILT_IN_MD,
+ NULL, NULL_TREE);
+
+ ftype = build_function_type (ptr_type_node, void_list_node);
+ builtin_function ("__builtin_thread_pointer", ftype,
+ ALPHA_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
+ NULL, NULL_TREE);
+
+ ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
+ builtin_function ("__builtin_set_thread_pointer", ftype,
+ ALPHA_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
+ NULL, NULL_TREE);
+}
+
+/* Expand an expression EXP that calls a built-in function,
+ with result going to TARGET if that's convenient
+ (and in mode MODE if that's convenient).
+ SUBTARGET may be used as the target for computing one of EXP's operands.
+ IGNORE is nonzero if the value is to be ignored. */
- addend = wide_ofs;
+static rtx
+alpha_expand_builtin (exp, target, subtarget, mode, ignore)
+ tree exp;
+ rtx target;
+ rtx subtarget ATTRIBUTE_UNUSED;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ int ignore ATTRIBUTE_UNUSED;
+{
+#define MAX_ARGS 2
- if (TYPE_MODE (type) == TFmode || TYPE_MODE (type) == TCmode)
- {
- indirect = 1;
- rounded_size = size_int (UNITS_PER_WORD);
- }
- else if (FLOAT_TYPE_P (type))
- {
- tree fpaddend, cond;
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum insn_code icode;
+ rtx op[MAX_ARGS], pat;
+ int arity;
+ bool nonvoid;
- fpaddend = fold (build (PLUS_EXPR, TREE_TYPE (addend),
- addend, build_int_2 (-6*8, 0)));
+ if (fcode >= ALPHA_BUILTIN_max)
+ internal_error ("bad builtin fcode");
+ icode = code_for_builtin[fcode];
+ if (icode == 0)
+ internal_error ("bad builtin fcode");
- cond = fold (build (LT_EXPR, integer_type_node,
- wide_ofs, build_int_2 (6*8, 0)));
+ nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
- addend = fold (build (COND_EXPR, TREE_TYPE (addend), cond,
- fpaddend, addend));
- }
+ for (arglist = TREE_OPERAND (exp, 1), arity = 0;
+ arglist;
+ arglist = TREE_CHAIN (arglist), arity++)
+ {
+ const struct insn_operand_data *insn_op;
- addr_tree = build (PLUS_EXPR, TREE_TYPE (base_field),
- base_field, addend);
+ tree arg = TREE_VALUE (arglist);
+ if (arg == error_mark_node)
+ return NULL_RTX;
+ if (arity > MAX_ARGS)
+ return NULL_RTX;
- addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
- addr = copy_to_reg (addr);
+ insn_op = &insn_data[icode].operand[arity + nonvoid];
- t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field,
- build (PLUS_EXPR, TREE_TYPE (offset_field),
- offset_field, rounded_size));
- TREE_SIDE_EFFECTS (t) = 1;
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
- if (indirect)
+ if (!(*insn_op->predicate) (op[arity], insn_op->mode))
+ op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+ }
+
+ if (nonvoid)
{
- addr = force_reg (Pmode, addr);
- addr = gen_rtx_MEM (Pmode, addr);
+ enum machine_mode tmode = insn_data[icode].operand[0].mode;
+ if (!target
+ || GET_MODE (target) != tmode
+ || !(*insn_data[icode].operand[0].predicate) (target, tmode))
+ target = gen_reg_rtx (tmode);
}
- return addr;
+ switch (arity)
+ {
+ case 0:
+ pat = GEN_FCN (icode) (target);
+ break;
+ case 1:
+ if (nonvoid)
+ pat = GEN_FCN (icode) (target, op[0]);
+ else
+ pat = GEN_FCN (icode) (op[0]);
+ break;
+ case 2:
+ pat = GEN_FCN (icode) (target, op[0], op[1]);
+ break;
+ default:
+ abort ();
+ }
+ if (!pat)
+ return NULL_RTX;
+ emit_insn (pat);
+
+ if (nonvoid)
+ return target;
+ else
+ return const0_rtx;
}
\f
/* This page contains routines that are used to determine what the function
unsigned int i;
/* Irritatingly, there are two kinds of thunks -- those created with
- ASM_OUTPUT_MI_THUNK and those with DECL_THUNK_P that go through
- the regular part of the compiler. In the ASM_OUTPUT_MI_THUNK case
- we don't have valid register life info, but assemble_start_function
- wants to output .frame and .mask directives. */
+ TARGET_ASM_OUTPUT_MI_THUNK and those with DECL_THUNK_P that go
+ through the regular part of the compiler. In the
+ TARGET_ASM_OUTPUT_MI_THUNK case we don't have valid register life
+ info, but assemble_start_function wants to output .frame and
+ .mask directives. */
if (current_function_is_thunk && !no_new_pseudos)
{
*imaskP = 0;
}
if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK)
- imask |= (1L << HARD_FRAME_POINTER_REGNUM);
+ imask |= (1UL << HARD_FRAME_POINTER_REGNUM);
/* One for every register we have to save. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
&& (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM))
{
if (i < 32)
- imask |= (1L << i);
+ imask |= (1UL << i);
else
- fmask |= (1L << (i - 32));
+ fmask |= (1UL << (i - 32));
}
/* We need to restore these for the handler. */
unsigned regno = EH_RETURN_DATA_REGNO (i);
if (regno == INVALID_REGNUM)
break;
- imask |= 1L << regno;
+ imask |= 1UL << regno;
}
/* If any register spilled, then spill the return address also. */
/* ??? This is required by the Digital stack unwind specification
and isn't needed if we're doing Dwarf2 unwinding. */
if (imask || fmask || alpha_ra_ever_killed ())
- imask |= (1L << REG_RA);
+ imask |= (1UL << REG_RA);
*imaskP = imask;
*fmaskP = fmask;
alpha_procedure_type
= (sa_size || get_frame_size() != 0
- || current_function_outgoing_args_size || current_function_varargs
+ || current_function_outgoing_args_size
|| current_function_stdarg || current_function_calls_alloca
|| frame_pointer_needed)
? PT_STACK : PT_REGISTER;
return sa_size * 8;
}
+/* Define the offset between two registers, one to be eliminated,
+ and the other its replacement, at the start of a routine. */
+
+HOST_WIDE_INT
+alpha_initial_elimination_offset (from, to)
+ unsigned int from, to ATTRIBUTE_UNUSED;
+{
+ HOST_WIDE_INT ret;
+
+ ret = alpha_sa_size ();
+ ret += ALPHA_ROUND (current_function_outgoing_args_size);
+
+ if (from == FRAME_POINTER_REGNUM)
+ ;
+ else if (from == ARG_POINTER_REGNUM)
+ ret += (ALPHA_ROUND (get_frame_size ()
+ + current_function_pretend_args_size)
+ - current_function_pretend_args_size);
+ else
+ abort ();
+
+ return ret;
+}
+
int
alpha_pv_save_size ()
{
#endif
static int
-find_lo_sum (px, data)
+find_lo_sum_using_gp (px, data)
rtx *px;
void *data ATTRIBUTE_UNUSED;
{
- return GET_CODE (*px) == LO_SUM;
+ return GET_CODE (*px) == LO_SUM && XEXP (*px, 0) == pic_offset_table_rtx;
+}
+
+int
+alpha_find_lo_sum_using_gp (insn)
+ rtx insn;
+{
+ return for_each_rtx (&PATTERN (insn), find_lo_sum_using_gp, NULL) > 0;
}
static int
for (; insn; insn = NEXT_INSN (insn))
if (INSN_P (insn)
&& 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;
- if (TARGET_EXPLICIT_RELOCS
- && for_each_rtx (&PATTERN (insn), find_lo_sum, NULL) > 0)
- return 1;
- }
+ && GET_CODE (PATTERN (insn)) != CLOBBER
+ && get_attr_usegp (insn))
+ return 1;
return 0;
}
static rtx
set_frame_related_p ()
{
- rtx seq = gen_sequence ();
+ rtx seq = get_insns ();
+ rtx insn;
+
end_sequence ();
- if (GET_CODE (seq) == SEQUENCE)
+ if (!seq)
+ return NULL_RTX;
+
+ if (INSN_P (seq))
{
- int i = XVECLEN (seq, 0);
- while (--i >= 0)
- RTX_FRAME_RELATED_P (XVECEXP (seq, 0, i)) = 1;
- return emit_insn (seq);
+ insn = seq;
+ while (insn != NULL_RTX)
+ {
+ RTX_FRAME_RELATED_P (insn) = 1;
+ insn = NEXT_INSN (insn);
+ }
+ seq = emit_insn (seq);
}
else
{
seq = emit_insn (seq);
RTX_FRAME_RELATED_P (seq) = 1;
- return seq;
}
+ return seq;
}
#define FRP(exp) (start_sequence (), exp, set_frame_related_p ())
}
/* Save register RA next. */
- if (imask & (1L << REG_RA))
+ if (imask & (1UL << REG_RA))
{
mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset));
set_mem_alias_set (mem, alpha_sr_alias_set);
FRP (emit_move_insn (mem, gen_rtx_REG (DImode, REG_RA)));
- imask &= ~(1L << REG_RA);
+ imask &= ~(1UL << REG_RA);
reg_offset += 8;
}
/* Now save any other registers required to be saved. */
for (i = 0; i < 32; i++)
- if (imask & (1L << i))
+ if (imask & (1UL << i))
{
mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset));
set_mem_alias_set (mem, alpha_sr_alias_set);
}
for (i = 0; i < 32; i++)
- if (fmask & (1L << i))
+ if (fmask & (1UL << i))
{
mem = gen_rtx_MEM (DFmode, plus_constant (sa_reg, reg_offset));
set_mem_alias_set (mem, alpha_sr_alias_set);
reg_offset = -56;
for (i = 9; i < 15; i++)
- if (imask & (1L << i))
+ if (imask & (1UL << i))
{
mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx,
reg_offset));
reg_offset -= 8;
}
for (i = 2; i < 10; i++)
- if (fmask & (1L << i))
+ if (fmask & (1UL << i))
{
mem = gen_rtx_MEM (DFmode, plus_constant (hard_frame_pointer_rtx,
reg_offset));
/* If we have to allocate space for outgoing args, do it now. */
if (current_function_outgoing_args_size != 0)
- FRP (emit_move_insn
- (stack_pointer_rtx,
- plus_constant (hard_frame_pointer_rtx,
- - (ALPHA_ROUND
- (current_function_outgoing_args_size)))));
+ {
+ rtx seq
+ = emit_move_insn (stack_pointer_rtx,
+ plus_constant
+ (hard_frame_pointer_rtx,
+ - (ALPHA_ROUND
+ (current_function_outgoing_args_size))));
+
+ /* Only set FRAME_RELATED_P on the stack adjustment we just emitted
+ if ! frame_pointer_needed. Setting the bit will change the CFA
+ computation rule to use sp again, which would be wrong if we had
+ frame_pointer_needed, as this means sp might move unpredictably
+ later on.
+
+ Also, note that
+ frame_pointer_needed
+ => vms_unwind_regno == HARD_FRAME_POINTER_REGNUM
+ and
+ current_function_outgoing_args_size != 0
+ => alpha_procedure_type != PT_NULL,
+
+ so when we are not setting the bit here, we are guaranteed to
+ have emited an FRP frame pointer update just before. */
+ RTX_FRAME_RELATED_P (seq) = ! frame_pointer_needed;
+ }
}
else if (!TARGET_ABI_UNICOSMK)
{
/* Stack space needed for pushing registers clobbered by us. */
HOST_WIDE_INT sa_size;
/* Complete stack size needed. */
- HOST_WIDE_INT frame_size;
+ unsigned HOST_WIDE_INT frame_size;
/* Offset from base reg to register save area. */
HOST_WIDE_INT reg_offset;
char *entry_label = (char *) alloca (strlen (fnname) + 6);
if (TARGET_ABI_UNICOSMK)
;
else if (TARGET_ABI_OPEN_VMS)
- {
- fprintf (file, "\t.frame $%d,", vms_unwind_regno);
- fprintf (file, HOST_WIDE_INT_PRINT_DEC,
- frame_size >= ((HOST_WIDE_INT) 1 << 31) ? 0 : frame_size);
- fputs (",$26,", file);
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, reg_offset);
- fputs ("\n", file);
- }
+ fprintf (file, "\t.frame $%d," HOST_WIDE_INT_PRINT_DEC ",$26,"
+ HOST_WIDE_INT_PRINT_DEC "\n",
+ vms_unwind_regno,
+ frame_size >= (1UL << 31) ? 0 : frame_size,
+ reg_offset);
else if (!flag_inhibit_size_directive)
- {
- fprintf (file, "\t.frame $%d,",
- (frame_pointer_needed
- ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM));
- fprintf (file, HOST_WIDE_INT_PRINT_DEC,
- frame_size >= (1l << 31) ? 0 : frame_size);
- fprintf (file, ",$26,%d\n", current_function_pretend_args_size);
- }
+ fprintf (file, "\t.frame $%d," HOST_WIDE_INT_PRINT_DEC ",$26,%d\n",
+ (frame_pointer_needed
+ ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM),
+ frame_size >= (1UL << 31) ? 0 : frame_size,
+ current_function_pretend_args_size);
/* Describe which registers were spilled. */
if (TARGET_ABI_UNICOSMK)
if (imask)
/* ??? Does VMS care if mask contains ra? The old code didn't
set it, so I don't here. */
- fprintf (file, "\t.mask 0x%lx,0\n", imask & ~(1L << REG_RA));
+ fprintf (file, "\t.mask 0x%lx,0\n", imask & ~(1UL << REG_RA));
if (fmask)
fprintf (file, "\t.fmask 0x%lx,0\n", fmask);
if (alpha_procedure_type == PT_REGISTER)
{
if (imask)
{
- fprintf (file, "\t.mask 0x%lx,", imask);
- fprintf (file, HOST_WIDE_INT_PRINT_DEC,
- frame_size >= (1l << 31) ? 0 : reg_offset - frame_size);
- putc ('\n', file);
+ fprintf (file, "\t.mask 0x%lx," HOST_WIDE_INT_PRINT_DEC "\n", imask,
+ frame_size >= (1UL << 31) ? 0 : reg_offset - frame_size);
for (i = 0; i < 32; ++i)
- if (imask & (1L << i))
+ if (imask & (1UL << i))
reg_offset += 8;
}
if (fmask)
- {
- fprintf (file, "\t.fmask 0x%lx,", fmask);
- fprintf (file, HOST_WIDE_INT_PRINT_DEC,
- frame_size >= (1l << 31) ? 0 : reg_offset - frame_size);
- putc ('\n', file);
- }
+ fprintf (file, "\t.fmask 0x%lx," HOST_WIDE_INT_PRINT_DEC "\n", fmask,
+ frame_size >= (1UL << 31) ? 0 : reg_offset - frame_size);
}
#if TARGET_ABI_OPEN_VMS
- /* Ifdef'ed cause readonly_section and link_section are only
- available then. */
- readonly_section ();
+ /* Ifdef'ed cause link_section are only available then. */
+ readonly_data_section ();
fprintf (file, "\t.align 3\n");
assemble_name (file, fnname); fputs ("..na:\n", file);
fputs ("\t.ascii \"", file);
assemble_name (file, fnname);
fputs ("\\0\"\n", file);
-
- link_section ();
- fprintf (file, "\t.align 3\n");
- fputs ("\t.name ", file);
- assemble_name (file, fnname);
- fputs ("..na\n", file);
- ASM_OUTPUT_LABEL (file, fnname);
- fprintf (file, "\t.pdesc ");
- assemble_name (file, fnname);
- fprintf (file, "..en,%s\n",
- alpha_procedure_type == PT_STACK ? "stack"
- : alpha_procedure_type == PT_REGISTER ? "reg" : "null");
alpha_need_linkage (fnname, 1);
text_section ();
#endif
FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem));
reg_offset += 8;
- imask &= ~(1L << REG_RA);
+ imask &= ~(1UL << REG_RA);
for (i = 0; i < 32; ++i)
- if (imask & (1L << i))
+ if (imask & (1UL << i))
{
if (i == HARD_FRAME_POINTER_REGNUM && fp_is_frame_pointer)
fp_offset = reg_offset;
}
for (i = 0; i < 32; ++i)
- if (fmask & (1L << i))
+ if (fmask & (1UL << i))
{
mem = gen_rtx_MEM (DFmode, plus_constant(sa_reg, reg_offset));
set_mem_alias_set (mem, alpha_sr_alias_set);
reg_offset = -56;
for (i = 9; i < 15; i++)
- if (imask & (1L << i))
+ if (imask & (1UL << i))
{
mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx,
reg_offset));
}
for (i = 2; i < 10; i++)
- if (fmask & (1L << i))
+ if (fmask & (1UL << i))
{
mem = gen_rtx_MEM (DFmode, plus_constant(hard_frame_pointer_rtx,
reg_offset));
}
}
}
-
+\f
/* Output the rest of the textual info surrounding the epilogue. */
void
}
inside_function = FALSE;
- /* Show that we know this function if it is called again.
-
- Don't do this for global functions in object files destined for a
- shared library because the function may be overridden by the application
- or other libraries. Similarly, don't do this for weak functions.
-
- Don't do this for functions not defined in the .text section, as
- otherwise it's not unlikely that the destination is out of range
- for a direct branch. */
-
- if (!DECL_WEAK (current_function_decl)
- && (!flag_pic || !TREE_PUBLIC (current_function_decl))
- && decl_in_text_section (current_function_decl))
- SYMBOL_REF_FLAG (XEXP (DECL_RTL (current_function_decl), 0)) = 1;
+#if TARGET_ABI_OPEN_VMS
+ alpha_write_linkage (file, fnname, decl);
+#endif
/* Output jump tables and the static subroutine information block. */
if (TARGET_ABI_UNICOSMK)
}
}
-/* Emit a tail call to FUNCTION after adjusting THIS by DELTA.
+#if TARGET_ABI_OSF
+/* Emit a tail call to FUNCTION after adjusting THIS by DELTA.
In order to avoid the hordes of differences between generated code
with and without TARGET_EXPLICIT_RELOCS, and to avoid duplicating
Not sure why this idea hasn't been explored before... */
-void
-alpha_output_mi_thunk_osf (file, thunk_fndecl, delta, function)
+static void
+alpha_output_mi_thunk_osf (file, thunk_fndecl, delta, vcall_offset, function)
FILE *file;
tree thunk_fndecl ATTRIBUTE_UNUSED;
HOST_WIDE_INT delta;
+ HOST_WIDE_INT vcall_offset;
tree function;
{
HOST_WIDE_INT hi, lo;
emit_insn (gen_adddi3 (this, this, tmp));
}
+ /* Add a delta stored in the vtable at VCALL_OFFSET. */
+ if (vcall_offset)
+ {
+ rtx tmp, tmp2;
+
+ tmp = gen_rtx_REG (Pmode, 0);
+ emit_move_insn (tmp, gen_rtx_MEM (Pmode, this));
+
+ lo = ((vcall_offset & 0xffff) ^ 0x8000) - 0x8000;
+ hi = (((vcall_offset - lo) & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ if (hi + lo == vcall_offset)
+ {
+ if (hi)
+ emit_insn (gen_adddi3 (tmp, tmp, GEN_INT (hi)));
+ }
+ else
+ {
+ tmp2 = alpha_emit_set_long_const (gen_rtx_REG (Pmode, 1),
+ vcall_offset, -(vcall_offset < 0));
+ emit_insn (gen_adddi3 (tmp, tmp, tmp2));
+ lo = 0;
+ }
+ if (lo)
+ tmp2 = gen_rtx_PLUS (Pmode, tmp, GEN_INT (lo));
+ else
+ tmp2 = tmp;
+ emit_move_insn (tmp, gen_rtx_MEM (Pmode, tmp2));
+
+ emit_insn (gen_adddi3 (this, this, tmp));
+ }
+
/* Generate a tail call to the target function. */
if (! TREE_USED (function))
{
final (insn, file, 1, 0);
final_end_function ();
}
+#endif /* TARGET_ABI_OSF */
\f
/* Debugging support. */
};
static void summarize_insn PARAMS ((rtx, struct shadow_summary *, int));
-static void alpha_handle_trap_shadows PARAMS ((rtx));
+static void alpha_handle_trap_shadows PARAMS ((void));
/* 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
(d) The trap shadow may not include any branch instructions. */
static void
-alpha_handle_trap_shadows (insns)
- rtx insns;
+alpha_handle_trap_shadows ()
{
struct shadow_summary shadow;
int trap_pending, exception_nesting;
shadow.used.mem = 0;
shadow.defd = shadow.used;
- for (i = insns; i ; i = NEXT_INSN (i))
+ for (i = get_insns (); i ; i = NEXT_INSN (i))
{
if (GET_CODE (i) == NOTE)
{
static rtx alphaev5_next_nop PARAMS ((int *));
static void alpha_align_insns
- PARAMS ((rtx, unsigned int, rtx (*)(rtx, int *, int *), rtx (*)(int *)));
+ PARAMS ((unsigned int, rtx (*)(rtx, int *, int *), rtx (*)(int *)));
static enum alphaev4_pipe
alphaev4_insn_pipe (insn)
case TYPE_MISC:
case TYPE_IBR:
case TYPE_JSR:
+ case TYPE_CALLPAL:
case TYPE_FCPYS:
case TYPE_FCMOV:
case TYPE_FADD:
case TYPE_IBR:
case TYPE_JSR:
+ case TYPE_CALLPAL:
return EV5_E1;
case TYPE_FCPYS:
/* The instruction group alignment main loop. */
static void
-alpha_align_insns (insns, max_align, next_group, next_nop)
- rtx insns;
+alpha_align_insns (max_align, next_group, next_nop)
unsigned int max_align;
rtx (*next_group) PARAMS ((rtx, int *, int *));
rtx (*next_nop) PARAMS ((int *));
rtx i, next;
/* Let shorten branches care for assigning alignments to code labels. */
- shorten_branches (insns);
+ shorten_branches (get_insns ());
if (align_functions < 4)
align = 4;
align = max_align;
ofs = prev_in_use = 0;
- i = insns;
+ i = get_insns ();
if (GET_CODE (i) == NOTE)
i = next_nonnote_insn (i);
\f
/* Machine dependent reorg pass. */
-void
-alpha_reorg (insns)
- rtx insns;
+static void
+alpha_reorg ()
{
if (alpha_tp != ALPHA_TP_PROG || flag_exceptions)
- alpha_handle_trap_shadows (insns);
+ alpha_handle_trap_shadows ();
/* Due to the number of extra trapb insns, don't bother fixing up
alignment when trap precision is instruction. Moreover, we can
&& flag_schedule_insns_after_reload)
{
if (alpha_cpu == PROCESSOR_EV4)
- alpha_align_insns (insns, 8, alphaev4_next_group, alphaev4_next_nop);
+ alpha_align_insns (8, alphaev4_next_group, alphaev4_next_nop);
else if (alpha_cpu == PROCESSOR_EV5)
- alpha_align_insns (insns, 16, alphaev5_next_group, alphaev5_next_nop);
+ alpha_align_insns (16, alphaev5_next_group, alphaev5_next_nop);
}
}
\f
-/* Check a floating-point value for validity for a particular machine mode. */
-
-static const char * const float_strings[] =
-{
- /* These are for FLOAT_VAX. */
- "1.70141173319264430e+38", /* 2^127 (2^24 - 1) / 2^24 */
- "-1.70141173319264430e+38",
- "2.93873587705571877e-39", /* 2^-128 */
- "-2.93873587705571877e-39",
- /* These are for the default broken IEEE mode, which traps
- on infinity or denormal numbers. */
- "3.402823466385288598117e+38", /* 2^128 (1 - 2^-24) */
- "-3.402823466385288598117e+38",
- "1.1754943508222875079687e-38", /* 2^-126 */
- "-1.1754943508222875079687e-38",
-};
+#ifdef OBJECT_FORMAT_ELF
-static REAL_VALUE_TYPE float_values[8];
-static int inited_float_values = 0;
+/* Switch to the section to which we should output X. The only thing
+ special we do here is to honor small data. */
-int
-check_float_value (mode, d, overflow)
+static void
+alpha_elf_select_rtx_section (mode, x, align)
enum machine_mode mode;
- REAL_VALUE_TYPE *d;
- int overflow ATTRIBUTE_UNUSED;
+ rtx x;
+ unsigned HOST_WIDE_INT align;
{
+ if (TARGET_SMALL_DATA && GET_MODE_SIZE (mode) <= g_switch_value)
+ /* ??? Consider using mergable sdata sections. */
+ sdata_section ();
+ else
+ default_elf_select_rtx_section (mode, x, align);
+}
- if (TARGET_IEEE || TARGET_IEEE_CONFORMANT || TARGET_IEEE_WITH_INEXACT)
- return 0;
+#endif /* OBJECT_FORMAT_ELF */
+\f
+/* Structure to collect function names for final output in link section. */
+/* Note that items marked with GTY can't be ifdef'ed out. */
- if (inited_float_values == 0)
- {
- int i;
- for (i = 0; i < 8; i++)
- float_values[i] = REAL_VALUE_ATOF (float_strings[i], DFmode);
+enum links_kind {KIND_UNUSED, KIND_LOCAL, KIND_EXTERN};
+enum reloc_kind {KIND_LINKAGE, KIND_CODEADDR};
- inited_float_values = 1;
- }
+struct alpha_links GTY(())
+{
+ int num;
+ rtx linkage;
+ enum links_kind lkind;
+ enum reloc_kind rkind;
+};
- if (mode == SFmode)
- {
- REAL_VALUE_TYPE r;
- REAL_VALUE_TYPE *fvptr;
+struct alpha_funcs GTY(())
+{
+ int num;
+ splay_tree GTY ((param1_is (char *), param2_is (struct alpha_links *)))
+ links;
+};
- if (TARGET_FLOAT_VAX)
- fvptr = &float_values[0];
- else
- fvptr = &float_values[4];
+static GTY ((param1_is (char *), param2_is (struct alpha_links *)))
+ splay_tree alpha_links_tree;
+static GTY ((param1_is (tree), param2_is (struct alpha_funcs *)))
+ splay_tree alpha_funcs_tree;
- memcpy (&r, d, sizeof (REAL_VALUE_TYPE));
- if (REAL_VALUES_LESS (fvptr[0], r))
- {
- memcpy (d, &fvptr[0], sizeof (REAL_VALUE_TYPE));
- return 1;
- }
- else if (REAL_VALUES_LESS (r, fvptr[1]))
- {
- memcpy (d, &fvptr[1], sizeof (REAL_VALUE_TYPE));
- return 1;
- }
- else if (REAL_VALUES_LESS (dconst0, r)
- && REAL_VALUES_LESS (r, fvptr[2]))
- {
- memcpy (d, &dconst0, sizeof (REAL_VALUE_TYPE));
- return 1;
- }
- else if (REAL_VALUES_LESS (r, dconst0)
- && REAL_VALUES_LESS (fvptr[3], r))
- {
- memcpy (d, &dconst0, sizeof (REAL_VALUE_TYPE));
- return 1;
- }
- }
+static GTY(()) int alpha_funcs_num;
- return 0;
-}
-\f
#if TARGET_ABI_OPEN_VMS
/* Return the VMS argument type corresponding to MODE. */
return GEN_INT (regval);
}
\f
-#include <splay-tree.h>
-
-/* Structure to collect function names for final output
- in link section. */
-
-enum links_kind {KIND_UNUSED, KIND_LOCAL, KIND_EXTERN};
-
-struct alpha_links
-{
- rtx linkage;
- enum links_kind kind;
-};
-
-static splay_tree alpha_links;
-
-static int mark_alpha_links_node PARAMS ((splay_tree_node, void *));
-static void mark_alpha_links PARAMS ((void *));
-static int alpha_write_one_linkage PARAMS ((splay_tree_node, void *));
-
-/* Protect alpha_links from garbage collection. */
-
-static int
-mark_alpha_links_node (node, data)
- splay_tree_node node;
- void *data ATTRIBUTE_UNUSED;
-{
- struct alpha_links *links = (struct alpha_links *) node->value;
- ggc_mark_rtx (links->linkage);
- return 0;
-}
-
-static void
-mark_alpha_links (ptr)
- void *ptr;
-{
- splay_tree tree = *(splay_tree *) ptr;
- splay_tree_foreach (tree, mark_alpha_links_node, NULL);
-}
-
/* 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.
if (name[0] == '*')
name++;
- if (alpha_links)
+ if (is_local)
+ {
+ struct alpha_funcs *cfaf;
+
+ if (!alpha_funcs_tree)
+ alpha_funcs_tree = splay_tree_new_ggc ((splay_tree_compare_fn)
+ splay_tree_compare_pointers);
+
+ cfaf = (struct alpha_funcs *) ggc_alloc (sizeof (struct alpha_funcs));
+
+ cfaf->links = 0;
+ cfaf->num = ++alpha_funcs_num;
+
+ splay_tree_insert (alpha_funcs_tree,
+ (splay_tree_key) current_function_decl,
+ (splay_tree_value) cfaf);
+ }
+
+ if (alpha_links_tree)
{
/* Is this name already defined? */
- node = splay_tree_lookup (alpha_links, (splay_tree_key) name);
+ node = splay_tree_lookup (alpha_links_tree, (splay_tree_key) name);
if (node)
{
al = (struct alpha_links *) node->value;
if (is_local)
{
/* Defined here but external assumed. */
- if (al->kind == KIND_EXTERN)
- al->kind = KIND_LOCAL;
+ if (al->lkind == KIND_EXTERN)
+ al->lkind = KIND_LOCAL;
}
else
{
/* Used here but unused assumed. */
- if (al->kind == KIND_UNUSED)
- al->kind = KIND_LOCAL;
+ if (al->lkind == KIND_UNUSED)
+ al->lkind = KIND_LOCAL;
}
return al->linkage;
}
}
else
- {
- alpha_links = splay_tree_new ((splay_tree_compare_fn) strcmp,
- (splay_tree_delete_key_fn) free,
- (splay_tree_delete_key_fn) free);
- ggc_add_root (&alpha_links, 1, 1, mark_alpha_links);
- }
+ alpha_links_tree = splay_tree_new_ggc ((splay_tree_compare_fn) strcmp);
- al = (struct alpha_links *) xmalloc (sizeof (struct alpha_links));
- name = xstrdup (name);
+ al = (struct alpha_links *) ggc_alloc (sizeof (struct alpha_links));
+ name = ggc_strdup (name);
/* Assume external if no definition. */
- al->kind = (is_local ? KIND_UNUSED : KIND_EXTERN);
+ al->lkind = (is_local ? KIND_UNUSED : KIND_EXTERN);
/* Ensure we have an IDENTIFIER so assemble_name can mark it used. */
get_identifier (name);
ggc_alloc_string (linksym, name_len + 5));
}
- splay_tree_insert (alpha_links, (splay_tree_key) name,
+ splay_tree_insert (alpha_links_tree, (splay_tree_key) name,
(splay_tree_value) al);
return al->linkage;
}
+rtx
+alpha_use_linkage (linkage, cfundecl, lflag, rflag)
+ rtx linkage;
+ tree cfundecl;
+ int lflag;
+ int rflag;
+{
+ splay_tree_node cfunnode;
+ struct alpha_funcs *cfaf;
+ struct alpha_links *al;
+ const char *name = XSTR (linkage, 0);
+
+ cfaf = (struct alpha_funcs *) 0;
+ al = (struct alpha_links *) 0;
+
+ cfunnode = splay_tree_lookup (alpha_funcs_tree, (splay_tree_key) cfundecl);
+ cfaf = (struct alpha_funcs *) cfunnode->value;
+
+ if (cfaf->links)
+ {
+ splay_tree_node lnode;
+
+ /* Is this name already defined? */
+
+ lnode = splay_tree_lookup (cfaf->links, (splay_tree_key) name);
+ if (lnode)
+ al = (struct alpha_links *) lnode->value;
+ }
+ else
+ cfaf->links = splay_tree_new_ggc ((splay_tree_compare_fn) strcmp);
+
+ if (!al)
+ {
+ size_t name_len;
+ size_t buflen;
+ char buf [512];
+ char *linksym;
+ splay_tree_node node = 0;
+ struct alpha_links *anl;
+
+ if (name[0] == '*')
+ name++;
+
+ name_len = strlen (name);
+
+ al = (struct alpha_links *) ggc_alloc (sizeof (struct alpha_links));
+ al->num = cfaf->num;
+
+ node = splay_tree_lookup (alpha_links_tree, (splay_tree_key) name);
+ if (node)
+ {
+ anl = (struct alpha_links *) node->value;
+ al->lkind = anl->lkind;
+ }
+
+ sprintf (buf, "$%d..%s..lk", cfaf->num, name);
+ buflen = strlen (buf);
+ linksym = alloca (buflen + 1);
+ memcpy (linksym, buf, buflen + 1);
+
+ al->linkage = gen_rtx_SYMBOL_REF
+ (Pmode, ggc_alloc_string (linksym, buflen + 1));
+
+ splay_tree_insert (cfaf->links, (splay_tree_key) name,
+ (splay_tree_value) al);
+ }
+
+ if (rflag)
+ al->rkind = KIND_CODEADDR;
+ else
+ al->rkind = KIND_LINKAGE;
+
+ if (lflag)
+ return gen_rtx_MEM (Pmode, plus_constant (al->linkage, 8));
+ else
+ return al->linkage;
+}
+
static int
alpha_write_one_linkage (node, data)
splay_tree_node node;
void *data;
{
const char *const name = (const char *) node->key;
- struct alpha_links *links = (struct alpha_links *) node->value;
+ struct alpha_links *link = (struct alpha_links *) node->value;
FILE *stream = (FILE *) data;
- if (links->kind == KIND_UNUSED
- || ! TREE_SYMBOL_REFERENCED (get_identifier (name)))
- return 0;
-
- fprintf (stream, "$%s..lk:\n", name);
- if (links->kind == KIND_LOCAL)
+ fprintf (stream, "$%d..%s..lk:\n", link->num, name);
+ if (link->rkind == KIND_CODEADDR)
{
- /* Local and used, build linkage pair. */
- fprintf (stream, "\t.quad %s..en\n", name);
- fprintf (stream, "\t.quad %s\n", name);
+ if (link->lkind == KIND_LOCAL)
+ {
+ /* Local and used */
+ fprintf (stream, "\t.quad %s..en\n", name);
+ }
+ else
+ {
+ /* External and used, request code address. */
+ fprintf (stream, "\t.code_address %s\n", name);
+ }
}
else
{
- /* External and used, request linkage pair. */
- fprintf (stream, "\t.linkage %s\n", name);
+ if (link->lkind == KIND_LOCAL)
+ {
+ /* Local and used, build linkage pair. */
+ fprintf (stream, "\t.quad %s..en\n", name);
+ fprintf (stream, "\t.quad %s\n", name);
+ }
+ else
+ {
+ /* External and used, request linkage pair. */
+ fprintf (stream, "\t.linkage %s\n", name);
+ }
}
return 0;
}
-void
-alpha_write_linkage (stream)
- FILE *stream;
+static void
+alpha_write_linkage (stream, funname, fundecl)
+ FILE *stream;
+ const char *funname;
+ tree fundecl;
{
- if (alpha_links)
+ splay_tree_node node;
+ struct alpha_funcs *func;
+
+ link_section ();
+ fprintf (stream, "\t.align 3\n");
+ node = splay_tree_lookup (alpha_funcs_tree, (splay_tree_key) fundecl);
+ func = (struct alpha_funcs *) node->value;
+
+ fputs ("\t.name ", stream);
+ assemble_name (stream, funname);
+ fputs ("..na\n", stream);
+ ASM_OUTPUT_LABEL (stream, funname);
+ fprintf (stream, "\t.pdesc ");
+ assemble_name (stream, funname);
+ fprintf (stream, "..en,%s\n",
+ alpha_procedure_type == PT_STACK ? "stack"
+ : alpha_procedure_type == PT_REGISTER ? "reg" : "null");
+
+ if (func->links)
{
- readonly_section ();
- fprintf (stream, "\t.align 3\n");
- splay_tree_foreach (alpha_links, alpha_write_one_linkage, stream);
+ splay_tree_foreach (func->links, alpha_write_one_linkage, stream);
+ /* splay_tree_delete (func->links); */
}
}
return NULL_RTX;
}
+rtx
+alpha_use_linkage (linkage, cfundecl, lflag, rflag)
+ rtx linkage ATTRIBUTE_UNUSED;
+ tree cfundecl ATTRIBUTE_UNUSED;
+ int lflag ATTRIBUTE_UNUSED;
+ int rflag ATTRIBUTE_UNUSED;
+{
+ return NULL_RTX;
+}
+
#endif /* TARGET_ABI_OPEN_VMS */
\f
#if TARGET_ABI_UNICOSMK
prefix the module name with a '$' if necessary. */
if (!ISALPHA (*name))
- fprintf (file, "$%s", name);
- else
- fputs (name, file);
+ putc ('$', file);
+ output_clean_symbol_name (file, name);
}
/* Output text that to appear at the beginning of an assembler file. */
/* Generate a section name for decl and associate it with the
declaration. */
-void
+static void
unicosmk_unique_section (decl, reloc)
tree decl;
int reloc ATTRIBUTE_UNUSED;
abort ();
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
- STRIP_NAME_ENCODING (name, name);
+ name = default_strip_name_encoding (name);
len = strlen (name);
if (TREE_CODE (decl) == FUNCTION_DECL)
{
if (DECL_P (decl)
&& (TREE_PUBLIC (decl) || TREE_CODE (decl) == FUNCTION_DECL))
- UNIQUE_SECTION (decl, 0);
+ unicosmk_unique_section (decl, 0);
}
/* Output an alignment directive. We have to use the macro 'gcc@code@align'
int vlen = XVECLEN (body, 0);
int idx;
- ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (lab));
+ (*targetm.asm_out.internal_label) (file, "L", CODE_LABEL_NUMBER (lab));
for (idx = 0; idx < vlen; idx++)
{
mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 56));
set_mem_alias_set (mem, alpha_sr_alias_set);
FRP (emit_move_insn (mem, gen_rtx_REG (DImode, REG_RA)));
- (*imaskP) &= ~(1L << REG_RA);
+ (*imaskP) &= ~(1UL << REG_RA);
/* Save the old frame pointer. */
mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 48));
set_mem_alias_set (mem, alpha_sr_alias_set);
FRP (emit_move_insn (mem, hard_frame_pointer_rtx));
- (*imaskP) &= ~(1L << HARD_FRAME_POINTER_REGNUM);
+ (*imaskP) &= ~(1UL << HARD_FRAME_POINTER_REGNUM);
emit_insn (gen_blockage ());
x = XEXP (x, 0);
if (GET_CODE (x) != SYMBOL_REF)
abort ();
- fnname = XSTR (x, 0);
- STRIP_NAME_ENCODING (fnname, fnname);
+ fnname = default_name_encoding (XSTR (x, 0));
len = strlen (fnname);
if (len + SSIB_PREFIX_LEN > 255)
for (x = machine->first_ciw; x; x = XEXP (x, 1))
{
ciw = XEXP (x, 0);
- fprintf (file, "\t.quad\t");
#if HOST_BITS_PER_WIDE_INT == 32
- fprintf (file, HOST_WIDE_INT_PRINT_DOUBLE_HEX,
+ fprintf (file, "\t.quad\t" HOST_WIDE_INT_PRINT_DOUBLE_HEX "\n",
CONST_DOUBLE_HIGH (ciw), CONST_DOUBLE_LOW (ciw));
#else
- fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (ciw));
+ fprintf (file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n", INTVAL (ciw));
#endif
- fprintf (file, "\n");
}
}
/* List of identifiers for which an extern declaration might have to be
emitted. */
+/* FIXME: needs to use GC, so it can be saved and restored for PCH. */
struct unicosmk_extern_list
{
/* We have to strip the encoding and possibly remove user_label_prefix
from the identifier in order to handle -fleading-underscore and
explicit asm names correctly (cf. gcc.dg/asm-names-1.c). */
- STRIP_NAME_ENCODING (real_name, p->name);
+ real_name = default_strip_name_encoding (p->name);
if (len && p->name[0] == '*'
&& !memcmp (real_name, user_label_prefix, len))
real_name += len;
struct unicosmk_extern_list *p;
p = (struct unicosmk_extern_list *)
- permalloc (sizeof (struct unicosmk_extern_list));
+ xmalloc (sizeof (struct unicosmk_extern_list));
p->next = unicosmk_extern_head;
p->name = name;
unicosmk_extern_head = p;
/* Structure to collect identifiers which have been replaced by DEX
expressions. */
+/* FIXME: needs to use GC, so it can be saved and restored for PCH. */
struct unicosmk_dex {
struct unicosmk_dex *next;
--i;
}
- dex = (struct unicosmk_dex *) permalloc (sizeof (struct unicosmk_dex));
+ dex = (struct unicosmk_dex *) xmalloc (sizeof (struct unicosmk_dex));
dex->name = name;
dex->next = unicosmk_dex_list;
unicosmk_dex_list = dex;
}
#endif /* TARGET_ABI_UNICOSMK */
+
+#include "gt-alpha.h"
+