X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fconfig%2Fpa%2Fpa.c;h=47de208483af05be743320fa6f17a9b69a2c6b2c;hp=d1ed7182e7a0f58145ebd800c97f3609d1d11ac2;hb=eb46b0b619e7f7d4fa2616d325aa930ff1843c9a;hpb=b04fab2aba38bf469795b687884b9656a498474d diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c index d1ed7182e7a..47de208483a 100644 --- a/gcc/config/pa/pa.c +++ b/gcc/config/pa/pa.c @@ -1,13 +1,13 @@ /* Subroutines for insn-output.c for HPPA. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - 2002, 2003, 2004 Free Software Foundation, Inc. + 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) +the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, @@ -16,9 +16,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" @@ -47,6 +46,7 @@ Boston, MA 02111-1307, USA. */ #include "tm_p.h" #include "target.h" #include "target-def.h" +#include "df.h" /* Return nonzero if there is a bypass for the output of OUT_INSN and the fp store IN_INSN. */ @@ -58,7 +58,8 @@ hppa_fpstore_bypass_p (rtx out_insn, rtx in_insn) rtx set; if (recog_memoized (in_insn) < 0 - || get_attr_type (in_insn) != TYPE_FPSTORE + || (get_attr_type (in_insn) != TYPE_FPSTORE + && get_attr_type (in_insn) != TYPE_FPSTORE_LOAD) || recog_memoized (out_insn) < 0) return 0; @@ -84,6 +85,7 @@ hppa_fpstore_bypass_p (rtx out_insn, rtx in_insn) static void copy_reg_pointer (rtx, rtx); static void fix_range (const char *); +static bool pa_handle_option (size_t, const char *, int); static int hppa_address_cost (rtx); static bool hppa_rtx_costs (rtx, int, int, int *); static inline rtx force_mode (enum machine_mode, rtx); @@ -91,7 +93,6 @@ static void pa_reorg (void); static void pa_combine_instructions (void); static int pa_can_combine_p (rtx, rtx, rtx, int, rtx, rtx, rtx); static int forward_branch_p (rtx); -static int shadd_constant_p (int); static void compute_zdepwi_operands (unsigned HOST_WIDE_INT, unsigned *); static int compute_movmem_length (rtx); static int compute_clrmem_length (rtx); @@ -107,7 +108,8 @@ static void pa_output_function_epilogue (FILE *, HOST_WIDE_INT); static int pa_adjust_cost (rtx, rtx, rtx, int); static int pa_adjust_priority (rtx, int); static int pa_issue_rate (void); -static void pa_select_section (tree, int, unsigned HOST_WIDE_INT) +static void pa_som_asm_init_sections (void) ATTRIBUTE_UNUSED; +static section *pa_select_section (tree, int, unsigned HOST_WIDE_INT) ATTRIBUTE_UNUSED; static void pa_encode_section_info (tree, rtx, int); static const char *pa_strip_name_encoding (const char *); @@ -123,10 +125,10 @@ static void pa_asm_out_destructor (rtx, int); static void pa_init_builtins (void); static rtx hppa_builtin_saveregs (void); static tree hppa_gimplify_va_arg_expr (tree, tree, tree *, tree *); +static bool pa_scalar_mode_supported_p (enum machine_mode); +static bool pa_commutative_p (const_rtx x, int outer_code); static void copy_fp_args (rtx) ATTRIBUTE_UNUSED; static int length_fp_args (rtx) ATTRIBUTE_UNUSED; -static struct deferred_plabel *get_plabel (const char *) - ATTRIBUTE_UNUSED; static inline void pa_file_start_level (void) ATTRIBUTE_UNUSED; static inline void pa_file_start_space (int) ATTRIBUTE_UNUSED; static inline void pa_file_start_file (int) ATTRIBUTE_UNUSED; @@ -137,39 +139,48 @@ static void pa_linux_file_start (void) ATTRIBUTE_UNUSED; static void pa_hpux64_gas_file_start (void) ATTRIBUTE_UNUSED; static void pa_hpux64_hpas_file_start (void) ATTRIBUTE_UNUSED; static void output_deferred_plabels (void); +static void output_deferred_profile_counters (void) ATTRIBUTE_UNUSED; +#ifdef ASM_OUTPUT_EXTERNAL_REAL +static void pa_hpux_file_end (void); +#endif #ifdef HPUX_LONG_DOUBLE_LIBRARY static void pa_hpux_init_libfuncs (void); #endif static rtx pa_struct_value_rtx (tree, int); -static bool pa_pass_by_reference (CUMULATIVE_ARGS *ca, enum machine_mode, - tree, bool); +static bool pa_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static int pa_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, + tree, bool); static struct machine_function * pa_init_machine_status (void); +static enum reg_class pa_secondary_reload (bool, rtx, enum reg_class, + enum machine_mode, + secondary_reload_info *); +static void pa_extra_live_on_entry (bitmap); +/* The following extra sections are only used for SOM. */ +static GTY(()) section *som_readonly_data_section; +static GTY(()) section *som_one_only_readonly_data_section; +static GTY(()) section *som_one_only_data_section; /* Save the operands last given to a compare for use when we generate a scc or bcc insn. */ rtx hppa_compare_op0, hppa_compare_op1; enum cmp_type hppa_branch_type; -/* Which architecture we are generating code for. */ -enum architecture_type pa_arch; - -/* String to hold which architecture we are generating code for. */ -const char *pa_arch_string; - -/* String used with the -mfixed-range= option. */ -const char *pa_fixed_range_string; - /* Which cpu we are scheduling for. */ -enum processor_type pa_cpu; +enum processor_type pa_cpu = TARGET_SCHED_DEFAULT; -/* String to hold which cpu we are scheduling for. */ -const char *pa_cpu_string; +/* The UNIX standard to use for predefines and linking. */ +int flag_pa_unix = TARGET_HPUX_11_11 ? 1998 : TARGET_HPUX_10_10 ? 1995 : 1993; /* Counts for the number of callee-saved general and floating point registers which were saved by the current function's prologue. */ static int gr_saved, fr_saved; +/* Boolean indicating whether the return pointer was saved by the + current function's prologue. */ +static bool rp_saved; + static rtx find_addr_reg (rtx); /* Keep track of the number of bytes we have output in the CODE subspace @@ -186,7 +197,7 @@ static int last_address; struct deferred_plabel GTY(()) { rtx internal_label; - const char *name; + rtx symbol; }; static GTY((length ("n_deferred_plabels"))) struct deferred_plabel * deferred_plabels; @@ -230,13 +241,20 @@ static size_t n_deferred_plabels = 0; #undef TARGET_FUNCTION_OK_FOR_SIBCALL #define TARGET_FUNCTION_OK_FOR_SIBCALL pa_function_ok_for_sibcall +#undef TARGET_COMMUTATIVE_P +#define TARGET_COMMUTATIVE_P pa_commutative_p + #undef TARGET_ASM_OUTPUT_MI_THUNK #define TARGET_ASM_OUTPUT_MI_THUNK pa_asm_output_mi_thunk #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK #define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall #undef TARGET_ASM_FILE_END +#ifdef ASM_OUTPUT_EXTERNAL_REAL +#define TARGET_ASM_FILE_END pa_hpux_file_end +#else #define TARGET_ASM_FILE_END output_deferred_plabels +#endif #if !defined(USE_COLLECT2) #undef TARGET_ASM_CONSTRUCTOR @@ -245,6 +263,11 @@ static size_t n_deferred_plabels = 0; #define TARGET_ASM_DESTRUCTOR pa_asm_out_destructor #endif +#undef TARGET_DEFAULT_TARGET_FLAGS +#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | TARGET_CPU_DEFAULT) +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION pa_handle_option + #undef TARGET_INIT_BUILTINS #define TARGET_INIT_BUILTINS pa_init_builtins @@ -262,9 +285,9 @@ static size_t n_deferred_plabels = 0; #endif #undef TARGET_PROMOTE_FUNCTION_RETURN -#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true +#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true #undef TARGET_PROMOTE_PROTOTYPES -#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true +#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true #undef TARGET_STRUCT_VALUE_RTX #define TARGET_STRUCT_VALUE_RTX pa_struct_value_rtx @@ -274,12 +297,28 @@ static size_t n_deferred_plabels = 0; #define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size #undef TARGET_PASS_BY_REFERENCE #define TARGET_PASS_BY_REFERENCE pa_pass_by_reference +#undef TARGET_CALLEE_COPIES +#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES pa_arg_partial_bytes #undef TARGET_EXPAND_BUILTIN_SAVEREGS #define TARGET_EXPAND_BUILTIN_SAVEREGS hppa_builtin_saveregs #undef TARGET_GIMPLIFY_VA_ARG_EXPR #define TARGET_GIMPLIFY_VA_ARG_EXPR hppa_gimplify_va_arg_expr +#undef TARGET_SCALAR_MODE_SUPPORTED_P +#define TARGET_SCALAR_MODE_SUPPORTED_P pa_scalar_mode_supported_p + +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM pa_tls_referenced_p + +#undef TARGET_SECONDARY_RELOAD +#define TARGET_SECONDARY_RELOAD pa_secondary_reload + +#undef TARGET_EXTRA_LIVE_ON_ENTRY +#define TARGET_EXTRA_LIVE_ON_ENTRY pa_extra_live_on_entry + struct gcc_target targetm = TARGET_INITIALIZER; /* Parse the -mfixed-range= option string. */ @@ -294,7 +333,7 @@ fix_range (const char *const_str) REG2 are either register names or register numbers. The effect of this option is to mark the registers in the range from REG1 to REG2 as ``fixed'' so they won't be used by the compiler. This is - used, e.g., to ensure that kernel mode code doesn't use f32-f127. */ + used, e.g., to ensure that kernel mode code doesn't use fr4-fr31. */ i = strlen (const_str); str = (char *) alloca (i + 1); @@ -305,7 +344,7 @@ fix_range (const char *const_str) dash = strchr (str, '-'); if (!dash) { - warning ("value of -mfixed-range must have form REG1-REG2"); + warning (0, "value of -mfixed-range must have form REG1-REG2"); return; } *dash = '\0'; @@ -317,14 +356,14 @@ fix_range (const char *const_str) first = decode_reg_name (str); if (first < 0) { - warning ("unknown register name: %s", str); + warning (0, "unknown register name: %s", str); return; } last = decode_reg_name (dash + 1); if (last < 0) { - warning ("unknown register name: %s", dash + 1); + warning (0, "unknown register name: %s", dash + 1); return; } @@ -332,7 +371,7 @@ fix_range (const char *const_str) if (first > last) { - warning ("%s-%s is an empty range", str, dash + 1); + warning (0, "%s-%s is an empty range", str, dash + 1); return; } @@ -355,75 +394,78 @@ fix_range (const char *const_str) target_flags |= MASK_DISABLE_FPREGS; } -void -override_options (void) -{ - if (pa_cpu_string == NULL) - pa_cpu_string = TARGET_SCHED_DEFAULT; - - if (! strcmp (pa_cpu_string, "8000")) - { - pa_cpu_string = "8000"; - pa_cpu = PROCESSOR_8000; - } - else if (! strcmp (pa_cpu_string, "7100")) - { - pa_cpu_string = "7100"; - pa_cpu = PROCESSOR_7100; - } - else if (! strcmp (pa_cpu_string, "700")) - { - pa_cpu_string = "700"; - pa_cpu = PROCESSOR_700; - } - else if (! strcmp (pa_cpu_string, "7100LC")) - { - pa_cpu_string = "7100LC"; - pa_cpu = PROCESSOR_7100LC; - } - else if (! strcmp (pa_cpu_string, "7200")) - { - pa_cpu_string = "7200"; - pa_cpu = PROCESSOR_7200; - } - else if (! strcmp (pa_cpu_string, "7300")) - { - pa_cpu_string = "7300"; - pa_cpu = PROCESSOR_7300; - } - else - { - warning ("unknown -mschedule= option (%s).\nValid options are 700, 7100, 7100LC, 7200, 7300, and 8000\n", pa_cpu_string); - } +/* Implement TARGET_HANDLE_OPTION. */ - /* Set the instruction architecture. */ - if (pa_arch_string && ! strcmp (pa_arch_string, "1.0")) +static bool +pa_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED) +{ + switch (code) { - pa_arch_string = "1.0"; - pa_arch = ARCHITECTURE_10; + case OPT_mnosnake: + case OPT_mpa_risc_1_0: + case OPT_march_1_0: target_flags &= ~(MASK_PA_11 | MASK_PA_20); - } - else if (pa_arch_string && ! strcmp (pa_arch_string, "1.1")) - { - pa_arch_string = "1.1"; - pa_arch = ARCHITECTURE_11; + return true; + + case OPT_msnake: + case OPT_mpa_risc_1_1: + case OPT_march_1_1: target_flags &= ~MASK_PA_20; target_flags |= MASK_PA_11; - } - else if (pa_arch_string && ! strcmp (pa_arch_string, "2.0")) - { - pa_arch_string = "2.0"; - pa_arch = ARCHITECTURE_20; + return true; + + case OPT_mpa_risc_2_0: + case OPT_march_2_0: target_flags |= MASK_PA_11 | MASK_PA_20; - } - else if (pa_arch_string) - { - warning ("unknown -march= option (%s).\nValid options are 1.0, 1.1, and 2.0\n", pa_arch_string); - } + return true; + + case OPT_mschedule_: + if (strcmp (arg, "8000") == 0) + pa_cpu = PROCESSOR_8000; + else if (strcmp (arg, "7100") == 0) + pa_cpu = PROCESSOR_7100; + else if (strcmp (arg, "700") == 0) + pa_cpu = PROCESSOR_700; + else if (strcmp (arg, "7100LC") == 0) + pa_cpu = PROCESSOR_7100LC; + else if (strcmp (arg, "7200") == 0) + pa_cpu = PROCESSOR_7200; + else if (strcmp (arg, "7300") == 0) + pa_cpu = PROCESSOR_7300; + else + return false; + return true; + + case OPT_mfixed_range_: + fix_range (arg); + return true; + +#if TARGET_HPUX + case OPT_munix_93: + flag_pa_unix = 1993; + return true; +#endif - if (pa_fixed_range_string) - fix_range (pa_fixed_range_string); +#if TARGET_HPUX_10_10 + case OPT_munix_95: + flag_pa_unix = 1995; + return true; +#endif +#if TARGET_HPUX_11_11 + case OPT_munix_98: + flag_pa_unix = 1998; + return true; +#endif + + default: + return true; + } +} + +void +override_options (void) +{ /* Unconditional branches in the delay slot are not compatible with dwarf2 call frame information. There is no benefit in using this optimization on PA8000 and later processors. */ @@ -434,18 +476,18 @@ override_options (void) if (flag_pic && TARGET_PORTABLE_RUNTIME) { - warning ("PIC code generation is not supported in the portable runtime model\n"); + warning (0, "PIC code generation is not supported in the portable runtime model"); } if (flag_pic && TARGET_FAST_INDIRECT_CALLS) { - warning ("PIC code generation is not compatible with fast indirect calls\n"); + warning (0, "PIC code generation is not compatible with fast indirect calls"); } if (! TARGET_GAS && write_symbols != NO_DEBUG) { - warning ("-g is only supported when using GAS on this processor,"); - warning ("-g option disabled"); + warning (0, "-g is only supported when using GAS on this processor,"); + warning (0, "-g option disabled"); write_symbols = NO_DEBUG; } @@ -473,8 +515,16 @@ static void pa_init_builtins (void) { #ifdef DONT_HAVE_FPUTC_UNLOCKED - built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE; - implicit_built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE; + built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = + built_in_decls[(int) BUILT_IN_PUTC_UNLOCKED]; + implicit_built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] + = implicit_built_in_decls[(int) BUILT_IN_PUTC_UNLOCKED]; +#endif +#if TARGET_HPUX_11 + if (built_in_decls [BUILT_IN_FINITE]) + set_user_assembler_name (built_in_decls [BUILT_IN_FINITE], "_Isfinite"); + if (built_in_decls [BUILT_IN_FINITEF]) + set_user_assembler_name (built_in_decls [BUILT_IN_FINITEF], "_Isfinitef"); #endif } @@ -498,26 +548,6 @@ copy_reg_pointer (rtx to, rtx from) mark_reg_pointer (to, REGNO_POINTER_ALIGN (REGNO (from))); } -/* Return nonzero only if OP is a register of mode MODE, - or CONST0_RTX. */ -int -reg_or_0_operand (rtx op, enum machine_mode mode) -{ - return (op == CONST0_RTX (mode) || register_operand (op, mode)); -} - -/* Return nonzero if OP is suitable for use in a call to a named - function. - - For 2.5 try to eliminate either call_operand_address or - function_label_operand, they perform very similar functions. */ -int -call_operand_address (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_MODE (op) == word_mode - && CONSTANT_P (op) && ! TARGET_PORTABLE_RUNTIME); -} - /* Return 1 if X contains a symbolic expression. We know these expressions will have one of a few well defined forms, so we need only check those forms. */ @@ -532,262 +562,17 @@ symbolic_expression_p (rtx x) return (symbolic_operand (x, VOIDmode)); } -int -symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - switch (GET_CODE (op)) - { - case SYMBOL_REF: - case LABEL_REF: - return 1; - case CONST: - op = XEXP (op, 0); - return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF - || GET_CODE (XEXP (op, 0)) == LABEL_REF) - && GET_CODE (XEXP (op, 1)) == CONST_INT); - default: - return 0; - } -} - -/* Return truth value of statement that OP is a symbolic memory - operand of mode MODE. */ - -int -symbolic_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - if (GET_CODE (op) != MEM) - return 0; - op = XEXP (op, 0); - return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST - || GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF); -} - -/* Return 1 if the operand is either a register, zero, or a memory operand - that is not symbolic. */ - -int -reg_or_0_or_nonsymb_mem_operand (rtx op, enum machine_mode mode) -{ - if (register_operand (op, mode)) - return 1; - - if (op == CONST0_RTX (mode)) - return 1; - - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - - if (GET_CODE (op) != MEM) - return 0; - - /* Until problems with management of the REG_POINTER flag are resolved, - we need to delay creating move insns with unscaled indexed addresses - until CSE is not expected. */ - if (!TARGET_NO_SPACE_REGS - && !cse_not_expected - && GET_CODE (XEXP (op, 0)) == PLUS - && REG_P (XEXP (XEXP (op, 0), 0)) - && REG_P (XEXP (XEXP (op, 0), 1))) - return 0; - - return (!symbolic_memory_operand (op, mode) - && memory_address_p (mode, XEXP (op, 0))); -} - -/* Return 1 if the operand is a register operand or a non-symbolic memory - operand after reload. This predicate is used for branch patterns that - internally handle register reloading. We need to accept non-symbolic - memory operands after reload to ensure that the pattern is still valid - if reload didn't find a hard register for the operand. */ - -int -reg_before_reload_operand (rtx op, enum machine_mode mode) -{ - /* Don't accept a SUBREG since it will need a reload. */ - if (GET_CODE (op) == SUBREG) - return 0; - - if (register_operand (op, mode)) - return 1; - - if (reload_completed - && memory_operand (op, mode) - && !symbolic_memory_operand (op, mode)) - return 1; - - return 0; -} - /* Accept any constant that can be moved in one instruction into a general register. */ int -cint_ok_for_move (HOST_WIDE_INT intval) +cint_ok_for_move (HOST_WIDE_INT ival) { /* OK if ldo, ldil, or zdepi, can be used. */ - return (CONST_OK_FOR_LETTER_P (intval, 'J') - || CONST_OK_FOR_LETTER_P (intval, 'N') - || CONST_OK_FOR_LETTER_P (intval, 'K')); -} - -/* Return 1 iff OP is an indexed memory operand. */ -int -indexed_memory_operand (rtx op, enum machine_mode mode) -{ - if (GET_MODE (op) != mode) - return 0; - - /* Before reload, a (SUBREG (MEM...)) forces reloading into a register. */ - if (reload_completed && GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - - if (GET_CODE (op) != MEM || symbolic_memory_operand (op, mode)) - return 0; - - op = XEXP (op, 0); - - return (memory_address_p (mode, op) && IS_INDEX_ADDR_P (op)); + return (VAL_14_BITS_P (ival) + || ldil_cint_p (ival) + || zdepi_cint_p (ival)); } - -/* Accept anything that can be used as a destination operand for a - move instruction. We don't accept indexed memory operands since - they are supported only for floating point stores. */ -int -move_dest_operand (rtx op, enum machine_mode mode) -{ - if (register_operand (op, mode)) - return 1; - - if (GET_MODE (op) != mode) - return 0; - - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - - if (GET_CODE (op) != MEM || symbolic_memory_operand (op, mode)) - return 0; - - op = XEXP (op, 0); - - return (memory_address_p (mode, op) - && !IS_INDEX_ADDR_P (op) - && !IS_LO_SUM_DLT_ADDR_P (op)); -} - -/* Accept anything that can be used as a source operand for a move - instruction. */ -int -move_src_operand (rtx op, enum machine_mode mode) -{ - if (register_operand (op, mode)) - return 1; - - if (GET_CODE (op) == CONST_INT) - return cint_ok_for_move (INTVAL (op)); - - if (GET_MODE (op) != mode) - return 0; - - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - - if (GET_CODE (op) != MEM) - return 0; - - /* Until problems with management of the REG_POINTER flag are resolved, - we need to delay creating move insns with unscaled indexed addresses - until CSE is not expected. */ - if (!TARGET_NO_SPACE_REGS - && !cse_not_expected - && GET_CODE (XEXP (op, 0)) == PLUS - && REG_P (XEXP (XEXP (op, 0), 0)) - && REG_P (XEXP (XEXP (op, 0), 1))) - return 0; - - return memory_address_p (mode, XEXP (op, 0)); -} - -/* Accept anything that can be used as the source operand for a prefetch - instruction. */ -int -prefetch_operand (rtx op, enum machine_mode mode) -{ - if (GET_CODE (op) != MEM) - return 0; - - /* Until problems with management of the REG_POINTER flag are resolved, - we need to delay creating prefetch insns with unscaled indexed addresses - until CSE is not expected. */ - if (!TARGET_NO_SPACE_REGS - && !cse_not_expected - && GET_CODE (XEXP (op, 0)) == PLUS - && REG_P (XEXP (XEXP (op, 0), 0)) - && REG_P (XEXP (XEXP (op, 0), 1))) - return 0; - - return memory_address_p (mode, XEXP (op, 0)); -} - -/* Accept REG and any CONST_INT that can be moved in one instruction into a - general register. */ -int -reg_or_cint_move_operand (rtx op, enum machine_mode mode) -{ - if (register_operand (op, mode)) - return 1; - - return (GET_CODE (op) == CONST_INT && cint_ok_for_move (INTVAL (op))); -} - -int -pic_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - if (!flag_pic) - return 0; - - switch (GET_CODE (op)) - { - case LABEL_REF: - return 1; - case CONST: - op = XEXP (op, 0); - return (GET_CODE (XEXP (op, 0)) == LABEL_REF - && GET_CODE (XEXP (op, 1)) == CONST_INT); - default: - return 0; - } -} - -int -fp_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return reg_renumber && FP_REG_P (op); -} - - -/* Return truth value of whether OP can be used as an operand in a - three operand arithmetic insn that accepts registers of mode MODE - or 14-bit signed integers. */ -int -arith_operand (rtx op, enum machine_mode mode) -{ - return (register_operand (op, mode) - || (GET_CODE (op) == CONST_INT && INT_14_BITS (op))); -} - -/* Return truth value of whether OP can be used as an operand in a - three operand arithmetic insn that accepts registers of mode MODE - or 11-bit signed integers. */ -int -arith11_operand (rtx op, enum machine_mode mode) -{ - return (register_operand (op, mode) - || (GET_CODE (op) == CONST_INT && INT_11_BITS (op))); -} - /* Return truth value of whether OP can be used as an operand in a adddi3 insn. */ int @@ -798,96 +583,38 @@ adddi3_operand (rtx op, enum machine_mode mode) && (TARGET_64BIT ? INT_14_BITS (op) : INT_11_BITS (op)))); } -/* A constant integer suitable for use in a PRE_MODIFY memory - reference. */ -int -pre_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == CONST_INT - && INTVAL (op) >= -0x2000 && INTVAL (op) < 0x10); -} - -/* A constant integer suitable for use in a POST_MODIFY memory - reference. */ -int -post_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == CONST_INT - && INTVAL (op) < 0x2000 && INTVAL (op) >= -0x10); -} - -int -arith_double_operand (rtx op, enum machine_mode mode) -{ - return (register_operand (op, mode) - || (GET_CODE (op) == CONST_DOUBLE - && GET_MODE (op) == mode - && VAL_14_BITS_P (CONST_DOUBLE_LOW (op)) - && ((CONST_DOUBLE_HIGH (op) >= 0) - == ((CONST_DOUBLE_LOW (op) & 0x1000) == 0)))); -} - -/* Return truth value of whether OP is an integer which fits the - range constraining immediate operands in three-address insns, or - is an integer register. */ - -int -ireg_or_int5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return ((GET_CODE (op) == CONST_INT && INT_5_BITS (op)) - || (GET_CODE (op) == REG && REGNO (op) > 0 && REGNO (op) < 32)); -} - -/* Return nonzero if OP is an integer register, else return zero. */ -int -ireg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == REG && REGNO (op) > 0 && REGNO (op) < 32); -} - -/* Return truth value of whether OP is an integer which fits the - range constraining immediate operands in three-address insns. */ - -int -int5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == CONST_INT && INT_5_BITS (op)); -} - -int -uint5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == CONST_INT && INT_U5_BITS (op)); -} - +/* True iff the operand OP can be used as the destination operand of + an integer store. This also implies the operand could be used as + the source operand of an integer load. Symbolic, lo_sum and indexed + memory operands are not allowed. We accept reloading pseudos and + other memory operands. */ int -int11_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == CONST_INT && INT_11_BITS (op)); -} - +integer_store_memory_operand (rtx op, enum machine_mode mode) +{ + return ((reload_in_progress + && REG_P (op) + && REGNO (op) >= FIRST_PSEUDO_REGISTER + && reg_renumber [REGNO (op)] < 0) + || (GET_CODE (op) == MEM + && (reload_in_progress || memory_address_p (mode, XEXP (op, 0))) + && !symbolic_memory_operand (op, VOIDmode) + && !IS_LO_SUM_DLT_ADDR_P (XEXP (op, 0)) + && !IS_INDEX_ADDR_P (XEXP (op, 0)))); +} + +/* True iff ldil can be used to load this CONST_INT. The least + significant 11 bits of the value must be zero and the value must + not change sign when extended from 32 to 64 bits. */ int -uint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +ldil_cint_p (HOST_WIDE_INT ival) { -#if HOST_BITS_PER_WIDE_INT > 32 - /* All allowed constants will fit a CONST_INT. */ - return (GET_CODE (op) == CONST_INT - && (INTVAL (op) >= 0 && INTVAL (op) < (HOST_WIDE_INT) 1 << 32)); -#else - return (GET_CODE (op) == CONST_INT - || (GET_CODE (op) == CONST_DOUBLE - && CONST_DOUBLE_HIGH (op) == 0)); -#endif -} + HOST_WIDE_INT x = ival & (((HOST_WIDE_INT) -1 << 31) | 0x7ff); -int -arith5_operand (rtx op, enum machine_mode mode) -{ - return register_operand (op, mode) || int5_operand (op, mode); + return x == 0 || x == ((HOST_WIDE_INT) -1 << 31); } /* True iff zdepi can be used to generate this CONST_INT. - zdepi first sign extends a 5 bit signed number to a given field + zdepi first sign extends a 5-bit signed number to a given field length, then places this field anywhere in a zero. */ int zdepi_cint_p (unsigned HOST_WIDE_INT x) @@ -915,14 +642,6 @@ and_mask_p (unsigned HOST_WIDE_INT mask) return (mask & (mask - 1)) == 0; } -/* True iff depi or extru can be used to compute (reg & OP). */ -int -and_operand (rtx op, enum machine_mode mode) -{ - return (register_operand (op, mode) - || (GET_CODE (op) == CONST_INT && and_mask_p (INTVAL (op)))); -} - /* True iff depi can be used to compute (reg | MASK). */ int ior_mask_p (unsigned HOST_WIDE_INT mask) @@ -930,44 +649,6 @@ ior_mask_p (unsigned HOST_WIDE_INT mask) mask += mask & -mask; return (mask & (mask - 1)) == 0; } - -/* True iff depi can be used to compute (reg | OP). */ -int -ior_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == CONST_INT && ior_mask_p (INTVAL (op))); -} - -int -lhs_lshift_operand (rtx op, enum machine_mode mode) -{ - return register_operand (op, mode) || lhs_lshift_cint_operand (op, mode); -} - -/* True iff OP is a CONST_INT of the forms 0...0xxxx or 0...01...1xxxx. - Such values can be the left hand side x in (x << r), using the zvdepi - instruction. */ -int -lhs_lshift_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - unsigned HOST_WIDE_INT x; - if (GET_CODE (op) != CONST_INT) - return 0; - x = INTVAL (op) >> 4; - return (x & (x + 1)) == 0; -} - -int -arith32_operand (rtx op, enum machine_mode mode) -{ - return register_operand (op, mode) || GET_CODE (op) == CONST_INT; -} - -int -pc_or_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF); -} /* Legitimize PIC addresses. If the address is already position-independent, we return ORIG. Newly generated @@ -979,6 +660,8 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg) { rtx pic_ref = orig; + gcc_assert (!PA_SYMBOL_REF_TLS_P (orig)); + /* Labels need special handling. */ if (pic_label_operand (orig, mode)) { @@ -1000,8 +683,7 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg) { rtx insn, tmp_reg; - if (reg == 0) - abort (); + gcc_assert (reg); /* Before reload, allocate a temporary register for the intermediate result. This allows the sequence to be deleted when the final @@ -1013,20 +695,18 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg) gen_rtx_PLUS (word_mode, pic_offset_table_rtx, gen_rtx_HIGH (word_mode, orig))); pic_ref - = gen_rtx_MEM (Pmode, - gen_rtx_LO_SUM (Pmode, tmp_reg, - gen_rtx_UNSPEC (Pmode, - gen_rtvec (1, orig), - UNSPEC_DLTIND14R))); + = gen_const_mem (Pmode, + gen_rtx_LO_SUM (Pmode, tmp_reg, + gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, orig), + UNSPEC_DLTIND14R))); current_function_uses_pic_offset_table = 1; - MEM_NOTRAP_P (pic_ref) = 1; - MEM_READONLY_P (pic_ref) = 1; mark_reg_pointer (reg, BITS_PER_UNIT); insn = emit_move_insn (reg, pic_ref); /* Put a REG_EQUAL note on this insn, so that it can be optimized. */ - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, REG_NOTES (insn)); + set_unique_reg_note (insn, REG_EQUAL, orig); return reg; } @@ -1034,33 +714,111 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg) { rtx base; - if (GET_CODE (XEXP (orig, 0)) == PLUS - && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) - return orig; + if (GET_CODE (XEXP (orig, 0)) == PLUS + && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) + return orig; + + gcc_assert (reg); + gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS); + + base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); + orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, + base == reg ? 0 : reg); + + if (GET_CODE (orig) == CONST_INT) + { + if (INT_14_BITS (orig)) + return plus_constant (base, INTVAL (orig)); + orig = force_reg (Pmode, orig); + } + pic_ref = gen_rtx_PLUS (Pmode, base, orig); + /* Likewise, should we set special REG_NOTEs here? */ + } + + return pic_ref; +} + +static GTY(()) rtx gen_tls_tga; + +static rtx +gen_tls_get_addr (void) +{ + if (!gen_tls_tga) + gen_tls_tga = init_one_libfunc ("__tls_get_addr"); + return gen_tls_tga; +} + +static rtx +hppa_tls_call (rtx arg) +{ + rtx ret; + + ret = gen_reg_rtx (Pmode); + emit_library_call_value (gen_tls_get_addr (), ret, + LCT_CONST, Pmode, 1, arg, Pmode); + + return ret; +} + +static rtx +legitimize_tls_address (rtx addr) +{ + rtx ret, insn, tmp, t1, t2, tp; + enum tls_model model = SYMBOL_REF_TLS_MODEL (addr); + + switch (model) + { + case TLS_MODEL_GLOBAL_DYNAMIC: + tmp = gen_reg_rtx (Pmode); + if (flag_pic) + emit_insn (gen_tgd_load_pic (tmp, addr)); + else + emit_insn (gen_tgd_load (tmp, addr)); + ret = hppa_tls_call (tmp); + break; + + case TLS_MODEL_LOCAL_DYNAMIC: + ret = gen_reg_rtx (Pmode); + tmp = gen_reg_rtx (Pmode); + start_sequence (); + if (flag_pic) + emit_insn (gen_tld_load_pic (tmp, addr)); + else + emit_insn (gen_tld_load (tmp, addr)); + t1 = hppa_tls_call (tmp); + insn = get_insns (); + end_sequence (); + t2 = gen_reg_rtx (Pmode); + emit_libcall_block (insn, t2, t1, + gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), + UNSPEC_TLSLDBASE)); + emit_insn (gen_tld_offset_load (ret, addr, t2)); + break; - if (reg == 0) - abort (); + case TLS_MODEL_INITIAL_EXEC: + tp = gen_reg_rtx (Pmode); + tmp = gen_reg_rtx (Pmode); + ret = gen_reg_rtx (Pmode); + emit_insn (gen_tp_load (tp)); + if (flag_pic) + emit_insn (gen_tie_load_pic (tmp, addr)); + else + emit_insn (gen_tie_load (tmp, addr)); + emit_move_insn (ret, gen_rtx_PLUS (Pmode, tp, tmp)); + break; - if (GET_CODE (XEXP (orig, 0)) == PLUS) - { - base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); - orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, - base == reg ? 0 : reg); - } - else - abort (); + case TLS_MODEL_LOCAL_EXEC: + tp = gen_reg_rtx (Pmode); + ret = gen_reg_rtx (Pmode); + emit_insn (gen_tp_load (tp)); + emit_insn (gen_tle_load (ret, addr, tp)); + break; - if (GET_CODE (orig) == CONST_INT) - { - if (INT_14_BITS (orig)) - return plus_constant (base, INTVAL (orig)); - orig = force_reg (Pmode, orig); - } - pic_ref = gen_rtx_PLUS (Pmode, base, orig); - /* Likewise, should we set special REG_NOTEs here? */ + default: + gcc_unreachable (); } - return pic_ref; + return ret; } /* Try machine-dependent ways of modifying an illegitimate address @@ -1132,7 +890,9 @@ hppa_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, && !REG_POINTER (XEXP (x, 1))) return gen_rtx_PLUS (Pmode, XEXP (x, 1), XEXP (x, 0)); - if (flag_pic) + if (PA_SYMBOL_REF_TLS_P (x)) + return legitimize_tls_address (x); + else if (flag_pic) return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode)); /* Strip off CONST. */ @@ -1577,12 +1337,30 @@ force_mode (enum machine_mode mode, rtx orig) if (mode == GET_MODE (orig)) return orig; - if (REGNO (orig) >= FIRST_PSEUDO_REGISTER) - abort (); + gcc_assert (REGNO (orig) < FIRST_PSEUDO_REGISTER); return gen_rtx_REG (mode, REGNO (orig)); } +/* Return 1 if *X is a thread-local symbol. */ + +static int +pa_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED) +{ + return PA_SYMBOL_REF_TLS_P (*x); +} + +/* Return 1 if X contains a thread-local symbol. */ + +bool +pa_tls_referenced_p (rtx x) +{ + if (!TARGET_HAVE_TLS) + return false; + + return for_each_rtx (&x, &pa_tls_symbol_ref_1, 0); +} + /* Emit insns to move operands[1] into operands[0]. Return 1 if we have written out everything that needs to be done to @@ -1605,9 +1383,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) addresses from the destination operand. */ if (GET_CODE (operand0) == MEM && IS_INDEX_ADDR_P (XEXP (operand0, 0))) { - /* This is only safe up to the beginning of life analysis. */ - if (no_new_pseudos) - abort (); + gcc_assert (can_create_pseudo_p ()); tem = copy_to_mode_reg (Pmode, XEXP (operand0, 0)); operand0 = replace_equiv_address (operand0, tem); @@ -1667,12 +1443,12 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) if (scratch_reg && reload_in_progress && GET_CODE (operand0) == MEM && ((tem = find_replacement (&XEXP (operand0, 0))) != XEXP (operand0, 0))) - operand0 = gen_rtx_MEM (GET_MODE (operand0), tem); + operand0 = replace_equiv_address (operand0, tem); if (scratch_reg && reload_in_progress && GET_CODE (operand1) == MEM && ((tem = find_replacement (&XEXP (operand1, 0))) != XEXP (operand1, 0))) - operand1 = gen_rtx_MEM (GET_MODE (operand1), tem); + operand1 = replace_equiv_address (operand1, tem); /* Handle secondary reloads for loads/stores of FP registers from REG+D addresses where D does not fit in 5 or 14 bits, including @@ -1710,7 +1486,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) else emit_move_insn (scratch_reg, XEXP (operand1, 0)); emit_insn (gen_rtx_SET (VOIDmode, operand0, - gen_rtx_MEM (mode, scratch_reg))); + replace_equiv_address (operand1, scratch_reg))); return 1; } else if (scratch_reg @@ -1747,7 +1523,8 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) } else emit_move_insn (scratch_reg, XEXP (operand0, 0)); - emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_MEM (mode, scratch_reg), + emit_insn (gen_rtx_SET (VOIDmode, + replace_equiv_address (operand0, scratch_reg), operand1)); return 1; } @@ -1764,7 +1541,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) && CONSTANT_P (operand1) && fp_reg_operand (operand0, mode)) { - rtx xoperands[2]; + rtx const_mem, xoperands[2]; /* SCRATCH_REG will hold an address and maybe the actual data. We want it in WORD_MODE regardless of what mode it was originally given @@ -1773,13 +1550,14 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) /* Force the constant into memory and put the address of the memory location into scratch_reg. */ + const_mem = force_const_mem (mode, operand1); xoperands[0] = scratch_reg; - xoperands[1] = XEXP (force_const_mem (mode, operand1), 0); + xoperands[1] = XEXP (const_mem, 0); emit_move_sequence (xoperands, Pmode, 0); /* Now load the destination register. */ emit_insn (gen_rtx_SET (mode, operand0, - gen_rtx_MEM (mode, scratch_reg))); + replace_equiv_address (const_mem, scratch_reg))); return 1; } /* Handle secondary reloads for SAR. These occur when trying to load @@ -1816,8 +1594,8 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) OPERAND0. */ scratch_reg = force_mode (GET_MODE (operand0), scratch_reg); - emit_move_insn (scratch_reg, gen_rtx_MEM (GET_MODE (operand0), - scratch_reg)); + emit_move_insn (scratch_reg, + replace_equiv_address (operand1, scratch_reg)); } else { @@ -1981,8 +1759,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) { /* Save away the constant part of the expression. */ const_part = XEXP (XEXP (operand1, 0), 1); - if (GET_CODE (const_part) != CONST_INT) - abort (); + gcc_assert (GET_CODE (const_part) == CONST_INT); /* Force the function label into memory. */ temp = force_const_mem (mode, XEXP (XEXP (operand1, 0), 0)); @@ -2045,10 +1822,10 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) && (reload_completed || reload_in_progress) && flag_pic) { - operands[1] = force_const_mem (mode, operand1); - operands[1] = legitimize_pic_address (XEXP (operands[1], 0), + rtx const_mem = force_const_mem (mode, operand1); + operands[1] = legitimize_pic_address (XEXP (const_mem, 0), mode, temp); - operands[1] = gen_rtx_MEM (mode, operands[1]); + operands[1] = replace_equiv_address (const_mem, operands[1]); emit_move_sequence (operands, mode, temp); } else @@ -2102,6 +1879,26 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) } return 1; } + else if (pa_tls_referenced_p (operand1)) + { + rtx tmp = operand1; + rtx addend = NULL; + + if (GET_CODE (tmp) == CONST && GET_CODE (XEXP (tmp, 0)) == PLUS) + { + addend = XEXP (XEXP (tmp, 0), 1); + tmp = XEXP (XEXP (tmp, 0), 0); + } + + gcc_assert (GET_CODE (tmp) == SYMBOL_REF); + tmp = legitimize_tls_address (tmp); + if (addend) + { + tmp = gen_rtx_PLUS (mode, tmp, addend); + tmp = force_operand (tmp, operands[0]); + } + operands[1] = tmp; + } else if (GET_CODE (operand1) != CONST_INT || !cint_ok_for_move (INTVAL (operand1))) { @@ -2148,6 +1945,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) because PLUS uses an 11-bit immediate and the insn sequence generated is not as efficient as the one using HIGH/LO_SUM. */ if (GET_CODE (operand1) == CONST_INT + && GET_MODE_BITSIZE (mode) <= BITS_PER_WORD && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT && !insert) { @@ -2232,8 +2030,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) } } - REG_NOTES (insn) - = gen_rtx_EXPR_LIST (REG_EQUAL, op1, REG_NOTES (insn)); + set_unique_reg_note (insn, REG_EQUAL, op1); return 1; } @@ -2255,6 +2052,7 @@ reloc_needed (tree exp) case ADDR_EXPR: return 1; + case POINTER_PLUS_EXPR: case PLUS_EXPR: case MINUS_EXPR: reloc = reloc_needed (TREE_OPERAND (exp, 0)); @@ -2269,10 +2067,12 @@ reloc_needed (tree exp) case CONSTRUCTOR: { - register tree link; - for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) - if (TREE_VALUE (link) != 0) - reloc |= reloc_needed (TREE_VALUE (link)); + tree value; + unsigned HOST_WIDE_INT ix; + + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), ix, value) + if (value) + reloc |= reloc_needed (value); } break; @@ -2324,8 +2124,7 @@ singlemove_string (rtx *operands) long i; REAL_VALUE_TYPE d; - if (GET_MODE (operands[1]) != SFmode) - abort (); + gcc_assert (GET_MODE (operands[1]) == SFmode); /* Translate the CONST_DOUBLE to a CONST_INT with the same target bit pattern. */ @@ -2460,11 +2259,27 @@ output_move_double (rtx *operands) optype1 = RNDOP; /* Check for the cases that the operand constraints are not - supposed to allow to happen. Abort if we get one, - because generating code for these cases is painful. */ + supposed to allow to happen. */ + gcc_assert (optype0 == REGOP || optype1 == REGOP); + + /* Handle copies between general and floating registers. */ - if (optype0 != REGOP && optype1 != REGOP) - abort (); + if (optype0 == REGOP && optype1 == REGOP + && FP_REG_P (operands[0]) ^ FP_REG_P (operands[1])) + { + if (FP_REG_P (operands[0])) + { + output_asm_insn ("{stws|stw} %1,-16(%%sp)", operands); + output_asm_insn ("{stws|stw} %R1,-12(%%sp)", operands); + return "{fldds|fldd} -16(%%sp),%0"; + } + else + { + output_asm_insn ("{fstds|fstd} %1,-16(%%sp)", operands); + output_asm_insn ("{ldws|ldw} -16(%%sp),%0", operands); + return "{ldws|ldw} -12(%%sp),%R0"; + } + } /* Handle auto decrementing and incrementing loads and stores specifically, since the structure of the function doesn't work @@ -2483,40 +2298,33 @@ output_move_double (rtx *operands) rtx high_reg = gen_rtx_SUBREG (SImode, operands[1], 0); operands[0] = XEXP (addr, 0); - if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG) - abort (); + gcc_assert (GET_CODE (operands[1]) == REG + && GET_CODE (operands[0]) == REG); - if (!reg_overlap_mentioned_p (high_reg, addr)) - { - /* No overlap between high target register and address - register. (We do this in a non-obvious way to - save a register file writeback) */ - if (GET_CODE (addr) == POST_INC) - return "{stws|stw},ma %1,8(%0)\n\tstw %R1,-4(%0)"; - return "{stws|stw},ma %1,-8(%0)\n\tstw %R1,12(%0)"; - } - else - abort (); + gcc_assert (!reg_overlap_mentioned_p (high_reg, addr)); + + /* No overlap between high target register and address + register. (We do this in a non-obvious way to + save a register file writeback) */ + if (GET_CODE (addr) == POST_INC) + return "{stws|stw},ma %1,8(%0)\n\tstw %R1,-4(%0)"; + return "{stws|stw},ma %1,-8(%0)\n\tstw %R1,12(%0)"; } else if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC) { rtx high_reg = gen_rtx_SUBREG (SImode, operands[1], 0); operands[0] = XEXP (addr, 0); - if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG) - abort (); - - if (!reg_overlap_mentioned_p (high_reg, addr)) - { - /* No overlap between high target register and address - register. (We do this in a non-obvious way to - save a register file writeback) */ - if (GET_CODE (addr) == PRE_INC) - return "{stws|stw},mb %1,8(%0)\n\tstw %R1,4(%0)"; - return "{stws|stw},mb %1,-8(%0)\n\tstw %R1,4(%0)"; - } - else - abort (); + gcc_assert (GET_CODE (operands[1]) == REG + && GET_CODE (operands[0]) == REG); + + gcc_assert (!reg_overlap_mentioned_p (high_reg, addr)); + /* No overlap between high target register and address + register. (We do this in a non-obvious way to save a + register file writeback) */ + if (GET_CODE (addr) == PRE_INC) + return "{stws|stw},mb %1,8(%0)\n\tstw %R1,4(%0)"; + return "{stws|stw},mb %1,-8(%0)\n\tstw %R1,4(%0)"; } } if (optype1 == MEMOP) @@ -2530,8 +2338,8 @@ output_move_double (rtx *operands) rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0); operands[1] = XEXP (addr, 0); - if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG) - abort (); + gcc_assert (GET_CODE (operands[0]) == REG + && GET_CODE (operands[1]) == REG); if (!reg_overlap_mentioned_p (high_reg, addr)) { @@ -2557,8 +2365,8 @@ output_move_double (rtx *operands) rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0); operands[1] = XEXP (addr, 0); - if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG) - abort (); + gcc_assert (GET_CODE (operands[0]) == REG + && GET_CODE (operands[1]) == REG); if (!reg_overlap_mentioned_p (high_reg, addr)) { @@ -2582,12 +2390,11 @@ output_move_double (rtx *operands) else if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 0)) == MULT) { + rtx xoperands[4]; rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0); if (!reg_overlap_mentioned_p (high_reg, addr)) { - rtx xoperands[3]; - xoperands[0] = high_reg; xoperands[1] = XEXP (addr, 1); xoperands[2] = XEXP (XEXP (addr, 0), 0); @@ -2598,8 +2405,6 @@ output_move_double (rtx *operands) } else { - rtx xoperands[3]; - xoperands[0] = high_reg; xoperands[1] = XEXP (addr, 1); xoperands[2] = XEXP (XEXP (addr, 0), 0); @@ -2720,23 +2525,22 @@ output_fp_move_double (rtx *operands) { output_asm_insn ("fstd%F0 %1,%0", operands); } - else if (operands[1] == CONST0_RTX (GET_MODE (operands[0]))) + else { - if (GET_CODE (operands[0]) == REG) - { - rtx xoperands[2]; - xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1); - xoperands[0] = operands[0]; - output_asm_insn ("copy %%r0,%0\n\tcopy %%r0,%1", xoperands); - } + rtx xoperands[2]; + + gcc_assert (operands[1] == CONST0_RTX (GET_MODE (operands[0]))); + /* This is a pain. You have to be prepared to deal with an arbitrary address here including pre/post increment/decrement. so avoid this in the MD. */ - else - abort (); + gcc_assert (GET_CODE (operands[0]) == REG); + + xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1); + xoperands[0] = operands[0]; + output_asm_insn ("copy %%r0,%0\n\tcopy %%r0,%1", xoperands); } - else abort (); return ""; } @@ -2757,11 +2561,10 @@ find_addr_reg (rtx addr) else if (CONSTANT_P (XEXP (addr, 1))) addr = XEXP (addr, 0); else - abort (); + gcc_unreachable (); } - if (GET_CODE (addr) == REG) - return addr; - abort (); + gcc_assert (GET_CODE (addr) == REG); + return addr; } /* Emit code to perform a block move. @@ -2892,7 +2695,7 @@ output_block_move (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED) return ""; default: - abort (); + gcc_unreachable (); } } @@ -3034,7 +2837,7 @@ output_block_clear (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED) return ""; default: - abort (); + gcc_unreachable (); } } @@ -3094,15 +2897,13 @@ output_and (rtx *operands) if ((mask & (1 << ms0)) == 0) break; - if (ms0 != 32) - abort (); + gcc_assert (ms0 == 32); if (ls1 == 32) { len = ls0; - if (len == 0) - abort (); + gcc_assert (len); operands[2] = GEN_INT (len); return "{extru|extrw,u} %1,31,%2,%0"; @@ -3146,15 +2947,13 @@ output_64bit_and (rtx *operands) if ((mask & ((unsigned HOST_WIDE_INT) 1 << ms0)) == 0) break; - if (ms0 != HOST_BITS_PER_WIDE_INT) - abort (); + gcc_assert (ms0 == HOST_BITS_PER_WIDE_INT); if (ls1 == HOST_BITS_PER_WIDE_INT) { len = ls0; - if (len == 0) - abort (); + gcc_assert (len); operands[2] = GEN_INT (len); return "extrd,u %1,63,%2,%0"; @@ -3193,8 +2992,7 @@ output_ior (rtx *operands) if ((mask & (1 << bs1)) == 0) break; - if (bs1 != 32 && ((unsigned HOST_WIDE_INT) 1 << bs1) <= mask) - abort (); + gcc_assert (bs1 == 32 || ((unsigned HOST_WIDE_INT) 1 << bs1) > mask); p = 31 - bs0; len = bs1 - bs0; @@ -3223,9 +3021,8 @@ output_64bit_ior (rtx *operands) if ((mask & ((unsigned HOST_WIDE_INT) 1 << bs1)) == 0) break; - if (bs1 != HOST_BITS_PER_WIDE_INT - && ((unsigned HOST_WIDE_INT) 1 << bs1) <= mask) - abort (); + gcc_assert (bs1 == HOST_BITS_PER_WIDE_INT + || ((unsigned HOST_WIDE_INT) 1 << bs1) > mask); p = 63 - bs0; len = bs1 - bs0; @@ -3236,13 +3033,14 @@ output_64bit_ior (rtx *operands) } /* Target hook for assembling integer objects. This code handles - aligned SI and DI integers specially, since function references must - be preceded by P%. */ + aligned SI and DI integers specially since function references + must be preceded by P%. */ static bool pa_assemble_integer (rtx x, unsigned int size, int aligned_p) { - if (size == UNITS_PER_WORD && aligned_p + if (size == UNITS_PER_WORD + && aligned_p && function_label_operand (x, VOIDmode)) { fputs (size == 8? "\t.dword\tP%" : "\t.word\tP%", asm_out_file); @@ -3423,7 +3221,7 @@ remove_useless_addtr_insns (int check_notes) { rtx pattern = PATTERN (next); - /* If it a reversed fp conditional branch (eg uses add,tr) + /* If it a reversed fp conditional branch (e.g. uses add,tr) and CCFP dies, then reverse our conditional and the branch to avoid the add,tr. */ if (GET_CODE (pattern) == SET @@ -3550,20 +3348,18 @@ store_reg (int reg, HOST_WIDE_INT disp, int base) rtx tmpreg = gen_rtx_REG (Pmode, 1); emit_move_insn (tmpreg, delta); - emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg)); - dest = gen_rtx_MEM (word_mode, tmpreg); - insn = emit_move_insn (dest, src); + insn = emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg)); if (DO_FRAME_NOTES) { REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, - gen_rtx_SET (VOIDmode, - gen_rtx_MEM (word_mode, - gen_rtx_PLUS (word_mode, basereg, - delta)), - src), + gen_rtx_SET (VOIDmode, tmpreg, + gen_rtx_PLUS (Pmode, basereg, delta)), REG_NOTES (insn)); + RTX_FRAME_RELATED_P (insn) = 1; } + dest = gen_rtx_MEM (word_mode, tmpreg); + insn = emit_move_insn (dest, src); } else { @@ -3599,8 +3395,7 @@ store_reg_modify (int base, int reg, HOST_WIDE_INT mod) { rtx insn, basereg, srcreg, delta; - if (!VAL_14_BITS_P (mod)) - abort (); + gcc_assert (VAL_14_BITS_P (mod)); basereg = gen_rtx_REG (Pmode, base); srcreg = gen_rtx_REG (word_mode, reg); @@ -3612,25 +3407,9 @@ store_reg_modify (int base, int reg, HOST_WIDE_INT mod) RTX_FRAME_RELATED_P (insn) = 1; /* RTX_FRAME_RELATED_P must be set on each frame related set - in a parallel with more than one element. Don't set - RTX_FRAME_RELATED_P in the first set if reg is temporary - register 1. The effect of this operation is recorded in - the initial copy. */ - if (reg != 1) - { - RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 0)) = 1; - RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 1)) = 1; - } - else - { - /* The first element of a PARALLEL is always processed if it is - a SET. Thus, we need an expression list for this case. */ - REG_NOTES (insn) - = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, - gen_rtx_SET (VOIDmode, basereg, - gen_rtx_PLUS (word_mode, basereg, delta)), - REG_NOTES (insn)); - } + in a parallel with more than one element. */ + RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 0)) = 1; + RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 1)) = 1; } } @@ -3660,6 +3439,12 @@ set_reg_plus_d (int reg, int base, HOST_WIDE_INT disp, int note) emit_move_insn (tmpreg, delta); insn = emit_move_insn (gen_rtx_REG (Pmode, reg), gen_rtx_PLUS (Pmode, tmpreg, basereg)); + if (DO_FRAME_NOTES) + REG_NOTES (insn) + = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, + gen_rtx_SET (VOIDmode, tmpreg, + gen_rtx_PLUS (Pmode, basereg, delta)), + REG_NOTES (insn)); } else { @@ -3713,13 +3498,13 @@ compute_frame_size (HOST_WIDE_INT size, int *fregs_live) /* Account for space used by the callee general register saves. */ for (i = 18, j = frame_pointer_needed ? 4 : 3; i >= j; i--) - if (regs_ever_live[i]) + if (df_regs_ever_live_p (i)) size += UNITS_PER_WORD; /* Account for space used by the callee floating point register saves. */ for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP) - if (regs_ever_live[i] - || (!TARGET_64BIT && regs_ever_live[i + 1])) + if (df_regs_ever_live_p (i) + || (!TARGET_64BIT && df_regs_ever_live_p (i + 1))) { freg_saved = 1; @@ -3784,15 +3569,17 @@ pa_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) to output the assembler directives which denote the start of a function. */ fprintf (file, "\t.CALLINFO FRAME=" HOST_WIDE_INT_PRINT_DEC, actual_fsize); - if (regs_ever_live[2]) - fputs (",CALLS,SAVE_RP", file); - else + if (current_function_is_leaf) fputs (",NO_CALLS", file); + else + fputs (",CALLS", file); + if (rp_saved) + fputs (",SAVE_RP", file); /* The SAVE_SP flag is used to indicate that register %r3 is stored at the beginning of the frame and that it is used as the frame pointer for the frame. We do this because our current frame - layout doesn't conform to that specified in the the HP runtime + layout doesn't conform to that specified in the HP runtime documentation and we need a way to indicate to programs such as GDB where %r3 is saved. The SAVE_SP flag was chosen because it isn't used by HP compilers but is supported by the assembler. @@ -3848,8 +3635,13 @@ hppa_expand_prologue (void) /* Save RP first. The calling conventions manual states RP will always be stored into the caller's frame at sp - 20 or sp - 16 depending on which ABI is in use. */ - if (regs_ever_live[2] || current_function_calls_eh_return) - store_reg (2, TARGET_64BIT ? -16 : -20, STACK_POINTER_REGNUM); + if (df_regs_ever_live_p (2) || current_function_calls_eh_return) + { + store_reg (2, TARGET_64BIT ? -16 : -20, STACK_POINTER_REGNUM); + rp_saved = true; + } + else + rp_saved = false; /* Allocate the local frame and set up the frame pointer if needed. */ if (actual_fsize != 0) @@ -3864,17 +3656,7 @@ hppa_expand_prologue (void) frames. */ insn = emit_move_insn (tmpreg, frame_pointer_rtx); if (DO_FRAME_NOTES) - { - /* We need to record the frame pointer save here since the - new frame pointer is set in the following insn. */ - RTX_FRAME_RELATED_P (insn) = 1; - REG_NOTES (insn) - = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, - gen_rtx_SET (VOIDmode, - gen_rtx_MEM (word_mode, stack_pointer_rtx), - frame_pointer_rtx), - REG_NOTES (insn)); - } + RTX_FRAME_RELATED_P (insn) = 1; insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); if (DO_FRAME_NOTES) @@ -3969,7 +3751,7 @@ hppa_expand_prologue (void) } for (i = 18; i >= 4; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { store_reg (i, offset, FRAME_POINTER_REGNUM); offset += UNITS_PER_WORD; @@ -4009,7 +3791,7 @@ hppa_expand_prologue (void) } for (i = 18; i >= 3; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { /* If merge_sp_adjust_with_store is nonzero, then we can optimize the first GR save. */ @@ -4072,8 +3854,8 @@ hppa_expand_prologue (void) /* Now actually save the FP registers. */ for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP) { - if (regs_ever_live[i] - || (! TARGET_64BIT && regs_ever_live[i + 1])) + if (df_regs_ever_live_p (i) + || (! TARGET_64BIT && df_regs_ever_live_p (i + 1))) { rtx addr, insn, reg; addr = gen_rtx_MEM (DFmode, gen_rtx_POST_INC (DFmode, tmpreg)); @@ -4228,7 +4010,8 @@ pa_output_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) /* We done with this subspace except possibly for some additional debug information. Forget that we are in this subspace to ensure that the next function is output in its own subspace. */ - forget_section (); + in_section = NULL; + cfun->machine->in_nsubspa = 2; } if (INSN_ADDRESSES_SET_P ()) @@ -4260,7 +4043,7 @@ hppa_expand_epilogue (void) /* Try to restore RP early to avoid load/use interlocks when RP gets used in the return (bv) instruction. This appears to still be necessary even when we schedule the prologue and epilogue. */ - if (regs_ever_live [2] || current_function_calls_eh_return) + if (rp_saved) { ret_off = TARGET_64BIT ? -16 : -20; if (frame_pointer_needed) @@ -4302,7 +4085,7 @@ hppa_expand_epilogue (void) } for (i = 18; i >= 4; i--) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { load_reg (i, offset, FRAME_POINTER_REGNUM); offset += UNITS_PER_WORD; @@ -4339,7 +4122,7 @@ hppa_expand_epilogue (void) for (i = 18; i >= 3; i--) { - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_used_regs[i]) { /* Only for the first load. merge_sp_adjust_with_load holds the register load @@ -4369,8 +4152,8 @@ hppa_expand_epilogue (void) /* Actually do the restores now. */ for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP) - if (regs_ever_live[i] - || (! TARGET_64BIT && regs_ever_live[i + 1])) + if (df_regs_ever_live_p (i) + || (! TARGET_64BIT && df_regs_ever_live_p (i + 1))) { rtx src = gen_rtx_MEM (DFmode, gen_rtx_POST_INC (DFmode, tmpreg)); rtx dest = gen_rtx_REG (DFmode, i); @@ -4428,6 +4211,37 @@ hppa_pic_save_rtx (void) return get_hard_reg_initial_val (word_mode, PIC_OFFSET_TABLE_REGNUM); } +#ifndef NO_DEFERRED_PROFILE_COUNTERS +#define NO_DEFERRED_PROFILE_COUNTERS 0 +#endif + + +/* Vector of funcdef numbers. */ +static VEC(int,heap) *funcdef_nos; + +/* Output deferred profile counters. */ +static void +output_deferred_profile_counters (void) +{ + unsigned int i; + int align, n; + + if (VEC_empty (int, funcdef_nos)) + return; + + switch_to_section (data_section); + align = MIN (BIGGEST_ALIGNMENT, LONG_TYPE_SIZE); + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + + for (i = 0; VEC_iterate (int, funcdef_nos, i, n); i++) + { + targetm.asm_out.internal_label (asm_out_file, "LP", n); + assemble_integer (const0_rtx, LONG_TYPE_SIZE / BITS_PER_UNIT, align, 1); + } + + VEC_free (int, heap, funcdef_nos); +} + void hppa_profile_hook (int label_no) { @@ -4450,7 +4264,7 @@ hppa_profile_hook (int label_no) emit_move_insn (gen_rtx_REG (word_mode, 26), gen_rtx_REG (word_mode, 2)); - /* The address of the function is loaded into %r25 with a instruction- + /* The address of the function is loaded into %r25 with an instruction- relative sequence that avoids the use of relocations. The sequence is split so that the load_offset_label_address instruction can occupy the delay slot of the call to _mcount. */ @@ -4462,11 +4276,12 @@ hppa_profile_hook (int label_no) emit_insn (gen_load_offset_label_address (gen_rtx_REG (SImode, 25), reg, begin_label_rtx, label_rtx)); -#ifndef NO_PROFILE_COUNTERS +#if !NO_DEFERRED_PROFILE_COUNTERS { rtx count_label_rtx, addr, r24; char count_label_name[16]; + VEC_safe_push (int, heap, funcdef_nos, label_no); ASM_GENERATE_INTERNAL_LABEL (count_label_name, "LP", label_no); count_label_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (count_label_name)); @@ -4576,8 +4391,10 @@ return_addr_rtx (int count, rtx frameaddr) GEN_INT (0x00011820), NE, NULL_RTX, SImode, 1); emit_jump_insn (gen_bne (label)); + /* 0xe0400002 must be specified as -532676606 so that it won't be + rejected as an invalid immediate operand on 64-bit hosts. */ emit_cmp_insn (gen_rtx_MEM (SImode, plus_constant (ins, 12)), - GEN_INT (0xe0400002), NE, NULL_RTX, SImode, 1); + GEN_INT (-532676606), NE, NULL_RTX, SImode, 1); /* If there is no export stub then just use the value saved from the return pointer register. */ @@ -4599,22 +4416,6 @@ return_addr_rtx (int count, rtx frameaddr) return saved_rp; } -/* This is only valid once reload has completed because it depends on - knowing exactly how much (if any) frame there is and... - - It's only valid if there is no frame marker to de-allocate and... - - It's only valid if %r2 hasn't been saved into the caller's frame - (we're not profiling and %r2 isn't live anywhere). */ -int -hppa_can_use_return_insn_p (void) -{ - return (reload_completed - && (compute_frame_size (get_frame_size (), 0) ? 0 : 1) - && ! regs_ever_live[2] - && ! frame_pointer_needed); -} - void emit_bcond_fp (enum rtx_code code, rtx operand0) { @@ -4654,8 +4455,9 @@ pa_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost) attr_type = get_attr_type (insn); - if (REG_NOTE_KIND (link) == REG_DEP_ANTI) + switch (REG_NOTE_KIND (link)) { + case REG_DEP_ANTI: /* Anti dependency; DEP_INSN reads a register that INSN writes some cycles later. */ @@ -4735,9 +4537,8 @@ pa_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost) /* For other anti dependencies, the cost is 0. */ return 0; - } - else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT) - { + + case REG_DEP_OUTPUT: /* Output dependency; DEP_INSN writes a register that INSN writes some cycles later. */ if (attr_type == TYPE_FPLOAD) @@ -4820,9 +4621,10 @@ pa_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost) /* For other output dependencies, the cost is 0. */ return 0; + + default: + gcc_unreachable (); } - else - abort (); } /* Adjust scheduling priorities. We use this to try and keep addil @@ -4872,7 +4674,7 @@ pa_issue_rate (void) case PROCESSOR_8000: return 4; default: - abort (); + gcc_unreachable (); } } @@ -5035,7 +4837,7 @@ print_operand (FILE *file, rtx x, int code) case LTU: fputs ("<<", file); break; default: - abort (); + gcc_unreachable (); } return; case 'N': /* Condition, (N)egated */ @@ -5062,11 +4864,14 @@ print_operand (FILE *file, rtx x, int code) case LTU: fputs (">>=", file); break; default: - abort (); + gcc_unreachable (); } return; /* For floating point comparisons. Note that the output - predicates are the complement of the desired mode. */ + predicates are the complement of the desired mode. The + conditions for GT, GE, LT, LE and LTGT cause an invalid + operation exception if the result is unordered and this + exception is enabled in the floating-point status register. */ case 'Y': switch (GET_CODE (x)) { @@ -5085,21 +4890,21 @@ print_operand (FILE *file, rtx x, int code) case LTGT: fputs ("!<>", file); break; case UNLE: - fputs (">", file); break; + fputs ("!?<=", file); break; case UNLT: - fputs (">=", file); break; + fputs ("!?<", file); break; case UNGE: - fputs ("<", file); break; + fputs ("!?>=", file); break; case UNGT: - fputs ("<=", file); break; + fputs ("!?>", file); break; case UNEQ: - fputs ("<>", file); break; + fputs ("!?=", file); break; case UNORDERED: - fputs ("<=>", file); break; + fputs ("!?", file); break; case ORDERED: - fputs ("!<=>", file); break; + fputs ("?", file); break; default: - abort (); + gcc_unreachable (); } return; case 'S': /* Condition, operands are (S)wapped. */ @@ -5126,7 +4931,7 @@ print_operand (FILE *file, rtx x, int code) case LTU: fputs (">>", file); break; default: - abort (); + gcc_unreachable (); } return; case 'B': /* Condition, (B)oth swapped and negate. */ @@ -5153,51 +4958,33 @@ print_operand (FILE *file, rtx x, int code) case LTU: fputs ("<<=", file); break; default: - abort (); + gcc_unreachable (); } return; case 'k': - if (GET_CODE (x) == CONST_INT) - { - fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~INTVAL (x)); - return; - } - abort (); + gcc_assert (GET_CODE (x) == CONST_INT); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~INTVAL (x)); + return; case 'Q': - if (GET_CODE (x) == CONST_INT) - { - fprintf (file, HOST_WIDE_INT_PRINT_DEC, 64 - (INTVAL (x) & 63)); - return; - } - abort (); + gcc_assert (GET_CODE (x) == CONST_INT); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, 64 - (INTVAL (x) & 63)); + return; case 'L': - if (GET_CODE (x) == CONST_INT) - { - fprintf (file, HOST_WIDE_INT_PRINT_DEC, 32 - (INTVAL (x) & 31)); - return; - } - abort (); + gcc_assert (GET_CODE (x) == CONST_INT); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, 32 - (INTVAL (x) & 31)); + return; case 'O': - if (GET_CODE (x) == CONST_INT && exact_log2 (INTVAL (x)) >= 0) - { - fprintf (file, "%d", exact_log2 (INTVAL (x))); - return; - } - abort (); + gcc_assert (GET_CODE (x) == CONST_INT && exact_log2 (INTVAL (x)) >= 0); + fprintf (file, "%d", exact_log2 (INTVAL (x))); + return; case 'p': - if (GET_CODE (x) == CONST_INT) - { - fprintf (file, HOST_WIDE_INT_PRINT_DEC, 63 - (INTVAL (x) & 63)); - return; - } - abort (); + gcc_assert (GET_CODE (x) == CONST_INT); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, 63 - (INTVAL (x) & 63)); + return; case 'P': - if (GET_CODE (x) == CONST_INT) - { - fprintf (file, HOST_WIDE_INT_PRINT_DEC, 31 - (INTVAL (x) & 31)); - return; - } - abort (); + gcc_assert (GET_CODE (x) == CONST_INT); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, 31 - (INTVAL (x) & 31)); + return; case 'I': if (GET_CODE (x) == CONST_INT) fputs ("i", file); @@ -5272,7 +5059,7 @@ print_operand (FILE *file, rtx x, int code) addresses. */ break; default: - abort (); + gcc_unreachable (); } if (GET_CODE (x) == REG) { @@ -5347,10 +5134,10 @@ output_global_address (FILE *file, rtx x, int round_constant) x = XEXP (x, 0); if (GET_CODE (x) == SYMBOL_REF && read_only_operand (x, VOIDmode)) - assemble_name (file, XSTR (x, 0)); + output_addr_const (file, x); else if (GET_CODE (x) == SYMBOL_REF && !flag_pic) { - assemble_name (file, XSTR (x, 0)); + output_addr_const (file, x); fputs ("-$global$", file); } else if (GET_CODE (x) == CONST) @@ -5359,23 +5146,31 @@ output_global_address (FILE *file, rtx x, int round_constant) int offset = 0; /* assembler wants -$global$ at end */ rtx base = NULL_RTX; - if (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF) + switch (GET_CODE (XEXP (XEXP (x, 0), 0))) { + case SYMBOL_REF: base = XEXP (XEXP (x, 0), 0); output_addr_const (file, base); + break; + case CONST_INT: + offset = INTVAL (XEXP (XEXP (x, 0), 0)); + break; + default: + gcc_unreachable (); } - else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == CONST_INT) - offset = INTVAL (XEXP (XEXP (x, 0), 0)); - else abort (); - if (GET_CODE (XEXP (XEXP (x, 0), 1)) == SYMBOL_REF) + switch (GET_CODE (XEXP (XEXP (x, 0), 1))) { + case SYMBOL_REF: base = XEXP (XEXP (x, 0), 1); output_addr_const (file, base); + break; + case CONST_INT: + offset = INTVAL (XEXP (XEXP (x, 0), 1)); + break; + default: + gcc_unreachable (); } - else if (GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) - offset = INTVAL (XEXP (XEXP (x, 0), 1)); - else abort (); /* How bogus. The compiler is apparently responsible for rounding the constant if it uses an LR field selector. @@ -5389,8 +5184,9 @@ output_global_address (FILE *file, rtx x, int round_constant) if (round_constant) offset = ((offset + 0x1000) & ~0x1fff); - if (GET_CODE (XEXP (x, 0)) == PLUS) + switch (GET_CODE (XEXP (x, 0))) { + case PLUS: if (offset < 0) { offset = -offset; @@ -5398,12 +5194,17 @@ output_global_address (FILE *file, rtx x, int round_constant) } else sep = "+"; - } - else if (GET_CODE (XEXP (x, 0)) == MINUS - && (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)) - sep = "-"; - else abort (); + break; + + case MINUS: + gcc_assert (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF); + sep = "-"; + break; + default: + gcc_unreachable (); + } + if (!read_only_operand (base, VOIDmode) && !flag_pic) fputs ("-$global$", file); if (offset) @@ -5510,23 +5311,27 @@ pa_hpux64_hpas_file_start (void) } #undef aputs -static struct deferred_plabel * -get_plabel (const char *fname) +/* Search the deferred plabel list for SYMBOL and return its internal + label. If an entry for SYMBOL is not found, a new entry is created. */ + +rtx +get_deferred_plabel (rtx symbol) { + const char *fname = XSTR (symbol, 0); size_t i; /* See if we have already put this function on the list of deferred plabels. This list is generally small, so a liner search is not too ugly. If it proves too slow replace it with something faster. */ for (i = 0; i < n_deferred_plabels; i++) - if (strcmp (fname, deferred_plabels[i].name) == 0) + if (strcmp (fname, XSTR (deferred_plabels[i].symbol, 0)) == 0) break; /* If the deferred plabel list is empty, or this entry was not found on the list, create a new entry on the list. */ if (deferred_plabels == NULL || i == n_deferred_plabels) { - const char *real_name; + tree id; if (deferred_plabels == 0) deferred_plabels = (struct deferred_plabel *) @@ -5539,36 +5344,38 @@ get_plabel (const char *fname) i = n_deferred_plabels++; deferred_plabels[i].internal_label = gen_label_rtx (); - deferred_plabels[i].name = ggc_strdup (fname); + deferred_plabels[i].symbol = symbol; - /* Gross. We have just implicitly taken the address of this function, - mark it as such. */ - real_name = (*targetm.strip_name_encoding) (fname); - TREE_SYMBOL_REFERENCED (get_identifier (real_name)) = 1; + /* Gross. We have just implicitly taken the address of this + function. Mark it in the same manner as assemble_name. */ + id = maybe_get_identifier (targetm.strip_name_encoding (fname)); + if (id) + mark_referenced (id); } - return &deferred_plabels[i]; + return deferred_plabels[i].internal_label; } static void output_deferred_plabels (void) { size_t i; - /* If we have deferred plabels, then we need to switch into the data - section and align it to a 4 byte boundary before we output the - deferred plabels. */ + + /* If we have some deferred plabels, then we need to switch into the + data or readonly data section, and align it to a 4 byte boundary + before outputting the deferred plabels. */ if (n_deferred_plabels) { - data_section (); + switch_to_section (flag_pic ? data_section : readonly_data_section); ASM_OUTPUT_ALIGN (asm_out_file, TARGET_64BIT ? 3 : 2); } /* Now output the deferred plabels. */ for (i = 0; i < n_deferred_plabels; i++) { - (*targetm.asm_out.internal_label) (asm_out_file, "L", + targetm.asm_out.internal_label (asm_out_file, "L", CODE_LABEL_NUMBER (deferred_plabels[i].internal_label)); - assemble_integer (gen_rtx_SYMBOL_REF (Pmode, deferred_plabels[i].name), + assemble_integer (deferred_plabels[i].symbol, TARGET_64BIT ? 8 : 4, TARGET_64BIT ? 64 : 32, 1); } } @@ -5610,6 +5417,8 @@ pa_hpux_init_libfuncs (void) set_conv_libfunc (sfloat_optab, TFmode, SImode, "_U_Qfcnvxf_sgl_to_quad"); set_conv_libfunc (sfloat_optab, TFmode, DImode, "_U_Qfcnvxf_dbl_to_quad"); + set_conv_libfunc (ufloat_optab, TFmode, SImode, "_U_Qfcnvxf_usgl_to_quad"); + set_conv_libfunc (ufloat_optab, TFmode, DImode, "_U_Qfcnvxf_udbl_to_quad"); } #endif @@ -5650,8 +5459,7 @@ output_mul_insn (int unsignedp ATTRIBUTE_UNUSED, rtx insn) /* Emit the rtl for doing a division by a constant. */ /* Do magic division millicodes exist for this value? */ -static const int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, - 1, 1}; +const int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1}; /* We'll use an array to keep track of the magic millicodes and whether or not we've used them already. [n][0] is signed, [n][1] is @@ -5660,15 +5468,6 @@ static const int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, static int div_milli[16][2]; int -div_operand (rtx op, enum machine_mode mode) -{ - return (mode == SImode - && ((GET_CODE (op) == REG && REGNO (op) == 25) - || (GET_CODE (op) == CONST_INT && INTVAL (op) > 0 - && INTVAL (op) < 16 && magic_milli[INTVAL (op)]))); -} - -int emit_hpdiv_const (rtx *operands, int unsignedp) { if (GET_CODE (operands[2]) == CONST_INT @@ -5795,9 +5594,9 @@ output_arg_descriptor (rtx call_insn) return; } - if (GET_CODE (call_insn) != CALL_INSN) - abort (); - for (link = CALL_INSN_FUNCTION_USAGE (call_insn); link; link = XEXP (link, 1)) + gcc_assert (GET_CODE (call_insn) == CALL_INSN); + for (link = CALL_INSN_FUNCTION_USAGE (call_insn); + link; link = XEXP (link, 1)) { rtx use = XEXP (link, 0); @@ -5843,104 +5642,138 @@ output_arg_descriptor (rtx call_insn) fputc ('\n', asm_out_file); } -/* Return the class of any secondary reload register that is needed to - move IN into a register in class CLASS using mode MODE. - - Profiling has showed this routine and its descendants account for - a significant amount of compile time (~7%). So it has been - optimized to reduce redundant computations and eliminate useless - function calls. - - It might be worthwhile to try and make this a leaf function too. */ - -enum reg_class -secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in) +static enum reg_class +pa_secondary_reload (bool in_p, rtx x, enum reg_class class, + enum machine_mode mode, secondary_reload_info *sri) { - int regno, is_symbolic; - - /* Trying to load a constant into a FP register during PIC code - generation will require %r1 as a scratch register. */ - if (flag_pic - && GET_MODE_CLASS (mode) == MODE_INT - && FP_REG_CLASS_P (class) - && (GET_CODE (in) == CONST_INT || GET_CODE (in) == CONST_DOUBLE)) - return R1_REGS; + int is_symbolic, regno; - /* Profiling showed the PA port spends about 1.3% of its compilation - time in true_regnum from calls inside secondary_reload_class. */ + /* Handle the easy stuff first. */ + if (class == R1_REGS) + return NO_REGS; - if (GET_CODE (in) == REG) + if (REG_P (x)) { - regno = REGNO (in); - if (regno >= FIRST_PSEUDO_REGISTER) - regno = true_regnum (in); + regno = REGNO (x); + if (class == BASE_REG_CLASS && regno < FIRST_PSEUDO_REGISTER) + return NO_REGS; } - else if (GET_CODE (in) == SUBREG) - regno = true_regnum (in); else regno = -1; /* If we have something like (mem (mem (...)), we can safely assume the inner MEM will end up in a general register after reloading, so there's no need for a secondary reload. */ - if (GET_CODE (in) == MEM - && GET_CODE (XEXP (in, 0)) == MEM) + if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == MEM) return NO_REGS; + /* Trying to load a constant into a FP register during PIC code + generation requires %r1 as a scratch register. */ + if (flag_pic + && (mode == SImode || mode == DImode) + && FP_REG_CLASS_P (class) + && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)) + { + sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1 + : CODE_FOR_reload_indi_r1); + return NO_REGS; + } + + /* Profiling showed the PA port spends about 1.3% of its compilation + time in true_regnum from calls inside pa_secondary_reload_class. */ + if (regno >= FIRST_PSEUDO_REGISTER || GET_CODE (x) == SUBREG) + regno = true_regnum (x); + /* Handle out of range displacement for integer mode loads/stores of FP registers. */ if (((regno >= FIRST_PSEUDO_REGISTER || regno == -1) && GET_MODE_CLASS (mode) == MODE_INT && FP_REG_CLASS_P (class)) || (class == SHIFT_REGS && (regno <= 0 || regno >= 32))) - return GENERAL_REGS; + { + sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode]; + return NO_REGS; + } /* A SAR<->FP register copy requires a secondary register (GPR) as well as secondary memory. */ if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER && ((REGNO_REG_CLASS (regno) == SHIFT_REGS && FP_REG_CLASS_P (class)) - || (class == SHIFT_REGS && FP_REG_CLASS_P (REGNO_REG_CLASS (regno))))) - return GENERAL_REGS; + || (class == SHIFT_REGS + && FP_REG_CLASS_P (REGNO_REG_CLASS (regno))))) + { + sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode]; + return NO_REGS; + } - if (GET_CODE (in) == HIGH) - in = XEXP (in, 0); + /* Secondary reloads of symbolic operands require %r1 as a scratch + register when we're generating PIC code and the operand isn't + readonly. */ + if (GET_CODE (x) == HIGH) + x = XEXP (x, 0); /* Profiling has showed GCC spends about 2.6% of its compilation - time in symbolic_operand from calls inside secondary_reload_class. - - We use an inline copy and only compute its return value once to avoid - useless work. */ - switch (GET_CODE (in)) + time in symbolic_operand from calls inside pa_secondary_reload_class. + So, we use an inline copy to avoid useless work. */ + switch (GET_CODE (x)) { - rtx tmp; + rtx op; case SYMBOL_REF: + is_symbolic = !SYMBOL_REF_TLS_MODEL (x); + break; case LABEL_REF: is_symbolic = 1; break; case CONST: - tmp = XEXP (in, 0); - is_symbolic = ((GET_CODE (XEXP (tmp, 0)) == SYMBOL_REF - || GET_CODE (XEXP (tmp, 0)) == LABEL_REF) - && GET_CODE (XEXP (tmp, 1)) == CONST_INT); + op = XEXP (x, 0); + is_symbolic = (((GET_CODE (XEXP (op, 0)) == SYMBOL_REF + && !SYMBOL_REF_TLS_MODEL (XEXP (op, 0))) + || GET_CODE (XEXP (op, 0)) == LABEL_REF) + && GET_CODE (XEXP (op, 1)) == CONST_INT); break; - default: is_symbolic = 0; break; } - if (!flag_pic - && is_symbolic - && read_only_operand (in, VOIDmode)) - return NO_REGS; - - if (class != R1_REGS && is_symbolic) - return R1_REGS; + if (is_symbolic && (flag_pic || !read_only_operand (x, VOIDmode))) + { + gcc_assert (mode == SImode || mode == DImode); + sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1 + : CODE_FOR_reload_indi_r1); + } return NO_REGS; } +/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. The argument pointer + is only marked as live on entry by df-scan when it is a fixed + register. It isn't a fixed register in the 64-bit runtime, + so we need to mark it here. */ + +static void +pa_extra_live_on_entry (bitmap regs) +{ + if (TARGET_64BIT) + bitmap_set_bit (regs, ARG_POINTER_REGNUM); +} + +/* Implement EH_RETURN_HANDLER_RTX. The MEM needs to be volatile + to prevent it from being deleted. */ + +rtx +pa_eh_return_handler_rtx (void) +{ + rtx tmp; + + tmp = gen_rtx_PLUS (word_mode, frame_pointer_rtx, + TARGET_64BIT ? GEN_INT (-16) : GEN_INT (-20)); + tmp = gen_rtx_MEM (word_mode, tmp); + tmp->volatil = 1; + return tmp; +} + /* In the 32-bit runtime, arguments larger than eight bytes are passed by invisible reference. As a GCC extension, we also pass anything with a zero or variable size by reference. @@ -5956,7 +5789,7 @@ secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in) static bool pa_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, - enum machine_mode mode, tree type, + enum machine_mode mode, const_tree type, bool named ATTRIBUTE_UNUSED) { HOST_WIDE_INT size; @@ -5973,7 +5806,7 @@ pa_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, } enum direction -function_arg_padding (enum machine_mode mode, tree type) +function_arg_padding (enum machine_mode mode, const_tree type) { if (mode == BLKmode || (TARGET_64BIT && type && AGGREGATE_TYPE_P (type))) @@ -6114,33 +5947,83 @@ hppa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) /* Args grow down. Not handled by generic routines. */ - u = fold_convert (valist_type, size_in_bytes (type)); - t = build (MINUS_EXPR, valist_type, valist, u); + u = fold_convert (sizetype, size_in_bytes (type)); + u = fold_build1 (NEGATE_EXPR, sizetype, u); + t = build2 (POINTER_PLUS_EXPR, valist_type, valist, u); /* Copied from va-pa.h, but we probably don't need to align to word size, since we generate and preserve that invariant. */ - u = build_int_cst (valist_type, (size > 4 ? -8 : -4), -1); - t = build (BIT_AND_EXPR, valist_type, t, u); + u = size_int (size > 4 ? -8 : -4); + t = fold_convert (sizetype, t); + t = build2 (BIT_AND_EXPR, sizetype, t, u); + t = fold_convert (valist_type, t); - t = build (MODIFY_EXPR, valist_type, valist, t); + t = build2 (MODIFY_EXPR, valist_type, valist, t); ofs = (8 - size) % 4; if (ofs != 0) { - u = fold_convert (valist_type, size_int (ofs)); - t = build (PLUS_EXPR, valist_type, t, u); + u = size_int (ofs); + t = build2 (POINTER_PLUS_EXPR, valist_type, t, u); } t = fold_convert (ptr, t); - t = build_fold_indirect_ref (t); + t = build_va_arg_indirect_ref (t); if (indirect) - t = build_fold_indirect_ref (t); + t = build_va_arg_indirect_ref (t); return t; } } +/* True if MODE is valid for the target. By "valid", we mean able to + be manipulated in non-trivial ways. In particular, this means all + the arithmetic is supported. + + Currently, TImode is not valid as the HP 64-bit runtime documentation + doesn't document the alignment and calling conventions for this type. + Thus, we return false when PRECISION is 2 * BITS_PER_WORD and + 2 * BITS_PER_WORD isn't equal LONG_LONG_TYPE_SIZE. */ + +static bool +pa_scalar_mode_supported_p (enum machine_mode mode) +{ + int precision = GET_MODE_PRECISION (mode); + + switch (GET_MODE_CLASS (mode)) + { + case MODE_PARTIAL_INT: + case MODE_INT: + if (precision == CHAR_TYPE_SIZE) + return true; + if (precision == SHORT_TYPE_SIZE) + return true; + if (precision == INT_TYPE_SIZE) + return true; + if (precision == LONG_TYPE_SIZE) + return true; + if (precision == LONG_LONG_TYPE_SIZE) + return true; + return false; + + case MODE_FLOAT: + if (precision == FLOAT_TYPE_SIZE) + return true; + if (precision == DOUBLE_TYPE_SIZE) + return true; + if (precision == LONG_DOUBLE_TYPE_SIZE) + return true; + return false; + + case MODE_DECIMAL_FLOAT: + return false; + + default: + gcc_unreachable (); + } +} + /* This routine handles all the normal conditional branch sequences we might need to generate. It handles compare immediate vs compare register, nullification of delay slots, varying length branches, @@ -6149,13 +6032,15 @@ hppa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) parameters. */ const char * -output_cbranch (rtx *operands, int nullify, int length, int negated, rtx insn) +output_cbranch (rtx *operands, int negated, rtx insn) { static char buf[100]; int useskip = 0; - rtx xoperands[5]; + int nullify = INSN_ANNULLED_BRANCH_P (insn); + int length = get_attr_length (insn); + int xdelay; - /* A conditional branch to the following instruction (eg the delay slot) + /* A conditional branch to the following instruction (e.g. the delay slot) is asking for a disaster. This can happen when not optimizing and when jump optimization fails. @@ -6172,6 +6057,8 @@ output_cbranch (rtx *operands, int nullify, int length, int negated, rtx insn) zero for cmpb, we must ensure that we use cmpb for the comparison. */ if (GET_MODE (operands[1]) == DImode && operands[2] == const0_rtx) operands[2] = gen_rtx_REG (DImode, 0); + if (GET_MODE (operands[2]) == DImode && operands[1] == const0_rtx) + operands[1] = gen_rtx_REG (DImode, 0); /* If this is a long branch with its delay slot unfilled, set `nullify' as it can nullify the delay slot and save a nop. */ @@ -6221,7 +6108,7 @@ output_cbranch (rtx *operands, int nullify, int length, int negated, rtx insn) with an unfilled delay slot. */ case 8: /* Handle weird backwards branch with a filled delay slot - with is nullified. */ + which is nullified. */ if (dbr_sequence_length () != 0 && ! forward_branch_p (insn) && nullify) @@ -6266,21 +6153,26 @@ output_cbranch (rtx *operands, int nullify, int length, int negated, rtx insn) else strcat (buf, " %2,%r1,%%r0\n\tb %0"); } - break; - - case 20: - case 28: - xoperands[0] = operands[0]; - xoperands[1] = operands[1]; - xoperands[2] = operands[2]; - xoperands[3] = operands[3]; - - /* The reversed conditional branch must branch over one additional - instruction if the delay slot is filled. If the delay slot - is empty, the instruction after the reversed condition branch - must be nullified. */ - nullify = dbr_sequence_length () == 0; - xoperands[4] = nullify ? GEN_INT (length) : GEN_INT (length + 4); + break; + + default: + /* The reversed conditional branch must branch over one additional + instruction if the delay slot is filled and needs to be extracted + by output_lbranch. If the delay slot is empty or this is a + nullified forward branch, the instruction after the reversed + condition branch must be nullified. */ + if (dbr_sequence_length () == 0 + || (nullify && forward_branch_p (insn))) + { + nullify = 1; + xdelay = 0; + operands[4] = GEN_INT (length); + } + else + { + xdelay = 1; + operands[4] = GEN_INT (length + 4); + } /* Create a reversed conditional branch which branches around the following insns. */ @@ -6327,39 +6219,47 @@ output_cbranch (rtx *operands, int nullify, int length, int negated, rtx insn) } } - output_asm_insn (buf, xoperands); - return output_lbranch (operands[0], insn); - - default: - abort (); + output_asm_insn (buf, operands); + return output_lbranch (operands[0], insn, xdelay); } return buf; } -/* This routine handles long unconditional branches that exceed the - maximum range of a simple branch instruction. */ +/* This routine handles output of long unconditional branches that + exceed the maximum range of a simple branch instruction. Since + we don't have a register available for the branch, we save register + %r1 in the frame marker, load the branch destination DEST into %r1, + execute the branch, and restore %r1 in the delay slot of the branch. + + Since long branches may have an insn in the delay slot and the + delay slot is used to restore %r1, we in general need to extract + this insn and execute it before the branch. However, to facilitate + use of this function by conditional branches, we also provide an + option to not extract the delay insn so that it will be emitted + after the long branch. So, if there is an insn in the delay slot, + it is extracted if XDELAY is nonzero. + + The lengths of the various long-branch sequences are 20, 16 and 24 + bytes for the portable runtime, non-PIC and PIC cases, respectively. */ const char * -output_lbranch (rtx dest, rtx insn) +output_lbranch (rtx dest, rtx insn, int xdelay) { rtx xoperands[2]; xoperands[0] = dest; /* First, free up the delay slot. */ - if (dbr_sequence_length () != 0) + if (xdelay && dbr_sequence_length () != 0) { /* We can't handle a jump in the delay slot. */ - if (GET_CODE (NEXT_INSN (insn)) == JUMP_INSN) - abort (); + gcc_assert (GET_CODE (NEXT_INSN (insn)) != JUMP_INSN); final_scan_insn (NEXT_INSN (insn), asm_out_file, - optimize, 0, 0, NULL); + optimize, 0, NULL); /* Now delete the delay insn. */ - PUT_CODE (NEXT_INSN (insn), NOTE); - NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + SET_INSN_DELETED (NEXT_INSN (insn)); } /* Output an insn to save %r1. The runtime documentation doesn't @@ -6384,7 +6284,7 @@ output_lbranch (rtx dest, rtx insn) for other purposes. */ if (TARGET_64BIT) { - if (actual_fsize == 0 && !regs_ever_live[2]) + if (actual_fsize == 0 && !df_regs_ever_live_p (2)) /* Use the return pointer slot in the frame marker. */ output_asm_insn ("std %%r1,-16(%%r30)", xoperands); else @@ -6394,7 +6294,7 @@ output_lbranch (rtx dest, rtx insn) } else { - if (actual_fsize == 0 && !regs_ever_live[2]) + if (actual_fsize == 0 && !df_regs_ever_live_p (2)) /* Use the return pointer slot in the frame marker. */ output_asm_insn ("stw %%r1,-20(%%r30)", xoperands); else @@ -6420,8 +6320,8 @@ output_lbranch (rtx dest, rtx insn) { xoperands[1] = gen_label_rtx (); output_asm_insn ("addil L'%l0-%l1,%%r1", xoperands); - (*targetm.asm_out.internal_label) (asm_out_file, "L", - CODE_LABEL_NUMBER (xoperands[1])); + targetm.asm_out.internal_label (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[1])); output_asm_insn ("ldo R'%l0-%l1(%%r1),%%r1", xoperands); } else @@ -6438,14 +6338,14 @@ output_lbranch (rtx dest, rtx insn) /* Now restore the value of %r1 in the delay slot. */ if (TARGET_64BIT) { - if (actual_fsize == 0 && !regs_ever_live[2]) + if (actual_fsize == 0 && !df_regs_ever_live_p (2)) return "ldd -16(%%r30),%%r1"; else return "ldd -40(%%r30),%%r1"; } else { - if (actual_fsize == 0 && !regs_ever_live[2]) + if (actual_fsize == 0 && !df_regs_ever_live_p (2)) return "ldw -20(%%r30),%%r1"; else return "ldw -12(%%r30),%%r1"; @@ -6458,13 +6358,15 @@ output_lbranch (rtx dest, rtx insn) above. it returns the appropriate output template to emit the branch. */ const char * -output_bb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, - int negated, rtx insn, int which) +output_bb (rtx *operands ATTRIBUTE_UNUSED, int negated, rtx insn, int which) { static char buf[100]; int useskip = 0; + int nullify = INSN_ANNULLED_BRANCH_P (insn); + int length = get_attr_length (insn); + int xdelay; - /* A conditional branch to the following instruction (eg the delay slot) is + /* A conditional branch to the following instruction (e.g. the delay slot) is asking for a disaster. I do not think this can happen as this pattern is only used when optimizing; jump optimization should eliminate the jump. But be prepared just in case. */ @@ -6529,7 +6431,7 @@ output_bb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, with an unfilled delay slot. */ case 8: /* Handle weird backwards branch with a filled delay slot - with is nullified. */ + which is nullified. */ if (dbr_sequence_length () != 0 && ! forward_branch_p (insn) && nullify) @@ -6571,9 +6473,10 @@ output_bb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, } else { - strcpy (buf, "{extrs,|extrw,s,}"); if (GET_MODE (operands[0]) == DImode) strcpy (buf, "extrd,s,*"); + else + strcpy (buf, "{extrs,|extrw,s,}"); if ((which == 0 && negated) || (which == 1 && ! negated)) strcat (buf, "<"); @@ -6591,7 +6494,40 @@ output_bb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, break; default: - abort (); + /* The reversed conditional branch must branch over one additional + instruction if the delay slot is filled and needs to be extracted + by output_lbranch. If the delay slot is empty or this is a + nullified forward branch, the instruction after the reversed + condition branch must be nullified. */ + if (dbr_sequence_length () == 0 + || (nullify && forward_branch_p (insn))) + { + nullify = 1; + xdelay = 0; + operands[4] = GEN_INT (length); + } + else + { + xdelay = 1; + operands[4] = GEN_INT (length + 4); + } + + if (GET_MODE (operands[0]) == DImode) + strcpy (buf, "bb,*"); + else + strcpy (buf, "bb,"); + if ((which == 0 && negated) + || (which == 1 && !negated)) + strcat (buf, "<"); + else + strcat (buf, ">="); + if (nullify) + strcat (buf, ",n %0,%1,.+%4"); + else + strcat (buf, " %0,%1,.+%4"); + output_asm_insn (buf, operands); + return output_lbranch (negated ? operands[3] : operands[2], + insn, xdelay); } return buf; } @@ -6603,13 +6539,15 @@ output_bb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, branch. */ const char * -output_bvb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, - int negated, rtx insn, int which) +output_bvb (rtx *operands ATTRIBUTE_UNUSED, int negated, rtx insn, int which) { static char buf[100]; int useskip = 0; + int nullify = INSN_ANNULLED_BRANCH_P (insn); + int length = get_attr_length (insn); + int xdelay; - /* A conditional branch to the following instruction (eg the delay slot) is + /* A conditional branch to the following instruction (e.g. the delay slot) is asking for a disaster. I do not think this can happen as this pattern is only used when optimizing; jump optimization should eliminate the jump. But be prepared just in case. */ @@ -6674,7 +6612,7 @@ output_bvb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, with an unfilled delay slot. */ case 8: /* Handle weird backwards branch with a filled delay slot - with is nullified. */ + which is nullified. */ if (dbr_sequence_length () != 0 && ! forward_branch_p (insn) && nullify) @@ -6736,7 +6674,40 @@ output_bvb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, break; default: - abort (); + /* The reversed conditional branch must branch over one additional + instruction if the delay slot is filled and needs to be extracted + by output_lbranch. If the delay slot is empty or this is a + nullified forward branch, the instruction after the reversed + condition branch must be nullified. */ + if (dbr_sequence_length () == 0 + || (nullify && forward_branch_p (insn))) + { + nullify = 1; + xdelay = 0; + operands[4] = GEN_INT (length); + } + else + { + xdelay = 1; + operands[4] = GEN_INT (length + 4); + } + + if (GET_MODE (operands[0]) == DImode) + strcpy (buf, "bb,*"); + else + strcpy (buf, "{bvb,|bb,}"); + if ((which == 0 && negated) + || (which == 1 && !negated)) + strcat (buf, "<"); + else + strcat (buf, ">="); + if (nullify) + strcat (buf, ",n {%0,.+%4|%0,%%sar,.+%4}"); + else + strcat (buf, " {%0,.+%4|%0,%%sar,.+%4}"); + output_asm_insn (buf, operands); + return output_lbranch (negated ? operands[3] : operands[2], + insn, xdelay); } return buf; } @@ -6748,8 +6719,9 @@ output_bvb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length, const char * output_dbra (rtx *operands, rtx insn, int which_alternative) { + int length = get_attr_length (insn); - /* A conditional branch to the following instruction (eg the delay slot) is + /* A conditional branch to the following instruction (e.g. the delay slot) is asking for a disaster. Be prepared! */ if (next_real_insn (JUMP_LABEL (insn)) == next_real_insn (insn)) @@ -6773,7 +6745,7 @@ output_dbra (rtx *operands, rtx insn, int which_alternative) if (which_alternative == 0) { int nullify = INSN_ANNULLED_BRANCH_P (insn); - int length = get_attr_length (insn); + int xdelay; /* If this is a long branch with its delay slot unfilled, set `nullify' as it can nullify the delay slot and save a nop. */ @@ -6785,13 +6757,15 @@ output_dbra (rtx *operands, rtx insn, int which_alternative) if (! nullify && length == 4 && dbr_sequence_length () == 0) nullify = forward_branch_p (insn); - /* Handle short versions first. */ - if (length == 4 && nullify) - return "addib,%C2,n %1,%0,%3"; - else if (length == 4 && ! nullify) - return "addib,%C2 %1,%0,%3"; - else if (length == 8) + switch (length) { + case 4: + if (nullify) + return "addib,%C2,n %1,%0,%3"; + else + return "addib,%C2 %1,%0,%3"; + + case 8: /* Handle weird backwards branch with a fulled delay slot which is nullified. */ if (dbr_sequence_length () != 0 @@ -6813,9 +6787,34 @@ output_dbra (rtx *operands, rtx insn, int which_alternative) return "addi,%N2 %1,%0,%0\n\tb,n %3"; else return "addi,%N2 %1,%0,%0\n\tb %3"; + + default: + /* The reversed conditional branch must branch over one additional + instruction if the delay slot is filled and needs to be extracted + by output_lbranch. If the delay slot is empty or this is a + nullified forward branch, the instruction after the reversed + condition branch must be nullified. */ + if (dbr_sequence_length () == 0 + || (nullify && forward_branch_p (insn))) + { + nullify = 1; + xdelay = 0; + operands[4] = GEN_INT (length); + } + else + { + xdelay = 1; + operands[4] = GEN_INT (length + 4); + } + + if (nullify) + output_asm_insn ("addib,%N2,n %1,%0,.+%4", operands); + else + output_asm_insn ("addib,%N2 %1,%0,.+%4", operands); + + return output_lbranch (operands[3], insn, xdelay); } - else - abort (); + } /* Deal with gross reload from FP register case. */ else if (which_alternative == 1) @@ -6826,10 +6825,17 @@ output_dbra (rtx *operands, rtx insn, int which_alternative) output_asm_insn ("{fstws|fstw} %0,-16(%%r30)\n\tldw -16(%%r30),%4", operands); output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(%%r30)", operands); - if (get_attr_length (insn) == 24) + if (length == 24) return "{comb|cmpb},%S2 %%r0,%4,%3\n\t{fldws|fldw} -16(%%r30),%0"; - else + else if (length == 28) return "{comclr|cmpclr},%B2 %%r0,%4,%%r0\n\tb %3\n\t{fldws|fldw} -16(%%r30),%0"; + else + { + operands[5] = GEN_INT (length - 16); + output_asm_insn ("{comb|cmpb},%B2 %%r0,%4,.+%5", operands); + output_asm_insn ("{fldws|fldw} -16(%%r30),%0", operands); + return output_lbranch (operands[3], insn, 0); + } } /* Deal with gross reload from memory case. */ else @@ -6837,14 +6843,20 @@ output_dbra (rtx *operands, rtx insn, int which_alternative) /* Reload loop counter from memory, the store back to memory happens in the branch's delay slot. */ output_asm_insn ("ldw %0,%4", operands); - if (get_attr_length (insn) == 12) + if (length == 12) return "addib,%C2 %1,%4,%3\n\tstw %4,%0"; - else + else if (length == 16) return "addi,%N2 %1,%4,%4\n\tb %3\n\tstw %4,%0"; + else + { + operands[5] = GEN_INT (length - 4); + output_asm_insn ("addib,%N2 %1,%4,.+%5\n\tstw %4,%0", operands); + return output_lbranch (operands[3], insn, 0); + } } } -/* Return the output template for emitting a dbra type insn. +/* Return the output template for emitting a movb type insn. Note it may perform some output operations on its own before returning the final output string. */ @@ -6852,8 +6864,9 @@ const char * output_movb (rtx *operands, rtx insn, int which_alternative, int reverse_comparison) { + int length = get_attr_length (insn); - /* A conditional branch to the following instruction (eg the delay slot) is + /* A conditional branch to the following instruction (e.g. the delay slot) is asking for a disaster. Be prepared! */ if (next_real_insn (JUMP_LABEL (insn)) == next_real_insn (insn)) @@ -6878,7 +6891,7 @@ output_movb (rtx *operands, rtx insn, int which_alternative, if (which_alternative == 0) { int nullify = INSN_ANNULLED_BRANCH_P (insn); - int length = get_attr_length (insn); + int xdelay; /* If this is a long branch with its delay slot unfilled, set `nullify' as it can nullify the delay slot and save a nop. */ @@ -6890,13 +6903,15 @@ output_movb (rtx *operands, rtx insn, int which_alternative, if (! nullify && length == 4 && dbr_sequence_length () == 0) nullify = forward_branch_p (insn); - /* Handle short versions first. */ - if (length == 4 && nullify) - return "movb,%C2,n %1,%0,%3"; - else if (length == 4 && ! nullify) - return "movb,%C2 %1,%0,%3"; - else if (length == 8) + switch (length) { + case 4: + if (nullify) + return "movb,%C2,n %1,%0,%3"; + else + return "movb,%C2 %1,%0,%3"; + + case 8: /* Handle weird backwards branch with a filled delay slot which is nullified. */ if (dbr_sequence_length () != 0 @@ -6918,39 +6933,84 @@ output_movb (rtx *operands, rtx insn, int which_alternative, return "or,%N2 %1,%%r0,%0\n\tb,n %3"; else return "or,%N2 %1,%%r0,%0\n\tb %3"; + + default: + /* The reversed conditional branch must branch over one additional + instruction if the delay slot is filled and needs to be extracted + by output_lbranch. If the delay slot is empty or this is a + nullified forward branch, the instruction after the reversed + condition branch must be nullified. */ + if (dbr_sequence_length () == 0 + || (nullify && forward_branch_p (insn))) + { + nullify = 1; + xdelay = 0; + operands[4] = GEN_INT (length); + } + else + { + xdelay = 1; + operands[4] = GEN_INT (length + 4); + } + + if (nullify) + output_asm_insn ("movb,%N2,n %1,%0,.+%4", operands); + else + output_asm_insn ("movb,%N2 %1,%0,.+%4", operands); + + return output_lbranch (operands[3], insn, xdelay); } - else - abort (); } - /* Deal with gross reload from FP register case. */ + /* Deal with gross reload for FP destination register case. */ else if (which_alternative == 1) { - /* Move loop counter from FP register to MEM then into a GR, - increment the GR, store the GR into MEM, and finally reload - the FP register from MEM from within the branch's delay slot. */ + /* Move source register to MEM, perform the branch test, then + finally load the FP register from MEM from within the branch's + delay slot. */ output_asm_insn ("stw %1,-16(%%r30)", operands); - if (get_attr_length (insn) == 12) + if (length == 12) return "{comb|cmpb},%S2 %%r0,%1,%3\n\t{fldws|fldw} -16(%%r30),%0"; - else + else if (length == 16) return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\t{fldws|fldw} -16(%%r30),%0"; + else + { + operands[4] = GEN_INT (length - 4); + output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4", operands); + output_asm_insn ("{fldws|fldw} -16(%%r30),%0", operands); + return output_lbranch (operands[3], insn, 0); + } } /* Deal with gross reload from memory case. */ else if (which_alternative == 2) { /* Reload loop counter from memory, the store back to memory happens in the branch's delay slot. */ - if (get_attr_length (insn) == 8) + if (length == 8) return "{comb|cmpb},%S2 %%r0,%1,%3\n\tstw %1,%0"; - else + else if (length == 12) return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\tstw %1,%0"; + else + { + operands[4] = GEN_INT (length); + output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4\n\tstw %1,%0", + operands); + return output_lbranch (operands[3], insn, 0); + } } /* Handle SAR as a destination. */ else { - if (get_attr_length (insn) == 8) + if (length == 8) return "{comb|cmpb},%S2 %%r0,%1,%3\n\tmtsar %r1"; - else + else if (length == 12) return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\tmtsar %r1"; + else + { + operands[4] = GEN_INT (length); + output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4\n\tmtsar %r1", + operands); + return output_lbranch (operands[3], insn, 0); + } } } @@ -7120,7 +7180,7 @@ output_millicode_call (rtx insn, rtx call_dest) { xoperands[1] = gen_label_rtx (); output_asm_insn ("addil L'%0-%l1,%%r1", xoperands); - (*targetm.asm_out.internal_label) (asm_out_file, "L", + targetm.asm_out.internal_label (asm_out_file, "L", CODE_LABEL_NUMBER (xoperands[1])); output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands); } @@ -7164,7 +7224,7 @@ output_millicode_call (rtx insn, rtx call_dest) millicode symbol but not an arbitrary external symbol when generating SOM output. */ xoperands[1] = gen_label_rtx (); - (*targetm.asm_out.internal_label) (asm_out_file, "L", + targetm.asm_out.internal_label (asm_out_file, "L", CODE_LABEL_NUMBER (xoperands[1])); output_asm_insn ("addil L'%0-%l1,%%r1", xoperands); output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands); @@ -7203,8 +7263,8 @@ output_millicode_call (rtx insn, rtx call_dest) { xoperands[1] = gen_label_rtx (); output_asm_insn ("ldo %0-%1(%2),%2", xoperands); - (*targetm.asm_out.internal_label) (asm_out_file, "L", - CODE_LABEL_NUMBER (xoperands[1])); + targetm.asm_out.internal_label (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[1])); } else /* ??? This branch may not reach its target. */ @@ -7215,9 +7275,7 @@ output_millicode_call (rtx insn, rtx call_dest) output_asm_insn ("nop\n\tb,n %0", xoperands); /* Delete the jump. */ - PUT_CODE (NEXT_INSN (insn), NOTE); - NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + SET_INSN_DELETED (NEXT_INSN (insn)); return ""; } @@ -7262,7 +7320,7 @@ attr_length_call (rtx insn, int sibcall) call_dest = XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0); call_decl = SYMBOL_REF_DECL (call_dest); - local_call = call_decl && (*targetm.binds_local_p) (call_decl); + local_call = call_decl && targetm.binds_local_p (call_decl); /* pc-relative branch. */ if (!TARGET_LONG_CALLS @@ -7281,7 +7339,8 @@ attr_length_call (rtx insn, int sibcall) /* long pc-relative branch sequence. */ else if ((TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL) || (TARGET_64BIT && !TARGET_GAS) - || (TARGET_GAS && (TARGET_LONG_PIC_PCREL_CALL || local_call))) + || (TARGET_GAS && !TARGET_SOM + && (TARGET_LONG_PIC_PCREL_CALL || local_call))) { length += 20; @@ -7325,7 +7384,7 @@ output_call (rtx insn, rtx call_dest, int sibcall) int delay_slot_filled = 0; int seq_length = dbr_sequence_length (); tree call_decl = SYMBOL_REF_DECL (call_dest); - int local_call = call_decl && (*targetm.binds_local_p) (call_decl); + int local_call = call_decl && targetm.binds_local_p (call_decl); rtx xoperands[2]; xoperands[0] = call_dest; @@ -7345,9 +7404,7 @@ output_call (rtx insn, rtx call_dest, int sibcall) /* ??? As far as I can tell, the HP linker doesn't support the long pc-relative sequence described in the 64-bit runtime architecture. So, we use a slightly longer indirect call. */ - struct deferred_plabel *p = get_plabel (XSTR (call_dest, 0)); - - xoperands[0] = p->internal_label; + xoperands[0] = get_deferred_plabel (call_dest); xoperands[1] = gen_label_rtx (); /* If this isn't a sibcall, we put the load of %r27 into the @@ -7358,12 +7415,10 @@ output_call (rtx insn, rtx call_dest, int sibcall) && !sibcall) { final_scan_insn (NEXT_INSN (insn), asm_out_file, - optimize, 0, 0, NULL); + optimize, 0, NULL); /* Now delete the delay insn. */ - PUT_CODE (NEXT_INSN (insn), NOTE); - NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + SET_INSN_DELETED (NEXT_INSN (insn)); delay_insn_deleted = 1; } @@ -7394,7 +7449,8 @@ output_call (rtx insn, rtx call_dest, int sibcall) they don't allow an instruction in the delay slot. */ if (!((TARGET_LONG_ABS_CALL || local_call) && !flag_pic) && !(TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL) - && !(TARGET_GAS && (TARGET_LONG_PIC_PCREL_CALL || local_call)) + && !(TARGET_GAS && !TARGET_SOM + && (TARGET_LONG_PIC_PCREL_CALL || local_call)) && !TARGET_64BIT) indirect_call = 1; @@ -7406,13 +7462,11 @@ output_call (rtx insn, rtx call_dest, int sibcall) /* A non-jump insn in the delay slot. By definition we can emit this insn before the call (and in fact before argument relocating. */ - final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0, + final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, NULL); /* Now delete the delay insn. */ - PUT_CODE (NEXT_INSN (insn), NOTE); - NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + SET_INSN_DELETED (NEXT_INSN (insn)); delay_insn_deleted = 1; } @@ -7450,11 +7504,12 @@ output_call (rtx insn, rtx call_dest, int sibcall) xoperands[1] = gen_label_rtx (); output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands); output_asm_insn ("addil L'%0-%l1,%%r1", xoperands); - (*targetm.asm_out.internal_label) (asm_out_file, "L", + targetm.asm_out.internal_label (asm_out_file, "L", CODE_LABEL_NUMBER (xoperands[1])); output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands); } - else if (TARGET_GAS && (TARGET_LONG_PIC_PCREL_CALL || local_call)) + else if (TARGET_GAS && !TARGET_SOM + && (TARGET_LONG_PIC_PCREL_CALL || local_call)) { /* GAS currently can't generate the relocations that are needed for the SOM linker under HP-UX using this @@ -7474,9 +7529,7 @@ output_call (rtx insn, rtx call_dest, int sibcall) essentially an inline implementation of $$dyncall. We don't actually try to call $$dyncall as this is as difficult as calling the function itself. */ - struct deferred_plabel *p = get_plabel (XSTR (call_dest, 0)); - - xoperands[0] = p->internal_label; + xoperands[0] = get_deferred_plabel (call_dest); xoperands[1] = gen_label_rtx (); /* Since the call is indirect, FP arguments in registers @@ -7571,8 +7624,7 @@ output_call (rtx insn, rtx call_dest, int sibcall) return ""; /* A sibcall should never have a branch in the delay slot. */ - if (sibcall) - abort (); + gcc_assert (!sibcall); /* This call has an unconditional jump in its delay slot. */ xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1); @@ -7589,8 +7641,8 @@ output_call (rtx insn, rtx call_dest, int sibcall) { xoperands[1] = gen_label_rtx (); output_asm_insn ("ldo %0-%1(%%r2),%%r2", xoperands); - (*targetm.asm_out.internal_label) (asm_out_file, "L", - CODE_LABEL_NUMBER (xoperands[1])); + targetm.asm_out.internal_label (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[1])); } else output_asm_insn ("nop\n\tb,n %0", xoperands); @@ -7599,9 +7651,7 @@ output_call (rtx insn, rtx call_dest, int sibcall) output_asm_insn ("b,n %0", xoperands); /* Delete the jump. */ - PUT_CODE (NEXT_INSN (insn), NOTE); - NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + SET_INSN_DELETED (NEXT_INSN (insn)); return ""; } @@ -7630,7 +7680,8 @@ attr_length_indirect_call (rtx insn) if (TARGET_FAST_INDIRECT_CALLS || (!TARGET_PORTABLE_RUNTIME - && ((TARGET_PA_20 && distance < 7600000) || distance < 240000))) + && ((TARGET_PA_20 && !TARGET_SOM && distance < 7600000) + || distance < 240000))) return 8; if (flag_pic) @@ -7667,10 +7718,10 @@ output_indirect_call (rtx insn, rtx call_dest) the remaining cases. */ if (attr_length_indirect_call (insn) == 8) { - /* The HP linker substitutes a BLE for millicode calls using - the short PIC PCREL form. Thus, we must use %r31 as the - link register when generating PA 1.x code. */ - if (TARGET_PA_20) + /* The HP linker sometimes substitutes a BLE for BL/B,L calls to + $$dyncall. Since BLE uses %r31 as the link register, the 22-bit + variant of the B,L instruction can't be used on the SOM target. */ + if (TARGET_PA_20 && !TARGET_SOM) return ".CALL\tARGW0=GR\n\tb,l $$dyncall,%%r2\n\tcopy %%r2,%%r31"; else return ".CALL\tARGW0=GR\n\tbl $$dyncall,%%r31\n\tcopy %%r31,%%r2"; @@ -7692,8 +7743,8 @@ output_indirect_call (rtx insn, rtx call_dest) { xoperands[0] = gen_label_rtx (); output_asm_insn ("addil L'$$dyncall-%0,%%r1", xoperands); - (*targetm.asm_out.internal_label) (asm_out_file, "L", - CODE_LABEL_NUMBER (xoperands[0])); + targetm.asm_out.internal_label (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[0])); output_asm_insn ("ldo R'$$dyncall-%0(%%r1),%%r1", xoperands); } else @@ -7744,6 +7795,8 @@ hppa_encode_label (rtx sym) static void pa_encode_section_info (tree decl, rtx rtl, int first) { + default_encode_section_info (decl, rtl, first); + if (first && TEXT_SPACE_P (decl)) { SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1; @@ -7790,18 +7843,18 @@ pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, tree function) { - const char *fname = XSTR (XEXP (DECL_RTL (function), 0), 0); - const char *tname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0); + static unsigned int current_thunk_number; int val_14 = VAL_14_BITS_P (delta); int nbytes = 0; - static unsigned int current_thunk_number; char label[16]; + rtx xoperands[4]; - ASM_OUTPUT_LABEL (file, tname); - fprintf (file, "\t.PROC\n\t.CALLINFO FRAME=0,NO_CALLS\n\t.ENTRY\n"); + xoperands[0] = XEXP (DECL_RTL (function), 0); + xoperands[1] = XEXP (DECL_RTL (thunk_fndecl), 0); + xoperands[2] = GEN_INT (delta); - fname = (*targetm.strip_name_encoding) (fname); - tname = (*targetm.strip_name_encoding) (tname); + ASM_OUTPUT_LABEL (file, XSTR (xoperands[1], 0)); + fprintf (file, "\t.PROC\n\t.CALLINFO FRAME=0,NO_CALLS\n\t.ENTRY\n"); /* Output the thunk. We know that the function is in the same translation unit (i.e., the same space) as the thunk, and that @@ -7833,18 +7886,19 @@ pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta, && last_address < 262132))) || (!targetm.have_named_sections && last_address < 262132)))) { + if (!val_14) + output_asm_insn ("addil L'%2,%%r26", xoperands); + + output_asm_insn ("b %0", xoperands); + if (val_14) { - fprintf (file, "\tb %s\n\tldo " HOST_WIDE_INT_PRINT_DEC - "(%%r26),%%r26\n", fname, delta); + output_asm_insn ("ldo %2(%%r26),%%r26", xoperands); nbytes += 8; } else { - fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC - ",%%r26\n", delta); - fprintf (file, "\tb %s\n\tldo R'" HOST_WIDE_INT_PRINT_DEC - "(%%r1),%%r26\n", fname, delta); + output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands); nbytes += 12; } } @@ -7853,53 +7907,54 @@ pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta, /* We only have one call-clobbered scratch register, so we can't make use of the delay slot if delta doesn't fit in 14 bits. */ if (!val_14) - fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC - ",%%r26\n\tldo R'" HOST_WIDE_INT_PRINT_DEC - "(%%r1),%%r26\n", delta, delta); + { + output_asm_insn ("addil L'%2,%%r26", xoperands); + output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands); + } - fprintf (file, "\tb,l .+8,%%r1\n"); + output_asm_insn ("b,l .+8,%%r1", xoperands); if (TARGET_GAS) { - fprintf (file, "\taddil L'%s-$PIC_pcrel$0+4,%%r1\n", fname); - fprintf (file, "\tldo R'%s-$PIC_pcrel$0+8(%%r1),%%r1\n", fname); + output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands); + output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1", xoperands); } else { - int off = val_14 ? 8 : 16; - fprintf (file, "\taddil L'%s-%s-%d,%%r1\n", fname, tname, off); - fprintf (file, "\tldo R'%s-%s-%d(%%r1),%%r1\n", fname, tname, off); + xoperands[3] = GEN_INT (val_14 ? 8 : 16); + output_asm_insn ("addil L'%0-%1-%3,%%r1", xoperands); } if (val_14) { - fprintf (file, "\tbv %%r0(%%r1)\n\tldo "); - fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + output_asm_insn ("bv %%r0(%%r1)", xoperands); + output_asm_insn ("ldo %2(%%r26),%%r26", xoperands); nbytes += 20; } else { - fprintf (file, "\tbv,n %%r0(%%r1)\n"); + output_asm_insn ("bv,n %%r0(%%r1)", xoperands); nbytes += 24; } } else if (TARGET_PORTABLE_RUNTIME) { - fprintf (file, "\tldil L'%s,%%r1\n", fname); - fprintf (file, "\tldo R'%s(%%r1),%%r22\n", fname); + output_asm_insn ("ldil L'%0,%%r1", xoperands); + output_asm_insn ("ldo R'%0(%%r1),%%r22", xoperands); + + if (!val_14) + output_asm_insn ("addil L'%2,%%r26", xoperands); + + output_asm_insn ("bv %%r0(%%r22)", xoperands); if (val_14) { - fprintf (file, "\tbv %%r0(%%r22)\n\tldo "); - fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + output_asm_insn ("ldo %2(%%r26),%%r26", xoperands); nbytes += 16; } else { - fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC - ",%%r26\n", delta); - fprintf (file, "\tbv %%r0(%%r22)\n\tldo "); - fprintf (file, "R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", delta); + output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands); nbytes += 20; } } @@ -7910,114 +7965,114 @@ pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta, call the function directly with an indirect sequence similar to that used by $$dyncall. This is possible because $$dyncall acts as the import stub in an indirect call. */ - const char *lab; - ASM_GENERATE_INTERNAL_LABEL (label, "LTHN", current_thunk_number); - lab = (*targetm.strip_name_encoding) (label); - - fprintf (file, "\taddil LT'%s,%%r19\n", lab); - fprintf (file, "\tldw RT'%s(%%r1),%%r22\n", lab); - fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); - fprintf (file, "\tbb,>=,n %%r22,30,.+16\n"); - fprintf (file, "\tdepi 0,31,2,%%r22\n"); - fprintf (file, "\tldw 4(%%sr0,%%r22),%%r19\n"); - fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); + xoperands[3] = gen_rtx_SYMBOL_REF (Pmode, label); + output_asm_insn ("addil LT'%3,%%r19", xoperands); + output_asm_insn ("ldw RT'%3(%%r1),%%r22", xoperands); + output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands); + output_asm_insn ("bb,>=,n %%r22,30,.+16", xoperands); + output_asm_insn ("depi 0,31,2,%%r22", xoperands); + output_asm_insn ("ldw 4(%%sr0,%%r22),%%r19", xoperands); + output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands); + if (!val_14) { - fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC - ",%%r26\n", delta); + output_asm_insn ("addil L'%2,%%r26", xoperands); nbytes += 4; } + if (TARGET_PA_20) { - fprintf (file, "\tbve (%%r22)\n\tldo "); + output_asm_insn ("bve (%%r22)", xoperands); + nbytes += 36; + } + else if (TARGET_NO_SPACE_REGS) + { + output_asm_insn ("be 0(%%sr4,%%r22)", xoperands); nbytes += 36; } else { - if (TARGET_NO_SPACE_REGS) - { - fprintf (file, "\tbe 0(%%sr4,%%r22)\n\tldo "); - nbytes += 36; - } - else - { - fprintf (file, "\tldsid (%%sr0,%%r22),%%r21\n"); - fprintf (file, "\tmtsp %%r21,%%sr0\n"); - fprintf (file, "\tbe 0(%%sr0,%%r22)\n\tldo "); - nbytes += 44; - } + output_asm_insn ("ldsid (%%sr0,%%r22),%%r21", xoperands); + output_asm_insn ("mtsp %%r21,%%sr0", xoperands); + output_asm_insn ("be 0(%%sr0,%%r22)", xoperands); + nbytes += 44; } if (val_14) - fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + output_asm_insn ("ldo %2(%%r26),%%r26", xoperands); else - fprintf (file, "R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", delta); + output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands); } else if (flag_pic) { - if (TARGET_PA_20) - fprintf (file, "\tb,l .+8,%%r1\n"); - else - fprintf (file, "\tbl .+8,%%r1\n"); + output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands); if (TARGET_SOM || !TARGET_GAS) { - fprintf (file, "\taddil L'%s-%s-8,%%r1\n", fname, tname); - fprintf (file, "\tldo R'%s-%s-8(%%r1),%%r22\n", fname, tname); + output_asm_insn ("addil L'%0-%1-8,%%r1", xoperands); + output_asm_insn ("ldo R'%0-%1-8(%%r1),%%r22", xoperands); } else { - fprintf (file, "\taddil L'%s-$PIC_pcrel$0+4,%%r1\n", fname); - fprintf (file, "\tldo R'%s-$PIC_pcrel$0+8(%%r1),%%r22\n", fname); + output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands); + output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r22", xoperands); } + if (!val_14) + output_asm_insn ("addil L'%2,%%r26", xoperands); + + output_asm_insn ("bv %%r0(%%r22)", xoperands); + if (val_14) { - fprintf (file, "\tbv %%r0(%%r22)\n\tldo "); - fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + output_asm_insn ("ldo %2(%%r26),%%r26", xoperands); nbytes += 20; } else { - fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC - ",%%r26\n", delta); - fprintf (file, "\tbv %%r0(%%r22)\n\tldo "); - fprintf (file, "R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", delta); + output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands); nbytes += 24; } } else { if (!val_14) - fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC ",%%r26\n", delta); + output_asm_insn ("addil L'%2,%%r26", xoperands); - fprintf (file, "\tldil L'%s,%%r22\n", fname); - fprintf (file, "\tbe R'%s(%%sr4,%%r22)\n\tldo ", fname); + output_asm_insn ("ldil L'%0,%%r22", xoperands); + output_asm_insn ("be R'%0(%%sr4,%%r22)", xoperands); if (val_14) { - fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + output_asm_insn ("ldo %2(%%r26),%%r26", xoperands); nbytes += 12; } else { - fprintf (file, "R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", delta); + output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands); nbytes += 16; } } fprintf (file, "\t.EXIT\n\t.PROCEND\n"); + if (TARGET_SOM && TARGET_GAS) + { + /* We done with this subspace except possibly for some additional + debug information. Forget that we are in this subspace to ensure + that the next function is output in its own subspace. */ + in_section = NULL; + cfun->machine->in_nsubspa = 2; + } + if (TARGET_SOM && flag_pic && TREE_PUBLIC (function)) { - data_section (); - fprintf (file, "\t.align 4\n"); + switch_to_section (data_section); + output_asm_insn (".align 4", xoperands); ASM_OUTPUT_LABEL (file, label); - fprintf (file, "\t.word P'%s\n", fname); + output_asm_insn (".word P'%0", xoperands); } - else if (TARGET_SOM && TARGET_GAS) - forget_section (); current_thunk_number++; nbytes = ((nbytes + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1) @@ -8052,6 +8107,9 @@ pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta, static bool pa_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) { + if (TARGET_PORTABLE_RUNTIME) + return false; + /* Sibcalls are ok for TARGET_ELF32 as along as the linker is used in single subspace mode and the call is not indirect. As far as I know, there is no operating system support for the multiple subspace mode. @@ -8069,9 +8127,20 @@ pa_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) if (TARGET_64BIT) return false; - return (decl - && !TARGET_PORTABLE_RUNTIME - && !TREE_PUBLIC (decl)); + /* Sibcalls are only ok within a translation unit. */ + return (decl && !TREE_PUBLIC (decl)); +} + +/* ??? Addition is not commutative on the PA due to the weird implicit + space register selection rules for memory addresses. Therefore, we + don't consider a + b == b + a, as this might be inside a MEM. */ +static bool +pa_commutative_p (const_rtx x, int outer_code) +{ + return (COMMUTATIVE_P (x) + && (TARGET_NO_SPACE_REGS + || (outer_code != UNKNOWN && outer_code != MEM) + || GET_CODE (x) != PLUS)); } /* Returns 1 if the 6 operands specified in OPERANDS are suitable for @@ -8179,7 +8248,7 @@ pa_asm_output_aligned_bss (FILE *stream, unsigned HOST_WIDE_INT size, unsigned int align) { - bss_section (); + switch_to_section (bss_section); fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT); #ifdef ASM_OUTPUT_TYPE_DIRECTIVE @@ -8208,7 +8277,18 @@ pa_asm_output_aligned_common (FILE *stream, unsigned HOST_WIDE_INT size, unsigned int align) { - bss_section (); + unsigned int max_common_align; + + max_common_align = TARGET_64BIT ? 128 : (size >= 4096 ? 256 : 64); + if (align > max_common_align) + { + warning (0, "alignment (%u) for %s exceeds maximum alignment " + "for global common data. Using %u", + align / BITS_PER_UNIT, name, max_common_align / BITS_PER_UNIT); + align = max_common_align; + } + + switch_to_section (bss_section); assemble_name (stream, name); fprintf (stream, "\t.comm "HOST_WIDE_INT_PRINT_UNSIGNED"\n", @@ -8228,7 +8308,7 @@ pa_asm_output_aligned_local (FILE *stream, unsigned HOST_WIDE_INT size, unsigned int align) { - bss_section (); + switch_to_section (bss_section); fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT); #ifdef LOCAL_ASM_OP @@ -8297,16 +8377,9 @@ fmpysuboperands (rtx *operands) return 1; } -int -plus_xor_ior_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == PLUS || GET_CODE (op) == XOR - || GET_CODE (op) == IOR); -} - /* Return 1 if the given constant is 2, 4, or 8. These are the valid constants for shadd instructions. */ -static int +int shadd_constant_p (int val) { if (val == 2 || val == 4 || val == 8) @@ -8315,14 +8388,6 @@ shadd_constant_p (int val) return 0; } -/* Return 1 if OP is a CONST_INT with the value 2, 4, or 8. These are - the valid constant for shadd instructions. */ -int -shadd_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == CONST_INT && shadd_constant_p (INTVAL (op))); -} - /* Return 1 if OP is valid as a base or index register in a REG+REG address. */ @@ -8385,14 +8450,6 @@ eq_neq_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) return (GET_CODE (op) == EQ || GET_CODE (op) == NE); } -/* Return 1 if OP is an operator suitable for use in a movb instruction. */ -int -movb_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == EQ || GET_CODE (op) == NE - || GET_CODE (op) == LT || GET_CODE (op) == GE); -} - /* Return 1 if INSN is in the delay slot of a call instruction. */ int jump_in_call_delay (rtx insn) @@ -8418,37 +8475,50 @@ jump_in_call_delay (rtx insn) /* Output an unconditional move and branch insn. */ const char * -output_parallel_movb (rtx *operands, int length) +output_parallel_movb (rtx *operands, rtx insn) { + int length = get_attr_length (insn); + /* These are the cases in which we win. */ if (length == 4) return "mov%I1b,tr %1,%0,%2"; - /* None of these cases wins, but they don't lose either. */ - if (dbr_sequence_length () == 0) + /* None of the following cases win, but they don't lose either. */ + if (length == 8) { - /* Nothing in the delay slot, fake it by putting the combined - insn (the copy or add) in the delay slot of a bl. */ - if (GET_CODE (operands[1]) == CONST_INT) - return "b %2\n\tldi %1,%0"; + if (dbr_sequence_length () == 0) + { + /* Nothing in the delay slot, fake it by putting the combined + insn (the copy or add) in the delay slot of a bl. */ + if (GET_CODE (operands[1]) == CONST_INT) + return "b %2\n\tldi %1,%0"; + else + return "b %2\n\tcopy %1,%0"; + } else - return "b %2\n\tcopy %1,%0"; + { + /* Something in the delay slot, but we've got a long branch. */ + if (GET_CODE (operands[1]) == CONST_INT) + return "ldi %1,%0\n\tb %2"; + else + return "copy %1,%0\n\tb %2"; + } } + + if (GET_CODE (operands[1]) == CONST_INT) + output_asm_insn ("ldi %1,%0", operands); else - { - /* Something in the delay slot, but we've got a long branch. */ - if (GET_CODE (operands[1]) == CONST_INT) - return "ldi %1,%0\n\tb %2"; - else - return "copy %1,%0\n\tb %2"; - } + output_asm_insn ("copy %1,%0", operands); + return output_lbranch (operands[2], insn, 1); } /* Output an unconditional add and branch insn. */ const char * -output_parallel_addb (rtx *operands, int length) +output_parallel_addb (rtx *operands, rtx insn) { + int length = get_attr_length (insn); + /* To make life easy we want operand0 to be the shared input/output operand and operand1 to be the readonly operand. */ if (operands[0] == operands[1]) @@ -8458,18 +8528,20 @@ output_parallel_addb (rtx *operands, int length) if (length == 4) return "add%I1b,tr %1,%0,%3"; - /* None of these cases win, but they don't lose either. */ - if (dbr_sequence_length () == 0) - { - /* Nothing in the delay slot, fake it by putting the combined - insn (the copy or add) in the delay slot of a bl. */ - return "b %3\n\tadd%I1 %1,%0,%0"; - } - else + /* None of the following cases win, but they don't lose either. */ + if (length == 8) { - /* Something in the delay slot, but we've got a long branch. */ - return "add%I1 %1,%0,%0\n\tb %3"; + if (dbr_sequence_length () == 0) + /* Nothing in the delay slot, fake it by putting the combined + insn (the copy or add) in the delay slot of a bl. */ + return "b %3\n\tadd%I1 %1,%0,%0"; + else + /* Something in the delay slot, but we've got a long branch. */ + return "add%I1 %1,%0,%0\n\tb %3"; } + + output_asm_insn ("add%I1 %1,%0,%0", operands); + return output_lbranch (operands[3], insn, 1); } /* Return nonzero if INSN (a jump insn) immediately follows a call @@ -8517,7 +8589,7 @@ following_call (rtx insn) will adhere to those rules. So, late in the compilation process we find all the jump tables, and - expand them into real code -- eg each entry in the jump table vector + expand them into real code -- e.g. each entry in the jump table vector will get an appropriate label followed by a jump to the final target. Reorg and the final jump pass can then optimize these branches and @@ -8830,9 +8902,7 @@ pa_combine_instructions (void) PATTERN (floater))), anchor); - PUT_CODE (anchor, NOTE); - NOTE_LINE_NUMBER (anchor) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (anchor) = 0; + SET_INSN_DELETED (anchor); /* Emit a special USE insn for FLOATER, then delete the floating insn. */ @@ -8854,9 +8924,7 @@ pa_combine_instructions (void) anchor); JUMP_LABEL (temp) = JUMP_LABEL (anchor); - PUT_CODE (anchor, NOTE); - NOTE_LINE_NUMBER (anchor) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (anchor) = 0; + SET_INSN_DELETED (anchor); /* Emit a special USE insn for FLOATER, then delete the floating insn. */ @@ -8964,38 +9032,57 @@ insn_refs_are_delayed (rtx insn) to match the HP Compiler ABI. */ rtx -function_value (tree valtype, tree func ATTRIBUTE_UNUSED) +function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED) { enum machine_mode valmode; - /* Aggregates with a size less than or equal to 128 bits are returned - in GR 28(-29). They are left justified. The pad bits are undefined. - Larger aggregates are returned in memory. */ - if (TARGET_64BIT && AGGREGATE_TYPE_P (valtype)) + if (AGGREGATE_TYPE_P (valtype) + || TREE_CODE (valtype) == COMPLEX_TYPE + || TREE_CODE (valtype) == VECTOR_TYPE) { - rtx loc[2]; - int i, offset = 0; - int ub = int_size_in_bytes (valtype) <= UNITS_PER_WORD ? 1 : 2; + if (TARGET_64BIT) + { + /* Aggregates with a size less than or equal to 128 bits are + returned in GR 28(-29). They are left justified. The pad + bits are undefined. Larger aggregates are returned in + memory. */ + rtx loc[2]; + int i, offset = 0; + int ub = int_size_in_bytes (valtype) <= UNITS_PER_WORD ? 1 : 2; + + for (i = 0; i < ub; i++) + { + loc[i] = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (DImode, 28 + i), + GEN_INT (offset)); + offset += 8; + } - for (i = 0; i < ub; i++) + return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (ub, loc)); + } + else if (int_size_in_bytes (valtype) > UNITS_PER_WORD) { - loc[i] = gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (DImode, 28 + i), - GEN_INT (offset)); - offset += 8; + /* Aggregates 5 to 8 bytes in size are returned in general + registers r28-r29 in the same manner as other non + floating-point objects. The data is right-justified and + zero-extended to 64 bits. This is opposite to the normal + justification used on big endian targets and requires + special treatment. */ + rtx loc = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (DImode, 28), const0_rtx); + return gen_rtx_PARALLEL (BLKmode, gen_rtvec (1, loc)); } - - return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (ub, loc)); } if ((INTEGRAL_TYPE_P (valtype) - && TYPE_PRECISION (valtype) < BITS_PER_WORD) + && GET_MODE_BITSIZE (TYPE_MODE (valtype)) < BITS_PER_WORD) || POINTER_TYPE_P (valtype)) valmode = word_mode; else valmode = TYPE_MODE (valtype); if (TREE_CODE (valtype) == REAL_TYPE + && !AGGREGATE_TYPE_P (valtype) && TYPE_MODE (valtype) != TFmode && !TARGET_SOFT_FLOAT) return gen_rtx_REG (valmode, 32); @@ -9028,10 +9115,10 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, arg_size = FUNCTION_ARG_SIZE (mode, type); /* If this arg would be passed partially or totally on the stack, then - this routine should return zero. FUNCTION_ARG_PARTIAL_NREGS will + this routine should return zero. pa_arg_partial_bytes will handle arguments which are split between regs and stack slots if the ABI mandates split arguments. */ - if (! TARGET_64BIT) + if (!TARGET_64BIT) { /* The 32-bit ABI does not split arguments. */ if (cum->words + arg_size > max_arg_words) @@ -9066,7 +9153,9 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, treatment. */ if (arg_size > 1 || mode == BLKmode - || (type && AGGREGATE_TYPE_P (type))) + || (type && (AGGREGATE_TYPE_P (type) + || TREE_CODE (type) == COMPLEX_TYPE + || TREE_CODE (type) == VECTOR_TYPE))) { /* Double-extended precision (80-bit), quad-precision (128-bit) and aggregates including complex numbers are aligned on @@ -9120,13 +9209,18 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, objects. The data is right-justified and zero-extended to 64 bits. This is opposite to the normal justification used on big endian targets and requires special treatment. - We now define BLOCK_REG_PADDING to pad these objects. */ - if (mode == BLKmode) + We now define BLOCK_REG_PADDING to pad these objects. + Aggregates, complex and vector types are passed in the same + manner as structures. */ + if (mode == BLKmode + || (type && (AGGREGATE_TYPE_P (type) + || TREE_CODE (type) == COMPLEX_TYPE + || TREE_CODE (type) == VECTOR_TYPE))) { rtx loc = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (DImode, gpr_reg_base), const0_rtx); - return gen_rtx_PARALLEL (mode, gen_rtvec (1, loc)); + return gen_rtx_PARALLEL (BLKmode, gen_rtvec (1, loc)); } } else @@ -9144,9 +9238,9 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, /* If we are doing soft-float with portable runtime, then there is no need to worry about FP regs. */ && !TARGET_SOFT_FLOAT - /* The parameter must be some kind of float, else we can just + /* The parameter must be some kind of scalar float, else we just pass it in integer registers. */ - && FLOAT_MODE_P (mode) + && GET_MODE_CLASS (mode) == MODE_FLOAT /* The target function must not have a prototype. */ && cum->nargs_prototype <= 0 /* libcalls do not need to pass items in both FP and general @@ -9162,7 +9256,7 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, && !TARGET_GAS && !cum->incoming && cum->indirect - && FLOAT_MODE_P (mode))) + && GET_MODE_CLASS (mode) == MODE_FLOAT)) { retval = gen_rtx_PARALLEL @@ -9185,9 +9279,11 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, && !TARGET_64BIT && !TARGET_ELF32 && cum->indirect) - /* If the parameter is not a floating point parameter, then - it belongs in GPRs. */ - || !FLOAT_MODE_P (mode)) + /* If the parameter is not a scalar floating-point parameter, + then it belongs in GPRs. */ + || GET_MODE_CLASS (mode) != MODE_FLOAT + /* Structure with single SFmode field belongs in GPR. */ + || (type && AGGREGATE_TYPE_P (type))) retval = gen_rtx_REG (mode, gpr_reg_base); else retval = gen_rtx_REG (mode, fpr_reg_base); @@ -9197,15 +9293,18 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, /* If this arg would be passed totally in registers or totally on the stack, - then this routine should return zero. It is currently called only for - the 64-bit target. */ -int -function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, - tree type, int named ATTRIBUTE_UNUSED) + then this routine should return zero. */ + +static int +pa_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, bool named ATTRIBUTE_UNUSED) { unsigned int max_arg_words = 8; unsigned int offset = 0; + if (!TARGET_64BIT) + return 0; + if (FUNCTION_ARG_SIZE (mode, type) > 1 && (cum->words & 1)) offset = 1; @@ -9217,50 +9316,23 @@ function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, return 0; else /* Arg is split. */ - return max_arg_words - cum->words - offset; -} - - -/* Return 1 if this is a comparison operator. This allows the use of - MATCH_OPERATOR to recognize all the branch insns. */ - -int -cmpib_comparison_operator (rtx op, enum machine_mode mode) -{ - return ((mode == VOIDmode || GET_MODE (op) == mode) - && (GET_CODE (op) == EQ - || GET_CODE (op) == NE - || GET_CODE (op) == GT - || GET_CODE (op) == GTU - || GET_CODE (op) == GE - || GET_CODE (op) == LT - || GET_CODE (op) == LE - || GET_CODE (op) == LEU)); + return (max_arg_words - cum->words - offset) * UNITS_PER_WORD; } -#ifndef ONE_ONLY_TEXT_SECTION_ASM_OP -#define ONE_ONLY_TEXT_SECTION_ASM_OP "" -#endif - -#ifndef NEW_TEXT_SECTION_ASM_OP -#define NEW_TEXT_SECTION_ASM_OP "" -#endif - -#ifndef DEFAULT_TEXT_SECTION_ASM_OP -#define DEFAULT_TEXT_SECTION_ASM_OP "" -#endif -/* Select and return a TEXT_SECTION_ASM_OP for the current function. +/* A get_unnamed_section callback for switching to the text section. This function is only used with SOM. Because we don't support named subspaces, we can only create a new subspace or switch back - into the default text subspace. */ -const char * -som_text_section_asm_op (void) + to the default text subspace. */ + +static void +som_output_text_section_asm_op (const void *data ATTRIBUTE_UNUSED) { - if (TARGET_SOM && TARGET_GAS) + gcc_assert (TARGET_SOM); + if (TARGET_GAS) { - if (cfun && !cfun->machine->in_nsubspa) + if (cfun && cfun->machine && !cfun->machine->in_nsubspa) { /* We only want to emit a .nsubspa directive once at the start of the function. */ @@ -9271,23 +9343,93 @@ som_text_section_asm_op (void) if (cfun->decl && DECL_ONE_ONLY (cfun->decl) && !DECL_WEAK (cfun->decl)) - return ONE_ONLY_TEXT_SECTION_ASM_OP; - - return NEW_TEXT_SECTION_ASM_OP; + { + output_section_asm_op ("\t.SPACE $TEXT$\n" + "\t.NSUBSPA $CODE$,QUAD=0,ALIGN=8," + "ACCESS=44,SORT=24,COMDAT"); + return; + } } else { /* There isn't a current function or the body of the current function has been completed. So, we are changing to the - text section to output debugging information. Do this in - the default text section. We need to forget that we are - in the text section so that text_section will call us the - next time around. */ - forget_section (); - } + text section to output debugging information. Thus, we + need to forget that we are in the text section so that + varasm.c will call us when text_section is selected again. */ + gcc_assert (!cfun || !cfun->machine + || cfun->machine->in_nsubspa == 2); + in_section = NULL; + } + output_section_asm_op ("\t.SPACE $TEXT$\n\t.NSUBSPA $CODE$"); + return; } + output_section_asm_op ("\t.SPACE $TEXT$\n\t.SUBSPA $CODE$"); +} + +/* A get_unnamed_section callback for switching to comdat data + sections. This function is only used with SOM. */ - return DEFAULT_TEXT_SECTION_ASM_OP; +static void +som_output_comdat_data_section_asm_op (const void *data) +{ + in_section = NULL; + output_section_asm_op (data); +} + +/* Implement TARGET_ASM_INITIALIZE_SECTIONS */ + +static void +pa_som_asm_init_sections (void) +{ + text_section + = get_unnamed_section (0, som_output_text_section_asm_op, NULL); + + /* SOM puts readonly data in the default $LIT$ subspace when PIC code + is not being generated. */ + som_readonly_data_section + = get_unnamed_section (0, output_section_asm_op, + "\t.SPACE $TEXT$\n\t.SUBSPA $LIT$"); + + /* When secondary definitions are not supported, SOM makes readonly + data one-only by creating a new $LIT$ subspace in $TEXT$ with + the comdat flag. */ + som_one_only_readonly_data_section + = get_unnamed_section (0, som_output_comdat_data_section_asm_op, + "\t.SPACE $TEXT$\n" + "\t.NSUBSPA $LIT$,QUAD=0,ALIGN=8," + "ACCESS=0x2c,SORT=16,COMDAT"); + + + /* When secondary definitions are not supported, SOM makes data one-only + by creating a new $DATA$ subspace in $PRIVATE$ with the comdat flag. */ + som_one_only_data_section + = get_unnamed_section (SECTION_WRITE, + som_output_comdat_data_section_asm_op, + "\t.SPACE $PRIVATE$\n" + "\t.NSUBSPA $DATA$,QUAD=1,ALIGN=8," + "ACCESS=31,SORT=24,COMDAT"); + + /* FIXME: HPUX ld generates incorrect GOT entries for "T" fixups + which reference data within the $TEXT$ space (for example constant + strings in the $LIT$ subspace). + + The assemblers (GAS and HP as) both have problems with handling + the difference of two symbols which is the other correct way to + reference constant data during PIC code generation. + + So, there's no way to reference constant data which is in the + $TEXT$ space during PIC generation. Instead place all constant + data into the $PRIVATE$ subspace (this reduces sharing, but it + works correctly). */ + readonly_data_section = flag_pic ? data_section : som_readonly_data_section; + + /* We must not have a reference to an external symbol defined in a + shared library in a readonly section, else the SOM linker will + complain. + + So, we force exception information into the data section. */ + exception_section = data_section; } /* On hpux10, the linker will give an error if we have a reference @@ -9295,7 +9437,7 @@ som_text_section_asm_op (void) library. Therefore, expressions that might require a reloc can not be placed in the read-only data section. */ -static void +static section * pa_select_section (tree exp, int reloc, unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED) { @@ -9310,21 +9452,19 @@ pa_select_section (tree exp, int reloc, if (TARGET_SOM && DECL_ONE_ONLY (exp) && !DECL_WEAK (exp)) - one_only_readonly_data_section (); + return som_one_only_readonly_data_section; else - readonly_data_section (); + return readonly_data_section; } - else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c' - && !reloc) - readonly_data_section (); + else if (CONSTANT_CLASS_P (exp) && !reloc) + return readonly_data_section; else if (TARGET_SOM && TREE_CODE (exp) == VAR_DECL && DECL_ONE_ONLY (exp) - && !DECL_WEAK (exp) - && DECL_INITIAL (exp)) - one_only_data_section (); + && !DECL_WEAK (exp)) + return som_one_only_data_section; else - data_section (); + return data_section; } static void @@ -9352,7 +9492,7 @@ pa_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED, /* Worker function for TARGET_RETURN_IN_MEMORY. */ bool -pa_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED) +pa_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) { /* SOM ABI says that objects larger than 64 bits are returned in memory. PA64 ABI says that objects larger than 128 bits are returned in memory. @@ -9366,4 +9506,65 @@ pa_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED) || int_size_in_bytes (type) <= 0); } +/* Structure to hold declaration and name of external symbols that are + emitted by GCC. We generate a vector of these symbols and output them + at the end of the file if and only if SYMBOL_REF_REFERENCED_P is true. + This avoids putting out names that are never really used. */ + +typedef struct extern_symbol GTY(()) +{ + tree decl; + const char *name; +} extern_symbol; + +/* Define gc'd vector type for extern_symbol. */ +DEF_VEC_O(extern_symbol); +DEF_VEC_ALLOC_O(extern_symbol,gc); + +/* Vector of extern_symbol pointers. */ +static GTY(()) VEC(extern_symbol,gc) *extern_symbols; + +#ifdef ASM_OUTPUT_EXTERNAL_REAL +/* Mark DECL (name NAME) as an external reference (assembler output + file FILE). This saves the names to output at the end of the file + if actually referenced. */ + +void +pa_hpux_asm_output_external (FILE *file, tree decl, const char *name) +{ + extern_symbol * p = VEC_safe_push (extern_symbol, gc, extern_symbols, NULL); + + gcc_assert (file == asm_out_file); + p->decl = decl; + p->name = name; +} + +/* Output text required at the end of an assembler file. + This includes deferred plabels and .import directives for + all external symbols that were actually referenced. */ + +static void +pa_hpux_file_end (void) +{ + unsigned int i; + extern_symbol *p; + + if (!NO_DEFERRED_PROFILE_COUNTERS) + output_deferred_profile_counters (); + + output_deferred_plabels (); + + for (i = 0; VEC_iterate (extern_symbol, extern_symbols, i, p); i++) + { + tree decl = p->decl; + + if (!TREE_ASM_WRITTEN (decl) + && SYMBOL_REF_REFERENCED_P (XEXP (DECL_RTL (decl), 0))) + ASM_OUTPUT_EXTERNAL_REAL (asm_out_file, decl, p->name); + } + + VEC_free (extern_symbol, gc, extern_symbols); +} +#endif + #include "gt-pa.h"