X-Git-Url: https://gcc.gnu.org/git/?a=blobdiff_plain;f=gcc%2Fconfig%2Falpha%2Falpha.c;h=76f399e3c6e15119f1d5eb7ef5893763b50ec392;hb=307b599c91ee93049fd69228b9d59b6ea2e030ef;hp=a4ca0df7c6c0f70d99f7c3e26d9bc41a6be4b929;hpb=30864e1408a8663cf6f2b4c7cd07fce1fd13128f;p=gcc.git diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c index a4ca0df7c6c0..76f399e3c6e1 100644 --- a/gcc/config/alpha/alpha.c +++ b/gcc/config/alpha/alpha.c @@ -1,6 +1,6 @@ /* Subroutines used for code generation on the DEC Alpha. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001 Free Software Foundation, Inc. + 2000, 2001, 2002, 2003 Free Software Foundation, Inc. Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) This file is part of GNU CC. @@ -23,6 +23,8 @@ Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "rtl.h" #include "tree.h" #include "regs.h" @@ -46,9 +48,9 @@ Boston, MA 02111-1307, USA. */ #include "tm_p.h" #include "target.h" #include "target-def.h" - -/* External data. */ -extern int rtx_equal_function_value_matters; +#include "debug.h" +#include "langhooks.h" +#include /* Specify which cpu to schedule for. */ @@ -70,6 +72,10 @@ enum alpha_fp_rounding_mode alpha_fprm; enum alpha_fp_trap_mode alpha_fptm; +/* Specify bit size of immediate TLS offsets. */ + +int alpha_tls_size = 32; + /* Strings decoded into the above options. */ const char *alpha_cpu_string; /* -mcpu= */ @@ -78,13 +84,14 @@ 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] */ const char *alpha_mlat_string; /* -mmemory-latency= */ +const char *alpha_tls_size_string; /* -mtls-size=[16|32|64] */ /* Save information from a "cmpxx" operation until the branch or scc is emitted. */ struct alpha_compare alpha_compare; -/* Non-zero if inside of a function, because the Alpha asm can't +/* Nonzero if inside of a function, because the Alpha asm can't handle .files inside of functions. */ static int inside_function = FALSE; @@ -99,25 +106,92 @@ static int alpha_function_needs_gp; /* The alias set for prologue/epilogue register save/restore. */ -static int alpha_sr_alias_set; +static GTY(()) int alpha_sr_alias_set; /* The assembler name of the current function. */ static const char *alpha_fnname; /* The next explicit relocation sequence number. */ +extern GTY(()) int alpha_next_sequence_number; int alpha_next_sequence_number = 1; /* The literal and gpdisp sequence numbers for this insn, as printed by %# and %* respectively. */ +extern GTY(()) int alpha_this_literal_sequence_number; +extern GTY(()) int alpha_this_gpdisp_sequence_number; int alpha_this_literal_sequence_number; int alpha_this_gpdisp_sequence_number; +/* Costs of various operations on the different architectures. */ + +struct alpha_rtx_cost_data +{ + unsigned char fp_add; + unsigned char fp_mult; + unsigned char fp_div_sf; + unsigned char fp_div_df; + unsigned char int_mult_si; + unsigned char int_mult_di; + unsigned char int_shift; + unsigned char int_cmov; +}; + +static struct alpha_rtx_cost_data const alpha_rtx_cost_data[PROCESSOR_MAX] = +{ + { /* EV4 */ + COSTS_N_INSNS (6), /* fp_add */ + COSTS_N_INSNS (6), /* fp_mult */ + COSTS_N_INSNS (34), /* fp_div_sf */ + COSTS_N_INSNS (63), /* fp_div_df */ + COSTS_N_INSNS (23), /* int_mult_si */ + COSTS_N_INSNS (23), /* int_mult_di */ + COSTS_N_INSNS (2), /* int_shift */ + COSTS_N_INSNS (2), /* int_cmov */ + }, + { /* EV5 */ + COSTS_N_INSNS (4), /* fp_add */ + COSTS_N_INSNS (4), /* fp_mult */ + COSTS_N_INSNS (15), /* fp_div_sf */ + COSTS_N_INSNS (22), /* fp_div_df */ + COSTS_N_INSNS (8), /* int_mult_si */ + COSTS_N_INSNS (12), /* int_mult_di */ + COSTS_N_INSNS (1) + 1, /* int_shift */ + COSTS_N_INSNS (1), /* int_cmov */ + }, + { /* EV6 */ + COSTS_N_INSNS (4), /* fp_add */ + COSTS_N_INSNS (4), /* fp_mult */ + COSTS_N_INSNS (12), /* fp_div_sf */ + COSTS_N_INSNS (15), /* fp_div_df */ + COSTS_N_INSNS (7), /* int_mult_si */ + COSTS_N_INSNS (7), /* int_mult_di */ + COSTS_N_INSNS (1), /* int_shift */ + COSTS_N_INSNS (2), /* int_cmov */ + }, +}; + /* Declarations of static functions. */ -static bool decl_in_text_section +static bool alpha_function_ok_for_sibcall + PARAMS ((tree, tree)); +static int tls_symbolic_operand_1 + PARAMS ((rtx, enum machine_mode, int, int)); +static enum tls_model tls_symbolic_operand_type + PARAMS ((rtx)); +static bool decl_has_samegp + PARAMS ((tree)); +static bool alpha_in_small_data_p PARAMS ((tree)); -static bool local_symbol_p +static rtx get_tls_get_addr + PARAMS ((void)); +static int some_small_symbolic_operand_1 + PARAMS ((rtx *, void *)); +static int split_small_symbolic_operand_1 + PARAMS ((rtx *, void *)); +static bool alpha_cannot_copy_insn_p PARAMS ((rtx)); +static bool alpha_rtx_costs + PARAMS ((rtx, int, int, int *)); static void alpha_set_memflags_1 PARAMS ((rtx, int, int, int)); static rtx alpha_emit_set_const_1 @@ -126,9 +200,13 @@ 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_init_builtins + PARAMS ((void)); +static rtx alpha_expand_builtin + PARAMS ((tree, rtx, rtx, enum machine_mode, int)); static void alpha_sa_mask PARAMS ((unsigned long *imaskP, unsigned long *fmaskP)); -static int find_lo_sum +static int find_lo_sum_using_gp PARAMS ((rtx *, void *)); static int alpha_does_function_need_gp PARAMS ((void)); @@ -138,6 +216,10 @@ static const char *get_trap_mode_suffix PARAMS ((void)); static const char *get_round_mode_suffix PARAMS ((void)); +static const char *get_some_local_dynamic_name + PARAMS ((void)); +static int get_some_local_dynamic_name_1 + PARAMS ((rtx *, void *)); static rtx set_frame_related_p PARAMS ((void)); static const char *alpha_lookup_xfloating_lib_func @@ -154,18 +236,35 @@ 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)); +static int alpha_use_dfa_pipeline_interface + PARAMS ((void)); +static int alpha_multipass_dfa_lookahead + PARAMS ((void)); +static void alpha_reorg + PARAMS ((void)); -#if TARGET_ABI_UNICOSMK -static void alpha_init_machine_status - PARAMS ((struct function *p)); -static void alpha_mark_machine_status - PARAMS ((struct function *p)); -static void alpha_free_machine_status - PARAMS ((struct function *p)); +#ifdef OBJECT_FORMAT_ELF +static void alpha_elf_select_rtx_section + PARAMS ((enum machine_mode, rtx, unsigned HOST_WIDE_INT)); +#endif + +#if TARGET_ABI_OPEN_VMS +static bool alpha_linkage_symbol_p + PARAMS ((const char *symname)); +static int alpha_write_one_linkage + PARAMS ((splay_tree_node, void *)); +static void alpha_write_linkage + PARAMS ((FILE *, const char *, tree)); +#endif + +#if TARGET_ABI_OSF +static void alpha_output_mi_thunk_osf + PARAMS ((FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree)); #endif +static struct machine_function * alpha_init_machine_status + PARAMS ((void)); + static void unicosmk_output_deferred_case_vectors PARAMS ((FILE *)); static void unicosmk_gen_dsib PARAMS ((unsigned long *imaskP)); static void unicosmk_output_ssib PARAMS ((FILE *, const char *)); @@ -194,15 +293,44 @@ static void vms_asm_out_destructor PARAMS ((rtx, int)); # define TARGET_SECTION_TYPE_FLAGS vms_section_type_flags #endif +#undef TARGET_IN_SMALL_DATA_P +#define TARGET_IN_SMALL_DATA_P alpha_in_small_data_p + #if TARGET_ABI_UNICOSMK static void unicosmk_asm_named_section PARAMS ((const char *, unsigned int)); static void unicosmk_insert_attributes PARAMS ((tree, tree *)); static unsigned int unicosmk_section_type_flags PARAMS ((tree, const char *, int)); +static void unicosmk_unique_section PARAMS ((tree, int)); # undef TARGET_INSERT_ATTRIBUTES # define TARGET_INSERT_ATTRIBUTES unicosmk_insert_attributes # undef TARGET_SECTION_TYPE_FLAGS # define TARGET_SECTION_TYPE_FLAGS unicosmk_section_type_flags +# undef TARGET_ASM_UNIQUE_SECTION +# define TARGET_ASM_UNIQUE_SECTION unicosmk_unique_section +# undef TARGET_ASM_GLOBALIZE_LABEL +# define TARGET_ASM_GLOBALIZE_LABEL hook_FILEptr_constcharptr_void +#endif + +#undef TARGET_ASM_ALIGNED_HI_OP +#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" +#undef TARGET_ASM_ALIGNED_DI_OP +#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t" + +/* Default unaligned ops are provided for ELF systems. To get unaligned + data for non-ELF systems, we have to turn off auto alignment. */ +#ifndef OBJECT_FORMAT_ELF +#undef TARGET_ASM_UNALIGNED_HI_OP +#define TARGET_ASM_UNALIGNED_HI_OP "\t.align 0\n\t.word\t" +#undef TARGET_ASM_UNALIGNED_SI_OP +#define TARGET_ASM_UNALIGNED_SI_OP "\t.align 0\n\t.long\t" +#undef TARGET_ASM_UNALIGNED_DI_OP +#define TARGET_ASM_UNALIGNED_DI_OP "\t.align 0\n\t.quad\t" +#endif + +#ifdef OBJECT_FORMAT_ELF +#undef TARGET_ASM_SELECT_RTX_SECTION +#define TARGET_ASM_SELECT_RTX_SECTION alpha_elf_select_rtx_section #endif #undef TARGET_ASM_FUNCTION_END_PROLOGUE @@ -212,8 +340,40 @@ static unsigned int unicosmk_section_type_flags PARAMS ((tree, const char *, #define TARGET_SCHED_ADJUST_COST alpha_adjust_cost #undef TARGET_SCHED_ISSUE_RATE #define TARGET_SCHED_ISSUE_RATE alpha_issue_rate -#undef TARGET_SCHED_VARIABLE_ISSUE -#define TARGET_SCHED_VARIABLE_ISSUE alpha_variable_issue +#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE +#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE \ + alpha_use_dfa_pipeline_interface +#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD +#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \ + alpha_multipass_dfa_lookahead + +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS HAVE_AS_TLS + +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS alpha_init_builtins +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN alpha_expand_builtin + +#undef TARGET_FUNCTION_OK_FOR_SIBCALL +#define TARGET_FUNCTION_OK_FOR_SIBCALL alpha_function_ok_for_sibcall +#undef TARGET_CANNOT_COPY_INSN_P +#define TARGET_CANNOT_COPY_INSN_P alpha_cannot_copy_insn_p + +#if TARGET_ABI_OSF +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK alpha_output_mi_thunk_osf +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true +#endif + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS alpha_rtx_costs +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST hook_int_rtx_0 + +#undef TARGET_MACHINE_DEPENDENT_REORG +#define TARGET_MACHINE_DEPENDENT_REORG alpha_reorg struct gcc_target targetm = TARGET_INITIALIZER; @@ -334,6 +494,18 @@ override_options () error ("bad value `%s' for -mfp-trap-mode switch", alpha_fptm_string); } + if (alpha_tls_size_string) + { + if (strcmp (alpha_tls_size_string, "16") == 0) + alpha_tls_size = 16; + else if (strcmp (alpha_tls_size_string, "32") == 0) + alpha_tls_size = 32; + else if (strcmp (alpha_tls_size_string, "64") == 0) + alpha_tls_size = 64; + else + error ("bad value `%s' for -mtls-size switch", alpha_tls_size_string); + } + alpha_cpu = TARGET_CPU_DEFAULT & MASK_CPU_EV6 ? PROCESSOR_EV6 : (TARGET_CPU_DEFAULT & MASK_CPU_EV5 ? PROCESSOR_EV5 : PROCESSOR_EV4); @@ -476,12 +648,16 @@ override_options () /* Register variables and functions with the garbage collector. */ -#if TARGET_ABI_UNICOSMK /* Set up function hooks. */ init_machine_status = alpha_init_machine_status; - mark_machine_status = alpha_mark_machine_status; - free_machine_status = alpha_free_machine_status; -#endif + + /* Tell the compiler when we're using VAX floating point. */ + if (TARGET_FLOAT_VAX) + { + real_format_for_mode[SFmode - QFmode] = &vax_f_format; + real_format_for_mode[DFmode - QFmode] = &vax_g_format; + real_format_for_mode[TFmode - QFmode] = NULL; + } } /* Returns 1 if VALUE is a mask that contains full bytes of zero or ones. */ @@ -508,7 +684,7 @@ reg_or_0_operand (op, mode) register rtx op; enum machine_mode mode; { - return op == const0_rtx || register_operand (op, mode); + return op == CONST0_RTX (mode) || register_operand (op, mode); } /* Return 1 if OP is a constant in the range of 0-63 (for a shift) or @@ -537,6 +713,16 @@ reg_or_8bit_operand (op, mode) || register_operand (op, mode)); } +/* Return 1 if OP is a constant or any register. */ + +int +reg_or_const_int_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return GET_CODE (op) == CONST_INT || register_operand (op, mode); +} + /* Return 1 if OP is an 8-bit constant. */ int @@ -643,24 +829,26 @@ mode_mask_operand (op, mode) register rtx op; enum machine_mode mode ATTRIBUTE_UNUSED; { -#if HOST_BITS_PER_WIDE_INT == 32 - if (GET_CODE (op) == CONST_DOUBLE) - return (CONST_DOUBLE_LOW (op) == -1 - && (CONST_DOUBLE_HIGH (op) == -1 - || CONST_DOUBLE_HIGH (op) == 0)); -#else - if (GET_CODE (op) == CONST_DOUBLE) - return (CONST_DOUBLE_LOW (op) == -1 && CONST_DOUBLE_HIGH (op) == 0); -#endif + if (GET_CODE (op) == CONST_INT) + { + HOST_WIDE_INT value = INTVAL (op); - return (GET_CODE (op) == CONST_INT - && (INTVAL (op) == 0xff - || INTVAL (op) == 0xffff - || INTVAL (op) == (HOST_WIDE_INT)0xffffffff -#if HOST_BITS_PER_WIDE_INT == 64 - || INTVAL (op) == -1 -#endif - )); + if (value == 0xff) + return 1; + if (value == 0xffff) + return 1; + if (value == 0xffffffff) + return 1; + if (value == -1) + return 1; + } + else if (HOST_BITS_PER_WIDE_INT == 32 && GET_CODE (op) == CONST_DOUBLE) + { + if (CONST_DOUBLE_LOW (op) == 0xffffffff && CONST_DOUBLE_HIGH (op) == 0) + return 1; + } + + return 0; } /* Return 1 if OP is a multiple of 8 less than 64. */ @@ -675,25 +863,14 @@ mul8_operand (op, mode) && (INTVAL (op) & 7) == 0); } -/* Return 1 if OP is the constant zero in floating-point. */ +/* Return 1 if OP is the zero constant for MODE. */ int -fp0_operand (op, mode) +const0_operand (op, mode) register rtx op; enum machine_mode mode; { - return (GET_MODE (op) == mode - && GET_MODE_CLASS (mode) == MODE_FLOAT && op == CONST0_RTX (mode)); -} - -/* Return 1 if OP is the floating-point constant zero or a register. */ - -int -reg_or_fp0_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return fp0_operand (op, mode) || register_operand (op, mode); + return op == CONST0_RTX (mode); } /* Return 1 if OP is a hard floating-point register. */ @@ -751,8 +928,15 @@ 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 REG: + case MEM: + case CONST_INT: + case CONST_DOUBLE: + case CONST_VECTOR: + case LABEL_REF: + case SYMBOL_REF: + case CONST: + case HIGH: return 1; case SUBREG: @@ -800,11 +984,24 @@ input_operand (op, mode) case SYMBOL_REF: case CONST: if (TARGET_EXPLICIT_RELOCS) - return 0; + { + /* We don't split symbolic operands into something unintelligable + until after reload, but we do not wish non-small, non-global + symbolic operands to be reconstructed from their high/lo_sum + form. */ + return (small_symbolic_operand (op, mode) + || global_symbolic_operand (op, mode) + || gotdtp_symbolic_operand (op, mode) + || gottp_symbolic_operand (op, mode)); + } /* This handles both the Windows/NT and OSF cases. */ return mode == ptr_mode || mode == DImode; + case HIGH: + return (TARGET_EXPLICIT_RELOCS + && local_symbolic_operand (XEXP (op, 0), mode)); + case REG: case ADDRESSOF: return 1; @@ -818,7 +1015,8 @@ input_operand (op, mode) && general_operand (op, mode)); case CONST_DOUBLE: - return GET_MODE_CLASS (mode) == MODE_FLOAT && op == CONST0_RTX (mode); + case CONST_VECTOR: + return op == CONST0_RTX (mode); case CONST_INT: return mode == QImode || mode == HImode || add_operand (op, mode); @@ -837,25 +1035,32 @@ input_operand (op, mode) file, and in the same section as the current function. */ int -current_file_function_operand (op, mode) +samegp_function_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED; { if (GET_CODE (op) != SYMBOL_REF) - return 0; + return false; /* Easy test for recursion. */ if (op == XEXP (DECL_RTL (current_function_decl), 0)) - return 1; + return true; - /* Otherwise, we need the DECL for the SYMBOL_REF, which we can't get. - So SYMBOL_REF_FLAG has been declared to imply that the function is - in the default text section. So we must also check that the current - function is also in the text section. */ - if (SYMBOL_REF_FLAG (op) && decl_in_text_section (current_function_decl)) - return 1; + /* Functions that are not local can be overridden, and thus may + not share the same gp. */ + if (! SYMBOL_REF_LOCAL_P (op)) + return false; - return 0; + /* If -msmall-data is in effect, assume that there is only one GP + for the module, and so any local symbol has this property. We + need explicit relocations to be able to enforce this for symbols + not defined in this unit of translation, however. */ + if (TARGET_EXPLICIT_RELOCS && TARGET_SMALL_DATA) + return true; + + /* Functions that are not external are defined in this UoT, + and thus must share the same gp. */ + return ! SYMBOL_REF_EXTERNAL_P (op); } /* Return 1 if OP is a SYMBOL_REF for which we can make a call via bsr. */ @@ -865,43 +1070,55 @@ 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; + tree op_decl, cfun_sec, op_sec; + + /* Must share the same GP. */ + if (!samegp_function_operand (op, mode)) + return false; /* If profiling is implemented via linker tricks, we can't jump - to the nogp alternate entry point. */ + to the nogp alternate entry point. Note that current_function_profile + would not be correct, since that doesn't indicate if the target + function uses profiling. */ /* ??? TARGET_PROFILING_NEEDS_GP isn't really the right test, but is approximately correct for the OSF ABIs. Don't know what to do for VMS, NT, or UMK. */ - if (! TARGET_PROFILING_NEEDS_GP - && ! profile_flag) - return 0; - - return 1; -} + if (!TARGET_PROFILING_NEEDS_GP && profile_flag) + return false; -/* Return true if OP is a LABEL_REF, or SYMBOL_REF or CONST referencing - a variable known to be defined in this file. */ + /* Must be a function. In some cases folks create thunks in static + data structures and then make calls to them. If we allow the + direct call, we'll get an error from the linker about !samegp reloc + against a symbol without a .prologue directive. */ + if (!SYMBOL_REF_FUNCTION_P (op)) + return false; + + /* Must be "near" so that the branch is assumed to reach. With + -msmall-text, this is assumed true of all local symbols. Since + we've already checked samegp, locality is already assured. */ + if (TARGET_SMALL_TEXT) + return true; -static bool -local_symbol_p (op) - rtx op; -{ - const char *str = XSTR (op, 0); + /* Otherwise, a decl is "near" if it is defined in the same section. */ + if (flag_function_sections) + return false; - /* ??? SYMBOL_REF_FLAG is set for local function symbols, but we - run into problems with the rtl inliner in that the symbol was - once external, but is local after inlining, which results in - unrecognizable insns. */ + op_decl = SYMBOL_REF_DECL (op); + if (DECL_ONE_ONLY (current_function_decl) + || (op_decl && DECL_ONE_ONLY (op_decl))) + return false; - return (CONSTANT_POOL_ADDRESS_P (op) - /* If @, then ENCODE_SECTION_INFO sez it's local. */ - || str[0] == '@' - /* If *$, then ASM_GENERATE_INTERNAL_LABEL sez it's local. */ - || (str[0] == '*' && str[1] == '$')); + cfun_sec = DECL_SECTION_NAME (current_function_decl); + op_sec = op_decl ? DECL_SECTION_NAME (op_decl) : NULL; + return ((!cfun_sec && !op_sec) + || (cfun_sec && op_sec + && strcmp (TREE_STRING_POINTER (cfun_sec), + TREE_STRING_POINTER (op_sec)) == 0)); } +/* Return true if OP is a LABEL_REF, or SYMBOL_REF or CONST referencing + a (non-tls) variable known to be defined in this file. */ + int local_symbolic_operand (op, mode) rtx op; @@ -921,7 +1138,7 @@ local_symbolic_operand (op, mode) if (GET_CODE (op) != SYMBOL_REF) return 0; - return local_symbol_p (op); + return SYMBOL_REF_LOCAL_P (op) && !SYMBOL_REF_TLS_MODEL (op); } /* Return true if OP is a SYMBOL_REF or CONST referencing a variable @@ -932,8 +1149,6 @@ small_symbolic_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED; { - const char *str; - if (! TARGET_SMALL_DATA) return 0; @@ -948,13 +1163,14 @@ small_symbolic_operand (op, mode) if (GET_CODE (op) != SYMBOL_REF) return 0; + /* ??? There's no encode_section_info equivalent for the rtl + constant pool, so SYMBOL_FLAG_SMALL never gets set. */ if (CONSTANT_POOL_ADDRESS_P (op)) - return GET_MODE_SIZE (get_pool_mode (op)) <= (unsigned) g_switch_value; - else - { - str = XSTR (op, 0); - return str[0] == '@' && str[1] == 's'; - } + return GET_MODE_SIZE (get_pool_mode (op)) <= g_switch_value; + + return (SYMBOL_REF_LOCAL_P (op) + && SYMBOL_REF_SMALL_P (op) + && SYMBOL_REF_TLS_MODEL (op) == 0); } /* Return true if OP is a SYMBOL_REF or CONST referencing a variable @@ -976,7 +1192,7 @@ global_symbolic_operand (op, mode) if (GET_CODE (op) != SYMBOL_REF) return 0; - return ! local_symbol_p (op); + return !SYMBOL_REF_LOCAL_P (op) && !SYMBOL_REF_TLS_MODEL (op); } /* Return 1 if OP is a valid operand for the MEM of a CALL insn. */ @@ -992,7 +1208,12 @@ call_operand (op, mode) if (GET_CODE (op) == REG) { if (TARGET_ABI_OSF) - return REGNO (op) == 27; + { + /* Disallow virtual registers to cope with pathalogical test cases + such as compile/930117-1.c in which the virtual reg decomposes + to the frame pointer. Which is a hard reg that is not $27. */ + return (REGNO (op) == 27 || REGNO (op) > LAST_VIRTUAL_REGISTER); + } else return 1; } @@ -1024,6 +1245,112 @@ symbolic_operand (op, mode) return 0; } +/* Return true if OP is valid for a particular TLS relocation. */ + +static int +tls_symbolic_operand_1 (op, mode, size, unspec) + rtx op; + enum machine_mode mode; + int size, unspec; +{ + if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) + return 0; + + if (GET_CODE (op) != CONST) + return 0; + op = XEXP (op, 0); + + if (GET_CODE (op) != UNSPEC || XINT (op, 1) != unspec) + return 0; + op = XVECEXP (op, 0, 0); + + if (GET_CODE (op) != SYMBOL_REF) + return 0; + + if (SYMBOL_REF_LOCAL_P (op)) + { + if (alpha_tls_size > size) + return 0; + } + else + { + if (size != 64) + return 0; + } + + switch (SYMBOL_REF_TLS_MODEL (op)) + { + case TLS_MODEL_LOCAL_DYNAMIC: + return unspec == UNSPEC_DTPREL; + case TLS_MODEL_INITIAL_EXEC: + return unspec == UNSPEC_TPREL && size == 64; + case TLS_MODEL_LOCAL_EXEC: + return unspec == UNSPEC_TPREL; + default: + abort (); + } +} + +/* Return true if OP is valid for 16-bit DTP relative relocations. */ + +int +dtp16_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return tls_symbolic_operand_1 (op, mode, 16, UNSPEC_DTPREL); +} + +/* Return true if OP is valid for 32-bit DTP relative relocations. */ + +int +dtp32_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return tls_symbolic_operand_1 (op, mode, 32, UNSPEC_DTPREL); +} + +/* Return true if OP is valid for 64-bit DTP relative relocations. */ + +int +gotdtp_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return tls_symbolic_operand_1 (op, mode, 64, UNSPEC_DTPREL); +} + +/* Return true if OP is valid for 16-bit TP relative relocations. */ + +int +tp16_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return tls_symbolic_operand_1 (op, mode, 16, UNSPEC_TPREL); +} + +/* Return true if OP is valid for 32-bit TP relative relocations. */ + +int +tp32_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return tls_symbolic_operand_1 (op, mode, 32, UNSPEC_TPREL); +} + +/* Return true if OP is valid for 64-bit TP relative relocations. */ + +int +gottp_symbolic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return tls_symbolic_operand_1 (op, mode, 64, UNSPEC_TPREL); +} + /* Return 1 if OP is a valid Alpha comparison operator. Here we know which comparisons are valid in which insn. */ @@ -1309,7 +1636,7 @@ 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); } @@ -1333,6 +1660,102 @@ addition_operation (op, mode) return 0; } +/* Implements CONST_OK_FOR_LETTER_P. Return true if the value matches + the range defined for C in [I-P]. */ + +bool +alpha_const_ok_for_letter_p (value, c) + HOST_WIDE_INT value; + int c; +{ + switch (c) + { + case 'I': + /* An unsigned 8 bit constant. */ + return (unsigned HOST_WIDE_INT) value < 0x100; + case 'J': + /* The constant zero. */ + return value == 0; + case 'K': + /* A signed 16 bit constant. */ + return (unsigned HOST_WIDE_INT) (value + 0x8000) < 0x10000; + case 'L': + /* A shifted signed 16 bit constant appropriate for LDAH. */ + return ((value & 0xffff) == 0 + && ((value) >> 31 == -1 || value >> 31 == 0)); + case 'M': + /* A constant that can be AND'ed with using a ZAP insn. */ + return zap_mask (value); + case 'N': + /* A complemented unsigned 8 bit constant. */ + return (unsigned HOST_WIDE_INT) (~ value) < 0x100; + case 'O': + /* A negated unsigned 8 bit constant. */ + return (unsigned HOST_WIDE_INT) (- value) < 0x100; + case 'P': + /* The constant 1, 2 or 3. */ + return value == 1 || value == 2 || value == 3; + + default: + return false; + } +} + +/* Implements CONST_DOUBLE_OK_FOR_LETTER_P. Return true if VALUE + matches for C in [GH]. */ + +bool +alpha_const_double_ok_for_letter_p (value, c) + rtx value; + int c; +{ + switch (c) + { + case 'G': + /* The floating point zero constant. */ + return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT + && value == CONST0_RTX (GET_MODE (value))); + + case 'H': + /* A valid operand of a ZAP insn. */ + return (GET_MODE (value) == VOIDmode + && zap_mask (CONST_DOUBLE_LOW (value)) + && zap_mask (CONST_DOUBLE_HIGH (value))); + + default: + return false; + } +} + +/* Implements CONST_DOUBLE_OK_FOR_LETTER_P. Return true if VALUE + matches for C. */ + +bool +alpha_extra_constraint (value, c) + rtx value; + int c; +{ + switch (c) + { + case 'Q': + return normal_memory_operand (value, VOIDmode); + case 'R': + return direct_call_operand (value, Pmode); + case 'S': + return (GET_CODE (value) == CONST_INT + && (unsigned HOST_WIDE_INT) INTVAL (value) < 64); + case 'T': + return GET_CODE (value) == HIGH; + case 'U': + return TARGET_ABI_UNICOSMK && symbolic_operand (value, VOIDmode); + case 'W': + return (GET_CODE (value) == CONST_VECTOR + && value == CONST0_RTX (GET_MODE (value))); + default: + return false; + } +} + /* Return 1 if this function can directly return via $26. */ int @@ -1400,145 +1823,103 @@ alpha_tablejump_best_label (insn) 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 the TLS model to use for SYMBOL. */ + +static enum tls_model +tls_symbolic_operand_type (symbol) + rtx symbol; { - return (DECL_SECTION_NAME (decl) == NULL_TREE - && ! (flag_function_sections - || (targetm.have_named_sections - && DECL_ONE_ONLY (decl)))); -} + enum tls_model model; -/* If we are referencing a function that is static, make the SYMBOL_REF - special. We use this to see indicate we can branch to this function - without setting PV or restoring GP. + if (GET_CODE (symbol) != SYMBOL_REF) + return 0; + model = SYMBOL_REF_TLS_MODEL (symbol); - If this is a variable that is known to be defined locally, add "@v" - to the name. If in addition the variable is to go in .sdata/.sbss, - then add "@s" instead. */ + /* Local-exec with a 64-bit size is the same code as initial-exec. */ + if (model == TLS_MODEL_LOCAL_EXEC && alpha_tls_size == 64) + model = TLS_MODEL_INITIAL_EXEC; -void -alpha_encode_section_info (decl) + return model; +} + +/* Return true if the function DECL will share the same GP as any + function in the current unit of translation. */ + +static bool +decl_has_samegp (decl) tree decl; { - const char *symbol_str; - bool is_local, is_small; - - if (TREE_CODE (decl) == FUNCTION_DECL) - { - /* We mark public functions once they are emitted; otherwise we - don't know that they exist in this unit of translation. */ - if (TREE_PUBLIC (decl)) - return; - /* Do not mark functions that are not in .text; otherwise we - don't know that they are near enough for a direct branch. */ - if (! decl_in_text_section (decl)) - return; + /* Functions that are not local can be overridden, and thus may + not share the same gp. */ + if (!(*targetm.binds_local_p) (decl)) + return false; - SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1; - return; - } + /* If -msmall-data is in effect, assume that there is only one GP + for the module, and so any local symbol has this property. We + need explicit relocations to be able to enforce this for symbols + not defined in this unit of translation, however. */ + if (TARGET_EXPLICIT_RELOCS && TARGET_SMALL_DATA) + return true; - /* Early out if we're not going to do anything with this data. */ - if (! TARGET_EXPLICIT_RELOCS) - return; + /* Functions that are not external are defined in this UoT. */ + /* ??? Irritatingly, static functions not yet emitted are still + marked "external". Apply this to non-static functions only. */ + return !TREE_PUBLIC (decl) || !DECL_EXTERNAL (decl); +} - /* Careful not to prod global register variables. */ - if (TREE_CODE (decl) != VAR_DECL - || GET_CODE (DECL_RTL (decl)) != MEM - || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF) - return; - - symbol_str = XSTR (XEXP (DECL_RTL (decl), 0), 0); - - /* A variable is considered "local" if it is defined in this module. */ - - if (DECL_EXTERNAL (decl)) - is_local = false; - /* Linkonce and weak data is never local. */ - else if (DECL_ONE_ONLY (decl) || DECL_WEAK (decl)) - is_local = false; - else if (! TREE_PUBLIC (decl)) - is_local = true; - /* If PIC, then assume that any global name can be overridden by - symbols resolved from other modules. */ - else if (flag_pic) - is_local = false; - /* Uninitialized COMMON variable may be unified with symbols - resolved from other modules. */ - else if (DECL_COMMON (decl) - && (DECL_INITIAL (decl) == NULL - || DECL_INITIAL (decl) == error_mark_node)) - is_local = false; - /* Otherwise we're left with initialized (or non-common) global data - which is of necessity defined locally. */ - else - is_local = true; +/* Return true if EXP should be placed in the small data section. */ - /* Determine if DECL will wind up in .sdata/.sbss. */ +static bool +alpha_in_small_data_p (exp) + tree exp; +{ + /* We want to merge strings, so we never consider them small data. */ + if (TREE_CODE (exp) == STRING_CST) + return false; - is_small = false; - if (DECL_SECTION_NAME (decl)) + if (TREE_CODE (exp) == VAR_DECL && DECL_SECTION_NAME (exp)) { - const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); + const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (exp)); if (strcmp (section, ".sdata") == 0 || strcmp (section, ".sbss") == 0) - is_small = true; + return true; } else { - HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl)); + HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp)); - /* If the variable has already been defined in the output file, then it - is too late to put it in sdata if it wasn't put there in the first - place. The test is here rather than above, because if it is already - in sdata, then it can stay there. */ + /* If this is an incomplete type with size 0, then we can't put it + in sdata because it might be too big when completed. */ + if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value) + return true; + } - if (TREE_ASM_WRITTEN (decl)) - ; - - /* If this is an incomplete type with size 0, then we can't put it in - sdata because it might be too big when completed. */ - else if (size > 0 && size <= g_switch_value) - is_small = true; - } - - /* Finally, encode this into the symbol string. */ - if (is_local) - { - const char *string; - char *newstr; - size_t len; + return false; +} - if (symbol_str[0] == '@') - { - if (symbol_str[1] == (is_small ? 's' : 'v')) - return; - symbol_str += 2; - } +#if TARGET_ABI_OPEN_VMS +static bool +alpha_linkage_symbol_p (symname) + const char *symname; +{ + int symlen = strlen (symname); - len = strlen (symbol_str) + 1; - newstr = alloca (len + 2); + if (symlen > 4) + return strcmp (&symname [symlen - 4], "..lk") == 0; - newstr[0] = '@'; - newstr[1] = (is_small ? 's' : 'v'); - memcpy (newstr + 2, symbol_str, len); - - string = ggc_alloc_string (newstr, len + 2 - 1); - XSTR (XEXP (DECL_RTL (decl), 0), 0) = string; - } - else if (symbol_str[0] == '@') - abort (); + return false; } +#define LINKAGE_SYMBOL_REF_P(X) \ + ((GET_CODE (X) == SYMBOL_REF \ + && alpha_linkage_symbol_p (XSTR (X, 0))) \ + || (GET_CODE (X) == CONST \ + && GET_CODE (XEXP (X, 0)) == PLUS \ + && GET_CODE (XEXP (XEXP (X, 0), 0)) == SYMBOL_REF \ + && alpha_linkage_symbol_p (XSTR (XEXP (XEXP (X, 0), 0), 0)))) +#endif + /* legitimate_address_p recognizes an RTL expression that is a valid memory address for an instruction. The MODE argument is the machine mode for the MEM expression that wants to use this address. @@ -1578,6 +1959,11 @@ alpha_legitimate_address_p (mode, x, strict) if (CONSTANT_ADDRESS_P (x)) return true; +#if TARGET_ABI_OPEN_VMS + if (LINKAGE_SYMBOL_REF_P (x)) + return true; +#endif + /* Register plus a small constant offset is valid. */ if (GET_CODE (x) == PLUS) { @@ -1607,40 +1993,61 @@ alpha_legitimate_address_p (mode, x, strict) return true; } - /* If we're managing explicit relocations, LO_SUM is valid. */ - else if (TARGET_EXPLICIT_RELOCS && GET_CODE (x) == LO_SUM) + /* If we're managing explicit relocations, LO_SUM is valid, as + are small data symbols. */ + else if (TARGET_EXPLICIT_RELOCS) { - rtx ofs = XEXP (x, 1); - x = XEXP (x, 0); - - /* Discard non-paradoxical subregs. */ - if (GET_CODE (x) == SUBREG - && (GET_MODE_SIZE (GET_MODE (x)) - < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) - x = SUBREG_REG (x); - - /* Must have a valid base register. */ - if (! (REG_P (x) - && (strict - ? STRICT_REG_OK_FOR_BASE_P (x) - : NONSTRICT_REG_OK_FOR_BASE_P (x)))) - return false; - - /* The symbol must be local. */ - if (local_symbolic_operand (ofs, Pmode)) + if (small_symbolic_operand (x, Pmode)) return true; + + if (GET_CODE (x) == LO_SUM) + { + rtx ofs = XEXP (x, 1); + x = XEXP (x, 0); + + /* Discard non-paradoxical subregs. */ + if (GET_CODE (x) == SUBREG + && (GET_MODE_SIZE (GET_MODE (x)) + < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) + x = SUBREG_REG (x); + + /* Must have a valid base register. */ + if (! (REG_P (x) + && (strict + ? STRICT_REG_OK_FOR_BASE_P (x) + : NONSTRICT_REG_OK_FOR_BASE_P (x)))) + return false; + + /* The symbol must be local. */ + if (local_symbolic_operand (ofs, Pmode) + || dtp32_symbolic_operand (ofs, Pmode) + || tp32_symbolic_operand (ofs, Pmode)) + return true; + } } return false; } +/* Build the SYMBOL_REF for __tls_get_addr. */ + +static GTY(()) rtx tls_get_addr_libfunc; + +static rtx +get_tls_get_addr () +{ + if (!tls_get_addr_libfunc) + tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr"); + return tls_get_addr_libfunc; +} + /* Try machine-dependent ways of modifying an illegitimate address to be legitimate. If we find one, return the new, valid address. */ rtx -alpha_legitimize_address (x, oldx, mode) +alpha_legitimize_address (x, scratch, mode) rtx x; - rtx oldx ATTRIBUTE_UNUSED; + rtx scratch; enum machine_mode mode ATTRIBUTE_UNUSED; { HOST_WIDE_INT addend; @@ -1662,7 +2069,8 @@ alpha_legitimize_address (x, oldx, mode) part of the CONST_INT. Then load FOO plus any high-order part of the CONST_INT into a register. Our address is (plus reg low-part-const). This is done to reduce the number of GOT entries. */ - if (GET_CODE (x) == CONST + if (!no_new_pseudos + && GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) { @@ -1674,7 +2082,8 @@ alpha_legitimize_address (x, oldx, mode) /* If we have a (plus reg const), emit the load as in (2), then add the two registers, and finally generate (plus reg low-part-const) as our address. */ - if (GET_CODE (x) == PLUS + if (!no_new_pseudos + && GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG && GET_CODE (XEXP (x, 1)) == CONST && GET_CODE (XEXP (XEXP (x, 1), 0)) == PLUS @@ -1690,33 +2099,112 @@ alpha_legitimize_address (x, oldx, mode) /* If this is a local symbol, split the address into HIGH/LO_SUM parts. */ if (TARGET_EXPLICIT_RELOCS && symbolic_operand (x, Pmode)) { - rtx scratch; - if (local_symbolic_operand (x, Pmode)) + rtx r0, r16, eqv, tga, tp, insn, dest, seq; + + switch (tls_symbolic_operand_type (x)) { - if (small_symbolic_operand (x, Pmode)) - scratch = pic_offset_table_rtx; - else - { - rtx insn, tmp; + case TLS_MODEL_GLOBAL_DYNAMIC: + start_sequence (); + + r0 = gen_rtx_REG (Pmode, 0); + r16 = gen_rtx_REG (Pmode, 16); + tga = get_tls_get_addr (); + dest = gen_reg_rtx (Pmode); + seq = GEN_INT (alpha_next_sequence_number++); + + emit_insn (gen_movdi_er_tlsgd (r16, pic_offset_table_rtx, x, seq)); + insn = gen_call_value_osf_tlsgd (r0, tga, seq); + insn = emit_call_insn (insn); + CONST_OR_PURE_CALL_P (insn) = 1; + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16); - scratch = gen_reg_rtx (Pmode); + insn = get_insns (); + end_sequence (); + + emit_libcall_block (insn, dest, r0, x); + return dest; + + case TLS_MODEL_LOCAL_DYNAMIC: + start_sequence (); + + r0 = gen_rtx_REG (Pmode, 0); + r16 = gen_rtx_REG (Pmode, 16); + tga = get_tls_get_addr (); + scratch = gen_reg_rtx (Pmode); + seq = GEN_INT (alpha_next_sequence_number++); + + emit_insn (gen_movdi_er_tlsldm (r16, pic_offset_table_rtx, seq)); + insn = gen_call_value_osf_tlsldm (r0, tga, seq); + insn = emit_call_insn (insn); + CONST_OR_PURE_CALL_P (insn) = 1; + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16); + + insn = get_insns (); + end_sequence (); - tmp = gen_rtx_HIGH (Pmode, x); - tmp = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, tmp); - insn = emit_insn (gen_rtx_SET (VOIDmode, scratch, tmp)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, tmp, - REG_NOTES (insn)); + eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), + UNSPEC_TLSLDM_CALL); + emit_libcall_block (insn, scratch, r0, eqv); + + eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_DTPREL); + eqv = gen_rtx_CONST (Pmode, eqv); + + if (alpha_tls_size == 64) + { + dest = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (VOIDmode, dest, eqv)); + emit_insn (gen_adddi3 (dest, dest, scratch)); + return dest; + } + if (alpha_tls_size == 32) + { + insn = gen_rtx_HIGH (Pmode, eqv); + insn = gen_rtx_PLUS (Pmode, scratch, insn); + scratch = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (VOIDmode, scratch, insn)); } + return gen_rtx_LO_SUM (Pmode, scratch, eqv); + + case TLS_MODEL_INITIAL_EXEC: + eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPREL); + eqv = gen_rtx_CONST (Pmode, eqv); + tp = gen_reg_rtx (Pmode); + scratch = gen_reg_rtx (Pmode); + dest = gen_reg_rtx (Pmode); - return gen_rtx_LO_SUM (Pmode, scratch, x); + emit_insn (gen_load_tp (tp)); + emit_insn (gen_rtx_SET (VOIDmode, scratch, eqv)); + emit_insn (gen_adddi3 (dest, tp, scratch)); + return dest; + + case TLS_MODEL_LOCAL_EXEC: + eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPREL); + eqv = gen_rtx_CONST (Pmode, eqv); + tp = gen_reg_rtx (Pmode); + + emit_insn (gen_load_tp (tp)); + if (alpha_tls_size == 32) + { + insn = gen_rtx_HIGH (Pmode, eqv); + insn = gen_rtx_PLUS (Pmode, tp, insn); + tp = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (VOIDmode, tp, insn)); + } + return gen_rtx_LO_SUM (Pmode, tp, eqv); } - else + + if (local_symbolic_operand (x, Pmode)) { - scratch = gen_reg_rtx (Pmode); - emit_insn (gen_movdi_er_high_g (scratch, pic_offset_table_rtx, - x, const0_rtx)); - /* ??? FIXME: Tag the use of scratch with a lituse. */ - return scratch; + if (small_symbolic_operand (x, Pmode)) + return x; + else + { + if (!no_new_pseudos) + scratch = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (VOIDmode, scratch, + gen_rtx_HIGH (Pmode, x))); + return gen_rtx_LO_SUM (Pmode, scratch, x); + } } } @@ -1724,14 +2212,145 @@ alpha_legitimize_address (x, oldx, mode) split_addend: { - HOST_WIDE_INT lowpart = (addend & 0xffff) - 2 * (addend & 0x8000); - HOST_WIDE_INT highpart = addend - lowpart; - x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (highpart), - NULL_RTX, 1, OPTAB_LIB_WIDEN); - return plus_constant (x, lowpart); + HOST_WIDE_INT low, high; + + low = ((addend & 0xffff) ^ 0x8000) - 0x8000; + addend -= low; + high = ((addend & 0xffffffff) ^ 0x80000000) - 0x80000000; + addend -= high; + + if (addend) + x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (addend), + (no_new_pseudos ? scratch : NULL_RTX), + 1, OPTAB_LIB_WIDEN); + if (high) + x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (high), + (no_new_pseudos ? scratch : NULL_RTX), + 1, OPTAB_LIB_WIDEN); + + return plus_constant (x, low); } } +/* We do not allow indirect calls to be optimized into sibling calls, nor + can we allow a call to a function with a different GP to be optimized + into a sibcall. */ + +static bool +alpha_function_ok_for_sibcall (decl, exp) + tree decl; + tree exp ATTRIBUTE_UNUSED; +{ + /* Can't do indirect tail calls, since we don't know if the target + uses the same GP. */ + if (!decl) + return false; + + /* Otherwise, we can make a tail call if the target function shares + the same GP. */ + return decl_has_samegp (decl); +} + +/* For TARGET_EXPLICIT_RELOCS, we don't obfuscate a SYMBOL_REF to a + small symbolic operand until after reload. At which point we need + to replace (mem (symbol_ref)) with (mem (lo_sum $29 symbol_ref)) + so that sched2 has the proper dependency information. */ + +int +some_small_symbolic_operand (x, mode) + rtx x; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return for_each_rtx (&x, some_small_symbolic_operand_1, NULL); +} + +static int +some_small_symbolic_operand_1 (px, data) + rtx *px; + void *data ATTRIBUTE_UNUSED; +{ + rtx x = *px; + + /* Don't re-split. */ + if (GET_CODE (x) == LO_SUM) + return -1; + + return small_symbolic_operand (x, Pmode) != 0; +} + +rtx +split_small_symbolic_operand (x) + rtx x; +{ + x = copy_insn (x); + for_each_rtx (&x, split_small_symbolic_operand_1, NULL); + return x; +} + +static int +split_small_symbolic_operand_1 (px, data) + rtx *px; + void *data ATTRIBUTE_UNUSED; +{ + rtx x = *px; + + /* Don't re-split. */ + if (GET_CODE (x) == LO_SUM) + return -1; + + if (small_symbolic_operand (x, Pmode)) + { + x = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, x); + *px = x; + return -1; + } + + return 0; +} + +/* Indicate that INSN cannot be duplicated. This is true for any insn + that we've marked with gpdisp relocs, since those have to stay in + 1-1 correspondence with one another. + + Techinically we could copy them if we could set up a mapping from one + sequence number to another, across the set of insns to be duplicated. + This seems overly complicated and error-prone since interblock motion + from sched-ebb could move one of the pair of insns to a different block. */ + +static bool +alpha_cannot_copy_insn_p (insn) + rtx insn; +{ + rtx pat; + + if (!reload_completed || !TARGET_EXPLICIT_RELOCS) + return false; + + if (GET_CODE (insn) != INSN) + return false; + if (asm_noperands (insn) >= 0) + return false; + + pat = PATTERN (insn); + if (GET_CODE (pat) != SET) + return false; + pat = SET_SRC (pat); + if (GET_CODE (pat) == UNSPEC_VOLATILE) + { + if (XINT (pat, 1) == UNSPECV_LDGP1 + || XINT (pat, 1) == UNSPECV_PLDGP2) + return true; + } + else if (GET_CODE (pat) == UNSPEC) + { + if (XINT (pat, 1) == UNSPEC_LDGP2) + return true; + } + + return false; +} + + /* Try a machine-dependent way of reloading an illegitimate address operand. If we find one, push the reload and return the new rtx. */ @@ -1790,6 +2409,150 @@ alpha_legitimize_reload_address (x, mode, opnum, type, ind_levels) return NULL_RTX; } +/* Compute a (partial) cost for rtx X. Return true if the complete + cost has been computed, and false if subexpressions should be + scanned. In either case, *TOTAL contains the cost result. */ + +static bool +alpha_rtx_costs (x, code, outer_code, total) + rtx x; + int code, outer_code; + int *total; +{ + enum machine_mode mode = GET_MODE (x); + bool float_mode_p = FLOAT_MODE_P (mode); + + switch (code) + { + /* If this is an 8-bit constant, return zero since it can be used + nearly anywhere with no cost. If it is a valid operand for an + ADD or AND, likewise return 0 if we know it will be used in that + context. Otherwise, return 2 since it might be used there later. + All other constants take at least two insns. */ + case CONST_INT: + if (INTVAL (x) >= 0 && INTVAL (x) < 256) + { + *total = 0; + return true; + } + /* FALLTHRU */ + + case CONST_DOUBLE: + if (x == CONST0_RTX (mode)) + *total = 0; + else if ((outer_code == PLUS && add_operand (x, VOIDmode)) + || (outer_code == AND && and_operand (x, VOIDmode))) + *total = 0; + else if (add_operand (x, VOIDmode) || and_operand (x, VOIDmode)) + *total = 2; + else + *total = COSTS_N_INSNS (2); + return true; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + if (TARGET_EXPLICIT_RELOCS && small_symbolic_operand (x, VOIDmode)) + *total = COSTS_N_INSNS (outer_code != MEM); + else if (TARGET_EXPLICIT_RELOCS && local_symbolic_operand (x, VOIDmode)) + *total = COSTS_N_INSNS (1 + (outer_code != MEM)); + else if (tls_symbolic_operand_type (x)) + /* Estimate of cost for call_pal rduniq. */ + *total = COSTS_N_INSNS (15); + else + /* Otherwise we do a load from the GOT. */ + *total = COSTS_N_INSNS (alpha_memory_latency); + return true; + + case PLUS: + case MINUS: + if (float_mode_p) + *total = alpha_rtx_cost_data[alpha_cpu].fp_add; + else if (GET_CODE (XEXP (x, 0)) == MULT + && const48_operand (XEXP (XEXP (x, 0), 1), VOIDmode)) + { + *total = (rtx_cost (XEXP (XEXP (x, 0), 0), outer_code) + + rtx_cost (XEXP (x, 1), outer_code) + 2); + return true; + } + return false; + + case MULT: + if (float_mode_p) + *total = alpha_rtx_cost_data[alpha_cpu].fp_mult; + else if (mode == DImode) + *total = alpha_rtx_cost_data[alpha_cpu].int_mult_di; + else + *total = alpha_rtx_cost_data[alpha_cpu].int_mult_si; + return false; + + case ASHIFT: + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) <= 3) + { + *total = COSTS_N_INSNS (1); + return false; + } + /* FALLTHRU */ + + case ASHIFTRT: + case LSHIFTRT: + *total = alpha_rtx_cost_data[alpha_cpu].int_shift; + return false; + + case IF_THEN_ELSE: + if (float_mode_p) + *total = alpha_rtx_cost_data[alpha_cpu].fp_add; + else + *total = alpha_rtx_cost_data[alpha_cpu].int_cmov; + return false; + + case DIV: + case UDIV: + case MOD: + case UMOD: + if (!float_mode_p) + *total = COSTS_N_INSNS (70); /* ??? */ + else if (mode == SFmode) + *total = alpha_rtx_cost_data[alpha_cpu].fp_div_sf; + else + *total = alpha_rtx_cost_data[alpha_cpu].fp_div_df; + return false; + + case MEM: + *total = COSTS_N_INSNS (alpha_memory_latency); + return true; + + case NEG: + if (! float_mode_p) + { + *total = COSTS_N_INSNS (1); + return false; + } + /* FALLTHRU */ + + case ABS: + if (! float_mode_p) + { + *total = COSTS_N_INSNS (1) + alpha_rtx_cost_data[alpha_cpu].int_cmov; + return false; + } + /* FALLTHRU */ + + case FLOAT: + case UNSIGNED_FLOAT: + case FIX: + case UNSIGNED_FIX: + case FLOAT_EXTEND: + case FLOAT_TRUNCATE: + *total = alpha_rtx_cost_data[alpha_cpu].fp_add; + return false; + + default: + return false; + } +} + /* 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 @@ -1822,12 +2585,8 @@ 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); - - /* Sadly, we cannot use alias sets here because we may overlap other - data in a different alias set. */ - set_mem_alias_set (*paligned_mem, 0); + *paligned_mem + = widen_memory_access (ref, SImode, (offset & ~3) - offset); if (WORDS_BIG_ENDIAN) *pbitnum = GEN_INT (32 - (GET_MODE_BITSIZE (GET_MODE (ref)) @@ -1869,6 +2628,39 @@ 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 @@ -1932,6 +2724,8 @@ alpha_set_memflags_1 (x, in_struct_p, volatile_p, unchanging_p) switch (GET_CODE (x)) { case SEQUENCE: + abort (); + case PARALLEL: for (i = XVECLEN (x, 0) - 1; i >= 0; i--) alpha_set_memflags_1 (XVECEXP (x, 0, i), in_struct_p, volatile_p, @@ -1966,11 +2760,11 @@ alpha_set_memflags_1 (x, in_struct_p, volatile_p, unchanging_p) } } -/* Given INSN, which is either an INSN or a SEQUENCE generated to - perform a memory operation, look for any MEMs in either a SET_DEST or - a SET_SRC and copy the in-struct, unchanging, and volatile flags from - REF into each of the MEMs found. If REF is not a MEM, don't do - anything. */ +/* Given INSN, which is an INSN list or the PATTERN of a single insn + generated to perform a memory operation, look for any MEMs in either + a SET_DEST or a SET_SRC and copy the in-struct, unchanging, and + volatile flags from REF into each of the MEMs found. If REF is not + a MEM, don't do anything. */ void alpha_set_memflags (insn, ref) @@ -2008,15 +2802,39 @@ alpha_emit_set_const (target, mode, c, n) HOST_WIDE_INT c; int n; { - rtx pat; + rtx result = 0; + rtx orig_target = target; int i; + /* If we can't make any pseudos, TARGET is an SImode hard register, we + can't load this constant in one insn, do this in DImode. */ + if (no_new_pseudos && mode == SImode + && GET_CODE (target) == REG && REGNO (target) < FIRST_PSEUDO_REGISTER + && (result = alpha_emit_set_const_1 (target, mode, c, 1)) == 0) + { + target = gen_lowpart (DImode, target); + mode = DImode; + } + /* Try 1 insn, then 2, then up to N. */ for (i = 1; i <= n; i++) - if ((pat = alpha_emit_set_const_1 (target, mode, c, i)) != 0) - return pat; + { + result = alpha_emit_set_const_1 (target, mode, c, i); + if (result) + { + rtx insn = get_last_insn (); + rtx set = single_set (insn); + if (! CONSTANT_P (SET_SRC (set))) + set_unique_reg_note (get_last_insn (), REG_EQUAL, GEN_INT (c)); + break; + } + } - return 0; + /* Allow for the case where we changed the mode of TARGET. */ + if (result == target) + result = orig_target; + + return result; } /* Internal routine for the above to check for N or below insns. */ @@ -2032,18 +2850,8 @@ alpha_emit_set_const_1 (target, mode, c, n) int i, bits; /* Use a pseudo if highly optimizing and still generating RTL. */ rtx subtarget - = (flag_expensive_optimizations && rtx_equal_function_value_matters - ? 0 : target); - rtx temp; - -#if HOST_BITS_PER_WIDE_INT == 64 - /* We are only called for SImode and DImode. If this is SImode, ensure that - we are sign extended to a full word. This does not make any sense when - cross-compiling on a narrow machine. */ - - if (mode == SImode) - c = ((c & 0xffffffff) ^ 0x80000000) - 0x80000000; -#endif + = (flag_expensive_optimizations && !no_new_pseudos ? 0 : target); + rtx temp, insn; /* If this is a sign-extended 32-bit constant, we can do this in at most three insns, so do it if we have enough insns left. We always have @@ -2084,12 +2892,28 @@ alpha_emit_set_const_1 (target, mode, c, n) { temp = copy_to_suggested_reg (GEN_INT (high << 16), subtarget, mode); + /* As of 2002-02-23, addsi3 is only available when not optimizing. + This means that if we go through expand_binop, we'll try to + generate extensions, etc, which will require new pseudos, which + will fail during some split phases. The SImode add patterns + still exist, but are not named. So build the insns by hand. */ + if (extra != 0) - temp = expand_binop (mode, add_optab, temp, GEN_INT (extra << 16), - subtarget, 0, OPTAB_WIDEN); + { + if (! subtarget) + subtarget = gen_reg_rtx (mode); + insn = gen_rtx_PLUS (mode, temp, GEN_INT (extra << 16)); + insn = gen_rtx_SET (VOIDmode, subtarget, insn); + emit_insn (insn); + temp = subtarget; + } - return expand_binop (mode, add_optab, temp, GEN_INT (low), - target, 0, OPTAB_WIDEN); + if (target == NULL) + target = gen_reg_rtx (mode); + insn = gen_rtx_PLUS (mode, temp, GEN_INT (low)); + insn = gen_rtx_SET (VOIDmode, target, insn); + emit_insn (insn); + return target; } } @@ -2098,8 +2922,7 @@ alpha_emit_set_const_1 (target, mode, c, n) we can't make pseudos, we can't do anything since the expand_binop and expand_unop calls will widen and try to make pseudos. */ - if (n == 1 - || (mode == SImode && ! rtx_equal_function_value_matters)) + if (n == 1 || (mode == SImode && no_new_pseudos)) return 0; /* Next, see if we can load a related constant and then shift and possibly @@ -2277,36 +3100,36 @@ alpha_expand_mov (mode, operands) && ! reg_or_0_operand (operands[1], mode)) operands[1] = force_reg (mode, operands[1]); - if (TARGET_EXPLICIT_RELOCS && symbolic_operand (operands[1], mode)) + /* Allow legitimize_address to perform some simplifications. */ + if (mode == Pmode && symbolic_operand (operands[1], mode)) { - if (local_symbolic_operand (operands[1], mode)) - { - rtx scratch; - - if (small_symbolic_operand (operands[1], Pmode)) - scratch = pic_offset_table_rtx; - else - { - rtx insn, tmp; + rtx tmp; - scratch = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode); - - tmp = gen_rtx_HIGH (Pmode, operands[1]); - tmp = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, tmp); - insn = emit_insn (gen_rtx_SET (VOIDmode, scratch, tmp)); - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, tmp, - REG_NOTES (insn)); - } + /* With RTL inlining, at -O3, rtl is generated, stored, then actually + compiled at the end of compilation. In the meantime, someone can + re-encode-section-info on some symbol changing it e.g. from global + to local-not-small. If this happens, we'd have emitted a plain + load rather than a high+losum load and not recognize the insn. - operands[1] = gen_rtx_LO_SUM (Pmode, scratch, operands[1]); - return false; - } - else + So if rtl inlining is in effect, we delay the global/not-global + decision until rest_of_compilation by wrapping it in an + UNSPEC_SYMBOL. */ + if (TARGET_EXPLICIT_RELOCS && flag_inline_functions + && rtx_equal_function_value_matters + && global_symbolic_operand (operands[1], mode)) { - emit_insn (gen_movdi_er_high_g (operands[0], pic_offset_table_rtx, - operands[1], const0_rtx)); + emit_insn (gen_movdi_er_maybe_g (operands[0], operands[1])); return true; } + + tmp = alpha_legitimize_address (operands[1], operands[0], mode); + if (tmp) + { + if (tmp == operands[0]) + return true; + operands[1] = tmp; + return false; + } } /* Early out for non-constants and valid constants. */ @@ -2352,7 +3175,7 @@ alpha_expand_mov (mode, operands) } /* Otherwise we've nothing left but to drop the thing to memory. */ - operands[1] = force_const_mem (DImode, operands[1]); + operands[1] = force_const_mem (mode, operands[1]); if (reload_in_progress) { emit_move_insn (operands[0], XEXP (operands[1], 0)); @@ -2577,21 +3400,29 @@ alpha_emit_conditional_branch (code) 1 true Convert the compare against the raw return value. */ - if (code == UNORDERED || code == ORDERED) - cmp_code = EQ; - else - cmp_code = code; + switch (code) + { + case UNORDERED: + cmp_code = EQ; + code = LT; + break; + case ORDERED: + cmp_code = EQ; + code = GE; + break; + case NE: + cmp_code = NE; + code = NE; + break; + default: + cmp_code = code; + code = GT; + break; + } op0 = alpha_emit_xfloating_compare (cmp_code, op0, op1); op1 = const0_rtx; alpha_compare.fp_p = 0; - - if (code == UNORDERED) - code = LT; - else if (code == ORDERED) - code = GE; - else - code = GT; } /* The general case: fold the comparison code to the types of compares @@ -2648,7 +3479,7 @@ alpha_emit_conditional_branch (code) } else { - /* ??? We mark the the branch mode to be CCmode to prevent the + /* ??? We mark the branch mode to be CCmode to prevent the compare and branch from being combined, since the compare insn follows IEEE rules that the branch does not. */ branch_mode = CCmode; @@ -2816,7 +3647,7 @@ alpha_emit_setcc (code) /* Rewrite a comparison against zero CMP of the form (CODE (cc0) (const_int 0)) so it can be written validly in a conditional move (if_then_else CMP ...). - If both of the operands that set cc0 are non-zero we must emit + If both of the operands that set cc0 are nonzero we must emit an insn to perform the compare (it can't be done within the conditional move). */ rtx @@ -2848,7 +3679,7 @@ alpha_emit_conditional_move (cmp, mode) /* If we have fp<->int register move instructions, do a cmov by performing the comparison in fp registers, and move the - zero/non-zero value to integer registers, where we can then + zero/nonzero value to integer registers, where we can then use a normal cmov, or vice-versa. */ switch (code) @@ -2987,6 +3818,9 @@ alpha_split_conditional_move (code, dest, cond, t_rtx, f_rtx) 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 @@ -2995,33 +3829,35 @@ alpha_split_conditional_move (code, dest, cond, t_rtx, f_rtx) && (diff <= 8 || alpha_cpu == PROCESSOR_EV6)) { tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx); - emit_insn (gen_rtx_SET (VOIDmode, subtarget, tmp)); + emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp)); - tmp = gen_rtx_ASHIFT (DImode, subtarget, GEN_INT (exact_log2 (t))); + tmp = gen_rtx_ASHIFT (DImode, copy_rtx (subtarget), + GEN_INT (exact_log2 (t))); emit_insn (gen_rtx_SET (VOIDmode, target, tmp)); } else if (f == 0 && t == -1) { tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx); - emit_insn (gen_rtx_SET (VOIDmode, subtarget, tmp)); + emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp)); - emit_insn (gen_negdi2 (target, subtarget)); + emit_insn (gen_negdi2 (target, copy_rtx (subtarget))); } else if (diff == 1 || diff == 4 || diff == 8) { rtx add_op; tmp = gen_rtx_fmt_ee (code, DImode, cond, const0_rtx); - emit_insn (gen_rtx_SET (VOIDmode, subtarget, tmp)); + emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (subtarget), tmp)); if (diff == 1) - emit_insn (gen_adddi3 (target, subtarget, GEN_INT (f))); + emit_insn (gen_adddi3 (target, copy_rtx (subtarget), GEN_INT (f))); else { add_op = GEN_INT (f); if (sext_add_operand (add_op, mode)) { - tmp = gen_rtx_MULT (DImode, subtarget, GEN_INT (diff)); + tmp = gen_rtx_MULT (DImode, copy_rtx (subtarget), + GEN_INT (diff)); tmp = gen_rtx_PLUS (DImode, tmp, add_op); emit_insn (gen_rtx_SET (VOIDmode, target, tmp)); } @@ -3230,7 +4066,7 @@ alpha_emit_xfloating_libcall (func, target, operands, noperands, equiv) abort (); } - tmp = gen_rtx_MEM (QImode, gen_rtx_SYMBOL_REF (Pmode, (char *) func)); + tmp = gen_rtx_MEM (QImode, init_one_libfunc (func)); tmp = emit_call_insn (GEN_CALL_VALUE (reg, tmp, const0_rtx, const0_rtx, const0_rtx)); CALL_INSN_FUNCTION_USAGE (tmp) = usage; @@ -3375,7 +4211,7 @@ alpha_split_tfmode_frobsign (operands, operation) alpha_split_tfmode_pair (operands); - /* Detect three flavours of operand overlap. */ + /* Detect three flavors of operand overlap. */ move = 1; if (rtx_equal_p (operands[0], operands[2])) move = 0; @@ -3621,17 +4457,13 @@ alpha_expand_unaligned_store (dst, src, size, ofs) emit_insn (gen_mskxl_be (dsth, dsth, GEN_INT (0xffff), addr)); break; case 4: - emit_insn (gen_mskxl_be (dsth, dsth, GEN_INT (0xffffffff), addr)); - break; - case 8: { -#if HOST_BITS_PER_WIDE_INT == 32 - rtx msk = immed_double_const (0xffffffff, 0xffffffff, DImode); -#else - rtx msk = constm1_rtx; -#endif + rtx msk = immed_double_const (0xffffffff, 0, DImode); emit_insn (gen_mskxl_be (dsth, dsth, msk, addr)); + break; } + case 8: + emit_insn (gen_mskxl_be (dsth, dsth, constm1_rtx, addr)); break; } @@ -3668,17 +4500,13 @@ alpha_expand_unaligned_store (dst, src, size, ofs) emit_insn (gen_mskxl_le (dstl, dstl, GEN_INT (0xffff), addr)); break; case 4: - emit_insn (gen_mskxl_le (dstl, dstl, GEN_INT (0xffffffff), addr)); - break; - case 8: { -#if HOST_BITS_PER_WIDE_INT == 32 - rtx msk = immed_double_const (0xffffffff, 0xffffffff, DImode); -#else - rtx msk = constm1_rtx; -#endif + rtx msk = immed_double_const (0xffffffff, 0, DImode); emit_insn (gen_mskxl_le (dstl, dstl, msk, addr)); + break; } + case 8: + emit_insn (gen_mskxl_le (dstl, dstl, constm1_rtx, addr)); break; } } @@ -3804,11 +4632,6 @@ alpha_expand_unaligned_store_words (data_regs, dmem, words, ofs) { rtx const im8 = GEN_INT (-8); rtx const i64 = GEN_INT (64); -#if HOST_BITS_PER_WIDE_INT == 32 - rtx const im1 = immed_double_const (0xffffffff, 0xffffffff, DImode); -#else - rtx const im1 = constm1_rtx; -#endif rtx ins_tmps[MAX_MOVE_WORDS]; rtx st_tmp_1, st_tmp_2, dreg; rtx st_addr_1, st_addr_2, dmema; @@ -3872,13 +4695,13 @@ alpha_expand_unaligned_store_words (data_regs, dmem, words, ofs) /* Split and merge the ends with the destination data. */ if (WORDS_BIG_ENDIAN) { - emit_insn (gen_mskxl_be (st_tmp_2, st_tmp_2, im1, dreg)); + emit_insn (gen_mskxl_be (st_tmp_2, st_tmp_2, constm1_rtx, dreg)); emit_insn (gen_mskxh (st_tmp_1, st_tmp_1, i64, dreg)); } else { emit_insn (gen_mskxh (st_tmp_2, st_tmp_2, i64, dreg)); - emit_insn (gen_mskxl_le (st_tmp_1, st_tmp_1, im1, dreg)); + emit_insn (gen_mskxl_le (st_tmp_1, st_tmp_1, constm1_rtx, dreg)); } if (data_regs != NULL) @@ -4543,6 +5366,75 @@ alpha_expand_block_clear (operands) return 1; } + +/* Returns a mask so that zap(x, value) == x & mask. */ + +rtx +alpha_expand_zap_mask (value) + HOST_WIDE_INT value; +{ + rtx result; + int i; + + if (HOST_BITS_PER_WIDE_INT >= 64) + { + HOST_WIDE_INT mask = 0; + + for (i = 7; i >= 0; --i) + { + mask <<= 8; + if (!((value >> i) & 1)) + mask |= 0xff; + } + + result = gen_int_mode (mask, DImode); + } + else if (HOST_BITS_PER_WIDE_INT == 32) + { + HOST_WIDE_INT mask_lo = 0, mask_hi = 0; + + for (i = 7; i >= 4; --i) + { + mask_hi <<= 8; + if (!((value >> i) & 1)) + mask_hi |= 0xff; + } + + for (i = 3; i >= 0; --i) + { + mask_lo <<= 8; + if (!((value >> i) & 1)) + mask_lo |= 0xff; + } + + result = immed_double_const (mask_lo, mask_hi, DImode); + } + else + abort (); + + return result; +} + +void +alpha_expand_builtin_vector_binop (gen, mode, op0, op1, op2) + rtx (*gen) PARAMS ((rtx, rtx, rtx)); + enum machine_mode mode; + rtx op0, op1, op2; +{ + op0 = gen_lowpart (mode, op0); + + if (op1 == const0_rtx) + op1 = CONST0_RTX (mode); + else + op1 = gen_lowpart (mode, op1); + + if (op2 == const0_rtx) + op2 = CONST0_RTX (mode); + else + op2 = gen_lowpart (mode, op2); + + emit_insn ((*gen) (op0, op1, op2)); +} /* 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. */ @@ -4554,15 +5446,13 @@ alpha_adjust_cost (insn, link, dep_insn, cost) rtx dep_insn; int cost; { - rtx set, set_src; enum attr_type insn_type, dep_insn_type; /* If the dependence is an anti-dependence, there is no cost. For an output dependence, there is sometimes a cost, but it doesn't seem worth handling those few cases. */ - if (REG_NOTE_KIND (link) != 0) - return 0; + return cost; /* If we can't recognize the insns, we can't really do anything. */ if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0) @@ -4577,122 +5467,13 @@ alpha_adjust_cost (insn, link, dep_insn, cost) || dep_insn_type == TYPE_LDSYM) cost += alpha_memory_latency-1; - switch (alpha_cpu) - { - case PROCESSOR_EV4: - /* On EV4, if INSN is a store insn and DEP_INSN is setting the data - being stored, we can sometimes lower the cost. */ - - if ((insn_type == TYPE_IST || insn_type == TYPE_FST) - && (set = single_set (dep_insn)) != 0 - && GET_CODE (PATTERN (insn)) == SET - && rtx_equal_p (SET_DEST (set), SET_SRC (PATTERN (insn)))) - { - switch (dep_insn_type) - { - case TYPE_ILD: - case TYPE_FLD: - /* No savings here. */ - return cost; - - case TYPE_IMUL: - /* In these cases, we save one cycle. */ - return cost - 1; - - default: - /* In all other cases, we save two cycles. */ - return MAX (0, cost - 2); - } - } - - /* Another case that needs adjustment is an arithmetic or logical - operation. It's cost is usually one cycle, but we default it to - two in the MD file. The only case that it is actually two is - for the address in loads, stores, and jumps. */ - - if (dep_insn_type == TYPE_IADD || dep_insn_type == TYPE_ILOG) - { - switch (insn_type) - { - case TYPE_ILD: - case TYPE_IST: - case TYPE_FLD: - case TYPE_FST: - case TYPE_JSR: - return cost; - default: - return 1; - } - } - - /* The final case is when a compare feeds into an integer branch; - the cost is only one cycle in that case. */ - - if (dep_insn_type == TYPE_ICMP && insn_type == TYPE_IBR) - return 1; - break; - - case PROCESSOR_EV5: - /* And the lord DEC saith: "A special bypass provides an effective - latency of 0 cycles for an ICMP or ILOG insn producing the test - operand of an IBR or ICMOV insn." */ - - if ((dep_insn_type == TYPE_ICMP || dep_insn_type == TYPE_ILOG) - && (set = single_set (dep_insn)) != 0) - { - /* A branch only has one input. This must be it. */ - if (insn_type == TYPE_IBR) - return 0; - /* A conditional move has three, make sure it is the test. */ - if (insn_type == TYPE_ICMOV - && GET_CODE (set_src = PATTERN (insn)) == SET - && GET_CODE (set_src = SET_SRC (set_src)) == IF_THEN_ELSE - && rtx_equal_p (SET_DEST (set), XEXP (set_src, 0))) - return 0; - } - - /* "The multiplier is unable to receive data from IEU bypass paths. - The instruction issues at the expected time, but its latency is - increased by the time it takes for the input data to become - available to the multiplier" -- which happens in pipeline stage - six, when results are comitted to the register file. */ - - if (insn_type == TYPE_IMUL) - { - switch (dep_insn_type) - { - /* These insns produce their results in pipeline stage five. */ - case TYPE_ILD: - case TYPE_ICMOV: - case TYPE_IMUL: - case TYPE_MVI: - return cost + 1; - - /* Other integer insns produce results in pipeline stage four. */ - default: - return cost + 2; - } - } - break; - - case PROCESSOR_EV6: - /* There is additional latency to move the result of (most) FP - operations anywhere but the FP register file. */ - - if ((insn_type == TYPE_FST || insn_type == TYPE_FTOI) - && (dep_insn_type == TYPE_FADD || - dep_insn_type == TYPE_FMUL || - dep_insn_type == TYPE_FCMOV)) - return cost + 2; - - break; - } + /* Everything else handled in DFA bypasses now. */ - /* Otherwise, return the default cost. */ return cost; } -/* Function to initialize the issue rate used by the scheduler. */ +/* The number of instructions that can be issued per cycle. */ + static int alpha_issue_rate () { @@ -4700,57 +5481,50 @@ alpha_issue_rate () } static int -alpha_variable_issue (dump, verbose, insn, cim) - FILE *dump ATTRIBUTE_UNUSED; - int verbose ATTRIBUTE_UNUSED; - rtx insn; - int cim; +alpha_use_dfa_pipeline_interface () { - if (recog_memoized (insn) < 0 || get_attr_type (insn) == TYPE_MULTI) - return 0; - - return cim - 1; + return true; } - -/* Register global variables and machine-specific functions with the - garbage collector. */ +/* How many alternative schedules to try. This should be as wide as the + scheduling freedom in the DFA, but no wider. Making this value too + large results extra work for the scheduler. -#if TARGET_ABI_UNICOSMK -static void -alpha_init_machine_status (p) - struct function *p; -{ - p->machine = - (struct machine_function *) xcalloc (1, sizeof (struct machine_function)); + For EV4, loads can be issued to either IB0 or IB1, thus we have 2 + alternative schedules. For EV5, we can choose between E0/E1 and + FA/FM. For EV6, an arithmatic insn can be issued to U0/U1/L0/L1. */ - p->machine->first_ciw = NULL_RTX; - p->machine->last_ciw = NULL_RTX; - p->machine->ciw_count = 0; - p->machine->addr_list = NULL_RTX; +static int +alpha_multipass_dfa_lookahead () +{ + return (alpha_cpu == PROCESSOR_EV6 ? 4 : 2); } + +/* Machine-specific function data. */ -static void -alpha_mark_machine_status (p) - struct function *p; +struct machine_function GTY(()) { - struct machine_function *machine = p->machine; + /* For unicosmk. */ + /* List of call information words for calls from this function. */ + struct rtx_def *first_ciw; + struct rtx_def *last_ciw; + int ciw_count; - if (machine) - { - ggc_mark_rtx (machine->first_ciw); - ggc_mark_rtx (machine->addr_list); - } -} + /* List of deferred case vectors. */ + struct rtx_def *addr_list; -static void -alpha_free_machine_status (p) - struct function *p; + /* For OSF. */ + const char *some_ld_name; +}; + +/* How to allocate a 'struct machine_function'. */ + +static struct machine_function * +alpha_init_machine_status () { - free (p->machine); - p->machine = NULL; + return ((struct machine_function *) + ggc_alloc_cleared (sizeof (struct machine_function))); } -#endif /* TARGET_ABI_UNICOSMK */ /* Functions to save and restore alpha_return_addr_rtx. */ @@ -4773,7 +5547,10 @@ alpha_return_addr (count, frame) rtx alpha_gp_save_rtx () { - return get_hard_reg_initial_val (DImode, 29); + rtx r = get_hard_reg_initial_val (DImode, 29); + if (GET_CODE (r) != MEM) + r = gen_mem_addressof (r, NULL_TREE, /*rescan=*/true); + return r; } static int @@ -4781,10 +5558,6 @@ alpha_ra_ever_killed () { rtx top; -#ifdef ASM_OUTPUT_MI_THUNK - if (current_function_is_thunk) - return 0; -#endif if (!has_hard_reg_initial_val (Pmode, REG_RA)) return regs_ever_live[REG_RA]; @@ -4895,6 +5668,42 @@ get_round_mode_suffix () abort (); } +/* Locate some local-dynamic symbol still in use by this function + so that we can print its name in some movdi_er_tlsldm pattern. */ + +static const char * +get_some_local_dynamic_name () +{ + rtx insn; + + if (cfun->machine->some_ld_name) + return cfun->machine->some_ld_name; + + for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) + if (INSN_P (insn) + && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0)) + return cfun->machine->some_ld_name; + + abort (); +} + +static int +get_some_local_dynamic_name_1 (px, data) + rtx *px; + void *data ATTRIBUTE_UNUSED; +{ + rtx x = *px; + + if (GET_CODE (x) == SYMBOL_REF + && SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC) + { + cfun->machine->some_ld_name = XSTR (x, 0); + return 1; + } + + return 0; +} + /* Print an operand. Recognize special options, documented below. */ void @@ -4912,6 +5721,10 @@ print_operand (file, x, code) assemble_name (file, alpha_fnname); break; + case '&': + assemble_name (file, get_some_local_dynamic_name ()); + break; + case '/': { const char *trap = get_trap_mode_suffix (); @@ -4933,6 +5746,13 @@ print_operand (file, x, code) fputc ((TARGET_FLOAT_VAX ? 'g' : 't'), file); break; + case '+': + /* Generates a nop after a noreturn call at the very end of the + function. */ + if (next_real_insn (current_output_insn) == 0) + fprintf (file, "\n\tnop"); + break; + case '#': if (alpha_this_literal_sequence_number == 0) alpha_this_literal_sequence_number = alpha_next_sequence_number++; @@ -4952,6 +5772,33 @@ print_operand (file, x, code) output_operand_lossage ("invalid %%H value"); break; + case 'J': + { + const char *lituse; + + if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD_CALL) + { + x = XVECEXP (x, 0, 0); + lituse = "lituse_tlsgd"; + } + else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM_CALL) + { + x = XVECEXP (x, 0, 0); + lituse = "lituse_tlsldm"; + } + else if (GET_CODE (x) == CONST_INT) + lituse = "lituse_jsr"; + else + { + output_operand_lossage ("invalid %%J value"); + break; + } + + if (x != const0_rtx) + fprintf (file, "\t\t!%s!%d", lituse, (int) INTVAL (x)); + } + break; + case 'r': /* If this operand is the constant zero, write it as "$31". */ if (GET_CODE (x) == REG) @@ -5057,31 +5904,40 @@ print_operand (file, x, code) case 'U': /* Similar, except do it from the mask. */ - if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0xff) - fprintf (file, "b"); - else if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0xffff) - fprintf (file, "w"); - else if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0xffffffff) - fprintf (file, "l"); -#if HOST_BITS_PER_WIDE_INT == 32 - else if (GET_CODE (x) == CONST_DOUBLE - && CONST_DOUBLE_HIGH (x) == 0 - && CONST_DOUBLE_LOW (x) == -1) - fprintf (file, "l"); - else if (GET_CODE (x) == CONST_DOUBLE - && CONST_DOUBLE_HIGH (x) == -1 - && CONST_DOUBLE_LOW (x) == -1) - fprintf (file, "q"); -#else - else if (GET_CODE (x) == CONST_INT && INTVAL (x) == -1) - fprintf (file, "q"); - else if (GET_CODE (x) == CONST_DOUBLE - && CONST_DOUBLE_HIGH (x) == 0 - && CONST_DOUBLE_LOW (x) == -1) - fprintf (file, "q"); -#endif - else - output_operand_lossage ("invalid %%U value"); + if (GET_CODE (x) == CONST_INT) + { + HOST_WIDE_INT value = INTVAL (x); + + if (value == 0xff) + { + fputc ('b', file); + break; + } + if (value == 0xffff) + { + fputc ('w', file); + break; + } + if (value == 0xffffffff) + { + fputc ('l', file); + break; + } + if (value == -1) + { + fputc ('q', file); + break; + } + } + else if (HOST_BITS_PER_WIDE_INT == 32 + && GET_CODE (x) == CONST_DOUBLE + && CONST_DOUBLE_LOW (x) == 0xffffffff + && CONST_DOUBLE_HIGH (x) == 0) + { + fputc ('l', file); + break; + } + output_operand_lossage ("invalid %%U value"); break; case 's': @@ -5183,6 +6039,19 @@ print_operand (file, x, code) fprintf (file, "%s", reg_names[REGNO (x)]); else if (GET_CODE (x) == MEM) output_address (XEXP (x, 0)); + else if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == UNSPEC) + { + switch (XINT (XEXP (x, 0), 1)) + { + case UNSPEC_DTPREL: + case UNSPEC_TPREL: + output_addr_const (file, XVECEXP (XEXP (x, 0), 0, 0)); + break; + default: + output_operand_lossage ("unknown relocation unspec"); + break; + } + } else output_addr_const (file, x); break; @@ -5212,12 +6081,38 @@ print_operand_address (file, addr) if (GET_CODE (addr) == LO_SUM) { - output_addr_const (file, XEXP (addr, 1)); - if (offset) + const char *reloc16, *reloclo; + rtx op1 = XEXP (addr, 1); + + if (GET_CODE (op1) == CONST && GET_CODE (XEXP (op1, 0)) == UNSPEC) + { + op1 = XEXP (op1, 0); + switch (XINT (op1, 1)) + { + case UNSPEC_DTPREL: + reloc16 = NULL; + reloclo = (alpha_tls_size == 16 ? "dtprel" : "dtprello"); + break; + case UNSPEC_TPREL: + reloc16 = NULL; + reloclo = (alpha_tls_size == 16 ? "tprel" : "tprello"); + break; + default: + output_operand_lossage ("unknown relocation unspec"); + return; + } + + output_addr_const (file, XVECEXP (op1, 0, 0)); + } + else { - fputc ('+', file); - fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset); + reloc16 = "gprel"; + reloclo = "gprellow"; + output_addr_const (file, op1); } + + if (offset) + fprintf (file, "+" HOST_WIDE_INT_PRINT_DEC, offset); addr = XEXP (addr, 0); if (GET_CODE (addr) == REG) @@ -5229,7 +6124,7 @@ print_operand_address (file, addr) abort (); fprintf (file, "($%d)\t\t!%s", basereg, - (basereg == 29 ? "gprel" : "gprellow")); + (basereg == 29 ? reloc16 : reloclo)); return; } @@ -5240,11 +6135,28 @@ print_operand_address (file, addr) basereg = subreg_regno (addr); else if (GET_CODE (addr) == CONST_INT) offset = INTVAL (addr); + +#if TARGET_ABI_OPEN_VMS + else if (GET_CODE (addr) == SYMBOL_REF) + { + fprintf (file, "%s", XSTR (addr, 0)); + return; + } + else if (GET_CODE (addr) == CONST + && GET_CODE (XEXP (addr, 0)) == PLUS + && GET_CODE (XEXP (XEXP (addr, 0), 0)) == SYMBOL_REF) + { + fprintf (file, "%s+%d", + XSTR (XEXP (XEXP (addr, 0), 0), 0), + INTVAL (XEXP (XEXP (addr, 0), 1))); + return; + } +#endif + else abort (); - fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset); - fprintf (file, "($%d)", basereg); + fprintf (file, HOST_WIDE_INT_PRINT_DEC "($%d)", offset, basereg); } /* Emit RTL insns to initialize the variable parts of a trampoline at @@ -5290,20 +6202,21 @@ alpha_initialize_trampoline (tramp, fnaddr, cxt, fnofs, cxtofs, jmpofs) OPTAB_WIDEN); temp = expand_shift (RSHIFT_EXPR, Pmode, temp, build_int_2 (2, 0), NULL_RTX, 1); - temp = expand_and (gen_lowpart (SImode, temp), GEN_INT (0x3fff), 0); + temp = expand_and (SImode, gen_lowpart (SImode, temp), + GEN_INT (0x3fff), 0); /* Merge in the hint. */ addr = memory_address (SImode, plus_constant (tramp, jmpofs)); temp1 = force_reg (SImode, gen_rtx_MEM (SImode, addr)); - temp1 = expand_and (temp1, GEN_INT (0xffffc000), NULL_RTX); + temp1 = expand_and (SImode, temp1, GEN_INT (0xffffc000), NULL_RTX); temp1 = expand_binop (SImode, ior_optab, temp1, temp, temp1, 1, OPTAB_WIDEN); emit_move_insn (gen_rtx_MEM (SImode, addr), temp1); } #ifdef TRANSFER_FROM_TRAMPOLINE - emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"), - 0, VOIDmode, 1, addr, Pmode); + emit_library_call (init_one_libfunc ("__enable_execute_stack"), + 0, VOIDmode, 1, tramp, Pmode); #endif if (jmpofs >= 0) @@ -5449,7 +6362,7 @@ alpha_build_va_list () if (TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK) return ptr_type_node; - record = make_lang_type (RECORD_TYPE); + record = (*lang_hooks.types.make_type) (RECORD_TYPE); type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); TREE_CHAIN (record) = type_decl; TYPE_NAME (record) = type_decl; @@ -5471,9 +6384,55 @@ alpha_build_va_list () return record; } +/* Perform any needed actions needed for a function that is receiving a + variable number of arguments. + + On the Alpha, we allocate space for all 12 arg registers, but only + push those that are remaining. However, if NO registers need to be + saved, don't allocate any space. This is not only because we won't + need the space, but because AP includes the current_pretend_args_size + and we don't want to mess up any ap-relative addresses already made. + + If we are not to use the floating-point registers, save the integer + registers where we would put the floating-point registers. This is + not the most efficient way to implement varargs with just one register + class, but it isn't worth doing anything more efficient in this rare + case. */ + +void +alpha_setup_incoming_varargs(cum, mode, type, pretend_size, no_rtl) + CUMULATIVE_ARGS cum; + enum machine_mode mode ATTRIBUTE_UNUSED; + tree type ATTRIBUTE_UNUSED; + int *pretend_size; + int no_rtl; +{ + if (cum >= 6) + return; + + if (!no_rtl) + { + int set = get_varargs_alias_set (); + rtx tmp; + + tmp = gen_rtx_MEM (BLKmode, + plus_constant (virtual_incoming_args_rtx, + (cum + 6) * UNITS_PER_WORD)); + set_mem_alias_set (tmp, set); + move_block_from_reg (16 + cum, tmp, 6 - cum); + + tmp = gen_rtx_MEM (BLKmode, + plus_constant (virtual_incoming_args_rtx, + cum * UNITS_PER_WORD)); + set_mem_alias_set (tmp, set); + move_block_from_reg (16 + (TARGET_FPREGS ? 32 : 0) + cum, tmp, + 6 - cum); + } + *pretend_size = 12 * UNITS_PER_WORD; +} + void -alpha_va_start (stdarg_p, valist, nextarg) - int stdarg_p; +alpha_va_start (valist, nextarg) tree valist; rtx nextarg ATTRIBUTE_UNUSED; { @@ -5484,7 +6443,7 @@ alpha_va_start (stdarg_p, valist, nextarg) return; if (TARGET_ABI_UNICOSMK) - std_expand_builtin_va_start (stdarg_p, valist, nextarg); + std_expand_builtin_va_start (valist, nextarg); /* For Unix, SETUP_INCOMING_VARARGS moves the starting address base up by 48, storing fp arg registers in the first 48 bytes, and the @@ -5493,12 +6452,15 @@ alpha_va_start (stdarg_p, valist, nextarg) If no integer registers need be stored, then we must subtract 48 in order to account for the integer arg registers which are counted - in argsize above, but which are not actually stored on the stack. */ + in argsize above, but which are not actually stored on the stack. + Must further be careful here about structures straddling the last + integer argument register; that futzes with pretend_args_size, + which changes the meaning of AP. */ - if (NUM_ARGS <= 5 + stdarg_p) + if (NUM_ARGS <= 6) offset = TARGET_ABI_OPEN_VMS ? UNITS_PER_WORD : 6 * UNITS_PER_WORD; else - offset = -6 * UNITS_PER_WORD; + offset = -6 * UNITS_PER_WORD + current_function_pretend_args_size; if (TARGET_ABI_OPEN_VMS) { @@ -5537,9 +6499,8 @@ rtx alpha_va_arg (valist, type) tree valist, type; { - HOST_WIDE_INT tsize; rtx addr; - tree t; + tree t, type_size, rounded_size; tree offset_field, base_field, addr_tree, addend; tree wide_type, wide_ofs; int indirect = 0; @@ -5547,7 +6508,18 @@ alpha_va_arg (valist, type) if (TARGET_ABI_OPEN_VMS || TARGET_ABI_UNICOSMK) return std_expand_builtin_va_arg (valist, type); - tsize = ((TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_UNIT + 7) / 8) * 8; + if (type == error_mark_node + || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL + || TREE_OVERFLOW (type_size)) + rounded_size = size_zero_node; + else + rounded_size = fold (build (MULT_EXPR, sizetype, + fold (build (TRUNC_DIV_EXPR, sizetype, + fold (build (PLUS_EXPR, sizetype, + type_size, + size_int (7))), + size_int (8))), + size_int (8))); base_field = TYPE_FIELDS (TREE_TYPE (valist)); offset_field = TREE_CHAIN (base_field); @@ -5557,6 +6529,17 @@ alpha_va_arg (valist, type) offset_field = build (COMPONENT_REF, TREE_TYPE (offset_field), valist, offset_field); + /* If the type could not be passed in registers, skip the block + reserved for the registers. */ + if (MUST_PASS_IN_STACK (TYPE_MODE (type), type)) + { + t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field, + build (MAX_EXPR, TREE_TYPE (offset_field), + offset_field, build_int_2 (6*8, 0))); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } + wide_type = make_signed_type (64); wide_ofs = save_expr (build1 (CONVERT_EXPR, wide_type, offset_field)); @@ -5565,7 +6548,7 @@ alpha_va_arg (valist, type) if (TYPE_MODE (type) == TFmode || TYPE_MODE (type) == TCmode) { indirect = 1; - tsize = UNITS_PER_WORD; + rounded_size = size_int (UNITS_PER_WORD); } else if (FLOAT_TYPE_P (type)) { @@ -5589,7 +6572,7 @@ alpha_va_arg (valist, type) t = build (MODIFY_EXPR, TREE_TYPE (offset_field), offset_field, build (PLUS_EXPR, TREE_TYPE (offset_field), - offset_field, build_int_2 (tsize, 0))); + offset_field, rounded_size)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); @@ -5602,6 +6585,310 @@ alpha_va_arg (valist, type) return addr; } +/* Builtins. */ + +enum alpha_builtin +{ + ALPHA_BUILTIN_CMPBGE, + ALPHA_BUILTIN_EXTBL, + ALPHA_BUILTIN_EXTWL, + ALPHA_BUILTIN_EXTLL, + ALPHA_BUILTIN_EXTQL, + ALPHA_BUILTIN_EXTWH, + ALPHA_BUILTIN_EXTLH, + ALPHA_BUILTIN_EXTQH, + ALPHA_BUILTIN_INSBL, + ALPHA_BUILTIN_INSWL, + ALPHA_BUILTIN_INSLL, + ALPHA_BUILTIN_INSQL, + ALPHA_BUILTIN_INSWH, + ALPHA_BUILTIN_INSLH, + ALPHA_BUILTIN_INSQH, + ALPHA_BUILTIN_MSKBL, + ALPHA_BUILTIN_MSKWL, + ALPHA_BUILTIN_MSKLL, + ALPHA_BUILTIN_MSKQL, + ALPHA_BUILTIN_MSKWH, + ALPHA_BUILTIN_MSKLH, + ALPHA_BUILTIN_MSKQH, + ALPHA_BUILTIN_UMULH, + ALPHA_BUILTIN_ZAP, + ALPHA_BUILTIN_ZAPNOT, + ALPHA_BUILTIN_AMASK, + ALPHA_BUILTIN_IMPLVER, + ALPHA_BUILTIN_RPCC, + ALPHA_BUILTIN_THREAD_POINTER, + ALPHA_BUILTIN_SET_THREAD_POINTER, + + /* TARGET_MAX */ + ALPHA_BUILTIN_MINUB8, + ALPHA_BUILTIN_MINSB8, + ALPHA_BUILTIN_MINUW4, + ALPHA_BUILTIN_MINSW4, + ALPHA_BUILTIN_MAXUB8, + ALPHA_BUILTIN_MAXSB8, + ALPHA_BUILTIN_MAXUW4, + ALPHA_BUILTIN_MAXSW4, + ALPHA_BUILTIN_PERR, + ALPHA_BUILTIN_PKLB, + ALPHA_BUILTIN_PKWB, + ALPHA_BUILTIN_UNPKBL, + ALPHA_BUILTIN_UNPKBW, + + /* TARGET_CIX */ + ALPHA_BUILTIN_CTTZ, + ALPHA_BUILTIN_CTLZ, + ALPHA_BUILTIN_CTPOP, + + ALPHA_BUILTIN_max +}; + +static unsigned int const code_for_builtin[ALPHA_BUILTIN_max] = { + CODE_FOR_builtin_cmpbge, + CODE_FOR_builtin_extbl, + CODE_FOR_builtin_extwl, + CODE_FOR_builtin_extll, + CODE_FOR_builtin_extql, + CODE_FOR_builtin_extwh, + CODE_FOR_builtin_extlh, + CODE_FOR_builtin_extqh, + CODE_FOR_builtin_insbl, + CODE_FOR_builtin_inswl, + CODE_FOR_builtin_insll, + CODE_FOR_builtin_insql, + CODE_FOR_builtin_inswh, + CODE_FOR_builtin_inslh, + CODE_FOR_builtin_insqh, + CODE_FOR_builtin_mskbl, + CODE_FOR_builtin_mskwl, + CODE_FOR_builtin_mskll, + CODE_FOR_builtin_mskql, + CODE_FOR_builtin_mskwh, + CODE_FOR_builtin_msklh, + CODE_FOR_builtin_mskqh, + CODE_FOR_umuldi3_highpart, + CODE_FOR_builtin_zap, + CODE_FOR_builtin_zapnot, + CODE_FOR_builtin_amask, + CODE_FOR_builtin_implver, + CODE_FOR_builtin_rpcc, + CODE_FOR_load_tp, + CODE_FOR_set_tp, + + /* TARGET_MAX */ + CODE_FOR_builtin_minub8, + CODE_FOR_builtin_minsb8, + CODE_FOR_builtin_minuw4, + CODE_FOR_builtin_minsw4, + CODE_FOR_builtin_maxub8, + CODE_FOR_builtin_maxsb8, + CODE_FOR_builtin_maxuw4, + CODE_FOR_builtin_maxsw4, + CODE_FOR_builtin_perr, + CODE_FOR_builtin_pklb, + CODE_FOR_builtin_pkwb, + CODE_FOR_builtin_unpkbl, + CODE_FOR_builtin_unpkbw, + + /* TARGET_CIX */ + CODE_FOR_builtin_cttz, + CODE_FOR_builtin_ctlz, + CODE_FOR_builtin_ctpop +}; + +struct alpha_builtin_def +{ + const char *name; + enum alpha_builtin code; + unsigned int target_mask; +}; + +static struct alpha_builtin_def const zero_arg_builtins[] = { + { "__builtin_alpha_implver", ALPHA_BUILTIN_IMPLVER, 0 }, + { "__builtin_alpha_rpcc", ALPHA_BUILTIN_RPCC, 0 } +}; + +static struct alpha_builtin_def const one_arg_builtins[] = { + { "__builtin_alpha_amask", ALPHA_BUILTIN_AMASK, 0 }, + { "__builtin_alpha_pklb", ALPHA_BUILTIN_PKLB, MASK_MAX }, + { "__builtin_alpha_pkwb", ALPHA_BUILTIN_PKWB, MASK_MAX }, + { "__builtin_alpha_unpkbl", ALPHA_BUILTIN_UNPKBL, MASK_MAX }, + { "__builtin_alpha_unpkbw", ALPHA_BUILTIN_UNPKBW, MASK_MAX }, + { "__builtin_alpha_cttz", ALPHA_BUILTIN_CTTZ, MASK_CIX }, + { "__builtin_alpha_ctlz", ALPHA_BUILTIN_CTLZ, MASK_CIX }, + { "__builtin_alpha_ctpop", ALPHA_BUILTIN_CTPOP, MASK_CIX } +}; + +static struct alpha_builtin_def const two_arg_builtins[] = { + { "__builtin_alpha_cmpbge", ALPHA_BUILTIN_CMPBGE, 0 }, + { "__builtin_alpha_extbl", ALPHA_BUILTIN_EXTBL, 0 }, + { "__builtin_alpha_extwl", ALPHA_BUILTIN_EXTWL, 0 }, + { "__builtin_alpha_extll", ALPHA_BUILTIN_EXTLL, 0 }, + { "__builtin_alpha_extql", ALPHA_BUILTIN_EXTQL, 0 }, + { "__builtin_alpha_extwh", ALPHA_BUILTIN_EXTWH, 0 }, + { "__builtin_alpha_extlh", ALPHA_BUILTIN_EXTLH, 0 }, + { "__builtin_alpha_extqh", ALPHA_BUILTIN_EXTQH, 0 }, + { "__builtin_alpha_insbl", ALPHA_BUILTIN_INSBL, 0 }, + { "__builtin_alpha_inswl", ALPHA_BUILTIN_INSWL, 0 }, + { "__builtin_alpha_insll", ALPHA_BUILTIN_INSLL, 0 }, + { "__builtin_alpha_insql", ALPHA_BUILTIN_INSQL, 0 }, + { "__builtin_alpha_inswh", ALPHA_BUILTIN_INSWH, 0 }, + { "__builtin_alpha_inslh", ALPHA_BUILTIN_INSLH, 0 }, + { "__builtin_alpha_insqh", ALPHA_BUILTIN_INSQH, 0 }, + { "__builtin_alpha_mskbl", ALPHA_BUILTIN_MSKBL, 0 }, + { "__builtin_alpha_mskwl", ALPHA_BUILTIN_MSKWL, 0 }, + { "__builtin_alpha_mskll", ALPHA_BUILTIN_MSKLL, 0 }, + { "__builtin_alpha_mskql", ALPHA_BUILTIN_MSKQL, 0 }, + { "__builtin_alpha_mskwh", ALPHA_BUILTIN_MSKWH, 0 }, + { "__builtin_alpha_msklh", ALPHA_BUILTIN_MSKLH, 0 }, + { "__builtin_alpha_mskqh", ALPHA_BUILTIN_MSKQH, 0 }, + { "__builtin_alpha_umulh", ALPHA_BUILTIN_UMULH, 0 }, + { "__builtin_alpha_zap", ALPHA_BUILTIN_ZAP, 0 }, + { "__builtin_alpha_zapnot", ALPHA_BUILTIN_ZAPNOT, 0 }, + { "__builtin_alpha_minub8", ALPHA_BUILTIN_MINUB8, MASK_MAX }, + { "__builtin_alpha_minsb8", ALPHA_BUILTIN_MINSB8, MASK_MAX }, + { "__builtin_alpha_minuw4", ALPHA_BUILTIN_MINUW4, MASK_MAX }, + { "__builtin_alpha_minsw4", ALPHA_BUILTIN_MINSW4, MASK_MAX }, + { "__builtin_alpha_maxub8", ALPHA_BUILTIN_MAXUB8, MASK_MAX }, + { "__builtin_alpha_maxsb8", ALPHA_BUILTIN_MAXSB8, MASK_MAX }, + { "__builtin_alpha_maxuw4", ALPHA_BUILTIN_MAXUW4, MASK_MAX }, + { "__builtin_alpha_maxsw4", ALPHA_BUILTIN_MAXSW4, MASK_MAX }, + { "__builtin_alpha_perr", ALPHA_BUILTIN_PERR, MASK_MAX } +}; + +static void +alpha_init_builtins () +{ + const struct alpha_builtin_def *p; + tree ftype; + size_t i; + + ftype = build_function_type (long_integer_type_node, void_list_node); + + p = zero_arg_builtins; + for (i = 0; i < ARRAY_SIZE (zero_arg_builtins); ++i, ++p) + if ((target_flags & p->target_mask) == p->target_mask) + builtin_function (p->name, ftype, p->code, BUILT_IN_MD, + NULL, NULL_TREE); + + ftype = build_function_type_list (long_integer_type_node, + long_integer_type_node, NULL_TREE); + + p = one_arg_builtins; + for (i = 0; i < ARRAY_SIZE (one_arg_builtins); ++i, ++p) + if ((target_flags & p->target_mask) == p->target_mask) + builtin_function (p->name, ftype, p->code, BUILT_IN_MD, + NULL, NULL_TREE); + + ftype = build_function_type_list (long_integer_type_node, + long_integer_type_node, + long_integer_type_node, NULL_TREE); + + p = two_arg_builtins; + for (i = 0; i < ARRAY_SIZE (two_arg_builtins); ++i, ++p) + if ((target_flags & p->target_mask) == p->target_mask) + builtin_function (p->name, ftype, p->code, BUILT_IN_MD, + NULL, NULL_TREE); + + ftype = build_function_type (ptr_type_node, void_list_node); + builtin_function ("__builtin_thread_pointer", ftype, + ALPHA_BUILTIN_THREAD_POINTER, BUILT_IN_MD, + NULL, NULL_TREE); + + ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + builtin_function ("__builtin_set_thread_pointer", ftype, + ALPHA_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD, + NULL, NULL_TREE); +} + +/* Expand an expression EXP that calls a built-in function, + with result going to TARGET if that's convenient + (and in mode MODE if that's convenient). + SUBTARGET may be used as the target for computing one of EXP's operands. + IGNORE is nonzero if the value is to be ignored. */ + +static rtx +alpha_expand_builtin (exp, target, subtarget, mode, ignore) + tree exp; + rtx target; + rtx subtarget ATTRIBUTE_UNUSED; + enum machine_mode mode ATTRIBUTE_UNUSED; + int ignore ATTRIBUTE_UNUSED; +{ +#define MAX_ARGS 2 + + tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + unsigned int fcode = DECL_FUNCTION_CODE (fndecl); + tree arglist = TREE_OPERAND (exp, 1); + enum insn_code icode; + rtx op[MAX_ARGS], pat; + int arity; + bool nonvoid; + + if (fcode >= ALPHA_BUILTIN_max) + internal_error ("bad builtin fcode"); + icode = code_for_builtin[fcode]; + if (icode == 0) + internal_error ("bad builtin fcode"); + + nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; + + for (arglist = TREE_OPERAND (exp, 1), arity = 0; + arglist; + arglist = TREE_CHAIN (arglist), arity++) + { + const struct insn_operand_data *insn_op; + + tree arg = TREE_VALUE (arglist); + if (arg == error_mark_node) + return NULL_RTX; + if (arity > MAX_ARGS) + return NULL_RTX; + + insn_op = &insn_data[icode].operand[arity + nonvoid]; + + op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0); + + if (!(*insn_op->predicate) (op[arity], insn_op->mode)) + op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]); + } + + if (nonvoid) + { + enum machine_mode tmode = insn_data[icode].operand[0].mode; + if (!target + || GET_MODE (target) != tmode + || !(*insn_data[icode].operand[0].predicate) (target, tmode)) + target = gen_reg_rtx (tmode); + } + + switch (arity) + { + case 0: + pat = GEN_FCN (icode) (target); + break; + case 1: + if (nonvoid) + pat = GEN_FCN (icode) (target, op[0]); + else + pat = GEN_FCN (icode) (op[0]); + break; + case 2: + pat = GEN_FCN (icode) (target, op[0], op[1]); + break; + default: + abort (); + } + if (!pat) + return NULL_RTX; + emit_insn (pat); + + if (nonvoid) + return target; + else + return const0_rtx; +} + /* This page contains routines that are used to determine what the function prologue and epilogue code will do and write them out. */ @@ -5613,7 +6900,8 @@ alpha_va_arg (valist, type) descriptior to generate. */ /* Nonzero if we need a stack procedure. */ -static int alpha_is_stack_procedure; +enum alpha_procedure_types {PT_NULL = 0, PT_REGISTER = 1, PT_STACK = 2}; +static enum alpha_procedure_types alpha_procedure_type; /* Register number (either FP or SP) that is used to unwind the frame. */ static int vms_unwind_regno; @@ -5637,46 +6925,49 @@ alpha_sa_mask (imaskP, fmaskP) unsigned long fmask = 0; unsigned int i; -#ifdef ASM_OUTPUT_MI_THUNK - if (!current_function_is_thunk) -#endif + /* Irritatingly, there are two kinds of thunks -- those created with + TARGET_ASM_OUTPUT_MI_THUNK and those with DECL_THUNK_P that go + through the regular part of the compiler. In the + TARGET_ASM_OUTPUT_MI_THUNK case we don't have valid register life + info, but assemble_start_function wants to output .frame and + .mask directives. */ + if (current_function_is_thunk && !no_new_pseudos) { - if (TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure) - imask |= (1L << HARD_FRAME_POINTER_REGNUM); + *imaskP = 0; + *fmaskP = 0; + return; + } - /* One for every register we have to save. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (! fixed_regs[i] && ! call_used_regs[i] - && regs_ever_live[i] && i != REG_RA - && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM)) - { - if (i < 32) - imask |= (1L << i); - else - fmask |= (1L << (i - 32)); - } + if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK) + imask |= (1UL << HARD_FRAME_POINTER_REGNUM); - /* We need to restore these for the handler. */ - if (current_function_calls_eh_return) - { - for (i = 0; ; ++i) - { - unsigned regno = EH_RETURN_DATA_REGNO (i); - if (regno == INVALID_REGNUM) - break; - imask |= 1L << regno; - } - } + /* One for every register we have to save. */ + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (! fixed_regs[i] && ! call_used_regs[i] + && regs_ever_live[i] && i != REG_RA + && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM)) + { + if (i < 32) + imask |= (1UL << i); + else + fmask |= (1UL << (i - 32)); + } + + /* We need to restore these for the handler. */ + if (current_function_calls_eh_return) + for (i = 0; ; ++i) + { + unsigned regno = EH_RETURN_DATA_REGNO (i); + if (regno == INVALID_REGNUM) + break; + imask |= 1UL << regno; + } - if (!TARGET_ABI_UNICOSMK) - { - /* If any register spilled, then spill the return address also. */ - /* ??? This is required by the Digital stack unwind specification - and isn't needed if we're doing Dwarf2 unwinding. */ - if (imask || fmask || alpha_ra_ever_killed ()) - imask |= (1L << REG_RA); - } - } + /* If any register spilled, then spill the return address also. */ + /* ??? This is required by the Digital stack unwind specification + and isn't needed if we're doing Dwarf2 unwinding. */ + if (imask || fmask || alpha_ra_ever_killed ()) + imask |= (1UL << REG_RA); *imaskP = imask; *fmaskP = fmask; @@ -5685,34 +6976,23 @@ 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; + alpha_sa_mask (&mask[0], &mask[1]); + + if (TARGET_ABI_UNICOSMK) + { + if (mask[0] || mask[1]) + sa_size = 14; + } else -#endif { - if (TARGET_ABI_UNICOSMK) - { - for (i = 9; i < 15 && sa_size == 0; i++) - if (! fixed_regs[i] && ! call_used_regs[i] - && regs_ever_live[i]) - sa_size = 14; - for (i = 32 + 2; i < 32 + 10 && sa_size == 0; i++) - if (! fixed_regs[i] && ! call_used_regs[i] - && regs_ever_live[i]) - sa_size = 14; - } - else - { - /* One for every register we have to save. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (! fixed_regs[i] && ! call_used_regs[i] - && regs_ever_live[i] && i != REG_RA) - sa_size++; - } + for (j = 0; j < 2; ++j) + for (i = 0; i < 32; ++i) + if ((mask[j] >> i) & 1) + sa_size++; } if (TARGET_ABI_UNICOSMK) @@ -5723,18 +7003,16 @@ alpha_sa_size () use alloca and have not determined that we need a frame for other reasons. */ - alpha_is_stack_procedure = sa_size != 0 - || alpha_ra_ever_killed () - || get_frame_size() != 0 - || current_function_outgoing_args_size - || current_function_varargs - || current_function_stdarg - || current_function_calls_alloca - || frame_pointer_needed; + alpha_procedure_type + = (sa_size || get_frame_size() != 0 + || current_function_outgoing_args_size + || current_function_stdarg || current_function_calls_alloca + || frame_pointer_needed) + ? PT_STACK : PT_REGISTER; /* Always reserve space for saving callee-saved registers if we need a frame as required by the calling convention. */ - if (alpha_is_stack_procedure) + if (alpha_procedure_type == PT_STACK) sa_size = 14; } else if (TARGET_ABI_OPEN_VMS) @@ -5742,17 +7020,29 @@ alpha_sa_size () /* Start by assuming we can use a register procedure if we don't make any calls (REG_RA not used) or need to save any registers and a stack procedure if we do. */ - alpha_is_stack_procedure = sa_size != 0 || alpha_ra_ever_killed (); + if ((mask[0] >> REG_RA) & 1) + alpha_procedure_type = PT_STACK; + else if (get_frame_size() != 0) + alpha_procedure_type = PT_REGISTER; + else + alpha_procedure_type = PT_NULL; + + /* Don't reserve space for saving FP & RA yet. Do that later after we've + made the final decision on stack procedure vs register procedure. */ + if (alpha_procedure_type == PT_STACK) + sa_size -= 2; /* Decide whether to refer to objects off our PV via FP or PV. If we need FP for something else or if we receive a nonlocal goto (which expects PV to contain the value), we must use PV. Otherwise, start by assuming we can use FP. */ - vms_base_regno = (frame_pointer_needed - || current_function_has_nonlocal_label - || alpha_is_stack_procedure - || current_function_outgoing_args_size - ? REG_PV : HARD_FRAME_POINTER_REGNUM); + + vms_base_regno + = (frame_pointer_needed + || current_function_has_nonlocal_label + || alpha_procedure_type == PT_STACK + || current_function_outgoing_args_size) + ? REG_PV : HARD_FRAME_POINTER_REGNUM; /* If we want to copy PV into FP, we need to find some register in which to save FP. */ @@ -5763,24 +7053,21 @@ alpha_sa_size () if (! fixed_regs[i] && call_used_regs[i] && ! regs_ever_live[i]) vms_save_fp_regno = i; - if (vms_save_fp_regno == -1) - vms_base_regno = REG_PV, alpha_is_stack_procedure = 1; + if (vms_save_fp_regno == -1 && alpha_procedure_type == PT_REGISTER) + vms_base_regno = REG_PV, alpha_procedure_type = PT_STACK; + else if (alpha_procedure_type == PT_NULL) + vms_base_regno = REG_PV; /* Stack unwinding should be done via FP unless we use it for PV. */ vms_unwind_regno = (vms_base_regno == REG_PV ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM); /* If this is a stack procedure, allow space for saving FP and RA. */ - if (alpha_is_stack_procedure) + if (alpha_procedure_type == PT_STACK) sa_size += 2; } else { - /* If some registers were saved but not RA, RA must also be saved, - so leave space for it. */ - if (!TARGET_ABI_UNICOSMK && (sa_size != 0 || alpha_ra_ever_killed ())) - sa_size++; - /* Our size must be even (multiple of 16 bytes). */ if (sa_size & 1) sa_size++; @@ -5789,11 +7076,35 @@ alpha_sa_size () return sa_size * 8; } +/* Define the offset between two registers, one to be eliminated, + and the other its replacement, at the start of a routine. */ + +HOST_WIDE_INT +alpha_initial_elimination_offset (from, to) + unsigned int from, to ATTRIBUTE_UNUSED; +{ + HOST_WIDE_INT ret; + + ret = alpha_sa_size (); + ret += ALPHA_ROUND (current_function_outgoing_args_size); + + if (from == FRAME_POINTER_REGNUM) + ; + else if (from == ARG_POINTER_REGNUM) + ret += (ALPHA_ROUND (get_frame_size () + + current_function_pretend_args_size) + - current_function_pretend_args_size); + else + abort (); + + return ret; +} + int alpha_pv_save_size () { alpha_sa_size (); - return alpha_is_stack_procedure ? 8 : 0; + return alpha_procedure_type == PT_STACK ? 8 : 0; } int @@ -5808,18 +7119,27 @@ alpha_using_fp () const struct attribute_spec vms_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ - { "overlaid", 0, 0, true, false, false, NULL }, - { NULL, 0, 0, false, false, false, NULL } + { "overlaid", 0, 0, true, false, false, NULL }, + { "global", 0, 0, true, false, false, NULL }, + { "initialize", 0, 0, true, false, false, NULL }, + { NULL, 0, 0, false, false, false, NULL } }; #endif static int -find_lo_sum (px, data) +find_lo_sum_using_gp (px, data) rtx *px; void *data ATTRIBUTE_UNUSED; { - return GET_CODE (*px) == LO_SUM; + return GET_CODE (*px) == LO_SUM && XEXP (*px, 0) == pic_offset_table_rtx; +} + +int +alpha_find_lo_sum_using_gp (insn) + rtx insn; +{ + return for_each_rtx (&PATTERN (insn), find_lo_sum_using_gp, NULL) > 0; } static int @@ -5831,13 +7151,11 @@ alpha_does_function_need_gp () if (! TARGET_ABI_OSF) return 0; - if (TARGET_PROFILING_NEEDS_GP && profile_flag) + if (TARGET_PROFILING_NEEDS_GP && current_function_profile) return 1; -#ifdef ASM_OUTPUT_MI_THUNK if (current_function_is_thunk) return 1; -#endif /* If we need a GP (we have a LDSYM insn or a CALL_INSN), load it first. Even if we are a static function, we still need to do this in case @@ -5850,15 +7168,9 @@ alpha_does_function_need_gp () for (; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn) && GET_CODE (PATTERN (insn)) != USE - && GET_CODE (PATTERN (insn)) != CLOBBER) - { - enum attr_type type = get_attr_type (insn); - if (type == TYPE_LDSYM || type == TYPE_JSR) - return 1; - if (TARGET_EXPLICIT_RELOCS - && for_each_rtx (&PATTERN (insn), find_lo_sum, NULL) > 0) - return 1; - } + && GET_CODE (PATTERN (insn)) != CLOBBER + && get_attr_usegp (insn)) + return 1; return 0; } @@ -5885,22 +7197,30 @@ alpha_write_verstamp (file) static rtx set_frame_related_p () { - rtx seq = gen_sequence (); + rtx seq = get_insns (); + rtx insn; + end_sequence (); - if (GET_CODE (seq) == SEQUENCE) + if (!seq) + return NULL_RTX; + + if (INSN_P (seq)) { - int i = XVECLEN (seq, 0); - while (--i >= 0) - RTX_FRAME_RELATED_P (XVECEXP (seq, 0, i)) = 1; - return emit_insn (seq); + insn = seq; + while (insn != NULL_RTX) + { + RTX_FRAME_RELATED_P (insn) = 1; + insn = NEXT_INSN (insn); + } + seq = emit_insn (seq); } else { seq = emit_insn (seq); RTX_FRAME_RELATED_P (seq) = 1; - return seq; } + return seq; } #define FRP(exp) (start_sequence (), exp, set_frame_related_p ()) @@ -5942,13 +7262,13 @@ alpha_expand_prologue () frame_size = get_frame_size (); if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size - + (alpha_is_stack_procedure ? 8 : 0) + + (alpha_procedure_type == PT_STACK ? 8 : 0) + frame_size + current_function_pretend_args_size); else if (TARGET_ABI_UNICOSMK) /* We have to allocate space for the DSIB if we generate a frame. */ frame_size = ALPHA_ROUND (sa_size - + (alpha_is_stack_procedure ? 48 : 0)) + + (alpha_procedure_type == PT_STACK ? 48 : 0)) + ALPHA_ROUND (frame_size + current_function_outgoing_args_size); else @@ -5976,7 +7296,7 @@ alpha_expand_prologue () the call to mcount ourselves, rather than having the linker do it magically in response to -pg. Since _mcount has special linkage, don't represent the call as a call. */ - if (TARGET_PROFILING_NEEDS_GP && profile_flag) + if (TARGET_PROFILING_NEEDS_GP && current_function_profile) emit_insn (gen_prologue_mcount ()); if (TARGET_ABI_UNICOSMK) @@ -6103,7 +7423,7 @@ alpha_expand_prologue () } /* Save regs in stack order. Beginning with VMS PV. */ - if (TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure) + if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK) { mem = gen_rtx_MEM (DImode, stack_pointer_rtx); set_mem_alias_set (mem, alpha_sr_alias_set); @@ -6111,18 +7431,18 @@ alpha_expand_prologue () } /* Save register RA next. */ - if (imask & (1L << REG_RA)) + if (imask & (1UL << REG_RA)) { mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (mem, gen_rtx_REG (DImode, REG_RA))); - imask &= ~(1L << REG_RA); + imask &= ~(1UL << REG_RA); reg_offset += 8; } /* Now save any other registers required to be saved. */ for (i = 0; i < 32; i++) - if (imask & (1L << i)) + if (imask & (1UL << i)) { mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); @@ -6131,7 +7451,7 @@ alpha_expand_prologue () } for (i = 0; i < 32; i++) - if (fmask & (1L << i)) + if (fmask & (1UL << i)) { mem = gen_rtx_MEM (DFmode, plus_constant (sa_reg, reg_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); @@ -6139,7 +7459,7 @@ alpha_expand_prologue () reg_offset += 8; } } - else if (TARGET_ABI_UNICOSMK && alpha_is_stack_procedure) + else if (TARGET_ABI_UNICOSMK && alpha_procedure_type == PT_STACK) { /* The standard frame on the T3E includes space for saving registers. We just have to use it. We don't have to save the return address and @@ -6147,7 +7467,7 @@ alpha_expand_prologue () reg_offset = -56; for (i = 9; i < 15; i++) - if (imask & (1L << i)) + if (imask & (1UL << i)) { mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx, reg_offset)); @@ -6156,7 +7476,7 @@ alpha_expand_prologue () reg_offset -= 8; } for (i = 2; i < 10; i++) - if (fmask & (1L << i)) + if (fmask & (1UL << i)) { mem = gen_rtx_MEM (DFmode, plus_constant (hard_frame_pointer_rtx, reg_offset)); @@ -6168,26 +7488,47 @@ alpha_expand_prologue () if (TARGET_ABI_OPEN_VMS) { - if (!alpha_is_stack_procedure) - /* Register frame procedures save the fp. */ - /* ??? Ought to have a dwarf2 save for this. */ + if (alpha_procedure_type == PT_REGISTER) + /* Register frame procedures save the fp. + ?? Ought to have a dwarf2 save for this. */ emit_move_insn (gen_rtx_REG (DImode, vms_save_fp_regno), hard_frame_pointer_rtx); - if (vms_base_regno != REG_PV) + if (alpha_procedure_type != PT_NULL && vms_base_regno != REG_PV) emit_insn (gen_force_movdi (gen_rtx_REG (DImode, vms_base_regno), gen_rtx_REG (DImode, REG_PV))); - if (vms_unwind_regno == HARD_FRAME_POINTER_REGNUM) + if (alpha_procedure_type != PT_NULL + && vms_unwind_regno == HARD_FRAME_POINTER_REGNUM) FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx)); /* If we have to allocate space for outgoing args, do it now. */ if (current_function_outgoing_args_size != 0) - FRP (emit_move_insn - (stack_pointer_rtx, - plus_constant (hard_frame_pointer_rtx, - - (ALPHA_ROUND - (current_function_outgoing_args_size))))); + { + rtx seq + = emit_move_insn (stack_pointer_rtx, + plus_constant + (hard_frame_pointer_rtx, + - (ALPHA_ROUND + (current_function_outgoing_args_size)))); + + /* Only set FRAME_RELATED_P on the stack adjustment we just emitted + if ! frame_pointer_needed. Setting the bit will change the CFA + computation rule to use sp again, which would be wrong if we had + frame_pointer_needed, as this means sp might move unpredictably + later on. + + Also, note that + frame_pointer_needed + => vms_unwind_regno == HARD_FRAME_POINTER_REGNUM + and + current_function_outgoing_args_size != 0 + => alpha_procedure_type != PT_NULL, + + so when we are not setting the bit here, we are guaranteed to + have emited an FRP frame pointer update just before. */ + RTX_FRAME_RELATED_P (seq) = ! frame_pointer_needed; + } } else if (!TARGET_ABI_UNICOSMK) { @@ -6231,7 +7572,7 @@ alpha_start_function (file, fnname, decl) /* Stack space needed for pushing registers clobbered by us. */ HOST_WIDE_INT sa_size; /* Complete stack size needed. */ - HOST_WIDE_INT frame_size; + unsigned HOST_WIDE_INT frame_size; /* Offset from base reg to register save area. */ HOST_WIDE_INT reg_offset; char *entry_label = (char *) alloca (strlen (fnname) + 6); @@ -6251,12 +7592,12 @@ alpha_start_function (file, fnname, decl) frame_size = get_frame_size (); if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size - + (alpha_is_stack_procedure ? 8 : 0) + + (alpha_procedure_type == PT_STACK ? 8 : 0) + frame_size + current_function_pretend_args_size); else if (TARGET_ABI_UNICOSMK) frame_size = ALPHA_ROUND (sa_size - + (alpha_is_stack_procedure ? 48 : 0)) + + (alpha_procedure_type == PT_STACK ? 48 : 0)) + ALPHA_ROUND (frame_size + current_function_outgoing_args_size); else @@ -6303,7 +7644,9 @@ alpha_start_function (file, fnname, decl) /* If the function needs GP, we'll write the "..ng" label there. Otherwise, do it here. */ - if (TARGET_ABI_OSF && ! alpha_function_needs_gp) + if (TARGET_ABI_OSF + && ! alpha_function_needs_gp + && ! current_function_is_thunk) { putc ('$', file); assemble_name (file, fnname); @@ -6345,23 +7688,17 @@ alpha_start_function (file, fnname, decl) if (TARGET_ABI_UNICOSMK) ; else if (TARGET_ABI_OPEN_VMS) - { - fprintf (file, "\t.frame $%d,", vms_unwind_regno); - fprintf (file, HOST_WIDE_INT_PRINT_DEC, - frame_size >= ((HOST_WIDE_INT) 1 << 31) ? 0 : frame_size); - fputs (",$26,", file); - fprintf (file, HOST_WIDE_INT_PRINT_DEC, reg_offset); - fputs ("\n", file); - } + fprintf (file, "\t.frame $%d," HOST_WIDE_INT_PRINT_DEC ",$26," + HOST_WIDE_INT_PRINT_DEC "\n", + vms_unwind_regno, + frame_size >= (1UL << 31) ? 0 : frame_size, + reg_offset); else if (!flag_inhibit_size_directive) - { - fprintf (file, "\t.frame $%d,", - (frame_pointer_needed - ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM)); - fprintf (file, HOST_WIDE_INT_PRINT_DEC, - frame_size >= (1l << 31) ? 0 : frame_size); - fprintf (file, ",$26,%d\n", current_function_pretend_args_size); - } + fprintf (file, "\t.frame $%d," HOST_WIDE_INT_PRINT_DEC ",$26,%d\n", + (frame_pointer_needed + ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM), + frame_size >= (1UL << 31) ? 0 : frame_size, + current_function_pretend_args_size); /* Describe which registers were spilled. */ if (TARGET_ABI_UNICOSMK) @@ -6371,54 +7708,37 @@ alpha_start_function (file, fnname, decl) if (imask) /* ??? Does VMS care if mask contains ra? The old code didn't set it, so I don't here. */ - fprintf (file, "\t.mask 0x%lx,0\n", imask & ~(1L << REG_RA)); + fprintf (file, "\t.mask 0x%lx,0\n", imask & ~(1UL << REG_RA)); if (fmask) fprintf (file, "\t.fmask 0x%lx,0\n", fmask); - if (!alpha_is_stack_procedure) + if (alpha_procedure_type == PT_REGISTER) fprintf (file, "\t.fp_save $%d\n", vms_save_fp_regno); } else if (!flag_inhibit_size_directive) { if (imask) { - fprintf (file, "\t.mask 0x%lx,", imask); - fprintf (file, HOST_WIDE_INT_PRINT_DEC, - frame_size >= (1l << 31) ? 0 : reg_offset - frame_size); - putc ('\n', file); + fprintf (file, "\t.mask 0x%lx," HOST_WIDE_INT_PRINT_DEC "\n", imask, + frame_size >= (1UL << 31) ? 0 : reg_offset - frame_size); for (i = 0; i < 32; ++i) - if (imask & (1L << i)) + if (imask & (1UL << i)) reg_offset += 8; } if (fmask) - { - fprintf (file, "\t.fmask 0x%lx,", fmask); - fprintf (file, HOST_WIDE_INT_PRINT_DEC, - frame_size >= (1l << 31) ? 0 : reg_offset - frame_size); - putc ('\n', file); - } + fprintf (file, "\t.fmask 0x%lx," HOST_WIDE_INT_PRINT_DEC "\n", fmask, + frame_size >= (1UL << 31) ? 0 : reg_offset - frame_size); } #if TARGET_ABI_OPEN_VMS - /* Ifdef'ed cause readonly_section and link_section are only - available then. */ - readonly_section (); + /* Ifdef'ed cause link_section are only available then. */ + readonly_data_section (); fprintf (file, "\t.align 3\n"); assemble_name (file, fnname); fputs ("..na:\n", file); fputs ("\t.ascii \"", file); assemble_name (file, fnname); fputs ("\\0\"\n", file); - - link_section (); - fprintf (file, "\t.align 3\n"); - fputs ("\t.name ", file); - assemble_name (file, fnname); - fputs ("..na\n", file); - ASM_OUTPUT_LABEL (file, fnname); - fprintf (file, "\t.pdesc "); - assemble_name (file, fnname); - fprintf (file, "..en,%s\n", alpha_is_stack_procedure ? "stack" : "reg"); alpha_need_linkage (fnname, 1); text_section (); #endif @@ -6437,7 +7757,8 @@ alpha_output_function_end_prologue (file) else if (TARGET_ABI_WINDOWS_NT) fputs ("\t.prologue 0\n", file); else if (!flag_inhibit_size_directive) - fprintf (file, "\t.prologue %d\n", alpha_function_needs_gp); + fprintf (file, "\t.prologue %d\n", + alpha_function_needs_gp || current_function_is_thunk); } /* Write function epilogue. */ @@ -6471,12 +7792,12 @@ alpha_expand_epilogue () frame_size = get_frame_size (); if (TARGET_ABI_OPEN_VMS) frame_size = ALPHA_ROUND (sa_size - + (alpha_is_stack_procedure ? 8 : 0) + + (alpha_procedure_type == PT_STACK ? 8 : 0) + frame_size + current_function_pretend_args_size); else if (TARGET_ABI_UNICOSMK) frame_size = ALPHA_ROUND (sa_size - + (alpha_is_stack_procedure ? 48 : 0)) + + (alpha_procedure_type == PT_STACK ? 48 : 0)) + ALPHA_ROUND (frame_size + current_function_outgoing_args_size); else @@ -6486,14 +7807,20 @@ alpha_expand_epilogue () + current_function_pretend_args_size)); if (TARGET_ABI_OPEN_VMS) - reg_offset = 8; + { + if (alpha_procedure_type == PT_STACK) + reg_offset = 8; + else + reg_offset = 0; + } else reg_offset = ALPHA_ROUND (current_function_outgoing_args_size); alpha_sa_mask (&imask, &fmask); - fp_is_frame_pointer = ((TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure) - || (!TARGET_ABI_OPEN_VMS && frame_pointer_needed)); + fp_is_frame_pointer + = ((TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_STACK) + || (!TARGET_ABI_OPEN_VMS && frame_pointer_needed)); fp_offset = 0; sa_reg = stack_pointer_rtx; @@ -6535,10 +7862,10 @@ alpha_expand_epilogue () FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem)); reg_offset += 8; - imask &= ~(1L << REG_RA); + imask &= ~(1UL << REG_RA); for (i = 0; i < 32; ++i) - if (imask & (1L << i)) + if (imask & (1UL << i)) { if (i == HARD_FRAME_POINTER_REGNUM && fp_is_frame_pointer) fp_offset = reg_offset; @@ -6552,7 +7879,7 @@ alpha_expand_epilogue () } for (i = 0; i < 32; ++i) - if (fmask & (1L << i)) + if (fmask & (1UL << i)) { mem = gen_rtx_MEM (DFmode, plus_constant(sa_reg, reg_offset)); set_mem_alias_set (mem, alpha_sr_alias_set); @@ -6560,14 +7887,14 @@ alpha_expand_epilogue () reg_offset += 8; } } - else if (TARGET_ABI_UNICOSMK && alpha_is_stack_procedure) + else if (TARGET_ABI_UNICOSMK && alpha_procedure_type == PT_STACK) { /* Restore callee-saved general-purpose registers. */ reg_offset = -56; for (i = 9; i < 15; i++) - if (imask & (1L << i)) + if (imask & (1UL << i)) { mem = gen_rtx_MEM (DImode, plus_constant(hard_frame_pointer_rtx, reg_offset)); @@ -6577,7 +7904,7 @@ alpha_expand_epilogue () } for (i = 2; i < 10; i++) - if (fmask & (1L << i)) + if (fmask & (1UL << i)) { mem = gen_rtx_MEM (DFmode, plus_constant(hard_frame_pointer_rtx, reg_offset)); @@ -6680,13 +8007,13 @@ alpha_expand_epilogue () } else { - if (TARGET_ABI_OPEN_VMS && !alpha_is_stack_procedure) + if (TARGET_ABI_OPEN_VMS && alpha_procedure_type == PT_REGISTER) { emit_insn (gen_blockage ()); FRP (emit_move_insn (hard_frame_pointer_rtx, gen_rtx_REG (DImode, vms_save_fp_regno))); } - else if (TARGET_ABI_UNICOSMK && !alpha_is_stack_procedure) + else if (TARGET_ABI_UNICOSMK && alpha_procedure_type != PT_STACK) { /* Decrement the frame pointer if the function does not have a frame. */ @@ -6697,7 +8024,7 @@ alpha_expand_epilogue () } } } - + /* Output the rest of the textual info surrounding the epilogue. */ void @@ -6715,20 +8042,9 @@ alpha_end_function (file, fnname, decl) } inside_function = FALSE; - /* Show that we know this function if it is called again. - - Don't do this for global functions in object files destined for a - shared library because the function may be overridden by the application - or other libraries. Similarly, don't do this for weak functions. - - Don't do this for functions not defined in the .text section, as - otherwise it's not unlikely that the destination is out of range - for a direct branch. */ - - if (!DECL_WEAK (current_function_decl) - && (!flag_pic || !TREE_PUBLIC (current_function_decl)) - && decl_in_text_section (current_function_decl)) - SYMBOL_REF_FLAG (XEXP (DECL_RTL (current_function_decl), 0)) = 1; +#if TARGET_ABI_OPEN_VMS + alpha_write_linkage (file, fnname, decl); +#endif /* Output jump tables and the static subroutine information block. */ if (TARGET_ABI_UNICOSMK) @@ -6737,6 +8053,110 @@ alpha_end_function (file, fnname, decl) unicosmk_output_deferred_case_vectors (file); } } + +#if TARGET_ABI_OSF +/* Emit a tail call to FUNCTION after adjusting THIS by DELTA. + + In order to avoid the hordes of differences between generated code + with and without TARGET_EXPLICIT_RELOCS, and to avoid duplicating + lots of code loading up large constants, generate rtl and emit it + instead of going straight to text. + + Not sure why this idea hasn't been explored before... */ + +static void +alpha_output_mi_thunk_osf (file, thunk_fndecl, delta, vcall_offset, function) + FILE *file; + tree thunk_fndecl ATTRIBUTE_UNUSED; + HOST_WIDE_INT delta; + HOST_WIDE_INT vcall_offset; + tree function; +{ + HOST_WIDE_INT hi, lo; + rtx this, insn, funexp; + + /* We always require a valid GP. */ + emit_insn (gen_prologue_ldgp ()); + emit_note (NULL, NOTE_INSN_PROLOGUE_END); + + /* Find the "this" pointer. If the function returns a structure, + the structure return pointer is in $16. */ + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)))) + this = gen_rtx_REG (Pmode, 17); + else + this = gen_rtx_REG (Pmode, 16); + + /* Add DELTA. When possible we use ldah+lda. Otherwise load the + entire constant for the add. */ + lo = ((delta & 0xffff) ^ 0x8000) - 0x8000; + hi = (((delta - lo) & 0xffffffff) ^ 0x80000000) - 0x80000000; + if (hi + lo == delta) + { + if (hi) + emit_insn (gen_adddi3 (this, this, GEN_INT (hi))); + if (lo) + emit_insn (gen_adddi3 (this, this, GEN_INT (lo))); + } + else + { + rtx tmp = alpha_emit_set_long_const (gen_rtx_REG (Pmode, 0), + delta, -(delta < 0)); + emit_insn (gen_adddi3 (this, this, tmp)); + } + + /* Add a delta stored in the vtable at VCALL_OFFSET. */ + if (vcall_offset) + { + rtx tmp, tmp2; + + tmp = gen_rtx_REG (Pmode, 0); + emit_move_insn (tmp, gen_rtx_MEM (Pmode, this)); + + lo = ((vcall_offset & 0xffff) ^ 0x8000) - 0x8000; + hi = (((vcall_offset - lo) & 0xffffffff) ^ 0x80000000) - 0x80000000; + if (hi + lo == vcall_offset) + { + if (hi) + emit_insn (gen_adddi3 (tmp, tmp, GEN_INT (hi))); + } + else + { + tmp2 = alpha_emit_set_long_const (gen_rtx_REG (Pmode, 1), + vcall_offset, -(vcall_offset < 0)); + emit_insn (gen_adddi3 (tmp, tmp, tmp2)); + lo = 0; + } + if (lo) + tmp2 = gen_rtx_PLUS (Pmode, tmp, GEN_INT (lo)); + else + tmp2 = tmp; + emit_move_insn (tmp, gen_rtx_MEM (Pmode, tmp2)); + + emit_insn (gen_adddi3 (this, this, tmp)); + } + + /* Generate a tail call to the target function. */ + if (! TREE_USED (function)) + { + assemble_external (function); + TREE_USED (function) = 1; + } + funexp = XEXP (DECL_RTL (function), 0); + funexp = gen_rtx_MEM (FUNCTION_MODE, funexp); + insn = emit_call_insn (gen_sibcall (funexp, const0_rtx)); + SIBLING_CALL_P (insn) = 1; + + /* Run just enough of rest_of_compilation to get the insns emitted. + There's not really enough bulk here to make other passes such as + instruction scheduling worth while. Note that use_thunk calls + assemble_start_function and assemble_end_function. */ + insn = get_insns (); + shorten_branches (insn); + final_start_function (insn, file, 1); + final (insn, file, 1, 0); + final_end_function (); +} +#endif /* TARGET_ABI_OSF */ /* Debugging support. */ @@ -6841,7 +8261,7 @@ struct shadow_summary }; static void summarize_insn PARAMS ((rtx, struct shadow_summary *, int)); -static void alpha_handle_trap_shadows PARAMS ((rtx)); +static void alpha_handle_trap_shadows PARAMS ((void)); /* Summary the effects of expression X on the machine. Update SUM, a pointer to the summary structure. SET is nonzero if the insn is setting the @@ -7005,8 +8425,7 @@ summarize_insn (x, sum, set) (d) The trap shadow may not include any branch instructions. */ static void -alpha_handle_trap_shadows (insns) - rtx insns; +alpha_handle_trap_shadows () { struct shadow_summary shadow; int trap_pending, exception_nesting; @@ -7019,7 +8438,7 @@ alpha_handle_trap_shadows (insns) shadow.used.mem = 0; shadow.defd = shadow.used; - for (i = insns; i ; i = NEXT_INSN (i)) + for (i = get_insns (); i ; i = NEXT_INSN (i)) { if (GET_CODE (i) == NOTE) { @@ -7167,7 +8586,7 @@ static rtx alphaev4_next_nop PARAMS ((int *)); static rtx alphaev5_next_nop PARAMS ((int *)); static void alpha_align_insns - PARAMS ((rtx, unsigned int, rtx (*)(rtx, int *, int *), rtx (*)(int *))); + PARAMS ((unsigned int, rtx (*)(rtx, int *, int *), rtx (*)(int *))); static enum alphaev4_pipe alphaev4_insn_pipe (insn) @@ -7199,6 +8618,7 @@ alphaev4_insn_pipe (insn) case TYPE_MISC: case TYPE_IBR: case TYPE_JSR: + case TYPE_CALLPAL: case TYPE_FCPYS: case TYPE_FCMOV: case TYPE_FADD: @@ -7241,6 +8661,7 @@ alphaev5_insn_pipe (insn) case TYPE_IBR: case TYPE_JSR: + case TYPE_CALLPAL: return EV5_E1; case TYPE_FCPYS: @@ -7554,8 +8975,7 @@ alphaev5_next_nop (pin_use) /* The instruction group alignment main loop. */ static void -alpha_align_insns (insns, max_align, next_group, next_nop) - rtx insns; +alpha_align_insns (max_align, next_group, next_nop) unsigned int max_align; rtx (*next_group) PARAMS ((rtx, int *, int *)); rtx (*next_nop) PARAMS ((int *)); @@ -7568,17 +8988,17 @@ alpha_align_insns (insns, max_align, next_group, next_nop) rtx i, next; /* Let shorten branches care for assigning alignments to code labels. */ - shorten_branches (insns); + shorten_branches (get_insns ()); if (align_functions < 4) align = 4; - else if (align_functions < max_align) + else if ((unsigned int) align_functions < max_align) align = align_functions; else align = max_align; ofs = prev_in_use = 0; - i = insns; + i = get_insns (); if (GET_CODE (i) == NOTE) i = next_nonnote_insn (i); @@ -7622,15 +9042,20 @@ alpha_align_insns (insns, max_align, next_group, next_nop) else if ((int) align < len) { unsigned int new_log_align = len > 8 ? 4 : 3; - rtx where; + rtx prev, where; - where = prev_nonnote_insn (i); + where = prev = prev_nonnote_insn (i); if (!where || GET_CODE (where) != CODE_LABEL) where = i; - emit_insn_before (gen_realign (GEN_INT (new_log_align)), where); - align = 1 << new_log_align; - ofs = 0; + /* Can't realign between a call and its gp reload. */ + if (! (TARGET_EXPLICIT_RELOCS + && prev && GET_CODE (prev) == CALL_INSN)) + { + emit_insn_before (gen_realign (GEN_INT (new_log_align)), where); + align = 1 << new_log_align; + ofs = 0; + } } /* If the group won't fit in the same INT16 as the previous, @@ -7644,8 +9069,8 @@ alpha_align_insns (insns, max_align, next_group, next_nop) 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) { @@ -7655,7 +9080,7 @@ alpha_align_insns (insns, max_align, next_group, next_nop) 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 @@ -7675,12 +9100,11 @@ alpha_align_insns (insns, max_align, next_group, next_nop) /* Machine dependent reorg pass. */ -void -alpha_reorg (insns) - rtx insns; +static void +alpha_reorg () { if (alpha_tp != ALPHA_TP_PROG || flag_exceptions) - alpha_handle_trap_shadows (insns); + alpha_handle_trap_shadows (); /* Due to the number of extra trapb insns, don't bother fixing up alignment when trap precision is instruction. Moreover, we can @@ -7690,89 +9114,60 @@ alpha_reorg (insns) && flag_schedule_insns_after_reload) { if (alpha_cpu == PROCESSOR_EV4) - alpha_align_insns (insns, 8, alphaev4_next_group, alphaev4_next_nop); + alpha_align_insns (8, alphaev4_next_group, alphaev4_next_nop); else if (alpha_cpu == PROCESSOR_EV5) - alpha_align_insns (insns, 16, alphaev5_next_group, alphaev5_next_nop); + alpha_align_insns (16, alphaev5_next_group, alphaev5_next_nop); } } -/* Check a floating-point value for validity for a particular machine mode. */ - -static const char * const float_strings[] = -{ - /* These are for FLOAT_VAX. */ - "1.70141173319264430e+38", /* 2^127 (2^24 - 1) / 2^24 */ - "-1.70141173319264430e+38", - "2.93873587705571877e-39", /* 2^-128 */ - "-2.93873587705571877e-39", - /* These are for the default broken IEEE mode, which traps - on infinity or denormal numbers. */ - "3.402823466385288598117e+38", /* 2^128 (1 - 2^-24) */ - "-3.402823466385288598117e+38", - "1.1754943508222875079687e-38", /* 2^-126 */ - "-1.1754943508222875079687e-38", -}; +#ifdef OBJECT_FORMAT_ELF -static REAL_VALUE_TYPE float_values[8]; -static int inited_float_values = 0; +/* Switch to the section to which we should output X. The only thing + special we do here is to honor small data. */ -int -check_float_value (mode, d, overflow) +static void +alpha_elf_select_rtx_section (mode, x, align) enum machine_mode mode; - REAL_VALUE_TYPE *d; - int overflow ATTRIBUTE_UNUSED; + rtx x; + unsigned HOST_WIDE_INT align; { + if (TARGET_SMALL_DATA && GET_MODE_SIZE (mode) <= g_switch_value) + /* ??? Consider using mergable sdata sections. */ + sdata_section (); + else + default_elf_select_rtx_section (mode, x, align); +} - if (TARGET_IEEE || TARGET_IEEE_CONFORMANT || TARGET_IEEE_WITH_INEXACT) - return 0; +#endif /* OBJECT_FORMAT_ELF */ + +/* Structure to collect function names for final output in link section. */ +/* Note that items marked with GTY can't be ifdef'ed out. */ - if (inited_float_values == 0) - { - int i; - for (i = 0; i < 8; i++) - float_values[i] = REAL_VALUE_ATOF (float_strings[i], DFmode); +enum links_kind {KIND_UNUSED, KIND_LOCAL, KIND_EXTERN}; +enum reloc_kind {KIND_LINKAGE, KIND_CODEADDR}; - inited_float_values = 1; - } +struct alpha_links GTY(()) +{ + int num; + rtx linkage; + enum links_kind lkind; + enum reloc_kind rkind; +}; - if (mode == SFmode) - { - REAL_VALUE_TYPE r; - REAL_VALUE_TYPE *fvptr; +struct alpha_funcs GTY(()) +{ + int num; + splay_tree GTY ((param1_is (char *), param2_is (struct alpha_links *))) + links; +}; - if (TARGET_FLOAT_VAX) - fvptr = &float_values[0]; - else - fvptr = &float_values[4]; +static GTY ((param1_is (char *), param2_is (struct alpha_links *))) + splay_tree alpha_links_tree; +static GTY ((param1_is (tree), param2_is (struct alpha_funcs *))) + splay_tree alpha_funcs_tree; - memcpy (&r, d, sizeof (REAL_VALUE_TYPE)); - if (REAL_VALUES_LESS (fvptr[0], r)) - { - memcpy (d, &fvptr[0], sizeof (REAL_VALUE_TYPE)); - return 1; - } - else if (REAL_VALUES_LESS (r, fvptr[1])) - { - memcpy (d, &fvptr[1], sizeof (REAL_VALUE_TYPE)); - return 1; - } - else if (REAL_VALUES_LESS (dconst0, r) - && REAL_VALUES_LESS (r, fvptr[2])) - { - memcpy (d, &dconst0, sizeof (REAL_VALUE_TYPE)); - return 1; - } - else if (REAL_VALUES_LESS (r, dconst0) - && REAL_VALUES_LESS (fvptr[3], r)) - { - memcpy (d, &dconst0, sizeof (REAL_VALUE_TYPE)); - return 1; - } - } +static GTY(()) int alpha_funcs_num; - return 0; -} - #if TARGET_ABI_OPEN_VMS /* Return the VMS argument type corresponding to MODE. */ @@ -7808,45 +9203,6 @@ 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 -{ - rtx linkage; - enum links_kind kind; -}; - -static splay_tree alpha_links; - -static int mark_alpha_links_node PARAMS ((splay_tree_node, void *)); -static void mark_alpha_links PARAMS ((void *)); -static int alpha_write_one_linkage PARAMS ((splay_tree_node, void *)); - -/* Protect alpha_links from garbage collection. */ - -static int -mark_alpha_links_node (node, data) - splay_tree_node node; - void *data ATTRIBUTE_UNUSED; -{ - struct alpha_links *links = (struct alpha_links *) node->value; - ggc_mark_rtx (links->linkage); - return 0; -} - -static void -mark_alpha_links (ptr) - void *ptr; -{ - splay_tree tree = *(splay_tree *) ptr; - splay_tree_foreach (tree, mark_alpha_links_node, NULL); -} - /* Make (or fake) .linkage entry for function call. IS_LOCAL is 0 if name is used in call, 1 if name is used in definition. @@ -7864,42 +9220,55 @@ alpha_need_linkage (name, is_local) if (name[0] == '*') name++; - if (alpha_links) + if (is_local) + { + struct alpha_funcs *cfaf; + + if (!alpha_funcs_tree) + alpha_funcs_tree = splay_tree_new_ggc ((splay_tree_compare_fn) + splay_tree_compare_pointers); + + cfaf = (struct alpha_funcs *) ggc_alloc (sizeof (struct alpha_funcs)); + + cfaf->links = 0; + cfaf->num = ++alpha_funcs_num; + + splay_tree_insert (alpha_funcs_tree, + (splay_tree_key) current_function_decl, + (splay_tree_value) cfaf); + } + + if (alpha_links_tree) { /* Is this name already defined? */ - node = splay_tree_lookup (alpha_links, (splay_tree_key) name); + node = splay_tree_lookup (alpha_links_tree, (splay_tree_key) name); if (node) { al = (struct alpha_links *) node->value; if (is_local) { /* Defined here but external assumed. */ - if (al->kind == KIND_EXTERN) - al->kind = KIND_LOCAL; + if (al->lkind == KIND_EXTERN) + al->lkind = KIND_LOCAL; } else { /* Used here but unused assumed. */ - if (al->kind == KIND_UNUSED) - al->kind = KIND_LOCAL; + if (al->lkind == KIND_UNUSED) + al->lkind = KIND_LOCAL; } return al->linkage; } } else - { - alpha_links = splay_tree_new ((splay_tree_compare_fn) strcmp, - (splay_tree_delete_key_fn) free, - (splay_tree_delete_key_fn) free); - ggc_add_root (&alpha_links, 1, 1, mark_alpha_links); - } + alpha_links_tree = splay_tree_new_ggc ((splay_tree_compare_fn) strcmp); - al = (struct alpha_links *) xmalloc (sizeof (struct alpha_links)); - name = xstrdup (name); + al = (struct alpha_links *) ggc_alloc (sizeof (struct alpha_links)); + name = ggc_strdup (name); /* Assume external if no definition. */ - al->kind = (is_local ? KIND_UNUSED : KIND_EXTERN); + al->lkind = (is_local ? KIND_UNUSED : KIND_EXTERN); /* Ensure we have an IDENTIFIER so assemble_name can mark it used. */ get_identifier (name); @@ -7915,54 +9284,168 @@ alpha_need_linkage (name, is_local) ggc_alloc_string (linksym, name_len + 5)); } - splay_tree_insert (alpha_links, (splay_tree_key) name, + splay_tree_insert (alpha_links_tree, (splay_tree_key) name, (splay_tree_value) al); return al->linkage; } +rtx +alpha_use_linkage (linkage, cfundecl, lflag, rflag) + rtx linkage; + tree cfundecl; + int lflag; + int rflag; +{ + splay_tree_node cfunnode; + struct alpha_funcs *cfaf; + struct alpha_links *al; + const char *name = XSTR (linkage, 0); + + cfaf = (struct alpha_funcs *) 0; + al = (struct alpha_links *) 0; + + cfunnode = splay_tree_lookup (alpha_funcs_tree, (splay_tree_key) cfundecl); + cfaf = (struct alpha_funcs *) cfunnode->value; + + if (cfaf->links) + { + splay_tree_node lnode; + + /* Is this name already defined? */ + + lnode = splay_tree_lookup (cfaf->links, (splay_tree_key) name); + if (lnode) + al = (struct alpha_links *) lnode->value; + } + else + cfaf->links = splay_tree_new_ggc ((splay_tree_compare_fn) strcmp); + + if (!al) + { + size_t name_len; + size_t buflen; + char buf [512]; + char *linksym; + splay_tree_node node = 0; + struct alpha_links *anl; + + if (name[0] == '*') + name++; + + name_len = strlen (name); + + al = (struct alpha_links *) ggc_alloc (sizeof (struct alpha_links)); + al->num = cfaf->num; + + node = splay_tree_lookup (alpha_links_tree, (splay_tree_key) name); + if (node) + { + anl = (struct alpha_links *) node->value; + al->lkind = anl->lkind; + } + + sprintf (buf, "$%d..%s..lk", cfaf->num, name); + buflen = strlen (buf); + linksym = alloca (buflen + 1); + memcpy (linksym, buf, buflen + 1); + + al->linkage = gen_rtx_SYMBOL_REF + (Pmode, ggc_alloc_string (linksym, buflen + 1)); + + splay_tree_insert (cfaf->links, (splay_tree_key) name, + (splay_tree_value) al); + } + + if (rflag) + al->rkind = KIND_CODEADDR; + else + al->rkind = KIND_LINKAGE; + + if (lflag) + return gen_rtx_MEM (Pmode, plus_constant (al->linkage, 8)); + else + return al->linkage; +} + static int alpha_write_one_linkage (node, data) splay_tree_node node; void *data; { const char *const name = (const char *) node->key; - struct alpha_links *links = (struct alpha_links *) node->value; + struct alpha_links *link = (struct alpha_links *) node->value; FILE *stream = (FILE *) data; - if (links->kind == KIND_UNUSED - || ! TREE_SYMBOL_REFERENCED (get_identifier (name))) - return 0; - - fprintf (stream, "$%s..lk:\n", name); - if (links->kind == KIND_LOCAL) + fprintf (stream, "$%d..%s..lk:\n", link->num, name); + if (link->rkind == KIND_CODEADDR) { - /* Local and used, build linkage pair. */ - fprintf (stream, "\t.quad %s..en\n", name); - fprintf (stream, "\t.quad %s\n", name); + if (link->lkind == KIND_LOCAL) + { + /* Local and used */ + fprintf (stream, "\t.quad %s..en\n", name); + } + else + { + /* External and used, request code address. */ + fprintf (stream, "\t.code_address %s\n", name); + } } else { - /* External and used, request linkage pair. */ - fprintf (stream, "\t.linkage %s\n", name); + if (link->lkind == KIND_LOCAL) + { + /* Local and used, build linkage pair. */ + fprintf (stream, "\t.quad %s..en\n", name); + fprintf (stream, "\t.quad %s\n", name); + } + else + { + /* External and used, request linkage pair. */ + fprintf (stream, "\t.linkage %s\n", name); + } } return 0; } -void -alpha_write_linkage (stream) - FILE *stream; +static void +alpha_write_linkage (stream, funname, fundecl) + FILE *stream; + const char *funname; + tree fundecl; { - readonly_section (); + splay_tree_node node; + struct alpha_funcs *func; + + link_section (); fprintf (stream, "\t.align 3\n"); - splay_tree_foreach (alpha_links, alpha_write_one_linkage, stream); + node = splay_tree_lookup (alpha_funcs_tree, (splay_tree_key) fundecl); + func = (struct alpha_funcs *) node->value; + + fputs ("\t.name ", stream); + assemble_name (stream, funname); + fputs ("..na\n", stream); + ASM_OUTPUT_LABEL (stream, funname); + fprintf (stream, "\t.pdesc "); + assemble_name (stream, funname); + fprintf (stream, "..en,%s\n", + alpha_procedure_type == PT_STACK ? "stack" + : alpha_procedure_type == PT_REGISTER ? "reg" : "null"); + + if (func->links) + { + splay_tree_foreach (func->links, alpha_write_one_linkage, stream); + /* splay_tree_delete (func->links); */ + } } /* Given a decl, a section name, and whether the decl initializer has relocs, choose attributes for the section. */ #define SECTION_VMS_OVERLAY SECTION_FORGET +#define SECTION_VMS_GLOBAL SECTION_MACH_DEP +#define SECTION_VMS_INITIALIZE (SECTION_VMS_GLOBAL << 1) static unsigned int vms_section_type_flags (decl, name, reloc) @@ -7975,6 +9458,12 @@ vms_section_type_flags (decl, name, reloc) if (decl && DECL_ATTRIBUTES (decl) && lookup_attribute ("overlaid", DECL_ATTRIBUTES (decl))) flags |= SECTION_VMS_OVERLAY; + if (decl && DECL_ATTRIBUTES (decl) + && lookup_attribute ("global", DECL_ATTRIBUTES (decl))) + flags |= SECTION_VMS_GLOBAL; + if (decl && DECL_ATTRIBUTES (decl) + && lookup_attribute ("initialize", DECL_ATTRIBUTES (decl))) + flags |= SECTION_VMS_INITIALIZE; return flags; } @@ -7988,14 +9477,19 @@ vms_asm_named_section (name, flags) const char *name; unsigned int flags; { - const char *flag_str = ""; + fputc ('\n', asm_out_file); + fprintf (asm_out_file, ".section\t%s", name); if (flags & SECTION_VMS_OVERLAY) - flag_str = ",OVR"; - else if (flags & SECTION_DEBUG) - flag_str = ",NOWRT"; + fprintf (asm_out_file, ",OVR"); + if (flags & SECTION_VMS_GLOBAL) + fprintf (asm_out_file, ",GBL"); + if (flags & SECTION_VMS_INITIALIZE) + fprintf (asm_out_file, ",NOMOD"); + if (flags & SECTION_DEBUG) + fprintf (asm_out_file, ",NOWRT"); - fprintf (asm_out_file, ".section\t%s%s\n", name, flag_str); + fputc ('\n', asm_out_file); } /* Record an element in the table of global constructors. SYMBOL is @@ -8035,6 +9529,16 @@ alpha_need_linkage (name, is_local) return NULL_RTX; } +rtx +alpha_use_linkage (linkage, cfundecl, lflag, rflag) + rtx linkage ATTRIBUTE_UNUSED; + tree cfundecl ATTRIBUTE_UNUSED; + int lflag ATTRIBUTE_UNUSED; + int rflag ATTRIBUTE_UNUSED; +{ + return NULL_RTX; +} + #endif /* TARGET_ABI_OPEN_VMS */ #if TARGET_ABI_UNICOSMK @@ -8098,9 +9602,8 @@ unicosmk_output_module_name (file) prefix the module name with a '$' if necessary. */ if (!ISALPHA (*name)) - fprintf (file, "$%s", name); - else - fputs (name, file); + putc ('$', file); + output_clean_symbol_name (file, name); } /* Output text that to appear at the beginning of an assembler file. */ @@ -8239,7 +9742,7 @@ unicosmk_section_type_flags (decl, name, reloc) /* Generate a section name for decl and associate it with the declaration. */ -void +static void unicosmk_unique_section (decl, reloc) tree decl; int reloc ATTRIBUTE_UNUSED; @@ -8251,7 +9754,7 @@ unicosmk_unique_section (decl, reloc) abort (); name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); - STRIP_NAME_ENCODING (name, name); + name = default_strip_name_encoding (name); len = strlen (name); if (TREE_CODE (decl) == FUNCTION_DECL) @@ -8319,7 +9822,7 @@ unicosmk_insert_attributes (decl, attr_ptr) { if (DECL_P (decl) && (TREE_PUBLIC (decl) || TREE_CODE (decl) == FUNCTION_DECL)) - UNIQUE_SECTION (decl, 0); + unicosmk_unique_section (decl, 0); } /* Output an alignment directive. We have to use the macro 'gcc@code@align' @@ -8364,7 +9867,7 @@ unicosmk_output_addr_vec (file, vec) int vlen = XVECLEN (body, 0); int idx; - ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (lab)); + (*targetm.asm_out.internal_label) (file, "L", CODE_LABEL_NUMBER (lab)); for (idx = 0; idx < vlen; idx++) { @@ -8398,7 +9901,7 @@ static void unicosmk_gen_dsib (imaskP) unsigned long * imaskP; { - if (alpha_is_stack_procedure) + if (alpha_procedure_type == PT_STACK) { const char *ssib_name; rtx mem; @@ -8414,14 +9917,14 @@ unicosmk_gen_dsib (imaskP) mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 56)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (mem, gen_rtx_REG (DImode, REG_RA))); - (*imaskP) &= ~(1L << REG_RA); + (*imaskP) &= ~(1UL << REG_RA); /* Save the old frame pointer. */ mem = gen_rtx_MEM (DImode, plus_constant (stack_pointer_rtx, 48)); set_mem_alias_set (mem, alpha_sr_alias_set); FRP (emit_move_insn (mem, hard_frame_pointer_rtx)); - (*imaskP) &= ~(1L << HARD_FRAME_POINTER_REGNUM); + (*imaskP) &= ~(1UL << HARD_FRAME_POINTER_REGNUM); emit_insn (gen_blockage ()); @@ -8482,8 +9985,7 @@ unicosmk_ssib_name () x = XEXP (x, 0); if (GET_CODE (x) != SYMBOL_REF) abort (); - fnname = XSTR (x, 0); - STRIP_NAME_ENCODING (fnname, fnname); + fnname = default_name_encoding (XSTR (x, 0)); len = strlen (fnname); if (len + SSIB_PREFIX_LEN > 255) @@ -8549,14 +10051,12 @@ unicosmk_output_ssib (file, fnname) for (x = machine->first_ciw; x; x = XEXP (x, 1)) { ciw = XEXP (x, 0); - fprintf (file, "\t.quad\t"); #if HOST_BITS_PER_WIDE_INT == 32 - fprintf (file, HOST_WIDE_INT_PRINT_DOUBLE_HEX, + fprintf (file, "\t.quad\t" HOST_WIDE_INT_PRINT_DOUBLE_HEX "\n", CONST_DOUBLE_HIGH (ciw), CONST_DOUBLE_LOW (ciw)); #else - fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (ciw)); + fprintf (file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n", INTVAL (ciw)); #endif - fprintf (file, "\n"); } } @@ -8613,6 +10113,7 @@ unicosmk_data_section () /* List of identifiers for which an extern declaration might have to be emitted. */ +/* FIXME: needs to use GC, so it can be saved and restored for PCH. */ struct unicosmk_extern_list { @@ -8658,7 +10159,7 @@ unicosmk_output_externs (file) /* We have to strip the encoding and possibly remove user_label_prefix from the identifier in order to handle -fleading-underscore and explicit asm names correctly (cf. gcc.dg/asm-names-1.c). */ - STRIP_NAME_ENCODING (real_name, p->name); + real_name = default_strip_name_encoding (p->name); if (len && p->name[0] == '*' && !memcmp (real_name, user_label_prefix, len)) real_name += len; @@ -8683,7 +10184,7 @@ unicosmk_add_extern (name) struct unicosmk_extern_list *p; p = (struct unicosmk_extern_list *) - permalloc (sizeof (struct unicosmk_extern_list)); + xmalloc (sizeof (struct unicosmk_extern_list)); p->next = unicosmk_extern_head; p->name = name; unicosmk_extern_head = p; @@ -8695,6 +10196,7 @@ unicosmk_add_extern (name) /* Structure to collect identifiers which have been replaced by DEX expressions. */ +/* FIXME: needs to use GC, so it can be saved and restored for PCH. */ struct unicosmk_dex { struct unicosmk_dex *next; @@ -8765,7 +10267,7 @@ unicosmk_need_dex (x) --i; } - dex = (struct unicosmk_dex *) permalloc (sizeof (struct unicosmk_dex)); + dex = (struct unicosmk_dex *) xmalloc (sizeof (struct unicosmk_dex)); dex->name = name; dex->next = unicosmk_dex_list; unicosmk_dex_list = dex; @@ -8833,3 +10335,6 @@ unicosmk_need_dex (x) } #endif /* TARGET_ABI_UNICOSMK */ + +#include "gt-alpha.h" +