/* Subroutines for insn-output.c for HPPA.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003 Free Software Foundation, Inc.
+ 2002, 2003, 2004 Free Software Foundation, Inc.
Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC is free software; you can redistribute it and/or modify
+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)
any later version.
-GNU CC is distributed in the hope that it will be useful,
+GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
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 GNU CC; see the file COPYING. If not, write to
+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. */
#include "except.h"
#include "expr.h"
#include "optabs.h"
-#include "libfuncs.h"
#include "reload.h"
#include "integrate.h"
#include "function.h"
-#include "obstack.h"
#include "toplev.h"
#include "ggc.h"
#include "recog.h"
#include "target.h"
#include "target-def.h"
-static int hppa_use_dfa_pipeline_interface PARAMS ((void));
-
-#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
-#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE hppa_use_dfa_pipeline_interface
-
-static int
-hppa_use_dfa_pipeline_interface ()
-{
- return 1;
-}
-
/* Return nonzero if there is a bypass for the output of
OUT_INSN and the fp store IN_INSN. */
int
-hppa_fpstore_bypass_p (out_insn, in_insn)
- rtx out_insn, in_insn;
+hppa_fpstore_bypass_p (rtx out_insn, rtx in_insn)
{
enum machine_mode store_mode;
enum machine_mode other_mode;
#endif
#endif
-static int hppa_address_cost PARAMS ((rtx));
-static bool hppa_rtx_costs PARAMS ((rtx, int, int, int *));
-static inline rtx force_mode PARAMS ((enum machine_mode, rtx));
-static void pa_reorg PARAMS ((void));
-static void pa_combine_instructions PARAMS ((void));
-static int pa_can_combine_p PARAMS ((rtx, rtx, rtx, int, rtx, rtx, rtx));
-static int forward_branch_p PARAMS ((rtx));
-static int shadd_constant_p PARAMS ((int));
-static void compute_zdepwi_operands PARAMS ((unsigned HOST_WIDE_INT, unsigned *));
-static int compute_movstrsi_length PARAMS ((rtx));
-static bool pa_assemble_integer PARAMS ((rtx, unsigned int, int));
-static void remove_useless_addtr_insns PARAMS ((int));
-static void store_reg PARAMS ((int, int, int));
-static void store_reg_modify PARAMS ((int, int, int));
-static void load_reg PARAMS ((int, int, int));
-static void set_reg_plus_d PARAMS ((int, int, int, int));
-static void pa_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
-static void update_total_code_bytes PARAMS ((int));
-static void pa_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
-static int pa_adjust_cost PARAMS ((rtx, rtx, rtx, int));
-static int pa_adjust_priority PARAMS ((rtx, int));
-static int pa_issue_rate PARAMS ((void));
-static void pa_select_section PARAMS ((tree, int, unsigned HOST_WIDE_INT))
+static void copy_reg_pointer (rtx, rtx);
+static void fix_range (const char *);
+static int hppa_address_cost (rtx);
+static bool hppa_rtx_costs (rtx, int, int, int *);
+static inline rtx force_mode (enum machine_mode, rtx);
+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);
+static bool pa_assemble_integer (rtx, unsigned int, int);
+static void remove_useless_addtr_insns (int);
+static void store_reg (int, HOST_WIDE_INT, int);
+static void store_reg_modify (int, int, HOST_WIDE_INT);
+static void load_reg (int, HOST_WIDE_INT, int);
+static void set_reg_plus_d (int, int, HOST_WIDE_INT, int);
+static void pa_output_function_prologue (FILE *, HOST_WIDE_INT);
+static void update_total_code_bytes (int);
+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)
ATTRIBUTE_UNUSED;
-static void pa_encode_section_info PARAMS ((tree, rtx, int));
-static const char *pa_strip_name_encoding PARAMS ((const char *));
-static bool pa_function_ok_for_sibcall PARAMS ((tree, tree));
-static void pa_globalize_label PARAMS ((FILE *, const char *))
+static void pa_encode_section_info (tree, rtx, int);
+static const char *pa_strip_name_encoding (const char *);
+static bool pa_function_ok_for_sibcall (tree, tree);
+static void pa_globalize_label (FILE *, const char *)
ATTRIBUTE_UNUSED;
-static void pa_asm_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
- HOST_WIDE_INT, tree));
+static void pa_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
+ HOST_WIDE_INT, tree);
#if !defined(USE_COLLECT2)
-static void pa_asm_out_constructor PARAMS ((rtx, int));
-static void pa_asm_out_destructor PARAMS ((rtx, int));
+static void pa_asm_out_constructor (rtx, int);
+static void pa_asm_out_destructor (rtx, int);
#endif
-static void pa_init_builtins PARAMS ((void));
-static void copy_fp_args PARAMS ((rtx)) ATTRIBUTE_UNUSED;
-static int length_fp_args PARAMS ((rtx)) ATTRIBUTE_UNUSED;
-static struct deferred_plabel *get_plabel PARAMS ((const char *))
+static void pa_init_builtins (void);
+static rtx hppa_builtin_saveregs (void);
+static tree hppa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
+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 PARAMS ((void)) ATTRIBUTE_UNUSED;
-static inline void pa_file_start_space PARAMS ((int)) ATTRIBUTE_UNUSED;
-static inline void pa_file_start_file PARAMS ((int)) ATTRIBUTE_UNUSED;
-static inline void pa_file_start_mcount PARAMS ((const char*)) ATTRIBUTE_UNUSED;
-static void pa_elf_file_start PARAMS ((void)) ATTRIBUTE_UNUSED;
-static void pa_som_file_start PARAMS ((void)) ATTRIBUTE_UNUSED;
-static void pa_linux_file_start PARAMS ((void)) ATTRIBUTE_UNUSED;
-static void pa_hpux64_gas_file_start PARAMS ((void)) ATTRIBUTE_UNUSED;
-static void pa_hpux64_hpas_file_start PARAMS ((void)) ATTRIBUTE_UNUSED;
-static void output_deferred_plabels PARAMS ((void));
+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;
+static inline void pa_file_start_mcount (const char*) ATTRIBUTE_UNUSED;
+static void pa_elf_file_start (void) ATTRIBUTE_UNUSED;
+static void pa_som_file_start (void) ATTRIBUTE_UNUSED;
+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);
+#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);
+
/* 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 cpu we are scheduling for. */
-enum processor_type pa_cpu;
-
-/* String to hold which cpu we are scheduling for. */
-const char *pa_cpu_string;
-
/* 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;
+
+/* String to hold which cpu we are scheduling for. */
+const char *pa_cpu_string;
+
/* 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;
-static rtx find_addr_reg PARAMS ((rtx));
+static rtx find_addr_reg (rtx);
/* Keep track of the number of bytes we have output in the CODE subspace
during this compilation so we'll know when to emit inline long-calls. */
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG pa_reorg
+#ifdef HPUX_LONG_DOUBLE_LIBRARY
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS pa_hpux_init_libfuncs
+#endif
+
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX pa_struct_value_rtx
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY pa_return_in_memory
+#undef TARGET_MUST_PASS_IN_STACK
+#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_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
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
+/* Parse the -mfixed-range= option string. */
+
+static void
+fix_range (const char *const_str)
+{
+ int i, first, last;
+ char *str, *dash, *comma;
+
+ /* str must be of the form REG1'-'REG2{,REG1'-'REG} where REG1 and
+ 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. */
+
+ i = strlen (const_str);
+ str = (char *) alloca (i + 1);
+ memcpy (str, const_str, i + 1);
+
+ while (1)
+ {
+ dash = strchr (str, '-');
+ if (!dash)
+ {
+ warning ("value of -mfixed-range must have form REG1-REG2");
+ return;
+ }
+ *dash = '\0';
+
+ comma = strchr (dash + 1, ',');
+ if (comma)
+ *comma = '\0';
+
+ first = decode_reg_name (str);
+ if (first < 0)
+ {
+ warning ("unknown register name: %s", str);
+ return;
+ }
+
+ last = decode_reg_name (dash + 1);
+ if (last < 0)
+ {
+ warning ("unknown register name: %s", dash + 1);
+ return;
+ }
+
+ *dash = '-';
+
+ if (first > last)
+ {
+ warning ("%s-%s is an empty range", str, dash + 1);
+ return;
+ }
+
+ for (i = first; i <= last; ++i)
+ fixed_regs[i] = call_used_regs[i] = 1;
+
+ if (!comma)
+ break;
+
+ *comma = ',';
+ str = comma + 1;
+ }
+
+ /* Check if all floating point registers have been fixed. */
+ for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
+ if (!fixed_regs[i])
+ break;
+
+ if (i > FP_REG_LAST)
+ target_flags |= MASK_DISABLE_FPREGS;
+}
+
void
-override_options ()
+override_options (void)
{
if (pa_cpu_string == NULL)
pa_cpu_string = TARGET_SCHED_DEFAULT;
warning ("unknown -mschedule= option (%s).\nValid options are 700, 7100, 7100LC, 7200, 7300, and 8000\n", pa_cpu_string);
}
- /* Set the instruction set architecture. */
+ /* Set the instruction architecture. */
if (pa_arch_string && ! strcmp (pa_arch_string, "1.0"))
{
pa_arch_string = "1.0";
warning ("unknown -march= option (%s).\nValid options are 1.0, 1.1, and 2.0\n", pa_arch_string);
}
+ if (pa_fixed_range_string)
+ fix_range (pa_fixed_range_string);
+
/* 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. */
}
static void
-pa_init_builtins ()
+pa_init_builtins (void)
{
#ifdef DONT_HAVE_FPUTC_UNLOCKED
built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE;
#endif
}
+/* If FROM is a probable pointer register, mark TO as a probable
+ pointer register with the same pointer alignment as FROM. */
+
+static void
+copy_reg_pointer (rtx to, rtx from)
+{
+ if (REG_POINTER (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 (op, mode)
- rtx op;
- enum machine_mode mode;
+reg_or_0_operand (rtx op, enum machine_mode mode)
{
return (op == CONST0_RTX (mode) || register_operand (op, mode));
}
For 2.5 try to eliminate either call_operand_address or
function_label_operand, they perform very similar functions. */
int
-call_operand_address (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+call_operand_address (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_MODE (op) == word_mode
&& CONSTANT_P (op) && ! TARGET_PORTABLE_RUNTIME);
expressions will have one of a few well defined forms, so
we need only check those forms. */
int
-symbolic_expression_p (x)
- register rtx x;
+symbolic_expression_p (rtx x)
{
/* Strip off any HIGH. */
}
int
-symbolic_operand (op, mode)
- register rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
switch (GET_CODE (op))
{
operand of mode MODE. */
int
-symbolic_memory_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+symbolic_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
|| GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF);
}
-/* Return 1 if the operand is either a register or a memory operand that is
- not symbolic. */
-
-int
-reg_or_nonsymb_mem_operand (op, mode)
- register rtx op;
- enum machine_mode mode;
-{
- if (register_operand (op, mode))
- return 1;
-
- if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
- return 1;
-
- return 0;
-}
-
/* 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 (op, mode)
- register rtx op;
- enum machine_mode mode;
+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 (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
- return 1;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
- return 0;
+ 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
if reload didn't find a hard register for the operand. */
int
-reg_before_reload_operand (op, mode)
- register rtx op;
- enum machine_mode mode;
+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)
if (reload_completed
&& memory_operand (op, mode)
- && ! symbolic_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 (intval)
- HOST_WIDE_INT intval;
+cint_ok_for_move (HOST_WIDE_INT intval)
{
/* OK if ldo, ldil, or zdepi, can be used. */
return (CONST_OK_FOR_LETTER_P (intval, 'J')
|| CONST_OK_FOR_LETTER_P (intval, 'K'));
}
-/* Accept anything that can be moved in one instruction into a general
- register. */
+/* Return 1 iff OP is an indexed memory operand. */
int
-move_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+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));
+}
+
+/* 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_CODE (op) == CONSTANT_P_RTX)
+ 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;
- op = XEXP (op, 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;
- /* We consider a LO_SUM DLT reference a move_operand now since it has
- been merged into the normal movsi/movdi patterns. */
- if (GET_CODE (op) == LO_SUM
- && GET_CODE (XEXP (op, 0)) == REG
- && REG_OK_FOR_BASE_P (XEXP (op, 0))
- && GET_CODE (XEXP (op, 1)) == UNSPEC
- && GET_MODE (op) == Pmode)
- return 1;
+ return memory_address_p (mode, XEXP (op, 0));
+}
- /* Since move_operand is only used for source operands, we can always
- allow scaled indexing! */
- if (! TARGET_DISABLE_INDEXING
- && GET_CODE (op) == PLUS
- && ((GET_CODE (XEXP (op, 0)) == MULT
- && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
- && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT
- && INTVAL (XEXP (XEXP (op, 0), 1))
- == (HOST_WIDE_INT) GET_MODE_SIZE (mode)
- && GET_CODE (XEXP (op, 1)) == REG)
- || (GET_CODE (XEXP (op, 1)) == MULT
- &&GET_CODE (XEXP (XEXP (op, 1), 0)) == REG
- && GET_CODE (XEXP (XEXP (op, 1), 1)) == CONST_INT
- && INTVAL (XEXP (XEXP (op, 1), 1))
- == (HOST_WIDE_INT) GET_MODE_SIZE (mode)
- && GET_CODE (XEXP (op, 0)) == REG)))
- return 1;
+/* 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, op);
+ 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 (op, mode)
- rtx op;
- enum machine_mode mode;
+reg_or_cint_move_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));
-
- return 0;
+ return (GET_CODE (op) == CONST_INT && cint_ok_for_move (INTVAL (op)));
}
int
-pic_label_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+pic_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (!flag_pic)
return 0;
}
int
-fp_reg_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+fp_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return reg_renumber && FP_REG_P (op);
}
three operand arithmetic insn that accepts registers of mode MODE
or 14-bit signed integers. */
int
-arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+arith_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && INT_14_BITS (op)));
three operand arithmetic insn that accepts registers of mode MODE
or 11-bit signed integers. */
int
-arith11_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+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
-adddi3_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+adddi3_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT
/* A constant integer suitable for use in a PRE_MODIFY memory
reference. */
int
-pre_cint_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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 (op, mode)
- rtx op;
- enum machine_mode mode;
+arith_double_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_DOUBLE
is an integer register. */
int
-ireg_or_int5_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+ireg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == REG && REGNO (op) > 0 && REGNO (op) < 32);
}
range constraining immediate operands in three-address insns. */
int
-int5_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+int5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && INT_5_BITS (op));
}
int
-uint5_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+uint5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && INT_U5_BITS (op));
}
int
-int11_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+int11_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && INT_11_BITS (op));
}
int
-uint32_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+uint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
#if HOST_BITS_PER_WIDE_INT > 32
/* All allowed constants will fit a CONST_INT. */
}
int
-arith5_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+arith5_operand (rtx op, enum machine_mode mode)
{
return register_operand (op, mode) || int5_operand (op, mode);
}
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 (x)
- unsigned HOST_WIDE_INT x;
+zdepi_cint_p (unsigned HOST_WIDE_INT x)
{
unsigned HOST_WIDE_INT lsb_mask, t;
1....10....0
1..10..01..1 */
int
-and_mask_p (mask)
- unsigned HOST_WIDE_INT mask;
+and_mask_p (unsigned HOST_WIDE_INT mask)
{
mask = ~mask;
mask += mask & -mask;
/* True iff depi or extru can be used to compute (reg & OP). */
int
-and_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+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 (mask)
- unsigned HOST_WIDE_INT mask;
+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 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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 (op, mode)
- rtx op;
- enum machine_mode mode;
+lhs_lshift_operand (rtx op, enum machine_mode mode)
{
return register_operand (op, mode) || lhs_lshift_cint_operand (op, mode);
}
Such values can be the left hand side x in (x << r), using the zvdepi
instruction. */
int
-lhs_lshift_cint_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+lhs_lshift_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
unsigned HOST_WIDE_INT x;
if (GET_CODE (op) != CONST_INT)
}
int
-arith32_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+arith32_operand (rtx op, enum machine_mode mode)
{
return register_operand (op, mode) || GET_CODE (op) == CONST_INT;
}
int
-pc_or_label_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+pc_or_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF);
}
than one register, we lose. */
rtx
-legitimize_pic_address (orig, mode, reg)
- rtx orig, reg;
- enum machine_mode mode;
+legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
{
rtx pic_ref = orig;
So instead we just emit the raw set, which avoids the movXX
expanders completely. */
+ mark_reg_pointer (reg, BITS_PER_UNIT);
emit_insn (gen_rtx_SET (VOIDmode, reg, orig));
current_function_uses_pic_offset_table = 1;
return reg;
}
if (GET_CODE (orig) == SYMBOL_REF)
{
+ rtx insn, tmp_reg;
+
if (reg == 0)
abort ();
- emit_move_insn (reg,
+ /* Before reload, allocate a temporary register for the intermediate
+ result. This allows the sequence to be deleted when the final
+ result is unused and the insns are trivially dead. */
+ tmp_reg = ((reload_in_progress || reload_completed)
+ ? reg : gen_reg_rtx (Pmode));
+
+ emit_move_insn (tmp_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, reg,
+ gen_rtx_LO_SUM (Pmode, tmp_reg,
gen_rtx_UNSPEC (Pmode,
gen_rtvec (1, orig),
- 0)));
+ UNSPEC_DLTIND14R)));
current_function_uses_pic_offset_table = 1;
+ MEM_NOTRAP_P (pic_ref) = 1;
RTX_UNCHANGING_P (pic_ref) = 1;
- emit_move_insn (reg, pic_ref);
+ 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));
+
return reg;
}
else if (GET_CODE (orig) == CONST)
orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
base == reg ? 0 : reg);
}
- else abort ();
+ else
+ abort ();
+
if (GET_CODE (orig) == CONST_INT)
{
if (INT_14_BITS (orig))
pic_ref = gen_rtx_PLUS (Pmode, base, orig);
/* Likewise, should we set special REG_NOTEs here? */
}
+
return pic_ref;
}
a register. */
rtx
-hppa_legitimize_address (x, oldx, mode)
- rtx x, oldx ATTRIBUTE_UNUSED;
- enum machine_mode mode;
+hppa_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode)
{
rtx orig = x;
+ /* We need to canonicalize the order of operands in unscaled indexed
+ addresses since the code that checks if an address is valid doesn't
+ always try both orders. */
+ if (!TARGET_NO_SPACE_REGS
+ && GET_CODE (x) == PLUS
+ && GET_MODE (x) == Pmode
+ && REG_P (XEXP (x, 0))
+ && REG_P (XEXP (x, 1))
+ && REG_POINTER (XEXP (x, 0))
+ && !REG_POINTER (XEXP (x, 1)))
+ return gen_rtx_PLUS (Pmode, XEXP (x, 1), XEXP (x, 0));
+
if (flag_pic)
return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode));
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))
- && (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == 'o'
+ && (OBJECT_P (XEXP (x, 1))
|| GET_CODE (XEXP (x, 1)) == SUBREG)
&& GET_CODE (XEXP (x, 1)) != CONST)
{
as GO_IF_LEGITIMATE_ADDRESS. */
static int
-hppa_address_cost (X)
- rtx X;
+hppa_address_cost (rtx X)
{
switch (GET_CODE (X))
{
scanned. In either case, *TOTAL contains the cost result. */
static bool
-hppa_rtx_costs (x, code, outer_code, total)
- rtx x;
- int code, outer_code;
- int *total;
+hppa_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
/* Ensure mode of ORIG, a REG rtx, is MODE. Returns either ORIG or a
new rtx with the correct mode. */
static inline rtx
-force_mode (mode, orig)
- enum machine_mode mode;
- rtx orig;
+force_mode (enum machine_mode mode, rtx orig)
{
if (mode == GET_MODE (orig))
return orig;
normally.
Note SCRATCH_REG may not be in the proper mode depending on how it
- will be used. This routine is resposible for creating a new copy
+ will be used. This routine is responsible for creating a new copy
of SCRATCH_REG in the proper mode. */
int
-emit_move_sequence (operands, mode, scratch_reg)
- rtx *operands;
- enum machine_mode mode;
- rtx scratch_reg;
+emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
{
register rtx operand0 = operands[0];
register rtx operand1 = operands[1];
register rtx tem;
+ /* We can only handle indexed addresses in the destination operand
+ of floating point stores. Thus, we need to break out indexed
+ 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 ();
+
+ tem = copy_to_mode_reg (Pmode, XEXP (operand0, 0));
+ operand0 = replace_equiv_address (operand0, tem);
+ }
+
+ /* On targets with non-equivalent space registers, break out unscaled
+ indexed addresses from the source operand before the final CSE.
+ We have to do this because the REG_POINTER flag is not correctly
+ carried through various optimization passes and CSE may substitute
+ a pseudo without the pointer set for one with the pointer set. As
+ a result, we loose various opportunities to create insns with
+ unscaled indexed addresses. */
+ if (!TARGET_NO_SPACE_REGS
+ && !cse_not_expected
+ && GET_CODE (operand1) == MEM
+ && GET_CODE (XEXP (operand1, 0)) == PLUS
+ && REG_P (XEXP (XEXP (operand1, 0), 0))
+ && REG_P (XEXP (XEXP (operand1, 0), 1)))
+ operand1
+ = replace_equiv_address (operand1,
+ copy_to_mode_reg (Pmode, XEXP (operand1, 0)));
+
if (scratch_reg
&& reload_in_progress && GET_CODE (operand0) == REG
&& REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
&& ((tem = find_replacement (&XEXP (operand0, 0)))
!= XEXP (operand0, 0)))
operand0 = gen_rtx_MEM (GET_MODE (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);
/* Handle secondary reloads for loads/stores of FP registers from
- REG+D addresses where D does not fit in 5 bits, including
+ REG+D addresses where D does not fit in 5 or 14 bits, including
(subreg (mem (addr))) cases. */
- if (fp_reg_operand (operand0, mode)
+ if (scratch_reg
+ && fp_reg_operand (operand0, mode)
&& ((GET_CODE (operand1) == MEM
- && ! memory_address_p (DFmode, XEXP (operand1, 0)))
+ && !memory_address_p ((GET_MODE_SIZE (mode) == 4 ? SFmode : DFmode),
+ XEXP (operand1, 0)))
|| ((GET_CODE (operand1) == SUBREG
&& GET_CODE (XEXP (operand1, 0)) == MEM
- && !memory_address_p (DFmode, XEXP (XEXP (operand1, 0), 0)))))
- && scratch_reg)
+ && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+ ? SFmode : DFmode),
+ XEXP (XEXP (operand1, 0), 0))))))
{
if (GET_CODE (operand1) == SUBREG)
operand1 = XEXP (operand1, 0);
if (!memory_address_p (Pmode, XEXP (operand1, 0)))
{
emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1));
- emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)),
- Pmode,
- XEXP (XEXP (operand1, 0), 0),
- scratch_reg));
+ emit_move_insn (scratch_reg,
+ gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)),
+ Pmode,
+ XEXP (XEXP (operand1, 0), 0),
+ scratch_reg));
}
else
emit_move_insn (scratch_reg, XEXP (operand1, 0));
gen_rtx_MEM (mode, scratch_reg)));
return 1;
}
- else if (fp_reg_operand (operand1, mode)
+ else if (scratch_reg
+ && fp_reg_operand (operand1, mode)
&& ((GET_CODE (operand0) == MEM
- && ! memory_address_p (DFmode, XEXP (operand0, 0)))
+ && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+ ? SFmode : DFmode),
+ XEXP (operand0, 0)))
|| ((GET_CODE (operand0) == SUBREG)
&& GET_CODE (XEXP (operand0, 0)) == MEM
- && !memory_address_p (DFmode, XEXP (XEXP (operand0, 0), 0))))
- && scratch_reg)
+ && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+ ? SFmode : DFmode),
+ XEXP (XEXP (operand0, 0), 0)))))
{
if (GET_CODE (operand0) == SUBREG)
operand0 = XEXP (operand0, 0);
/* Handle secondary reloads for loads of FP registers from constant
expressions by forcing the constant into memory.
- use scratch_reg to hold the address of the memory location.
+ Use scratch_reg to hold the address of the memory location.
The proper fix is to change PREFERRED_RELOAD_CLASS to return
NO_REGS when presented with a const_int and a register class
containing only FP registers. Doing so unfortunately creates
more problems than it solves. Fix this for 2.5. */
- else if (fp_reg_operand (operand0, mode)
+ else if (scratch_reg
&& CONSTANT_P (operand1)
- && scratch_reg)
+ && fp_reg_operand (operand0, mode))
{
rtx xoperands[2];
}
/* Handle secondary reloads for SAR. These occur when trying to load
the SAR from memory, FP register, or with a constant. */
- else if (GET_CODE (operand0) == REG
+ else if (scratch_reg
+ && GET_CODE (operand0) == REG
&& REGNO (operand0) < FIRST_PSEUDO_REGISTER
&& REGNO_REG_CLASS (REGNO (operand0)) == SHIFT_REGS
&& (GET_CODE (operand1) == MEM
|| GET_CODE (operand1) == CONST_INT
|| (GET_CODE (operand1) == REG
- && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1)))))
- && scratch_reg)
+ && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1))))))
{
/* D might not fit in 14 bits either; for such cases load D into
scratch reg. */
emit_move_insn (operand0, scratch_reg);
return 1;
}
- /* Handle most common case: storing into a register. */
+ /* Handle the most common case: storing into a register. */
else if (register_operand (operand0, mode))
{
if (register_operand (operand1, mode)
/* Only `general_operands' can come here, so MEM is ok. */
|| GET_CODE (operand1) == MEM)
{
- /* Run this case quickly. */
+ /* Various sets are created during RTL generation which don't
+ have the REG_POINTER flag correctly set. After the CSE pass,
+ instruction recognition can fail if we don't consistently
+ set this flag when performing register copies. This should
+ also improve the opportunities for creating insns that use
+ unscaled indexing. */
+ if (REG_P (operand0) && REG_P (operand1))
+ {
+ if (REG_POINTER (operand1)
+ && !REG_POINTER (operand0)
+ && !HARD_REGISTER_P (operand0))
+ copy_reg_pointer (operand0, operand1);
+ else if (REG_POINTER (operand0)
+ && !REG_POINTER (operand1)
+ && !HARD_REGISTER_P (operand1))
+ copy_reg_pointer (operand1, operand0);
+ }
+
+ /* When MEMs are broken out, the REG_POINTER flag doesn't
+ get set. In some cases, we can set the REG_POINTER flag
+ from the declaration for the MEM. */
+ if (REG_P (operand0)
+ && GET_CODE (operand1) == MEM
+ && !REG_POINTER (operand0))
+ {
+ tree decl = MEM_EXPR (operand1);
+
+ /* Set the register pointer flag and register alignment
+ if the declaration for this memory reference is a
+ pointer type. Fortran indirect argument references
+ are ignored. */
+ if (decl
+ && !(flag_argument_noalias > 1
+ && TREE_CODE (decl) == INDIRECT_REF
+ && TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL))
+ {
+ tree type;
+
+ /* If this is a COMPONENT_REF, use the FIELD_DECL from
+ tree operand 1. */
+ if (TREE_CODE (decl) == COMPONENT_REF)
+ decl = TREE_OPERAND (decl, 1);
+
+ type = TREE_TYPE (decl);
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ type = get_inner_array_type (type);
+
+ if (POINTER_TYPE_P (type))
+ {
+ int align;
+
+ type = TREE_TYPE (type);
+ /* Using TYPE_ALIGN_OK is rather conservative as
+ only the ada frontend actually sets it. */
+ align = (TYPE_ALIGN_OK (type) ? TYPE_ALIGN (type)
+ : BITS_PER_UNIT);
+ mark_reg_pointer (operand0, align);
+ }
+ }
+ }
+
emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1));
return 1;
}
operands[1] = force_const_mem (mode, operand1);
operands[1] = legitimize_pic_address (XEXP (operands[1], 0),
mode, temp);
+ operands[1] = gen_rtx_MEM (mode, operands[1]);
emit_move_sequence (operands, mode, temp);
}
else
{
operands[1] = legitimize_pic_address (operand1, mode, temp);
+ if (REG_P (operand0) && REG_P (operands[1]))
+ copy_reg_pointer (operand0, operands[1]);
emit_insn (gen_rtx_SET (VOIDmode, operand0, operands[1]));
}
}
Don't mark hard registers though. That loses. */
if (GET_CODE (operand0) == REG
&& REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
- REG_POINTER (operand0) = 1;
+ mark_reg_pointer (operand0, BITS_PER_UNIT);
if (REGNO (temp) >= FIRST_PSEUDO_REGISTER)
- REG_POINTER (temp) = 1;
+ mark_reg_pointer (temp, BITS_PER_UNIT);
+
if (ishighonly)
set = gen_rtx_SET (mode, operand0, temp);
else
return 1;
}
else if (GET_CODE (operand1) != CONST_INT
- || ! cint_ok_for_move (INTVAL (operand1)))
+ || !cint_ok_for_move (INTVAL (operand1)))
{
- rtx extend = NULL_RTX;
- rtx temp;
+ rtx insn, temp;
+ rtx op1 = operand1;
+ HOST_WIDE_INT value = 0;
+ HOST_WIDE_INT insv = 0;
+ int insert = 0;
- if (TARGET_64BIT && GET_CODE (operand1) == CONST_INT
+ if (GET_CODE (operand1) == CONST_INT)
+ value = INTVAL (operand1);
+
+ if (TARGET_64BIT
+ && GET_CODE (operand1) == CONST_INT
&& HOST_BITS_PER_WIDE_INT > 32
&& GET_MODE_BITSIZE (GET_MODE (operand0)) > 32)
{
- HOST_WIDE_INT val = INTVAL (operand1);
HOST_WIDE_INT nval;
/* Extract the low order 32 bits of the value and sign extend.
can use the original value as-is. If the new value is
different, we use it and insert the most-significant 32-bits
of the original value into the final result. */
- nval = ((val & (((HOST_WIDE_INT) 2 << 31) - 1))
+ nval = ((value & (((HOST_WIDE_INT) 2 << 31) - 1))
^ ((HOST_WIDE_INT) 1 << 31)) - ((HOST_WIDE_INT) 1 << 31);
- if (val != nval)
+ if (value != nval)
{
#if HOST_BITS_PER_WIDE_INT > 32
- extend = GEN_INT (val >> 32);
+ insv = value >= 0 ? value >> 32 : ~(~value >> 32);
#endif
+ insert = 1;
+ value = nval;
operand1 = GEN_INT (nval);
}
}
if (reload_in_progress || reload_completed)
- temp = operand0;
+ temp = scratch_reg ? scratch_reg : operand0;
else
temp = gen_reg_rtx (mode);
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) <= HOST_BITS_PER_WIDE_INT)
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && !insert)
{
/* Directly break constant into high and low parts. This
provides better optimization opportunities because various
We use a 14-bit signed low part except when the addition
of 0x4000 to the high part might change the sign of the
high part. */
- HOST_WIDE_INT value = INTVAL (operand1);
HOST_WIDE_INT low = value & 0x3fff;
HOST_WIDE_INT high = value & ~ 0x3fff;
operands[1] = gen_rtx_LO_SUM (mode, temp, operand1);
}
- emit_move_insn (operands[0], operands[1]);
+ insn = emit_move_insn (operands[0], operands[1]);
+
+ /* Now insert the most significant 32 bits of the value
+ into the register. When we don't have a second register
+ available, it could take up to nine instructions to load
+ a 64-bit integer constant. Prior to reload, we force
+ constants that would take more than three instructions
+ to load to the constant pool. During and after reload,
+ we have to handle all possible values. */
+ if (insert)
+ {
+ /* Use a HIGH/LO_SUM/INSV sequence if we have a second
+ register and the value to be inserted is outside the
+ range that can be loaded with three depdi instructions. */
+ if (temp != operand0 && (insv >= 16384 || insv < -16384))
+ {
+ operand1 = GEN_INT (insv);
- if (extend != NULL_RTX)
- emit_insn (gen_insv (operands[0], GEN_INT (32), const0_rtx,
- extend));
+ emit_insn (gen_rtx_SET (VOIDmode, temp,
+ gen_rtx_HIGH (mode, operand1)));
+ emit_move_insn (temp, gen_rtx_LO_SUM (mode, temp, operand1));
+ emit_insn (gen_insv (operand0, GEN_INT (32),
+ const0_rtx, temp));
+ }
+ else
+ {
+ int len = 5, pos = 27;
+
+ /* Insert the bits using the depdi instruction. */
+ while (pos >= 0)
+ {
+ HOST_WIDE_INT v5 = ((insv & 31) ^ 16) - 16;
+ HOST_WIDE_INT sign = v5 < 0;
+
+ /* Left extend the insertion. */
+ insv = (insv >= 0 ? insv >> len : ~(~insv >> len));
+ while (pos > 0 && (insv & 1) == sign)
+ {
+ insv = (insv >= 0 ? insv >> 1 : ~(~insv >> 1));
+ len += 1;
+ pos -= 1;
+ }
+
+ emit_insn (gen_insv (operand0, GEN_INT (len),
+ GEN_INT (pos), GEN_INT (v5)));
+
+ len = pos > 0 && pos < 5 ? pos : 5;
+ pos -= len;
+ }
+ }
+ }
+
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_EQUAL, op1, REG_NOTES (insn));
return 1;
}
it will need a link/runtime reloc). */
int
-reloc_needed (exp)
- tree exp;
+reloc_needed (tree exp)
{
int reloc = 0;
will be true. */
int
-read_only_operand (operand, mode)
- rtx operand;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+read_only_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (operand) == CONST)
operand = XEXP (XEXP (operand, 0), 0);
\f
/* Return the best assembler insn template
- for moving operands[1] into operands[0] as a fullword. */
+ for moving operands[1] into operands[0] as a fullword. */
const char *
-singlemove_string (operands)
- rtx *operands;
+singlemove_string (rtx *operands)
{
HOST_WIDE_INT intval;
useful for copying IMM to a register using the zdepi
instructions. Store the immediate value to insert in OP[0]. */
static void
-compute_zdepwi_operands (imm, op)
- unsigned HOST_WIDE_INT imm;
- unsigned *op;
+compute_zdepwi_operands (unsigned HOST_WIDE_INT imm, unsigned *op)
{
int lsb, len;
useful for copying IMM to a register using the depdi,z
instructions. Store the immediate value to insert in OP[0]. */
void
-compute_zdepdi_operands (imm, op)
- unsigned HOST_WIDE_INT imm;
- unsigned *op;
+compute_zdepdi_operands (unsigned HOST_WIDE_INT imm, unsigned *op)
{
HOST_WIDE_INT lsb, len;
with operands OPERANDS. */
const char *
-output_move_double (operands)
- rtx *operands;
+output_move_double (rtx *operands)
{
enum { REGOP, OFFSOP, MEMOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
}
\f
const char *
-output_fp_move_double (operands)
- rtx *operands;
+output_fp_move_double (rtx *operands)
{
if (FP_REG_P (operands[0]))
{
ADDR can be effectively incremented by incrementing REG. */
static rtx
-find_addr_reg (addr)
- rtx addr;
+find_addr_reg (rtx addr)
{
while (GET_CODE (addr) == PLUS)
{
OPERANDS[0] is the destination pointer as a REG, clobbered.
OPERANDS[1] is the source pointer as a REG, clobbered.
OPERANDS[2] is a register for temporary storage.
- OPERANDS[4] is the size as a CONST_INT
OPERANDS[3] is a register for temporary storage.
+ OPERANDS[4] is the size as a CONST_INT
OPERANDS[5] is the alignment safe to use, as a CONST_INT.
- OPERANDS[6] is another temporary register. */
+ OPERANDS[6] is another temporary register. */
const char *
-output_block_move (operands, size_is_constant)
- rtx *operands;
- int size_is_constant ATTRIBUTE_UNUSED;
+output_block_move (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
{
int align = INTVAL (operands[5]);
unsigned long n_bytes = INTVAL (operands[4]);
- /* We can't move more than four bytes at a time because the PA
+ /* We can't move more than a word at a time because the PA
has no longer integer move insns. (Could use fp mem ops?) */
- if (align > 4)
- align = 4;
+ if (align > (TARGET_64BIT ? 8 : 4))
+ align = (TARGET_64BIT ? 8 : 4);
/* Note that we know each loop below will execute at least twice
(else we would have open-coded the copy). */
switch (align)
{
+ case 8:
+ /* Pre-adjust the loop counter. */
+ operands[4] = GEN_INT (n_bytes - 16);
+ output_asm_insn ("ldi %4,%2", operands);
+
+ /* Copying loop. */
+ output_asm_insn ("ldd,ma 8(%1),%3", operands);
+ output_asm_insn ("ldd,ma 8(%1),%6", operands);
+ output_asm_insn ("std,ma %3,8(%0)", operands);
+ output_asm_insn ("addib,>= -16,%2,.-12", operands);
+ output_asm_insn ("std,ma %6,8(%0)", operands);
+
+ /* Handle the residual. There could be up to 7 bytes of
+ residual to copy! */
+ if (n_bytes % 16 != 0)
+ {
+ operands[4] = GEN_INT (n_bytes % 8);
+ if (n_bytes % 16 >= 8)
+ output_asm_insn ("ldd,ma 8(%1),%3", operands);
+ if (n_bytes % 8 != 0)
+ output_asm_insn ("ldd 0(%1),%6", operands);
+ if (n_bytes % 16 >= 8)
+ output_asm_insn ("std,ma %3,8(%0)", operands);
+ if (n_bytes % 8 != 0)
+ output_asm_insn ("stdby,e %6,%4(%0)", operands);
+ }
+ return "";
+
case 4:
/* Pre-adjust the loop counter. */
operands[4] = GEN_INT (n_bytes - 8);
count insns rather than emit them. */
static int
-compute_movstrsi_length (insn)
- rtx insn;
+compute_movmem_length (rtx insn)
{
rtx pat = PATTERN (insn);
unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 7), 0));
/* We can't move more than four bytes at a time because the PA
has no longer integer move insns. (Could use fp mem ops?) */
- if (align > 4)
- align = 4;
+ if (align > (TARGET_64BIT ? 8 : 4))
+ align = (TARGET_64BIT ? 8 : 4);
/* The basic copying loop. */
n_insns = 6;
/* Lengths are expressed in bytes now; each insn is 4 bytes. */
return n_insns * 4;
}
+
+/* Emit code to perform a block clear.
+
+ OPERANDS[0] is the destination pointer as a REG, clobbered.
+ OPERANDS[1] is a register for temporary storage.
+ OPERANDS[2] is the size as a CONST_INT
+ OPERANDS[3] is the alignment safe to use, as a CONST_INT. */
+
+const char *
+output_block_clear (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
+{
+ int align = INTVAL (operands[3]);
+ unsigned long n_bytes = INTVAL (operands[2]);
+
+ /* We can't clear more than a word at a time because the PA
+ has no longer integer move insns. */
+ if (align > (TARGET_64BIT ? 8 : 4))
+ align = (TARGET_64BIT ? 8 : 4);
+
+ /* Note that we know each loop below will execute at least twice
+ (else we would have open-coded the copy). */
+ switch (align)
+ {
+ case 8:
+ /* Pre-adjust the loop counter. */
+ operands[2] = GEN_INT (n_bytes - 16);
+ output_asm_insn ("ldi %2,%1", operands);
+
+ /* Loop. */
+ output_asm_insn ("std,ma %%r0,8(%0)", operands);
+ output_asm_insn ("addib,>= -16,%1,.-4", operands);
+ output_asm_insn ("std,ma %%r0,8(%0)", operands);
+
+ /* Handle the residual. There could be up to 7 bytes of
+ residual to copy! */
+ if (n_bytes % 16 != 0)
+ {
+ operands[2] = GEN_INT (n_bytes % 8);
+ if (n_bytes % 16 >= 8)
+ output_asm_insn ("std,ma %%r0,8(%0)", operands);
+ if (n_bytes % 8 != 0)
+ output_asm_insn ("stdby,e %%r0,%2(%0)", operands);
+ }
+ return "";
+
+ case 4:
+ /* Pre-adjust the loop counter. */
+ operands[2] = GEN_INT (n_bytes - 8);
+ output_asm_insn ("ldi %2,%1", operands);
+
+ /* Loop. */
+ output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
+ output_asm_insn ("addib,>= -8,%1,.-4", operands);
+ output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
+
+ /* Handle the residual. There could be up to 7 bytes of
+ residual to copy! */
+ if (n_bytes % 8 != 0)
+ {
+ operands[2] = GEN_INT (n_bytes % 4);
+ if (n_bytes % 8 >= 4)
+ output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
+ if (n_bytes % 4 != 0)
+ output_asm_insn ("{stbys|stby},e %%r0,%2(%0)", operands);
+ }
+ return "";
+
+ case 2:
+ /* Pre-adjust the loop counter. */
+ operands[2] = GEN_INT (n_bytes - 4);
+ output_asm_insn ("ldi %2,%1", operands);
+
+ /* Loop. */
+ output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
+ output_asm_insn ("addib,>= -4,%1,.-4", operands);
+ output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
+
+ /* Handle the residual. */
+ if (n_bytes % 4 != 0)
+ {
+ if (n_bytes % 4 >= 2)
+ output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
+ if (n_bytes % 2 != 0)
+ output_asm_insn ("stb %%r0,0(%0)", operands);
+ }
+ return "";
+
+ case 1:
+ /* Pre-adjust the loop counter. */
+ operands[2] = GEN_INT (n_bytes - 2);
+ output_asm_insn ("ldi %2,%1", operands);
+
+ /* Loop. */
+ output_asm_insn ("{stbs|stb},ma %%r0,1(%0)", operands);
+ output_asm_insn ("addib,>= -2,%1,.-4", operands);
+ output_asm_insn ("{stbs|stb},ma %%r0,1(%0)", operands);
+
+ /* Handle the residual. */
+ if (n_bytes % 2 != 0)
+ output_asm_insn ("stb %%r0,0(%0)", operands);
+
+ return "";
+
+ default:
+ abort ();
+ }
+}
+
+/* Count the number of insns necessary to handle this block move.
+
+ Basic structure is the same as emit_block_move, except that we
+ count insns rather than emit them. */
+
+static int
+compute_clrmem_length (rtx insn)
+{
+ rtx pat = PATTERN (insn);
+ unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 4), 0));
+ unsigned long n_bytes = INTVAL (XEXP (XVECEXP (pat, 0, 3), 0));
+ unsigned int n_insns = 0;
+
+ /* We can't clear more than a word at a time because the PA
+ has no longer integer move insns. */
+ if (align > (TARGET_64BIT ? 8 : 4))
+ align = (TARGET_64BIT ? 8 : 4);
+
+ /* The basic loop. */
+ n_insns = 4;
+
+ /* Residuals. */
+ if (n_bytes % (2 * align) != 0)
+ {
+ if ((n_bytes % (2 * align)) >= align)
+ n_insns++;
+
+ if ((n_bytes % align) != 0)
+ n_insns++;
+ }
+
+ /* Lengths are expressed in bytes now; each insn is 4 bytes. */
+ return n_insns * 4;
+}
\f
const char *
-output_and (operands)
- rtx *operands;
+output_and (rtx *operands)
{
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0)
{
/* Return a string to perform a bitwise-and of operands[1] with operands[2]
storing the result in operands[0]. */
const char *
-output_64bit_and (operands)
- rtx *operands;
+output_64bit_and (rtx *operands)
{
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0)
{
}
const char *
-output_ior (operands)
- rtx *operands;
+output_ior (rtx *operands)
{
unsigned HOST_WIDE_INT mask = INTVAL (operands[2]);
int bs0, bs1, p, len;
/* Return a string to perform a bitwise-and of operands[1] with operands[2]
storing the result in operands[0]. */
const char *
-output_64bit_ior (operands)
- rtx *operands;
+output_64bit_ior (rtx *operands)
{
unsigned HOST_WIDE_INT mask = INTVAL (operands[2]);
int bs0, bs1, p, len;
be preceded by P%. */
static bool
-pa_assemble_integer (x, size, aligned_p)
- rtx x;
- unsigned int size;
- int aligned_p;
+pa_assemble_integer (rtx x, unsigned int size, int aligned_p)
{
if (size == UNITS_PER_WORD && aligned_p
&& function_label_operand (x, VOIDmode))
\f
/* Output an ascii string. */
void
-output_ascii (file, p, size)
- FILE *file;
- const char *p;
- int size;
+output_ascii (FILE *file, const char *p, int size)
{
int i;
int chars_output;
- unsigned char partial_output[16]; /* Max space 4 chars can occupy. */
+ unsigned char partial_output[16]; /* Max space 4 chars can occupy. */
/* The HP assembler can only take strings of 256 characters at one
time. This is a limitation on input line length, *not* the
when there's a 1:1 correspondence between fcmp and ftest/fbranch
instructions. */
static void
-remove_useless_addtr_insns (check_notes)
- int check_notes;
+remove_useless_addtr_insns (int check_notes)
{
rtx insn;
static int pass = 0;
/* Global variables set by output_function_prologue(). */
/* Size of frame. Need to know this to emit return insns from
leaf procedures. */
-static int actual_fsize;
-static int local_fsize, save_fregs;
+static HOST_WIDE_INT actual_fsize, local_fsize;
+static int save_fregs;
/* Emit RTL to store REG at the memory location specified by BASE+DISP.
Handle case where DISP > 8k by using the add_high_const patterns.
in %r1. There is code in expand_hppa_{prologue,epilogue} that knows this.*/
static void
-store_reg (reg, disp, base)
- int reg, disp, base;
+store_reg (int reg, HOST_WIDE_INT disp, int base)
{
rtx insn, dest, src, basereg;
dest = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
insn = emit_move_insn (dest, src);
}
+ else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
+ {
+ rtx delta = GEN_INT (disp);
+ 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);
+ 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),
+ REG_NOTES (insn));
+ }
+ }
else
{
rtx delta = GEN_INT (disp);
rtx high = gen_rtx_PLUS (Pmode, basereg, gen_rtx_HIGH (Pmode, delta));
rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
emit_move_insn (tmpreg, high);
dest = gen_rtx_MEM (word_mode, gen_rtx_LO_SUM (Pmode, tmpreg, delta));
insn = emit_move_insn (dest, src);
add MOD to BASE. MOD must be <= 8k. */
static void
-store_reg_modify (base, reg, mod)
- int base, reg, mod;
+store_reg_modify (int base, int reg, HOST_WIDE_INT mod)
{
rtx insn, basereg, srcreg, delta;
- if (! VAL_14_BITS_P (mod))
+ if (!VAL_14_BITS_P (mod))
abort ();
basereg = gen_rtx_REG (Pmode, base);
There is code in expand_hppa_{prologue,epilogue} that knows about this. */
static void
-set_reg_plus_d (reg, base, disp, note)
- int reg, base, disp, note;
+set_reg_plus_d (int reg, int base, HOST_WIDE_INT disp, int note)
{
rtx insn;
insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
plus_constant (gen_rtx_REG (Pmode, base), disp));
}
+ else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
+ {
+ rtx basereg = gen_rtx_REG (Pmode, base);
+ rtx delta = GEN_INT (disp);
+ rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
+ emit_move_insn (tmpreg, delta);
+ insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
+ gen_rtx_PLUS (Pmode, tmpreg, basereg));
+ }
else
{
rtx basereg = gen_rtx_REG (Pmode, base);
rtx delta = GEN_INT (disp);
+ rtx tmpreg = gen_rtx_REG (Pmode, 1);
- emit_move_insn (gen_rtx_REG (Pmode, 1),
+ emit_move_insn (tmpreg,
gen_rtx_PLUS (Pmode, basereg,
gen_rtx_HIGH (Pmode, delta)));
insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
- gen_rtx_LO_SUM (Pmode, gen_rtx_REG (Pmode, 1),
- delta));
+ gen_rtx_LO_SUM (Pmode, tmpreg, delta));
}
if (DO_FRAME_NOTES && note)
RTX_FRAME_RELATED_P (insn) = 1;
}
-int
-compute_frame_size (size, fregs_live)
- int size;
- int *fregs_live;
+HOST_WIDE_INT
+compute_frame_size (HOST_WIDE_INT size, int *fregs_live)
{
int freg_saved = 0;
int i, j;
to do this is made in regclass.c. */
static void
-pa_output_function_prologue (file, size)
- FILE *file;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+pa_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
/* The function's label and associated .PROC must never be
separated and must be output *after* any profiling declarations
/* hppa_expand_prologue does the dirty work now. We just need
to output the assembler directives which denote the start
of a function. */
- fprintf (file, "\t.CALLINFO FRAME=%d", actual_fsize);
+ fprintf (file, "\t.CALLINFO FRAME=" HOST_WIDE_INT_PRINT_DEC, actual_fsize);
if (regs_ever_live[2])
fputs (",CALLS,SAVE_RP", file);
else
}
void
-hppa_expand_prologue ()
+hppa_expand_prologue (void)
{
int merge_sp_adjust_with_store = 0;
- int size = get_frame_size ();
- int i, offset;
+ HOST_WIDE_INT size = get_frame_size ();
+ HOST_WIDE_INT offset;
+ int i;
rtx insn, tmpreg;
gr_saved = 0;
So instead use stwm to store at *sp and post-increment the
stack pointer as an atomic operation. Then increment sp to
finish allocating the new frame. */
- int adjust1 = 8192 - 64;
- int adjust2 = actual_fsize - adjust1;
+ HOST_WIDE_INT adjust1 = 8192 - 64;
+ HOST_WIDE_INT adjust2 = actual_fsize - adjust1;
store_reg_modify (STACK_POINTER_REGNUM, 1, adjust1);
set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM,
when generating PIC code. FIXME: What is the correct thing
to do for functions which make no calls and allocate no
frame? Do we need to allocate a frame, or can we just omit
- the save? For now we'll just omit the save. */
+ the save? For now we'll just omit the save.
+
+ We don't want a note on this insn as the frame marker can
+ move if there is a dynamic stack allocation. */
if (flag_pic && actual_fsize != 0 && !TARGET_64BIT)
- store_reg (PIC_OFFSET_TABLE_REGNUM, -32, STACK_POINTER_REGNUM);
+ {
+ rtx addr = gen_rtx_PLUS (word_mode, stack_pointer_rtx, GEN_INT (-32));
+
+ emit_move_insn (gen_rtx_MEM (word_mode, addr), pic_offset_table_rtx);
+
+ }
/* Align pointer properly (doubleword boundary). */
offset = (offset + 7) & ~7;
Handle case where DISP > 8k by using the add_high_const patterns. */
static void
-load_reg (reg, disp, base)
- int reg, disp, base;
+load_reg (int reg, HOST_WIDE_INT disp, int base)
{
- rtx src, dest, basereg;
+ rtx dest = gen_rtx_REG (word_mode, reg);
+ rtx basereg = gen_rtx_REG (Pmode, base);
+ rtx src;
- dest = gen_rtx_REG (word_mode, reg);
- basereg = gen_rtx_REG (Pmode, base);
if (VAL_14_BITS_P (disp))
+ src = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
+ else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
{
- src = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
- emit_move_insn (dest, src);
+ rtx delta = GEN_INT (disp);
+ rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
+ emit_move_insn (tmpreg, delta);
+ if (TARGET_DISABLE_INDEXING)
+ {
+ emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg));
+ src = gen_rtx_MEM (word_mode, tmpreg);
+ }
+ else
+ src = gen_rtx_MEM (word_mode, gen_rtx_PLUS (Pmode, tmpreg, basereg));
}
else
{
rtx delta = GEN_INT (disp);
rtx high = gen_rtx_PLUS (Pmode, basereg, gen_rtx_HIGH (Pmode, delta));
rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
emit_move_insn (tmpreg, high);
src = gen_rtx_MEM (word_mode, gen_rtx_LO_SUM (Pmode, tmpreg, delta));
- emit_move_insn (dest, src);
}
+
+ emit_move_insn (dest, src);
}
/* Update the total code bytes output to the text section. */
static void
-update_total_code_bytes (nbytes)
- int nbytes;
+update_total_code_bytes (int nbytes)
{
if ((TARGET_PORTABLE_RUNTIME || !TARGET_GAS || !TARGET_SOM)
- && in_text_section ())
+ && !IN_NAMED_SECTION_P (cfun->decl))
{
if (INSN_ADDRESSES_SET_P ())
{
adjustments before returning. */
static void
-pa_output_function_epilogue (file, size)
- FILE *file;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+pa_output_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
rtx insn = get_last_insn ();
}
void
-hppa_expand_epilogue ()
+hppa_expand_epilogue (void)
{
rtx tmpreg;
- int offset, i;
+ HOST_WIDE_INT offset;
+ HOST_WIDE_INT ret_off = 0;
+ int i;
int merge_sp_adjust_with_load = 0;
- int ret_off = 0;
/* We will use this often. */
tmpreg = gen_rtx_REG (word_mode, 1);
}
rtx
-hppa_pic_save_rtx ()
+hppa_pic_save_rtx (void)
{
return get_hard_reg_initial_val (word_mode, PIC_OFFSET_TABLE_REGNUM);
}
void
-hppa_profile_hook (label_no)
- int label_no;
+hppa_profile_hook (int label_no)
{
+ /* We use SImode for the address of the function in both 32 and
+ 64-bit code to avoid having to provide DImode versions of the
+ lcla2 and load_offset_label_address insn patterns. */
+ rtx reg = gen_reg_rtx (SImode);
+ rtx label_rtx = gen_label_rtx ();
rtx begin_label_rtx, call_insn;
char begin_label_name[16];
ASM_GENERATE_INTERNAL_LABEL (begin_label_name, FUNC_BEGIN_PROLOG_LABEL,
label_no);
- begin_label_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (begin_label_name));
+ begin_label_rtx = gen_rtx_SYMBOL_REF (SImode, ggc_strdup (begin_label_name));
if (TARGET_64BIT)
emit_move_insn (arg_pointer_rtx,
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-
+ 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. */
+ if (TARGET_PA_20)
+ emit_insn (gen_lcla2 (reg, label_rtx));
+ else
+ emit_insn (gen_lcla1 (reg, label_rtx));
+
+ emit_insn (gen_load_offset_label_address (gen_rtx_REG (SImode, 25),
+ reg, begin_label_rtx, label_rtx));
+
#ifndef NO_PROFILE_COUNTERS
{
rtx count_label_rtx, addr, r24;
r24 = gen_rtx_REG (Pmode, 24);
emit_move_insn (r24, addr);
- /* %r25 is set from within the output pattern. */
call_insn =
- emit_call_insn (gen_call_profiler (gen_rtx_SYMBOL_REF (Pmode, "_mcount"),
- GEN_INT (TARGET_64BIT ? 24 : 12),
- begin_label_rtx));
+ emit_call_insn (gen_call (gen_rtx_MEM (Pmode,
+ gen_rtx_SYMBOL_REF (Pmode,
+ "_mcount")),
+ GEN_INT (TARGET_64BIT ? 24 : 12)));
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), r24);
}
#else
- /* %r25 is set from within the output pattern. */
+
call_insn =
- emit_call_insn (gen_call_profiler (gen_rtx_SYMBOL_REF (Pmode, "_mcount"),
- GEN_INT (TARGET_64BIT ? 16 : 8),
- begin_label_rtx));
+ emit_call_insn (gen_call (gen_rtx_MEM (Pmode,
+ gen_rtx_SYMBOL_REF (Pmode,
+ "_mcount")),
+ GEN_INT (TARGET_64BIT ? 16 : 8)));
+
#endif
+ use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), gen_rtx_REG (SImode, 25));
+ use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), gen_rtx_REG (SImode, 26));
+
/* Indicate the _mcount call cannot throw, nor will it execute a
non-local goto. */
REG_NOTES (call_insn)
= gen_rtx_EXPR_LIST (REG_EH_REGION, constm1_rtx, REG_NOTES (call_insn));
-
- if (flag_pic)
- {
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx);
- if (TARGET_64BIT)
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
-
- emit_move_insn (pic_offset_table_rtx, hppa_pic_save_rtx ());
- }
}
/* Fetch the return address for the frame COUNT steps up from
return location is in a shared library. */
rtx
-return_addr_rtx (count, frameaddr)
- int count;
- rtx frameaddr;
+return_addr_rtx (int count, rtx frameaddr)
{
rtx label;
rtx rp;
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 ()
+hppa_can_use_return_insn_p (void)
{
return (reload_completed
&& (compute_frame_size (get_frame_size (), 0) ? 0 : 1)
}
void
-emit_bcond_fp (code, operand0)
- enum rtx_code code;
- rtx operand0;
+emit_bcond_fp (enum rtx_code code, rtx operand0)
{
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode,
}
rtx
-gen_cmp_fp (code, operand0, operand1)
- enum rtx_code code;
- rtx operand0, operand1;
+gen_cmp_fp (enum rtx_code code, rtx operand0, rtx operand1)
{
return gen_rtx_SET (VOIDmode, gen_rtx_REG (CCFPmode, 0),
gen_rtx_fmt_ee (code, CCFPmode, operand0, operand1));
a dependency LINK or INSN on DEP_INSN. COST is the current cost. */
static int
-pa_adjust_cost (insn, link, dep_insn, cost)
- rtx insn;
- rtx link;
- rtx dep_insn;
- int cost;
+pa_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
{
enum attr_type attr_type;
/* Adjust scheduling priorities. We use this to try and keep addil
and the next use of %r1 close together. */
static int
-pa_adjust_priority (insn, priority)
- rtx insn;
- int priority;
+pa_adjust_priority (rtx insn, int priority)
{
rtx set = single_set (insn);
rtx src, dest;
The 7XXX processors can issue two insns at a time.
The 8000 can issue 4 insns at a time. */
static int
-pa_issue_rate ()
+pa_issue_rate (void)
{
switch (pa_cpu)
{
Also compute the length of an inline block move here as it is too
complicated to express as a length attribute in pa.md. */
int
-pa_adjust_insn_length (insn, length)
- rtx insn;
- int length;
+pa_adjust_insn_length (rtx insn, int length)
{
rtx pat = PATTERN (insn);
- /* Call insns which are *not* indirect and have unfilled delay slots. */
- if (GET_CODE (insn) == CALL_INSN)
- {
-
- if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL
- && GET_CODE (XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)) == SYMBOL_REF)
- return 4;
- else if (GET_CODE (XVECEXP (pat, 0, 0)) == SET
- && GET_CODE (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0))
- == SYMBOL_REF)
- return 4;
- else
- return 0;
- }
- /* Jumps inside switch tables which have unfilled delay slots
- also need adjustment. */
- else if (GET_CODE (insn) == JUMP_INSN
- && simplejump_p (insn)
- && GET_MODE (insn) == SImode)
+ /* Jumps inside switch tables which have unfilled delay slots need
+ adjustment. */
+ if (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (pat) == PARALLEL
+ && get_attr_type (insn) == TYPE_BTABLE_BRANCH)
return 4;
/* Millicode insn with an unfilled delay slot. */
else if (GET_CODE (insn) == INSN
&& GET_CODE (XEXP (XVECEXP (pat, 0, 0), 1)) == MEM
&& GET_MODE (XEXP (XVECEXP (pat, 0, 0), 0)) == BLKmode
&& GET_MODE (XEXP (XVECEXP (pat, 0, 0), 1)) == BLKmode)
- return compute_movstrsi_length (insn) - 4;
+ return compute_movmem_length (insn) - 4;
+ /* Block clear pattern. */
+ else if (GET_CODE (insn) == INSN
+ && GET_CODE (pat) == PARALLEL
+ && GET_CODE (XVECEXP (pat, 0, 0)) == SET
+ && GET_CODE (XEXP (XVECEXP (pat, 0, 0), 0)) == MEM
+ && XEXP (XVECEXP (pat, 0, 0), 1) == const0_rtx
+ && GET_MODE (XEXP (XVECEXP (pat, 0, 0), 0)) == BLKmode)
+ return compute_clrmem_length (insn) - 4;
/* Conditional branch with an unfilled delay slot. */
else if (GET_CODE (insn) == JUMP_INSN && ! simplejump_p (insn))
{
For `%' followed by punctuation, CODE is the punctuation and X is null. */
void
-print_operand (file, x, code)
- FILE *file;
- rtx x;
- int code;
+print_operand (FILE *file, rtx x, int code)
{
switch (code)
{
fputs (",ma", file);
break;
case PLUS:
- if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
- || GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
+ if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == REG)
+ {
+ if (ASSEMBLER_DIALECT == 0)
+ fputs ("x", file);
+ }
+ else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
+ || GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
{
if (ASSEMBLER_DIALECT == 0)
fputs ("x,s", file);
base = XEXP (XEXP (x, 0), 0);
fprintf (file, "%d(%s)", size, reg_names [REGNO (base)]);
break;
- default:
- if (GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT)
+ case PLUS:
+ if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT)
fprintf (file, "%s(%s)",
reg_names [REGNO (XEXP (XEXP (XEXP (x, 0), 0), 0))],
reg_names [REGNO (XEXP (XEXP (x, 0), 1))]);
- else if (GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
+ else if (GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
fprintf (file, "%s(%s)",
reg_names [REGNO (XEXP (XEXP (XEXP (x, 0), 1), 0))],
reg_names [REGNO (XEXP (XEXP (x, 0), 0))]);
+ else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == REG)
+ {
+ /* Because the REG_POINTER flag can get lost during reload,
+ GO_IF_LEGITIMATE_ADDRESS canonicalizes the order of the
+ index and base registers in the combined move patterns. */
+ rtx base = XEXP (XEXP (x, 0), 1);
+ rtx index = XEXP (XEXP (x, 0), 0);
+
+ fprintf (file, "%s(%s)",
+ reg_names [REGNO (index)], reg_names [REGNO (base)]);
+ }
else
output_address (XEXP (x, 0));
break;
+ default:
+ output_address (XEXP (x, 0));
+ break;
}
}
else
/* output a SYMBOL_REF or a CONST expression involving a SYMBOL_REF. */
void
-output_global_address (file, x, round_constant)
- FILE *file;
- rtx x;
- int round_constant;
+output_global_address (FILE *file, rtx x, int round_constant)
{
/* Imagine (high (const (plus ...))). */
There are several possible versions. */
#define aputs(x) fputs(x, asm_out_file)
static inline void
-pa_file_start_level ()
+pa_file_start_level (void)
{
if (TARGET_64BIT)
aputs ("\t.LEVEL 2.0w\n");
}
static inline void
-pa_file_start_space (sortspace)
- int sortspace;
+pa_file_start_space (int sortspace)
{
aputs ("\t.SPACE $PRIVATE$");
if (sortspace)
}
static inline void
-pa_file_start_file (want_version)
- int want_version;
+pa_file_start_file (int want_version)
{
if (write_symbols != NO_DEBUG)
{
}
static inline void
-pa_file_start_mcount (aswhat)
- const char *aswhat;
+pa_file_start_mcount (const char *aswhat)
{
if (profile_flag)
fprintf (asm_out_file, "\t.IMPORT _mcount,%s\n", aswhat);
}
static void
-pa_elf_file_start ()
+pa_elf_file_start (void)
{
pa_file_start_level ();
pa_file_start_mcount ("ENTRY");
}
static void
-pa_som_file_start ()
+pa_som_file_start (void)
{
pa_file_start_level ();
pa_file_start_space (0);
}
static void
-pa_linux_file_start ()
+pa_linux_file_start (void)
{
pa_file_start_file (1);
pa_file_start_level ();
}
static void
-pa_hpux64_gas_file_start ()
+pa_hpux64_gas_file_start (void)
{
pa_file_start_level ();
#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
}
static void
-pa_hpux64_hpas_file_start ()
+pa_hpux64_hpas_file_start (void)
{
pa_file_start_level ();
pa_file_start_space (1);
#undef aputs
static struct deferred_plabel *
-get_plabel (fname)
- const char *fname;
+get_plabel (const char *fname)
{
size_t i;
}
static void
-output_deferred_plabels ()
+output_deferred_plabels (void)
{
size_t i;
/* If we have deferred plabels, then we need to switch into the data
}
}
+#ifdef HPUX_LONG_DOUBLE_LIBRARY
+/* Initialize optabs to point to HPUX long double emulation routines. */
+static void
+pa_hpux_init_libfuncs (void)
+{
+ set_optab_libfunc (add_optab, TFmode, "_U_Qfadd");
+ set_optab_libfunc (sub_optab, TFmode, "_U_Qfsub");
+ set_optab_libfunc (smul_optab, TFmode, "_U_Qfmpy");
+ set_optab_libfunc (sdiv_optab, TFmode, "_U_Qfdiv");
+ set_optab_libfunc (smin_optab, TFmode, "_U_Qmin");
+ set_optab_libfunc (smax_optab, TFmode, "_U_Qfmax");
+ set_optab_libfunc (sqrt_optab, TFmode, "_U_Qfsqrt");
+ set_optab_libfunc (abs_optab, TFmode, "_U_Qfabs");
+ set_optab_libfunc (neg_optab, TFmode, "_U_Qfneg");
+
+ set_optab_libfunc (eq_optab, TFmode, "_U_Qfeq");
+ set_optab_libfunc (ne_optab, TFmode, "_U_Qfne");
+ set_optab_libfunc (gt_optab, TFmode, "_U_Qfgt");
+ set_optab_libfunc (ge_optab, TFmode, "_U_Qfge");
+ set_optab_libfunc (lt_optab, TFmode, "_U_Qflt");
+ set_optab_libfunc (le_optab, TFmode, "_U_Qfle");
+ set_optab_libfunc (unord_optab, TFmode, "_U_Qfunord");
+
+ set_conv_libfunc (sext_optab, TFmode, SFmode, "_U_Qfcnvff_sgl_to_quad");
+ set_conv_libfunc (sext_optab, TFmode, DFmode, "_U_Qfcnvff_dbl_to_quad");
+ set_conv_libfunc (trunc_optab, SFmode, TFmode, "_U_Qfcnvff_quad_to_sgl");
+ set_conv_libfunc (trunc_optab, DFmode, TFmode, "_U_Qfcnvff_quad_to_dbl");
+
+ set_conv_libfunc (sfix_optab, SImode, TFmode, TARGET_64BIT
+ ? "__U_Qfcnvfxt_quad_to_sgl"
+ : "_U_Qfcnvfxt_quad_to_sgl");
+ set_conv_libfunc (sfix_optab, DImode, TFmode, "_U_Qfcnvfxt_quad_to_dbl");
+ set_conv_libfunc (ufix_optab, SImode, TFmode, "_U_Qfcnvfxt_quad_to_usgl");
+ set_conv_libfunc (ufix_optab, DImode, TFmode, "_U_Qfcnvfxt_quad_to_udbl");
+
+ set_conv_libfunc (sfloat_optab, TFmode, SImode, "_U_Qfcnvxf_sgl_to_quad");
+ set_conv_libfunc (sfloat_optab, TFmode, DImode, "_U_Qfcnvxf_dbl_to_quad");
+}
+#endif
+
/* HP's millicode routines mean something special to the assembler.
Keep track of which ones we have used. */
enum millicodes { remI, remU, divI, divU, mulI, end1000 };
-static void import_milli PARAMS ((enum millicodes));
+static void import_milli (enum millicodes);
static char imported[(int) end1000];
static const char * const milli_names[] = {"remI", "remU", "divI", "divU", "mulI"};
static const char import_string[] = ".IMPORT $$....,MILLICODE";
#define MILLI_START 10
static void
-import_milli (code)
- enum millicodes code;
+import_milli (enum millicodes code)
{
char str[sizeof (import_string)];
the proper registers. */
const char *
-output_mul_insn (unsignedp, insn)
- int unsignedp ATTRIBUTE_UNUSED;
- rtx insn;
+output_mul_insn (int unsignedp ATTRIBUTE_UNUSED, rtx insn)
{
import_milli (mulI);
return output_millicode_call (insn, gen_rtx_SYMBOL_REF (Pmode, "$$mulI"));
static int div_milli[16][2];
int
-div_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+div_operand (rtx op, enum machine_mode mode)
{
return (mode == SImode
&& ((GET_CODE (op) == REG && REGNO (op) == 25)
}
int
-emit_hpdiv_const (operands, unsignedp)
- rtx *operands;
- int unsignedp;
+emit_hpdiv_const (rtx *operands, int unsignedp)
{
if (GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) > 0
emit_move_insn (gen_rtx_REG (SImode, 26), operands[1]);
emit
- (gen_rtx
- (PARALLEL, VOIDmode,
+ (gen_rtx_PARALLEL
+ (VOIDmode,
gen_rtvec (6, gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, 29),
gen_rtx_fmt_ee (unsignedp ? UDIV : DIV,
SImode,
}
const char *
-output_div_insn (operands, unsignedp, insn)
- rtx *operands;
- int unsignedp;
- rtx insn;
+output_div_insn (rtx *operands, int unsignedp, rtx insn)
{
int divisor;
/* Output a $$rem millicode to do mod. */
const char *
-output_mod_insn (unsignedp, insn)
- int unsignedp;
- rtx insn;
+output_mod_insn (int unsignedp, rtx insn)
{
if (unsignedp)
{
}
void
-output_arg_descriptor (call_insn)
- rtx call_insn;
+output_arg_descriptor (rtx call_insn)
{
const char *arg_regs[4];
enum machine_mode arg_mode;
It might be worthwhile to try and make this a leaf function too. */
enum reg_class
-secondary_reload_class (class, mode, in)
- enum reg_class class;
- enum machine_mode mode;
- rtx in;
+secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in)
{
int regno, is_symbolic;
return NO_REGS;
}
+/* 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.
+
+ The 64-bit runtime does not describe passing any types by invisible
+ reference. The internals of GCC can't currently handle passing
+ empty structures, and zero or variable length arrays when they are
+ not passed entirely on the stack or by reference. Thus, as a GCC
+ extension, we pass these types by reference. The HP compiler doesn't
+ support these types, so hopefully there shouldn't be any compatibility
+ issues. This may have to be revisited when HP releases a C99 compiler
+ or updates the ABI. */
+
+static bool
+pa_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
+ enum machine_mode mode, tree type,
+ bool named ATTRIBUTE_UNUSED)
+{
+ HOST_WIDE_INT size;
+
+ if (type)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ if (TARGET_64BIT)
+ return size <= 0;
+ else
+ return size <= 0 || size > 8;
+}
+
enum direction
-function_arg_padding (mode, type)
- enum machine_mode mode;
- tree type;
+function_arg_padding (enum machine_mode mode, tree type)
{
if (mode == BLKmode
|| (TARGET_64BIT && type && AGGREGATE_TYPE_P (type)))
to determine if stdargs or varargs is used and fill in an initial
va_list. A pointer to this constructor is returned. */
-struct rtx_def *
-hppa_builtin_saveregs ()
+static rtx
+hppa_builtin_saveregs (void)
{
rtx offset, dest;
tree fntype = TREE_TYPE (current_function_decl);
}
void
-hppa_va_start (valist, nextarg)
- tree valist;
- rtx nextarg;
+hppa_va_start (tree valist, rtx nextarg)
{
nextarg = expand_builtin_saveregs ();
std_expand_builtin_va_start (valist, nextarg);
}
-rtx
-hppa_va_arg (valist, type)
- tree valist, type;
+static tree
+hppa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
{
- HOST_WIDE_INT size = int_size_in_bytes (type);
- HOST_WIDE_INT ofs;
- tree t, ptr, pptr;
-
if (TARGET_64BIT)
{
- /* Every argument in PA64 is supposed to be passed by value
- (including large structs). However, as a GCC extension, we
- pass zero and variable sized arguments by reference. Empty
- structures are a GCC extension not supported by the HP
- compilers. Thus, passing them by reference isn't likely
- to conflict with the ABI. For variable sized arguments,
- GCC doesn't have the infrastructure to allocate these to
- registers. */
-
- /* Arguments with a size greater than 8 must be aligned 0 MOD 16. */
-
- if (size > UNITS_PER_WORD)
- {
- t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (2 * UNITS_PER_WORD - 1, 0));
- t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
- build_int_2 (-2 * UNITS_PER_WORD, -1));
- t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
- TREE_SIDE_EFFECTS (t) = 1;
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- }
-
- if (size > 0)
- return std_expand_builtin_va_arg (valist, type);
- else
- {
- ptr = build_pointer_type (type);
-
- /* Args grow upward. */
- t = build (POSTINCREMENT_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
- TREE_SIDE_EFFECTS (t) = 1;
-
- pptr = build_pointer_type (ptr);
- t = build1 (NOP_EXPR, pptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
-
- t = build1 (INDIRECT_REF, ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
- }
+ /* Args grow upward. We can use the generic routines. */
+ return std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
}
else /* !TARGET_64BIT */
{
- ptr = build_pointer_type (type);
+ tree ptr = build_pointer_type (type);
+ tree valist_type;
+ tree t, u;
+ unsigned int size, ofs;
+ bool indirect;
- /* "Large" and variable sized types are passed by reference. */
- if (size > 8 || size <= 0)
+ indirect = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+ if (indirect)
{
- /* Args grow downward. */
- t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
- TREE_SIDE_EFFECTS (t) = 1;
-
- pptr = build_pointer_type (ptr);
- t = build1 (NOP_EXPR, pptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
-
- t = build1 (INDIRECT_REF, ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
+ type = ptr;
+ ptr = build_pointer_type (type);
}
- else
- {
- t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (-size, -1));
+ size = int_size_in_bytes (type);
+ valist_type = TREE_TYPE (valist);
- /* Copied from va-pa.h, but we probably don't need to align to
- word size, since we generate and preserve that invariant. */
- t = build (BIT_AND_EXPR, TREE_TYPE (valist), t,
- build_int_2 ((size > 4 ? -8 : -4), -1));
+ /* Args grow down. Not handled by generic routines. */
- t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
- TREE_SIDE_EFFECTS (t) = 1;
+ u = fold_convert (valist_type, size_in_bytes (type));
+ t = build (MINUS_EXPR, valist_type, valist, u);
- ofs = (8 - size) % 4;
- if (ofs)
- {
- t = build (PLUS_EXPR, TREE_TYPE (valist), t,
- build_int_2 (ofs, 0));
- TREE_SIDE_EFFECTS (t) = 1;
- }
+ /* 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_2 ((size > 4 ? -8 : -4), -1);
+ u = fold_convert (valist_type, u);
+ t = build (BIT_AND_EXPR, valist_type, t, u);
+
+ t = build (MODIFY_EXPR, valist_type, valist, t);
- t = build1 (NOP_EXPR, ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
+ ofs = (8 - size) % 4;
+ if (ofs != 0)
+ {
+ u = fold_convert (valist_type, size_int (ofs));
+ t = build (PLUS_EXPR, valist_type, t, u);
}
- }
- /* Calculate! */
- return expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL);
-}
+ t = fold_convert (ptr, t);
+ t = build_fold_indirect_ref (t);
+ if (indirect)
+ t = build_fold_indirect_ref (t);
+ return t;
+ }
+}
/* This routine handles all the normal conditional branch sequences we
might need to generate. It handles compare immediate vs compare
parameters. */
const char *
-output_cbranch (operands, nullify, length, negated, insn)
- rtx *operands;
- int nullify, length, negated;
- rtx insn;
+output_cbranch (rtx *operands, int nullify, int length, int negated, rtx insn)
{
static char buf[100];
int useskip = 0;
if (next_real_insn (JUMP_LABEL (insn)) == next_real_insn (insn))
return "nop";
+ /* The doubleword form of the cmpib instruction doesn't have the LEU
+ and GTU conditions while the cmpb instruction does. Since we accept
+ 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 this is a long branch with its delay slot unfilled, set `nullify'
as it can nullify the delay slot and save a nop. */
if (length == 8 && dbr_sequence_length () == 0)
maximum range of a simple branch instruction. */
const char *
-output_lbranch (dest, insn)
- rtx dest, insn;
+output_lbranch (rtx dest, rtx insn)
{
rtx xoperands[2];
abort ();
final_scan_insn (NEXT_INSN (insn), asm_out_file,
- optimize, 0, 0);
+ optimize, 0, 0, NULL);
/* Now delete the delay insn. */
PUT_CODE (NEXT_INSN (insn), NOTE);
above. it returns the appropriate output template to emit the branch. */
const char *
-output_bb (operands, nullify, length, negated, insn, which)
- rtx *operands ATTRIBUTE_UNUSED;
- int nullify, length, negated;
- rtx insn;
- int which;
+output_bb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length,
+ int negated, rtx insn, int which)
{
static char buf[100];
int useskip = 0;
branch. */
const char *
-output_bvb (operands, nullify, length, negated, insn, which)
- rtx *operands ATTRIBUTE_UNUSED;
- int nullify, length, negated;
- rtx insn;
- int which;
+output_bvb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length,
+ int negated, rtx insn, int which)
{
static char buf[100];
int useskip = 0;
else
strcpy (buf, "{bvb,|bb,}");
if (useskip && GET_MODE (operands[0]) == DImode)
- strcpy (buf, "extrd,s,*}");
+ strcpy (buf, "extrd,s,*");
else if (GET_MODE (operands[0]) == DImode)
strcpy (buf, "bb,*");
if ((which == 0 && negated)
Note it may perform some output operations on its own before
returning the final output string. */
const char *
-output_dbra (operands, insn, which_alternative)
- rtx *operands;
- rtx insn;
- int which_alternative;
+output_dbra (rtx *operands, rtx insn, int which_alternative)
{
/* A conditional branch to the following instruction (eg the delay slot) is
else
{
/* Reload loop counter from memory, the store back to memory
- happens in the branch's delay slot. */
+ happens in the branch's delay slot. */
output_asm_insn ("ldw %0,%4", operands);
if (get_attr_length (insn) == 12)
return "addib,%C2 %1,%4,%3\n\tstw %4,%0";
Note it may perform some output operations on its own before
returning the final output string. */
const char *
-output_movb (operands, insn, which_alternative, reverse_comparison)
- rtx *operands;
- rtx insn;
- int which_alternative;
- int reverse_comparison;
+output_movb (rtx *operands, rtx insn, int which_alternative,
+ int reverse_comparison)
{
/* A conditional branch to the following instruction (eg the delay slot) is
else if (which_alternative == 2)
{
/* Reload loop counter from memory, the store back to memory
- happens in the branch's delay slot. */
+ happens in the branch's delay slot. */
if (get_attr_length (insn) == 8)
return "{comb|cmpb},%S2 %%r0,%1,%3\n\tstw %1,%0";
else
/* Copy any FP arguments in INSN into integer registers. */
static void
-copy_fp_args (insn)
- rtx insn;
+copy_fp_args (rtx insn)
{
rtx link;
rtx xoperands[2];
/* Compute length of the FP argument copy sequence for INSN. */
static int
-length_fp_args (insn)
- rtx insn;
+length_fp_args (rtx insn)
{
int length = 0;
rtx link;
over estimate the length than to under estimate it. */
int
-attr_length_millicode_call (insn)
- rtx insn;
+attr_length_millicode_call (rtx insn)
{
unsigned long distance = -1;
- unsigned long total = in_text_section () ? total_code_bytes : 0;
+ unsigned long total = IN_NAMED_SECTION_P (cfun->decl) ? 0 : total_code_bytes;
if (INSN_ADDRESSES_SET_P ())
{
CALL_DEST is the routine we are calling. */
const char *
-output_millicode_call (insn, call_dest)
- rtx insn;
- rtx call_dest;
+output_millicode_call (rtx insn, rtx call_dest)
{
int attr_length = get_attr_length (insn);
int seq_length = dbr_sequence_length ();
/* Return the attribute length of the call instruction INSN. The SIBCALL
flag indicates whether INSN is a regular call or a sibling call. The
- length must match the code generated by output_call. We include the delay
- slot in the returned length as it is better to over estimate the length
- than to under estimate it. */
+ length returned must be longer than the code actually generated by
+ output_call. Since branch shortening is done before delay branch
+ sequencing, there is no way to determine whether or not the delay
+ slot will be filled during branch shortening. Even when the delay
+ slot is filled, we may have to add a nop if the delay slot contains
+ a branch that can't reach its target. Thus, we always have to include
+ the delay slot in the length estimate. This used to be done in
+ pa_adjust_insn_length but we do it here now as some sequences always
+ fill the delay slot and we can save four bytes in the estimate for
+ these sequences. */
int
-attr_length_call (insn, sibcall)
- rtx insn;
- int sibcall;
+attr_length_call (rtx insn, int sibcall)
{
+ int local_call;
+ rtx call_dest;
+ tree call_decl;
+ int length = 0;
+ rtx pat = PATTERN (insn);
unsigned long distance = -1;
- unsigned long total = in_text_section ()? total_code_bytes : 0;
if (INSN_ADDRESSES_SET_P ())
{
+ unsigned long total;
+
+ total = IN_NAMED_SECTION_P (cfun->decl) ? 0 : total_code_bytes;
distance = (total + insn_current_reference_address (insn));
if (distance < total)
distance = -1;
}
- if (TARGET_64BIT)
- {
- if (!TARGET_LONG_CALLS
- && ((!sibcall && distance < 7600000) || distance < 240000))
- return 8;
-
- return (sibcall ? 28 : 24);
- }
+ /* Determine if this is a local call. */
+ if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL)
+ call_dest = XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0);
else
- {
- if (!TARGET_LONG_CALLS
- && ((TARGET_PA_20 && !sibcall && distance < 7600000)
- || distance < 240000))
- return 8;
+ call_dest = XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0);
- if (TARGET_LONG_ABS_CALL && !flag_pic)
- return 12;
+ call_decl = SYMBOL_REF_DECL (call_dest);
+ local_call = call_decl && (*targetm.binds_local_p) (call_decl);
- if ((TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
- || (TARGET_GAS && TARGET_LONG_PIC_PCREL_CALL))
- {
- if (TARGET_PA_20)
- return 20;
+ /* pc-relative branch. */
+ if (!TARGET_LONG_CALLS
+ && ((TARGET_PA_20 && !sibcall && distance < 7600000)
+ || distance < 240000))
+ length += 8;
- return 28;
- }
- else
- {
- int length = 0;
+ /* 64-bit plabel sequence. */
+ else if (TARGET_64BIT && !local_call)
+ length += sibcall ? 28 : 24;
- if (TARGET_SOM)
- length += length_fp_args (insn);
+ /* non-pic long absolute branch sequence. */
+ else if ((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)
+ length += 12;
- if (flag_pic)
- length += 4;
+ /* 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)))
+ {
+ length += 20;
- if (TARGET_PA_20)
- return (length + 32);
+ if (!TARGET_PA_20 && !TARGET_NO_SPACE_REGS)
+ length += 8;
+ }
- if (!TARGET_NO_SPACE_REGS)
- length += 8;
+ /* 32-bit plabel sequence. */
+ else
+ {
+ length += 32;
+
+ if (TARGET_SOM)
+ length += length_fp_args (insn);
+ if (flag_pic)
+ length += 4;
+
+ if (!TARGET_PA_20)
+ {
if (!sibcall)
length += 8;
- return (length + 32);
+ if (!TARGET_NO_SPACE_REGS)
+ length += 8;
}
}
+
+ return length;
}
/* INSN is a function call. It may have an unconditional jump
CALL_DEST is the routine we are calling. */
const char *
-output_call (insn, call_dest, sibcall)
- rtx insn;
- rtx call_dest;
- int sibcall;
+output_call (rtx insn, rtx call_dest, int sibcall)
{
int delay_insn_deleted = 0;
int delay_slot_filled = 0;
int seq_length = dbr_sequence_length ();
tree call_decl = SYMBOL_REF_DECL (call_dest);
- int local_call = call_decl && !TREE_PUBLIC (call_decl);
+ int local_call = call_decl && (*targetm.binds_local_p) (call_decl);
rtx xoperands[2];
xoperands[0] = call_dest;
&& !sibcall)
{
final_scan_insn (NEXT_INSN (insn), asm_out_file,
- optimize, 0, 0);
+ optimize, 0, 0, NULL);
/* Now delete the delay insn. */
PUT_CODE (NEXT_INSN (insn), NOTE);
/* 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, 0,
+ NULL);
/* Now delete the delay insn. */
PUT_CODE (NEXT_INSN (insn), NOTE);
}
}
- if (seq_length == 0 || (delay_insn_deleted && !delay_slot_filled))
+ if (!delay_slot_filled && (seq_length == 0 || delay_insn_deleted))
output_asm_insn ("nop", xoperands);
/* We are done if there isn't a jump in the delay slot. */
the sequence itself. */
int
-attr_length_indirect_call (insn)
- rtx insn;
+attr_length_indirect_call (rtx insn)
{
unsigned long distance = -1;
- unsigned long total = in_text_section () ? total_code_bytes : 0;
+ unsigned long total = IN_NAMED_SECTION_P (cfun->decl) ? 0 : total_code_bytes;
if (INSN_ADDRESSES_SET_P ())
{
}
const char *
-output_indirect_call (insn, call_dest)
- rtx insn;
- rtx call_dest;
+output_indirect_call (rtx insn, rtx call_dest)
{
rtx xoperands[1];
No need to check target flags as the length uniquely identifies
the remaining cases. */
if (attr_length_indirect_call (insn) == 8)
- return ".CALL\tARGW0=GR\n\t{bl|b,l} $$dyncall,%%r31\n\tcopy %%r31,%%r2";
+ {
+ /* 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)
+ 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";
+ }
/* Long millicode call, but we are not generating PIC or portable runtime
code. */
within the same translation unit. */
int
-attr_length_save_restore_dltp (insn)
- rtx insn;
+attr_length_save_restore_dltp (rtx insn)
{
if (find_reg_note (insn, REG_NORETURN, NULL_RTX))
return 0;
space), and special magic is needed to construct their address. */
void
-hppa_encode_label (sym)
- rtx sym;
+hppa_encode_label (rtx sym)
{
const char *str = XSTR (sym, 0);
int len = strlen (str) + 1;
}
static void
-pa_encode_section_info (decl, rtl, first)
- tree decl;
- rtx rtl;
- int first;
+pa_encode_section_info (tree decl, rtx rtl, int first)
{
if (first && TEXT_SPACE_P (decl))
{
/* This is sort of inverse to pa_encode_section_info. */
static const char *
-pa_strip_name_encoding (str)
- const char *str;
+pa_strip_name_encoding (const char *str)
{
str += (*str == '@');
str += (*str == '*');
}
int
-function_label_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+function_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (op) == SYMBOL_REF && FUNCTION_NAME_P (XSTR (op, 0));
}
with a constant. Used to keep certain patterns from matching
during instruction combination. */
int
-is_function_label_plus_const (op)
- rtx op;
+is_function_label_plus_const (rtx op)
{
/* Strip off any CONST. */
if (GET_CODE (op) == CONST)
/* Output assembly code for a thunk to FUNCTION. */
static void
-pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
- FILE *file;
- tree thunk_fndecl;
- HOST_WIDE_INT delta;
- HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED;
- tree function;
+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);
}
else
{
- fprintf (file, "\tldsid (%%sr0,%%r22),%%r1\n");
+ fprintf (file, "\tldsid (%%sr0,%%r22),%%r21\n");
fprintf (file, "\tmtsp %%r21,%%sr0\n");
fprintf (file, "\tbe 0(%%sr0,%%r22)\n\tldo ");
nbytes += 44;
It is safe to perform a sibcall optimization when the target function
will never return. */
static bool
-pa_function_ok_for_sibcall (decl, exp)
- tree decl;
- tree exp ATTRIBUTE_UNUSED;
+pa_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
/* 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,
return (decl != NULL_TREE);
/* Sibcalls are not ok because the arg pointer register is not a fixed
- register. This prevents the sibcall optimization from occuring. In
+ register. This prevents the sibcall optimization from occurring. In
addition, there are problems with stub placement using GNU ld. This
is because a normal sibcall branch uses a 17-bit relocation while
a regular call branch uses a 22-bit relocation. As a result, more
/* Returns 1 if the 6 operands specified in OPERANDS are suitable for
use in fmpyadd instructions. */
int
-fmpyaddoperands (operands)
- rtx *operands;
+fmpyaddoperands (rtx *operands)
{
enum machine_mode mode = GET_MODE (operands[0]);
#if !defined(USE_COLLECT2)
static void
-pa_asm_out_constructor (symbol, priority)
- rtx symbol;
- int priority;
+pa_asm_out_constructor (rtx symbol, int priority)
{
if (!function_label_operand (symbol, VOIDmode))
hppa_encode_label (symbol);
}
static void
-pa_asm_out_destructor (symbol, priority)
- rtx symbol;
- int priority;
+pa_asm_out_destructor (rtx symbol, int priority)
{
if (!function_label_operand (symbol, VOIDmode))
hppa_encode_label (symbol);
/* Returns 1 if the 6 operands specified in OPERANDS are suitable for
use in fmpysub instructions. */
int
-fmpysuboperands (operands)
- rtx *operands;
+fmpysuboperands (rtx *operands)
{
enum machine_mode mode = GET_MODE (operands[0]);
}
int
-plus_xor_ior_operator (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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
-shadd_constant_p (val)
- int val;
+shadd_constant_p (int val)
{
if (val == 2 || val == 4 || val == 8)
return 1;
/* 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 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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 register in a reg + reg address. */
+/* Return 1 if OP is valid as a base or index register in a
+ REG+REG address. */
int
-basereg_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- /* cse will create some unscaled indexed addresses, however; it
- generally isn't a win on the PA, so avoid creating unscaled
- indexed addresses until after cse is finished. */
- if (!cse_not_expected)
+borx_reg_operand (rtx op, enum machine_mode mode)
+{
+ if (GET_CODE (op) != REG)
return 0;
- /* Allow any register when TARGET_NO_SPACE_REGS is in effect since
- we don't have to worry about the braindamaged implicit space
- register selection from the basereg. */
- if (TARGET_NO_SPACE_REGS)
- return (GET_CODE (op) == REG);
+ /* We must reject virtual registers as the only expressions that
+ can be instantiated are REG and REG+CONST. */
+ if (op == virtual_incoming_args_rtx
+ || op == virtual_stack_vars_rtx
+ || op == virtual_stack_dynamic_rtx
+ || op == virtual_outgoing_args_rtx
+ || op == virtual_cfa_rtx)
+ return 0;
/* While it's always safe to index off the frame pointer, it's not
- always profitable, particularly when the frame pointer is being
- eliminated. */
- if (! flag_omit_frame_pointer && op == frame_pointer_rtx)
- return 1;
+ profitable to do so when the frame pointer is being eliminated. */
+ if (!reload_completed
+ && flag_omit_frame_pointer
+ && !current_function_calls_alloca
+ && op == frame_pointer_rtx)
+ return 0;
- return (GET_CODE (op) == REG
- && REG_POINTER (op)
- && register_operand (op, mode));
+ return register_operand (op, mode);
}
/* Return 1 if this operand is anything other than a hard register. */
int
-non_hard_reg_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+non_hard_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return ! (GET_CODE (op) == REG && REGNO (op) < FIRST_PSEUDO_REGISTER);
}
/* Return 1 if INSN branches forward. Should be using insn_addresses
to avoid walking through all the insns... */
static int
-forward_branch_p (insn)
- rtx insn;
+forward_branch_p (rtx insn)
{
rtx label = JUMP_LABEL (insn);
/* Return 1 if OP is an equality comparison, else return 0. */
int
-eq_neq_comparison_operator (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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 (insn)
- rtx insn;
+jump_in_call_delay (rtx insn)
{
if (GET_CODE (insn) != JUMP_INSN)
/* Output an unconditional move and branch insn. */
const char *
-output_parallel_movb (operands, length)
- rtx *operands;
- int length;
+output_parallel_movb (rtx *operands, int length)
{
/* These are the cases in which we win. */
if (length == 4)
/* Output an unconditional add and branch insn. */
const char *
-output_parallel_addb (operands, length)
- rtx *operands;
- int length;
+output_parallel_addb (rtx *operands, int length)
{
/* To make life easy we want operand0 to be the shared input/output
operand and operand1 to be the readonly operand. */
the delay slot of the call. */
int
-following_call (insn)
- rtx insn;
+following_call (rtx insn)
{
if (! TARGET_JUMP_IN_DELAY)
return 0;
The jump instructions within the table are special; we must be able
to identify them during assembly output (if the jumps don't get filled
we need to emit a nop rather than nullifying the delay slot)). We
- identify jumps in switch tables by marking the SET with DImode.
+ identify jumps in switch tables by using insns with the attribute
+ type TYPE_BTABLE_BRANCH.
We also surround the jump table itself with BEGIN_BRTAB and END_BRTAB
insns. This serves two purposes, first it prevents jump.c from
when using GAS (allows for better link time optimizations). */
static void
-pa_reorg ()
+pa_reorg (void)
{
rtx insn;
/* Find and explode all ADDR_VEC or ADDR_DIFF_VEC insns. */
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
- rtx pattern, tmp, location;
+ rtx pattern, tmp, location, label;
unsigned int length, i;
/* Find an ADDR_VEC or ADDR_DIFF_VEC insn to explode. */
location = NEXT_INSN (location);
if (GET_CODE (pattern) == ADDR_VEC)
- {
- /* Emit the jump itself. */
- tmp = gen_jump (XEXP (XVECEXP (pattern, 0, i), 0));
- tmp = emit_jump_insn_after (tmp, location);
- JUMP_LABEL (tmp) = XEXP (XVECEXP (pattern, 0, i), 0);
- /* It is easy to rely on the branch table markers
- during assembly output to trigger the correct code
- for a switch table jump with an unfilled delay slot,
-
- However, that requires state and assumes that we look
- at insns in order.
-
- We can't make such assumptions when computing the length
- of instructions. Ugh. We could walk the insn chain to
- determine if this instruction is in a branch table, but
- that can get rather expensive, particularly during the
- branch shortening phase of the compiler.
-
- So instead we mark this jump as being special. This is
- far from ideal and knows that no code after this will
- muck around with the mode of the JUMP_INSN itself. */
- PUT_MODE (tmp, SImode);
- LABEL_NUSES (JUMP_LABEL (tmp))++;
- location = NEXT_INSN (location);
- }
+ label = XEXP (XVECEXP (pattern, 0, i), 0);
else
- {
- /* Emit the jump itself. */
- tmp = gen_jump (XEXP (XVECEXP (pattern, 1, i), 0));
- tmp = emit_jump_insn_after (tmp, location);
- JUMP_LABEL (tmp) = XEXP (XVECEXP (pattern, 1, i), 0);
- /* It is easy to rely on the branch table markers
- during assembly output to trigger the correct code
- for a switch table jump with an unfilled delay slot,
-
- However, that requires state and assumes that we look
- at insns in order.
-
- We can't make such assumptions when computing the length
- of instructions. Ugh. We could walk the insn chain to
- determine if this instruction is in a branch table, but
- that can get rather expensive, particularly during the
- branch shortening phase of the compiler.
-
- So instead we mark this jump as being special. This is
- far from ideal and knows that no code after this will
- muck around with the mode of the JUMP_INSN itself. */
- PUT_MODE (tmp, SImode);
- LABEL_NUSES (JUMP_LABEL (tmp))++;
- location = NEXT_INSN (location);
- }
+ label = XEXP (XVECEXP (pattern, 1, i), 0);
+
+ tmp = gen_short_jump (label);
+
+ /* Emit the jump itself. */
+ tmp = emit_jump_insn_after (tmp, location);
+ JUMP_LABEL (tmp) = label;
+ LABEL_NUSES (label)++;
+ location = NEXT_INSN (location);
/* Emit a BARRIER after the jump. */
emit_barrier_after (location);
}
else
{
- /* Sill need an end_brtab insn. */
+ /* Still need brtab marker insns. FIXME: the presence of these
+ markers disables output of the branch table to readonly memory,
+ and any alignment directives that might be needed. Possibly,
+ the begin_brtab insn should be output before the label for the
+ table. This doesn't matter at the moment since the tables are
+ always output in the text section. */
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
/* Find an ADDR_VEC insn. */
branch length restrictions. */
static void
-pa_combine_instructions ()
+pa_combine_instructions (void)
{
rtx anchor, new;
}
static int
-pa_can_combine_p (new, anchor, floater, reversed, dest, src1, src2)
- rtx new, anchor, floater;
- int reversed;
- rtx dest, src1, src2;
+pa_can_combine_p (rtx new, rtx anchor, rtx floater, int reversed, rtx dest,
+ rtx src1, rtx src2)
{
int insn_code_number;
rtx start, end;
filter out things it will not accept -- SEQUENCE, USE and CLOBBER insns
in particular. */
int
-insn_refs_are_delayed (insn)
- rtx insn;
+insn_refs_are_delayed (rtx insn)
{
return ((GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) != SEQUENCE
the mode is SF or DF. Then the value is returned in fr4 (32).
This must perform the same promotions as PROMOTE_MODE, else
- PROMOTE_FUNCTION_RETURN will not work correctly.
+ TARGET_PROMOTE_FUNCTION_RETURN will not work correctly.
Small structures must be returned in a PARALLEL on PA64 in order
to match the HP Compiler ABI. */
rtx
-function_value (valtype, func)
- tree valtype;
- tree func ATTRIBUTE_UNUSED;
+function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
{
enum machine_mode valmode;
??? We might want to restructure this so that it looks more like other
ports. */
rtx
-function_arg (cum, mode, type, named)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type;
- int named ATTRIBUTE_UNUSED;
+function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+ int named ATTRIBUTE_UNUSED)
{
int max_arg_words = (TARGET_64BIT ? 8 : 4);
int alignment = 0;
/* Structures 5 to 8 bytes in size are passed in the general
registers in the same manner as other non floating-point
objects. The data is right-justified and zero-extended
- to 64 bits.
-
- This is magic. Normally, using a PARALLEL results in left
- justified data on a big-endian target. However, using a
- single double-word register provides the required right
- justication for 5 to 8 byte structures. This has nothing
- to do with the direction of padding specified for the argument.
- It has to do with how the data is widened and shifted into
- and from the register.
-
- Aside from adding load_multiple and store_multiple patterns,
- this is the only way that I have found to obtain right
- justification of BLKmode data when it has a size greater
- than one word. Splitting the operation into two SImode loads
- or returning a DImode REG results in left justified data. */
+ 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)
{
rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
then this routine should return zero. It is currently called only for
the 64-bit target. */
int
-function_arg_partial_nregs (cum, mode, type, named)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type;
- int named ATTRIBUTE_UNUSED;
+function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, int named ATTRIBUTE_UNUSED)
{
unsigned int max_arg_words = 8;
unsigned int offset = 0;
MATCH_OPERATOR to recognize all the branch insns. */
int
-cmpib_comparison_operator (op, mode)
- register rtx op;
- enum machine_mode mode;
+cmpib_comparison_operator (rtx op, enum machine_mode mode)
{
return ((mode == VOIDmode || GET_MODE (op) == mode)
&& (GET_CODE (op) == EQ
not be placed in the read-only data section. */
static void
-pa_select_section (exp, reloc, align)
- tree exp;
- int reloc;
- unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+pa_select_section (tree exp, int reloc,
+ unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
if (TREE_CODE (exp) == VAR_DECL
&& TREE_READONLY (exp)
&& !reloc)
readonly_data_section ();
else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c'
- && !(TREE_CODE (exp) == STRING_CST && flag_writable_strings)
&& !reloc)
readonly_data_section ();
else
}
static void
-pa_globalize_label (stream, name)
- FILE *stream;
- const char *name;
+pa_globalize_label (FILE *stream, const char *name)
{
/* We only handle DATA objects here, functions are globalized in
ASM_DECLARE_FUNCTION_NAME. */
fputs (",DATA\n", stream);
}
}
+
+/* Worker function for TARGET_STRUCT_VALUE_RTX. */
+
+static rtx
+pa_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+ int incoming ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (Pmode, PA_STRUCT_VALUE_REGNUM);
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+bool
+pa_return_in_memory (tree type, 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.
+ Note, int_size_in_bytes can return -1 if the size of the object is
+ variable or larger than the maximum value that can be expressed as
+ a HOST_WIDE_INT. It can also return zero for an empty type. The
+ simplest way to handle variable and empty types is to pass them in
+ memory. This avoids problems in defining the boundaries of argument
+ slots, allocating registers, etc. */
+ return (int_size_in_bytes (type) > (TARGET_64BIT ? 16 : 8)
+ || int_size_in_bytes (type) <= 0);
+}
+
#include "gt-pa.h"