/* 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 "tm_p.h"
#include "target.h"
#include "target-def.h"
-
-/* External data. */
-extern int rtx_equal_function_value_matters;
+#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 bool local_symbol_p
+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 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_ALIGNED_HI_OP "\t.word\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
+
+/* Default unaligned ops are provided for ELF systems. To get unaligned
+ data for non-ELF systems, we have to turn off auto alignment. */
+#ifndef OBJECT_FORMAT_ELF
+#undef TARGET_ASM_UNALIGNED_HI_OP
+#define TARGET_ASM_UNALIGNED_HI_OP "\t.align 0\n\t.word\t"
+#undef TARGET_ASM_UNALIGNED_SI_OP
+#define TARGET_ASM_UNALIGNED_SI_OP "\t.align 0\n\t.long\t"
+#undef TARGET_ASM_UNALIGNED_DI_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_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. */
+/* Return 1 if OP is the zero constant for MODE. */
int
-fp0_operand (op, mode)
+const0_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. */
-
-int
-reg_or_fp0_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 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:
case SYMBOL_REF:
case CONST:
if (TARGET_EXPLICIT_RELOCS)
- return 0;
+ {
+ /* We don't split symbolic operands into something unintelligable
+ until after reload, but we do not wish non-small, non-global
+ symbolic operands to be reconstructed from their high/lo_sum
+ form. */
+ return (small_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. */
return mode == ptr_mode || mode == DImode;
+ case HIGH:
+ return (TARGET_EXPLICIT_RELOCS
+ && local_symbolic_operand (XEXP (op, 0), mode));
+
case REG:
case ADDRESSOF:
return 1;
&& 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
- && ! profile_flag)
- 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. */
if (GET_CODE (op) == REG)
{
if (TARGET_ABI_OSF)
- return REGNO (op) == 27;
+ {
+ /* Disallow virtual registers to cope with pathalogical test cases
+ such as compile/930117-1.c in which the virtual reg decomposes
+ to the frame pointer. Which is a hard reg that is not $27. */
+ return (REGNO (op) == 27 || REGNO (op) > LAST_VIRTUAL_REGISTER);
+ }
else
return 1;
}
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. */
register rtx op;
enum machine_mode mode;
{
- if (GET_CODE (op) == SUBREG)
+ if (GET_CODE (op) != REG)
return 0;
return register_operand (op, mode);
}
return 0;
}
+/* Implements CONST_OK_FOR_LETTER_P. Return true if the value matches
+ the range defined for C in [I-P]. */
+
+bool
+alpha_const_ok_for_letter_p (value, c)
+ HOST_WIDE_INT value;
+ int c;
+{
+ switch (c)
+ {
+ case 'I':
+ /* An unsigned 8 bit constant. */
+ return (unsigned HOST_WIDE_INT) value < 0x100;
+ case 'J':
+ /* The constant zero. */
+ return value == 0;
+ case 'K':
+ /* A signed 16 bit constant. */
+ return (unsigned HOST_WIDE_INT) (value + 0x8000) < 0x10000;
+ case 'L':
+ /* A shifted signed 16 bit constant appropriate for LDAH. */
+ return ((value & 0xffff) == 0
+ && ((value) >> 31 == -1 || value >> 31 == 0));
+ case 'M':
+ /* A constant that can be AND'ed with using a ZAP insn. */
+ return zap_mask (value);
+ case 'N':
+ /* A complemented unsigned 8 bit constant. */
+ return (unsigned HOST_WIDE_INT) (~ value) < 0x100;
+ case 'O':
+ /* A negated unsigned 8 bit constant. */
+ return (unsigned HOST_WIDE_INT) (- value) < 0x100;
+ case 'P':
+ /* The constant 1, 2 or 3. */
+ return value == 1 || value == 2 || value == 3;
+
+ default:
+ return false;
+ }
+}
+
+/* Implements CONST_DOUBLE_OK_FOR_LETTER_P. Return true if VALUE
+ matches for C in [GH]. */
+
+bool
+alpha_const_double_ok_for_letter_p (value, c)
+ rtx value;
+ int c;
+{
+ switch (c)
+ {
+ case 'G':
+ /* The floating point zero constant. */
+ return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT
+ && value == CONST0_RTX (GET_MODE (value)));
+
+ case 'H':
+ /* A valid operand of a ZAP insn. */
+ return (GET_MODE (value) == VOIDmode
+ && zap_mask (CONST_DOUBLE_LOW (value))
+ && zap_mask (CONST_DOUBLE_HIGH (value)));
+
+ default:
+ return false;
+ }
+}
+
+/* Implements CONST_DOUBLE_OK_FOR_LETTER_P. Return true if VALUE
+ matches for C. */
+
+bool
+alpha_extra_constraint (value, c)
+ rtx value;
+ int c;
+{
+ switch (c)
+ {
+ case 'Q':
+ return normal_memory_operand (value, VOIDmode);
+ case 'R':
+ return direct_call_operand (value, Pmode);
+ case 'S':
+ return (GET_CODE (value) == CONST_INT
+ && (unsigned HOST_WIDE_INT) INTVAL (value) < 64);
+ case 'T':
+ 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 1 if this function can directly return via $26. */
int
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)
+ 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;
{
- 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. */
-
- 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;
- 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 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;
+ }
- 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;
- }
-
- /* 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 true;
}
- /* If we're managing explicit relocations, LO_SUM is valid. */
- else if (TARGET_EXPLICIT_RELOCS && GET_CODE (x) == LO_SUM)
+ /* If we're managing explicit relocations, LO_SUM is valid, as
+ are small data symbols. */
+ else if (TARGET_EXPLICIT_RELOCS)
{
- rtx ofs = XEXP (x, 1);
- x = XEXP (x, 0);
-
- /* Discard non-paradoxical subregs. */
- if (GET_CODE (x) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (x))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
- x = SUBREG_REG (x);
-
- /* Must have a valid base register. */
- if (! (REG_P (x)
- && (strict
- ? STRICT_REG_OK_FOR_BASE_P (x)
- : NONSTRICT_REG_OK_FOR_BASE_P (x))))
- return false;
-
- /* The symbol must be local. */
- if (local_symbolic_operand (ofs, Pmode))
+ if (small_symbolic_operand (x, Pmode))
return true;
+
+ if (GET_CODE (x) == LO_SUM)
+ {
+ rtx ofs = XEXP (x, 1);
+ x = XEXP (x, 0);
+
+ /* Discard non-paradoxical subregs. */
+ if (GET_CODE (x) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (x))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
+ x = SUBREG_REG (x);
+
+ /* Must have a valid base register. */
+ if (! (REG_P (x)
+ && (strict
+ ? STRICT_REG_OK_FOR_BASE_P (x)
+ : NONSTRICT_REG_OK_FOR_BASE_P (x))))
+ return false;
+
+ /* The symbol must be local. */
+ 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. */
rtx
-alpha_legitimize_address (x, oldx, mode)
+alpha_legitimize_address (x, scratch, mode)
rtx x;
- rtx oldx ATTRIBUTE_UNUSED;
+ rtx scratch;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
HOST_WIDE_INT addend;
part of the CONST_INT. Then load FOO plus any high-order part of the
CONST_INT into a register. Our address is (plus reg low-part-const).
This is done to reduce the number of GOT entries. */
- if (GET_CODE (x) == CONST
+ if (!no_new_pseudos
+ && GET_CODE (x) == CONST
&& GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
{
/* If we have a (plus reg const), emit the load as in (2), then add
the two registers, and finally generate (plus reg low-part-const) as
our address. */
- if (GET_CODE (x) == PLUS
+ if (!no_new_pseudos
+ && GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == REG
&& GET_CODE (XEXP (x, 1)) == CONST
&& GET_CODE (XEXP (XEXP (x, 1), 0)) == PLUS
/* If this is a local symbol, split the address into HIGH/LO_SUM parts. */
if (TARGET_EXPLICIT_RELOCS && symbolic_operand (x, Pmode))
{
- rtx scratch;
- 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))
- scratch = pic_offset_table_rtx;
- else
- {
- rtx insn, tmp;
+ 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);
- scratch = gen_reg_rtx (Pmode);
+ 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 ();
- tmp = gen_rtx_HIGH (Pmode, x);
- tmp = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, tmp);
- insn = emit_insn (gen_rtx_SET (VOIDmode, scratch, tmp));
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, tmp,
- REG_NOTES (insn));
+ 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);
- return gen_rtx_LO_SUM (Pmode, scratch, x);
+ 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);
}
- else
+
+ if (local_symbolic_operand (x, Pmode))
{
- scratch = gen_reg_rtx (Pmode);
- emit_insn (gen_movdi_er_high_g (scratch, pic_offset_table_rtx,
- x, const0_rtx));
- /* ??? FIXME: Tag the use of scratch with a lituse. */
- return scratch;
+ 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);
+ }
}
}
split_addend:
{
- HOST_WIDE_INT lowpart = (addend & 0xffff) - 2 * (addend & 0x8000);
- HOST_WIDE_INT highpart = addend - lowpart;
- x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (highpart),
- NULL_RTX, 1, OPTAB_LIB_WIDEN);
- return plus_constant (x, lowpart);
+ HOST_WIDE_INT low, high;
+
+ low = ((addend & 0xffff) ^ 0x8000) - 0x8000;
+ addend -= low;
+ high = ((addend & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ addend -= high;
+
+ if (addend)
+ x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (addend),
+ (no_new_pseudos ? scratch : NULL_RTX),
+ 1, OPTAB_LIB_WIDEN);
+ if (high)
+ x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (high),
+ (no_new_pseudos ? scratch : NULL_RTX),
+ 1, OPTAB_LIB_WIDEN);
+
+ return plus_constant (x, low);
}
}
+/* 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))
+ so that sched2 has the proper dependency information. */
+
+int
+some_small_symbolic_operand (x, mode)
+ rtx x;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return for_each_rtx (&x, some_small_symbolic_operand_1, NULL);
+}
+
+static int
+some_small_symbolic_operand_1 (px, data)
+ rtx *px;
+ void *data ATTRIBUTE_UNUSED;
+{
+ rtx x = *px;
+
+ /* Don't re-split. */
+ if (GET_CODE (x) == LO_SUM)
+ return -1;
+
+ return small_symbolic_operand (x, Pmode) != 0;
+}
+
+rtx
+split_small_symbolic_operand (x)
+ rtx x;
+{
+ x = copy_insn (x);
+ for_each_rtx (&x, split_small_symbolic_operand_1, NULL);
+ return x;
+}
+
+static int
+split_small_symbolic_operand_1 (px, data)
+ rtx *px;
+ void *data ATTRIBUTE_UNUSED;
+{
+ rtx x = *px;
+
+ /* Don't re-split. */
+ if (GET_CODE (x) == LO_SUM)
+ return -1;
+
+ if (small_symbolic_operand (x, Pmode))
+ {
+ x = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, x);
+ *px = x;
+ return -1;
+ }
+
+ 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
if (GET_CODE (base) == PLUS)
offset += INTVAL (XEXP (base, 1)), base = XEXP (base, 0);
- *paligned_mem = gen_rtx_MEM (SImode, plus_constant (base, offset & ~3));
- MEM_COPY_ATTRIBUTES (*paligned_mem, ref);
-
- /* Sadly, we cannot use alias sets here because we may overlap other
- data in a different alias set. */
- set_mem_alias_set (*paligned_mem, 0);
+ *paligned_mem
+ = widen_memory_access (ref, SImode, (offset & ~3) - offset);
if (WORDS_BIG_ENDIAN)
*pbitnum = GEN_INT (32 - (GET_MODE_BITSIZE (GET_MODE (ref))
return plus_constant (base, offset + extra_offset);
}
+/* On the Alpha, all (non-symbolic) constants except zero go into
+ a floating-point register via memory. Note that we cannot
+ return anything that is not a subset of CLASS, and that some
+ symbolic constants cannot be dropped to memory. */
+
+enum reg_class
+alpha_preferred_reload_class(x, class)
+ rtx x;
+ enum reg_class class;
+{
+ /* Zero is present in any register class. */
+ if (x == CONST0_RTX (GET_MODE (x)))
+ return class;
+
+ /* These sorts of constants we can easily drop to memory. */
+ if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
+ {
+ if (class == FLOAT_REGS)
+ return NO_REGS;
+ if (class == ALL_REGS)
+ return GENERAL_REGS;
+ return class;
+ }
+
+ /* All other kinds of constants should not (and in the case of HIGH
+ cannot) be dropped to memory -- instead we use a GENERAL_REGS
+ secondary reload. */
+ if (CONSTANT_P (x))
+ return (class == ALL_REGS ? GENERAL_REGS : class);
+
+ return class;
+}
+
/* Loading and storing HImode or QImode values to and from memory
usually requires a scratch register. The exceptions are loading
QImode and HImode from an aligned address to a general register
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)
HOST_WIDE_INT c;
int n;
{
- rtx pat;
+ rtx result = 0;
+ rtx orig_target = target;
int i;
+ /* If we can't make any pseudos, TARGET is an SImode hard register, we
+ can't load this constant in one insn, do this in DImode. */
+ if (no_new_pseudos && mode == SImode
+ && GET_CODE (target) == REG && REGNO (target) < FIRST_PSEUDO_REGISTER
+ && (result = alpha_emit_set_const_1 (target, mode, c, 1)) == 0)
+ {
+ target = gen_lowpart (DImode, target);
+ mode = DImode;
+ }
+
/* Try 1 insn, then 2, then up to N. */
for (i = 1; i <= n; i++)
- if ((pat = alpha_emit_set_const_1 (target, mode, c, i)) != 0)
- return pat;
+ {
+ result = alpha_emit_set_const_1 (target, mode, c, i);
+ if (result)
+ {
+ rtx insn = get_last_insn ();
+ rtx set = single_set (insn);
+ if (! CONSTANT_P (SET_SRC (set)))
+ set_unique_reg_note (get_last_insn (), REG_EQUAL, GEN_INT (c));
+ break;
+ }
+ }
- return 0;
+ /* Allow for the case where we changed the mode of TARGET. */
+ if (result == target)
+ result = orig_target;
+
+ return result;
}
/* Internal routine for the above to check for N or below insns. */
int i, bits;
/* Use a pseudo if highly optimizing and still generating RTL. */
rtx subtarget
- = (flag_expensive_optimizations && rtx_equal_function_value_matters
- ? 0 : target);
- rtx temp;
-
-#if HOST_BITS_PER_WIDE_INT == 64
- /* We are only called for SImode and DImode. If this is SImode, ensure that
- we are sign extended to a full word. This does not make any sense when
- cross-compiling on a narrow machine. */
-
- if (mode == SImode)
- c = ((c & 0xffffffff) ^ 0x80000000) - 0x80000000;
-#endif
+ = (flag_expensive_optimizations && !no_new_pseudos ? 0 : target);
+ rtx temp, insn;
/* If this is a sign-extended 32-bit constant, we can do this in at most
three insns, so do it if we have enough insns left. We always have
{
temp = copy_to_suggested_reg (GEN_INT (high << 16), subtarget, mode);
+ /* As of 2002-02-23, addsi3 is only available when not optimizing.
+ This means that if we go through expand_binop, we'll try to
+ generate extensions, etc, which will require new pseudos, which
+ will fail during some split phases. The SImode add patterns
+ still exist, but are not named. So build the insns by hand. */
+
if (extra != 0)
- temp = expand_binop (mode, add_optab, temp, GEN_INT (extra << 16),
- subtarget, 0, OPTAB_WIDEN);
+ {
+ if (! subtarget)
+ subtarget = gen_reg_rtx (mode);
+ insn = gen_rtx_PLUS (mode, temp, GEN_INT (extra << 16));
+ insn = gen_rtx_SET (VOIDmode, subtarget, insn);
+ emit_insn (insn);
+ temp = subtarget;
+ }
- return expand_binop (mode, add_optab, temp, GEN_INT (low),
- target, 0, OPTAB_WIDEN);
+ if (target == NULL)
+ target = gen_reg_rtx (mode);
+ insn = gen_rtx_PLUS (mode, temp, GEN_INT (low));
+ insn = gen_rtx_SET (VOIDmode, target, insn);
+ emit_insn (insn);
+ return target;
}
}
we can't make pseudos, we can't do anything since the expand_binop
and expand_unop calls will widen and try to make pseudos. */
- if (n == 1
- || (mode == SImode && ! rtx_equal_function_value_matters))
+ if (n == 1 || (mode == SImode && no_new_pseudos))
return 0;
/* Next, see if we can load a related constant and then shift and possibly
&& ! reg_or_0_operand (operands[1], mode))
operands[1] = force_reg (mode, operands[1]);
- if (TARGET_EXPLICIT_RELOCS && symbolic_operand (operands[1], mode))
+ /* Allow legitimize_address to perform some simplifications. */
+ if (mode == Pmode && symbolic_operand (operands[1], mode))
{
- if (local_symbolic_operand (operands[1], mode))
- {
- rtx scratch;
-
- if (small_symbolic_operand (operands[1], Pmode))
- scratch = pic_offset_table_rtx;
- else
- {
- rtx insn, tmp;
+ rtx tmp;
- scratch = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
-
- tmp = gen_rtx_HIGH (Pmode, operands[1]);
- tmp = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, tmp);
- insn = emit_insn (gen_rtx_SET (VOIDmode, scratch, tmp));
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, tmp,
- REG_NOTES (insn));
- }
+ /* 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.
- operands[1] = gen_rtx_LO_SUM (Pmode, scratch, operands[1]);
- return false;
- }
- else
+ 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_high_g (operands[0], pic_offset_table_rtx,
- operands[1], const0_rtx));
+ 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;
+ }
}
/* Early out for non-constants and valid constants. */
}
/* 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));
1 true
Convert the compare against the raw return value. */
- if (code == UNORDERED || code == ORDERED)
- cmp_code = EQ;
- else
- cmp_code = code;
+ switch (code)
+ {
+ case UNORDERED:
+ cmp_code = EQ;
+ code = LT;
+ break;
+ case ORDERED:
+ cmp_code = EQ;
+ code = GE;
+ break;
+ case NE:
+ cmp_code = NE;
+ code = NE;
+ break;
+ default:
+ cmp_code = code;
+ code = GT;
+ break;
+ }
op0 = alpha_emit_xfloating_compare (cmp_code, op0, op1);
op1 = const0_rtx;
alpha_compare.fp_p = 0;
-
- if (code == UNORDERED)
- code = LT;
- else if (code == ORDERED)
- code = GE;
- else
- code = GT;
}
/* The general case: fold the comparison code to the types of compares
}
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)
else
subtarget = target;
}
+ /* Below, we must be careful to use copy_rtx on target and subtarget
+ in intermediate insns, as they may be a subreg rtx, which may not
+ be shared. */
if (f == 0 && exact_log2 (diff) > 0
/* On EV6, we've got enough shifters to make non-arithmatic shifts
&& (diff <= 8 || alpha_cpu == PROCESSOR_EV6))
{
tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx);
- emit_insn (gen_rtx_SET (VOIDmode, subtarget, tmp));
+ emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp));
- tmp = gen_rtx_ASHIFT (DImode, subtarget, GEN_INT (exact_log2 (t)));
+ tmp = gen_rtx_ASHIFT (DImode, copy_rtx (subtarget),
+ GEN_INT (exact_log2 (t)));
emit_insn (gen_rtx_SET (VOIDmode, target, tmp));
}
else if (f == 0 && t == -1)
{
tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx);
- emit_insn (gen_rtx_SET (VOIDmode, subtarget, tmp));
+ emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp));
- emit_insn (gen_negdi2 (target, subtarget));
+ emit_insn (gen_negdi2 (target, copy_rtx (subtarget)));
}
else if (diff == 1 || diff == 4 || diff == 8)
{
rtx add_op;
tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx);
- emit_insn (gen_rtx_SET (VOIDmode, subtarget, tmp));
+ emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp));
if (diff == 1)
- emit_insn (gen_adddi3 (target, subtarget, GEN_INT (f)));
+ emit_insn (gen_adddi3 (target, copy_rtx (subtarget), GEN_INT (f)));
else
{
add_op = GEN_INT (f);
if (sext_add_operand (add_op, mode))
{
- tmp = gen_rtx_MULT (DImode, subtarget, GEN_INT (diff));
+ tmp = gen_rtx_MULT (DImode, copy_rtx (subtarget),
+ GEN_INT (diff));
tmp = gen_rtx_PLUS (DImode, tmp, add_op);
emit_insn (gen_rtx_SET (VOIDmode, target, tmp));
}
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;
-
- break;
- }
+ /* Everything else handled in DFA bypasses now. */
- /* 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
alpha_gp_save_rtx ()
{
- return get_hard_reg_initial_val (DImode, 29);
+ rtx r = get_hard_reg_initial_val (DImode, 29);
+ if (GET_CODE (r) != MEM)
+ r = gen_mem_addressof (r, NULL_TREE, /*rescan=*/true);
+ return r;
}
static int
{
rtx top;
-#ifdef ASM_OUTPUT_MI_THUNK
- if (current_function_is_thunk)
- return 0;
-#endif
if (!has_hard_reg_initial_val (Pmode, REG_RA))
return regs_ever_live[REG_RA];
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++;
output_operand_lossage ("invalid %%H value");
break;
+ case 'J':
+ {
+ 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':
/* If this operand is the constant zero, write it as "$31". */
if (GET_CODE (x) == REG)
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
OPTAB_WIDEN);
temp = expand_shift (RSHIFT_EXPR, Pmode, temp,
build_int_2 (2, 0), NULL_RTX, 1);
- temp = expand_and (gen_lowpart (SImode, temp), GEN_INT (0x3fff), 0);
+ temp = expand_and (SImode, gen_lowpart (SImode, temp),
+ GEN_INT (0x3fff), 0);
/* Merge in the hint. */
addr = memory_address (SImode, plus_constant (tramp, jmpofs));
temp1 = force_reg (SImode, gen_rtx_MEM (SImode, addr));
- temp1 = expand_and (temp1, GEN_INT (0xffffc000), NULL_RTX);
+ temp1 = expand_and (SImode, temp1, GEN_INT (0xffffc000), NULL_RTX);
temp1 = expand_binop (SImode, ior_optab, temp1, temp, temp1, 1,
OPTAB_WIDEN);
emit_move_insn (gen_rtx_MEM (SImode, addr), temp1);
}
#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)
if (TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK)
return ptr_type_node;
- record = make_lang_type (RECORD_TYPE);
+ record = (*lang_hooks.types.make_type) (RECORD_TYPE);
type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
TREE_CHAIN (record) = type_decl;
TYPE_NAME (record) = type_decl;
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)
{
alpha_va_arg (valist, type)
tree valist, type;
{
- HOST_WIDE_INT tsize;
rtx addr;
- tree t;
+ tree t, type_size, rounded_size;
tree offset_field, base_field, addr_tree, addend;
tree wide_type, wide_ofs;
int indirect = 0;
if (TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK)
return std_expand_builtin_va_arg (valist, type);
- tsize = ((TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_UNIT + 7) / 8) * 8;
+ if (type == error_mark_node
+ || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL
+ || TREE_OVERFLOW (type_size))
+ rounded_size = size_zero_node;
+ else
+ rounded_size = fold (build (MULT_EXPR, sizetype,
+ fold (build (TRUNC_DIV_EXPR, sizetype,
+ fold (build (PLUS_EXPR, sizetype,
+ type_size,
+ size_int (7))),
+ size_int (8))),
+ size_int (8)));
base_field = TYPE_FIELDS (TREE_TYPE (valist));
offset_field = TREE_CHAIN (base_field);
offset_field = build (COMPONENT_REF, TREE_TYPE (offset_field),
valist, offset_field);
+ /* If the type could not be passed in registers, skip the block
+ reserved for the registers. */
+ if (MUST_PASS_IN_STACK (TYPE_MODE (type), type))
+ {
+ t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field,
+ build (MAX_EXPR, TREE_TYPE (offset_field),
+ offset_field, build_int_2 (6*8, 0)));
+ TREE_SIDE_EFFECTS (t) = 1;
+ 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));
if (TYPE_MODE (type) == TFmode || TYPE_MODE (type) == TCmode)
{
indirect = 1;
- tsize = UNITS_PER_WORD;
+ rounded_size = size_int (UNITS_PER_WORD);
}
else if (FLOAT_TYPE_P (type))
{
t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field,
build (PLUS_EXPR, TREE_TYPE (offset_field),
- offset_field, build_int_2 (tsize, 0)));
+ offset_field, rounded_size));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
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. */
+
+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
+
+ 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;
+
+ if (fcode >= ALPHA_BUILTIN_max)
+ internal_error ("bad builtin fcode");
+ icode = code_for_builtin[fcode];
+ if (icode == 0)
+ internal_error ("bad builtin fcode");
+
+ nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
+
+ for (arglist = TREE_OPERAND (exp, 1), arity = 0;
+ arglist;
+ arglist = TREE_CHAIN (arglist), arity++)
+ {
+ const struct insn_operand_data *insn_op;
+
+ tree arg = TREE_VALUE (arglist);
+ if (arg == error_mark_node)
+ return NULL_RTX;
+ if (arity > MAX_ARGS)
+ return NULL_RTX;
+
+ insn_op = &insn_data[icode].operand[arity + nonvoid];
+
+ op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
+
+ if (!(*insn_op->predicate) (op[arity], insn_op->mode))
+ op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+ }
+
+ if (nonvoid)
+ {
+ 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);
+ }
+
+ 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
prologue and epilogue code will do and write them out. */
descriptior to generate. */
/* Nonzero if we need a stack procedure. */
-static int alpha_is_stack_procedure;
+enum alpha_procedure_types {PT_NULL = 0, PT_REGISTER = 1, PT_STACK = 2};
+static enum alpha_procedure_types alpha_procedure_type;
/* Register number (either FP or SP) that is used to unwind the frame. */
static int vms_unwind_regno;
unsigned long fmask = 0;
unsigned int i;
-#ifdef ASM_OUTPUT_MI_THUNK
- if (!current_function_is_thunk)
-#endif
+ /* Irritatingly, there are two kinds of thunks -- those created with
+ 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)
{
- if (TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure)
- imask |= (1L << HARD_FRAME_POINTER_REGNUM);
+ *imaskP = 0;
+ *fmaskP = 0;
+ return;
+ }
- /* One for every register we have to save. */
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (! fixed_regs[i] && ! call_used_regs[i]
- && regs_ever_live[i] && i != REG_RA
- && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM))
- {
- if (i < 32)
- imask |= (1L << i);
- else
- fmask |= (1L << (i - 32));
- }
+ if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK)
+ imask |= (1UL << HARD_FRAME_POINTER_REGNUM);
- /* We need to restore these for the handler. */
- if (current_function_calls_eh_return)
- {
- for (i = 0; ; ++i)
- {
- unsigned regno = EH_RETURN_DATA_REGNO (i);
- if (regno == INVALID_REGNUM)
- break;
- imask |= 1L << regno;
- }
- }
+ /* One for every register we have to save. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (! fixed_regs[i] && ! call_used_regs[i]
+ && regs_ever_live[i] && i != REG_RA
+ && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM))
+ {
+ if (i < 32)
+ imask |= (1UL << i);
+ else
+ fmask |= (1UL << (i - 32));
+ }
+
+ /* We need to restore these for the handler. */
+ if (current_function_calls_eh_return)
+ for (i = 0; ; ++i)
+ {
+ unsigned regno = EH_RETURN_DATA_REGNO (i);
+ if (regno == INVALID_REGNUM)
+ break;
+ imask |= 1UL << regno;
+ }
- if (!TARGET_ABI_UNICOSMK)
- {
- /* 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);
- }
- }
+ /* 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 |= (1UL << REG_RA);
*imaskP = imask;
*fmaskP = fmask;
int
alpha_sa_size ()
{
+ unsigned long mask[2];
int sa_size = 0;
- int i;
+ int i, j;
-#ifdef ASM_OUTPUT_MI_THUNK
- if (current_function_is_thunk)
- sa_size = 0;
+ alpha_sa_mask (&mask[0], &mask[1]);
+
+ if (TARGET_ABI_UNICOSMK)
+ {
+ if (mask[0] || mask[1])
+ sa_size = 14;
+ }
else
-#endif
{
- if (TARGET_ABI_UNICOSMK)
- {
- for (i = 9; i < 15 && sa_size == 0; i++)
- if (! fixed_regs[i] && ! call_used_regs[i]
- && regs_ever_live[i])
- sa_size = 14;
- for (i = 32 + 2; i < 32 + 10 && sa_size == 0; i++)
- if (! fixed_regs[i] && ! call_used_regs[i]
- && regs_ever_live[i])
- sa_size = 14;
- }
- else
- {
- /* One for every register we have to save. */
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (! fixed_regs[i] && ! call_used_regs[i]
- && regs_ever_live[i] && i != REG_RA)
- sa_size++;
- }
+ for (j = 0; j < 2; ++j)
+ for (i = 0; i < 32; ++i)
+ if ((mask[j] >> i) & 1)
+ sa_size++;
}
if (TARGET_ABI_UNICOSMK)
use alloca and have not determined that we need a frame for other
reasons. */
- alpha_is_stack_procedure = sa_size != 0
- || alpha_ra_ever_killed ()
- || get_frame_size() != 0
- || current_function_outgoing_args_size
- || current_function_varargs
- || current_function_stdarg
- || current_function_calls_alloca
- || frame_pointer_needed;
+ alpha_procedure_type
+ = (sa_size || get_frame_size() != 0
+ || current_function_outgoing_args_size
+ || current_function_stdarg || current_function_calls_alloca
+ || frame_pointer_needed)
+ ? PT_STACK : PT_REGISTER;
/* Always reserve space for saving callee-saved registers if we
need a frame as required by the calling convention. */
- if (alpha_is_stack_procedure)
+ if (alpha_procedure_type == PT_STACK)
sa_size = 14;
}
else if (TARGET_ABI_OPEN_VMS)
/* Start by assuming we can use a register procedure if we don't
make any calls (REG_RA not used) or need to save any
registers and a stack procedure if we do. */
- alpha_is_stack_procedure = sa_size != 0 || alpha_ra_ever_killed ();
+ if ((mask[0] >> REG_RA) & 1)
+ alpha_procedure_type = PT_STACK;
+ else if (get_frame_size() != 0)
+ alpha_procedure_type = PT_REGISTER;
+ else
+ alpha_procedure_type = PT_NULL;
+
+ /* Don't reserve space for saving FP & RA yet. Do that later after we've
+ made the final decision on stack procedure vs register procedure. */
+ if (alpha_procedure_type == PT_STACK)
+ sa_size -= 2;
/* Decide whether to refer to objects off our PV via FP or PV.
If we need FP for something else or if we receive a nonlocal
goto (which expects PV to contain the value), we must use PV.
Otherwise, start by assuming we can use FP. */
- vms_base_regno = (frame_pointer_needed
- || current_function_has_nonlocal_label
- || alpha_is_stack_procedure
- || current_function_outgoing_args_size
- ? REG_PV : HARD_FRAME_POINTER_REGNUM);
+
+ vms_base_regno
+ = (frame_pointer_needed
+ || current_function_has_nonlocal_label
+ || alpha_procedure_type == PT_STACK
+ || current_function_outgoing_args_size)
+ ? REG_PV : HARD_FRAME_POINTER_REGNUM;
/* If we want to copy PV into FP, we need to find some register
in which to save FP. */
if (! fixed_regs[i] && call_used_regs[i] && ! regs_ever_live[i])
vms_save_fp_regno = i;
- if (vms_save_fp_regno == -1)
- vms_base_regno = REG_PV, alpha_is_stack_procedure = 1;
+ if (vms_save_fp_regno == -1 && alpha_procedure_type == PT_REGISTER)
+ vms_base_regno = REG_PV, alpha_procedure_type = PT_STACK;
+ else if (alpha_procedure_type == PT_NULL)
+ vms_base_regno = REG_PV;
/* Stack unwinding should be done via FP unless we use it for PV. */
vms_unwind_regno = (vms_base_regno == REG_PV
? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
/* If this is a stack procedure, allow space for saving FP and RA. */
- if (alpha_is_stack_procedure)
+ if (alpha_procedure_type == PT_STACK)
sa_size += 2;
}
else
{
- /* If some registers were saved but not RA, RA must also be saved,
- so leave space for it. */
- if (!TARGET_ABI_UNICOSMK && (sa_size != 0 || alpha_ra_ever_killed ()))
- sa_size++;
-
/* Our size must be even (multiple of 16 bytes). */
if (sa_size & 1)
sa_size++;
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 ()
{
alpha_sa_size ();
- return alpha_is_stack_procedure ? 8 : 0;
+ return alpha_procedure_type == PT_STACK ? 8 : 0;
}
int
const struct attribute_spec vms_attribute_table[] =
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
- { "overlaid", 0, 0, true, false, false, NULL },
- { NULL, 0, 0, false, false, false, NULL }
+ { "overlaid", 0, 0, true, false, false, NULL },
+ { "global", 0, 0, true, false, false, NULL },
+ { "initialize", 0, 0, true, false, false, NULL },
+ { NULL, 0, 0, false, false, false, NULL }
};
#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
if (! TARGET_ABI_OSF)
return 0;
- if (TARGET_PROFILING_NEEDS_GP && profile_flag)
+ if (TARGET_PROFILING_NEEDS_GP && current_function_profile)
return 1;
-#ifdef ASM_OUTPUT_MI_THUNK
if (current_function_is_thunk)
return 1;
-#endif
/* If we need a GP (we have a LDSYM insn or a CALL_INSN), load it first.
Even if we are a static function, we still need to do this in case
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 ())
frame_size = get_frame_size ();
if (TARGET_ABI_OPEN_VMS)
frame_size = ALPHA_ROUND (sa_size
- + (alpha_is_stack_procedure ? 8 : 0)
+ + (alpha_procedure_type == PT_STACK ? 8 : 0)
+ frame_size
+ current_function_pretend_args_size);
else if (TARGET_ABI_UNICOSMK)
/* We have to allocate space for the DSIB if we generate a frame. */
frame_size = ALPHA_ROUND (sa_size
- + (alpha_is_stack_procedure ? 48 : 0))
+ + (alpha_procedure_type == PT_STACK ? 48 : 0))
+ ALPHA_ROUND (frame_size
+ current_function_outgoing_args_size);
else
the call to mcount ourselves, rather than having the linker do it
magically in response to -pg. Since _mcount has special linkage,
don't represent the call as a call. */
- if (TARGET_PROFILING_NEEDS_GP && profile_flag)
+ if (TARGET_PROFILING_NEEDS_GP && current_function_profile)
emit_insn (gen_prologue_mcount ());
if (TARGET_ABI_UNICOSMK)
}
/* Save regs in stack order. Beginning with VMS PV. */
- if (TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure)
+ if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK)
{
mem = gen_rtx_MEM (DImode, stack_pointer_rtx);
set_mem_alias_set (mem, alpha_sr_alias_set);
}
/* 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 += 8;
}
}
- else if (TARGET_ABI_UNICOSMK && alpha_is_stack_procedure)
+ else if (TARGET_ABI_UNICOSMK && alpha_procedure_type == PT_STACK)
{
/* The standard frame on the T3E includes space for saving registers.
We just have to use it. We don't have to save the return address and
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 (TARGET_ABI_OPEN_VMS)
{
- if (!alpha_is_stack_procedure)
- /* Register frame procedures save the fp. */
- /* ??? Ought to have a dwarf2 save for this. */
+ if (alpha_procedure_type == PT_REGISTER)
+ /* Register frame procedures save the fp.
+ ?? Ought to have a dwarf2 save for this. */
emit_move_insn (gen_rtx_REG (DImode, vms_save_fp_regno),
hard_frame_pointer_rtx);
- if (vms_base_regno != REG_PV)
+ if (alpha_procedure_type != PT_NULL && vms_base_regno != REG_PV)
emit_insn (gen_force_movdi (gen_rtx_REG (DImode, vms_base_regno),
gen_rtx_REG (DImode, REG_PV)));
- if (vms_unwind_regno == HARD_FRAME_POINTER_REGNUM)
+ if (alpha_procedure_type != PT_NULL
+ && vms_unwind_regno == HARD_FRAME_POINTER_REGNUM)
FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx));
/* 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);
frame_size = get_frame_size ();
if (TARGET_ABI_OPEN_VMS)
frame_size = ALPHA_ROUND (sa_size
- + (alpha_is_stack_procedure ? 8 : 0)
+ + (alpha_procedure_type == PT_STACK ? 8 : 0)
+ frame_size
+ current_function_pretend_args_size);
else if (TARGET_ABI_UNICOSMK)
frame_size = ALPHA_ROUND (sa_size
- + (alpha_is_stack_procedure ? 48 : 0))
+ + (alpha_procedure_type == PT_STACK ? 48 : 0))
+ ALPHA_ROUND (frame_size
+ current_function_outgoing_args_size);
else
/* If the function needs GP, we'll write the "..ng" label there.
Otherwise, do it here. */
- if (TARGET_ABI_OSF && ! alpha_function_needs_gp)
+ if (TARGET_ABI_OSF
+ && ! alpha_function_needs_gp
+ && ! current_function_is_thunk)
{
putc ('$', file);
assemble_name (file, fnname);
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_is_stack_procedure)
+ if (alpha_procedure_type == PT_REGISTER)
fprintf (file, "\t.fp_save $%d\n", vms_save_fp_regno);
}
else if (!flag_inhibit_size_directive)
{
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_is_stack_procedure ? "stack" : "reg");
alpha_need_linkage (fnname, 1);
text_section ();
#endif
else if (TARGET_ABI_WINDOWS_NT)
fputs ("\t.prologue 0\n", file);
else if (!flag_inhibit_size_directive)
- fprintf (file, "\t.prologue %d\n", alpha_function_needs_gp);
+ fprintf (file, "\t.prologue %d\n",
+ alpha_function_needs_gp || current_function_is_thunk);
}
/* Write function epilogue. */
frame_size = get_frame_size ();
if (TARGET_ABI_OPEN_VMS)
frame_size = ALPHA_ROUND (sa_size
- + (alpha_is_stack_procedure ? 8 : 0)
+ + (alpha_procedure_type == PT_STACK ? 8 : 0)
+ frame_size
+ current_function_pretend_args_size);
else if (TARGET_ABI_UNICOSMK)
frame_size = ALPHA_ROUND (sa_size
- + (alpha_is_stack_procedure ? 48 : 0))
+ + (alpha_procedure_type == PT_STACK ? 48 : 0))
+ ALPHA_ROUND (frame_size
+ current_function_outgoing_args_size);
else
+ current_function_pretend_args_size));
if (TARGET_ABI_OPEN_VMS)
- reg_offset = 8;
+ {
+ if (alpha_procedure_type == PT_STACK)
+ reg_offset = 8;
+ else
+ reg_offset = 0;
+ }
else
reg_offset = ALPHA_ROUND (current_function_outgoing_args_size);
alpha_sa_mask (&imask, &fmask);
- fp_is_frame_pointer = ((TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure)
- || (!TARGET_ABI_OPEN_VMS && frame_pointer_needed));
+ fp_is_frame_pointer
+ = ((TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK)
+ || (!TARGET_ABI_OPEN_VMS && frame_pointer_needed));
fp_offset = 0;
sa_reg = stack_pointer_rtx;
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 += 8;
}
}
- else if (TARGET_ABI_UNICOSMK && alpha_is_stack_procedure)
+ else if (TARGET_ABI_UNICOSMK && alpha_procedure_type == PT_STACK)
{
/* Restore callee-saved general-purpose registers. */
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));
}
else
{
- if (TARGET_ABI_OPEN_VMS && !alpha_is_stack_procedure)
+ if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_REGISTER)
{
emit_insn (gen_blockage ());
FRP (emit_move_insn (hard_frame_pointer_rtx,
gen_rtx_REG (DImode, vms_save_fp_regno)));
}
- else if (TARGET_ABI_UNICOSMK && !alpha_is_stack_procedure)
+ else if (TARGET_ABI_UNICOSMK && alpha_procedure_type != PT_STACK)
{
/* Decrement the frame pointer if the function does not have a
frame. */
}
}
}
-
+\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)
unicosmk_output_deferred_case_vectors (file);
}
}
+
+#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
+ lots of code loading up large constants, generate rtl and emit it
+ instead of going straight to text.
+
+ Not sure why this idea hasn't been explored before... */
+
+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;
+ rtx this, insn, funexp;
+
+ /* We always require a valid GP. */
+ emit_insn (gen_prologue_ldgp ());
+ emit_note (NULL, NOTE_INSN_PROLOGUE_END);
+
+ /* Find the "this" pointer. If the function returns a structure,
+ the structure return pointer is in $16. */
+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function))))
+ this = gen_rtx_REG (Pmode, 17);
+ else
+ this = gen_rtx_REG (Pmode, 16);
+
+ /* Add DELTA. When possible we use ldah+lda. Otherwise load the
+ entire constant for the add. */
+ lo = ((delta & 0xffff) ^ 0x8000) - 0x8000;
+ hi = (((delta - lo) & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ if (hi + lo == delta)
+ {
+ if (hi)
+ emit_insn (gen_adddi3 (this, this, GEN_INT (hi)));
+ if (lo)
+ emit_insn (gen_adddi3 (this, this, GEN_INT (lo)));
+ }
+ else
+ {
+ rtx tmp = alpha_emit_set_long_const (gen_rtx_REG (Pmode, 0),
+ delta, -(delta < 0));
+ 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))
+ {
+ assemble_external (function);
+ TREE_USED (function) = 1;
+ }
+ funexp = XEXP (DECL_RTL (function), 0);
+ funexp = gen_rtx_MEM (FUNCTION_MODE, funexp);
+ insn = emit_call_insn (gen_sibcall (funexp, const0_rtx));
+ SIBLING_CALL_P (insn) = 1;
+
+ /* Run just enough of rest_of_compilation to get the insns emitted.
+ There's not really enough bulk here to make other passes such as
+ instruction scheduling worth while. Note that use_thunk calls
+ assemble_start_function and assemble_end_function. */
+ insn = get_insns ();
+ shorten_branches (insn);
+ final_start_function (insn, file, 1);
+ 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;
- else if (align_functions < max_align)
+ else if ((unsigned int) align_functions < max_align)
align = align_functions;
else
align = max_align;
ofs = prev_in_use = 0;
- i = insns;
+ i = get_insns ();
if (GET_CODE (i) == NOTE)
i = next_nonnote_insn (i);
else if ((int) align < len)
{
unsigned int new_log_align = len > 8 ? 4 : 3;
- rtx where;
+ rtx prev, where;
- where = prev_nonnote_insn (i);
+ where = prev = prev_nonnote_insn (i);
if (!where || GET_CODE (where) != CODE_LABEL)
where = i;
- emit_insn_before (gen_realign (GEN_INT (new_log_align)), where);
- align = 1 << new_log_align;
- ofs = 0;
+ /* Can't realign between a call and its gp reload. */
+ if (! (TARGET_EXPLICIT_RELOCS
+ && prev && GET_CODE (prev) == CALL_INSN))
+ {
+ emit_insn_before (gen_realign (GEN_INT (new_log_align)), where);
+ align = 1 << new_log_align;
+ ofs = 0;
+ }
}
/* If the group won't fit in the same INT16 as the previous,
int nop_count = (align - ofs) / 4;
rtx where;
- /* Insert nops before labels and branches to truely merge the
- execution of the nops with the previous instruction group. */
+ /* Insert nops before labels, branches, and calls to truely merge
+ the execution of the nops with the previous instruction group. */
where = prev_nonnote_insn (i);
if (where)
{
if (where2 && GET_CODE (where2) == JUMP_INSN)
where = where2;
}
- else if (GET_CODE (where) != JUMP_INSN)
+ else if (GET_CODE (where) == INSN)
where = i;
}
else
\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;
{
- readonly_section ();
+ splay_tree_node node;
+ struct alpha_funcs *func;
+
+ link_section ();
fprintf (stream, "\t.align 3\n");
- splay_tree_foreach (alpha_links, alpha_write_one_linkage, stream);
+ 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)
+ {
+ splay_tree_foreach (func->links, alpha_write_one_linkage, stream);
+ /* splay_tree_delete (func->links); */
+ }
}
/* Given a decl, a section name, and whether the decl initializer
has relocs, choose attributes for the section. */
#define SECTION_VMS_OVERLAY SECTION_FORGET
+#define SECTION_VMS_GLOBAL SECTION_MACH_DEP
+#define SECTION_VMS_INITIALIZE (SECTION_VMS_GLOBAL << 1)
static unsigned int
vms_section_type_flags (decl, name, reloc)
if (decl && DECL_ATTRIBUTES (decl)
&& lookup_attribute ("overlaid", DECL_ATTRIBUTES (decl)))
flags |= SECTION_VMS_OVERLAY;
+ if (decl && DECL_ATTRIBUTES (decl)
+ && lookup_attribute ("global", DECL_ATTRIBUTES (decl)))
+ flags |= SECTION_VMS_GLOBAL;
+ if (decl && DECL_ATTRIBUTES (decl)
+ && lookup_attribute ("initialize", DECL_ATTRIBUTES (decl)))
+ flags |= SECTION_VMS_INITIALIZE;
return flags;
}
const char *name;
unsigned int flags;
{
- const char *flag_str = "";
+ fputc ('\n', asm_out_file);
+ fprintf (asm_out_file, ".section\t%s", name);
if (flags & SECTION_VMS_OVERLAY)
- flag_str = ",OVR";
- else if (flags & SECTION_DEBUG)
- flag_str = ",NOWRT";
+ fprintf (asm_out_file, ",OVR");
+ if (flags & SECTION_VMS_GLOBAL)
+ fprintf (asm_out_file, ",GBL");
+ if (flags & SECTION_VMS_INITIALIZE)
+ fprintf (asm_out_file, ",NOMOD");
+ if (flags & SECTION_DEBUG)
+ fprintf (asm_out_file, ",NOWRT");
- fprintf (asm_out_file, ".section\t%s%s\n", name, flag_str);
+ fputc ('\n', asm_out_file);
}
/* Record an element in the table of global constructors. SYMBOL is
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++)
{
unicosmk_gen_dsib (imaskP)
unsigned long * imaskP;
{
- if (alpha_is_stack_procedure)
+ if (alpha_procedure_type == PT_STACK)
{
const char *ssib_name;
rtx mem;
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"
+