diff --git a/contrib/gcc/config/alpha/alpha.c b/contrib/gcc/config/alpha/alpha.c index a8f493691b9d..2320f1c41554 100644 --- a/contrib/gcc/config/alpha/alpha.c +++ b/contrib/gcc/config/alpha/alpha.c @@ -1,5 +1,6 @@ /* Subroutines used for code generation on the DEC Alpha. - Copyright (C) 1992, 93-98, 1999 Free Software Foundation, Inc. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001 Free Software Foundation, Inc. Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) This file is part of GNU CC. @@ -25,29 +26,33 @@ Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "rtl.h" +#include "tree.h" #include "regs.h" #include "hard-reg-set.h" #include "real.h" #include "insn-config.h" #include "conditions.h" -#include "insn-flags.h" #include "output.h" #include "insn-attr.h" #include "flags.h" #include "recog.h" -#include "reload.h" -#include "tree.h" #include "expr.h" +#include "optabs.h" +#include "reload.h" #include "obstack.h" #include "except.h" #include "function.h" #include "toplev.h" +#include "ggc.h" +#include "integrate.h" +#include "tm_p.h" +#include "target.h" +#include "target-def.h" /* External data. */ -extern char *version_string; extern int rtx_equal_function_value_matters; -/* Specify which cpu to schedule for. */ +/* Specify which cpu to schedule for. */ enum processor_type alpha_cpu; static const char * const alpha_cpu_name[] = @@ -70,6 +75,7 @@ enum alpha_fp_trap_mode alpha_fptm; /* Strings decoded into the above options. */ const char *alpha_cpu_string; /* -mcpu= */ +const char *alpha_tune_string; /* -mtune= */ const char *alpha_tp_string; /* -mtrap-precision=[p|s|i] */ const char *alpha_fprm_string; /* -mfp-rounding-mode=[n|m|c|d] */ const char *alpha_fptm_string; /* -mfp-trap-mode=[n|u|su|sui] */ @@ -78,22 +84,13 @@ const char *alpha_mlat_string; /* -mmemory-latency= */ /* Save information from a "cmpxx" operation until the branch or scc is emitted. */ -rtx alpha_compare_op0, alpha_compare_op1; -int alpha_compare_fp_p; - -/* Define the information needed to modify the epilogue for EH. */ - -rtx alpha_eh_epilogue_sp_ofs; +struct alpha_compare alpha_compare; /* Non-zero if inside of a function, because the Alpha asm can't handle .files inside of functions. */ static int inside_function = FALSE; -/* If non-null, this rtx holds the return address for the function. */ - -static rtx alpha_return_addr_rtx; - /* The number of cycles of latency we should assume on memory reads. */ int alpha_memory_latency = 3; @@ -106,31 +103,82 @@ static int alpha_function_needs_gp; static int alpha_sr_alias_set; -/* Declarations of static functions. */ -static void alpha_set_memflags_1 - PROTO((rtx, int, int, int)); -static rtx alpha_emit_set_const_1 - PROTO((rtx, enum machine_mode, HOST_WIDE_INT, int)); -static void alpha_expand_unaligned_load_words - PROTO((rtx *out_regs, rtx smem, HOST_WIDE_INT words, HOST_WIDE_INT ofs)); -static void alpha_expand_unaligned_store_words - PROTO((rtx *out_regs, rtx smem, HOST_WIDE_INT words, HOST_WIDE_INT ofs)); -static void alpha_sa_mask - PROTO((unsigned long *imaskP, unsigned long *fmaskP)); -static int alpha_does_function_need_gp - PROTO((void)); +/* The assembler name of the current function. */ -int zap_mask - PROTO((HOST_WIDE_INT)); -rtx alpha_emit_set_long_const - PROTO((rtx, HOST_WIDE_INT, HOST_WIDE_INT)); -void alpha_expand_unaligned_load - PROTO((rtx, rtx, HOST_WIDE_INT, HOST_WIDE_INT, int)); -void alpha_expand_unaligned_store - PROTO((rtx, rtx, HOST_WIDE_INT, HOST_WIDE_INT)); +static const char *alpha_fnname; + +/* The next explicit relocation sequence number. */ +int alpha_next_sequence_number = 1; + +/* The literal and gpdisp sequence numbers for this insn, as printed + by %# and %* respectively. */ +int alpha_this_literal_sequence_number; +int alpha_this_gpdisp_sequence_number; + +/* Declarations of static functions. */ +static bool decl_in_text_section + PARAMS ((tree)); +static int some_small_symbolic_mem_operand_1 + PARAMS ((rtx *, void *)); +static int split_small_symbolic_mem_operand_1 + PARAMS ((rtx *, void *)); +static bool local_symbol_p + PARAMS ((rtx)); +static void alpha_set_memflags_1 + PARAMS ((rtx, int, int, int)); +static rtx alpha_emit_set_const_1 + PARAMS ((rtx, enum machine_mode, HOST_WIDE_INT, int)); +static void alpha_expand_unaligned_load_words + 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_sa_mask + PARAMS ((unsigned long *imaskP, unsigned long *fmaskP)); +static int find_lo_sum + PARAMS ((rtx *, void *)); +static int alpha_does_function_need_gp + PARAMS ((void)); +static int alpha_ra_ever_killed + PARAMS ((void)); +static const char *get_trap_mode_suffix + PARAMS ((void)); +static const char *get_round_mode_suffix + PARAMS ((void)); +static rtx set_frame_related_p + PARAMS ((void)); +static const char *alpha_lookup_xfloating_lib_func + PARAMS ((enum rtx_code)); +static int alpha_compute_xfloating_mode_arg + PARAMS ((enum rtx_code, enum alpha_fp_rounding_mode)); +static void alpha_emit_xfloating_libcall + PARAMS ((const char *, rtx, rtx[], int, rtx)); +static rtx alpha_emit_xfloating_compare + PARAMS ((enum rtx_code, rtx, rtx)); +static void alpha_output_function_end_prologue + PARAMS ((FILE *)); +static int alpha_adjust_cost + PARAMS ((rtx, rtx, rtx, int)); +static int alpha_issue_rate + PARAMS ((void)); +static int alpha_variable_issue + PARAMS ((FILE *, int, rtx, int)); + +#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)); +#endif + +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 *)); +static int unicosmk_need_dex PARAMS ((rtx)); /* Get the number of args of a function in one of two ways. */ -#ifdef OPEN_VMS +#if TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK #define NUM_ARGS current_function_args_info.num_args #else #define NUM_ARGS current_function_args_info @@ -139,25 +187,132 @@ void alpha_expand_unaligned_store #define REG_PV 27 #define REG_RA 26 -/* Parse target option strings. */ +/* Initialize the GCC target structure. */ +#if TARGET_ABI_OPEN_VMS +const struct attribute_spec vms_attribute_table[]; +static unsigned int vms_section_type_flags PARAMS ((tree, const char *, int)); +static void vms_asm_named_section PARAMS ((const char *, unsigned int)); +static void vms_asm_out_constructor PARAMS ((rtx, int)); +static void vms_asm_out_destructor PARAMS ((rtx, int)); +# undef TARGET_ATTRIBUTE_TABLE +# define TARGET_ATTRIBUTE_TABLE vms_attribute_table +# undef TARGET_SECTION_TYPE_FLAGS +# define TARGET_SECTION_TYPE_FLAGS vms_section_type_flags +#endif + +#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)); +# 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 +#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 + +#undef TARGET_ASM_FUNCTION_END_PROLOGUE +#define TARGET_ASM_FUNCTION_END_PROLOGUE alpha_output_function_end_prologue + +#undef TARGET_SCHED_ADJUST_COST +#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 + +struct gcc_target targetm = TARGET_INITIALIZER; + +/* Parse target option strings. */ void override_options () { + int i; + static const struct cpu_table { + const char *const name; + const enum processor_type processor; + const int flags; + } cpu_table[] = { +#define EV5_MASK (MASK_CPU_EV5) +#define EV6_MASK (MASK_CPU_EV6|MASK_BWX|MASK_MAX|MASK_FIX) + { "ev4", PROCESSOR_EV4, 0 }, + { "ev45", PROCESSOR_EV4, 0 }, + { "21064", PROCESSOR_EV4, 0 }, + { "ev5", PROCESSOR_EV5, EV5_MASK }, + { "21164", PROCESSOR_EV5, EV5_MASK }, + { "ev56", PROCESSOR_EV5, EV5_MASK|MASK_BWX }, + { "21164a", PROCESSOR_EV5, EV5_MASK|MASK_BWX }, + { "pca56", PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX }, + { "21164PC",PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX }, + { "21164pc",PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX }, + { "ev6", PROCESSOR_EV6, EV6_MASK }, + { "21264", PROCESSOR_EV6, EV6_MASK }, + { "ev67", PROCESSOR_EV6, EV6_MASK|MASK_CIX }, + { "21264a", PROCESSOR_EV6, EV6_MASK|MASK_CIX }, + { 0, 0, 0 } + }; + + /* Unicos/Mk doesn't have shared libraries. */ + if (TARGET_ABI_UNICOSMK && flag_pic) + { + warning ("-f%s ignored for Unicos/Mk (not supported)", + (flag_pic > 1) ? "PIC" : "pic"); + flag_pic = 0; + } + + /* On Unicos/Mk, the native compiler consistenly generates /d suffices for + floating-point instructions. Make that the default for this target. */ + if (TARGET_ABI_UNICOSMK) + alpha_fprm = ALPHA_FPRM_DYN; + else + alpha_fprm = ALPHA_FPRM_NORM; + alpha_tp = ALPHA_TP_PROG; - alpha_fprm = ALPHA_FPRM_NORM; alpha_fptm = ALPHA_FPTM_N; + /* We cannot use su and sui qualifiers for conversion instructions on + Unicos/Mk. I'm not sure if this is due to assembler or hardware + limitations. Right now, we issue a warning if -mieee is specified + and then ignore it; eventually, we should either get it right or + disable the option altogether. */ + if (TARGET_IEEE) { - alpha_tp = ALPHA_TP_INSN; - alpha_fptm = ALPHA_FPTM_SU; + if (TARGET_ABI_UNICOSMK) + warning ("-mieee not supported on Unicos/Mk"); + else + { + alpha_tp = ALPHA_TP_INSN; + alpha_fptm = ALPHA_FPTM_SU; + } } if (TARGET_IEEE_WITH_INEXACT) { - alpha_tp = ALPHA_TP_INSN; - alpha_fptm = ALPHA_FPTM_SUI; + if (TARGET_ABI_UNICOSMK) + warning ("-mieee-with-inexact not supported on Unicos/Mk"); + else + { + alpha_tp = ALPHA_TP_INSN; + alpha_fptm = ALPHA_FPTM_SUI; + } } if (alpha_tp_string) @@ -207,53 +362,54 @@ override_options () if (alpha_cpu_string) { - if (! strcmp (alpha_cpu_string, "ev4") - || ! strcmp (alpha_cpu_string, "21064")) - { - alpha_cpu = PROCESSOR_EV4; - target_flags &= ~ (MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX); - } - else if (! strcmp (alpha_cpu_string, "ev5") - || ! strcmp (alpha_cpu_string, "21164")) - { - alpha_cpu = PROCESSOR_EV5; - target_flags &= ~ (MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX); - } - else if (! strcmp (alpha_cpu_string, "ev56") - || ! strcmp (alpha_cpu_string, "21164a")) - { - alpha_cpu = PROCESSOR_EV5; - target_flags |= MASK_BWX; - target_flags &= ~ (MASK_MAX | MASK_FIX | MASK_CIX); - } - else if (! strcmp (alpha_cpu_string, "pca56") - || ! strcmp (alpha_cpu_string, "21164PC") - || ! strcmp (alpha_cpu_string, "21164pc")) - { - alpha_cpu = PROCESSOR_EV5; - target_flags |= MASK_BWX | MASK_MAX; - target_flags &= ~ (MASK_FIX | MASK_CIX); - } - else if (! strcmp (alpha_cpu_string, "ev6") - || ! strcmp (alpha_cpu_string, "21264")) - { - alpha_cpu = PROCESSOR_EV6; - target_flags |= MASK_BWX | MASK_MAX | MASK_FIX; - target_flags &= ~ (MASK_CIX); - } - else + for (i = 0; cpu_table [i].name; i++) + if (! strcmp (alpha_cpu_string, cpu_table [i].name)) + { + alpha_cpu = cpu_table [i].processor; + target_flags &= ~ (MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX + | MASK_CPU_EV5 | MASK_CPU_EV6); + target_flags |= cpu_table [i].flags; + break; + } + if (! cpu_table [i].name) error ("bad value `%s' for -mcpu switch", alpha_cpu_string); } - /* Do some sanity checks on the above options. */ + if (alpha_tune_string) + { + for (i = 0; cpu_table [i].name; i++) + if (! strcmp (alpha_tune_string, cpu_table [i].name)) + { + alpha_cpu = cpu_table [i].processor; + break; + } + if (! cpu_table [i].name) + error ("bad value `%s' for -mcpu switch", alpha_tune_string); + } + + /* Do some sanity checks on the above options. */ + + if (TARGET_ABI_UNICOSMK && alpha_fptm != ALPHA_FPTM_N) + { + warning ("trap mode not supported on Unicos/Mk"); + alpha_fptm = ALPHA_FPTM_N; + } if ((alpha_fptm == ALPHA_FPTM_SU || alpha_fptm == ALPHA_FPTM_SUI) - && alpha_tp != ALPHA_TP_INSN && alpha_cpu != PROCESSOR_EV6) + && alpha_tp != ALPHA_TP_INSN && ! TARGET_CPU_EV6) { warning ("fp software completion requires -mtrap-precision=i"); alpha_tp = ALPHA_TP_INSN; } + if (TARGET_CPU_EV6) + { + /* Except for EV6 pass 1 (not released), we always have precise + arithmetic traps. Which means we can do software completion + without minding trap shadows. */ + alpha_tp = ALPHA_TP_PROG; + } + if (TARGET_FLOAT_VAX) { if (alpha_fprm == ALPHA_FPRM_MINF || alpha_fprm == ALPHA_FPRM_DYN) @@ -286,11 +442,11 @@ override_options () { { 3, 30, -1 }, /* ev4 -- Bcache is a guess */ { 2, 12, 38 }, /* ev5 -- Bcache from PC164 LMbench numbers */ - { 3, 13, -1 }, /* ev6 -- Ho hum, doesn't exist yet */ + { 3, 12, 30 }, /* ev6 -- Bcache from DS20 LMbench. */ }; lat = alpha_mlat_string[1] - '0'; - if (lat < 0 || lat > 3 || cache_latency[alpha_cpu][lat-1] == -1) + if (lat <= 0 || lat > 3 || cache_latency[alpha_cpu][lat-1] == -1) { warning ("L%d cache latency unknown for %s", lat, alpha_cpu_name[alpha_cpu]); @@ -318,8 +474,36 @@ override_options () if (!g_switch_set) g_switch_value = 8; + /* Infer TARGET_SMALL_DATA from -fpic/-fPIC. */ + if (flag_pic == 1) + target_flags |= MASK_SMALL_DATA; + else if (flag_pic == 2) + target_flags &= ~MASK_SMALL_DATA; + + /* Align labels and loops for optimal branching. */ + /* ??? Kludge these by not doing anything if we don't optimize and also if + we are writing ECOFF symbols to work around a bug in DEC's assembler. */ + if (optimize > 0 && write_symbols != SDB_DEBUG) + { + if (align_loops <= 0) + align_loops = 16; + if (align_jumps <= 0) + align_jumps = 16; + } + if (align_functions <= 0) + align_functions = 16; + /* Acquire a unique set number for our register saves and restores. */ alpha_sr_alias_set = new_alias_set (); + + /* 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 } /* Returns 1 if VALUE is a mask that contains full bytes of zero or ones. */ @@ -413,7 +597,7 @@ sext_add_operand (op, mode) return (CONST_OK_FOR_LETTER_P (INTVAL (op), 'I') || CONST_OK_FOR_LETTER_P (INTVAL (op), 'O')); - return register_operand (op, mode); + return reg_not_elim_operand (op, mode); } /* Return 1 if OP is the constant 4 or 8. */ @@ -541,9 +725,27 @@ hard_fp_register_operand (op, mode) register rtx op; enum machine_mode mode; { - return ((GET_CODE (op) == REG && REGNO_REG_CLASS (REGNO (op)) == FLOAT_REGS) - || (GET_CODE (op) == SUBREG - && hard_fp_register_operand (SUBREG_REG (op), mode))); + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) + return 0; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + return GET_CODE (op) == REG && REGNO_REG_CLASS (REGNO (op)) == FLOAT_REGS; +} + +/* Return 1 if OP is a hard general register. */ + +int +hard_int_register_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) + return 0; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + return GET_CODE (op) == REG && REGNO_REG_CLASS (REGNO (op)) == GENERAL_REGS; } /* Return 1 if OP is a register or a constant integer. */ @@ -572,7 +774,7 @@ some_operand (op, mode) switch (GET_CODE (op)) { case REG: case MEM: case CONST_DOUBLE: case CONST_INT: case LABEL_REF: - case SYMBOL_REF: case CONST: + case SYMBOL_REF: case CONST: case HIGH: return 1; case SUBREG: @@ -585,6 +787,22 @@ some_operand (op, mode) return 0; } +/* Likewise, but don't accept constants. */ + +int +some_ni_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + return (GET_CODE (op) == REG || GET_CODE (op) == MEM); +} + /* Return 1 if OP is a valid operand for the source of a move insn. */ int @@ -603,16 +821,31 @@ input_operand (op, mode) case LABEL_REF: case SYMBOL_REF: case CONST: + if (TARGET_EXPLICIT_RELOCS) + { + /* 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)); + } + /* 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; case SUBREG: if (register_operand (op, mode)) return 1; - /* ... fall through ... */ + /* ... fall through ... */ case MEM: return ((TARGET_BWX || (mode != HImode && mode != QImode)) && general_operand (op, mode)); @@ -634,17 +867,149 @@ input_operand (op, mode) } /* Return 1 if OP is a SYMBOL_REF for a function known to be in this - file. */ + file, and in the same section as the current function. */ int current_file_function_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED; { - return (GET_CODE (op) == SYMBOL_REF - && ! profile_flag && ! profile_block_flag - && (SYMBOL_REF_FLAG (op) - || op == XEXP (DECL_RTL (current_function_decl), 0))); + if (GET_CODE (op) != SYMBOL_REF) + return 0; + + /* Easy test for recursion. */ + if (op == XEXP (DECL_RTL (current_function_decl), 0)) + return 1; + + /* 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; + + return 0; +} + +/* Return 1 if OP is a SYMBOL_REF for which we can make a call via bsr. */ + +int +direct_call_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + /* Must be defined in this file. */ + if (! current_file_function_operand (op, mode)) + return 0; + + /* If profiling is implemented via linker tricks, we can't jump + to the nogp alternate entry point. */ + /* ??? TARGET_PROFILING_NEEDS_GP isn't really the right test, + but is approximately correct for the OSF ABIs. Don't know + what to do for VMS, NT, or UMK. */ + if (! TARGET_PROFILING_NEEDS_GP + && ! current_function_profile) + return 0; + + return 1; +} + +/* Return true if OP is a LABEL_REF, or SYMBOL_REF or CONST referencing + a variable known to be defined in this file. */ + +static bool +local_symbol_p (op) + rtx op; +{ + const char *str = XSTR (op, 0); + + /* ??? 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. */ + + 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] == '$')); +} + +int +local_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) + return 0; + + if (GET_CODE (op) == LABEL_REF) + return 1; + + if (GET_CODE (op) == CONST + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) + op = XEXP (XEXP (op, 0), 0); + + if (GET_CODE (op) != SYMBOL_REF) + return 0; + + return local_symbol_p (op); +} + +/* Return true if OP is a SYMBOL_REF or CONST referencing a variable + known to be defined in this file in the small data area. */ + +int +small_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + const char *str; + + if (! TARGET_SMALL_DATA) + return 0; + + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) + return 0; + + if (GET_CODE (op) == CONST + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) + op = XEXP (XEXP (op, 0), 0); + + if (GET_CODE (op) != SYMBOL_REF) + return 0; + + 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 true if OP is a SYMBOL_REF or CONST referencing a variable + not known (or known not) to be defined in this file. */ + +int +global_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) + return 0; + + if (GET_CODE (op) == CONST + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) + op = XEXP (XEXP (op, 0), 0); + + if (GET_CODE (op) != SYMBOL_REF) + return 0; + + return ! local_symbol_p (op); } /* Return 1 if OP is a valid operand for the MEM of a CALL insn. */ @@ -657,9 +1022,44 @@ call_operand (op, mode) if (mode != Pmode) return 0; - return (GET_CODE (op) == SYMBOL_REF - || (GET_CODE (op) == REG - && (TARGET_OPEN_VMS || TARGET_WINDOWS_NT || REGNO (op) == 27))); + if (GET_CODE (op) == REG) + { + if (TARGET_ABI_OSF) + { + /* 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; + } + if (TARGET_ABI_UNICOSMK) + return 0; + if (GET_CODE (op) == SYMBOL_REF) + return 1; + + return 0; +} + +/* Returns 1 if OP is a symbolic operand, i.e. a symbol_ref or a label_ref, + possibly with an offset. */ + +int +symbolic_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) + return 0; + if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) + return 1; + if (GET_CODE (op) == CONST + && GET_CODE (XEXP (op,0)) == PLUS + && GET_CODE (XEXP (XEXP (op,0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (op,0), 1)) == CONST_INT) + return 1; + return 0; } /* Return 1 if OP is a valid Alpha comparison operator. Here we know which @@ -672,11 +1072,28 @@ alpha_comparison_operator (op, mode) { enum rtx_code code = GET_CODE (op); - if (mode != GET_MODE (op) || GET_RTX_CLASS (code) != '<') + if (mode != GET_MODE (op) && mode != VOIDmode) return 0; return (code == EQ || code == LE || code == LT - || (mode == DImode && (code == LEU || code == LTU))); + || code == LEU || code == LTU); +} + +/* Return 1 if OP is a valid Alpha comparison operator against zero. + Here we know which comparisons are valid in which insn. */ + +int +alpha_zero_comparison_operator (op, mode) + register rtx op; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (op); + + if (mode != GET_MODE (op) && mode != VOIDmode) + return 0; + + return (code == EQ || code == NE || code == LE || code == LT + || code == LEU || code == LTU); } /* Return 1 if OP is a valid Alpha swapped comparison operator. */ @@ -688,12 +1105,13 @@ alpha_swapped_comparison_operator (op, mode) { enum rtx_code code = GET_CODE (op); - if (mode != GET_MODE (op) || GET_RTX_CLASS (code) != '<') + if ((mode != GET_MODE (op) && mode != VOIDmode) + || GET_RTX_CLASS (code) != '<') return 0; code = swap_condition (code); return (code == EQ || code == LE || code == LT - || (mode == DImode && (code == LEU || code == LTU))); + || code == LEU || code == LTU); } /* Return 1 if OP is a signed comparison operation. */ @@ -703,16 +1121,30 @@ signed_comparison_operator (op, mode) register rtx op; enum machine_mode mode ATTRIBUTE_UNUSED; { - switch (GET_CODE (op)) - { - case EQ: case NE: case LE: case LT: case GE: case GT: - return 1; + enum rtx_code code = GET_CODE (op); - default: - break; - } + if (mode != GET_MODE (op) && mode != VOIDmode) + return 0; - return 0; + return (code == EQ || code == NE + || code == LE || code == LT + || code == GE || code == GT); +} + +/* Return 1 if OP is a valid Alpha floating point comparison operator. + Here we know which comparisons are valid in which insn. */ + +int +alpha_fp_comparison_operator (op, mode) + register rtx op; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (op); + + if (mode != GET_MODE (op) && mode != VOIDmode) + return 0; + + return (code == EQ || code == LE || code == LT || code == UNORDERED); } /* Return 1 if this is a divide or modulus operator. */ @@ -779,8 +1211,7 @@ aligned_memory_operand (op, mode) base = (GET_CODE (op) == PLUS ? XEXP (op, 0) : op); } - return (GET_CODE (base) == REG - && REGNO_POINTER_ALIGN (REGNO (base)) >= 4); + return (GET_CODE (base) == REG && REGNO_POINTER_ALIGN (REGNO (base)) >= 32); } /* Similar, but return 1 if OP is a MEM which is not alignable. */ @@ -824,8 +1255,7 @@ unaligned_memory_operand (op, mode) base = (GET_CODE (op) == PLUS ? XEXP (op, 0) : op); } - return (GET_CODE (base) == REG - && REGNO_POINTER_ALIGN (REGNO (base)) < 4); + return (GET_CODE (base) == REG && REGNO_POINTER_ALIGN (REGNO (base)) < 32); } /* Return 1 if OP is either a register or an unaligned memory location. */ @@ -917,22 +1347,650 @@ reg_no_subreg_operand (op, mode) register rtx op; enum machine_mode mode; { - if (GET_CODE (op) == SUBREG) + if (GET_CODE (op) != REG) return 0; return register_operand (op, mode); } - + +/* Recognize an addition operation that includes a constant. Used to + convince reload to canonize (plus (plus reg c1) c2) during register + elimination. */ + +int +addition_operation (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + if (GET_CODE (op) == PLUS + && register_operand (XEXP (op, 0), mode) + && GET_CODE (XEXP (op, 1)) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (op, 1)), 'K')) + return 1; + 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); + + default: + return false; + } +} + /* Return 1 if this function can directly return via $26. */ int direct_return () { - return (! TARGET_OPEN_VMS && reload_completed && alpha_sa_size () == 0 + return (! TARGET_ABI_OPEN_VMS && ! TARGET_ABI_UNICOSMK + && reload_completed + && alpha_sa_size () == 0 && get_frame_size () == 0 && current_function_outgoing_args_size == 0 && current_function_pretend_args_size == 0); } +/* Return the ADDR_VEC associated with a tablejump insn. */ + +rtx +alpha_tablejump_addr_vec (insn) + rtx insn; +{ + rtx tmp; + + tmp = JUMP_LABEL (insn); + if (!tmp) + return NULL_RTX; + tmp = NEXT_INSN (tmp); + if (!tmp) + return NULL_RTX; + if (GET_CODE (tmp) == JUMP_INSN + && GET_CODE (PATTERN (tmp)) == ADDR_DIFF_VEC) + return PATTERN (tmp); + return NULL_RTX; +} + +/* Return the label of the predicted edge, or CONST0_RTX if we don't know. */ + +rtx +alpha_tablejump_best_label (insn) + rtx insn; +{ + rtx jump_table = alpha_tablejump_addr_vec (insn); + rtx best_label = NULL_RTX; + + /* ??? Once the CFG doesn't keep getting completely rebuilt, look + there for edge frequency counts from profile data. */ + + if (jump_table) + { + int n_labels = XVECLEN (jump_table, 1); + int best_count = -1; + int i, j; + + for (i = 0; i < n_labels; i++) + { + int count = 1; + + for (j = i + 1; j < n_labels; j++) + if (XEXP (XVECEXP (jump_table, 1, i), 0) + == XEXP (XVECEXP (jump_table, 1, j), 0)) + count++; + + if (count > best_count) + best_count = count, best_label = XVECEXP (jump_table, 1, i); + } + } + + return best_label ? best_label : const0_rtx; +} + +/* 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 (DECL_SECTION_NAME (decl) == NULL_TREE + && ! (flag_function_sections + || (targetm.have_named_sections + && DECL_ONE_ONLY (decl)))); +} + +/* 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 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. */ + +void +alpha_encode_section_info (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; + + SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1; + return; + } + + /* Early out if we're not going to do anything with this data. */ + if (! TARGET_EXPLICIT_RELOCS) + return; + + /* 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; + + /* Determine if DECL will wind up in .sdata/.sbss. */ + + is_small = false; + if (DECL_SECTION_NAME (decl)) + { + const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); + if (strcmp (section, ".sdata") == 0 + || strcmp (section, ".sbss") == 0) + is_small = true; + } + else + { + HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl)); + + /* If the variable has already been defined in the output file, then it + is too late to put it in sdata if it wasn't put there in the first + place. The test is here rather than above, because if it is already + in sdata, then it can stay there. */ + + if (TREE_ASM_WRITTEN (decl)) + ; + + /* If this is an incomplete type with size 0, then we can't put it in + sdata because it might be too big when completed. */ + else if (size > 0 && size <= g_switch_value) + is_small = true; + } + + /* Finally, encode this into the symbol string. */ + if (is_local) + { + const char *string; + char *newstr; + size_t len; + + if (symbol_str[0] == '@') + { + if (symbol_str[1] == (is_small ? 's' : 'v')) + return; + symbol_str += 2; + } + + len = strlen (symbol_str) + 1; + newstr = alloca (len + 2); + + 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 (); +} + +/* 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. + + For Alpha, we have either a constant address or the sum of a + register and a constant address, or just a register. For DImode, + any of those forms can be surrounded with an AND that clear the + low-order three bits; this is an "unaligned" access. */ + +bool +alpha_legitimate_address_p (mode, x, strict) + enum machine_mode mode; + rtx x; + int strict; +{ + /* If this is an ldq_u type address, discard the outer AND. */ + if (mode == DImode + && GET_CODE (x) == AND + && GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) == -8) + 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); + + /* Unadorned general registers are valid. */ + if (REG_P (x) + && (strict + ? STRICT_REG_OK_FOR_BASE_P (x) + : NONSTRICT_REG_OK_FOR_BASE_P (x))) + return true; + + /* Constant addresses (i.e. +/- 32k) are valid. */ + if (CONSTANT_ADDRESS_P (x)) + return true; + + /* Register plus a small constant offset is valid. */ + if (GET_CODE (x) == PLUS) + { + 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); + + if (REG_P (x)) + { + if (! strict + && NONSTRICT_REG_OK_FP_BASE_P (x) + && GET_CODE (ofs) == CONST_INT) + return true; + if ((strict + ? STRICT_REG_OK_FOR_BASE_P (x) + : NONSTRICT_REG_OK_FOR_BASE_P (x)) + && CONSTANT_ADDRESS_P (ofs)) + return true; + } + else if (GET_CODE (x) == ADDRESSOF + && GET_CODE (ofs) == CONST_INT) + return true; + } + + /* If we're managing explicit relocations, LO_SUM is valid, as + are small data symbols. */ + else if (TARGET_EXPLICIT_RELOCS) + { + 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)) + return true; + } + } + + return false; +} + +/* 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, scratch, mode) + rtx x; + rtx scratch; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + HOST_WIDE_INT addend; + + /* If the address is (plus reg const_int) and the CONST_INT is not a + valid offset, compute the high part of the constant and add it to + the register. Then our address is (plus temp low-part-const). */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 0)) == REG + && GET_CODE (XEXP (x, 1)) == CONST_INT + && ! CONSTANT_ADDRESS_P (XEXP (x, 1))) + { + addend = INTVAL (XEXP (x, 1)); + x = XEXP (x, 0); + goto split_addend; + } + + /* If the address is (const (plus FOO const_int)), find the low-order + 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 (!no_new_pseudos + && GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) + { + addend = INTVAL (XEXP (XEXP (x, 0), 1)); + x = force_reg (Pmode, XEXP (XEXP (x, 0), 0)); + goto split_addend; + } + + /* 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 (!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 + && GET_CODE (XEXP (XEXP (XEXP (x, 1), 0), 1)) == CONST_INT) + { + addend = INTVAL (XEXP (XEXP (XEXP (x, 1), 0), 1)); + x = expand_simple_binop (Pmode, PLUS, XEXP (x, 0), + XEXP (XEXP (XEXP (x, 1), 0), 0), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + goto split_addend; + } + + /* If this is a local symbol, split the address into HIGH/LO_SUM parts. */ + if (TARGET_EXPLICIT_RELOCS && symbolic_operand (x, Pmode)) + { + if (local_symbolic_operand (x, Pmode)) + { + if (small_symbolic_operand (x, Pmode)) + return x; + else + { + if (!no_new_pseudos) + scratch = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (VOIDmode, scratch, + gen_rtx_HIGH (Pmode, x))); + return gen_rtx_LO_SUM (Pmode, scratch, x); + } + } + } + + return NULL; + + split_addend: + { + 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); + } +} + +/* 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_mem_operand (x, mode) + rtx x; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return for_each_rtx (&x, some_small_symbolic_mem_operand_1, NULL); +} + +static int +some_small_symbolic_mem_operand_1 (px, data) + rtx *px; + void *data ATTRIBUTE_UNUSED; +{ + rtx x = *px; + + if (GET_CODE (x) != MEM) + return 0; + x = XEXP (x, 0); + + /* If this is an ldq_u type address, discard the outer AND. */ + if (GET_CODE (x) == AND) + x = XEXP (x, 0); + + return small_symbolic_operand (x, Pmode) ? 1 : -1; +} + +rtx +split_small_symbolic_mem_operand (x) + rtx x; +{ + x = copy_insn (x); + for_each_rtx (&x, split_small_symbolic_mem_operand_1, NULL); + return x; +} + +static int +split_small_symbolic_mem_operand_1 (px, data) + rtx *px; + void *data ATTRIBUTE_UNUSED; +{ + rtx x = *px; + + if (GET_CODE (x) != MEM) + return 0; + + px = &XEXP (x, 0), x = *px; + if (GET_CODE (x) == AND) + px = &XEXP (x, 0), x = *px; + + if (small_symbolic_operand (x, Pmode)) + { + x = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, x); + *px = x; + } + + return -1; +} + +/* Try a machine-dependent way of reloading an illegitimate address + operand. If we find one, push the reload and return the new rtx. */ + +rtx +alpha_legitimize_reload_address (x, mode, opnum, type, ind_levels) + rtx x; + enum machine_mode mode ATTRIBUTE_UNUSED; + int opnum; + int type; + int ind_levels ATTRIBUTE_UNUSED; +{ + /* We must recognize output that we have already generated ourselves. */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && GET_CODE (XEXP (x, 1)) == CONST_INT) + { + push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, + BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0, + opnum, type); + return x; + } + + /* We wish to handle large displacements off a base register by + splitting the addend across an ldah and the mem insn. This + cuts number of extra insns needed from 3 to 1. */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 0)) == REG + && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER + && REGNO_OK_FOR_BASE_P (REGNO (XEXP (x, 0))) + && GET_CODE (XEXP (x, 1)) == CONST_INT) + { + HOST_WIDE_INT val = INTVAL (XEXP (x, 1)); + HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000; + HOST_WIDE_INT high + = (((val - low) & 0xffffffff) ^ 0x80000000) - 0x80000000; + + /* Check for 32-bit overflow. */ + if (high + low != val) + return NULL_RTX; + + /* Reload the high part into a base reg; leave the low part + in the mem directly. */ + x = gen_rtx_PLUS (GET_MODE (x), + gen_rtx_PLUS (GET_MODE (x), XEXP (x, 0), + GEN_INT (high)), + GEN_INT (low)); + + push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, + BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0, + opnum, type); + return x; + } + + return NULL_RTX; +} + /* 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 @@ -965,15 +2023,14 @@ get_aligned_mem (ref, paligned_mem, pbitnum) 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); - RTX_UNCHANGING_P (*paligned_mem) = RTX_UNCHANGING_P (ref); + *paligned_mem + = widen_memory_access (ref, SImode, (offset & ~3) - offset); - /* Sadly, we cannot use alias sets here because we may overlap other - data in a different alias set. */ - /* MEM_ALIAS_SET (*paligned_mem) = MEM_ALIAS_SET (ref); */ - - *pbitnum = GEN_INT ((offset & 3) * 8); + if (WORDS_BIG_ENDIAN) + *pbitnum = GEN_INT (32 - (GET_MODE_BITSIZE (GET_MODE (ref)) + + (offset & 3) * 8)); + else + *pbitnum = GEN_INT ((offset & 3) * 8); } /* Similar, but just get the address. Handle the two reload cases. @@ -1008,6 +2065,89 @@ get_unaligned_address (ref, extra_offset) 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 + unless byte instructions are permitted. + + We also cannot load an unaligned address or a paradoxical SUBREG + into an FP register. + + We also cannot do integral arithmetic into FP regs, as might result + from register elimination into a DImode fp register. */ + +enum reg_class +secondary_reload_class (class, mode, x, in) + enum reg_class class; + enum machine_mode mode; + rtx x; + int in; +{ + if ((mode == QImode || mode == HImode) && ! TARGET_BWX) + { + if (GET_CODE (x) == MEM + || (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER) + || (GET_CODE (x) == SUBREG + && (GET_CODE (SUBREG_REG (x)) == MEM + || (GET_CODE (SUBREG_REG (x)) == REG + && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)))) + { + if (!in || !aligned_memory_operand(x, mode)) + return GENERAL_REGS; + } + } + + if (class == FLOAT_REGS) + { + if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == AND) + return GENERAL_REGS; + + if (GET_CODE (x) == SUBREG + && (GET_MODE_SIZE (GET_MODE (x)) + > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) + return GENERAL_REGS; + + if (in && INTEGRAL_MODE_P (mode) + && ! (memory_operand (x, mode) || x == const0_rtx)) + return GENERAL_REGS; + } + + return NO_REGS; +} /* Subfunction of the following function. Update the flags of any MEM found in part of X. */ @@ -1049,7 +2189,6 @@ alpha_set_memflags_1 (x, in_struct_p, volatile_p, unchanging_p) are the only thing we would be able to differentiate anyway, there does not seem to be any point in convoluting the early out of the alias check. */ - /* MEM_ALIAS_SET (x) = alias_set; */ break; default: @@ -1102,7 +2241,7 @@ alpha_emit_set_const (target, mode, c, n) rtx pat; int i; - /* Try 1 insn, then 2, then up to N. */ + /* 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; @@ -1119,7 +2258,7 @@ alpha_emit_set_const_1 (target, mode, c, n) HOST_WIDE_INT c; int n; { - HOST_WIDE_INT new = c; + HOST_WIDE_INT new; int i, bits; /* Use a pseudo if highly optimizing and still generating RTL. */ rtx subtarget @@ -1133,20 +2272,19 @@ alpha_emit_set_const_1 (target, mode, c, n) cross-compiling on a narrow machine. */ if (mode == SImode) - c = (c & 0xffffffff) - 2 * (c & 0x80000000); + c = ((c & 0xffffffff) ^ 0x80000000) - 0x80000000; #endif /* 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 - a sign-extended 32-bit constant when compiling on a narrow machine. */ + a sign-extended 32-bit constant when compiling on a narrow machine. */ if (HOST_BITS_PER_WIDE_INT != 64 || c >> 31 == -1 || c >> 31 == 0) { - HOST_WIDE_INT low = (c & 0xffff) - 2 * (c & 0x8000); + HOST_WIDE_INT low = ((c & 0xffff) ^ 0x8000) - 0x8000; HOST_WIDE_INT tmp1 = c - low; - HOST_WIDE_INT high - = ((tmp1 >> 16) & 0xffff) - 2 * ((tmp1 >> 16) & 0x8000); + HOST_WIDE_INT high = (((tmp1 >> 16) & 0xffff) ^ 0x8000) - 0x8000; HOST_WIDE_INT extra = 0; /* If HIGH will be interpreted as negative but the constant is @@ -1174,13 +2312,13 @@ alpha_emit_set_const_1 (target, mode, c, n) } else if (n >= 2 + (extra != 0)) { - temp = copy_to_suggested_reg (GEN_INT (low), subtarget, mode); + temp = copy_to_suggested_reg (GEN_INT (high << 16), subtarget, mode); if (extra != 0) temp = expand_binop (mode, add_optab, temp, GEN_INT (extra << 16), subtarget, 0, OPTAB_WIDEN); - return expand_binop (mode, add_optab, temp, GEN_INT (high << 16), + return expand_binop (mode, add_optab, temp, GEN_INT (low), target, 0, OPTAB_WIDEN); } } @@ -1194,34 +2332,22 @@ alpha_emit_set_const_1 (target, mode, c, n) || (mode == SImode && ! rtx_equal_function_value_matters)) return 0; -#if HOST_BITS_PER_WIDE_INT == 64 - /* First, see if can load a value into the target that is the same as the - constant except that all bytes that are 0 are changed to be 0xff. If we - can, then we can do a ZAPNOT to obtain the desired constant. */ - - for (i = 0; i < 64; i += 8) - if ((new & ((HOST_WIDE_INT) 0xff << i)) == 0) - new |= (HOST_WIDE_INT) 0xff << i; - - /* We are only called for SImode and DImode. If this is SImode, ensure that - we are sign extended to a full word. */ - - if (mode == SImode) - new = (new & 0xffffffff) - 2 * (new & 0x80000000); - - if (new != c - && (temp = alpha_emit_set_const (subtarget, mode, new, n - 1)) != 0) - return expand_binop (mode, and_optab, temp, GEN_INT (c | ~ new), - target, 0, OPTAB_WIDEN); -#endif - /* Next, see if we can load a related constant and then shift and possibly negate it to get the constant we want. Try this once each increasing numbers of insns. */ for (i = 1; i < n; i++) { - /* First try complementing. */ + /* First, see if minus some low bits, we've an easy load of + high bits. */ + + new = ((c & 0xffff) ^ 0x8000) - 0x8000; + if (new != 0 + && (temp = alpha_emit_set_const (subtarget, mode, c - new, i)) != 0) + return expand_binop (mode, add_optab, temp, GEN_INT (new), + target, 0, OPTAB_WIDEN); + + /* Next try complementing. */ if ((temp = alpha_emit_set_const (subtarget, mode, ~ c, i)) != 0) return expand_unop (mode, one_cmpl_optab, temp, target, 0); @@ -1237,8 +2363,7 @@ alpha_emit_set_const_1 (target, mode, c, n) if ((bits = exact_log2 (c & - c)) > 0) for (; bits > 0; bits--) if ((temp = (alpha_emit_set_const - (subtarget, mode, - (unsigned HOST_WIDE_INT) (c >> bits), i))) != 0 + (subtarget, mode, c >> bits, i))) != 0 || ((temp = (alpha_emit_set_const (subtarget, mode, ((unsigned HOST_WIDE_INT) c) >> bits, i))) @@ -1267,8 +2392,8 @@ alpha_emit_set_const_1 (target, mode, c, n) /* Now try high-order 1 bits. We get that with a sign-extension. But one bit isn't enough here. Be careful to avoid shifting outside - the mode and to avoid shifting outside the host wide int size. */ - + the mode and to avoid shifting outside the host wide int size. */ + if ((bits = (MIN (HOST_BITS_PER_WIDE_INT, GET_MODE_SIZE (mode) * 8) - floor_log2 (~ c) - 2)) > 0) for (; bits > 0; bits--) @@ -1283,6 +2408,28 @@ alpha_emit_set_const_1 (target, mode, c, n) target, 0, OPTAB_WIDEN); } +#if HOST_BITS_PER_WIDE_INT == 64 + /* Finally, see if can load a value into the target that is the same as the + constant except that all bytes that are 0 are changed to be 0xff. If we + can, then we can do a ZAPNOT to obtain the desired constant. */ + + new = c; + for (i = 0; i < 64; i += 8) + if ((new & ((HOST_WIDE_INT) 0xff << i)) == 0) + new |= (HOST_WIDE_INT) 0xff << i; + + /* We are only called for SImode and DImode. If this is SImode, ensure that + we are sign extended to a full word. */ + + if (mode == SImode) + new = ((new & 0xffffffff) ^ 0x80000000) - 0x80000000; + + if (new != c && new != -1 + && (temp = alpha_emit_set_const (subtarget, mode, new, n - 1)) != 0) + return expand_binop (mode, and_optab, temp, GEN_INT (c | ~ new), + target, 0, OPTAB_WIDEN); +#endif + return 0; } @@ -1347,6 +2494,276 @@ alpha_emit_set_long_const (target, c1, c2) return target; } +/* Expand a move instruction; return true if all work is done. + We don't handle non-bwx subword loads here. */ + +bool +alpha_expand_mov (mode, operands) + enum machine_mode mode; + rtx *operands; +{ + /* If the output is not a register, the input must be. */ + if (GET_CODE (operands[0]) == MEM + && ! reg_or_0_operand (operands[1], mode)) + operands[1] = force_reg (mode, operands[1]); + + /* Allow legitimize_address to perform some simplifications. */ + if (mode == Pmode && symbolic_operand (operands[1], mode)) + { + rtx tmp = alpha_legitimize_address (operands[1], operands[0], mode); + if (tmp) + { + operands[1] = tmp; + return false; + } + } + + /* Early out for non-constants and valid constants. */ + if (! CONSTANT_P (operands[1]) || input_operand (operands[1], mode)) + return false; + + /* Split large integers. */ + if (GET_CODE (operands[1]) == CONST_INT + || GET_CODE (operands[1]) == CONST_DOUBLE) + { + HOST_WIDE_INT i0, i1; + rtx temp = NULL_RTX; + + if (GET_CODE (operands[1]) == CONST_INT) + { + i0 = INTVAL (operands[1]); + i1 = -(i0 < 0); + } + else if (HOST_BITS_PER_WIDE_INT >= 64) + { + i0 = CONST_DOUBLE_LOW (operands[1]); + i1 = -(i0 < 0); + } + else + { + i0 = CONST_DOUBLE_LOW (operands[1]); + i1 = CONST_DOUBLE_HIGH (operands[1]); + } + + if (HOST_BITS_PER_WIDE_INT >= 64 || i1 == -(i0 < 0)) + temp = alpha_emit_set_const (operands[0], mode, i0, 3); + + if (!temp && TARGET_BUILD_CONSTANTS) + temp = alpha_emit_set_long_const (operands[0], i0, i1); + + if (temp) + { + if (rtx_equal_p (operands[0], temp)) + return true; + operands[1] = temp; + return false; + } + } + + /* Otherwise we've nothing left but to drop the thing to memory. */ + operands[1] = force_const_mem (DImode, operands[1]); + if (reload_in_progress) + { + emit_move_insn (operands[0], XEXP (operands[1], 0)); + operands[1] = copy_rtx (operands[1]); + XEXP (operands[1], 0) = operands[0]; + } + else + operands[1] = validize_mem (operands[1]); + return false; +} + +/* Expand a non-bwx QImode or HImode move instruction; + return true if all work is done. */ + +bool +alpha_expand_mov_nobwx (mode, operands) + enum machine_mode mode; + rtx *operands; +{ + /* If the output is not a register, the input must be. */ + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (mode, operands[1]); + + /* Handle four memory cases, unaligned and aligned for either the input + or the output. The only case where we can be called during reload is + for aligned loads; all other cases require temporaries. */ + + if (GET_CODE (operands[1]) == MEM + || (GET_CODE (operands[1]) == SUBREG + && GET_CODE (SUBREG_REG (operands[1])) == MEM) + || (reload_in_progress && GET_CODE (operands[1]) == REG + && REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER) + || (reload_in_progress && GET_CODE (operands[1]) == SUBREG + && GET_CODE (SUBREG_REG (operands[1])) == REG + && REGNO (SUBREG_REG (operands[1])) >= FIRST_PSEUDO_REGISTER)) + { + if (aligned_memory_operand (operands[1], mode)) + { + if (reload_in_progress) + { + emit_insn ((mode == QImode + ? gen_reload_inqi_help + : gen_reload_inhi_help) + (operands[0], operands[1], + gen_rtx_REG (SImode, REGNO (operands[0])))); + } + else + { + rtx aligned_mem, bitnum; + rtx scratch = gen_reg_rtx (SImode); + + get_aligned_mem (operands[1], &aligned_mem, &bitnum); + + emit_insn ((mode == QImode + ? gen_aligned_loadqi + : gen_aligned_loadhi) + (operands[0], aligned_mem, bitnum, scratch)); + } + } + else + { + /* Don't pass these as parameters since that makes the generated + code depend on parameter evaluation order which will cause + bootstrap failures. */ + + rtx temp1 = gen_reg_rtx (DImode); + rtx temp2 = gen_reg_rtx (DImode); + rtx seq = ((mode == QImode + ? gen_unaligned_loadqi + : gen_unaligned_loadhi) + (operands[0], get_unaligned_address (operands[1], 0), + temp1, temp2)); + + alpha_set_memflags (seq, operands[1]); + emit_insn (seq); + } + return true; + } + + if (GET_CODE (operands[0]) == MEM + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == MEM) + || (reload_in_progress && GET_CODE (operands[0]) == REG + && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER) + || (reload_in_progress && GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER)) + { + if (aligned_memory_operand (operands[0], mode)) + { + rtx aligned_mem, bitnum; + rtx temp1 = gen_reg_rtx (SImode); + rtx temp2 = gen_reg_rtx (SImode); + + get_aligned_mem (operands[0], &aligned_mem, &bitnum); + + emit_insn (gen_aligned_store (aligned_mem, operands[1], bitnum, + temp1, temp2)); + } + else + { + rtx temp1 = gen_reg_rtx (DImode); + rtx temp2 = gen_reg_rtx (DImode); + rtx temp3 = gen_reg_rtx (DImode); + rtx seq = ((mode == QImode + ? gen_unaligned_storeqi + : gen_unaligned_storehi) + (get_unaligned_address (operands[0], 0), + operands[1], temp1, temp2, temp3)); + + alpha_set_memflags (seq, operands[0]); + emit_insn (seq); + } + return true; + } + + return false; +} + +/* Generate an unsigned DImode to FP conversion. This is the same code + optabs would emit if we didn't have TFmode patterns. + + For SFmode, this is the only construction I've found that can pass + gcc.c-torture/execute/ieee/rbug.c. No scenario that uses DFmode + intermediates will work, because you'll get intermediate rounding + that ruins the end result. Some of this could be fixed by turning + on round-to-positive-infinity, but that requires diddling the fpsr, + which kills performance. I tried turning this around and converting + to a negative number, so that I could turn on /m, but either I did + it wrong or there's something else cause I wound up with the exact + same single-bit error. There is a branch-less form of this same code: + + srl $16,1,$1 + and $16,1,$2 + cmplt $16,0,$3 + or $1,$2,$2 + cmovge $16,$16,$2 + itoft $3,$f10 + itoft $2,$f11 + cvtqs $f11,$f11 + adds $f11,$f11,$f0 + fcmoveq $f10,$f11,$f0 + + I'm not using it because it's the same number of instructions as + this branch-full form, and it has more serialized long latency + instructions on the critical path. + + For DFmode, we can avoid rounding errors by breaking up the word + into two pieces, converting them separately, and adding them back: + + LC0: .long 0,0x5f800000 + + itoft $16,$f11 + lda $2,LC0 + cmplt $16,0,$1 + cpyse $f11,$f31,$f10 + cpyse $f31,$f11,$f11 + s4addq $1,$2,$1 + lds $f12,0($1) + cvtqt $f10,$f10 + cvtqt $f11,$f11 + addt $f12,$f10,$f0 + addt $f0,$f11,$f0 + + This doesn't seem to be a clear-cut win over the optabs form. + It probably all depends on the distribution of numbers being + converted -- in the optabs form, all but high-bit-set has a + much lower minimum execution time. */ + +void +alpha_emit_floatuns (operands) + rtx operands[2]; +{ + rtx neglab, donelab, i0, i1, f0, in, out; + enum machine_mode mode; + + out = operands[0]; + in = force_reg (DImode, operands[1]); + mode = GET_MODE (out); + neglab = gen_label_rtx (); + donelab = gen_label_rtx (); + i0 = gen_reg_rtx (DImode); + i1 = gen_reg_rtx (DImode); + f0 = gen_reg_rtx (mode); + + emit_cmp_and_jump_insns (in, const0_rtx, LT, const0_rtx, DImode, 0, neglab); + + emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_FLOAT (mode, in))); + emit_jump_insn (gen_jump (donelab)); + emit_barrier (); + + emit_label (neglab); + + emit_insn (gen_lshrdi3 (i0, in, const1_rtx)); + emit_insn (gen_anddi3 (i1, in, const1_rtx)); + emit_insn (gen_iordi3 (i0, i0, i1)); + emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_FLOAT (mode, i0))); + emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_PLUS (mode, f0, f0))); + + emit_label (donelab); +} + /* Generate the comparison for a conditional branch. */ rtx @@ -1355,26 +2772,56 @@ alpha_emit_conditional_branch (code) { enum rtx_code cmp_code, branch_code; enum machine_mode cmp_mode, branch_mode = VOIDmode; - rtx op0 = alpha_compare_op0, op1 = alpha_compare_op1; + rtx op0 = alpha_compare.op0, op1 = alpha_compare.op1; rtx tem; + if (alpha_compare.fp_p && GET_MODE (op0) == TFmode) + { + if (! TARGET_HAS_XFLOATING_LIBS) + abort (); + + /* X_floating library comparison functions return + -1 unordered + 0 false + 1 true + Convert the compare against the raw return value. */ + + if (code == UNORDERED || code == ORDERED) + cmp_code = EQ; + else + cmp_code = code; + + 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 that we have, choosing the branch as necessary. */ switch (code) { case EQ: case LE: case LT: case LEU: case LTU: + case UNORDERED: /* We have these compares: */ cmp_code = code, branch_code = NE; break; case NE: - /* This must be reversed. */ - cmp_code = EQ, branch_code = EQ; + case ORDERED: + /* These must be reversed. */ + cmp_code = reverse_condition (code), branch_code = EQ; break; case GE: case GT: case GEU: case GTU: /* For FP, we swap them, for INT, we reverse them. */ - if (alpha_compare_fp_p) + if (alpha_compare.fp_p) { cmp_code = swap_condition (code); branch_code = NE; @@ -1391,10 +2838,10 @@ alpha_emit_conditional_branch (code) abort (); } - if (alpha_compare_fp_p) + if (alpha_compare.fp_p) { cmp_mode = DFmode; - if (flag_fast_math) + if (flag_unsafe_math_optimizations) { /* When we are not as concerned about non-finite values, and we are comparing against zero, we can branch directly. */ @@ -1443,11 +2890,12 @@ alpha_emit_conditional_branch (code) } } } - } - /* Force op0 into a register. */ - if (GET_CODE (op0) != REG) - op0 = force_reg (cmp_mode, op0); + if (!reg_or_0_operand (op0, DImode)) + op0 = force_reg (DImode, op0); + if (cmp_code != PLUS && !reg_or_8bit_operand (op1, DImode)) + op1 = force_reg (DImode, op1); + } /* Emit an initial compare instruction, if necessary. */ tem = op0; @@ -1457,17 +2905,129 @@ alpha_emit_conditional_branch (code) emit_move_insn (tem, gen_rtx_fmt_ee (cmp_code, cmp_mode, op0, op1)); } + /* Zero the operands. */ + memset (&alpha_compare, 0, sizeof (alpha_compare)); + /* Return the branch comparison. */ return gen_rtx_fmt_ee (branch_code, branch_mode, tem, CONST0_RTX (cmp_mode)); } +/* Certain simplifications can be done to make invalid setcc operations + valid. Return the final comparison, or NULL if we can't work. */ + +rtx +alpha_emit_setcc (code) + enum rtx_code code; +{ + enum rtx_code cmp_code; + rtx op0 = alpha_compare.op0, op1 = alpha_compare.op1; + int fp_p = alpha_compare.fp_p; + rtx tmp; + + /* Zero the operands. */ + memset (&alpha_compare, 0, sizeof (alpha_compare)); + + if (fp_p && GET_MODE (op0) == TFmode) + { + if (! TARGET_HAS_XFLOATING_LIBS) + abort (); + + /* X_floating library comparison functions return + -1 unordered + 0 false + 1 true + Convert the compare against the raw return value. */ + + if (code == UNORDERED || code == ORDERED) + cmp_code = EQ; + else + cmp_code = code; + + op0 = alpha_emit_xfloating_compare (cmp_code, op0, op1); + op1 = const0_rtx; + fp_p = 0; + + if (code == UNORDERED) + code = LT; + else if (code == ORDERED) + code = GE; + else + code = GT; + } + + if (fp_p && !TARGET_FIX) + return NULL_RTX; + + /* The general case: fold the comparison code to the types of compares + that we have, choosing the branch as necessary. */ + + cmp_code = NIL; + switch (code) + { + case EQ: case LE: case LT: case LEU: case LTU: + case UNORDERED: + /* We have these compares. */ + if (fp_p) + cmp_code = code, code = NE; + break; + + case NE: + if (!fp_p && op1 == const0_rtx) + break; + /* FALLTHRU */ + + case ORDERED: + cmp_code = reverse_condition (code); + code = EQ; + break; + + case GE: case GT: case GEU: case GTU: + /* These normally need swapping, but for integer zero we have + special patterns that recognize swapped operands. */ + if (!fp_p && op1 == const0_rtx) + break; + code = swap_condition (code); + if (fp_p) + cmp_code = code, code = NE; + tmp = op0, op0 = op1, op1 = tmp; + break; + + default: + abort (); + } + + if (!fp_p) + { + if (!register_operand (op0, DImode)) + op0 = force_reg (DImode, op0); + if (!reg_or_8bit_operand (op1, DImode)) + op1 = force_reg (DImode, op1); + } + + /* Emit an initial compare instruction, if necessary. */ + if (cmp_code != NIL) + { + enum machine_mode mode = fp_p ? DFmode : DImode; + + tmp = gen_reg_rtx (mode); + emit_insn (gen_rtx_SET (VOIDmode, tmp, + gen_rtx_fmt_ee (cmp_code, mode, op0, op1))); + + op0 = fp_p ? gen_lowpart (DImode, tmp) : tmp; + op1 = const0_rtx; + } + + /* Return the setcc comparison. */ + return gen_rtx_fmt_ee (code, DImode, op0, op1); +} + /* Rewrite a comparison against zero CMP of the form (CODE (cc0) (const_int 0)) so it can be written validly in a conditional move (if_then_else CMP ...). If both of the operands that set cc0 are non-zero we must emit an insn to perform the compare (it can't be done within - the conditional move). */ + the conditional move). */ rtx alpha_emit_conditional_move (cmp, mode) rtx cmp; @@ -1475,29 +3035,87 @@ alpha_emit_conditional_move (cmp, mode) { enum rtx_code code = GET_CODE (cmp); enum rtx_code cmov_code = NE; - rtx op0 = alpha_compare_op0; - rtx op1 = alpha_compare_op1; + rtx op0 = alpha_compare.op0; + rtx op1 = alpha_compare.op1; + int fp_p = alpha_compare.fp_p; enum machine_mode cmp_mode = (GET_MODE (op0) == VOIDmode ? DImode : GET_MODE (op0)); - enum machine_mode cmp_op_mode = alpha_compare_fp_p ? DFmode : DImode; + enum machine_mode cmp_op_mode = fp_p ? DFmode : DImode; enum machine_mode cmov_mode = VOIDmode; + int local_fast_math = flag_unsafe_math_optimizations; rtx tem; - if (alpha_compare_fp_p != FLOAT_MODE_P (mode)) - return 0; + /* Zero the operands. */ + memset (&alpha_compare, 0, sizeof (alpha_compare)); + + if (fp_p != FLOAT_MODE_P (mode)) + { + enum rtx_code cmp_code; + + if (! TARGET_FIX) + return 0; + + /* 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 + use a normal cmov, or vice-versa. */ + + switch (code) + { + case EQ: case LE: case LT: case LEU: case LTU: + /* We have these compares. */ + cmp_code = code, code = NE; + break; + + case NE: + /* This must be reversed. */ + cmp_code = EQ, code = EQ; + break; + + case GE: case GT: case GEU: case GTU: + /* These normally need swapping, but for integer zero we have + special patterns that recognize swapped operands. */ + if (!fp_p && op1 == const0_rtx) + cmp_code = code, code = NE; + else + { + cmp_code = swap_condition (code); + code = NE; + tem = op0, op0 = op1, op1 = tem; + } + break; + + default: + abort (); + } + + tem = gen_reg_rtx (cmp_op_mode); + emit_insn (gen_rtx_SET (VOIDmode, tem, + gen_rtx_fmt_ee (cmp_code, cmp_op_mode, + op0, op1))); + + cmp_mode = cmp_op_mode = fp_p ? DImode : DFmode; + op0 = gen_lowpart (cmp_op_mode, tem); + op1 = CONST0_RTX (cmp_op_mode); + fp_p = !fp_p; + local_fast_math = 1; + } /* We may be able to use a conditional move directly. - This avoids emitting spurious compares. */ - if (signed_comparison_operator (cmp, cmp_op_mode) - && (!alpha_compare_fp_p || flag_fast_math) + This avoids emitting spurious compares. */ + if (signed_comparison_operator (cmp, VOIDmode) + && (!fp_p || local_fast_math) && (op0 == CONST0_RTX (cmp_mode) || op1 == CONST0_RTX (cmp_mode))) return gen_rtx_fmt_ee (code, VOIDmode, op0, op1); - /* We can't put the comparison insides a conditional move; + /* We can't put the comparison inside the conditional move; emit a compare instruction and put that inside the conditional move. Make sure we emit only comparisons we have; swap or reverse as necessary. */ + if (no_new_pseudos) + return NULL_RTX; + switch (code) { case EQ: case LE: case LT: case LEU: case LTU: @@ -1505,33 +3123,502 @@ alpha_emit_conditional_move (cmp, mode) break; case NE: - /* This must be reversed. */ + /* This must be reversed. */ code = reverse_condition (code); cmov_code = EQ; break; case GE: case GT: case GEU: case GTU: - /* These must be swapped. Make sure the new first operand is in - a register. */ - code = swap_condition (code); - tem = op0, op0 = op1, op1 = tem; - op0 = force_reg (cmp_mode, op0); + /* These must be swapped. */ + if (op1 != CONST0_RTX (cmp_mode)) + { + code = swap_condition (code); + tem = op0, op0 = op1, op1 = tem; + } break; default: abort (); } + if (!fp_p) + { + if (!reg_or_0_operand (op0, DImode)) + op0 = force_reg (DImode, op0); + if (!reg_or_8bit_operand (op1, DImode)) + op1 = force_reg (DImode, op1); + } + /* ??? We mark the branch mode to be CCmode to prevent the compare and cmov from being combined, since the compare insn follows IEEE rules that the cmov does not. */ - if (alpha_compare_fp_p && !flag_fast_math) + if (fp_p && !local_fast_math) cmov_mode = CCmode; tem = gen_reg_rtx (cmp_op_mode); emit_move_insn (tem, gen_rtx_fmt_ee (code, cmp_op_mode, op0, op1)); return gen_rtx_fmt_ee (cmov_code, cmov_mode, tem, CONST0_RTX (cmp_op_mode)); } + +/* Simplify a conditional move of two constants into a setcc with + arithmetic. This is done with a splitter since combine would + just undo the work if done during code generation. It also catches + cases we wouldn't have before cse. */ + +int +alpha_split_conditional_move (code, dest, cond, t_rtx, f_rtx) + enum rtx_code code; + rtx dest, cond, t_rtx, f_rtx; +{ + HOST_WIDE_INT t, f, diff; + enum machine_mode mode; + rtx target, subtarget, tmp; + + mode = GET_MODE (dest); + t = INTVAL (t_rtx); + f = INTVAL (f_rtx); + diff = t - f; + + if (((code == NE || code == EQ) && diff < 0) + || (code == GE || code == GT)) + { + code = reverse_condition (code); + diff = t, t = f, f = diff; + diff = t - f; + } + + subtarget = target = dest; + if (mode != DImode) + { + target = gen_lowpart (DImode, dest); + if (! no_new_pseudos) + subtarget = gen_reg_rtx (DImode); + 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 + viable over a longer latency cmove. On EV5, the E0 slot is a + scarce resource, and on EV4 shift has the same latency as a cmove. */ + && (diff <= 8 || alpha_cpu == PROCESSOR_EV6)) + { + tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx); + emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp)); + + 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, copy_rtx (subtarget), tmp)); + + 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, copy_rtx (subtarget), tmp)); + + if (diff == 1) + 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, copy_rtx (subtarget), + GEN_INT (diff)); + tmp = gen_rtx_PLUS (DImode, tmp, add_op); + emit_insn (gen_rtx_SET (VOIDmode, target, tmp)); + } + else + return 0; + } + } + else + return 0; + + return 1; +} + +/* Look up the function X_floating library function name for the + given operation. */ + +static const char * +alpha_lookup_xfloating_lib_func (code) + enum rtx_code code; +{ + struct xfloating_op + { + const enum rtx_code code; + const char *const func; + }; + + static const struct xfloating_op vms_xfloating_ops[] = + { + { PLUS, "OTS$ADD_X" }, + { MINUS, "OTS$SUB_X" }, + { MULT, "OTS$MUL_X" }, + { DIV, "OTS$DIV_X" }, + { EQ, "OTS$EQL_X" }, + { NE, "OTS$NEQ_X" }, + { LT, "OTS$LSS_X" }, + { LE, "OTS$LEQ_X" }, + { GT, "OTS$GTR_X" }, + { GE, "OTS$GEQ_X" }, + { FIX, "OTS$CVTXQ" }, + { FLOAT, "OTS$CVTQX" }, + { UNSIGNED_FLOAT, "OTS$CVTQUX" }, + { FLOAT_EXTEND, "OTS$CVT_FLOAT_T_X" }, + { FLOAT_TRUNCATE, "OTS$CVT_FLOAT_X_T" }, + }; + + static const struct xfloating_op osf_xfloating_ops[] = + { + { PLUS, "_OtsAddX" }, + { MINUS, "_OtsSubX" }, + { MULT, "_OtsMulX" }, + { DIV, "_OtsDivX" }, + { EQ, "_OtsEqlX" }, + { NE, "_OtsNeqX" }, + { LT, "_OtsLssX" }, + { LE, "_OtsLeqX" }, + { GT, "_OtsGtrX" }, + { GE, "_OtsGeqX" }, + { FIX, "_OtsCvtXQ" }, + { FLOAT, "_OtsCvtQX" }, + { UNSIGNED_FLOAT, "_OtsCvtQUX" }, + { FLOAT_EXTEND, "_OtsConvertFloatTX" }, + { FLOAT_TRUNCATE, "_OtsConvertFloatXT" }, + }; + + const struct xfloating_op *ops; + const long n = ARRAY_SIZE (osf_xfloating_ops); + long i; + + /* How irritating. Nothing to key off for the table. Hardcode + knowledge of the G_floating routines. */ + if (TARGET_FLOAT_VAX) + { + if (TARGET_ABI_OPEN_VMS) + { + if (code == FLOAT_EXTEND) + return "OTS$CVT_FLOAT_G_X"; + if (code == FLOAT_TRUNCATE) + return "OTS$CVT_FLOAT_X_G"; + } + else + { + if (code == FLOAT_EXTEND) + return "_OtsConvertFloatGX"; + if (code == FLOAT_TRUNCATE) + return "_OtsConvertFloatXG"; + } + } + + if (TARGET_ABI_OPEN_VMS) + ops = vms_xfloating_ops; + else + ops = osf_xfloating_ops; + + for (i = 0; i < n; ++i) + if (ops[i].code == code) + return ops[i].func; + + abort(); +} + +/* Most X_floating operations take the rounding mode as an argument. + Compute that here. */ + +static int +alpha_compute_xfloating_mode_arg (code, round) + enum rtx_code code; + enum alpha_fp_rounding_mode round; +{ + int mode; + + switch (round) + { + case ALPHA_FPRM_NORM: + mode = 2; + break; + case ALPHA_FPRM_MINF: + mode = 1; + break; + case ALPHA_FPRM_CHOP: + mode = 0; + break; + case ALPHA_FPRM_DYN: + mode = 4; + break; + default: + abort (); + + /* XXX For reference, round to +inf is mode = 3. */ + } + + if (code == FLOAT_TRUNCATE && alpha_fptm == ALPHA_FPTM_N) + mode |= 0x10000; + + return mode; +} + +/* Emit an X_floating library function call. + + Note that these functions do not follow normal calling conventions: + TFmode arguments are passed in two integer registers (as opposed to + indirect); TFmode return values appear in R16+R17. + + FUNC is the function name to call. + TARGET is where the output belongs. + OPERANDS are the inputs. + NOPERANDS is the count of inputs. + EQUIV is the expression equivalent for the function. +*/ + +static void +alpha_emit_xfloating_libcall (func, target, operands, noperands, equiv) + const char *func; + rtx target; + rtx operands[]; + int noperands; + rtx equiv; +{ + rtx usage = NULL_RTX, tmp, reg; + int regno = 16, i; + + start_sequence (); + + for (i = 0; i < noperands; ++i) + { + switch (GET_MODE (operands[i])) + { + case TFmode: + reg = gen_rtx_REG (TFmode, regno); + regno += 2; + break; + + case DFmode: + reg = gen_rtx_REG (DFmode, regno + 32); + regno += 1; + break; + + case VOIDmode: + if (GET_CODE (operands[i]) != CONST_INT) + abort (); + /* FALLTHRU */ + case DImode: + reg = gen_rtx_REG (DImode, regno); + regno += 1; + break; + + default: + abort (); + } + + emit_move_insn (reg, operands[i]); + usage = alloc_EXPR_LIST (0, gen_rtx_USE (VOIDmode, reg), usage); + } + + switch (GET_MODE (target)) + { + case TFmode: + reg = gen_rtx_REG (TFmode, 16); + break; + case DFmode: + reg = gen_rtx_REG (DFmode, 32); + break; + case DImode: + reg = gen_rtx_REG (DImode, 0); + break; + default: + abort (); + } + + tmp = gen_rtx_MEM (QImode, gen_rtx_SYMBOL_REF (Pmode, (char *) func)); + tmp = emit_call_insn (GEN_CALL_VALUE (reg, tmp, const0_rtx, + const0_rtx, const0_rtx)); + CALL_INSN_FUNCTION_USAGE (tmp) = usage; + + tmp = get_insns (); + end_sequence (); + + emit_libcall_block (tmp, target, reg, equiv); +} + +/* Emit an X_floating library function call for arithmetic (+,-,*,/). */ + +void +alpha_emit_xfloating_arith (code, operands) + enum rtx_code code; + rtx operands[]; +{ + const char *func; + int mode; + rtx out_operands[3]; + + func = alpha_lookup_xfloating_lib_func (code); + mode = alpha_compute_xfloating_mode_arg (code, alpha_fprm); + + out_operands[0] = operands[1]; + out_operands[1] = operands[2]; + out_operands[2] = GEN_INT (mode); + alpha_emit_xfloating_libcall (func, operands[0], out_operands, 3, + gen_rtx_fmt_ee (code, TFmode, operands[1], + operands[2])); +} + +/* Emit an X_floating library function call for a comparison. */ + +static rtx +alpha_emit_xfloating_compare (code, op0, op1) + enum rtx_code code; + rtx op0, op1; +{ + const char *func; + rtx out, operands[2]; + + func = alpha_lookup_xfloating_lib_func (code); + + operands[0] = op0; + operands[1] = op1; + out = gen_reg_rtx (DImode); + + /* ??? Strange mode for equiv because what's actually returned + is -1,0,1, not a proper boolean value. */ + alpha_emit_xfloating_libcall (func, out, operands, 2, + gen_rtx_fmt_ee (code, CCmode, op0, op1)); + + return out; +} + +/* Emit an X_floating library function call for a conversion. */ + +void +alpha_emit_xfloating_cvt (code, operands) + enum rtx_code code; + rtx operands[]; +{ + int noperands = 1, mode; + rtx out_operands[2]; + const char *func; + + func = alpha_lookup_xfloating_lib_func (code); + + out_operands[0] = operands[1]; + + switch (code) + { + case FIX: + mode = alpha_compute_xfloating_mode_arg (code, ALPHA_FPRM_CHOP); + out_operands[1] = GEN_INT (mode); + noperands = 2; + break; + case FLOAT_TRUNCATE: + mode = alpha_compute_xfloating_mode_arg (code, alpha_fprm); + out_operands[1] = GEN_INT (mode); + noperands = 2; + break; + default: + break; + } + + alpha_emit_xfloating_libcall (func, operands[0], out_operands, noperands, + gen_rtx_fmt_e (code, GET_MODE (operands[0]), + operands[1])); +} + +/* Split a TFmode OP[1] into DImode OP[2,3] and likewise for + OP[0] into OP[0,1]. Naturally, output operand ordering is + little-endian. */ + +void +alpha_split_tfmode_pair (operands) + rtx operands[4]; +{ + if (GET_CODE (operands[1]) == REG) + { + operands[3] = gen_rtx_REG (DImode, REGNO (operands[1]) + 1); + operands[2] = gen_rtx_REG (DImode, REGNO (operands[1])); + } + else if (GET_CODE (operands[1]) == MEM) + { + operands[3] = adjust_address (operands[1], DImode, 8); + operands[2] = adjust_address (operands[1], DImode, 0); + } + else if (operands[1] == CONST0_RTX (TFmode)) + operands[2] = operands[3] = const0_rtx; + else + abort (); + + if (GET_CODE (operands[0]) == REG) + { + operands[1] = gen_rtx_REG (DImode, REGNO (operands[0]) + 1); + operands[0] = gen_rtx_REG (DImode, REGNO (operands[0])); + } + else if (GET_CODE (operands[0]) == MEM) + { + operands[1] = adjust_address (operands[0], DImode, 8); + operands[0] = adjust_address (operands[0], DImode, 0); + } + else + abort (); +} + +/* Implement negtf2 or abstf2. Op0 is destination, op1 is source, + op2 is a register containing the sign bit, operation is the + logical operation to be performed. */ + +void +alpha_split_tfmode_frobsign (operands, operation) + rtx operands[3]; + rtx (*operation) PARAMS ((rtx, rtx, rtx)); +{ + rtx high_bit = operands[2]; + rtx scratch; + int move; + + alpha_split_tfmode_pair (operands); + + /* Detect three flavours of operand overlap. */ + move = 1; + if (rtx_equal_p (operands[0], operands[2])) + move = 0; + else if (rtx_equal_p (operands[1], operands[2])) + { + if (rtx_equal_p (operands[0], high_bit)) + move = 2; + else + move = -1; + } + + if (move < 0) + emit_move_insn (operands[0], operands[2]); + + /* ??? If the destination overlaps both source tf and high_bit, then + assume source tf is dead in its entirety and use the other half + for a scratch register. Otherwise "scratch" is just the proper + destination register. */ + scratch = operands[move < 2 ? 1 : 3]; + + emit_insn ((*operation) (scratch, high_bit, operands[3])); + + if (move > 0) + { + emit_move_insn (operands[0], operands[2]); + if (move > 1) + emit_move_insn (operands[1], scratch); + } +} /* Use ext[wlq][lh] as the Architecture Handbook describes for extracting unaligned data: @@ -1566,7 +3653,7 @@ alpha_expand_unaligned_load (tgt, mem, size, ofs, sign) HOST_WIDE_INT size, ofs; int sign; { - rtx meml, memh, addr, extl, exth; + rtx meml, memh, addr, extl, exth, tmp, mema; enum machine_mode mode; meml = gen_reg_rtx (DImode); @@ -1575,26 +3662,45 @@ alpha_expand_unaligned_load (tgt, mem, size, ofs, sign) extl = gen_reg_rtx (DImode); exth = gen_reg_rtx (DImode); - emit_move_insn (meml, - change_address (mem, DImode, - gen_rtx_AND (DImode, - plus_constant (XEXP (mem, 0), - ofs), - GEN_INT (-8)))); + mema = XEXP (mem, 0); + if (GET_CODE (mema) == LO_SUM) + mema = force_reg (Pmode, mema); - emit_move_insn (memh, - change_address (mem, DImode, - gen_rtx_AND (DImode, - plus_constant (XEXP (mem, 0), - ofs + size - 1), - GEN_INT (-8)))); + /* AND addresses cannot be in any alias set, since they may implicitly + alias surrounding code. Ideally we'd have some alias set that + covered all types except those with alignment 8 or higher. */ - if (sign && size == 2) + tmp = change_address (mem, DImode, + gen_rtx_AND (DImode, + plus_constant (mema, ofs), + GEN_INT (-8))); + set_mem_alias_set (tmp, 0); + emit_move_insn (meml, tmp); + + tmp = change_address (mem, DImode, + gen_rtx_AND (DImode, + plus_constant (mema, ofs + size - 1), + GEN_INT (-8))); + set_mem_alias_set (tmp, 0); + emit_move_insn (memh, tmp); + + if (WORDS_BIG_ENDIAN && sign && (size == 2 || size == 4)) { - emit_move_insn (addr, plus_constant (XEXP (mem, 0), ofs+2)); + emit_move_insn (addr, plus_constant (mema, -1)); - emit_insn (gen_extxl (extl, meml, GEN_INT (64), addr)); - emit_insn (gen_extqh (exth, memh, addr)); + emit_insn (gen_extqh_be (extl, meml, addr)); + emit_insn (gen_extxl_be (exth, memh, GEN_INT (64), addr)); + + addr = expand_binop (DImode, ior_optab, extl, exth, tgt, 1, OPTAB_WIDEN); + addr = expand_binop (DImode, ashr_optab, addr, GEN_INT (64 - size*8), + addr, 1, OPTAB_WIDEN); + } + else if (sign && size == 2) + { + emit_move_insn (addr, plus_constant (mema, ofs+2)); + + emit_insn (gen_extxl_le (extl, meml, GEN_INT (64), addr)); + emit_insn (gen_extqh_le (exth, memh, addr)); /* We must use tgt here for the target. Alpha-vms port fails if we use addr for the target, because addr is marked as a pointer and combine @@ -1605,26 +3711,55 @@ alpha_expand_unaligned_load (tgt, mem, size, ofs, sign) } else { - emit_move_insn (addr, plus_constant (XEXP (mem, 0), ofs)); - emit_insn (gen_extxl (extl, meml, GEN_INT (size*8), addr)); - switch (size) + if (WORDS_BIG_ENDIAN) { - case 2: - emit_insn (gen_extwh (exth, memh, addr)); - mode = HImode; - break; + emit_move_insn (addr, plus_constant (mema, ofs+size-1)); + switch ((int) size) + { + case 2: + emit_insn (gen_extwh_be (extl, meml, addr)); + mode = HImode; + break; - case 4: - emit_insn (gen_extlh (exth, memh, addr)); - mode = SImode; - break; + case 4: + emit_insn (gen_extlh_be (extl, meml, addr)); + mode = SImode; + break; - case 8: - emit_insn (gen_extqh (exth, memh, addr)); - mode = DImode; - break; - default: - abort(); + case 8: + emit_insn (gen_extqh_be (extl, meml, addr)); + mode = DImode; + break; + + default: + abort (); + } + emit_insn (gen_extxl_be (exth, memh, GEN_INT (size*8), addr)); + } + else + { + emit_move_insn (addr, plus_constant (mema, ofs)); + emit_insn (gen_extxl_le (extl, meml, GEN_INT (size*8), addr)); + switch ((int) size) + { + case 2: + emit_insn (gen_extwh_le (exth, memh, addr)); + mode = HImode; + break; + + case 4: + emit_insn (gen_extlh_le (exth, memh, addr)); + mode = SImode; + break; + + case 8: + emit_insn (gen_extqh_le (exth, memh, addr)); + mode = DImode; + break; + + default: + abort(); + } } addr = expand_binop (mode, ior_optab, gen_lowpart (mode, extl), @@ -1643,66 +3778,123 @@ alpha_expand_unaligned_store (dst, src, size, ofs) rtx dst, src; HOST_WIDE_INT size, ofs; { - rtx dstl, dsth, addr, insl, insh, meml, memh; + rtx dstl, dsth, addr, insl, insh, meml, memh, dsta; dstl = gen_reg_rtx (DImode); dsth = gen_reg_rtx (DImode); insl = gen_reg_rtx (DImode); insh = gen_reg_rtx (DImode); + dsta = XEXP (dst, 0); + if (GET_CODE (dsta) == LO_SUM) + dsta = force_reg (Pmode, dsta); + + /* AND addresses cannot be in any alias set, since they may implicitly + alias surrounding code. Ideally we'd have some alias set that + covered all types except those with alignment 8 or higher. */ + meml = change_address (dst, DImode, gen_rtx_AND (DImode, - plus_constant (XEXP (dst, 0), ofs), + plus_constant (dsta, ofs), GEN_INT (-8))); + set_mem_alias_set (meml, 0); + memh = change_address (dst, DImode, gen_rtx_AND (DImode, - plus_constant (XEXP (dst, 0), - ofs+size-1), + plus_constant (dsta, ofs + size - 1), GEN_INT (-8))); + set_mem_alias_set (memh, 0); emit_move_insn (dsth, memh); emit_move_insn (dstl, meml); - addr = copy_addr_to_reg (plus_constant (XEXP (dst, 0), ofs)); - - if (src != const0_rtx) + if (WORDS_BIG_ENDIAN) { - emit_insn (gen_insxh (insh, gen_lowpart (DImode, src), - GEN_INT (size*8), addr)); + addr = copy_addr_to_reg (plus_constant (dsta, ofs+size-1)); - switch (size) + if (src != const0_rtx) + { + switch ((int) size) + { + case 2: + emit_insn (gen_inswl_be (insh, gen_lowpart (HImode,src), addr)); + break; + case 4: + emit_insn (gen_insll_be (insh, gen_lowpart (SImode,src), addr)); + break; + case 8: + emit_insn (gen_insql_be (insh, gen_lowpart (DImode,src), addr)); + break; + } + emit_insn (gen_insxh (insl, gen_lowpart (DImode, src), + GEN_INT (size*8), addr)); + } + + switch ((int) size) { case 2: - emit_insn (gen_inswl (insl, gen_lowpart (HImode, src), addr)); + emit_insn (gen_mskxl_be (dsth, dsth, GEN_INT (0xffff), addr)); break; case 4: - emit_insn (gen_insll (insl, gen_lowpart (SImode, src), addr)); + emit_insn (gen_mskxl_be (dsth, dsth, GEN_INT (0xffffffff), addr)); break; case 8: - emit_insn (gen_insql (insl, src, addr)); + { +#if HOST_BITS_PER_WIDE_INT == 32 + rtx msk = immed_double_const (0xffffffff, 0xffffffff, DImode); +#else + rtx msk = constm1_rtx; +#endif + emit_insn (gen_mskxl_be (dsth, dsth, msk, addr)); + } break; } + + emit_insn (gen_mskxh (dstl, dstl, GEN_INT (size*8), addr)); } - - emit_insn (gen_mskxh (dsth, dsth, GEN_INT (size*8), addr)); - - switch (size) + else { - case 2: - emit_insn (gen_mskxl (dstl, dstl, GEN_INT (0xffff), addr)); - break; - case 4: - emit_insn (gen_mskxl (dstl, dstl, GEN_INT (0xffffffff), addr)); - break; - case 8: - { + addr = copy_addr_to_reg (plus_constant (dsta, ofs)); + + if (src != const0_rtx) + { + emit_insn (gen_insxh (insh, gen_lowpart (DImode, src), + GEN_INT (size*8), addr)); + + switch ((int) size) + { + case 2: + emit_insn (gen_inswl_le (insl, gen_lowpart (HImode, src), addr)); + break; + case 4: + emit_insn (gen_insll_le (insl, gen_lowpart (SImode, src), addr)); + break; + case 8: + emit_insn (gen_insql_le (insl, src, addr)); + break; + } + } + + emit_insn (gen_mskxh (dsth, dsth, GEN_INT (size*8), addr)); + + switch ((int) size) + { + case 2: + 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); + rtx msk = immed_double_const (0xffffffff, 0xffffffff, DImode); #else - rtx msk = immed_double_const (0xffffffffffffffff, 0, DImode); + rtx msk = constm1_rtx; #endif - emit_insn (gen_mskxl (dstl, dstl, msk, addr)); - } - break; + emit_insn (gen_mskxl_le (dstl, dstl, msk, addr)); + } + break; + } } if (src != const0_rtx) @@ -1710,10 +3902,18 @@ alpha_expand_unaligned_store (dst, src, size, ofs) dsth = expand_binop (DImode, ior_optab, insh, dsth, dsth, 0, OPTAB_WIDEN); dstl = expand_binop (DImode, ior_optab, insl, dstl, dstl, 0, OPTAB_WIDEN); } - - /* Must store high before low for degenerate case of aligned. */ - emit_move_insn (memh, dsth); - emit_move_insn (meml, dstl); + + if (WORDS_BIG_ENDIAN) + { + emit_move_insn (meml, dstl); + emit_move_insn (memh, dsth); + } + else + { + /* Must store high before low for degenerate case of aligned. */ + emit_move_insn (memh, dsth); + emit_move_insn (meml, dstl); + } } /* The block move code tries to maximize speed by separating loads and @@ -1735,9 +3935,13 @@ alpha_expand_unaligned_load_words (out_regs, smem, words, ofs) rtx const im8 = GEN_INT (-8); rtx const i64 = GEN_INT (64); rtx ext_tmps[MAX_MOVE_WORDS], data_regs[MAX_MOVE_WORDS+1]; - rtx sreg, areg; + rtx sreg, areg, tmp, smema; HOST_WIDE_INT i; + smema = XEXP (smem, 0); + if (GET_CODE (smema) == LO_SUM) + smema = force_reg (Pmode, smema); + /* Generate all the tmp registers we need. */ for (i = 0; i < words; ++i) { @@ -1747,38 +3951,47 @@ alpha_expand_unaligned_load_words (out_regs, smem, words, ofs) data_regs[words] = gen_reg_rtx (DImode); if (ofs != 0) - smem = change_address (smem, GET_MODE (smem), - plus_constant (XEXP (smem, 0), ofs)); + smem = adjust_address (smem, GET_MODE (smem), ofs); /* Load up all of the source data. */ for (i = 0; i < words; ++i) { - emit_move_insn (data_regs[i], - change_address (smem, DImode, - gen_rtx_AND (DImode, - plus_constant (XEXP(smem,0), - 8*i), - im8))); + tmp = change_address (smem, DImode, + gen_rtx_AND (DImode, + plus_constant (smema, 8*i), + im8)); + set_mem_alias_set (tmp, 0); + emit_move_insn (data_regs[i], tmp); } - emit_move_insn (data_regs[words], - change_address (smem, DImode, - gen_rtx_AND (DImode, - plus_constant (XEXP(smem,0), - 8*words - 1), - im8))); + + tmp = change_address (smem, DImode, + gen_rtx_AND (DImode, + plus_constant (smema, 8*words - 1), + im8)); + set_mem_alias_set (tmp, 0); + emit_move_insn (data_regs[words], tmp); /* Extract the half-word fragments. Unfortunately DEC decided to make extxh with offset zero a noop instead of zeroing the register, so we must take care of that edge condition ourselves with cmov. */ - sreg = copy_addr_to_reg (XEXP (smem, 0)); + sreg = copy_addr_to_reg (smema); areg = expand_binop (DImode, and_optab, sreg, GEN_INT (7), NULL, 1, OPTAB_WIDEN); + if (WORDS_BIG_ENDIAN) + emit_move_insn (sreg, plus_constant (sreg, 7)); for (i = 0; i < words; ++i) { - emit_insn (gen_extxl (data_regs[i], data_regs[i], i64, sreg)); - - emit_insn (gen_extqh (ext_tmps[i], data_regs[i+1], sreg)); + if (WORDS_BIG_ENDIAN) + { + emit_insn (gen_extqh_be (data_regs[i], data_regs[i], sreg)); + emit_insn (gen_extxl_be (ext_tmps[i], data_regs[i+1], i64, sreg)); + } + else + { + emit_insn (gen_extxl_le (data_regs[i], data_regs[i], i64, sreg)); + emit_insn (gen_extqh_le (ext_tmps[i], data_regs[i+1], sreg)); + } emit_insn (gen_rtx_SET (VOIDmode, ext_tmps[i], gen_rtx_IF_THEN_ELSE (DImode, gen_rtx_EQ (DImode, areg, @@ -1808,13 +4021,17 @@ alpha_expand_unaligned_store_words (data_regs, dmem, words, ofs) #if HOST_BITS_PER_WIDE_INT == 32 rtx const im1 = immed_double_const (0xffffffff, 0xffffffff, DImode); #else - rtx const im1 = immed_double_const (0xffffffffffffffff, 0, DImode); + 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; + rtx st_addr_1, st_addr_2, dmema; HOST_WIDE_INT i; + dmema = XEXP (dmem, 0); + if (GET_CODE (dmema) == LO_SUM) + dmema = force_reg (Pmode, dmema); + /* Generate all the tmp registers we need. */ if (data_regs != NULL) for (i = 0; i < words; ++i) @@ -1823,32 +4040,40 @@ alpha_expand_unaligned_store_words (data_regs, dmem, words, ofs) st_tmp_2 = gen_reg_rtx(DImode); if (ofs != 0) - dmem = change_address (dmem, GET_MODE (dmem), - plus_constant (XEXP (dmem, 0), ofs)); - + dmem = adjust_address (dmem, GET_MODE (dmem), ofs); st_addr_2 = change_address (dmem, DImode, gen_rtx_AND (DImode, - plus_constant (XEXP(dmem,0), - words*8 - 1), + plus_constant (dmema, words*8 - 1), im8)); + set_mem_alias_set (st_addr_2, 0); + st_addr_1 = change_address (dmem, DImode, - gen_rtx_AND (DImode, - XEXP (dmem, 0), - im8)); + gen_rtx_AND (DImode, dmema, im8)); + set_mem_alias_set (st_addr_1, 0); /* Load up the destination end bits. */ emit_move_insn (st_tmp_2, st_addr_2); emit_move_insn (st_tmp_1, st_addr_1); /* Shift the input data into place. */ - dreg = copy_addr_to_reg (XEXP (dmem, 0)); + dreg = copy_addr_to_reg (dmema); + if (WORDS_BIG_ENDIAN) + emit_move_insn (dreg, plus_constant (dreg, 7)); if (data_regs != NULL) { for (i = words-1; i >= 0; --i) { - emit_insn (gen_insxh (ins_tmps[i], data_regs[i], i64, dreg)); - emit_insn (gen_insql (data_regs[i], data_regs[i], dreg)); + if (WORDS_BIG_ENDIAN) + { + emit_insn (gen_insql_be (ins_tmps[i], data_regs[i], dreg)); + emit_insn (gen_insxh (data_regs[i], data_regs[i], i64, dreg)); + } + else + { + emit_insn (gen_insxh (ins_tmps[i], data_regs[i], i64, dreg)); + emit_insn (gen_insql_le (data_regs[i], data_regs[i], dreg)); + } } for (i = words-1; i > 0; --i) { @@ -1859,8 +4084,16 @@ alpha_expand_unaligned_store_words (data_regs, dmem, words, ofs) } /* Split and merge the ends with the destination data. */ - emit_insn (gen_mskxh (st_tmp_2, st_tmp_2, i64, dreg)); - emit_insn (gen_mskxl (st_tmp_1, st_tmp_1, im1, dreg)); + if (WORDS_BIG_ENDIAN) + { + emit_insn (gen_mskxl_be (st_tmp_2, st_tmp_2, im1, 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)); + } if (data_regs != NULL) { @@ -1871,17 +4104,24 @@ alpha_expand_unaligned_store_words (data_regs, dmem, words, ofs) } /* Store it all. */ - emit_move_insn (st_addr_2, st_tmp_2); + if (WORDS_BIG_ENDIAN) + emit_move_insn (st_addr_1, st_tmp_1); + else + emit_move_insn (st_addr_2, st_tmp_2); for (i = words-1; i > 0; --i) { - emit_move_insn (change_address (dmem, DImode, - gen_rtx_AND (DImode, - plus_constant(XEXP (dmem,0), - i*8), - im8)), - data_regs ? ins_tmps[i-1] : const0_rtx); + rtx tmp = change_address (dmem, DImode, + gen_rtx_AND (DImode, + plus_constant(dmema, + WORDS_BIG_ENDIAN ? i*8-1 : i*8), + im8)); + set_mem_alias_set (tmp, 0); + emit_move_insn (tmp, data_regs ? ins_tmps[i-1] : const0_rtx); } - emit_move_insn (st_addr_1, st_tmp_1); + if (WORDS_BIG_ENDIAN) + emit_move_insn (st_addr_2, st_tmp_2); + else + emit_move_insn (st_addr_1, st_tmp_1); } @@ -1900,76 +4140,68 @@ alpha_expand_block_move (operands) rtx align_rtx = operands[3]; HOST_WIDE_INT orig_bytes = INTVAL (bytes_rtx); HOST_WIDE_INT bytes = orig_bytes; - HOST_WIDE_INT src_align = INTVAL (align_rtx); + HOST_WIDE_INT src_align = INTVAL (align_rtx) * BITS_PER_UNIT; HOST_WIDE_INT dst_align = src_align; - rtx orig_src = operands[1]; - rtx orig_dst = operands[0]; - rtx data_regs[2*MAX_MOVE_WORDS+16]; + rtx orig_src = operands[1]; + rtx orig_dst = operands[0]; + rtx data_regs[2 * MAX_MOVE_WORDS + 16]; rtx tmp; - int i, words, ofs, nregs = 0; + unsigned int i, words, ofs, nregs = 0; - if (bytes <= 0) + if (orig_bytes <= 0) return 1; - if (bytes > MAX_MOVE_WORDS*8) + else if (orig_bytes > MAX_MOVE_WORDS * UNITS_PER_WORD) return 0; /* Look for additional alignment information from recorded register info. */ tmp = XEXP (orig_src, 0); if (GET_CODE (tmp) == REG) - { - if (REGNO_POINTER_ALIGN (REGNO (tmp)) > src_align) - src_align = REGNO_POINTER_ALIGN (REGNO (tmp)); - } + src_align = MAX (src_align, REGNO_POINTER_ALIGN (REGNO (tmp))); else if (GET_CODE (tmp) == PLUS && GET_CODE (XEXP (tmp, 0)) == REG && GET_CODE (XEXP (tmp, 1)) == CONST_INT) { - HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1)); - int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0))); + unsigned HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1)); + unsigned int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0))); if (a > src_align) { - if (a >= 8 && c % 8 == 0) - src_align = 8; - else if (a >= 4 && c % 4 == 0) - src_align = 4; - else if (a >= 2 && c % 2 == 0) - src_align = 2; + if (a >= 64 && c % 8 == 0) + src_align = 64; + else if (a >= 32 && c % 4 == 0) + src_align = 32; + else if (a >= 16 && c % 2 == 0) + src_align = 16; } } tmp = XEXP (orig_dst, 0); if (GET_CODE (tmp) == REG) - { - if (REGNO_POINTER_ALIGN (REGNO (tmp)) > dst_align) - dst_align = REGNO_POINTER_ALIGN (REGNO (tmp)); - } + dst_align = MAX (dst_align, REGNO_POINTER_ALIGN (REGNO (tmp))); else if (GET_CODE (tmp) == PLUS && GET_CODE (XEXP (tmp, 0)) == REG && GET_CODE (XEXP (tmp, 1)) == CONST_INT) { - HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1)); - int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0))); + unsigned HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1)); + unsigned int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0))); if (a > dst_align) { - if (a >= 8 && c % 8 == 0) - dst_align = 8; - else if (a >= 4 && c % 4 == 0) - dst_align = 4; - else if (a >= 2 && c % 2 == 0) - dst_align = 2; + if (a >= 64 && c % 8 == 0) + dst_align = 64; + else if (a >= 32 && c % 4 == 0) + dst_align = 32; + else if (a >= 16 && c % 2 == 0) + dst_align = 16; } } - /* - * Load the entire block into registers. - */ - + /* Load the entire block into registers. */ if (GET_CODE (XEXP (orig_src, 0)) == ADDRESSOF) { enum machine_mode mode; + tmp = XEXP (XEXP (orig_src, 0), 0); /* Don't use the existing register if we're reading more than @@ -1982,64 +4214,60 @@ alpha_expand_block_move (operands) if (mode == TImode) { data_regs[nregs] = gen_lowpart (DImode, tmp); - data_regs[nregs+1] = gen_highpart (DImode, tmp); + data_regs[nregs + 1] = gen_highpart (DImode, tmp); nregs += 2; } else data_regs[nregs++] = gen_lowpart (mode, tmp); + goto src_done; } /* No appropriate mode; fall back on memory. */ - orig_src = change_address (orig_src, GET_MODE (orig_src), - copy_addr_to_reg (XEXP (orig_src, 0))); + orig_src = replace_equiv_address (orig_src, + copy_addr_to_reg (XEXP (orig_src, 0))); + src_align = GET_MODE_BITSIZE (GET_MODE (tmp)); } ofs = 0; - if (src_align >= 8 && bytes >= 8) + if (src_align >= 64 && bytes >= 8) { words = bytes / 8; for (i = 0; i < words; ++i) - data_regs[nregs+i] = gen_reg_rtx(DImode); + data_regs[nregs + i] = gen_reg_rtx (DImode); for (i = 0; i < words; ++i) - { - emit_move_insn (data_regs[nregs+i], - change_address (orig_src, DImode, - plus_constant (XEXP (orig_src, 0), - ofs + i*8))); - } + emit_move_insn (data_regs[nregs + i], + adjust_address (orig_src, DImode, ofs + i * 8)); nregs += words; bytes -= words * 8; ofs += words * 8; } - if (src_align >= 4 && bytes >= 4) + + if (src_align >= 32 && bytes >= 4) { words = bytes / 4; for (i = 0; i < words; ++i) - data_regs[nregs+i] = gen_reg_rtx(SImode); + data_regs[nregs + i] = gen_reg_rtx (SImode); for (i = 0; i < words; ++i) - { - emit_move_insn (data_regs[nregs+i], - change_address (orig_src, SImode, - plus_constant (XEXP (orig_src, 0), - ofs + i*4))); - } + emit_move_insn (data_regs[nregs + i], + adjust_address (orig_src, SImode, ofs + i * 4)); nregs += words; bytes -= words * 4; ofs += words * 4; } - if (bytes >= 16) + + if (bytes >= 8) { words = bytes / 8; for (i = 0; i < words+1; ++i) - data_regs[nregs+i] = gen_reg_rtx(DImode); + data_regs[nregs + i] = gen_reg_rtx (DImode); alpha_expand_unaligned_load_words (data_regs + nregs, orig_src, words, ofs); @@ -2048,35 +4276,27 @@ alpha_expand_block_move (operands) bytes -= words * 8; ofs += words * 8; } - if (!TARGET_BWX && bytes >= 8) - { - data_regs[nregs++] = tmp = gen_reg_rtx (DImode); - alpha_expand_unaligned_load (tmp, orig_src, 8, ofs, 0); - bytes -= 8; - ofs += 8; - } - if (!TARGET_BWX && bytes >= 4) + + if (! TARGET_BWX && bytes >= 4) { data_regs[nregs++] = tmp = gen_reg_rtx (SImode); alpha_expand_unaligned_load (tmp, orig_src, 4, ofs, 0); bytes -= 4; ofs += 4; } + if (bytes >= 2) { - if (src_align >= 2) + if (src_align >= 16) { do { data_regs[nregs++] = tmp = gen_reg_rtx (HImode); - emit_move_insn (tmp, - change_address (orig_src, HImode, - plus_constant (XEXP (orig_src, 0), - ofs))); + emit_move_insn (tmp, adjust_address (orig_src, HImode, ofs)); bytes -= 2; ofs += 2; } while (bytes >= 2); } - else if (!TARGET_BWX) + else if (! TARGET_BWX) { data_regs[nregs++] = tmp = gen_reg_rtx (HImode); alpha_expand_unaligned_load (tmp, orig_src, 2, ofs, 0); @@ -2084,24 +4304,21 @@ alpha_expand_block_move (operands) ofs += 2; } } + while (bytes > 0) { data_regs[nregs++] = tmp = gen_reg_rtx (QImode); - emit_move_insn (tmp, - change_address (orig_src, QImode, - plus_constant (XEXP (orig_src, 0), - ofs))); + emit_move_insn (tmp, adjust_address (orig_src, QImode, ofs)); bytes -= 1; ofs += 1; } + src_done: - if (nregs > (int)(sizeof(data_regs)/sizeof(*data_regs))) - abort(); + if (nregs > ARRAY_SIZE (data_regs)) + abort (); - /* - * Now save it back out again. - */ + /* Now save it back out again. */ i = 0, ofs = 0; @@ -2119,15 +4336,14 @@ alpha_expand_block_move (operands) i = 1; goto dst_done; } + else if (nregs == 2 && mode == TImode) { /* Undo the subregging done above when copying between two TImode registers. */ if (GET_CODE (data_regs[0]) == SUBREG && GET_MODE (SUBREG_REG (data_regs[0])) == TImode) - { - emit_move_insn (tmp, SUBREG_REG (data_regs[0])); - } + emit_move_insn (tmp, SUBREG_REG (data_regs[0])); else { rtx seq; @@ -2152,25 +4368,24 @@ alpha_expand_block_move (operands) /* No appropriate mode; fall back on memory. We can speed things up by recognizing extra alignment information. */ - orig_dst = change_address (orig_dst, GET_MODE (orig_dst), - copy_addr_to_reg (XEXP (orig_dst, 0))); - dst_align = GET_MODE_SIZE (GET_MODE (tmp)); + orig_dst = replace_equiv_address (orig_dst, + copy_addr_to_reg (XEXP (orig_dst, 0))); + dst_align = GET_MODE_BITSIZE (GET_MODE (tmp)); } /* Write out the data in whatever chunks reading the source allowed. */ - if (dst_align >= 8) + if (dst_align >= 64) { while (i < nregs && GET_MODE (data_regs[i]) == DImode) { - emit_move_insn (change_address (orig_dst, DImode, - plus_constant (XEXP (orig_dst, 0), - ofs)), + emit_move_insn (adjust_address (orig_dst, DImode, ofs), data_regs[i]); ofs += 8; i++; } } - if (dst_align >= 4) + + if (dst_align >= 32) { /* If the source has remaining DImode regs, write them out in two pieces. */ @@ -2179,13 +4394,9 @@ alpha_expand_block_move (operands) tmp = expand_binop (DImode, lshr_optab, data_regs[i], GEN_INT (32), NULL_RTX, 1, OPTAB_WIDEN); - emit_move_insn (change_address (orig_dst, SImode, - plus_constant (XEXP (orig_dst, 0), - ofs)), + emit_move_insn (adjust_address (orig_dst, SImode, ofs), gen_lowpart (SImode, data_regs[i])); - emit_move_insn (change_address (orig_dst, SImode, - plus_constant (XEXP (orig_dst, 0), - ofs+4)), + emit_move_insn (adjust_address (orig_dst, SImode, ofs + 4), gen_lowpart (SImode, tmp)); ofs += 8; i++; @@ -2193,26 +4404,26 @@ alpha_expand_block_move (operands) while (i < nregs && GET_MODE (data_regs[i]) == SImode) { - emit_move_insn (change_address(orig_dst, SImode, - plus_constant (XEXP (orig_dst, 0), - ofs)), + emit_move_insn (adjust_address (orig_dst, SImode, ofs), data_regs[i]); ofs += 4; i++; } } + if (i < nregs && GET_MODE (data_regs[i]) == DImode) { /* Write out a remaining block of words using unaligned methods. */ - for (words = 1; i+words < nregs ; ++words) - if (GET_MODE (data_regs[i+words]) != DImode) + for (words = 1; i + words < nregs; words++) + if (GET_MODE (data_regs[i + words]) != DImode) break; if (words == 1) alpha_expand_unaligned_store (orig_dst, data_regs[i], 8, ofs); else - alpha_expand_unaligned_store_words (data_regs+i, orig_dst, words, ofs); + alpha_expand_unaligned_store_words (data_regs + i, orig_dst, + words, ofs); i += words; ofs += words * 8; @@ -2228,13 +4439,10 @@ alpha_expand_block_move (operands) i++; } - if (dst_align >= 2) + if (dst_align >= 16) while (i < nregs && GET_MODE (data_regs[i]) == HImode) { - emit_move_insn (change_address (orig_dst, HImode, - plus_constant (XEXP (orig_dst, 0), - ofs)), - data_regs[i]); + emit_move_insn (adjust_address (orig_dst, HImode, ofs), data_regs[i]); i++; ofs += 2; } @@ -2245,19 +4453,18 @@ alpha_expand_block_move (operands) i++; ofs += 2; } + while (i < nregs && GET_MODE (data_regs[i]) == QImode) { - emit_move_insn (change_address (orig_dst, QImode, - plus_constant (XEXP (orig_dst, 0), - ofs)), - data_regs[i]); + emit_move_insn (adjust_address (orig_dst, QImode, ofs), data_regs[i]); i++; ofs += 1; } + dst_done: if (i != nregs) - abort(); + abort (); return 1; } @@ -2268,25 +4475,23 @@ alpha_expand_block_clear (operands) { rtx bytes_rtx = operands[1]; rtx align_rtx = operands[2]; - HOST_WIDE_INT bytes = INTVAL (bytes_rtx); - HOST_WIDE_INT align = INTVAL (align_rtx); - rtx orig_dst = operands[0]; + HOST_WIDE_INT orig_bytes = INTVAL (bytes_rtx); + HOST_WIDE_INT bytes = orig_bytes; + HOST_WIDE_INT align = INTVAL (align_rtx) * BITS_PER_UNIT; + HOST_WIDE_INT alignofs = 0; + rtx orig_dst = operands[0]; rtx tmp; - HOST_WIDE_INT i, words, ofs = 0; + int i, words, ofs = 0; - if (bytes <= 0) + if (orig_bytes <= 0) return 1; - if (bytes > MAX_MOVE_WORDS*8) + if (orig_bytes > MAX_MOVE_WORDS * UNITS_PER_WORD) return 0; /* Look for stricter alignment. */ - tmp = XEXP (orig_dst, 0); if (GET_CODE (tmp) == REG) - { - if (REGNO_POINTER_ALIGN (REGNO (tmp)) > align) - align = REGNO_POINTER_ALIGN (REGNO (tmp)); - } + align = MAX (align, REGNO_POINTER_ALIGN (REGNO (tmp))); else if (GET_CODE (tmp) == PLUS && GET_CODE (XEXP (tmp, 0)) == REG && GET_CODE (XEXP (tmp, 1)) == CONST_INT) @@ -2296,12 +4501,12 @@ alpha_expand_block_clear (operands) if (a > align) { - if (a >= 8 && c % 8 == 0) - align = 8; - else if (a >= 4 && c % 4 == 0) - align = 4; - else if (a >= 2 && c % 2 == 0) - align = 2; + if (a >= 64) + align = a, alignofs = 8 - c % 8; + else if (a >= 32) + align = a, alignofs = 4 - c % 4; + else if (a >= 16) + align = a, alignofs = 2 - c % 2; } } else if (GET_CODE (tmp) == ADDRESSOF) @@ -2316,44 +4521,149 @@ alpha_expand_block_clear (operands) } /* No appropriate mode; fall back on memory. */ - orig_dst = change_address (orig_dst, GET_MODE (orig_dst), - copy_addr_to_reg (tmp)); - align = GET_MODE_SIZE (GET_MODE (XEXP (tmp, 0))); + orig_dst = replace_equiv_address (orig_dst, copy_addr_to_reg (tmp)); + align = GET_MODE_BITSIZE (GET_MODE (XEXP (tmp, 0))); } - /* Handle a block of contiguous words first. */ + /* Handle an unaligned prefix first. */ - if (align >= 8 && bytes >= 8) + if (alignofs > 0) + { +#if HOST_BITS_PER_WIDE_INT >= 64 + /* Given that alignofs is bounded by align, the only time BWX could + generate three stores is for a 7 byte fill. Prefer two individual + stores over a load/mask/store sequence. */ + if ((!TARGET_BWX || alignofs == 7) + && align >= 32 + && !(alignofs == 4 && bytes >= 4)) + { + enum machine_mode mode = (align >= 64 ? DImode : SImode); + int inv_alignofs = (align >= 64 ? 8 : 4) - alignofs; + rtx mem, tmp; + HOST_WIDE_INT mask; + + mem = adjust_address (orig_dst, mode, ofs - inv_alignofs); + set_mem_alias_set (mem, 0); + + mask = ~(~(HOST_WIDE_INT)0 << (inv_alignofs * 8)); + if (bytes < alignofs) + { + mask |= ~(HOST_WIDE_INT)0 << ((inv_alignofs + bytes) * 8); + ofs += bytes; + bytes = 0; + } + else + { + bytes -= alignofs; + ofs += alignofs; + } + alignofs = 0; + + tmp = expand_binop (mode, and_optab, mem, GEN_INT (mask), + NULL_RTX, 1, OPTAB_WIDEN); + + emit_move_insn (mem, tmp); + } +#endif + + if (TARGET_BWX && (alignofs & 1) && bytes >= 1) + { + emit_move_insn (adjust_address (orig_dst, QImode, ofs), const0_rtx); + bytes -= 1; + ofs += 1; + alignofs -= 1; + } + if (TARGET_BWX && align >= 16 && (alignofs & 3) == 2 && bytes >= 2) + { + emit_move_insn (adjust_address (orig_dst, HImode, ofs), const0_rtx); + bytes -= 2; + ofs += 2; + alignofs -= 2; + } + if (alignofs == 4 && bytes >= 4) + { + emit_move_insn (adjust_address (orig_dst, SImode, ofs), const0_rtx); + bytes -= 4; + ofs += 4; + alignofs = 0; + } + + /* If we've not used the extra lead alignment information by now, + we won't be able to. Downgrade align to match what's left over. */ + if (alignofs > 0) + { + alignofs = alignofs & -alignofs; + align = MIN (align, alignofs * BITS_PER_UNIT); + } + } + + /* Handle a block of contiguous long-words. */ + + if (align >= 64 && bytes >= 8) { words = bytes / 8; for (i = 0; i < words; ++i) - { - emit_move_insn (change_address(orig_dst, DImode, - plus_constant (XEXP (orig_dst, 0), - ofs + i*8)), - const0_rtx); - } + emit_move_insn (adjust_address (orig_dst, DImode, ofs + i * 8), + const0_rtx); bytes -= words * 8; ofs += words * 8; } - if (align >= 4 && bytes >= 4) + + /* If the block is large and appropriately aligned, emit a single + store followed by a sequence of stq_u insns. */ + + if (align >= 32 && bytes > 16) + { + rtx orig_dsta; + + emit_move_insn (adjust_address (orig_dst, SImode, ofs), const0_rtx); + bytes -= 4; + ofs += 4; + + orig_dsta = XEXP (orig_dst, 0); + if (GET_CODE (orig_dsta) == LO_SUM) + orig_dsta = force_reg (Pmode, orig_dsta); + + words = bytes / 8; + for (i = 0; i < words; ++i) + { + rtx mem + = change_address (orig_dst, DImode, + gen_rtx_AND (DImode, + plus_constant (orig_dsta, ofs + i*8), + GEN_INT (-8))); + set_mem_alias_set (mem, 0); + emit_move_insn (mem, const0_rtx); + } + + /* Depending on the alignment, the first stq_u may have overlapped + with the initial stl, which means that the last stq_u didn't + write as much as it would appear. Leave those questionable bytes + unaccounted for. */ + bytes -= words * 8 - 4; + ofs += words * 8 - 4; + } + + /* Handle a smaller block of aligned words. */ + + if ((align >= 64 && bytes == 4) + || (align == 32 && bytes >= 4)) { words = bytes / 4; for (i = 0; i < words; ++i) - { - emit_move_insn (change_address (orig_dst, SImode, - plus_constant (XEXP (orig_dst, 0), - ofs + i*4)), - const0_rtx); - } + emit_move_insn (adjust_address (orig_dst, SImode, ofs + i * 4), + const0_rtx); bytes -= words * 4; ofs += words * 4; } - if (bytes >= 16) + + /* An unaligned block uses stq_u stores for as many as possible. */ + + if (bytes >= 8) { words = bytes / 8; @@ -2363,59 +4673,95 @@ alpha_expand_block_clear (operands) ofs += words * 8; } - /* Next clean up any trailing pieces. We know from the contiguous - block move that there are no aligned SImode or DImode hunks left. */ + /* Next clean up any trailing pieces. */ - if (!TARGET_BWX && bytes >= 8) +#if HOST_BITS_PER_WIDE_INT >= 64 + /* Count the number of bits in BYTES for which aligned stores could + be emitted. */ + words = 0; + for (i = (TARGET_BWX ? 1 : 4); i * BITS_PER_UNIT <= align ; i <<= 1) + if (bytes & i) + words += 1; + + /* If we have appropriate alignment (and it wouldn't take too many + instructions otherwise), mask out the bytes we need. */ + if (TARGET_BWX ? words > 2 : bytes > 0) { - alpha_expand_unaligned_store (orig_dst, const0_rtx, 8, ofs); - bytes -= 8; - ofs += 8; + if (align >= 64) + { + rtx mem, tmp; + HOST_WIDE_INT mask; + + mem = adjust_address (orig_dst, DImode, ofs); + set_mem_alias_set (mem, 0); + + mask = ~(HOST_WIDE_INT)0 << (bytes * 8); + + tmp = expand_binop (DImode, and_optab, mem, GEN_INT (mask), + NULL_RTX, 1, OPTAB_WIDEN); + + emit_move_insn (mem, tmp); + return 1; + } + else if (align >= 32 && bytes < 4) + { + rtx mem, tmp; + HOST_WIDE_INT mask; + + mem = adjust_address (orig_dst, SImode, ofs); + set_mem_alias_set (mem, 0); + + mask = ~(HOST_WIDE_INT)0 << (bytes * 8); + + tmp = expand_binop (SImode, and_optab, mem, GEN_INT (mask), + NULL_RTX, 1, OPTAB_WIDEN); + + emit_move_insn (mem, tmp); + return 1; + } } +#endif + if (!TARGET_BWX && bytes >= 4) { alpha_expand_unaligned_store (orig_dst, const0_rtx, 4, ofs); bytes -= 4; ofs += 4; } + if (bytes >= 2) { - if (align >= 2) + if (align >= 16) { do { - emit_move_insn (change_address (orig_dst, HImode, - plus_constant (XEXP (orig_dst, 0), - ofs)), + emit_move_insn (adjust_address (orig_dst, HImode, ofs), const0_rtx); bytes -= 2; ofs += 2; } while (bytes >= 2); } - else if (!TARGET_BWX) + else if (! TARGET_BWX) { alpha_expand_unaligned_store (orig_dst, const0_rtx, 2, ofs); bytes -= 2; ofs += 2; } } + while (bytes > 0) { - emit_move_insn (change_address (orig_dst, QImode, - plus_constant (XEXP (orig_dst, 0), - ofs)), - const0_rtx); + emit_move_insn (adjust_address (orig_dst, QImode, ofs), const0_rtx); bytes -= 1; ofs += 1; } return 1; } - /* 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. */ -int +static int alpha_adjust_cost (insn, link, dep_insn, cost) rtx insn; rtx link; @@ -2556,52 +4902,71 @@ alpha_adjust_cost (insn, link, dep_insn, cost) break; } - /* Otherwise, return the default cost. */ + /* Otherwise, return the default cost. */ return cost; } - -/* Functions to save and restore alpha_return_addr_rtx. */ -struct machine_function +/* Function to initialize the issue rate used by the scheduler. */ +static int +alpha_issue_rate () { - rtx ra_rtx; -}; + return (alpha_cpu == PROCESSOR_EV4 ? 2 : 4); +} +static int +alpha_variable_issue (dump, verbose, insn, cim) + FILE *dump ATTRIBUTE_UNUSED; + int verbose ATTRIBUTE_UNUSED; + rtx insn; + int cim; +{ + if (recog_memoized (insn) < 0 || get_attr_type (insn) == TYPE_MULTI) + return 0; + + return cim - 1; +} + + +/* Register global variables and machine-specific functions with the + garbage collector. */ + +#if TARGET_ABI_UNICOSMK static void -alpha_save_machine_status (p) +alpha_init_machine_status (p) struct function *p; { - struct machine_function *machine = - (struct machine_function *) xmalloc (sizeof (struct machine_function)); + p->machine = + (struct machine_function *) xcalloc (1, sizeof (struct machine_function)); - p->machine = machine; - machine->ra_rtx = alpha_return_addr_rtx; + p->machine->first_ciw = NULL_RTX; + p->machine->last_ciw = NULL_RTX; + p->machine->ciw_count = 0; + p->machine->addr_list = NULL_RTX; } static void -alpha_restore_machine_status (p) +alpha_mark_machine_status (p) struct function *p; { struct machine_function *machine = p->machine; - alpha_return_addr_rtx = machine->ra_rtx; - - free (machine); - p->machine = (struct machine_function *)0; + if (machine) + { + ggc_mark_rtx (machine->first_ciw); + ggc_mark_rtx (machine->addr_list); + } } -/* Do anything needed before RTL is emitted for each function. */ - -void -alpha_init_expanders () +static void +alpha_free_machine_status (p) + struct function *p; { - alpha_return_addr_rtx = NULL_RTX; - alpha_eh_epilogue_sp_ofs = NULL_RTX; - - /* Arrange to save and restore machine status around nested functions. */ - save_machine_status = alpha_save_machine_status; - restore_machine_status = alpha_restore_machine_status; + free (p->machine); + p->machine = NULL; } +#endif /* TARGET_ABI_UNICOSMK */ + +/* Functions to save and restore alpha_return_addr_rtx. */ /* Start the ball rolling with RETURN_ADDR_RTX. */ @@ -2610,25 +4975,19 @@ alpha_return_addr (count, frame) int count; rtx frame ATTRIBUTE_UNUSED; { - rtx init; - if (count != 0) return const0_rtx; - if (alpha_return_addr_rtx) - return alpha_return_addr_rtx; + return get_hard_reg_initial_val (Pmode, REG_RA); +} - /* No rtx yet. Invent one, and initialize it from $26 in the prologue. */ - alpha_return_addr_rtx = gen_reg_rtx (Pmode); - init = gen_rtx_SET (VOIDmode, alpha_return_addr_rtx, - gen_rtx_REG (Pmode, REG_RA)); +/* Return or create a pseudo containing the gp value for the current + function. Needed only if TARGET_LD_BUGGY_LDGP. */ - /* Emit the insn to the prologue with the other argument copies. */ - push_topmost_sequence (); - emit_insn_after (init, get_insns ()); - pop_topmost_sequence (); - - return alpha_return_addr_rtx; +rtx +alpha_gp_save_rtx () +{ + return get_hard_reg_initial_val (DImode, 29); } static int @@ -2640,7 +4999,7 @@ alpha_ra_ever_killed () if (current_function_is_thunk) return 0; #endif - if (!alpha_return_addr_rtx) + if (!has_hard_reg_initial_val (Pmode, REG_RA)) return regs_ever_live[REG_RA]; push_topmost_sequence (); @@ -2651,125 +5010,170 @@ alpha_ra_ever_killed () } +/* Return the trap mode suffix applicable to the current + instruction, or NULL. */ + +static const char * +get_trap_mode_suffix () +{ + enum attr_trap_suffix s = get_attr_trap_suffix (current_output_insn); + + switch (s) + { + case TRAP_SUFFIX_NONE: + return NULL; + + case TRAP_SUFFIX_SU: + if (alpha_fptm >= ALPHA_FPTM_SU) + return "su"; + return NULL; + + case TRAP_SUFFIX_SUI: + if (alpha_fptm >= ALPHA_FPTM_SUI) + return "sui"; + return NULL; + + case TRAP_SUFFIX_V_SV: + switch (alpha_fptm) + { + case ALPHA_FPTM_N: + return NULL; + case ALPHA_FPTM_U: + return "v"; + case ALPHA_FPTM_SU: + case ALPHA_FPTM_SUI: + return "sv"; + } + break; + + case TRAP_SUFFIX_V_SV_SVI: + switch (alpha_fptm) + { + case ALPHA_FPTM_N: + return NULL; + case ALPHA_FPTM_U: + return "v"; + case ALPHA_FPTM_SU: + return "sv"; + case ALPHA_FPTM_SUI: + return "svi"; + } + break; + + case TRAP_SUFFIX_U_SU_SUI: + switch (alpha_fptm) + { + case ALPHA_FPTM_N: + return NULL; + case ALPHA_FPTM_U: + return "u"; + case ALPHA_FPTM_SU: + return "su"; + case ALPHA_FPTM_SUI: + return "sui"; + } + break; + } + abort (); +} + +/* Return the rounding mode suffix applicable to the current + instruction, or NULL. */ + +static const char * +get_round_mode_suffix () +{ + enum attr_round_suffix s = get_attr_round_suffix (current_output_insn); + + switch (s) + { + case ROUND_SUFFIX_NONE: + return NULL; + case ROUND_SUFFIX_NORMAL: + switch (alpha_fprm) + { + case ALPHA_FPRM_NORM: + return NULL; + case ALPHA_FPRM_MINF: + return "m"; + case ALPHA_FPRM_CHOP: + return "c"; + case ALPHA_FPRM_DYN: + return "d"; + } + break; + + case ROUND_SUFFIX_C: + return "c"; + } + abort (); +} + /* Print an operand. Recognize special options, documented below. */ void print_operand (file, x, code) FILE *file; rtx x; - char code; + int code; { int i; switch (code) { - case '&': - /* Generates fp-rounding mode suffix: nothing for normal, 'c' for - chopped, 'm' for minus-infinity, and 'd' for dynamic rounding - mode. alpha_fprm controls which suffix is generated. */ - switch (alpha_fprm) - { - case ALPHA_FPRM_NORM: - break; - case ALPHA_FPRM_MINF: - fputc ('m', file); - break; - case ALPHA_FPRM_CHOP: - fputc ('c', file); - break; - case ALPHA_FPRM_DYN: - fputc ('d', file); - break; - } + case '~': + /* Print the assembler name of the current function. */ + assemble_name (file, alpha_fnname); break; - case '\'': - /* Generates trap-mode suffix for instructions that accept the su - suffix only (cmpt et al). */ - if (alpha_tp == ALPHA_TP_INSN) - fputs ("su", file); - break; + case '/': + { + const char *trap = get_trap_mode_suffix (); + const char *round = get_round_mode_suffix (); - case '`': - /* Generates trap-mode suffix for instructions that accept the - v and sv suffix. The only instruction that needs this is cvtql. */ - switch (alpha_fptm) - { - case ALPHA_FPTM_N: - break; - case ALPHA_FPTM_U: - fputs ("v", file); - break; - case ALPHA_FPTM_SU: - case ALPHA_FPTM_SUI: - fputs ("sv", file); - break; - } - break; - - case '(': - /* Generates trap-mode suffix for instructions that accept the - v, sv, and svi suffix. The only instruction that needs this - is cvttq. */ - switch (alpha_fptm) - { - case ALPHA_FPTM_N: - break; - case ALPHA_FPTM_U: - fputs ("v", file); - break; - case ALPHA_FPTM_SU: - fputs ("sv", file); - break; - case ALPHA_FPTM_SUI: - fputs ("svi", file); - break; - } - break; - - case ')': - /* Generates trap-mode suffix for instructions that accept the u, su, - and sui suffix. This is the bulk of the IEEE floating point - instructions (addt et al). */ - switch (alpha_fptm) - { - case ALPHA_FPTM_N: - break; - case ALPHA_FPTM_U: - fputc ('u', file); - break; - case ALPHA_FPTM_SU: - fputs ("su", file); - break; - case ALPHA_FPTM_SUI: - fputs ("sui", file); - break; - } - break; - - case '+': - /* Generates trap-mode suffix for instructions that accept the sui - suffix (cvtqt and cvtqs). */ - switch (alpha_fptm) - { - case ALPHA_FPTM_N: - case ALPHA_FPTM_U: - case ALPHA_FPTM_SU: /* cvtqt/cvtqs can't cause underflow */ - break; - case ALPHA_FPTM_SUI: - fputs ("sui", file); - break; - } - break; + if (trap || round) + fprintf (file, (TARGET_AS_SLASH_BEFORE_SUFFIX ? "/%s%s" : "%s%s"), + (trap ? trap : ""), (round ? round : "")); + break; + } case ',': /* Generates single precision instruction suffix. */ - fprintf (file, "%c", (TARGET_FLOAT_VAX ? 'f' : 's')); + fputc ((TARGET_FLOAT_VAX ? 'f' : 's'), file); break; case '-': /* Generates double precision instruction suffix. */ - fprintf (file, "%c", (TARGET_FLOAT_VAX ? 'g' : 't')); + fputc ((TARGET_FLOAT_VAX ? 'g' : 't'), file); + break; + + case '#': + if (alpha_this_literal_sequence_number == 0) + alpha_this_literal_sequence_number = alpha_next_sequence_number++; + fprintf (file, "%d", alpha_this_literal_sequence_number); + break; + + case '*': + if (alpha_this_gpdisp_sequence_number == 0) + alpha_this_gpdisp_sequence_number = alpha_next_sequence_number++; + fprintf (file, "%d", alpha_this_gpdisp_sequence_number); + break; + + case 'H': + if (GET_CODE (x) == HIGH) + output_addr_const (file, XEXP (x, 0)); + else + output_operand_lossage ("invalid %%H value"); + break; + + case 'J': + if (GET_CODE (x) == CONST_INT) + { + if (INTVAL (x) != 0) + fprintf (file, "\t\t!lituse_jsr!%d", (int) INTVAL (x)); + } + else + output_operand_lossage ("invalid %%J value"); break; case 'r': @@ -2780,7 +5184,6 @@ print_operand (file, x, code) fprintf (file, "$31"); else output_operand_lossage ("invalid %%r value"); - break; case 'R': @@ -2791,7 +5194,6 @@ print_operand (file, x, code) fprintf (file, "$f31"); else output_operand_lossage ("invalid %%R value"); - break; case 'N': @@ -2907,13 +5309,20 @@ print_operand (file, x, code) break; case 's': - /* Write the constant value divided by 8. */ + /* Write the constant value divided by 8 for little-endian mode or + (56 - value) / 8 for big-endian mode. */ + if (GET_CODE (x) != CONST_INT - && (unsigned HOST_WIDE_INT) INTVAL (x) >= 64 - && (INTVAL (x) & 7) != 8) + || (unsigned HOST_WIDE_INT) INTVAL (x) >= (WORDS_BIG_ENDIAN + ? 56 + : 64) + || (INTVAL (x) & 7) != 0) output_operand_lossage ("invalid %%s value"); - fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) / 8); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + WORDS_BIG_ENDIAN + ? (56 - INTVAL (x)) / 8 + : INTVAL (x) / 8); break; case 'S': @@ -2927,6 +5336,18 @@ print_operand (file, x, code) fprintf (file, HOST_WIDE_INT_PRINT_DEC, (64 - INTVAL (x)) / 8); break; + case 't': + { + /* On Unicos/Mk systems: use a DEX expression if the symbol + clashes with a register name. */ + int dex = unicosmk_need_dex (x); + if (dex) + fprintf (file, "DEX(%d)", dex); + else + output_addr_const (file, x); + } + break; + case 'C': case 'D': case 'c': case 'd': /* Write out comparison name. */ { @@ -2935,7 +5356,7 @@ print_operand (file, x, code) if (GET_RTX_CLASS (c) != '<') output_operand_lossage ("invalid %%C value"); - if (code == 'D') + else if (code == 'D') c = reverse_condition (c); else if (code == 'c') c = swap_condition (c); @@ -2946,6 +5367,8 @@ print_operand (file, x, code) fprintf (file, "ule"); else if (c == LTU) fprintf (file, "ult"); + else if (c == UNORDERED) + fprintf (file, "un"); else fprintf (file, "%s", GET_RTX_NAME (c)); } @@ -3010,11 +5433,35 @@ print_operand_address (file, addr) offset = INTVAL (XEXP (addr, 1)); addr = XEXP (addr, 0); } + + if (GET_CODE (addr) == LO_SUM) + { + output_addr_const (file, XEXP (addr, 1)); + if (offset) + { + fputc ('+', file); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset); + } + + addr = XEXP (addr, 0); + if (GET_CODE (addr) == REG) + basereg = REGNO (addr); + else if (GET_CODE (addr) == SUBREG + && GET_CODE (SUBREG_REG (addr)) == REG) + basereg = subreg_regno (addr); + else + abort (); + + fprintf (file, "($%d)\t\t!%s", basereg, + (basereg == 29 ? "gprel" : "gprellow")); + return; + } + if (GET_CODE (addr) == REG) basereg = REGNO (addr); else if (GET_CODE (addr) == SUBREG && GET_CODE (SUBREG_REG (addr)) == REG) - basereg = REGNO (SUBREG_REG (addr)) + SUBREG_WORD (addr); + basereg = subreg_regno (addr); else if (GET_CODE (addr) == CONST_INT) offset = INTVAL (addr); else @@ -3044,7 +5491,7 @@ alpha_initialize_trampoline (tramp, fnaddr, cxt, fnofs, cxtofs, jmpofs) { rtx temp, temp1, addr; /* VMS really uses DImode pointers in memory at this point. */ - enum machine_mode mode = TARGET_OPEN_VMS ? Pmode : ptr_mode; + enum machine_mode mode = TARGET_ABI_OPEN_VMS ? Pmode : ptr_mode; #ifdef POINTERS_EXTEND_UNSIGNED fnaddr = convert_memory_address (mode, fnaddr); @@ -3053,12 +5500,12 @@ alpha_initialize_trampoline (tramp, fnaddr, cxt, fnofs, cxtofs, jmpofs) /* Store function address and CXT. */ addr = memory_address (mode, plus_constant (tramp, fnofs)); - emit_move_insn (gen_rtx (MEM, mode, addr), fnaddr); + emit_move_insn (gen_rtx_MEM (mode, addr), fnaddr); addr = memory_address (mode, plus_constant (tramp, cxtofs)); - emit_move_insn (gen_rtx (MEM, mode, addr), cxt); + emit_move_insn (gen_rtx_MEM (mode, addr), cxt); /* This has been disabled since the hint only has a 32k range, and in - no existing OS is the stack within 32k of the text segment. */ + no existing OS is the stack within 32k of the text segment. */ if (0 && jmpofs >= 0) { /* Compute hint value. */ @@ -3071,15 +5518,15 @@ alpha_initialize_trampoline (tramp, fnaddr, cxt, fnofs, cxtofs, jmpofs) /* Merge in the hint. */ addr = memory_address (SImode, plus_constant (tramp, jmpofs)); - temp1 = force_reg (SImode, gen_rtx (MEM, SImode, addr)); + temp1 = force_reg (SImode, gen_rtx_MEM (SImode, addr)); temp1 = expand_and (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); + emit_move_insn (gen_rtx_MEM (SImode, addr), temp1); } #ifdef TRANSFER_FROM_TRAMPOLINE - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"), + emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"), 0, VOIDmode, 1, addr, Pmode); #endif @@ -3087,102 +5534,297 @@ alpha_initialize_trampoline (tramp, fnaddr, cxt, fnofs, cxtofs, jmpofs) emit_insn (gen_imb ()); } -/* Do what is necessary for `va_start'. The argument is ignored; - We look at the current function to determine if stdarg or varargs - is used and fill in an initial va_list. A pointer to this constructor - is returned. */ +/* Determine where to put an argument to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. -struct rtx_def * -alpha_builtin_saveregs (arglist) - tree arglist ATTRIBUTE_UNUSED; + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). + + On Alpha the first 6 words of args are normally in registers + and the rest are pushed. */ + +rtx +function_arg (cum, mode, type, named) + CUMULATIVE_ARGS cum; + enum machine_mode mode; + tree type; + int named ATTRIBUTE_UNUSED; { - rtx block, addr, dest, argsize; - tree fntype = TREE_TYPE (current_function_decl); - int stdarg = (TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - != void_type_node)); + int basereg; + int num_args; - /* Compute the current position into the args, taking into account - both registers and memory. Both of these are already included in - NUM_ARGS. */ - - argsize = GEN_INT (NUM_ARGS * UNITS_PER_WORD); - - /* For Unix, SETUP_INCOMING_VARARGS moves the starting address base up by 48, - storing fp arg registers in the first 48 bytes, and the integer arg - registers in the next 48 bytes. This is only done, however, if any - integer registers need to be stored. - - 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. */ - - if (TARGET_OPEN_VMS) - addr = plus_constant (virtual_incoming_args_rtx, - NUM_ARGS <= 5 + stdarg - ? UNITS_PER_WORD : - 6 * UNITS_PER_WORD); + /* Set up defaults for FP operands passed in FP registers, and + integral operands passed in integer registers. */ + if (TARGET_FPREGS + && (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT + || GET_MODE_CLASS (mode) == MODE_FLOAT)) + basereg = 32 + 16; else - addr = (NUM_ARGS <= 5 + stdarg - ? plus_constant (virtual_incoming_args_rtx, - 6 * UNITS_PER_WORD) - : plus_constant (virtual_incoming_args_rtx, - - (6 * UNITS_PER_WORD))); + basereg = 16; - /* For VMS, we include the argsize, while on Unix, it's handled as - a separate field. */ - if (TARGET_OPEN_VMS) - addr = plus_constant (addr, INTVAL (argsize)); + /* ??? Irritatingly, the definition of CUMULATIVE_ARGS is different for + the three platforms, so we can't avoid conditional compilation. */ +#if TARGET_ABI_OPEN_VMS + { + if (mode == VOIDmode) + return alpha_arg_info_reg_val (cum); - addr = force_operand (addr, NULL_RTX); + num_args = cum.num_args; + if (num_args >= 6 || MUST_PASS_IN_STACK (mode, type)) + return NULL_RTX; + } +#else +#if TARGET_ABI_UNICOSMK + { + int size; -#ifdef POINTERS_EXTEND_UNSIGNED - addr = convert_memory_address (ptr_mode, addr); + /* If this is the last argument, generate the call info word (CIW). */ + /* ??? We don't include the caller's line number in the CIW because + I don't know how to determine it if debug infos are turned off. */ + if (mode == VOIDmode) + { + int i; + HOST_WIDE_INT lo; + HOST_WIDE_INT hi; + rtx ciw; + + lo = 0; + + for (i = 0; i < cum.num_reg_words && i < 5; i++) + if (cum.reg_args_type[i]) + lo |= (1 << (7 - i)); + + if (cum.num_reg_words == 6 && cum.reg_args_type[5]) + lo |= 7; + else + lo |= cum.num_reg_words; + +#if HOST_BITS_PER_WIDE_INT == 32 + hi = (cum.num_args << 20) | cum.num_arg_words; +#else + lo = lo | ((HOST_WIDE_INT) cum.num_args << 52) + | ((HOST_WIDE_INT) cum.num_arg_words << 32); + hi = 0; #endif + ciw = immed_double_const (lo, hi, DImode); - if (TARGET_OPEN_VMS) - return addr; + return gen_rtx_UNSPEC (DImode, gen_rtvec (1, ciw), + UNSPEC_UMK_LOAD_CIW); + } + + size = ALPHA_ARG_SIZE (mode, type, named); + num_args = cum.num_reg_words; + if (MUST_PASS_IN_STACK (mode, type) + || cum.num_reg_words + size > 6 || cum.force_stack) + return NULL_RTX; + else if (type && TYPE_MODE (type) == BLKmode) + { + rtx reg1, reg2; + + reg1 = gen_rtx_REG (DImode, num_args + 16); + reg1 = gen_rtx_EXPR_LIST (DImode, reg1, const0_rtx); + + /* The argument fits in two registers. Note that we still need to + reserve a register for empty structures. */ + if (size == 0) + return NULL_RTX; + else if (size == 1) + return gen_rtx_PARALLEL (mode, gen_rtvec (1, reg1)); + else + { + reg2 = gen_rtx_REG (DImode, num_args + 17); + reg2 = gen_rtx_EXPR_LIST (DImode, reg2, GEN_INT (8)); + return gen_rtx_PARALLEL (mode, gen_rtvec (2, reg1, reg2)); + } + } + } +#else + { + if (cum >= 6) + return NULL_RTX; + num_args = cum; + + /* VOID is passed as a special flag for "last argument". */ + if (type == void_type_node) + basereg = 16; + else if (MUST_PASS_IN_STACK (mode, type)) + return NULL_RTX; + else if (FUNCTION_ARG_PASS_BY_REFERENCE (cum, mode, type, named)) + basereg = 16; + } +#endif /* TARGET_ABI_UNICOSMK */ +#endif /* TARGET_ABI_OPEN_VMS */ + + return gen_rtx_REG (mode, num_args + basereg); +} + +tree +alpha_build_va_list () +{ + tree base, ofs, record, type_decl; + + if (TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK) + return ptr_type_node; + + record = make_lang_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; + + /* C++? SET_IS_AGGR_TYPE (record, 1); */ + + ofs = build_decl (FIELD_DECL, get_identifier ("__offset"), + integer_type_node); + DECL_FIELD_CONTEXT (ofs) = record; + + base = build_decl (FIELD_DECL, get_identifier ("__base"), + ptr_type_node); + DECL_FIELD_CONTEXT (base) = record; + TREE_CHAIN (base) = ofs; + + TYPE_FIELDS (record) = base; + layout_type (record); + + return record; +} + +void +alpha_va_start (stdarg_p, valist, nextarg) + int stdarg_p; + tree valist; + rtx nextarg ATTRIBUTE_UNUSED; +{ + HOST_WIDE_INT offset; + tree t, offset_field, base_field; + + if (TREE_CODE (TREE_TYPE (valist)) == ERROR_MARK) + return; + + if (TARGET_ABI_UNICOSMK) + std_expand_builtin_va_start (stdarg_p, 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 + integer arg registers in the next 48 bytes. This is only done, + however, if any integer registers need to be stored. + + 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. */ + + if (NUM_ARGS <= 5 + stdarg_p) + offset = TARGET_ABI_OPEN_VMS ? UNITS_PER_WORD : 6 * UNITS_PER_WORD; + else + offset = -6 * UNITS_PER_WORD; + + if (TARGET_ABI_OPEN_VMS) + { + nextarg = plus_constant (nextarg, offset); + nextarg = plus_constant (nextarg, NUM_ARGS * UNITS_PER_WORD); + t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, + make_tree (ptr_type_node, nextarg)); + TREE_SIDE_EFFECTS (t) = 1; + + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } else { - /* Allocate the va_list constructor */ - block = assign_stack_local (BLKmode, 2 * UNITS_PER_WORD, BITS_PER_WORD); - RTX_UNCHANGING_P (block) = 1; - RTX_UNCHANGING_P (XEXP (block, 0)) = 1; + base_field = TYPE_FIELDS (TREE_TYPE (valist)); + offset_field = TREE_CHAIN (base_field); - /* Store the address of the first integer register in the __base - member. */ + base_field = build (COMPONENT_REF, TREE_TYPE (base_field), + valist, base_field); + offset_field = build (COMPONENT_REF, TREE_TYPE (offset_field), + valist, offset_field); - dest = change_address (block, ptr_mode, XEXP (block, 0)); - emit_move_insn (dest, addr); + t = make_tree (ptr_type_node, virtual_incoming_args_rtx); + t = build (PLUS_EXPR, ptr_type_node, t, build_int_2 (offset, 0)); + t = build (MODIFY_EXPR, TREE_TYPE (base_field), base_field, t); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); - if (current_function_check_memory_usage) - emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, - dest, ptr_mode, - GEN_INT (GET_MODE_SIZE (ptr_mode)), - TYPE_MODE (sizetype), - GEN_INT (MEMORY_USE_RW), - TYPE_MODE (integer_type_node)); - - /* Store the argsize as the __va_offset member. */ - dest = change_address (block, TYPE_MODE (integer_type_node), - plus_constant (XEXP (block, 0), - POINTER_SIZE/BITS_PER_UNIT)); - emit_move_insn (dest, argsize); - - if (current_function_check_memory_usage) - emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, - dest, ptr_mode, - GEN_INT (GET_MODE_SIZE - (TYPE_MODE (integer_type_node))), - TYPE_MODE (sizetype), - GEN_INT (MEMORY_USE_RW), - TYPE_MODE (integer_type_node)); - - /* Return the address of the va_list constructor, but don't put it in a - register. Doing so would fail when not optimizing and produce worse - code when optimizing. */ - return XEXP (block, 0); + t = build_int_2 (NUM_ARGS * UNITS_PER_WORD, 0); + t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field, t); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } } + +rtx +alpha_va_arg (valist, type) + tree valist, type; +{ + HOST_WIDE_INT tsize; + rtx addr; + tree t; + 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; + + base_field = TYPE_FIELDS (TREE_TYPE (valist)); + offset_field = TREE_CHAIN (base_field); + + base_field = build (COMPONENT_REF, TREE_TYPE (base_field), + valist, base_field); + offset_field = build (COMPONENT_REF, TREE_TYPE (offset_field), + valist, offset_field); + + wide_type = make_signed_type (64); + wide_ofs = save_expr (build1 (CONVERT_EXPR, wide_type, offset_field)); + + addend = wide_ofs; + + if (TYPE_MODE (type) == TFmode || TYPE_MODE (type) == TCmode) + { + indirect = 1; + tsize = UNITS_PER_WORD; + } + else if (FLOAT_TYPE_P (type)) + { + tree fpaddend, cond; + + fpaddend = fold (build (PLUS_EXPR, TREE_TYPE (addend), + addend, build_int_2 (-6*8, 0))); + + cond = fold (build (LT_EXPR, integer_type_node, + wide_ofs, build_int_2 (6*8, 0))); + + addend = fold (build (COND_EXPR, TREE_TYPE (addend), cond, + fpaddend, addend)); + } + + addr_tree = build (PLUS_EXPR, TREE_TYPE (base_field), + base_field, addend); + + addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL); + addr = copy_to_reg (addr); + + t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field, + build (PLUS_EXPR, TREE_TYPE (offset_field), + offset_field, build_int_2 (tsize, 0))); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + if (indirect) + { + addr = force_reg (Pmode, addr); + addr = gen_rtx_MEM (Pmode, addr); + } + + return addr; +} /* This page contains routines that are used to determine what the function prologue and epilogue code will do and write them out. */ @@ -3192,10 +5834,10 @@ alpha_builtin_saveregs (arglist) /* These variables are used for communication between the following functions. They indicate various things about the current function being compiled that are used to tell what kind of prologue, epilogue and procedure - descriptior to generate. */ + descriptior to generate. */ /* Nonzero if we need a stack procedure. */ -static int vms_is_stack_procedure; +static int alpha_is_stack_procedure; /* Register number (either FP or SP) that is used to unwind the frame. */ static int vms_unwind_regno; @@ -3217,19 +5859,20 @@ alpha_sa_mask (imaskP, fmaskP) { unsigned long imask = 0; unsigned long fmask = 0; - int i; + unsigned int i; #ifdef ASM_OUTPUT_MI_THUNK if (!current_function_is_thunk) #endif { - if (TARGET_OPEN_VMS && vms_is_stack_procedure) + if (TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure) imask |= (1L << HARD_FRAME_POINTER_REGNUM); /* One for every register we have to save. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (! fixed_regs[i] && ! call_used_regs[i] - && regs_ever_live[i] && i != REG_RA) + && regs_ever_live[i] && i != REG_RA + && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM)) { if (i < 32) imask |= (1L << i); @@ -3237,6 +5880,21 @@ alpha_sa_mask (imaskP, fmaskP) fmask |= (1L << (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 |= 1L << regno; + } + } + + /* If any register spilled, then spill the return address also. */ + /* ??? This is required by the Digital stack unwind specification + and isn't needed if we're doing Dwarf2 unwinding. */ if (imask || fmask || alpha_ra_ever_killed ()) imask |= (1L << REG_RA); } @@ -3248,28 +5906,57 @@ alpha_sa_mask (imaskP, fmaskP) 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; - else -#endif + alpha_sa_mask (&mask[0], &mask[1]); + + if (TARGET_ABI_UNICOSMK) { - /* 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++; + if (mask[0] || mask[1]) + sa_size = 14; + } + else + { + for (j = 0; j < 2; ++j) + for (i = 0; i < 32; ++i) + if ((mask[j] >> i) & 1) + sa_size++; } - if (TARGET_OPEN_VMS) + if (TARGET_ABI_UNICOSMK) + { + /* We might not need to generate a frame if we don't make any calls + (including calls to __T3E_MISMATCH if this is a vararg function), + don't have any local variables which require stack slots, don't + use alloca and have not determined that we need a frame for other + reasons. */ + + alpha_is_stack_procedure = (sa_size + || get_frame_size() != 0 + || current_function_outgoing_args_size + || current_function_varargs + || current_function_stdarg + || current_function_calls_alloca + || frame_pointer_needed); + + /* Always reserve space for saving callee-saved registers if we + need a frame as required by the calling convention. */ + if (alpha_is_stack_procedure) + 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. */ - vms_is_stack_procedure = sa_size != 0 || alpha_ra_ever_killed (); + alpha_is_stack_procedure = ((mask[0] >> REG_RA) & 1); + + /* Don't reserve space for saving RA yet. Do that later after we've + made the final decision on stack procedure vs register procedure. */ + if (alpha_is_stack_procedure) + sa_size--; /* 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 @@ -3277,7 +5964,7 @@ alpha_sa_size () Otherwise, start by assuming we can use FP. */ vms_base_regno = (frame_pointer_needed || current_function_has_nonlocal_label - || vms_is_stack_procedure + || alpha_is_stack_procedure || current_function_outgoing_args_size ? REG_PV : HARD_FRAME_POINTER_REGNUM); @@ -3291,23 +5978,18 @@ alpha_sa_size () vms_save_fp_regno = i; if (vms_save_fp_regno == -1) - vms_base_regno = REG_PV, vms_is_stack_procedure = 1; + vms_base_regno = REG_PV, alpha_is_stack_procedure = 1; /* 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 (vms_is_stack_procedure) + if (alpha_is_stack_procedure) sa_size += 2; } else { - /* If some registers were saved but not RA, RA must also be saved, - so leave space for it. */ - if (sa_size != 0 || alpha_ra_ever_killed ()) - sa_size++; - /* Our size must be even (multiple of 16 bytes). */ if (sa_size & 1) sa_size++; @@ -3320,7 +6002,7 @@ int alpha_pv_save_size () { alpha_sa_size (); - return vms_is_stack_procedure ? 8 : 0; + return alpha_is_stack_procedure ? 8 : 0; } int @@ -3330,16 +6012,25 @@ alpha_using_fp () return vms_unwind_regno == HARD_FRAME_POINTER_REGNUM; } -int -vms_valid_decl_attribute_p (decl, attributes, identifier, args) - tree decl ATTRIBUTE_UNUSED; - tree attributes ATTRIBUTE_UNUSED; - tree identifier; - tree args; +#if TARGET_ABI_OPEN_VMS + +const struct attribute_spec vms_attribute_table[] = { - if (is_attribute_p ("overlaid", identifier)) - return (args == NULL_TREE); - return 0; + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ + { "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) + rtx *px; + void *data ATTRIBUTE_UNUSED; +{ + return GET_CODE (*px) == LO_SUM; } static int @@ -3347,14 +6038,12 @@ alpha_does_function_need_gp () { rtx insn; - /* We never need a GP for Windows/NT or VMS. */ - if (TARGET_WINDOWS_NT || TARGET_OPEN_VMS) + /* The GP being variable is an OSF abi thing. */ + if (! TARGET_ABI_OSF) return 0; -#ifdef TARGET_PROFILING_NEEDS_GP - if (profile_flag) + if (TARGET_PROFILING_NEEDS_GP && current_function_profile) return 1; -#endif #ifdef ASM_OUTPUT_MI_THUNK if (current_function_is_thunk) @@ -3370,13 +6059,16 @@ alpha_does_function_need_gp () pop_topmost_sequence (); for (; insn; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + 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; } return 0; @@ -3459,24 +6151,48 @@ alpha_expand_prologue () sa_size = alpha_sa_size (); frame_size = get_frame_size (); - if (TARGET_OPEN_VMS) + if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size - + (vms_is_stack_procedure ? 8 : 0) + + (alpha_is_stack_procedure ? 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_ROUND (frame_size + + current_function_outgoing_args_size); else frame_size = (ALPHA_ROUND (current_function_outgoing_args_size) + sa_size + ALPHA_ROUND (frame_size + current_function_pretend_args_size)); - if (TARGET_OPEN_VMS) + if (TARGET_ABI_OPEN_VMS) reg_offset = 8; else reg_offset = ALPHA_ROUND (current_function_outgoing_args_size); alpha_sa_mask (&imask, &fmask); + /* Emit an insn to reload GP, if needed. */ + if (TARGET_ABI_OSF) + { + alpha_function_needs_gp = alpha_does_function_need_gp (); + if (alpha_function_needs_gp) + emit_insn (gen_prologue_ldgp ()); + } + + /* TARGET_PROFILING_NEEDS_GP actually implies that we need to insert + 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 && current_function_profile) + emit_insn (gen_prologue_mcount ()); + + if (TARGET_ABI_UNICOSMK) + unicosmk_gen_dsib (&imask); + /* Adjust the stack by the frame size. If the frame size is > 4096 bytes, we need to be sure we probe somewhere in the first and last 4096 bytes (we can probably get away without the latter test) and @@ -3493,7 +6209,9 @@ alpha_expand_prologue () int probed = 4096; do - emit_insn (gen_probe_stack (GEN_INT (-probed))); + emit_insn (gen_probe_stack (GEN_INT (TARGET_ABI_UNICOSMK + ? -probed + 64 + : -probed))); while ((probed += 8192) < frame_size); /* We only have to do this probe if we aren't saving registers. */ @@ -3502,10 +6220,10 @@ alpha_expand_prologue () } if (frame_size != 0) - { - FRP (emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, - GEN_INT (-frame_size)))); - } + FRP (emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (TARGET_ABI_UNICOSMK + ? -frame_size + 64 + : -frame_size)))); } else { @@ -3522,7 +6240,8 @@ alpha_expand_prologue () rtx seq; emit_move_insn (count, GEN_INT (blocks)); - emit_insn (gen_adddi3 (ptr, stack_pointer_rtx, GEN_INT (4096))); + emit_insn (gen_adddi3 (ptr, stack_pointer_rtx, + GEN_INT (TARGET_ABI_UNICOSMK ? 4096 - 64 : 4096))); /* Because of the difficulty in emitting a new basic block this late in the compilation, generate the loop as a single insn. */ @@ -3535,7 +6254,7 @@ alpha_expand_prologue () emit_move_insn (last, const0_rtx); } - if (TARGET_WINDOWS_NT) + if (TARGET_ABI_WINDOWS_NT) { /* For NT stack unwind (done by 'reverse execution'), it's not OK to take the result of a loop, even though the value @@ -3569,90 +6288,119 @@ alpha_expand_prologue () = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, gen_rtx_SET (VOIDmode, stack_pointer_rtx, gen_rtx_PLUS (Pmode, stack_pointer_rtx, - GEN_INT (-frame_size))), + GEN_INT (TARGET_ABI_UNICOSMK + ? -frame_size + 64 + : -frame_size))), REG_NOTES (seq)); } - /* Cope with very large offsets to the register save area. */ - sa_reg = stack_pointer_rtx; - if (reg_offset + sa_size > 0x8000) + if (!TARGET_ABI_UNICOSMK) { - int low = ((reg_offset & 0xffff) ^ 0x8000) - 0x8000; - HOST_WIDE_INT bias; - - if (low + sa_size <= 0x8000) - bias = reg_offset - low, reg_offset = low; - else - bias = reg_offset, reg_offset = 0; - - sa_reg = gen_rtx_REG (DImode, 24); - FRP (emit_insn (gen_adddi3 (sa_reg, stack_pointer_rtx, GEN_INT (bias)))); - } - - /* Save regs in stack order. Beginning with VMS PV. */ - if (TARGET_OPEN_VMS && vms_is_stack_procedure) - { - mem = gen_rtx_MEM (DImode, stack_pointer_rtx); - MEM_ALIAS_SET (mem) = alpha_sr_alias_set; - FRP (emit_move_insn (mem, gen_rtx_REG (DImode, REG_PV))); - } - - /* Save register RA next. */ - if (imask & (1L << REG_RA)) - { - mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset)); - MEM_ALIAS_SET (mem) = alpha_sr_alias_set; - FRP (emit_move_insn (mem, gen_rtx_REG (DImode, REG_RA))); - imask &= ~(1L << REG_RA); - reg_offset += 8; - } - - /* Now save any other registers required to be saved. */ - for (i = 0; i < 32; i++) - if (imask & (1L << i)) - { - mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset)); - MEM_ALIAS_SET (mem) = alpha_sr_alias_set; - FRP (emit_move_insn (mem, gen_rtx_REG (DImode, i))); - reg_offset += 8; - } - - for (i = 0; i < 32; i++) - if (fmask & (1L << i)) - { - mem = gen_rtx_MEM (DFmode, plus_constant (sa_reg, reg_offset)); - MEM_ALIAS_SET (mem) = alpha_sr_alias_set; - FRP (emit_move_insn (mem, gen_rtx_REG (DFmode, i+32))); - reg_offset += 8; - } - - if (TARGET_OPEN_VMS) - { - if (!vms_is_stack_procedure) + /* Cope with very large offsets to the register save area. */ + sa_reg = stack_pointer_rtx; + if (reg_offset + sa_size > 0x8000) { - /* Register frame procedures fave the fp. */ - FRP (emit_move_insn (gen_rtx_REG (DImode, vms_save_fp_regno), - hard_frame_pointer_rtx)); + int low = ((reg_offset & 0xffff) ^ 0x8000) - 0x8000; + HOST_WIDE_INT bias; + + if (low + sa_size <= 0x8000) + bias = reg_offset - low, reg_offset = low; + else + bias = reg_offset, reg_offset = 0; + + sa_reg = gen_rtx_REG (DImode, 24); + FRP (emit_insn (gen_adddi3 (sa_reg, stack_pointer_rtx, + GEN_INT (bias)))); } + + /* Save regs in stack order. Beginning with VMS PV. */ + if (TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure) + { + mem = gen_rtx_MEM (DImode, stack_pointer_rtx); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (mem, gen_rtx_REG (DImode, REG_PV))); + } + + /* Save register RA next. */ + if (imask & (1L << 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); + reg_offset += 8; + } + + /* Now save any other registers required to be saved. */ + for (i = 0; i < 32; i++) + if (imask & (1L << i)) + { + 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, i))); + reg_offset += 8; + } + + for (i = 0; i < 32; i++) + if (fmask & (1L << i)) + { + mem = gen_rtx_MEM (DFmode, plus_constant (sa_reg, reg_offset)); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (mem, gen_rtx_REG (DFmode, i+32))); + reg_offset += 8; + } + } + else if (TARGET_ABI_UNICOSMK && alpha_is_stack_procedure) + { + /* 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 + the old frame pointer here - they are saved in the DSIB. */ + + reg_offset = -56; + for (i = 9; i < 15; i++) + if (imask & (1L << i)) + { + mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx, + reg_offset)); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (mem, gen_rtx_REG (DImode, i))); + reg_offset -= 8; + } + for (i = 2; i < 10; i++) + if (fmask & (1L << i)) + { + mem = gen_rtx_MEM (DFmode, plus_constant (hard_frame_pointer_rtx, + reg_offset)); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (mem, gen_rtx_REG (DFmode, i+32))); + reg_offset -= 8; + } + } + + if (TARGET_ABI_OPEN_VMS) + { + if (!alpha_is_stack_procedure) + /* 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) - FRP (emit_move_insn (gen_rtx_REG (DImode, vms_base_regno), - gen_rtx_REG (DImode, 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) - { - FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx)); - } + 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)))); - } + FRP (emit_move_insn + (stack_pointer_rtx, + plus_constant (hard_frame_pointer_rtx, + - (ALPHA_ROUND + (current_function_outgoing_args_size))))); } - else + else if (!TARGET_ABI_UNICOSMK) { /* If we need a frame pointer, set it from the stack pointer. */ if (frame_pointer_needed) @@ -3660,12 +6408,10 @@ alpha_expand_prologue () if (TARGET_CAN_FAULT_IN_PROLOGUE) FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx)); else - { - /* This must always be the last instruction in the - prologue, thus we emit a special move + clobber. */ + /* This must always be the last instruction in the + prologue, thus we emit a special move + clobber. */ FRP (emit_insn (gen_init_fp (hard_frame_pointer_rtx, stack_pointer_rtx, sa_reg))); - } } } @@ -3688,7 +6434,7 @@ alpha_expand_prologue () void alpha_start_function (file, fnname, decl) FILE *file; - char *fnname; + const char *fnname; tree decl ATTRIBUTE_UNUSED; { unsigned long imask = 0; @@ -3702,21 +6448,35 @@ alpha_start_function (file, fnname, decl) char *entry_label = (char *) alloca (strlen (fnname) + 6); int i; + /* Don't emit an extern directive for functions defined in the same file. */ + if (TARGET_ABI_UNICOSMK) + { + tree name_tree; + name_tree = get_identifier (fnname); + TREE_ASM_WRITTEN (name_tree) = 1; + } + + alpha_fnname = fnname; sa_size = alpha_sa_size (); frame_size = get_frame_size (); - if (TARGET_OPEN_VMS) + if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size - + (vms_is_stack_procedure ? 8 : 0) + + (alpha_is_stack_procedure ? 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_ROUND (frame_size + + current_function_outgoing_args_size); else frame_size = (ALPHA_ROUND (current_function_outgoing_args_size) + sa_size + ALPHA_ROUND (frame_size + current_function_pretend_args_size)); - if (TARGET_OPEN_VMS) + if (TARGET_ABI_OPEN_VMS) reg_offset = 8; else reg_offset = ALPHA_ROUND (current_function_outgoing_args_size); @@ -3733,36 +6493,56 @@ alpha_start_function (file, fnname, decl) if (write_symbols == SDB_DEBUG) { +#ifdef ASM_OUTPUT_SOURCE_FILENAME ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl)); +#endif +#ifdef ASM_OUTPUT_SOURCE_LINE if (debug_info_level != DINFO_LEVEL_TERSE) ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl)); +#endif } /* Issue function start and label. */ - if (TARGET_OPEN_VMS || !flag_inhibit_size_directive) + if (TARGET_ABI_OPEN_VMS + || (!TARGET_ABI_UNICOSMK && !flag_inhibit_size_directive)) { fputs ("\t.ent ", file); assemble_name (file, fnname); putc ('\n', file); + + /* If the function needs GP, we'll write the "..ng" label there. + Otherwise, do it here. */ + if (TARGET_ABI_OSF && ! alpha_function_needs_gp) + { + putc ('$', file); + assemble_name (file, fnname); + fputs ("..ng:\n", file); + } } strcpy (entry_label, fnname); - if (TARGET_OPEN_VMS) + if (TARGET_ABI_OPEN_VMS) strcat (entry_label, "..en"); + + /* For public functions, the label must be globalized by appending an + additional colon. */ + if (TARGET_ABI_UNICOSMK && TREE_PUBLIC (decl)) + strcat (entry_label, ":"); + ASM_OUTPUT_LABEL (file, entry_label); inside_function = TRUE; - if (TARGET_OPEN_VMS) + if (TARGET_ABI_OPEN_VMS) fprintf (file, "\t.base $%d\n", vms_base_regno); - if (!TARGET_OPEN_VMS && TARGET_IEEE_CONFORMANT + if (!TARGET_ABI_OPEN_VMS && !TARGET_ABI_UNICOSMK && TARGET_IEEE_CONFORMANT && !flag_inhibit_size_directive) { /* Set flags in procedure descriptor to request IEEE-conformant math-library routines. The value we set it to is PDSC_EXC_IEEE - (/usr/include/pdsc.h). */ + (/usr/include/pdsc.h). */ fputs ("\t.eflag 48\n", file); } @@ -3773,11 +6553,13 @@ alpha_start_function (file, fnname, decl) /* Describe our frame. If the frame size is larger than an integer, print it as zero to avoid an assembler error. We won't be properly describing such a frame, but that's the best we can do. */ - if (TARGET_OPEN_VMS) + 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 >= (1l << 31) ? 0 : frame_size); + 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); @@ -3793,15 +6575,17 @@ alpha_start_function (file, fnname, decl) } /* Describe which registers were spilled. */ - if (TARGET_OPEN_VMS) + if (TARGET_ABI_UNICOSMK) + ; + else if (TARGET_ABI_OPEN_VMS) { if (imask) - /* ??? Does VMS care if mask contains ra? The old code did'nt + /* ??? 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)); if (fmask) fprintf (file, "\t.fmask 0x%lx,0\n", fmask); - if (!vms_is_stack_procedure) + if (!alpha_is_stack_procedure) fprintf (file, "\t.fp_save $%d\n", vms_save_fp_regno); } else if (!flag_inhibit_size_directive) @@ -3827,21 +6611,7 @@ alpha_start_function (file, fnname, decl) } } - /* Emit GP related things. It is rather unfortunate about the alignment - issues surrounding a CODE_LABEL that forces us to do the label in - plain text. */ - if (!TARGET_OPEN_VMS && !TARGET_WINDOWS_NT) - { - alpha_function_needs_gp = alpha_does_function_need_gp (); - if (alpha_function_needs_gp) - fputs ("\tldgp $29,0($27)\n", file); - - putc ('$', file); - assemble_name (file, fnname); - fputs ("..ng:\n", file); - } - -#ifdef OPEN_VMS +#if TARGET_ABI_OPEN_VMS /* Ifdef'ed cause readonly_section and link_section are only available then. */ readonly_section (); @@ -3859,7 +6629,7 @@ alpha_start_function (file, fnname, decl) ASM_OUTPUT_LABEL (file, fnname); fprintf (file, "\t.pdesc "); assemble_name (file, fnname); - fprintf (file, "..en,%s\n", vms_is_stack_procedure ? "stack" : "reg"); + fprintf (file, "..en,%s\n", alpha_is_stack_procedure ? "stack" : "reg"); alpha_need_linkage (fnname, 1); text_section (); #endif @@ -3867,13 +6637,15 @@ alpha_start_function (file, fnname, decl) /* Emit the .prologue note at the scheduled end of the prologue. */ -void -output_end_prologue (file) +static void +alpha_output_function_end_prologue (file) FILE *file; { - if (TARGET_OPEN_VMS) + if (TARGET_ABI_UNICOSMK) + ; + else if (TARGET_ABI_OPEN_VMS) fputs ("\t.prologue\n", file); - else if (TARGET_WINDOWS_NT) + 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); @@ -3902,44 +6674,54 @@ alpha_expand_epilogue () int fp_is_frame_pointer, fp_offset; rtx sa_reg, sa_reg_exp = NULL; rtx sp_adj1, sp_adj2, mem; + rtx eh_ofs; int i; sa_size = alpha_sa_size (); frame_size = get_frame_size (); - if (TARGET_OPEN_VMS) + if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size - + (vms_is_stack_procedure ? 8 : 0) + + (alpha_is_stack_procedure ? 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_ROUND (frame_size + + current_function_outgoing_args_size); else frame_size = (ALPHA_ROUND (current_function_outgoing_args_size) + sa_size + ALPHA_ROUND (frame_size + current_function_pretend_args_size)); - if (TARGET_OPEN_VMS) + if (TARGET_ABI_OPEN_VMS) reg_offset = 8; else reg_offset = ALPHA_ROUND (current_function_outgoing_args_size); alpha_sa_mask (&imask, &fmask); - fp_is_frame_pointer = ((TARGET_OPEN_VMS && vms_is_stack_procedure) - || (!TARGET_OPEN_VMS && frame_pointer_needed)); + fp_is_frame_pointer = ((TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure) + || (!TARGET_ABI_OPEN_VMS && frame_pointer_needed)); + fp_offset = 0; + sa_reg = stack_pointer_rtx; - if (sa_size) + if (current_function_calls_eh_return) + eh_ofs = EH_RETURN_STACKADJ_RTX; + else + eh_ofs = NULL_RTX; + + if (!TARGET_ABI_UNICOSMK && sa_size) { /* If we have a frame pointer, restore SP from it. */ - if ((TARGET_OPEN_VMS + if ((TARGET_ABI_OPEN_VMS && vms_unwind_regno == HARD_FRAME_POINTER_REGNUM) - || (!TARGET_OPEN_VMS && frame_pointer_needed)) - { - FRP (emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx)); - } + || (!TARGET_ABI_OPEN_VMS && frame_pointer_needed)) + FRP (emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx)); /* Cope with very large offsets to the register save area. */ - sa_reg = stack_pointer_rtx; if (reg_offset + sa_size > 0x8000) { int low = ((reg_offset & 0xffff) ^ 0x8000) - 0x8000; @@ -3956,14 +6738,13 @@ alpha_expand_epilogue () FRP (emit_move_insn (sa_reg, sa_reg_exp)); } - /* Restore registers in order, excepting a true frame pointer. */ + /* Restore registers in order, excepting a true frame pointer. */ + + mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset)); + if (! eh_ofs) + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem)); - if (! alpha_eh_epilogue_sp_ofs) - { - mem = gen_rtx_MEM (DImode, plus_constant(sa_reg, reg_offset)); - MEM_ALIAS_SET (mem) = alpha_sr_alias_set; - FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem)); - } reg_offset += 8; imask &= ~(1L << REG_RA); @@ -3975,7 +6756,7 @@ alpha_expand_epilogue () else { mem = gen_rtx_MEM (DImode, plus_constant(sa_reg, reg_offset)); - MEM_ALIAS_SET (mem) = alpha_sr_alias_set; + set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DImode, i), mem)); } reg_offset += 8; @@ -3985,29 +6766,67 @@ alpha_expand_epilogue () if (fmask & (1L << i)) { mem = gen_rtx_MEM (DFmode, plus_constant(sa_reg, reg_offset)); - MEM_ALIAS_SET (mem) = alpha_sr_alias_set; + set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (gen_rtx_REG (DFmode, i+32), mem)); reg_offset += 8; } } + else if (TARGET_ABI_UNICOSMK && alpha_is_stack_procedure) + { + /* Restore callee-saved general-purpose registers. */ - if (frame_size || alpha_eh_epilogue_sp_ofs) + reg_offset = -56; + + for (i = 9; i < 15; i++) + if (imask & (1L << i)) + { + mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx, + reg_offset)); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (gen_rtx_REG (DImode, i), mem)); + reg_offset -= 8; + } + + for (i = 2; i < 10; i++) + if (fmask & (1L << i)) + { + mem = gen_rtx_MEM (DFmode, plus_constant(hard_frame_pointer_rtx, + reg_offset)); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (gen_rtx_REG (DFmode, i+32), mem)); + reg_offset -= 8; + } + + /* Restore the return address from the DSIB. */ + + mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx, -8)); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem)); + } + + if (frame_size || eh_ofs) { sp_adj1 = stack_pointer_rtx; - if (alpha_eh_epilogue_sp_ofs) + if (eh_ofs) { sp_adj1 = gen_rtx_REG (DImode, 23); emit_move_insn (sp_adj1, - gen_rtx_PLUS (Pmode, stack_pointer_rtx, - alpha_eh_epilogue_sp_ofs)); + gen_rtx_PLUS (Pmode, stack_pointer_rtx, eh_ofs)); } /* If the stack size is large, begin computation into a temporary register so as not to interfere with a potential fp restore, which must be consecutive with an SP restore. */ - if (frame_size < 32768) + if (frame_size < 32768 + && ! (TARGET_ABI_UNICOSMK && current_function_calls_alloca)) sp_adj2 = GEN_INT (frame_size); + else if (TARGET_ABI_UNICOSMK) + { + sp_adj1 = gen_rtx_REG (DImode, 23); + FRP (emit_move_insn (sp_adj1, hard_frame_pointer_rtx)); + sp_adj2 = const0_rtx; + } else if (frame_size < 0x40007fffL) { int low = ((frame_size & 0xffff) ^ 0x8000) - 0x8000; @@ -4040,14 +6859,22 @@ alpha_expand_epilogue () /* From now on, things must be in order. So emit blockages. */ /* Restore the frame pointer. */ - if (fp_is_frame_pointer) + if (TARGET_ABI_UNICOSMK) { emit_insn (gen_blockage ()); - mem = gen_rtx_MEM (DImode, plus_constant(sa_reg, fp_offset)); - MEM_ALIAS_SET (mem) = alpha_sr_alias_set; + mem = gen_rtx_MEM (DImode, + plus_constant (hard_frame_pointer_rtx, -16)); + set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (hard_frame_pointer_rtx, mem)); } - else if (TARGET_OPEN_VMS) + else if (fp_is_frame_pointer) + { + emit_insn (gen_blockage ()); + mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, fp_offset)); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (hard_frame_pointer_rtx, mem)); + } + else if (TARGET_ABI_OPEN_VMS) { emit_insn (gen_blockage ()); FRP (emit_move_insn (hard_frame_pointer_rtx, @@ -4056,21 +6883,30 @@ alpha_expand_epilogue () /* Restore the stack pointer. */ emit_insn (gen_blockage ()); - FRP (emit_move_insn (stack_pointer_rtx, - gen_rtx_PLUS (DImode, sp_adj1, sp_adj2))); + if (sp_adj2 == const0_rtx) + FRP (emit_move_insn (stack_pointer_rtx, sp_adj1)); + else + FRP (emit_move_insn (stack_pointer_rtx, + gen_rtx_PLUS (DImode, sp_adj1, sp_adj2))); } else { - if (TARGET_OPEN_VMS && !vms_is_stack_procedure) + if (TARGET_ABI_OPEN_VMS && !alpha_is_stack_procedure) { 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) + { + /* Decrement the frame pointer if the function does not have a + frame. */ - /* Return. */ - emit_jump_insn (gen_return_internal ()); + emit_insn (gen_blockage ()); + FRP (emit_insn (gen_adddi3 (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, GEN_INT (-1)))); + } + } } /* Output the rest of the textual info surrounding the epilogue. */ @@ -4078,11 +6914,11 @@ alpha_expand_epilogue () void alpha_end_function (file, fnname, decl) FILE *file; - char *fnname; + const char *fnname; tree decl ATTRIBUTE_UNUSED; { /* End the function. */ - if (!flag_inhibit_size_directive) + if (!TARGET_ABI_UNICOSMK && !flag_inhibit_size_directive) { fputs ("\t.end ", file); assemble_name (file, fnname); @@ -4094,11 +6930,23 @@ alpha_end_function (file, fnname, decl) 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. */ + 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))) + && (!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; + + /* Output jump tables and the static subroutine information block. */ + if (TARGET_ABI_UNICOSMK) + { + unicosmk_output_ssib (file, fnname); + unicosmk_output_deferred_case_vectors (file); + } } /* Debugging support. */ @@ -4132,7 +6980,7 @@ long alpha_auto_offset; void alpha_output_filename (stream, name) FILE *stream; - char *name; + const char *name; { static int first_time = TRUE; char ltext_label_name[100]; @@ -4152,7 +7000,7 @@ alpha_output_filename (stream, name) else if (write_symbols == DBX_DEBUG) { ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); - fprintf (stream, "%s ", ASM_STABS_OP); + fprintf (stream, "%s", ASM_STABS_OP); output_quoted_string (stream, name); fprintf (stream, ",%d,0,0,%s\n", N_SOL, <ext_label_name[1]); } @@ -4185,7 +7033,7 @@ alpha_output_lineno (stream, line) { /* mips-tfile doesn't understand .stabd directives. */ ++sym_lineno; - fprintf (stream, "$LM%d:\n\t%s %d,0,%d,$LM%d\n", + fprintf (stream, "$LM%d:\n%s%d,0,%d,$LM%d\n", sym_lineno, ASM_STABN_OP, N_SLINE, line, sym_lineno); } else @@ -4197,14 +7045,14 @@ alpha_output_lineno (stream, line) struct shadow_summary { struct { - unsigned long i : 31; /* Mask of int regs */ - unsigned long fp : 31; /* Mask of fp regs */ - unsigned long mem : 1; /* mem == imem | fpmem */ + unsigned int i : 31; /* Mask of int regs */ + unsigned int fp : 31; /* Mask of fp regs */ + unsigned int mem : 1; /* mem == imem | fpmem */ } used, defd; }; -static void summarize_insn PROTO((rtx, struct shadow_summary *, int)); -static void alpha_handle_trap_shadows PROTO((rtx)); +static void summarize_insn PARAMS ((rtx, struct shadow_summary *, int)); +static void alpha_handle_trap_shadows PARAMS ((rtx)); /* 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 @@ -4216,7 +7064,7 @@ summarize_insn (x, sum, set) struct shadow_summary *sum; int set; { - char *format_ptr; + const char *format_ptr; int i, j; if (x == 0) @@ -4256,7 +7104,7 @@ summarize_insn (x, sum, set) case REG: { int regno = REGNO (x); - unsigned long mask = 1UL << (regno % 32); + unsigned long mask = ((unsigned long) 1) << (regno % 32); if (regno == 31 || regno == 63) break; @@ -4290,6 +7138,7 @@ summarize_insn (x, sum, set) case CONST_INT: case CONST_DOUBLE: case SYMBOL_REF: case LABEL_REF: case CONST: + case SCRATCH: case ASM_INPUT: break; /* Handle common unary and binary ops for efficiency. */ @@ -4500,7 +7349,6 @@ alpha_handle_trap_shadows (insns) } } -#ifdef HAIFA /* Alpha can only issue instruction groups simultaneously if they are suitibly aligned. This is very processor-specific. */ @@ -4522,15 +7370,15 @@ enum alphaev5_pipe { EV5_FM = 64 }; -static enum alphaev4_pipe alphaev4_insn_pipe PROTO((rtx)); -static enum alphaev5_pipe alphaev5_insn_pipe PROTO((rtx)); -static rtx alphaev4_next_group PROTO((rtx, int*, int*)); -static rtx alphaev5_next_group PROTO((rtx, int*, int*)); -static rtx alphaev4_next_nop PROTO((int*)); -static rtx alphaev5_next_nop PROTO((int*)); +static enum alphaev4_pipe alphaev4_insn_pipe PARAMS ((rtx)); +static enum alphaev5_pipe alphaev5_insn_pipe PARAMS ((rtx)); +static rtx alphaev4_next_group PARAMS ((rtx, int *, int *)); +static rtx alphaev5_next_group PARAMS ((rtx, int *, int *)); +static rtx alphaev4_next_nop PARAMS ((int *)); +static rtx alphaev5_next_nop PARAMS ((int *)); static void alpha_align_insns - PROTO((rtx, int, rtx (*)(rtx, int*, int*), rtx (*)(int*), int)); + PARAMS ((rtx, unsigned int, rtx (*)(rtx, int *, int *), rtx (*)(int *))); static enum alphaev4_pipe alphaev4_insn_pipe (insn) @@ -4570,7 +7418,7 @@ alphaev4_insn_pipe (insn) return EV4_IB1; default: - abort(); + abort (); } } @@ -4638,7 +7486,7 @@ alphaev4_next_group (insn, pin_use, plen) len = in_use = 0; - if (GET_RTX_CLASS (GET_CODE (insn)) != 'i' + if (! INSN_P (insn) || GET_CODE (PATTERN (insn)) == CLOBBER || GET_CODE (PATTERN (insn)) == USE) goto next_and_done; @@ -4703,7 +7551,7 @@ alphaev4_next_group (insn, pin_use, plen) next: insn = next_nonnote_insn (insn); - if (!insn || GET_RTX_CLASS (GET_CODE (insn)) != 'i') + if (!insn || ! INSN_P (insn)) goto done; /* Let Haifa tell us where it thinks insn group boundaries are. */ @@ -4738,7 +7586,7 @@ alphaev5_next_group (insn, pin_use, plen) len = in_use = 0; - if (GET_RTX_CLASS (GET_CODE (insn)) != 'i' + if (! INSN_P (insn) || GET_CODE (PATTERN (insn)) == CLOBBER || GET_CODE (PATTERN (insn)) == USE) goto next_and_done; @@ -4836,7 +7684,7 @@ alphaev5_next_group (insn, pin_use, plen) next: insn = next_nonnote_insn (insn); - if (!insn || GET_RTX_CLASS (GET_CODE (insn)) != 'i') + if (!insn || ! INSN_P (insn)) goto done; /* Let Haifa tell us where it thinks insn group boundaries are. */ @@ -4917,15 +7765,14 @@ alphaev5_next_nop (pin_use) /* The instruction group alignment main loop. */ static void -alpha_align_insns (insns, max_align, next_group, next_nop, gp_in_use) +alpha_align_insns (insns, max_align, next_group, next_nop) rtx insns; - int max_align; - rtx (*next_group) PROTO((rtx, int*, int*)); - rtx (*next_nop) PROTO((int*)); - int gp_in_use; + unsigned int max_align; + rtx (*next_group) PARAMS ((rtx, int *, int *)); + rtx (*next_nop) PARAMS ((int *)); { /* ALIGN is the known alignment for the insn group. */ - int align; + unsigned int align; /* OFS is the offset of the current insn in the insn group. */ int ofs; int prev_in_use, in_use, len; @@ -4934,35 +7781,33 @@ alpha_align_insns (insns, max_align, next_group, next_nop, gp_in_use) /* Let shorten branches care for assigning alignments to code labels. */ shorten_branches (insns); - align = (FUNCTION_BOUNDARY/BITS_PER_UNIT < max_align - ? FUNCTION_BOUNDARY/BITS_PER_UNIT : max_align); + if (align_functions < 4) + align = 4; + else if ((unsigned int) align_functions < max_align) + align = align_functions; + else + align = max_align; - /* Account for the initial GP load, which happens before the scheduled - prologue we emitted as RTL. */ ofs = prev_in_use = 0; - if (alpha_does_function_need_gp()) - { - ofs = 8 & (align - 1); - prev_in_use = gp_in_use; - } - i = insns; if (GET_CODE (i) == NOTE) i = next_nonnote_insn (i); while (i) { - next = (*next_group)(i, &in_use, &len); + next = (*next_group) (i, &in_use, &len); /* When we see a label, resync alignment etc. */ if (GET_CODE (i) == CODE_LABEL) { - int new_align = 1 << label_to_alignment (i); + unsigned int new_align = 1 << label_to_alignment (i); + if (new_align >= align) { align = new_align < max_align ? new_align : max_align; ofs = 0; } + else if (ofs & (new_align-1)) ofs = (ofs | (new_align-1)) + 1; if (len != 0) @@ -4985,18 +7830,23 @@ alpha_align_insns (insns, max_align, next_group, next_nop, gp_in_use) /* If the known alignment is smaller than the recognized insn group, realign the output. */ - else if (align < len) + else if ((int) align < len) { - int new_log_align = len > 8 ? 4 : 3; - rtx where; + unsigned int new_log_align = len > 8 ? 4 : 3; + 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, @@ -5005,13 +7855,13 @@ alpha_align_insns (insns, max_align, next_group, next_nop, gp_in_use) can make use of the knowledge of what sorts of instructions were issued in the previous group to make sure that all of the added nops are really free. */ - else if (ofs + len > align) + else if (ofs + len > (int) align) { 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) { @@ -5021,7 +7871,7 @@ alpha_align_insns (insns, max_align, next_group, next_nop, gp_in_use) 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 @@ -5038,9 +7888,8 @@ alpha_align_insns (insns, max_align, next_group, next_nop, gp_in_use) i = next; } } -#endif /* HAIFA */ -/* Machine dependant reorg pass. */ +/* Machine dependent reorg pass. */ void alpha_reorg (insns) @@ -5049,28 +7898,23 @@ alpha_reorg (insns) if (alpha_tp != ALPHA_TP_PROG || flag_exceptions) alpha_handle_trap_shadows (insns); -#ifdef HAIFA /* Due to the number of extra trapb insns, don't bother fixing up alignment when trap precision is instruction. Moreover, we can - only do our job when sched2 is run and Haifa is our scheduler. */ + only do our job when sched2 is run. */ if (optimize && !optimize_size && alpha_tp != ALPHA_TP_INSN && flag_schedule_insns_after_reload) { if (alpha_cpu == PROCESSOR_EV4) - alpha_align_insns (insns, 8, alphaev4_next_group, - alphaev4_next_nop, EV4_IB0); + alpha_align_insns (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, EV5_E01 | EV5_E0); + alpha_align_insns (insns, 16, alphaev5_next_group, alphaev5_next_nop); } -#endif } - /* Check a floating-point value for validity for a particular machine mode. */ -static char * const float_strings[] = +static const char * const float_strings[] = { /* These are for FLOAT_VAX. */ "1.70141173319264430e+38", /* 2^127 (2^24 - 1) / 2^24 */ @@ -5117,37 +7961,35 @@ check_float_value (mode, d, overflow) else fvptr = &float_values[4]; - bcopy ((char *) d, (char *) &r, sizeof (REAL_VALUE_TYPE)); + memcpy (&r, d, sizeof (REAL_VALUE_TYPE)); if (REAL_VALUES_LESS (fvptr[0], r)) { - bcopy ((char *) &fvptr[0], (char *) d, - sizeof (REAL_VALUE_TYPE)); + memcpy (d, &fvptr[0], sizeof (REAL_VALUE_TYPE)); return 1; } else if (REAL_VALUES_LESS (r, fvptr[1])) { - bcopy ((char *) &fvptr[1], (char *) d, - sizeof (REAL_VALUE_TYPE)); + memcpy (d, &fvptr[1], sizeof (REAL_VALUE_TYPE)); return 1; } else if (REAL_VALUES_LESS (dconst0, r) && REAL_VALUES_LESS (r, fvptr[2])) { - bcopy ((char *) &dconst0, (char *) d, sizeof (REAL_VALUE_TYPE)); + memcpy (d, &dconst0, sizeof (REAL_VALUE_TYPE)); return 1; } else if (REAL_VALUES_LESS (r, dconst0) && REAL_VALUES_LESS (fvptr[3], r)) { - bcopy ((char *) &dconst0, (char *) d, sizeof (REAL_VALUE_TYPE)); + memcpy (d, &dconst0, sizeof (REAL_VALUE_TYPE)); return 1; } } return 0; } - -#if OPEN_VMS + +#if TARGET_ABI_OPEN_VMS /* Return the VMS argument type corresponding to MODE. */ @@ -5169,7 +8011,7 @@ alpha_arg_type (mode) /* Return an rtx for an integer representing the VMS Argument Information register value. */ -struct rtx_def * +rtx alpha_arg_info_reg_val (cum) CUMULATIVE_ARGS cum; { @@ -5182,109 +8024,1044 @@ alpha_arg_info_reg_val (cum) return GEN_INT (regval); } +#include + /* Structure to collect function names for final output in link section. */ enum links_kind {KIND_UNUSED, KIND_LOCAL, KIND_EXTERN}; - -struct alpha_links { - struct alpha_links *next; - char *name; +struct alpha_links +{ + rtx linkage; enum links_kind kind; }; -static struct alpha_links *alpha_links_base = 0; +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. */ + IS_LOCAL is 0 if name is used in call, 1 if name is used in definition. -void + Return an SYMBOL_REF rtx for the linkage. */ + +rtx alpha_need_linkage (name, is_local) - char *name; + const char *name; int is_local; { - rtx x; - struct alpha_links *lptr, *nptr; + splay_tree_node node; + struct alpha_links *al; if (name[0] == '*') name++; - /* Is this name already defined ? */ + if (alpha_links) + { + /* Is this name already defined? */ - for (lptr = alpha_links_base; lptr; lptr = lptr->next) - if (strcmp (lptr->name, name) == 0) - { - if (is_local) - { - /* Defined here but external assumed. */ - if (lptr->kind == KIND_EXTERN) - lptr->kind = KIND_LOCAL; - } - else - { - /* Used here but unused assumed. */ - if (lptr->kind == KIND_UNUSED) - lptr->kind = KIND_LOCAL; - } - return; - } + node = splay_tree_lookup (alpha_links, (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; + } + else + { + /* Used here but unused assumed. */ + if (al->kind == KIND_UNUSED) + al->kind = 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); + } - nptr = (struct alpha_links *) xmalloc (sizeof (struct alpha_links)); - nptr->next = alpha_links_base; - nptr->name = xstrdup (name); + al = (struct alpha_links *) xmalloc (sizeof (struct alpha_links)); + name = xstrdup (name); /* Assume external if no definition. */ - nptr->kind = (is_local ? KIND_UNUSED : KIND_EXTERN); + al->kind = (is_local ? KIND_UNUSED : KIND_EXTERN); - /* Ensure we have an IDENTIFIER so assemble_name can mark is used. */ + /* Ensure we have an IDENTIFIER so assemble_name can mark it used. */ get_identifier (name); - alpha_links_base = nptr; + /* Construct a SYMBOL_REF for us to call. */ + { + size_t name_len = strlen (name); + char *linksym = alloca (name_len + 6); + linksym[0] = '$'; + memcpy (linksym + 1, name, name_len); + memcpy (linksym + 1 + name_len, "..lk", 5); + al->linkage = gen_rtx_SYMBOL_REF (Pmode, + ggc_alloc_string (linksym, name_len + 5)); + } - return; + splay_tree_insert (alpha_links, (splay_tree_key) name, + (splay_tree_value) al); + + 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; + 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) + { + /* 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; { - struct alpha_links *lptr, *nptr; - - readonly_section (); - - fprintf (stream, "\t.align 3\n"); - - for (lptr = alpha_links_base; lptr; lptr = nptr) + if (alpha_links) { - nptr = lptr->next; - - if (lptr->kind == KIND_UNUSED - || ! TREE_SYMBOL_REFERENCED (get_identifier (lptr->name))) - continue; - - fprintf (stream, "$%s..lk:\n", lptr->name); - if (lptr->kind == KIND_LOCAL) - { - /* Local and used, build linkage pair. */ - fprintf (stream, "\t.quad %s..en\n", lptr->name); - fprintf (stream, "\t.quad %s\n", lptr->name); - } - else - /* External and used, request linkage pair. */ - fprintf (stream, "\t.linkage %s\n", lptr->name); + readonly_section (); + fprintf (stream, "\t.align 3\n"); + splay_tree_foreach (alpha_links, alpha_write_one_linkage, stream); } } +/* 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) + tree decl; + const char *name; + int reloc; +{ + unsigned int flags = default_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; +} + +/* Switch to an arbitrary section NAME with attributes as specified + by FLAGS. ALIGN specifies any known alignment requirements for + the section; 0 if the default should be used. */ + +static void +vms_asm_named_section (name, flags) + const char *name; + unsigned int flags; +{ + fputc ('\n', asm_out_file); + fprintf (asm_out_file, ".section\t%s", name); + + if (flags & SECTION_VMS_OVERLAY) + 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"); + + fputc ('\n', asm_out_file); +} + +/* Record an element in the table of global constructors. SYMBOL is + a SYMBOL_REF of the function to be called; PRIORITY is a number + between 0 and MAX_INIT_PRIORITY. + + Differs from default_ctors_section_asm_out_constructor in that the + width of the .ctors entry is always 64 bits, rather than the 32 bits + used by a normal pointer. */ + +static void +vms_asm_out_constructor (symbol, priority) + rtx symbol; + int priority ATTRIBUTE_UNUSED; +{ + ctors_section (); + assemble_align (BITS_PER_WORD); + assemble_integer (symbol, UNITS_PER_WORD, BITS_PER_WORD, 1); +} + +static void +vms_asm_out_destructor (symbol, priority) + rtx symbol; + int priority ATTRIBUTE_UNUSED; +{ + dtors_section (); + assemble_align (BITS_PER_WORD); + assemble_integer (symbol, UNITS_PER_WORD, BITS_PER_WORD, 1); +} +#else + +rtx +alpha_need_linkage (name, is_local) + const char *name ATTRIBUTE_UNUSED; + int is_local ATTRIBUTE_UNUSED; +{ + return NULL_RTX; +} + +#endif /* TARGET_ABI_OPEN_VMS */ + +#if TARGET_ABI_UNICOSMK + +static void unicosmk_output_module_name PARAMS ((FILE *)); +static void unicosmk_output_default_externs PARAMS ((FILE *)); +static void unicosmk_output_dex PARAMS ((FILE *)); +static void unicosmk_output_externs PARAMS ((FILE *)); +static void unicosmk_output_addr_vec PARAMS ((FILE *, rtx)); +static const char *unicosmk_ssib_name PARAMS ((void)); +static int unicosmk_special_name PARAMS ((const char *)); + +/* Define the offset between two registers, one to be eliminated, and the + other its replacement, at the start of a routine. */ + +int +unicosmk_initial_elimination_offset (from, to) + int from; + int to; +{ + int fixed_size; + + fixed_size = alpha_sa_size(); + if (fixed_size != 0) + fixed_size += 48; + + if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) + return -fixed_size; + else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) + return 0; + else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + return (ALPHA_ROUND (current_function_outgoing_args_size) + + ALPHA_ROUND (get_frame_size())); + else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + return (ALPHA_ROUND (fixed_size) + + ALPHA_ROUND (get_frame_size() + + current_function_outgoing_args_size)); + else + abort (); +} + +/* Output the module name for .ident and .end directives. We have to strip + directories and add make sure that the module name starts with a letter + or '$'. */ + +static void +unicosmk_output_module_name (file) + FILE *file; +{ + const char *name; + + /* Strip directories. */ + + name = strrchr (main_input_filename, '/'); + if (name) + ++name; + else + name = main_input_filename; + + /* CAM only accepts module names that start with a letter or '$'. We + prefix the module name with a '$' if necessary. */ + + if (!ISALPHA (*name)) + fprintf (file, "$%s", name); + else + fputs (name, file); +} + +/* Output text that to appear at the beginning of an assembler file. */ + +void +unicosmk_asm_file_start (file) + FILE *file; +{ + int i; + + fputs ("\t.ident\t", file); + unicosmk_output_module_name (file); + fputs ("\n\n", file); + + /* The Unicos/Mk assembler uses different register names. Instead of trying + to support them, we simply use micro definitions. */ + + /* CAM has different register names: rN for the integer register N and fN + for the floating-point register N. Instead of trying to use these in + alpha.md, we define the symbols $N and $fN to refer to the appropriate + register. */ + + for (i = 0; i < 32; ++i) + fprintf (file, "$%d <- r%d\n", i, i); + + for (i = 0; i < 32; ++i) + fprintf (file, "$f%d <- f%d\n", i, i); + + putc ('\n', file); + + /* The .align directive fill unused space with zeroes which does not work + in code sections. We define the macro 'gcc@code@align' which uses nops + instead. Note that it assumes that code sections always have the + biggest possible alignment since . refers to the current offset from + the beginning of the section. */ + + fputs ("\t.macro gcc@code@align n\n", file); + fputs ("gcc@n@bytes = 1 << n\n", file); + fputs ("gcc@here = . % gcc@n@bytes\n", file); + fputs ("\t.if ne, gcc@here, 0\n", file); + fputs ("\t.repeat (gcc@n@bytes - gcc@here) / 4\n", file); + fputs ("\tbis r31,r31,r31\n", file); + fputs ("\t.endr\n", file); + fputs ("\t.endif\n", file); + fputs ("\t.endm gcc@code@align\n\n", file); + + /* Output extern declarations which should always be visible. */ + unicosmk_output_default_externs (file); + + /* Open a dummy section. We always need to be inside a section for the + section-switching code to work correctly. + ??? This should be a module id or something like that. I still have to + figure out what the rules for those are. */ + fputs ("\n\t.psect\t$SG00000,data\n", file); +} + +/* Output text to appear at the end of an assembler file. This includes all + pending extern declarations and DEX expressions. */ + +void +unicosmk_asm_file_end (file) + FILE *file; +{ + fputs ("\t.endp\n\n", file); + + /* Output all pending externs. */ + + unicosmk_output_externs (file); + + /* Output dex definitions used for functions whose names conflict with + register names. */ + + unicosmk_output_dex (file); + + fputs ("\t.end\t", file); + unicosmk_output_module_name (file); + putc ('\n', file); +} + +/* Output the definition of a common variable. */ + +void +unicosmk_output_common (file, name, size, align) + FILE *file; + const char *name; + int size; + int align; +{ + tree name_tree; + printf ("T3E__: common %s\n", name); + + common_section (); + fputs("\t.endp\n\n\t.psect ", file); + assemble_name(file, name); + fprintf(file, ",%d,common\n", floor_log2 (align / BITS_PER_UNIT)); + fprintf(file, "\t.byte\t0:%d\n", size); + + /* Mark the symbol as defined in this module. */ + name_tree = get_identifier (name); + TREE_ASM_WRITTEN (name_tree) = 1; +} + +#define SECTION_PUBLIC SECTION_MACH_DEP +#define SECTION_MAIN (SECTION_PUBLIC << 1) +static int current_section_align; + +static unsigned int +unicosmk_section_type_flags (decl, name, reloc) + tree decl; + const char *name; + int reloc ATTRIBUTE_UNUSED; +{ + unsigned int flags = default_section_type_flags (decl, name, reloc); + + if (!decl) + return flags; + + if (TREE_CODE (decl) == FUNCTION_DECL) + { + current_section_align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); + if (align_functions_log > current_section_align) + current_section_align = align_functions_log; + + if (! strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), "main")) + flags |= SECTION_MAIN; + } + else + current_section_align = floor_log2 (DECL_ALIGN (decl) / BITS_PER_UNIT); + + if (TREE_PUBLIC (decl)) + flags |= SECTION_PUBLIC; + + return flags; +} + +/* Generate a section name for decl and associate it with the + declaration. */ + +void +unicosmk_unique_section (decl, reloc) + tree decl; + int reloc ATTRIBUTE_UNUSED; +{ + const char *name; + int len; + + if (!decl) + abort (); + + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + STRIP_NAME_ENCODING (name, name); + len = strlen (name); + + if (TREE_CODE (decl) == FUNCTION_DECL) + { + char *string; + + /* It is essential that we prefix the section name here because + otherwise the section names generated for constructors and + destructors confuse collect2. */ + + string = alloca (len + 6); + sprintf (string, "code@%s", name); + DECL_SECTION_NAME (decl) = build_string (len + 5, string); + } + else if (TREE_PUBLIC (decl)) + DECL_SECTION_NAME (decl) = build_string (len, name); + else + { + char *string; + + string = alloca (len + 6); + sprintf (string, "data@%s", name); + DECL_SECTION_NAME (decl) = build_string (len + 5, string); + } +} + +/* Switch to an arbitrary section NAME with attributes as specified + by FLAGS. ALIGN specifies any known alignment requirements for + the section; 0 if the default should be used. */ + +static void +unicosmk_asm_named_section (name, flags) + const char *name; + unsigned int flags; +{ + const char *kind; + + /* Close the previous section. */ + + fputs ("\t.endp\n\n", asm_out_file); + + /* Find out what kind of section we are opening. */ + + if (flags & SECTION_MAIN) + fputs ("\t.start\tmain\n", asm_out_file); + + if (flags & SECTION_CODE) + kind = "code"; + else if (flags & SECTION_PUBLIC) + kind = "common"; + else + kind = "data"; + + if (current_section_align != 0) + fprintf (asm_out_file, "\t.psect\t%s,%d,%s\n", name, + current_section_align, kind); + else + fprintf (asm_out_file, "\t.psect\t%s,%s\n", name, kind); +} + +static void +unicosmk_insert_attributes (decl, attr_ptr) + tree decl; + tree *attr_ptr ATTRIBUTE_UNUSED; +{ + if (DECL_P (decl) + && (TREE_PUBLIC (decl) || TREE_CODE (decl) == FUNCTION_DECL)) + UNIQUE_SECTION (decl, 0); +} + +/* Output an alignment directive. We have to use the macro 'gcc@code@align' + in code sections because .align fill unused space with zeroes. */ + +void +unicosmk_output_align (file, align) + FILE *file; + int align; +{ + if (inside_function) + fprintf (file, "\tgcc@code@align\t%d\n", align); + else + fprintf (file, "\t.align\t%d\n", align); +} + +/* Add a case vector to the current function's list of deferred case + vectors. Case vectors have to be put into a separate section because CAM + does not allow data definitions in code sections. */ + +void +unicosmk_defer_case_vector (lab, vec) + rtx lab; + rtx vec; +{ + struct machine_function *machine = cfun->machine; + + vec = gen_rtx_EXPR_LIST (VOIDmode, lab, vec); + machine->addr_list = gen_rtx_EXPR_LIST (VOIDmode, vec, + machine->addr_list); +} + +/* Output a case vector. */ + +static void +unicosmk_output_addr_vec (file, vec) + FILE *file; + rtx vec; +{ + rtx lab = XEXP (vec, 0); + rtx body = XEXP (vec, 1); + int vlen = XVECLEN (body, 0); + int idx; + + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (lab)); + + for (idx = 0; idx < vlen; idx++) + { + ASM_OUTPUT_ADDR_VEC_ELT + (file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0))); + } +} + +/* Output current function's deferred case vectors. */ + +static void +unicosmk_output_deferred_case_vectors (file) + FILE *file; +{ + struct machine_function *machine = cfun->machine; + rtx t; + + if (machine->addr_list == NULL_RTX) + return; + + data_section (); + for (t = machine->addr_list; t; t = XEXP (t, 1)) + unicosmk_output_addr_vec (file, XEXP (t, 0)); +} + +/* Set up the dynamic subprogram information block (DSIB) and update the + frame pointer register ($15) for subroutines which have a frame. If the + subroutine doesn't have a frame, simply increment $15. */ + +static void +unicosmk_gen_dsib (imaskP) + unsigned long * imaskP; +{ + if (alpha_is_stack_procedure) + { + const char *ssib_name; + rtx mem; + + /* Allocate 64 bytes for the DSIB. */ + + FRP (emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-64)))); + emit_insn (gen_blockage ()); + + /* Save the return address. */ + + 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); + + /* 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); + + emit_insn (gen_blockage ()); + + /* Store the SSIB pointer. */ + + ssib_name = ggc_strdup (unicosmk_ssib_name ()); + mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 32)); + set_mem_alias_set (mem, alpha_sr_alias_set); + + FRP (emit_move_insn (gen_rtx_REG (DImode, 5), + gen_rtx_SYMBOL_REF (Pmode, ssib_name))); + FRP (emit_move_insn (mem, gen_rtx_REG (DImode, 5))); + + /* Save the CIW index. */ + + mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 24)); + set_mem_alias_set (mem, alpha_sr_alias_set); + FRP (emit_move_insn (mem, gen_rtx_REG (DImode, 25))); + + emit_insn (gen_blockage ()); + + /* Set the new frame pointer. */ + + FRP (emit_insn (gen_adddi3 (hard_frame_pointer_rtx, + stack_pointer_rtx, GEN_INT (64)))); + + } + else + { + /* Increment the frame pointer register to indicate that we do not + have a frame. */ + + FRP (emit_insn (gen_adddi3 (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, GEN_INT (1)))); + } +} + +#define SSIB_PREFIX "__SSIB_" +#define SSIB_PREFIX_LEN 7 + +/* Generate the name of the SSIB section for the current function. */ + +static const char * +unicosmk_ssib_name () +{ + /* This is ok since CAM won't be able to deal with names longer than that + anyway. */ + + static char name[256]; + + rtx x; + const char *fnname; + int len; + + x = DECL_RTL (cfun->decl); + if (GET_CODE (x) != MEM) + abort (); + x = XEXP (x, 0); + if (GET_CODE (x) != SYMBOL_REF) + abort (); + fnname = XSTR (x, 0); + STRIP_NAME_ENCODING (fnname, fnname); + + len = strlen (fnname); + if (len + SSIB_PREFIX_LEN > 255) + len = 255 - SSIB_PREFIX_LEN; + + strcpy (name, SSIB_PREFIX); + strncpy (name + SSIB_PREFIX_LEN, fnname, len); + name[len + SSIB_PREFIX_LEN] = 0; + + return name; +} + +/* Output the static subroutine information block for the current + function. */ + +static void +unicosmk_output_ssib (file, fnname) + FILE *file; + const char *fnname; +{ + int len; + int i; + rtx x; + rtx ciw; + struct machine_function *machine = cfun->machine; + + ssib_section (); + fprintf (file, "\t.endp\n\n\t.psect\t%s%s,data\n", user_label_prefix, + unicosmk_ssib_name ()); + + /* Some required stuff and the function name length. */ + + len = strlen (fnname); + fprintf (file, "\t.quad\t^X20008%2.2X28\n", len); + + /* Saved registers + ??? We don't do that yet. */ + + fputs ("\t.quad\t0\n", file); + + /* Function address. */ + + fputs ("\t.quad\t", file); + assemble_name (file, fnname); + putc ('\n', file); + + fputs ("\t.quad\t0\n", file); + fputs ("\t.quad\t0\n", file); + + /* Function name. + ??? We do it the same way Cray CC does it but this could be + simplified. */ + + for( i = 0; i < len; i++ ) + fprintf (file, "\t.byte\t%d\n", (int)(fnname[i])); + if( (len % 8) == 0 ) + fputs ("\t.quad\t0\n", file); + else + fprintf (file, "\t.bits\t%d : 0\n", (8 - (len % 8))*8); + + /* All call information words used in the function. */ + + 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, + CONST_DOUBLE_HIGH (ciw), CONST_DOUBLE_LOW (ciw)); +#else + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (ciw)); +#endif + fprintf (file, "\n"); + } +} + +/* Add a call information word (CIW) to the list of the current function's + CIWs and return its index. + + X is a CONST_INT or CONST_DOUBLE representing the CIW. */ + +rtx +unicosmk_add_call_info_word (x) + rtx x; +{ + rtx node; + struct machine_function *machine = cfun->machine; + + node = gen_rtx_EXPR_LIST (VOIDmode, x, NULL_RTX); + if (machine->first_ciw == NULL_RTX) + machine->first_ciw = node; + else + XEXP (machine->last_ciw, 1) = node; + + machine->last_ciw = node; + ++machine->ciw_count; + + return GEN_INT (machine->ciw_count + + strlen (current_function_name)/8 + 5); +} + +static char unicosmk_section_buf[100]; + +char * +unicosmk_text_section () +{ + static int count = 0; + sprintf (unicosmk_section_buf, "\t.endp\n\n\t.psect\tgcc@text___%d,code", + count++); + return unicosmk_section_buf; +} + +char * +unicosmk_data_section () +{ + static int count = 1; + sprintf (unicosmk_section_buf, "\t.endp\n\n\t.psect\tgcc@data___%d,data", + count++); + return unicosmk_section_buf; +} + +/* The Cray assembler doesn't accept extern declarations for symbols which + are defined in the same file. We have to keep track of all global + symbols which are referenced and/or defined in a source file and output + extern declarations for those which are referenced but not defined at + the end of file. */ + +/* List of identifiers for which an extern declaration might have to be + emitted. */ + +struct unicosmk_extern_list +{ + struct unicosmk_extern_list *next; + const char *name; +}; + +static struct unicosmk_extern_list *unicosmk_extern_head = 0; + +/* Output extern declarations which are required for every asm file. */ + +static void +unicosmk_output_default_externs (file) + FILE *file; +{ + static const char *const externs[] = + { "__T3E_MISMATCH" }; + + int i; + int n; + + n = ARRAY_SIZE (externs); + + for (i = 0; i < n; i++) + fprintf (file, "\t.extern\t%s\n", externs[i]); +} + +/* Output extern declarations for global symbols which are have been + referenced but not defined. */ + +static void +unicosmk_output_externs (file) + FILE *file; +{ + struct unicosmk_extern_list *p; + const char *real_name; + int len; + tree name_tree; + + len = strlen (user_label_prefix); + for (p = unicosmk_extern_head; p != 0; p = p->next) + { + /* 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); + if (len && p->name[0] == '*' + && !memcmp (real_name, user_label_prefix, len)) + real_name += len; + + name_tree = get_identifier (real_name); + if (! TREE_ASM_WRITTEN (name_tree)) + { + TREE_ASM_WRITTEN (name_tree) = 1; + fputs ("\t.extern\t", file); + assemble_name (file, p->name); + putc ('\n', file); + } + } +} + +/* Record an extern. */ + +void +unicosmk_add_extern (name) + const char *name; +{ + struct unicosmk_extern_list *p; + + p = (struct unicosmk_extern_list *) + permalloc (sizeof (struct unicosmk_extern_list)); + p->next = unicosmk_extern_head; + p->name = name; + unicosmk_extern_head = p; +} + +/* The Cray assembler generates incorrect code if identifiers which + conflict with register names are used as instruction operands. We have + to replace such identifiers with DEX expressions. */ + +/* Structure to collect identifiers which have been replaced by DEX + expressions. */ + +struct unicosmk_dex { + struct unicosmk_dex *next; + const char *name; +}; + +/* List of identifiers which have been replaced by DEX expressions. The DEX + number is determined by the position in the list. */ + +static struct unicosmk_dex *unicosmk_dex_list = NULL; + +/* The number of elements in the DEX list. */ + +static int unicosmk_dex_count = 0; + +/* Check if NAME must be replaced by a DEX expression. */ + +static int +unicosmk_special_name (name) + const char *name; +{ + if (name[0] == '*') + ++name; + + if (name[0] == '$') + ++name; + + if (name[0] != 'r' && name[0] != 'f' && name[0] != 'R' && name[0] != 'F') + return 0; + + switch (name[1]) + { + case '1': case '2': + return (name[2] == '\0' || (ISDIGIT (name[2]) && name[3] == '\0')); + + case '3': + return (name[2] == '\0' + || ((name[2] == '0' || name[2] == '1') && name[3] == '\0')); + + default: + return (ISDIGIT (name[1]) && name[2] == '\0'); + } +} + +/* Return the DEX number if X must be replaced by a DEX expression and 0 + otherwise. */ + +static int +unicosmk_need_dex (x) + rtx x; +{ + struct unicosmk_dex *dex; + const char *name; + int i; + + if (GET_CODE (x) != SYMBOL_REF) + return 0; + + name = XSTR (x,0); + if (! unicosmk_special_name (name)) + return 0; + + i = unicosmk_dex_count; + for (dex = unicosmk_dex_list; dex; dex = dex->next) + { + if (! strcmp (name, dex->name)) + return i; + --i; + } + + dex = (struct unicosmk_dex *) permalloc (sizeof (struct unicosmk_dex)); + dex->name = name; + dex->next = unicosmk_dex_list; + unicosmk_dex_list = dex; + + ++unicosmk_dex_count; + return unicosmk_dex_count; +} + +/* Output the DEX definitions for this file. */ + +static void +unicosmk_output_dex (file) + FILE *file; +{ + struct unicosmk_dex *dex; + int i; + + if (unicosmk_dex_list == NULL) + return; + + fprintf (file, "\t.dexstart\n"); + + i = unicosmk_dex_count; + for (dex = unicosmk_dex_list; dex; dex = dex->next) + { + fprintf (file, "\tDEX (%d) = ", i); + assemble_name (file, dex->name); + putc ('\n', file); + --i; + } + + fprintf (file, "\t.dexend\n"); +} + #else -void -alpha_need_linkage (name, is_local) - char *name ATTRIBUTE_UNUSED; - int is_local ATTRIBUTE_UNUSED; +static void +unicosmk_output_deferred_case_vectors (file) + FILE *file ATTRIBUTE_UNUSED; +{} + +static void +unicosmk_gen_dsib (imaskP) + unsigned long * imaskP ATTRIBUTE_UNUSED; +{} + +static void +unicosmk_output_ssib (file, fnname) + FILE * file ATTRIBUTE_UNUSED; + const char * fnname ATTRIBUTE_UNUSED; +{} + +rtx +unicosmk_add_call_info_word (x) + rtx x ATTRIBUTE_UNUSED; { + return NULL_RTX; } -#endif /* OPEN_VMS */ +static int +unicosmk_need_dex (x) + rtx x ATTRIBUTE_UNUSED; +{ + return 0; +} + +#endif /* TARGET_ABI_UNICOSMK */ diff --git a/contrib/gcc/config/alpha/elf.h b/contrib/gcc/config/alpha/elf.h index c3c15b2ace2f..4156be2409ea 100644 --- a/contrib/gcc/config/alpha/elf.h +++ b/contrib/gcc/config/alpha/elf.h @@ -1,5 +1,6 @@ /* Definitions of target machine for GNU compiler, for DEC Alpha w/ELF. - Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 + Free Software Foundation, Inc. Contributed by Richard Henderson (rth@tamu.edu). This file is part of GNU CC. @@ -25,21 +26,24 @@ Boston, MA 02111-1307, USA. */ #undef EXTENDED_COFF #define OBJECT_FORMAT_ELF +/* ??? Move all SDB stuff from alpha.h to osf.h. */ +#undef SDB_DEBUGGING_INFO + #define DBX_DEBUGGING_INFO #define DWARF2_DEBUGGING_INFO -#undef PREFERRED_DEBUGGING_TYPE -#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG #undef ASM_FINAL_SPEC -#undef CC1_SPEC +#undef CC1_SPEC #define CC1_SPEC "%{G*}" -#undef ASM_SPEC -#define ASM_SPEC "%{G*} %{relax:-relax} %{gdwarf*:-no-mdebug}" +#undef ASM_SPEC +#define ASM_SPEC "%{G*} %{relax:-relax} %{!gstabs*:-no-mdebug}%{gstabs*:-mdebug}" -#undef LINK_SPEC +#undef LINK_SPEC #define LINK_SPEC "-m elf64alpha %{G*} %{relax:-relax} \ %{O*:-O3} %{!O*:-O1} \ %{shared:-shared} \ @@ -50,48 +54,28 @@ Boston, MA 02111-1307, USA. */ %{static:-static}}" /* Output at beginning of assembler file. */ -#undef ASM_FILE_START +#undef ASM_FILE_START #define ASM_FILE_START(FILE) \ do { \ - if (write_symbols != DWARF2_DEBUG) \ + if (write_symbols == DBX_DEBUG) \ { \ alpha_write_verstamp (FILE); \ output_file_directive (FILE, main_input_filename); \ } \ fprintf (FILE, "\t.set noat\n"); \ fprintf (FILE, "\t.set noreorder\n"); \ + if (TARGET_EXPLICIT_RELOCS) \ + fprintf (FILE, "\t.set nomacro\n"); \ if (TARGET_BWX | TARGET_MAX | TARGET_FIX | TARGET_CIX) \ { \ fprintf (FILE, "\t.arch %s\n", \ - (alpha_cpu == PROCESSOR_EV6 ? "ev6" \ + (TARGET_CPU_EV6 ? "ev6" \ : TARGET_MAX ? "pca56" : "ev56")); \ } \ } while (0) -extern void output_file_directive (); - -/* Attach a special .ident directive to the end of the file to identify - the version of GCC which compiled this code. The format of the - .ident string is patterned after the ones produced by native svr4 - C compilers. */ - #undef IDENT_ASM_OP -#define IDENT_ASM_OP ".ident" - -#ifdef IDENTIFY_WITH_IDENT -#define ASM_IDENTIFY_GCC(FILE) /* nothing */ -#define ASM_IDENTIFY_LANGUAGE(FILE) \ - fprintf(FILE, "\t%s \"GCC (%s) %s\"\n", IDENT_ASM_OP, \ - lang_identify(), version_string) -#else -#undef ASM_FILE_END -#define ASM_FILE_END(FILE) \ -do { \ - if (!flag_no_ident) \ - fprintf ((FILE), "\t%s\t\"GCC: (GNU) %s\"\n", \ - IDENT_ASM_OP, version_string); \ - } while (0) -#endif +#define IDENT_ASM_OP "\t.ident\t" /* Allow #sccs in preprocessor. */ #define SCCS_DIRECTIVE @@ -99,17 +83,17 @@ do { \ /* Output #ident as a .ident. */ #undef ASM_OUTPUT_IDENT #define ASM_OUTPUT_IDENT(FILE, NAME) \ - fprintf (FILE, "\t%s\t\"%s\"\n", IDENT_ASM_OP, NAME); + fprintf (FILE, "%s\"%s\"\n", IDENT_ASM_OP, NAME); /* This is how to allocate empty space in some section. The .zero pseudo-op is used for this on most svr4 assemblers. */ #undef SKIP_ASM_OP -#define SKIP_ASM_OP ".zero" +#define SKIP_ASM_OP "\t.zero\t" -#undef ASM_OUTPUT_SKIP -#define ASM_OUTPUT_SKIP(FILE,SIZE) \ - fprintf (FILE, "\t%s\t%u\n", SKIP_ASM_OP, (SIZE)) +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(FILE, SIZE) \ + fprintf (FILE, "%s%u\n", SKIP_ASM_OP, (SIZE)) /* Output the label which precedes a jumptable. Note that for all svr4 systems where we actually generate jumptables (which is to say every @@ -120,15 +104,15 @@ do { \ perly re-aligned prior to the actual beginning of the jump table. */ #undef ALIGN_ASM_OP -#define ALIGN_ASM_OP ".align" +#define ALIGN_ASM_OP "\t.align\t" #ifndef ASM_OUTPUT_BEFORE_CASE_LABEL -#define ASM_OUTPUT_BEFORE_CASE_LABEL(FILE,PREFIX,NUM,TABLE) \ +#define ASM_OUTPUT_BEFORE_CASE_LABEL(FILE, PREFIX, NUM, TABLE) \ ASM_OUTPUT_ALIGN ((FILE), 2); #endif -#undef ASM_OUTPUT_CASE_LABEL -#define ASM_OUTPUT_CASE_LABEL(FILE,PREFIX,NUM,JUMPTABLE) \ +#undef ASM_OUTPUT_CASE_LABEL +#define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, JUMPTABLE) \ do { \ ASM_OUTPUT_BEFORE_CASE_LABEL (FILE, PREFIX, NUM, JUMPTABLE) \ ASM_OUTPUT_INTERNAL_LABEL (FILE, PREFIX, NUM); \ @@ -148,12 +132,12 @@ do { \ to depend on their types. We do exactly that here. */ #undef COMMON_ASM_OP -#define COMMON_ASM_OP ".comm" +#define COMMON_ASM_OP "\t.comm\t" -#undef ASM_OUTPUT_ALIGNED_COMMON +#undef ASM_OUTPUT_ALIGNED_COMMON #define ASM_OUTPUT_ALIGNED_COMMON(FILE, NAME, SIZE, ALIGN) \ do { \ - fprintf ((FILE), "\t%s\t", COMMON_ASM_OP); \ + fprintf ((FILE), "%s", COMMON_ASM_OP); \ assemble_name ((FILE), (NAME)); \ fprintf ((FILE), ",%u,%u\n", (SIZE), (ALIGN) / BITS_PER_UNIT); \ } while (0) @@ -163,21 +147,21 @@ do { \ the linker seems to want the alignment of data objects to depend on their types. We do exactly that here. */ -#undef ASM_OUTPUT_ALIGNED_LOCAL +#undef ASM_OUTPUT_ALIGNED_LOCAL #define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGN) \ do { \ if ((SIZE) <= g_switch_value) \ sbss_section(); \ else \ bss_section(); \ - fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \ + fprintf (FILE, "%s", TYPE_ASM_OP); \ assemble_name (FILE, NAME); \ putc (',', FILE); \ fprintf (FILE, TYPE_OPERAND_FMT, "object"); \ putc ('\n', FILE); \ if (!flag_inhibit_size_directive) \ { \ - fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ + fprintf (FILE, "%s", SIZE_ASM_OP); \ assemble_name (FILE, NAME); \ fprintf (FILE, ",%d\n", (SIZE)); \ } \ @@ -186,11 +170,6 @@ do { \ ASM_OUTPUT_SKIP((FILE), (SIZE)); \ } while (0) -/* This is the pseudo-op used to generate a 64-bit word of data with a - specific value in some section. */ - -#define INT_ASM_OP ".quad" - /* Biggest alignment supported by the object file format of this machine. Use this macro to limit the alignment which can be specified using the `__attribute__ ((aligned (N)))' construct. If @@ -200,6 +179,7 @@ do { \ we could only potentially get to 2^60 on suitible hosts. Due to other considerations in varasm, we must restrict this to what fits in an int. */ +#undef MAX_OFILE_ALIGNMENT #define MAX_OFILE_ALIGNMENT \ (1 << (HOST_BITS_PER_INT < 64 ? HOST_BITS_PER_INT - 2 : 62)) @@ -207,8 +187,8 @@ do { \ values from a double-quoted string WITHOUT HAVING A TERMINATING NUL AUTOMATICALLY APPENDED. This is the same for most svr4 assemblers. */ -#undef ASCII_DATA_ASM_OP -#define ASCII_DATA_ASM_OP ".ascii" +#undef ASCII_DATA_ASM_OP +#define ASCII_DATA_ASM_OP "\t.ascii\t" /* Support const sections and the ctors and dtors sections for g++. Note that there appears to be two different ways to support const @@ -218,36 +198,18 @@ do { \ EXTRA_SECTIONS, EXTRA_SECTION_FUNCTIONS, SELECT_SECTION, and SELECT_RTX_SECTION. We do both here just to be on the safe side. */ -#undef USE_CONST_SECTION +#undef USE_CONST_SECTION #define USE_CONST_SECTION 1 #undef CONST_SECTION_ASM_OP -#define CONST_SECTION_ASM_OP ".section\t.rodata" +#define CONST_SECTION_ASM_OP "\t.section\t.rodata" -/* Define the pseudo-ops used to switch to the .ctors and .dtors sections. - - Note that we want to give these sections the SHF_WRITE attribute - because these sections will actually contain data (i.e. tables of - addresses of functions in the current root executable or shared library - file) and, in the case of a shared library, the relocatable addresses - will have to be properly resolved/relocated (and then written into) by - the dynamic linker when it actually attaches the given shared library - to the executing process. (Note that on SVR4, you may wish to use the - `-z text' option to the ELF linker, when building a shared library, as - an additional check that you are doing everything right. But if you do - use the `-z text' option when building a shared library, you will get - errors unless the .ctors and .dtors sections are marked as writable - via the SHF_WRITE attribute.) */ - -#undef CTORS_SECTION_ASM_OP -#define CTORS_SECTION_ASM_OP ".section\t.ctors,\"aw\"" -#undef DTORS_SECTION_ASM_OP -#define DTORS_SECTION_ASM_OP ".section\t.dtors,\"aw\"" - -/* Handle the small data sections. */ -#define BSS_SECTION_ASM_OP ".section\t.bss" -#define SBSS_SECTION_ASM_OP ".section\t.sbss,\"aw\"" -#define SDATA_SECTION_ASM_OP ".section\t.sdata,\"aw\"" +#undef BSS_SECTION_ASM_OP +#define BSS_SECTION_ASM_OP "\t.section\t.bss" +#undef SBSS_SECTION_ASM_OP +#define SBSS_SECTION_ASM_OP "\t.section\t.sbss,\"aw\"" +#undef SDATA_SECTION_ASM_OP +#define SDATA_SECTION_ASM_OP "\t.section\t.sdata,\"aw\"" /* On svr4, we *do* have support for the .init and .fini sections, and we can put stuff in there to be executed before and after `main'. We let @@ -256,126 +218,225 @@ do { \ sections. This is the same for all known svr4 assemblers. */ #undef INIT_SECTION_ASM_OP -#define INIT_SECTION_ASM_OP ".section\t.init" +#define INIT_SECTION_ASM_OP "\t.section\t.init" #undef FINI_SECTION_ASM_OP -#define FINI_SECTION_ASM_OP ".section\t.fini" +#define FINI_SECTION_ASM_OP "\t.section\t.fini" + +#ifdef HAVE_GAS_SUBSECTION_ORDERING + +#define ASM_SECTION_START_OP "\t.subsection\t-1" + +/* Output assembly directive to move to the beginning of current section. */ +#define ASM_OUTPUT_SECTION_START(FILE) \ + fprintf ((FILE), "%s\n", ASM_SECTION_START_OP) + +#endif /* A default list of other sections which we might be "in" at any given time. For targets that use additional sections (e.g. .tdesc) you should override this definition in the target-specific file which includes this file. */ -#undef EXTRA_SECTIONS -#define EXTRA_SECTIONS in_const, in_ctors, in_dtors, in_sbss, in_sdata +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_const, in_sbss, in_sdata /* A default list of extra section function definitions. For targets that use additional sections (e.g. .tdesc) you should override this definition in the target-specific file which includes this file. */ -#undef EXTRA_SECTION_FUNCTIONS +#undef EXTRA_SECTION_FUNCTIONS #define EXTRA_SECTION_FUNCTIONS \ CONST_SECTION_FUNCTION \ - SECTION_FUNCTION_TEMPLATE(ctors_section, in_ctors, CTORS_SECTION_ASM_OP) \ - SECTION_FUNCTION_TEMPLATE(dtors_section, in_dtors, DTORS_SECTION_ASM_OP) \ SECTION_FUNCTION_TEMPLATE(sbss_section, in_sbss, SBSS_SECTION_ASM_OP) \ SECTION_FUNCTION_TEMPLATE(sdata_section, in_sdata, SDATA_SECTION_ASM_OP) -#undef READONLY_DATA_SECTION +extern void sbss_section PARAMS ((void)); +extern void sdata_section PARAMS ((void)); + +#undef READONLY_DATA_SECTION #define READONLY_DATA_SECTION() const_section () -extern void text_section (); - -#define CONST_SECTION_FUNCTION \ -void \ -const_section () \ -{ \ - if (!USE_CONST_SECTION) \ - text_section(); \ - else if (in_section != in_const) \ - { \ - fprintf (asm_out_file, "%s\n", CONST_SECTION_ASM_OP); \ - in_section = in_const; \ - } \ +#undef CONST_SECTION_FUNCTION +#define CONST_SECTION_FUNCTION \ +void \ +const_section () \ +{ \ + if (!USE_CONST_SECTION) \ + text_section(); \ + else if (in_section != in_const) \ + { \ + fprintf (asm_out_file, "%s\n", CONST_SECTION_ASM_OP); \ + in_section = in_const; \ + } \ } -#define SECTION_FUNCTION_TEMPLATE(FN, ENUM, OP) \ -void FN () \ -{ \ - if (in_section != ENUM) \ - { \ - fprintf (asm_out_file, "%s\n", OP); \ - in_section = ENUM; \ - } \ +#undef SECTION_FUNCTION_TEMPLATE +#define SECTION_FUNCTION_TEMPLATE(FN, ENUM, OP) \ +void FN () \ +{ \ + if (in_section != ENUM) \ + { \ + fprintf (asm_out_file, "%s\n", OP); \ + in_section = ENUM; \ + } \ } - -/* Switch into a generic section. - This is currently only used to support section attributes. - - We make the section read-only and executable for a function decl, - read-only for a const data decl, and writable for a non-const data decl. */ -#define ASM_OUTPUT_SECTION_NAME(FILE, DECL, NAME, RELOC) \ - fprintf (FILE, ".section\t%s,\"%s\",@progbits\n", NAME, \ - (DECL) && TREE_CODE (DECL) == FUNCTION_DECL ? "ax" : \ - (DECL) && DECL_READONLY_SECTION (DECL, RELOC) ? "a" : "aw") - - -/* A C statement (sans semicolon) to output an element in the table of - global constructors. */ -#undef ASM_OUTPUT_CONSTRUCTOR -#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \ - do { \ - ctors_section (); \ - fprintf (FILE, "\t%s\t ", INT_ASM_OP); \ - assemble_name (FILE, NAME); \ - fprintf (FILE, "\n"); \ - } while (0) - -/* A C statement (sans semicolon) to output an element in the table of - global destructors. */ -#undef ASM_OUTPUT_DESTRUCTOR -#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \ - do { \ - dtors_section (); \ - fprintf (FILE, "\t%s\t ", INT_ASM_OP); \ - assemble_name (FILE, NAME); \ - fprintf (FILE, "\n"); \ - } while (0) +/* Switch into a generic section. */ +#define TARGET_ASM_NAMED_SECTION default_elf_asm_named_section /* A C statement or statements to switch to the appropriate section for output of DECL. DECL is either a `VAR_DECL' node or a constant of some sort. RELOC indicates whether forming - the initial value of DECL requires link-time relocations. */ + the initial value of DECL requires link-time relocations. -#define SELECT_SECTION(DECL,RELOC) \ -{ \ - if (TREE_CODE (DECL) == STRING_CST) \ + Set SECNUM to: + 0 .text + 1 .rodata + 2 .data + 3 .sdata + 4 .bss + 5 .sbss +*/ + +#define DO_SELECT_SECTION(SECNUM, DECL, RELOC) \ + do \ + { \ + HOST_WIDE_INT size; \ + SECNUM = 1; \ + if (TREE_CODE (DECL) == FUNCTION_DECL) \ + { \ + SECNUM = 0; \ + break; \ + } \ + else if (TREE_CODE (DECL) == STRING_CST) \ + { \ + if (flag_writable_strings) \ + SECNUM = 2; \ + else \ + SECNUM = 0x101; \ + break; \ + } \ + else if (TREE_CODE (DECL) == VAR_DECL) \ + { \ + if (DECL_INITIAL (DECL) == NULL \ + || DECL_INITIAL (DECL) == error_mark_node) \ + SECNUM = 4; \ + else if ((flag_pic && RELOC) \ + || ! TREE_READONLY (DECL) \ + || TREE_SIDE_EFFECTS (DECL) \ + || ! TREE_CONSTANT (DECL_INITIAL (DECL))) \ + SECNUM = 2; \ + else if (flag_merge_constants >= 2) \ + { \ + /* C and C++ don't allow different variables to \ + share the same location. -fmerge-all-constants\ + allows even that (at the expense of not \ + conforming). */ \ + if (TREE_CODE (DECL_INITIAL (DECL)) == STRING_CST)\ + SECNUM = 0x201; \ + else \ + SECNUM = 0x301; \ + } \ + } \ + else if (TREE_CODE (DECL) == CONSTRUCTOR) \ + { \ + if ((flag_pic && RELOC) \ + || TREE_SIDE_EFFECTS (DECL) \ + || ! TREE_CONSTANT (DECL)) \ + SECNUM = 2; \ + } \ + \ + /* Select small data sections based on size. */ \ + size = int_size_in_bytes (TREE_TYPE (DECL)); \ + if (size >= 0 && size <= g_switch_value) \ + { \ + if ((SECNUM & 0xff) >= 2) \ + SECNUM += 1; \ + /* Move readonly data to .sdata only if -msmall-data. */ \ + /* ??? Consider .sdata.{lit4,lit8} as \ + SHF_MERGE|SHF_ALPHA_GPREL. */ \ + else if (TARGET_SMALL_DATA) \ + SECNUM = 3; \ + } \ + } \ + while (0) + +#undef SELECT_SECTION +#define SELECT_SECTION(DECL, RELOC, ALIGN) \ + do \ + { \ + typedef void (*sec_fn) PARAMS ((void)); \ + static sec_fn const sec_functions[6] = \ + { \ + text_section, \ + const_section, \ + data_section, \ + sdata_section, \ + bss_section, \ + sbss_section \ + }; \ + \ + int sec; \ + \ + DO_SELECT_SECTION (sec, DECL, RELOC); \ + \ + switch (sec) \ + { \ + case 0x101: \ + mergeable_string_section (DECL, ALIGN, 0); \ + break; \ + case 0x201: \ + mergeable_string_section (DECL_INITIAL (DECL),\ + ALIGN, 0); \ + break; \ + case 0x301: \ + mergeable_constant_section (DECL_MODE (DECL), \ + ALIGN, 0); \ + break; \ + default: \ + (*sec_functions[sec]) (); \ + break; \ + } \ + } \ + while (0) + +#define MAKE_DECL_ONE_ONLY(DECL) (DECL_WEAK (DECL) = 1) + +#undef UNIQUE_SECTION +#define UNIQUE_SECTION(DECL, RELOC) \ + do \ { \ - if (! flag_writable_strings) \ - const_section (); \ - else \ - data_section (); \ + static const char * const prefixes[6][2] = \ + { \ + { ".text.", ".gnu.linkonce.t." }, \ + { ".rodata.", ".gnu.linkonce.r." }, \ + { ".data.", ".gnu.linkonce.d." }, \ + { ".sdata.", ".gnu.linkonce.s." }, \ + { ".bss.", ".gnu.linkonce.b." }, \ + { ".sbss.", ".gnu.linkonce.sb." } \ + }; \ + \ + int nlen, plen, sec; \ + const char *name, *prefix; \ + char *string; \ + \ + DO_SELECT_SECTION (sec, DECL, RELOC); \ + \ + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (DECL)); \ + STRIP_NAME_ENCODING (name, name); \ + nlen = strlen (name); \ + \ + prefix = prefixes[sec & 0xff][DECL_ONE_ONLY(DECL)]; \ + plen = strlen (prefix); \ + \ + string = alloca (nlen + plen + 1); \ + \ + memcpy (string, prefix, plen); \ + memcpy (string + plen, name, nlen + 1); \ + \ + DECL_SECTION_NAME (DECL) = build_string (nlen + plen, string); \ } \ - else if (TREE_CODE (DECL) == VAR_DECL) \ - { \ - if ((flag_pic && RELOC) \ - || !TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL) \ - || !DECL_INITIAL (DECL) \ - || (DECL_INITIAL (DECL) != error_mark_node \ - && !TREE_CONSTANT (DECL_INITIAL (DECL)))) \ - { \ - int size = int_size_in_bytes (TREE_TYPE (DECL)); \ - if (size >= 0 && size <= g_switch_value) \ - sdata_section (); \ - else \ - data_section (); \ - } \ - else \ - const_section (); \ - } \ - else \ - const_section (); \ -} + while (0) /* A C statement or statements to switch to the appropriate section for output of RTX in mode MODE. RTX is some kind @@ -383,8 +444,15 @@ void FN () \ in the case of a `const_int' rtx. Currently, these always go into the const section. */ -#undef SELECT_RTX_SECTION -#define SELECT_RTX_SECTION(MODE,RTX) const_section() +#undef SELECT_RTX_SECTION +#define SELECT_RTX_SECTION(MODE, RTX, ALIGN) \ +do { \ + if (TARGET_SMALL_DATA && GET_MODE_SIZE (MODE) <= g_switch_value) \ + /* ??? Consider .sdata.{lit4,lit8} as SHF_MERGE|SHF_ALPHA_GPREL. */ \ + sdata_section (); \ + else \ + mergeable_constant_section((MODE), (ALIGN), 0); \ +} while (0) /* Define the strings used for the special svr4 .type and .size directives. These strings generally do not vary from one system running svr4 to @@ -393,20 +461,21 @@ void FN () \ file which includes this one. */ #undef TYPE_ASM_OP -#define TYPE_ASM_OP ".type" +#define TYPE_ASM_OP "\t.type\t" #undef SIZE_ASM_OP -#define SIZE_ASM_OP ".size" +#define SIZE_ASM_OP "\t.size\t" /* This is how we tell the assembler that a symbol is weak. */ #undef ASM_WEAKEN_LABEL -#define ASM_WEAKEN_LABEL(FILE,NAME) \ +#define ASM_WEAKEN_LABEL(FILE, NAME) \ do { fputs ("\t.weak\t", FILE); assemble_name (FILE, NAME); \ fputc ('\n', FILE); } while (0) /* This is how we tell the assembler that two symbols have the same value. */ -#define ASM_OUTPUT_DEF(FILE,NAME1,NAME2) \ +#undef ASM_OUTPUT_DEF +#define ASM_OUTPUT_DEF(FILE, NAME1, NAME2) \ do { assemble_name(FILE, NAME1); \ fputs(" = ", FILE); \ assemble_name(FILE, NAME2); \ @@ -418,6 +487,7 @@ void FN () \ is just a default. You may need to override it in your machine- specific tm.h file (depending upon the particulars of your assembler). */ +#undef TYPE_OPERAND_FMT #define TYPE_OPERAND_FMT "@%s" /* Write the extra assembler code needed to declare a function's result. @@ -436,22 +506,27 @@ void FN () \ /* Write the extra assembler code needed to declare an object properly. */ #undef ASM_DECLARE_OBJECT_NAME -#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \ - do { \ - fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \ - assemble_name (FILE, NAME); \ - putc (',', FILE); \ - fprintf (FILE, TYPE_OPERAND_FMT, "object"); \ - putc ('\n', FILE); \ - size_directive_output = 0; \ - if (!flag_inhibit_size_directive && DECL_SIZE (DECL)) \ - { \ - size_directive_output = 1; \ - fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ - assemble_name (FILE, NAME); \ - fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \ - } \ - ASM_OUTPUT_LABEL(FILE, NAME); \ +#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \ + do { \ + HOST_WIDE_INT size; \ + fprintf (FILE, "%s", TYPE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, TYPE_OPERAND_FMT, "object"); \ + putc ('\n', FILE); \ + size_directive_output = 0; \ + if (!flag_inhibit_size_directive \ + && DECL_SIZE (DECL) \ + && (size = int_size_in_bytes (TREE_TYPE (DECL))) > 0) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "%s", SIZE_ASM_OP); \ + assemble_name (FILE, NAME); \ + fputc (',', FILE); \ + fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, size); \ + fputc ('\n', FILE); \ + } \ + ASM_OUTPUT_LABEL(FILE, NAME); \ } while (0) /* Output the size directive for a decl in rest_of_decl_compilation @@ -462,22 +537,24 @@ void FN () \ #undef ASM_FINISH_DECLARE_OBJECT #define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END) \ -do { \ - char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \ - if (!flag_inhibit_size_directive && DECL_SIZE (DECL) \ - && ! AT_END && TOP_LEVEL \ - && DECL_INITIAL (DECL) == error_mark_node \ - && !size_directive_output) \ - { \ - size_directive_output = 1; \ - fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \ - assemble_name (FILE, name); \ - putc (',', FILE); \ - fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, \ - int_size_in_bytes (TREE_TYPE (DECL))); \ - putc ('\n', FILE); \ - } \ -} while (0) + do { \ + const char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \ + HOST_WIDE_INT size; \ + if (!flag_inhibit_size_directive \ + && DECL_SIZE (DECL) \ + && ! AT_END && TOP_LEVEL \ + && DECL_INITIAL (DECL) == error_mark_node \ + && !size_directive_output \ + && (size = int_size_in_bytes (TREE_TYPE (DECL))) > 0) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "%s", SIZE_ASM_OP); \ + assemble_name (FILE, name); \ + fputc (',', FILE); \ + fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, size); \ + fputc ('\n', FILE); \ + } \ + } while (0) /* A table of bytes codes used by the ASM_OUTPUT_ASCII and ASM_OUTPUT_LIMITED_STRING macros. Each byte in the table @@ -493,6 +570,7 @@ do { \ the i386) don't know about that. Also, we don't use \v since some versions of gas, such as 2.2 did not accept it. */ +#undef ESCAPES #define ESCAPES \ "\1\1\1\1\1\1\1\1btn\1fr\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\ \0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ @@ -514,29 +592,29 @@ do { \ If your target assembler doesn't support the .string directive, you should define this to zero. */ +#undef STRING_LIMIT #define STRING_LIMIT ((unsigned) 256) #undef STRING_ASM_OP -#define STRING_ASM_OP ".string" +#define STRING_ASM_OP "\t.string\t" /* GAS is the only Alpha/ELF assembler. */ -#undef TARGET_GAS +#undef TARGET_GAS #define TARGET_GAS (1) /* Provide a STARTFILE_SPEC appropriate for ELF. Here we add the (even more) magical crtbegin.o file which provides part of the - support for getting C++ file-scope static object constructed before - entering `main'. + support for getting C++ file-scope static object constructed + before entering `main'. - Don't bother seeing crtstuff.c -- there is absolutely no hope of - getting that file to understand multiple GPs. GNU Libc provides a - hand-coded version that is used on Linux; it could be copied here - if there is ever a need. */ + Don't bother seeing crtstuff.c -- there is absolutely no hope + of getting that file to understand multiple GPs. We provide a + hand-coded assembly version. */ #undef STARTFILE_SPEC #define STARTFILE_SPEC \ "%{!shared: \ %{pg:gcrt1.o%s} %{!pg:%{p:gcrt1.o%s} %{!p:crt1.o%s}}}\ - crti.o%s crtbegin.o%s" + crti.o%s %{shared:crtbeginS.o%s}%{!shared:crtbegin.o%s}" /* Provide a ENDFILE_SPEC appropriate for ELF. Here we tack on the magical crtend.o file which provides part of the support for @@ -545,13 +623,49 @@ do { \ #undef ENDFILE_SPEC #define ENDFILE_SPEC \ - "crtend.o%s crtn.o%s" + "%{ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \ + %{shared:crtendS.o%s}%{!shared:crtend.o%s} crtn.o%s" /* We support #pragma. */ #define HANDLE_SYSV_PRAGMA -/* Undo the auto-alignment stuff from alpha.h. ELF has unaligned data - pseudos natively. */ -#undef UNALIGNED_SHORT_ASM_OP -#undef UNALIGNED_INT_ASM_OP -#undef UNALIGNED_DOUBLE_INT_ASM_OP +/* Select a format to encode pointers in exception handling data. CODE + is 0 for data, 1 for code labels, 2 for function pointers. GLOBAL is + true if the symbol may be affected by dynamic relocations. + + Since application size is already constrained to <2GB by the form of + the ldgp relocation, we can use a 32-bit pc-relative relocation to + static data. Dynamic data is accessed indirectly to allow for read + only EH sections. */ +#define ASM_PREFERRED_EH_DATA_FORMAT(CODE,GLOBAL) \ + (((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_pcrel | DW_EH_PE_sdata4) + +/* If defined, a C statement to be executed just prior to the output of + assembler code for INSN. */ +#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \ + (alpha_this_literal_sequence_number = 0, \ + alpha_this_gpdisp_sequence_number = 0) +extern int alpha_this_literal_sequence_number; +extern int alpha_this_gpdisp_sequence_number; + +/* Since the bits of the _init and _fini function is spread across + many object files, each potentially with its own GP, we must assume + we need to load our GP. Further, the .init/.fini section can + easily be more than 4MB away from the function to call so we can't + use bsr. */ +#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ + asm (SECTION_OP "\n" \ +" br $29,1f\n" \ +"1: ldgp $29,0($29)\n" \ +" unop\n" \ +" jsr $26," USER_LABEL_PREFIX #FUNC "\n" \ +" .align 3\n" \ +" .previous"); + +/* If we have the capability create headers for efficient EH lookup. + As of Jan 2002, only glibc 2.2.4 can actually make use of this, but + I imagine that other systems will catch up. In the meantime, it + doesn't harm to make sure that the data exists to be used later. */ +#if defined(HAVE_LD_EH_FRAME_HDR) +#define LINK_EH_SPEC "%{!static:--eh-frame-hdr} " +#endif