/* Subroutines for insn-output.c for HPPA.
- Copyright (C) 1992-2015 Free Software Foundation, Inc.
+ Copyright (C) 1992-2020 Free Software Foundation, Inc.
Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c
This file is part of GCC.
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+#define IN_TARGET_CODE 1
+
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "memmodel.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
+#include "tree.h"
+#include "df.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "optabs.h"
#include "regs.h"
-#include "hard-reg-set.h"
-#include "insn-config.h"
-#include "conditions.h"
+#include "emit-rtl.h"
+#include "recog.h"
+#include "diagnostic-core.h"
#include "insn-attr.h"
-#include "flags.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "vec.h"
-#include "double-int.h"
-#include "input.h"
#include "alias.h"
-#include "symtab.h"
-#include "wide-int.h"
-#include "inchash.h"
-#include "tree.h"
#include "fold-const.h"
#include "stor-layout.h"
-#include "stringpool.h"
#include "varasm.h"
#include "calls.h"
#include "output.h"
-#include "dbxout.h"
#include "except.h"
-#include "hashtab.h"
-#include "function.h"
-#include "statistics.h"
-#include "real.h"
-#include "fixed-value.h"
-#include "expmed.h"
-#include "dojump.h"
#include "explow.h"
-#include "emit-rtl.h"
-#include "stmt.h"
#include "expr.h"
-#include "insn-codes.h"
-#include "optabs.h"
#include "reload.h"
-#include "diagnostic-core.h"
-#include "ggc.h"
-#include "recog.h"
-#include "predict.h"
-#include "tm_p.h"
-#include "target.h"
#include "common/common-target.h"
-#include "target-def.h"
#include "langhooks.h"
-#include "dominance.h"
-#include "cfg.h"
#include "cfgrtl.h"
-#include "cfganal.h"
-#include "lcm.h"
-#include "cfgbuild.h"
-#include "cfgcleanup.h"
-#include "basic-block.h"
-#include "df.h"
#include "opts.h"
#include "builtins.h"
+/* This file should be included last. */
+#include "target-def.h"
+
/* Return nonzero if there is a bypass for the output of
OUT_INSN and the fp store IN_INSN. */
int
static int hppa_register_move_cost (machine_mode mode, reg_class_t,
reg_class_t);
static int hppa_address_cost (rtx, machine_mode mode, addr_space_t, bool);
-static bool hppa_rtx_costs (rtx, int, int, int, int *, bool);
+static bool hppa_rtx_costs (rtx, machine_mode, int, int, int *, bool);
static inline rtx force_mode (machine_mode, rtx);
static void pa_reorg (void);
static void pa_combine_instructions (void);
static bool forward_branch_p (rtx_insn *);
static void compute_zdepwi_operands (unsigned HOST_WIDE_INT, unsigned *);
static void compute_zdepdi_operands (unsigned HOST_WIDE_INT, unsigned *);
-static int compute_movmem_length (rtx_insn *);
+static int compute_cpymem_length (rtx_insn *);
static int compute_clrmem_length (rtx_insn *);
static bool pa_assemble_integer (rtx, unsigned int, int);
static void remove_useless_addtr_insns (int);
static rtx pa_function_value (const_tree, const_tree, bool);
static rtx pa_libcall_value (machine_mode, const_rtx);
static bool pa_function_value_regno_p (const unsigned int);
-static void pa_output_function_prologue (FILE *, HOST_WIDE_INT);
+static void pa_output_function_prologue (FILE *) ATTRIBUTE_UNUSED;
+static void pa_linux_output_function_prologue (FILE *) ATTRIBUTE_UNUSED;
static void update_total_code_bytes (unsigned int);
-static void pa_output_function_epilogue (FILE *, HOST_WIDE_INT);
-static int pa_adjust_cost (rtx_insn *, rtx, rtx_insn *, int);
-static int pa_adjust_priority (rtx_insn *, int);
+static void pa_output_function_epilogue (FILE *);
+static int pa_adjust_cost (rtx_insn *, int, rtx_insn *, int, unsigned int);
static int pa_issue_rate (void);
static int pa_reloc_rw_mask (void);
static void pa_som_asm_init_sections (void) ATTRIBUTE_UNUSED;
static rtx hppa_builtin_saveregs (void);
static void hppa_va_start (tree, rtx);
static tree hppa_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
-static bool pa_scalar_mode_supported_p (machine_mode);
+static bool pa_scalar_mode_supported_p (scalar_mode);
static bool pa_commutative_p (const_rtx x, int outer_code);
static void copy_fp_args (rtx_insn *) ATTRIBUTE_UNUSED;
static int length_fp_args (rtx_insn *) ATTRIBUTE_UNUSED;
static void pa_hpux64_hpas_file_start (void) ATTRIBUTE_UNUSED;
static void output_deferred_plabels (void);
static void output_deferred_profile_counters (void) ATTRIBUTE_UNUSED;
-#ifdef ASM_OUTPUT_EXTERNAL_REAL
-static void pa_hpux_file_end (void);
-#endif
+static void pa_file_end (void);
static void pa_init_libfuncs (void);
static rtx pa_struct_value_rtx (tree, int);
-static bool pa_pass_by_reference (cumulative_args_t, machine_mode,
- const_tree, bool);
-static int pa_arg_partial_bytes (cumulative_args_t, machine_mode,
- tree, bool);
-static void pa_function_arg_advance (cumulative_args_t, machine_mode,
- const_tree, bool);
-static rtx pa_function_arg (cumulative_args_t, machine_mode,
- const_tree, bool);
+static bool pa_pass_by_reference (cumulative_args_t,
+ const function_arg_info &);
+static int pa_arg_partial_bytes (cumulative_args_t, const function_arg_info &);
+static void pa_function_arg_advance (cumulative_args_t,
+ const function_arg_info &);
+static rtx pa_function_arg (cumulative_args_t, const function_arg_info &);
+static pad_direction pa_function_arg_padding (machine_mode, const_tree);
static unsigned int pa_function_arg_boundary (machine_mode, const_tree);
static struct machine_function * pa_init_machine_status (void);
static reg_class_t pa_secondary_reload (bool, rtx, reg_class_t,
machine_mode,
secondary_reload_info *);
+static bool pa_secondary_memory_needed (machine_mode,
+ reg_class_t, reg_class_t);
static void pa_extra_live_on_entry (bitmap);
static machine_mode pa_promote_function_mode (const_tree,
machine_mode, int *,
static bool pa_legitimate_constant_p (machine_mode, rtx);
static unsigned int pa_section_type_flags (tree, const char *, int);
static bool pa_legitimate_address_p (machine_mode, rtx, bool);
+static bool pa_callee_copies (cumulative_args_t, const function_arg_info &);
+static unsigned int pa_hard_regno_nregs (unsigned int, machine_mode);
+static bool pa_hard_regno_mode_ok (unsigned int, machine_mode);
+static bool pa_modes_tieable_p (machine_mode, machine_mode);
+static bool pa_can_change_mode_class (machine_mode, machine_mode, reg_class_t);
+static HOST_WIDE_INT pa_starting_frame_offset (void);
/* The following extra sections are only used for SOM. */
static GTY(()) section *som_readonly_data_section;
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER pa_assemble_integer
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE pa_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE pa_output_function_epilogue
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST pa_adjust_cost
-#undef TARGET_SCHED_ADJUST_PRIORITY
-#define TARGET_SCHED_ADJUST_PRIORITY pa_adjust_priority
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE pa_issue_rate
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
#undef TARGET_ASM_FILE_END
-#ifdef ASM_OUTPUT_EXTERNAL_REAL
-#define TARGET_ASM_FILE_END pa_hpux_file_end
-#else
-#define TARGET_ASM_FILE_END output_deferred_plabels
-#endif
+#define TARGET_ASM_FILE_END pa_file_end
#undef TARGET_ASM_RELOC_RW_MASK
#define TARGET_ASM_RELOC_RW_MASK pa_reloc_rw_mask
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE pa_pass_by_reference
#undef TARGET_CALLEE_COPIES
-#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
+#define TARGET_CALLEE_COPIES pa_callee_copies
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES pa_arg_partial_bytes
#undef TARGET_FUNCTION_ARG
#define TARGET_FUNCTION_ARG pa_function_arg
#undef TARGET_FUNCTION_ARG_ADVANCE
#define TARGET_FUNCTION_ARG_ADVANCE pa_function_arg_advance
+#undef TARGET_FUNCTION_ARG_PADDING
+#define TARGET_FUNCTION_ARG_PADDING pa_function_arg_padding
#undef TARGET_FUNCTION_ARG_BOUNDARY
#define TARGET_FUNCTION_ARG_BOUNDARY pa_function_arg_boundary
#undef TARGET_SECONDARY_RELOAD
#define TARGET_SECONDARY_RELOAD pa_secondary_reload
+#undef TARGET_SECONDARY_MEMORY_NEEDED
+#define TARGET_SECONDARY_MEMORY_NEEDED pa_secondary_memory_needed
#undef TARGET_EXTRA_LIVE_ON_ENTRY
#define TARGET_EXTRA_LIVE_ON_ENTRY pa_extra_live_on_entry
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P pa_legitimate_address_p
+#undef TARGET_LRA_P
+#define TARGET_LRA_P hook_bool_void_false
+
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS pa_hard_regno_nregs
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK pa_hard_regno_mode_ok
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P pa_modes_tieable_p
+
+#undef TARGET_CAN_CHANGE_MODE_CLASS
+#define TARGET_CAN_CHANGE_MODE_CLASS pa_can_change_mode_class
+
+#undef TARGET_CONSTANT_ALIGNMENT
+#define TARGET_CONSTANT_ALIGNMENT constant_alignment_word_strings
+
+#undef TARGET_STARTING_FRAME_OFFSET
+#define TARGET_STARTING_FRAME_OFFSET pa_starting_frame_offset
+
+#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
+#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
/* Parse the -mfixed-range= option string. */
dash = strchr (str, '-');
if (!dash)
{
- warning (0, "value of -mfixed-range must have form REG1-REG2");
+ warning (0, "value of %<-mfixed-range%> must have form REG1-REG2");
return;
}
*dash = '\0';
if (! TARGET_GAS && write_symbols != NO_DEBUG)
{
- warning (0, "-g is only supported when using GAS on this processor,");
- warning (0, "-g option disabled");
+ warning (0, "%<-g%> is only supported when using GAS on this processor");
+ warning (0, "%<-g%> option disabled");
write_symbols = NO_DEBUG;
}
if (flag_reorder_blocks_and_partition)
{
inform (input_location,
- "-freorder-blocks-and-partition does not work "
- "on this architecture");
+ "%<-freorder-blocks-and-partition%> does not work "
+ "on this architecture");
flag_reorder_blocks_and_partition = 0;
flag_reorder_blocks = 1;
}
int ignore ATTRIBUTE_UNUSED)
{
tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
- unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ unsigned int fcode = DECL_MD_FUNCTION_CODE (fndecl);
switch (fcode)
{
rtx tmp;
real_inf (&inf);
- tmp = CONST_DOUBLE_FROM_REAL_VALUE (inf, target_mode);
+ tmp = const_double_from_real_value (inf, target_mode);
tmp = validize_mem (force_const_mem (target_mode, tmp));
/* Accept any constant that can be moved in one instruction into a
general register. */
int
-pa_cint_ok_for_move (HOST_WIDE_INT ival)
+pa_cint_ok_for_move (unsigned HOST_WIDE_INT ival)
{
/* OK if ldo, ldil, or zdepi, can be used. */
return (VAL_14_BITS_P (ival)
significant 11 bits of the value must be zero and the value must
not change sign when extended from 32 to 64 bits. */
int
-pa_ldil_cint_p (HOST_WIDE_INT ival)
+pa_ldil_cint_p (unsigned HOST_WIDE_INT ival)
{
- HOST_WIDE_INT x = ival & (((HOST_WIDE_INT) -1 << 31) | 0x7ff);
+ unsigned HOST_WIDE_INT x;
- return x == 0 || x == ((HOST_WIDE_INT) -1 << 31);
+ x = ival & (((unsigned HOST_WIDE_INT) -1 << 31) | 0x7ff);
+ return x == 0 || x == ((unsigned HOST_WIDE_INT) -1 << 31);
}
/* True iff zdepi can be used to generate this CONST_INT.
So instead we just emit the raw set, which avoids the movXX
expanders completely. */
mark_reg_pointer (reg, BITS_PER_UNIT);
- insn = emit_insn (gen_rtx_SET (VOIDmode, reg, orig));
+ insn = emit_insn (gen_rtx_SET (reg, orig));
/* Put a REG_EQUAL note on this insn, so that it can be optimized. */
add_reg_note (insn, REG_EQUAL, orig);
ret = gen_reg_rtx (Pmode);
emit_library_call_value (gen_tls_get_addr (), ret,
- LCT_CONST, Pmode, 1, arg, Pmode);
+ LCT_CONST, Pmode, arg, Pmode);
return ret;
}
return ret;
}
+/* Helper for hppa_legitimize_address. Given X, return true if it
+ is a left shift by 1, 2 or 3 positions or a multiply by 2, 4 or 8.
+
+ This respectively represent canonical shift-add rtxs or scaled
+ memory addresses. */
+static bool
+mem_shadd_or_shadd_rtx_p (rtx x)
+{
+ return ((GET_CODE (x) == ASHIFT
+ || GET_CODE (x) == MULT)
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && ((GET_CODE (x) == ASHIFT
+ && pa_shadd_constant_p (INTVAL (XEXP (x, 1))))
+ || (GET_CODE (x) == MULT
+ && pa_mem_shadd_constant_p (INTVAL (XEXP (x, 1))))));
+}
+
/* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address.
This macro is used in only one place: `memory_address' in explow.c.
manner if Y is 2, 4, or 8. (allows more shadd insns and shifted indexed
addressing modes to be used).
+ Note that the addresses passed into hppa_legitimize_address always
+ come from a MEM, so we only have to match the MULT form on incoming
+ addresses. But to be future proof we also match the ASHIFT form.
+
+ However, this routine always places those shift-add sequences into
+ registers, so we have to generate the ASHIFT form as our output.
+
Put X and Z into registers. Then put the entire expression into
a register. */
/* If the newoffset will not fit in 14 bits (ldo), then
handling this would take 4 or 5 instructions (2 to load
the SYMBOL_REF + 1 or 2 to load the newoffset + 1 to
- add the new offset and the SYMBOL_REF.) Combine can
- not handle 4->2 or 5->2 combinations, so do not create
+ add the new offset and the SYMBOL_REF.) Combine cannot
+ handle 4->2 or 5->2 combinations, so do not create
them. */
if (! VAL_14_BITS_P (newoffset)
&& GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
return plus_constant (Pmode, ptr_reg, offset - newoffset);
}
- /* Handle (plus (mult (a) (shadd_constant)) (b)). */
+ /* Handle (plus (mult (a) (mem_shadd_constant)) (b)). */
- if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
- && pa_shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))
+ if (GET_CODE (x) == PLUS
+ && mem_shadd_or_shadd_rtx_p (XEXP (x, 0))
&& (OBJECT_P (XEXP (x, 1))
|| GET_CODE (XEXP (x, 1)) == SUBREG)
&& GET_CODE (XEXP (x, 1)) != CONST)
{
- int val = INTVAL (XEXP (XEXP (x, 0), 1));
- rtx reg1, reg2;
+ /* If we were given a MULT, we must fix the constant
+ as we're going to create the ASHIFT form. */
+ int shift_val = INTVAL (XEXP (XEXP (x, 0), 1));
+ if (GET_CODE (XEXP (x, 0)) == MULT)
+ shift_val = exact_log2 (shift_val);
+ rtx reg1, reg2;
reg1 = XEXP (x, 1);
if (GET_CODE (reg1) != REG)
reg1 = force_reg (Pmode, force_operand (reg1, 0));
if (GET_CODE (reg2) != REG)
reg2 = force_reg (Pmode, force_operand (reg2, 0));
- return force_reg (Pmode, gen_rtx_PLUS (Pmode,
- gen_rtx_MULT (Pmode,
- reg2,
- GEN_INT (val)),
- reg1));
+ return force_reg (Pmode,
+ gen_rtx_PLUS (Pmode,
+ gen_rtx_ASHIFT (Pmode, reg2,
+ GEN_INT (shift_val)),
+ reg1));
}
- /* Similarly for (plus (plus (mult (a) (shadd_constant)) (b)) (c)).
+ /* Similarly for (plus (plus (mult (a) (mem_shadd_constant)) (b)) (c)).
Only do so for floating point modes since this is more speculative
and we lose if it's an integer store. */
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
- && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
- && pa_shadd_constant_p (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)))
+ && mem_shadd_or_shadd_rtx_p (XEXP (XEXP (x, 0), 0))
&& (mode == SFmode || mode == DFmode))
{
+ int shift_val = INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1));
+
+ /* If we were given a MULT, we must fix the constant
+ as we're going to create the ASHIFT form. */
+ if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT)
+ shift_val = exact_log2 (shift_val);
- /* First, try and figure out what to use as a base register. */
+ /* Try and figure out what to use as a base register. */
rtx reg1, reg2, base, idx;
reg1 = XEXP (XEXP (x, 0), 1);
{
base = reg1;
idx = gen_rtx_PLUS (Pmode,
- gen_rtx_MULT (Pmode,
- XEXP (XEXP (XEXP (x, 0), 0), 0),
- XEXP (XEXP (XEXP (x, 0), 0), 1)),
+ gen_rtx_ASHIFT (Pmode,
+ XEXP (XEXP (XEXP (x, 0), 0), 0),
+ GEN_INT (shift_val)),
XEXP (x, 1));
}
else if (GET_CODE (reg2) == REG
{
/* Divide the CONST_INT by the scale factor, then add it to A. */
int val = INTVAL (XEXP (idx, 1));
+ val /= (1 << shift_val);
- val /= INTVAL (XEXP (XEXP (idx, 0), 1));
reg1 = XEXP (XEXP (idx, 0), 0);
if (GET_CODE (reg1) != REG)
reg1 = force_reg (Pmode, force_operand (reg1, 0));
return
force_reg
(Pmode, gen_rtx_PLUS (Pmode,
- gen_rtx_MULT (Pmode, reg1,
- XEXP (XEXP (idx, 0), 1)),
+ gen_rtx_ASHIFT (Pmode, reg1,
+ GEN_INT (shift_val)),
base));
}
&& INTVAL (XEXP (idx, 1)) <= 4096
&& INTVAL (XEXP (idx, 1)) >= -4096)
{
- int val = INTVAL (XEXP (XEXP (idx, 0), 1));
rtx reg1, reg2;
reg1 = force_reg (Pmode, gen_rtx_PLUS (Pmode, base, XEXP (idx, 1)));
if (GET_CODE (reg2) != CONST_INT)
reg2 = force_reg (Pmode, force_operand (reg2, 0));
- return force_reg (Pmode, gen_rtx_PLUS (Pmode,
- gen_rtx_MULT (Pmode,
- reg2,
- GEN_INT (val)),
- reg1));
+ return force_reg (Pmode,
+ gen_rtx_PLUS (Pmode,
+ gen_rtx_ASHIFT (Pmode, reg2,
+ GEN_INT (shift_val)),
+ reg1));
}
/* Get the index into a register, then add the base + index and
reg1 = force_reg (Pmode,
gen_rtx_PLUS (Pmode,
- gen_rtx_MULT (Pmode, reg1,
- XEXP (XEXP (idx, 0), 1)),
+ gen_rtx_ASHIFT (Pmode, reg1,
+ GEN_INT (shift_val)),
reg2));
/* Add the result to our base register and return. */
if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS)
{
/* See if this looks like
- (plus (mult (reg) (shadd_const))
+ (plus (mult (reg) (mem_shadd_const))
(const (plus (symbol_ref) (const_int))))
Where const_int is small. In that case the const
If const_int is big, but can be divided evenly by shadd_const
and added to (reg). This allows more scaled indexed addresses. */
if (GET_CODE (XEXP (y, 0)) == SYMBOL_REF
- && GET_CODE (XEXP (x, 0)) == MULT
+ && mem_shadd_or_shadd_rtx_p (XEXP (x, 0))
&& GET_CODE (XEXP (y, 1)) == CONST_INT
&& INTVAL (XEXP (y, 1)) >= -4096
- && INTVAL (XEXP (y, 1)) <= 4095
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
- && pa_shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1))))
+ && INTVAL (XEXP (y, 1)) <= 4095)
{
- int val = INTVAL (XEXP (XEXP (x, 0), 1));
+ int shift_val = INTVAL (XEXP (XEXP (x, 0), 1));
+
+ /* If we were given a MULT, we must fix the constant
+ as we're going to create the ASHIFT form. */
+ if (GET_CODE (XEXP (x, 0)) == MULT)
+ shift_val = exact_log2 (shift_val);
+
rtx reg1, reg2;
reg1 = XEXP (x, 1);
if (GET_CODE (reg2) != REG)
reg2 = force_reg (Pmode, force_operand (reg2, 0));
- return force_reg (Pmode,
- gen_rtx_PLUS (Pmode,
- gen_rtx_MULT (Pmode,
- reg2,
- GEN_INT (val)),
- reg1));
+ return
+ force_reg (Pmode,
+ gen_rtx_PLUS (Pmode,
+ gen_rtx_ASHIFT (Pmode,
+ reg2,
+ GEN_INT (shift_val)),
+ reg1));
}
else if ((mode == DFmode || mode == SFmode)
&& GET_CODE (XEXP (y, 0)) == SYMBOL_REF
- && GET_CODE (XEXP (x, 0)) == MULT
+ && mem_shadd_or_shadd_rtx_p (XEXP (x, 0))
&& GET_CODE (XEXP (y, 1)) == CONST_INT
- && INTVAL (XEXP (y, 1)) % INTVAL (XEXP (XEXP (x, 0), 1)) == 0
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
- && pa_shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1))))
+ && INTVAL (XEXP (y, 1)) % (1 << INTVAL (XEXP (XEXP (x, 0), 1))) == 0)
{
+ int shift_val = INTVAL (XEXP (XEXP (x, 0), 1));
+
+ /* If we were given a MULT, we must fix the constant
+ as we're going to create the ASHIFT form. */
+ if (GET_CODE (XEXP (x, 0)) == MULT)
+ shift_val = exact_log2 (shift_val);
+
regx1
= force_reg (Pmode, GEN_INT (INTVAL (XEXP (y, 1))
/ INTVAL (XEXP (XEXP (x, 0), 1))));
return
force_reg (Pmode,
gen_rtx_PLUS (Pmode,
- gen_rtx_MULT (Pmode, regx2,
- XEXP (XEXP (x, 0), 1)),
+ gen_rtx_ASHIFT (Pmode, regx2,
+ GEN_INT (shift_val)),
force_reg (Pmode, XEXP (y, 0))));
}
else if (GET_CODE (XEXP (y, 1)) == CONST_INT
scanned. In either case, *TOTAL contains the cost result. */
static bool
-hppa_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
+hppa_rtx_costs (rtx x, machine_mode mode, int outer_code,
+ int opno ATTRIBUTE_UNUSED,
int *total, bool speed ATTRIBUTE_UNUSED)
{
int factor;
+ int code = GET_CODE (x);
switch (code)
{
return true;
case MULT:
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
*total = COSTS_N_INSNS (3);
return true;
}
/* A mode size N times larger than SImode needs O(N*N) more insns. */
- factor = GET_MODE_SIZE (GET_MODE (x)) / 4;
+ factor = GET_MODE_SIZE (mode) / 4;
if (factor == 0)
factor = 1;
return true;
case DIV:
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
*total = COSTS_N_INSNS (14);
return true;
case MOD:
case UMOD:
/* A mode size N times larger than SImode needs O(N*N) more insns. */
- factor = GET_MODE_SIZE (GET_MODE (x)) / 4;
+ factor = GET_MODE_SIZE (mode) / 4;
if (factor == 0)
factor = 1;
case PLUS: /* this includes shNadd insns */
case MINUS:
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
*total = COSTS_N_INSNS (3);
return true;
/* A size N times larger than UNITS_PER_WORD needs N times as
many insns, taking N times as long. */
- factor = GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD;
+ factor = GET_MODE_SIZE (mode) / UNITS_PER_WORD;
if (factor == 0)
factor = 1;
*total = factor * COSTS_N_INSNS (1);
/* Handle secondary reloads for loads/stores of FP registers from
REG+D addresses where D does not fit in 5 or 14 bits, including
- (subreg (mem (addr))) cases. */
+ (subreg (mem (addr))) cases, and reloads for other unsupported
+ memory operands. */
if (scratch_reg
- && fp_reg_operand (operand0, mode)
+ && FP_REG_P (operand0)
&& (MEM_P (operand1)
|| (GET_CODE (operand1) == SUBREG
- && MEM_P (XEXP (operand1, 0))))
- && !floating_point_store_memory_operand (operand1, mode))
+ && MEM_P (XEXP (operand1, 0)))))
{
- if (GET_CODE (operand1) == SUBREG)
- operand1 = XEXP (operand1, 0);
+ rtx op1 = operand1;
- /* SCRATCH_REG will hold an address and maybe the actual data. We want
- it in WORD_MODE regardless of what mode it was originally given
- to us. */
- scratch_reg = force_mode (word_mode, scratch_reg);
+ if (GET_CODE (op1) == SUBREG)
+ op1 = XEXP (op1, 0);
- /* D might not fit in 14 bits either; for such cases load D into
- scratch reg. */
- if (reg_plus_base_memory_operand (operand1, mode)
- && !(TARGET_PA_20
- && !TARGET_ELF32
- && INT_14_BITS (XEXP (XEXP (operand1, 0), 1))))
+ if (reg_plus_base_memory_operand (op1, GET_MODE (op1)))
{
- 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));
+ if (!(TARGET_PA_20
+ && !TARGET_ELF32
+ && INT_14_BITS (XEXP (XEXP (op1, 0), 1)))
+ && !INT_5_BITS (XEXP (XEXP (op1, 0), 1)))
+ {
+ /* SCRATCH_REG will hold an address and maybe the actual data.
+ We want it in WORD_MODE regardless of what mode it was
+ originally given to us. */
+ scratch_reg = force_mode (word_mode, scratch_reg);
+
+ /* D might not fit in 14 bits either; for such cases load D
+ into scratch reg. */
+ if (!INT_14_BITS (XEXP (XEXP (op1, 0), 1)))
+ {
+ emit_move_insn (scratch_reg, XEXP (XEXP (op1, 0), 1));
+ emit_move_insn (scratch_reg,
+ gen_rtx_fmt_ee (GET_CODE (XEXP (op1, 0)),
+ Pmode,
+ XEXP (XEXP (op1, 0), 0),
+ scratch_reg));
+ }
+ else
+ emit_move_insn (scratch_reg, XEXP (op1, 0));
+ op1 = replace_equiv_address (op1, scratch_reg);
+ }
}
- else
- emit_move_insn (scratch_reg, XEXP (operand1, 0));
- emit_insn (gen_rtx_SET (VOIDmode, operand0,
- replace_equiv_address (operand1, scratch_reg)));
+ else if ((!INT14_OK_STRICT && symbolic_memory_operand (op1, VOIDmode))
+ || IS_LO_SUM_DLT_ADDR_P (XEXP (op1, 0))
+ || IS_INDEX_ADDR_P (XEXP (op1, 0)))
+ {
+ /* Load memory address into SCRATCH_REG. */
+ scratch_reg = force_mode (word_mode, scratch_reg);
+ emit_move_insn (scratch_reg, XEXP (op1, 0));
+ op1 = replace_equiv_address (op1, scratch_reg);
+ }
+ emit_insn (gen_rtx_SET (operand0, op1));
return 1;
}
else if (scratch_reg
- && fp_reg_operand (operand1, mode)
+ && FP_REG_P (operand1)
&& (MEM_P (operand0)
|| (GET_CODE (operand0) == SUBREG
- && MEM_P (XEXP (operand0, 0))))
- && !floating_point_store_memory_operand (operand0, mode))
+ && MEM_P (XEXP (operand0, 0)))))
{
- if (GET_CODE (operand0) == SUBREG)
- operand0 = XEXP (operand0, 0);
+ rtx op0 = operand0;
- /* SCRATCH_REG will hold an address and maybe the actual data. We want
- it in WORD_MODE regardless of what mode it was originally given
- to us. */
- scratch_reg = force_mode (word_mode, scratch_reg);
+ if (GET_CODE (op0) == SUBREG)
+ op0 = XEXP (op0, 0);
- /* D might not fit in 14 bits either; for such cases load D into
- scratch reg. */
- if (reg_plus_base_memory_operand (operand0, mode)
- && !(TARGET_PA_20
- && !TARGET_ELF32
- && INT_14_BITS (XEXP (XEXP (operand0, 0), 1))))
+ if (reg_plus_base_memory_operand (op0, GET_MODE (op0)))
{
- emit_move_insn (scratch_reg, XEXP (XEXP (operand0, 0), 1));
- emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand0,
- 0)),
- Pmode,
- XEXP (XEXP (operand0, 0),
- 0),
- scratch_reg));
+ if (!(TARGET_PA_20
+ && !TARGET_ELF32
+ && INT_14_BITS (XEXP (XEXP (op0, 0), 1)))
+ && !INT_5_BITS (XEXP (XEXP (op0, 0), 1)))
+ {
+ /* SCRATCH_REG will hold an address and maybe the actual data.
+ We want it in WORD_MODE regardless of what mode it was
+ originally given to us. */
+ scratch_reg = force_mode (word_mode, scratch_reg);
+
+ /* D might not fit in 14 bits either; for such cases load D
+ into scratch reg. */
+ if (!INT_14_BITS (XEXP (XEXP (op0, 0), 1)))
+ {
+ emit_move_insn (scratch_reg, XEXP (XEXP (op0, 0), 1));
+ emit_move_insn (scratch_reg,
+ gen_rtx_fmt_ee (GET_CODE (XEXP (op0, 0)),
+ Pmode,
+ XEXP (XEXP (op0, 0), 0),
+ scratch_reg));
+ }
+ else
+ emit_move_insn (scratch_reg, XEXP (op0, 0));
+ op0 = replace_equiv_address (op0, scratch_reg);
+ }
}
- else
- emit_move_insn (scratch_reg, XEXP (operand0, 0));
- emit_insn (gen_rtx_SET (VOIDmode,
- replace_equiv_address (operand0, scratch_reg),
- operand1));
+ else if ((!INT14_OK_STRICT && symbolic_memory_operand (op0, VOIDmode))
+ || IS_LO_SUM_DLT_ADDR_P (XEXP (op0, 0))
+ || IS_INDEX_ADDR_P (XEXP (op0, 0)))
+ {
+ /* Load memory address into SCRATCH_REG. */
+ scratch_reg = force_mode (word_mode, scratch_reg);
+ emit_move_insn (scratch_reg, XEXP (op0, 0));
+ op0 = replace_equiv_address (op0, scratch_reg);
+ }
+ emit_insn (gen_rtx_SET (op0, operand1));
return 1;
}
/* Handle secondary reloads for loads of FP registers from constant
Use scratch_reg to hold the address of the memory location. */
else if (scratch_reg
&& CONSTANT_P (operand1)
- && fp_reg_operand (operand0, mode))
+ && FP_REG_P (operand0))
{
rtx const_mem, xoperands[2];
if (operand1 == CONST0_RTX (mode))
{
- emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1));
+ emit_insn (gen_rtx_SET (operand0, operand1));
return 1;
}
pa_emit_move_sequence (xoperands, Pmode, 0);
/* Now load the destination register. */
- emit_insn (gen_rtx_SET (mode, operand0,
+ emit_insn (gen_rtx_SET (operand0,
replace_equiv_address (const_mem, scratch_reg)));
return 1;
}
emit_move_insn (operand0, scratch_reg);
return 1;
}
+
/* Handle the most common case: storing into a register. */
- else if (register_operand (operand0, mode))
+ if (register_operand (operand0, mode))
{
/* Legitimize TLS symbol references. This happens for references
that aren't a legitimate constant. */
if (register_operand (operand1, mode)
|| (GET_CODE (operand1) == CONST_INT
- && pa_cint_ok_for_move (INTVAL (operand1)))
+ && pa_cint_ok_for_move (UINTVAL (operand1)))
|| (operand1 == CONST0_RTX (mode))
|| (GET_CODE (operand1) == HIGH
&& !symbolic_operand (XEXP (operand1, 0), VOIDmode))
type = strip_array_types (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);
- }
+ mark_reg_pointer (operand0, BITS_PER_UNIT);
}
}
- emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1));
+ emit_insn (gen_rtx_SET (operand0, operand1));
return 1;
}
}
{
rtx temp = gen_reg_rtx (DFmode);
- emit_insn (gen_rtx_SET (VOIDmode, temp, operand1));
- emit_insn (gen_rtx_SET (VOIDmode, operand0, temp));
+ emit_insn (gen_rtx_SET (temp, operand1));
+ emit_insn (gen_rtx_SET (operand0, temp));
return 1;
}
if (register_operand (operand1, mode) || operand1 == CONST0_RTX (mode))
{
/* Run this case quickly. */
- emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1));
+ emit_insn (gen_rtx_SET (operand0, operand1));
return 1;
}
if (! (reload_in_progress || reload_completed))
if (flag_pic)
{
+ rtx_insn *insn;
rtx temp;
if (reload_in_progress || reload_completed)
else
temp = gen_reg_rtx (Pmode);
- /* (const (plus (symbol) (const_int))) must be forced to
- memory during/after reload if the const_int will not fit
- in 14 bits. */
+ /* Force (const (plus (symbol) (const_int))) to memory
+ if the const_int will not fit in 14 bits. Although
+ this requires a relocation, the instruction sequence
+ needed to load the value is shorter. */
if (GET_CODE (operand1) == CONST
&& GET_CODE (XEXP (operand1, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (operand1, 0), 1)) == CONST_INT
- && !INT_14_BITS (XEXP (XEXP (operand1, 0), 1))
- && (reload_completed || reload_in_progress)
- && flag_pic)
+ && !INT_14_BITS (XEXP (XEXP (operand1, 0), 1)))
{
- rtx const_mem = force_const_mem (mode, operand1);
- operands[1] = legitimize_pic_address (XEXP (const_mem, 0),
- mode, temp);
- operands[1] = replace_equiv_address (const_mem, operands[1]);
- pa_emit_move_sequence (operands, mode, temp);
+ rtx x, m = force_const_mem (mode, operand1);
+
+ x = legitimize_pic_address (XEXP (m, 0), mode, temp);
+ x = replace_equiv_address (m, x);
+ insn = emit_move_insn (operand0, x);
}
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]));
+ insn = emit_move_insn (operand0, operands[1]);
}
+
+ /* Put a REG_EQUAL note on this insn. */
+ set_unique_reg_note (insn, REG_EQUAL, operand1);
}
/* On the HPPA, references to data space are supposed to use dp,
register 27, but showing it in the RTL inhibits various cse
mark_reg_pointer (temp, BITS_PER_UNIT);
if (ishighonly)
- set = gen_rtx_SET (mode, operand0, temp);
+ set = gen_rtx_SET (operand0, temp);
else
- set = gen_rtx_SET (VOIDmode,
- operand0,
+ set = gen_rtx_SET (operand0,
gen_rtx_LO_SUM (mode, temp, operand1));
- emit_insn (gen_rtx_SET (VOIDmode,
- temp,
- gen_rtx_HIGH (mode, operand1)));
+ emit_insn (gen_rtx_SET (temp, gen_rtx_HIGH (mode, operand1)));
emit_insn (set);
}
operands[1] = tmp;
}
else if (GET_CODE (operand1) != CONST_INT
- || !pa_cint_ok_for_move (INTVAL (operand1)))
+ || !pa_cint_ok_for_move (UINTVAL (operand1)))
{
rtx temp;
rtx_insn *insn;
low = value - high;
- emit_insn (gen_rtx_SET (VOIDmode, temp, GEN_INT (high)));
+ emit_insn (gen_rtx_SET (temp, GEN_INT (high)));
operands[1] = gen_rtx_PLUS (mode, temp, GEN_INT (low));
}
else
{
- emit_insn (gen_rtx_SET (VOIDmode, temp,
- gen_rtx_HIGH (mode, operand1)));
+ emit_insn (gen_rtx_SET (temp, gen_rtx_HIGH (mode, operand1)));
operands[1] = gen_rtx_LO_SUM (mode, temp, operand1);
}
{
operand1 = GEN_INT (insv);
- emit_insn (gen_rtx_SET (VOIDmode, temp,
+ emit_insn (gen_rtx_SET (temp,
gen_rtx_HIGH (mode, operand1)));
emit_move_insn (temp, gen_rtx_LO_SUM (mode, temp, operand1));
if (mode == DImode)
- emit_insn (gen_insvdi (operand0, GEN_INT (32),
- const0_rtx, temp));
+ insn = emit_insn (gen_insvdi (operand0, GEN_INT (32),
+ const0_rtx, temp));
else
- emit_insn (gen_insvsi (operand0, GEN_INT (32),
- const0_rtx, temp));
+ insn = emit_insn (gen_insvsi (operand0, GEN_INT (32),
+ const0_rtx, temp));
}
else
{
}
if (mode == DImode)
- emit_insn (gen_insvdi (operand0, GEN_INT (len),
- GEN_INT (pos), GEN_INT (v5)));
+ insn = emit_insn (gen_insvdi (operand0,
+ GEN_INT (len),
+ GEN_INT (pos),
+ GEN_INT (v5)));
else
- emit_insn (gen_insvsi (operand0, GEN_INT (len),
- GEN_INT (pos), GEN_INT (v5)));
+ insn = emit_insn (gen_insvsi (operand0,
+ GEN_INT (len),
+ GEN_INT (pos),
+ GEN_INT (v5)));
len = pos > 0 && pos < 5 ? pos : 5;
pos -= len;
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
long i;
- REAL_VALUE_TYPE d;
gcc_assert (GET_MODE (operands[1]) == SFmode);
/* Translate the CONST_DOUBLE to a CONST_INT with the same target
bit pattern. */
- REAL_VALUE_FROM_CONST_DOUBLE (d, operands[1]);
- REAL_VALUE_TO_TARGET_SINGLE (d, i);
+ REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (operands[1]), i);
operands[1] = GEN_INT (i);
/* Fall through to CONST_INT case. */
enum { REGOP, OFFSOP, MEMOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
rtx addreg0 = 0, addreg1 = 0;
+ int highonly = 0;
/* First classify both operands. */
&& GET_CODE (XEXP (addr, 0)) == MULT)
{
rtx xoperands[4];
- rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0);
- if (!reg_overlap_mentioned_p (high_reg, addr))
- {
- xoperands[0] = high_reg;
- xoperands[1] = XEXP (addr, 1);
- xoperands[2] = XEXP (XEXP (addr, 0), 0);
- xoperands[3] = XEXP (XEXP (addr, 0), 1);
- output_asm_insn ("{sh%O3addl %2,%1,%0|shladd,l %2,%O3,%1,%0}",
- xoperands);
- return "ldw 4(%0),%R0\n\tldw 0(%0),%0";
- }
- else
- {
- xoperands[0] = high_reg;
- xoperands[1] = XEXP (addr, 1);
- xoperands[2] = XEXP (XEXP (addr, 0), 0);
- xoperands[3] = XEXP (XEXP (addr, 0), 1);
- output_asm_insn ("{sh%O3addl %2,%1,%R0|shladd,l %2,%O3,%1,%R0}",
- xoperands);
- return "ldw 0(%R0),%0\n\tldw 4(%R0),%R0";
- }
+ /* Load address into left half of destination register. */
+ xoperands[0] = gen_rtx_SUBREG (SImode, operands[0], 0);
+ xoperands[1] = XEXP (addr, 1);
+ xoperands[2] = XEXP (XEXP (addr, 0), 0);
+ xoperands[3] = XEXP (XEXP (addr, 0), 1);
+ output_asm_insn ("{sh%O3addl %2,%1,%0|shladd,l %2,%O3,%1,%0}",
+ xoperands);
+ return "ldw 4(%0),%R0\n\tldw 0(%0),%0";
+ }
+ else if (GET_CODE (addr) == PLUS
+ && REG_P (XEXP (addr, 0))
+ && REG_P (XEXP (addr, 1)))
+ {
+ rtx xoperands[3];
+
+ /* Load address into left half of destination register. */
+ xoperands[0] = gen_rtx_SUBREG (SImode, operands[0], 0);
+ xoperands[1] = XEXP (addr, 0);
+ xoperands[2] = XEXP (addr, 1);
+ output_asm_insn ("{addl|add,l} %1,%2,%0",
+ xoperands);
+ return "ldw 4(%0),%R0\n\tldw 0(%0),%0";
}
}
else if (optype1 == OFFSOP)
latehalf[1] = adjust_address_nv (operands[1], SImode, 4);
else if (optype1 == CNSTOP)
- split_double (operands[1], &operands[1], &latehalf[1]);
+ {
+ if (GET_CODE (operands[1]) == HIGH)
+ {
+ operands[1] = XEXP (operands[1], 0);
+ highonly = 1;
+ }
+ split_double (operands[1], &operands[1], &latehalf[1]);
+ }
else
latehalf[1] = operands[1];
if (addreg1)
output_asm_insn ("ldo 4(%0),%0", &addreg1);
- /* Do that word. */
- output_asm_insn (pa_singlemove_string (latehalf), latehalf);
+ /* Do high-numbered word. */
+ if (highonly)
+ output_asm_insn ("ldil L'%1,%0", latehalf);
+ else
+ output_asm_insn (pa_singlemove_string (latehalf), latehalf);
/* Undo the adds we just did. */
if (addreg0)
count insns rather than emit them. */
static int
-compute_movmem_length (rtx_insn *insn)
+compute_cpymem_length (rtx_insn *insn)
{
rtx pat = PATTERN (insn);
unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 7), 0));
static bool
pa_assemble_integer (rtx x, unsigned int size, int aligned_p)
{
+ bool result;
+ tree decl = NULL;
+
+ /* When we have a SYMBOL_REF with a SYMBOL_REF_DECL, we need to call
+ call assemble_external and set the SYMBOL_REF_DECL to NULL before
+ calling output_addr_const. Otherwise, it may call assemble_external
+ in the midst of outputing the assembler code for the SYMBOL_REF.
+ We restore the SYMBOL_REF_DECL after the output is done. */
+ if (GET_CODE (x) == SYMBOL_REF)
+ {
+ decl = SYMBOL_REF_DECL (x);
+ if (decl)
+ {
+ assemble_external (decl);
+ SET_SYMBOL_REF_DECL (x, NULL);
+ }
+ }
+
if (size == UNITS_PER_WORD
&& aligned_p
&& function_label_operand (x, VOIDmode))
output_addr_const (asm_out_file, x);
fputc ('\n', asm_out_file);
- return true;
+ result = true;
}
- return default_assemble_integer (x, size, aligned_p);
+ else
+ result = default_assemble_integer (x, size, aligned_p);
+
+ if (decl)
+ SET_SYMBOL_REF_DECL (x, decl);
+
+ return result;
}
\f
/* Output an ascii string. */
if (DO_FRAME_NOTES)
{
add_reg_note (insn, REG_FRAME_RELATED_EXPR,
- gen_rtx_SET (VOIDmode, tmpreg,
+ gen_rtx_SET (tmpreg,
gen_rtx_PLUS (Pmode, basereg, delta)));
RTX_FRAME_RELATED_P (insn) = 1;
}
insn = emit_move_insn (dest, src);
if (DO_FRAME_NOTES)
add_reg_note (insn, REG_FRAME_RELATED_EXPR,
- gen_rtx_SET (VOIDmode,
- gen_rtx_MEM (word_mode,
+ gen_rtx_SET (gen_rtx_MEM (word_mode,
gen_rtx_PLUS (word_mode,
basereg,
delta)),
gen_rtx_PLUS (Pmode, tmpreg, basereg));
if (DO_FRAME_NOTES)
add_reg_note (insn, REG_FRAME_RELATED_EXPR,
- gen_rtx_SET (VOIDmode, tmpreg,
+ gen_rtx_SET (tmpreg,
gen_rtx_PLUS (Pmode, basereg, delta)));
}
else
}
HOST_WIDE_INT
-pa_compute_frame_size (HOST_WIDE_INT size, int *fregs_live)
+pa_compute_frame_size (poly_int64 size, int *fregs_live)
{
int freg_saved = 0;
int i, j;
size = (size + UNITS_PER_WORD - 1) & ~(UNITS_PER_WORD - 1);
/* Space for previous frame pointer + filler. If any frame is
- allocated, we need to add in the STARTING_FRAME_OFFSET. We
+ allocated, we need to add in the TARGET_STARTING_FRAME_OFFSET. We
waste some space here for the sake of HP compatibility. The
first slot is only used when the frame pointer is needed. */
if (size || frame_pointer_needed)
- size += STARTING_FRAME_OFFSET;
+ size += pa_starting_frame_offset ();
/* If the current function calls __builtin_eh_return, then we need
to allocate stack space for registers that will hold data for
& ~(PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1));
}
-/* Generate the assembly code for function entry. FILE is a stdio
- stream to output the code to. SIZE is an int: how many units of
- temporary storage to allocate.
-
- Refer to the array `regs_ever_live' to determine which registers to
- save; `regs_ever_live[I]' is nonzero if register number I is ever
- used in the function. This function is responsible for knowing
- which registers should not be saved even if used. */
+/* Output function label, and associated .PROC and .CALLINFO statements. */
-/* On HP-PA, move-double insns between fpu and cpu need an 8-byte block
- of memory. If any fpu reg is used in the function, we allocate
- such a block here, at the bottom of the frame, just in case it's needed.
-
- If this function is a leaf procedure, then we may choose not
- to do a "save" insn. The decision about whether or not
- to do this is made in regclass.c. */
-
-static void
-pa_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+void
+pa_output_function_label (FILE *file)
{
/* The function's label and associated .PROC must never be
separated and must be output *after* any profiling declarations
fprintf (file, ",ENTRY_FR=%d", fr_saved + 11);
fputs ("\n\t.ENTRY\n", file);
+}
+
+/* Output function prologue. */
+
+static void
+pa_output_function_prologue (FILE *file)
+{
+ pa_output_function_label (file);
+ remove_useless_addtr_insns (0);
+}
+/* The label is output by ASM_DECLARE_FUNCTION_NAME on linux. */
+
+static void
+pa_linux_output_function_prologue (FILE *file ATTRIBUTE_UNUSED)
+{
remove_useless_addtr_insns (0);
}
and must be changed in tandem with this code. */
local_fsize = (size + UNITS_PER_WORD - 1) & ~(UNITS_PER_WORD - 1);
if (local_fsize || frame_pointer_needed)
- local_fsize += STARTING_FRAME_OFFSET;
+ local_fsize += pa_starting_frame_offset ();
actual_fsize = pa_compute_frame_size (size, &save_fregs);
if (flag_stack_usage_info)
the callee registers. */
if (VAL_14_BITS_P (actual_fsize) && local_fsize == 0)
merge_sp_adjust_with_store = 1;
- /* Can not optimize. Adjust the stack frame by actual_fsize
+ /* Cannot optimize. Adjust the stack frame by actual_fsize
bytes. */
else
set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM,
}
for (i = 18; i >= 4; i--)
- if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && !call_used_or_fixed_reg_p (i))
{
store_reg (i, offset, HARD_FRAME_POINTER_REGNUM);
offset += UNITS_PER_WORD;
}
for (i = 18; i >= 3; i--)
- if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && !call_used_or_fixed_reg_p (i))
{
/* If merge_sp_adjust_with_store is nonzero, then we can
optimize the first GR save. */
plus_constant (Pmode, base,
offset));
add_reg_note (insn, REG_FRAME_RELATED_EXPR,
- gen_rtx_SET (VOIDmode, mem, reg));
+ gen_rtx_SET (mem, reg));
}
else
{
offset + 4));
rtx regl = gen_rtx_REG (SFmode, i);
rtx regr = gen_rtx_REG (SFmode, i + 1);
- rtx setl = gen_rtx_SET (VOIDmode, meml, regl);
- rtx setr = gen_rtx_SET (VOIDmode, memr, regr);
+ rtx setl = gen_rtx_SET (meml, regl);
+ rtx setr = gen_rtx_SET (memr, regr);
rtvec vec;
RTX_FRAME_RELATED_P (setl) = 1;
adjustments before returning. */
static void
-pa_output_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+pa_output_function_epilogue (FILE *file)
{
rtx_insn *insn = get_last_insn ();
bool extra_nop;
}
for (i = 18; i >= 4; i--)
- if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && !call_used_or_fixed_reg_p (i))
{
load_reg (i, offset, HARD_FRAME_POINTER_REGNUM);
offset += UNITS_PER_WORD;
for (i = 18; i >= 3; i--)
{
- if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && !call_used_or_fixed_reg_p (i))
{
/* Only for the first load.
merge_sp_adjust_with_load holds the register load
void
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_code_label *label_rtx = gen_label_rtx ();
- rtx begin_label_rtx;
+ int reg_parm_stack_space = REG_PARM_STACK_SPACE (NULL_TREE);
+ rtx arg_bytes, begin_label_rtx, mcount, sym;
rtx_insn *call_insn;
char begin_label_name[16];
+ bool use_mcount_pcrel_call;
+
+ /* Set up call destination. */
+ sym = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
+ pa_encode_label (sym);
+ mcount = gen_rtx_MEM (Pmode, sym);
+
+ /* If we can reach _mcount with a pc-relative call, we can optimize
+ loading the address of the current function. This requires linker
+ long branch stub support. */
+ if (!TARGET_PORTABLE_RUNTIME
+ && !TARGET_LONG_CALLS
+ && (TARGET_SOM || flag_function_sections))
+ use_mcount_pcrel_call = TRUE;
+ else
+ use_mcount_pcrel_call = FALSE;
ASM_GENERATE_INTERNAL_LABEL (begin_label_name, FUNC_BEGIN_PROLOG_LABEL,
label_no);
begin_label_rtx = gen_rtx_SYMBOL_REF (SImode, ggc_strdup (begin_label_name));
- if (TARGET_64BIT)
- emit_move_insn (arg_pointer_rtx,
- gen_rtx_PLUS (word_mode, virtual_outgoing_args_rtx,
- GEN_INT (64)));
-
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 an instruction-
- relative sequence that avoids the use of relocations. The sequence
- is split so that the load_offset_label_address instruction can
- occupy the delay slot of the call to _mcount. */
- 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));
-
-#if !NO_DEFERRED_PROFILE_COUNTERS
- {
- rtx count_label_rtx, addr, r24;
- char count_label_name[16];
-
- funcdef_nos.safe_push (label_no);
- ASM_GENERATE_INTERNAL_LABEL (count_label_name, "LP", label_no);
- count_label_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (count_label_name));
+ if (!use_mcount_pcrel_call)
+ {
+ /* The address of the function is loaded into %r25 with an instruction-
+ relative sequence that avoids the use of relocations. 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 pattern. */
+ if (TARGET_PA_20)
+ emit_insn (gen_lcla2 (gen_rtx_REG (SImode, 25), label_rtx));
+ else
+ emit_insn (gen_lcla1 (gen_rtx_REG (SImode, 25), label_rtx));
+ }
- addr = force_reg (Pmode, count_label_rtx);
- r24 = gen_rtx_REG (Pmode, 24);
- emit_move_insn (r24, addr);
+ if (!NO_DEFERRED_PROFILE_COUNTERS)
+ {
+ rtx count_label_rtx, addr, r24;
+ char count_label_name[16];
- call_insn =
- emit_call_insn (gen_call (gen_rtx_MEM (Pmode,
- gen_rtx_SYMBOL_REF (Pmode,
- "_mcount")),
- GEN_INT (TARGET_64BIT ? 24 : 12)));
+ funcdef_nos.safe_push (label_no);
+ ASM_GENERATE_INTERNAL_LABEL (count_label_name, "LP", label_no);
+ count_label_rtx = gen_rtx_SYMBOL_REF (Pmode,
+ ggc_strdup (count_label_name));
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), r24);
- }
-#else
+ addr = force_reg (Pmode, count_label_rtx);
+ r24 = gen_rtx_REG (Pmode, 24);
+ emit_move_insn (r24, addr);
- call_insn =
- emit_call_insn (gen_call (gen_rtx_MEM (Pmode,
- gen_rtx_SYMBOL_REF (Pmode,
- "_mcount")),
- GEN_INT (TARGET_64BIT ? 16 : 8)));
+ arg_bytes = GEN_INT (TARGET_64BIT ? 24 : 12);
+ if (use_mcount_pcrel_call)
+ call_insn = emit_call_insn (gen_call_mcount (mcount, arg_bytes,
+ begin_label_rtx));
+ else
+ call_insn = emit_call_insn (gen_call (mcount, arg_bytes));
-#endif
+ use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), r24);
+ }
+ else
+ {
+ arg_bytes = GEN_INT (TARGET_64BIT ? 16 : 8);
+ if (use_mcount_pcrel_call)
+ call_insn = emit_call_insn (gen_call_mcount (mcount, arg_bytes,
+ begin_label_rtx));
+ else
+ call_insn = emit_call_insn (gen_call (mcount, arg_bytes));
+ }
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. */
make_reg_eh_region_note_nothrow_nononlocal (call_insn);
+
+ /* Allocate space for fixed arguments. */
+ if (reg_parm_stack_space > crtl->outgoing_args_size)
+ crtl->outgoing_args_size = reg_parm_stack_space;
}
/* Fetch the return address for the frame COUNT steps up from
rtx operand1 = operands[2];
rtx label = operands[3];
- emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (CCFPmode, 0),
+ emit_insn (gen_rtx_SET (gen_rtx_REG (CCFPmode, 0),
gen_rtx_fmt_ee (code, CCFPmode, operand0, operand1)));
- emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+ emit_jump_insn (gen_rtx_SET (pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode,
gen_rtx_fmt_ee (NE,
VOIDmode,
a dependency LINK or INSN on DEP_INSN. COST is the current cost. */
static int
-pa_adjust_cost (rtx_insn *insn, rtx link, rtx_insn *dep_insn, int cost)
+pa_adjust_cost (rtx_insn *insn, int dep_type, rtx_insn *dep_insn, int cost,
+ unsigned int)
{
enum attr_type attr_type;
/* Don't adjust costs for a pa8000 chip, also do not adjust any
true dependencies as they are described with bypasses now. */
- if (pa_cpu >= PROCESSOR_8000 || REG_NOTE_KIND (link) == 0)
+ if (pa_cpu >= PROCESSOR_8000 || dep_type == 0)
return cost;
if (! recog_memoized (insn))
attr_type = get_attr_type (insn);
- switch (REG_NOTE_KIND (link))
+ switch (dep_type)
{
case REG_DEP_ANTI:
/* Anti dependency; DEP_INSN reads a register that INSN writes some
}
}
-/* Adjust scheduling priorities. We use this to try and keep addil
- and the next use of %r1 close together. */
-static int
-pa_adjust_priority (rtx_insn *insn, int priority)
-{
- rtx set = single_set (insn);
- rtx src, dest;
- if (set)
- {
- src = SET_SRC (set);
- dest = SET_DEST (set);
- if (GET_CODE (src) == LO_SUM
- && symbolic_operand (XEXP (src, 1), VOIDmode)
- && ! read_only_operand (XEXP (src, 1), VOIDmode))
- priority >>= 3;
-
- else if (GET_CODE (src) == MEM
- && GET_CODE (XEXP (src, 0)) == LO_SUM
- && symbolic_operand (XEXP (XEXP (src, 0), 1), VOIDmode)
- && ! read_only_operand (XEXP (XEXP (src, 0), 1), VOIDmode))
- priority >>= 1;
-
- else if (GET_CODE (dest) == MEM
- && GET_CODE (XEXP (dest, 0)) == LO_SUM
- && symbolic_operand (XEXP (XEXP (dest, 0), 1), VOIDmode)
- && ! read_only_operand (XEXP (XEXP (dest, 0), 1), VOIDmode))
- priority >>= 3;
- }
- return priority;
-}
-
/* The 700 can only issue a single insn at a time.
The 7XXX processors can issue two insns at a time.
The 8000 can issue 4 insns at a time. */
&& 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)
- length += compute_movmem_length (insn) - 4;
+ length += compute_cpymem_length (insn) - 4;
/* Block clear pattern. */
else if (NONJUMP_INSN_P (insn)
&& GET_CODE (pat) == PARALLEL
gcc_assert (GET_CODE (x) == CONST_INT);
fprintf (file, HOST_WIDE_INT_PRINT_DEC, 32 - (INTVAL (x) & 31));
return;
+ case 'o':
+ gcc_assert (GET_CODE (x) == CONST_INT
+ && (INTVAL (x) == 1 || INTVAL (x) == 2 || INTVAL (x) == 3));
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
+ return;
case 'O':
gcc_assert (GET_CODE (x) == CONST_INT && exact_log2 (INTVAL (x)) >= 0);
fprintf (file, "%d", exact_log2 (INTVAL (x)));
reg_names [REGNO (index)], reg_names [REGNO (base)]);
}
else
- output_address (XEXP (x, 0));
+ output_address (GET_MODE (x), XEXP (x, 0));
break;
default:
- output_address (XEXP (x, 0));
+ output_address (GET_MODE (x), XEXP (x, 0));
break;
}
}
switch (GET_CODE (XEXP (XEXP (x, 0), 0)))
{
+ case LABEL_REF:
case SYMBOL_REF:
base = XEXP (XEXP (x, 0), 0);
output_addr_const (file, base);
switch (GET_CODE (XEXP (XEXP (x, 0), 1)))
{
+ case LABEL_REF:
case SYMBOL_REF:
base = XEXP (XEXP (x, 0), 1);
output_addr_const (file, base);
}
if (TARGET_SYNC_LIBCALL)
- init_sync_libfuncs (UNITS_PER_WORD);
+ init_sync_libfuncs (8);
}
/* HP's millicode routines mean something special to the assembler.
emit
(gen_rtx_PARALLEL
(VOIDmode,
- gen_rtvec (6, gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, 29),
+ gen_rtvec (6, gen_rtx_SET (gen_rtx_REG (SImode, 29),
gen_rtx_fmt_ee (unsignedp ? UDIV : DIV,
SImode,
gen_rtx_REG (SImode, 26),
{
switch (mode)
{
- case SImode:
+ case E_SImode:
sri->icode = CODE_FOR_reload_insi_r1;
break;
- case DImode:
+ case E_DImode:
sri->icode = CODE_FOR_reload_indi_r1;
break;
- case SFmode:
+ case E_SFmode:
sri->icode = CODE_FOR_reload_insf_r1;
break;
- case DFmode:
+ case E_DFmode:
sri->icode = CODE_FOR_reload_indf_r1;
break;
{
switch (mode)
{
- case SImode:
+ case E_SImode:
sri->icode = CODE_FOR_reload_insi_r1;
break;
- case DImode:
+ case E_DImode:
sri->icode = CODE_FOR_reload_indi_r1;
break;
return NO_REGS;
}
+/* Implement TARGET_SECONDARY_MEMORY_NEEDED. */
+
+static bool
+pa_secondary_memory_needed (machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t class1 ATTRIBUTE_UNUSED,
+ reg_class_t class2 ATTRIBUTE_UNUSED)
+{
+#ifdef PA_SECONDARY_MEMORY_NEEDED
+ return PA_SECONDARY_MEMORY_NEEDED (mode, class1, class2);
+#else
+ return false;
+#endif
+}
+
/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. The argument pointer
is only marked as live on entry by df-scan when it is a fixed
register. It isn't a fixed register in the 64-bit runtime,
or updates the ABI. */
static bool
-pa_pass_by_reference (cumulative_args_t ca ATTRIBUTE_UNUSED,
- machine_mode mode, const_tree type,
- bool named ATTRIBUTE_UNUSED)
+pa_pass_by_reference (cumulative_args_t, const function_arg_info &arg)
{
- HOST_WIDE_INT size;
-
- if (type)
- size = int_size_in_bytes (type);
- else
- size = GET_MODE_SIZE (mode);
-
+ HOST_WIDE_INT size = arg.type_size_in_bytes ();
if (TARGET_64BIT)
return size <= 0;
else
return size <= 0 || size > 8;
}
-enum direction
+/* Implement TARGET_FUNCTION_ARG_PADDING. */
+
+static pad_direction
pa_function_arg_padding (machine_mode mode, const_tree type)
{
if (mode == BLKmode
|| TREE_CODE (type) == COMPLEX_TYPE
|| TREE_CODE (type) == VECTOR_TYPE)))
{
- /* Return none if justification is not required. */
+ /* Return PAD_NONE if justification is not required. */
if (type
&& TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
&& (int_size_in_bytes (type) * BITS_PER_UNIT) % PARM_BOUNDARY == 0)
- return none;
+ return PAD_NONE;
/* The directions set here are ignored when a BLKmode argument larger
than a word is placed in a register. Different code is used for
the stack and in registers should be identical. */
if (TARGET_64BIT)
/* The 64-bit runtime specifies left justification for aggregates. */
- return upward;
+ return PAD_UPWARD;
else
/* The 32-bit runtime architecture specifies right justification.
When the argument is passed on the stack, the argument is padded
with garbage on the left. The HP compiler pads with zeros. */
- return downward;
+ return PAD_DOWNWARD;
}
if (GET_MODE_BITSIZE (mode) < PARM_BOUNDARY)
- return downward;
+ return PAD_DOWNWARD;
else
- return none;
+ return PAD_NONE;
}
\f
unsigned int size, ofs;
bool indirect;
- indirect = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+ indirect = pass_va_arg_by_reference (type);
if (indirect)
{
type = ptr;
2 * BITS_PER_WORD isn't equal LONG_LONG_TYPE_SIZE. */
static bool
-pa_scalar_mode_supported_p (machine_mode mode)
+pa_scalar_mode_supported_p (scalar_mode mode)
{
int precision = GET_MODE_PRECISION (mode);
if (dbr_sequence_length ())
return FALSE;
- jump_insn = next_active_insn (JUMP_LABEL (insn));
+ jump_insn = next_active_insn (JUMP_LABEL_AS_INSN (insn));
while (insn)
{
insn = next_active_insn (insn);
the branch is followed by an asm. */
if (!insn
|| GET_CODE (PATTERN (insn)) == ASM_INPUT
- || extract_asm_operands (PATTERN (insn)) != NULL_RTX
+ || asm_noperands (PATTERN (insn)) >= 0
|| get_attr_length (insn) > 0)
break;
}
if (dbr_sequence_length ())
return FALSE;
- jump_insn = next_active_insn (JUMP_LABEL (insn));
+ jump_insn = next_active_insn (JUMP_LABEL_AS_INSN (insn));
while (insn)
{
insn = next_active_insn (insn);
return TRUE;
if (!(GET_CODE (PATTERN (insn)) == ASM_INPUT
- || extract_asm_operands (PATTERN (insn)) != NULL_RTX)
+ || asm_noperands (PATTERN (insn)) >= 0)
&& get_attr_length (insn) > 0)
break;
}
static bool
use_skip_p (rtx_insn *insn)
{
- rtx_insn *jump_insn = next_active_insn (JUMP_LABEL (insn));
+ rtx_insn *jump_insn = next_active_insn (JUMP_LABEL_AS_INSN (insn));
while (insn)
{
/* We can't rely on the length of asms, so we can't skip asms. */
if (!insn
|| GET_CODE (PATTERN (insn)) == ASM_INPUT
- || extract_asm_operands (PATTERN (insn)) != NULL_RTX)
+ || asm_noperands (PATTERN (insn)) >= 0)
break;
if (get_attr_length (insn) == 4
&& jump_insn == next_active_insn (insn))
return buf;
}
+/* Output a PIC pc-relative instruction sequence to load the address of
+ OPERANDS[0] to register OPERANDS[2]. OPERANDS[0] is a symbol ref
+ or a code label. OPERANDS[1] specifies the register to use to load
+ the program counter. OPERANDS[3] may be used for label generation
+ The sequence is always three instructions in length. The program
+ counter recorded for PA 1.X is eight bytes more than that for PA 2.0.
+ Register %r1 is clobbered. */
+
+static void
+pa_output_pic_pcrel_sequence (rtx *operands)
+{
+ gcc_assert (SYMBOL_REF_P (operands[0]) || LABEL_P (operands[0]));
+ if (TARGET_PA_20)
+ {
+ /* We can use mfia to determine the current program counter. */
+ if (TARGET_SOM || !TARGET_GAS)
+ {
+ operands[3] = gen_label_rtx ();
+ targetm.asm_out.internal_label (asm_out_file, "L",
+ CODE_LABEL_NUMBER (operands[3]));
+ output_asm_insn ("mfia %1", operands);
+ output_asm_insn ("addil L'%0-%l3,%1", operands);
+ output_asm_insn ("ldo R'%0-%l3(%%r1),%2", operands);
+ }
+ else
+ {
+ output_asm_insn ("mfia %1", operands);
+ output_asm_insn ("addil L'%0-$PIC_pcrel$0+12,%1", operands);
+ output_asm_insn ("ldo R'%0-$PIC_pcrel$0+16(%%r1),%2", operands);
+ }
+ }
+ else
+ {
+ /* We need to use a branch to determine the current program counter. */
+ output_asm_insn ("{bl|b,l} .+8,%1", operands);
+ if (TARGET_SOM || !TARGET_GAS)
+ {
+ operands[3] = gen_label_rtx ();
+ output_asm_insn ("addil L'%0-%l3,%1", operands);
+ targetm.asm_out.internal_label (asm_out_file, "L",
+ CODE_LABEL_NUMBER (operands[3]));
+ output_asm_insn ("ldo R'%0-%l3(%%r1),%2", operands);
+ }
+ else
+ {
+ output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%1", operands);
+ output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%2", operands);
+ }
+ }
+}
+
/* This routine handles output of long unconditional branches that
exceed the maximum range of a simple branch instruction. Since
we don't have a register available for the branch, we save register
const char *
pa_output_lbranch (rtx dest, rtx_insn *insn, int xdelay)
{
- rtx xoperands[2];
+ rtx xoperands[4];
xoperands[0] = dest;
}
else if (flag_pic)
{
- output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
- if (TARGET_SOM || !TARGET_GAS)
- {
- xoperands[1] = gen_label_rtx ();
- output_asm_insn ("addil L'%l0-%l1,%%r1", xoperands);
- targetm.asm_out.internal_label (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[1]));
- output_asm_insn ("ldo R'%l0-%l1(%%r1),%%r1", xoperands);
- }
- else
- {
- output_asm_insn ("addil L'%l0-$PIC_pcrel$0+4,%%r1", xoperands);
- output_asm_insn ("ldo R'%l0-$PIC_pcrel$0+8(%%r1),%%r1", xoperands);
- }
+ xoperands[1] = gen_rtx_REG (Pmode, 1);
+ xoperands[2] = xoperands[1];
+ pa_output_pic_pcrel_sequence (xoperands);
output_asm_insn ("bv %%r0(%%r1)", xoperands);
}
else
{
int attr_length = get_attr_length (insn);
int seq_length = dbr_sequence_length ();
- rtx xoperands[3];
+ rtx xoperands[4];
xoperands[0] = call_dest;
- xoperands[2] = gen_rtx_REG (Pmode, TARGET_64BIT ? 2 : 31);
/* Handle the common case where we are sure that the branch will
reach the beginning of the $CODE$ subspace. The within reach
|| (attr_length == 28
&& get_attr_type (insn) == TYPE_SH_FUNC_ADRS)))
{
- output_asm_insn ("{bl|b,l} %0,%2", xoperands);
+ xoperands[1] = gen_rtx_REG (Pmode, TARGET_64BIT ? 2 : 31);
+ output_asm_insn ("{bl|b,l} %0,%1", xoperands);
}
else
{
this doesn't work in shared libraries and other dynamically
loaded objects. Using a pc-relative sequence also avoids
problems related to the implicit use of the gp register. */
- output_asm_insn ("b,l .+8,%%r1", xoperands);
-
- if (TARGET_GAS)
- {
- output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands);
- output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1", xoperands);
- }
- else
- {
- xoperands[1] = gen_label_rtx ();
- output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
- targetm.asm_out.internal_label (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[1]));
- output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
- }
-
+ xoperands[1] = gen_rtx_REG (Pmode, 1);
+ xoperands[2] = xoperands[1];
+ pa_output_pic_pcrel_sequence (xoperands);
output_asm_insn ("bve,l (%%r1),%%r2", xoperands);
}
else if (TARGET_PORTABLE_RUNTIME)
}
else
{
- output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
- output_asm_insn ("addi 16,%%r1,%%r31", xoperands);
+ xoperands[1] = gen_rtx_REG (Pmode, 31);
+ xoperands[2] = gen_rtx_REG (Pmode, 1);
+ pa_output_pic_pcrel_sequence (xoperands);
- if (TARGET_SOM || !TARGET_GAS)
- {
- /* The HP assembler can generate relocations for the
- difference of two symbols. GAS can do this for a
- millicode symbol but not an arbitrary external
- symbol when generating SOM output. */
- xoperands[1] = gen_label_rtx ();
- targetm.asm_out.internal_label (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[1]));
- output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
- output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
- }
- else
- {
- output_asm_insn ("addil L'%0-$PIC_pcrel$0+8,%%r1", xoperands);
- output_asm_insn ("ldo R'%0-$PIC_pcrel$0+12(%%r1),%%r1",
- xoperands);
- }
+ /* Adjust return address. */
+ output_asm_insn ("ldo {16|24}(%%r31),%%r31", xoperands);
/* Jump to our target address in %r1. */
output_asm_insn ("bv %%r0(%%r1)", xoperands);
/* 64-bit plabel sequence. */
else if (TARGET_64BIT && !local_call)
- length += sibcall ? 28 : 24;
+ length += 24;
/* non-pic long absolute branch sequence. */
else if ((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)
/* long pc-relative branch sequence. */
else if (TARGET_LONG_PIC_SDIFF_CALL
- || (TARGET_GAS && !TARGET_SOM
- && (TARGET_LONG_PIC_PCREL_CALL || local_call)))
+ || (TARGET_GAS && !TARGET_SOM && local_call))
{
length += 20;
int seq_length = dbr_sequence_length ();
tree call_decl = SYMBOL_REF_DECL (call_dest);
int local_call = call_decl && targetm.binds_local_p (call_decl);
- rtx xoperands[2];
+ rtx xoperands[4];
xoperands[0] = call_dest;
xoperands[0] = pa_get_deferred_plabel (call_dest);
xoperands[1] = gen_label_rtx ();
- /* If this isn't a sibcall, we put the load of %r27 into the
- delay slot. We can't do this in a sibcall as we don't
- have a second call-clobbered scratch register available.
- We don't need to do anything when generating fast indirect
- calls. */
- if (seq_length != 0 && !sibcall)
+ /* Put the load of %r27 into the delay slot. We don't need to
+ do anything when generating fast indirect calls. */
+ if (seq_length != 0)
{
final_scan_insn (NEXT_INSN (insn), asm_out_file,
optimize, 0, NULL);
/* Now delete the delay insn. */
SET_INSN_DELETED (NEXT_INSN (insn));
- seq_length = 0;
}
output_asm_insn ("addil LT'%0,%%r27", xoperands);
output_asm_insn ("ldd RT'%0(%%r1),%%r1", xoperands);
output_asm_insn ("ldd 0(%%r1),%%r1", xoperands);
-
- if (sibcall)
- {
- output_asm_insn ("ldd 24(%%r1),%%r27", xoperands);
- output_asm_insn ("ldd 16(%%r1),%%r1", xoperands);
- output_asm_insn ("bve (%%r1)", xoperands);
- }
- else
- {
- output_asm_insn ("ldd 16(%%r1),%%r2", xoperands);
- output_asm_insn ("bve,l (%%r2),%%r2", xoperands);
- output_asm_insn ("ldd 24(%%r1),%%r27", xoperands);
- seq_length = 1;
- }
+ output_asm_insn ("ldd 16(%%r1),%%r2", xoperands);
+ output_asm_insn ("bve,l (%%r2),%%r2", xoperands);
+ output_asm_insn ("ldd 24(%%r1),%%r27", xoperands);
+ seq_length = 1;
}
else
{
they don't allow an instruction in the delay slot. */
if (!((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)
&& !TARGET_LONG_PIC_SDIFF_CALL
- && !(TARGET_GAS && !TARGET_SOM
- && (TARGET_LONG_PIC_PCREL_CALL || local_call))
+ && !(TARGET_GAS && !TARGET_SOM && local_call)
&& !TARGET_64BIT)
indirect_call = 1;
}
else
{
- if (TARGET_LONG_PIC_SDIFF_CALL)
+ /* The HP assembler and linker can handle relocations for
+ the difference of two symbols. The HP assembler
+ recognizes the sequence as a pc-relative call and
+ the linker provides stubs when needed. */
+
+ /* GAS currently can't generate the relocations that
+ are needed for the SOM linker under HP-UX using this
+ sequence. The GNU linker doesn't generate the stubs
+ that are needed for external calls on TARGET_ELF32
+ with this sequence. For now, we have to use a longer
+ plabel sequence when using GAS for non local calls. */
+ if (TARGET_LONG_PIC_SDIFF_CALL
+ || (TARGET_GAS && !TARGET_SOM && local_call))
{
- /* The HP assembler and linker can handle relocations
- for the difference of two symbols. The HP assembler
- recognizes the sequence as a pc-relative call and
- the linker provides stubs when needed. */
- xoperands[1] = gen_label_rtx ();
- output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
- output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
- targetm.asm_out.internal_label (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[1]));
- output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
- }
- else if (TARGET_GAS && !TARGET_SOM
- && (TARGET_LONG_PIC_PCREL_CALL || local_call))
- {
- /* GAS currently can't generate the relocations that
- are needed for the SOM linker under HP-UX using this
- sequence. The GNU linker doesn't generate the stubs
- that are needed for external calls on TARGET_ELF32
- with this sequence. For now, we have to use a
- longer plabel sequence when using GAS. */
- output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
- output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1",
- xoperands);
- output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1",
- xoperands);
+ xoperands[1] = gen_rtx_REG (Pmode, 1);
+ xoperands[2] = xoperands[1];
+ pa_output_pic_pcrel_sequence (xoperands);
}
else
{
{
output_asm_insn ("addil LT'%0,%%r19", xoperands);
output_asm_insn ("ldw RT'%0(%%r1),%%r1", xoperands);
- output_asm_insn ("ldw 0(%%r1),%%r1", xoperands);
+ output_asm_insn ("ldw 0(%%r1),%%r22", xoperands);
}
else
{
output_asm_insn ("addil LR'%0-$global$,%%r27",
xoperands);
- output_asm_insn ("ldw RR'%0-$global$(%%r1),%%r1",
+ output_asm_insn ("ldw RR'%0-$global$(%%r1),%%r22",
xoperands);
}
- output_asm_insn ("bb,>=,n %%r1,30,.+16", xoperands);
- output_asm_insn ("depi 0,31,2,%%r1", xoperands);
- output_asm_insn ("ldw 4(%%sr0,%%r1),%%r19", xoperands);
- output_asm_insn ("ldw 0(%%sr0,%%r1),%%r1", xoperands);
+ output_asm_insn ("bb,>=,n %%r22,30,.+16", xoperands);
+ output_asm_insn ("depi 0,31,2,%%r22", xoperands);
+ /* Should this be an ordered load to ensure the target
+ address is loaded before the global pointer? */
+ output_asm_insn ("ldw 0(%%r22),%%r1", xoperands);
+ output_asm_insn ("ldw 4(%%r22),%%r19", xoperands);
if (!sibcall && !TARGET_PA_20)
{
if (TARGET_64BIT)
return 12;
- if (TARGET_FAST_INDIRECT_CALLS
- || (!TARGET_LONG_CALLS
- && !TARGET_PORTABLE_RUNTIME
- && ((TARGET_PA_20 && !TARGET_SOM && distance < 7600000)
- || distance < MAX_PCREL17F_OFFSET)))
+ if (TARGET_FAST_INDIRECT_CALLS)
return 8;
- if (flag_pic)
- return 20;
-
if (TARGET_PORTABLE_RUNTIME)
return 16;
+ if (!TARGET_LONG_CALLS
+ && ((TARGET_PA_20 && !TARGET_SOM && distance < 7600000)
+ || distance < MAX_PCREL17F_OFFSET))
+ return 8;
+
/* Out of reach, can use ble. */
- return 12;
+ if (!flag_pic)
+ return 12;
+
+ /* Inline versions of $$dyncall. */
+ if (!optimize_size)
+ {
+ if (TARGET_NO_SPACE_REGS)
+ return 28;
+
+ if (TARGET_PA_20)
+ return 32;
+ }
+
+ /* Long PIC pc-relative call. */
+ return 20;
}
const char *
pa_output_indirect_call (rtx_insn *insn, rtx call_dest)
{
- rtx xoperands[1];
+ rtx xoperands[4];
+ int length;
if (TARGET_64BIT)
{
xoperands[0] = call_dest;
- output_asm_insn ("ldd 16(%0),%%r2", xoperands);
- output_asm_insn ("bve,l (%%r2),%%r2\n\tldd 24(%0),%%r27", xoperands);
+ output_asm_insn ("ldd 16(%0),%%r2\n\t"
+ "bve,l (%%r2),%%r2\n\t"
+ "ldd 24(%0),%%r27", xoperands);
return "";
}
/* First the special case for kernels, level 0 systems, etc. */
if (TARGET_FAST_INDIRECT_CALLS)
- return "ble 0(%%sr4,%%r22)\n\tcopy %%r31,%%r2";
+ {
+ pa_output_arg_descriptor (insn);
+ if (TARGET_PA_20)
+ return "bve,l,n (%%r22),%%r2\n\tnop";
+ return "ble 0(%%sr4,%%r22)\n\tcopy %%r31,%%r2";
+ }
+
+ if (TARGET_PORTABLE_RUNTIME)
+ {
+ output_asm_insn ("ldil L'$$dyncall,%%r31\n\t"
+ "ldo R'$$dyncall(%%r31),%%r31", xoperands);
+ pa_output_arg_descriptor (insn);
+ return "blr %%r0,%%r2\n\tbv,n %%r0(%%r31)";
+ }
/* Now the normal case -- we can reach $$dyncall directly or
we're sure that we can get there via a long-branch stub.
No need to check target flags as the length uniquely identifies
the remaining cases. */
- if (pa_attr_length_indirect_call (insn) == 8)
+ length = pa_attr_length_indirect_call (insn);
+ if (length == 8)
{
+ pa_output_arg_descriptor (insn);
+
/* The HP linker sometimes substitutes a BLE for BL/B,L calls to
$$dyncall. Since BLE uses %r31 as the link register, the 22-bit
variant of the B,L instruction can't be used on the SOM target. */
if (TARGET_PA_20 && !TARGET_SOM)
- return ".CALL\tARGW0=GR\n\tb,l $$dyncall,%%r2\n\tcopy %%r2,%%r31";
+ return "b,l,n $$dyncall,%%r2\n\tnop";
else
- return ".CALL\tARGW0=GR\n\tbl $$dyncall,%%r31\n\tcopy %%r31,%%r2";
+ return "bl $$dyncall,%%r31\n\tcopy %%r31,%%r2";
}
/* Long millicode call, but we are not generating PIC or portable runtime
code. */
- if (pa_attr_length_indirect_call (insn) == 12)
- return ".CALL\tARGW0=GR\n\tldil L'$$dyncall,%%r2\n\tble R'$$dyncall(%%sr4,%%r2)\n\tcopy %%r31,%%r2";
-
- /* Long millicode call for portable runtime. */
- if (pa_attr_length_indirect_call (insn) == 16)
- return "ldil L'$$dyncall,%%r31\n\tldo R'$$dyncall(%%r31),%%r31\n\tblr %%r0,%%r2\n\tbv,n %%r0(%%r31)";
-
- /* We need a long PIC call to $$dyncall. */
- xoperands[0] = NULL_RTX;
- output_asm_insn ("{bl|b,l} .+8,%%r2", xoperands);
- if (TARGET_SOM || !TARGET_GAS)
+ if (length == 12)
{
- xoperands[0] = gen_label_rtx ();
- output_asm_insn ("addil L'$$dyncall-%0,%%r2", xoperands);
- targetm.asm_out.internal_label (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[0]));
- output_asm_insn ("ldo R'$$dyncall-%0(%%r1),%%r1", xoperands);
+ output_asm_insn ("ldil L'$$dyncall,%%r2", xoperands);
+ pa_output_arg_descriptor (insn);
+ return "ble R'$$dyncall(%%sr4,%%r2)\n\tcopy %%r31,%%r2";
}
- else
+
+ /* The long PIC pc-relative call sequence is five instructions. So,
+ let's use an inline version of $$dyncall when the calling sequence
+ has a roughly similar number of instructions and we are not optimizing
+ for size. We need two instructions to load the return pointer plus
+ the $$dyncall implementation. */
+ if (!optimize_size)
{
- output_asm_insn ("addil L'$$dyncall-$PIC_pcrel$0+4,%%r2", xoperands);
- output_asm_insn ("ldo R'$$dyncall-$PIC_pcrel$0+8(%%r1),%%r1",
- xoperands);
+ if (TARGET_NO_SPACE_REGS)
+ {
+ pa_output_arg_descriptor (insn);
+ output_asm_insn ("bl .+8,%%r2\n\t"
+ "ldo 20(%%r2),%%r2\n\t"
+ "extru,<> %%r22,30,1,%%r0\n\t"
+ "bv,n %%r0(%%r22)\n\t"
+ "ldw -2(%%r22),%%r21\n\t"
+ "bv %%r0(%%r21)\n\t"
+ "ldw 2(%%r22),%%r19", xoperands);
+ return "";
+ }
+ if (TARGET_PA_20)
+ {
+ pa_output_arg_descriptor (insn);
+ output_asm_insn ("bl .+8,%%r2\n\t"
+ "ldo 24(%%r2),%%r2\n\t"
+ "stw %%r2,-24(%%sp)\n\t"
+ "extru,<> %r22,30,1,%%r0\n\t"
+ "bve,n (%%r22)\n\t"
+ "ldw -2(%%r22),%%r21\n\t"
+ "bve (%%r21)\n\t"
+ "ldw 2(%%r22),%%r19", xoperands);
+ return "";
+ }
}
- output_asm_insn ("bv %%r0(%%r1)", xoperands);
- output_asm_insn ("ldo 12(%%r2),%%r2", xoperands);
- return "";
+
+ /* We need a long PIC call to $$dyncall. */
+ xoperands[0] = gen_rtx_SYMBOL_REF (Pmode, "$$dyncall");
+ xoperands[1] = gen_rtx_REG (Pmode, 2);
+ xoperands[2] = gen_rtx_REG (Pmode, 1);
+ pa_output_pic_pcrel_sequence (xoperands);
+ pa_output_arg_descriptor (insn);
+ return "bv %%r0(%%r1)\n\tldo {12|20}(%%r2),%%r2";
}
/* In HPUX 8.0's shared library scheme, special relocations are needed
HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
tree function)
{
+ const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
static unsigned int current_thunk_number;
int val_14 = VAL_14_BITS_P (delta);
unsigned int old_last_address = last_address, nbytes = 0;
- char label[16];
+ char label[17];
rtx xoperands[4];
xoperands[0] = XEXP (DECL_RTL (function), 0);
xoperands[1] = XEXP (DECL_RTL (thunk_fndecl), 0);
xoperands[2] = GEN_INT (delta);
+ assemble_start_function (thunk_fndecl, fnname);
final_start_function (emit_barrier (), file, 1);
/* Output the thunk. We know that the function is in the same
}
else if (TARGET_64BIT)
{
+ rtx xop[4];
+
/* We only have one call-clobbered scratch register, so we can't
make use of the delay slot if delta doesn't fit in 14 bits. */
if (!val_14)
output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
}
- output_asm_insn ("b,l .+8,%%r1", xoperands);
-
- if (TARGET_GAS)
- {
- output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands);
- output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1", xoperands);
- }
- else
- {
- xoperands[3] = GEN_INT (val_14 ? 8 : 16);
- output_asm_insn ("addil L'%0-%1-%3,%%r1", xoperands);
- }
+ /* Load function address into %r1. */
+ xop[0] = xoperands[0];
+ xop[1] = gen_rtx_REG (Pmode, 1);
+ xop[2] = xop[1];
+ pa_output_pic_pcrel_sequence (xop);
if (val_14)
{
output_asm_insn ("ldo R'%0(%%r1),%%r22", xoperands);
if (!val_14)
- output_asm_insn ("addil L'%2,%%r26", xoperands);
+ output_asm_insn ("ldil L'%2,%%r26", xoperands);
output_asm_insn ("bv %%r0(%%r22)", xoperands);
}
else
{
- output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+ output_asm_insn ("ldo R'%2(%%r26),%%r26", xoperands);
nbytes += 20;
}
}
}
else if (flag_pic)
{
- output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
+ rtx xop[4];
- if (TARGET_SOM || !TARGET_GAS)
- {
- output_asm_insn ("addil L'%0-%1-8,%%r1", xoperands);
- output_asm_insn ("ldo R'%0-%1-8(%%r1),%%r22", xoperands);
- }
- else
- {
- output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands);
- output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r22", xoperands);
- }
+ /* Load function address into %r22. */
+ xop[0] = xoperands[0];
+ xop[1] = gen_rtx_REG (Pmode, 1);
+ xop[2] = gen_rtx_REG (Pmode, 22);
+ pa_output_pic_pcrel_sequence (xop);
if (!val_14)
output_asm_insn ("addil L'%2,%%r26", xoperands);
if (old_last_address > last_address)
last_address = UINT_MAX;
update_total_code_bytes (nbytes);
+ assemble_end_function (thunk_fndecl, fnname);
}
/* Only direct calls to static functions are allowed to be sibling (tail)
static bool
pa_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
- if (TARGET_PORTABLE_RUNTIME)
- return false;
-
- /* Sibcalls are ok for TARGET_ELF32 as along as the linker is used in
- single subspace mode and the call is not indirect. As far as I know,
- there is no operating system support for the multiple subspace mode.
- It might be possible to support indirect calls if we didn't use
- $$dyncall (see the indirect sequence generated in pa_output_call). */
- if (TARGET_ELF32)
- return (decl != NULL_TREE);
-
/* Sibcalls are not ok because the arg pointer register is not a fixed
register. This prevents the sibcall optimization from occurring. In
addition, there are problems with stub placement using GNU ld. This
if (TARGET_64BIT)
return false;
+ if (TARGET_PORTABLE_RUNTIME)
+ return false;
+
/* Sibcalls are only ok within a translation unit. */
- return (decl && !TREE_PUBLIC (decl));
+ return decl && targetm.binds_local_p (decl);
}
/* ??? Addition is not commutative on the PA due to the weird implicit
fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT);
ASM_OUTPUT_LABEL (stream, name);
- fprintf (stream, "\t.block "HOST_WIDE_INT_PRINT_UNSIGNED"\n", size);
+ fprintf (stream, "\t.block " HOST_WIDE_INT_PRINT_UNSIGNED"\n", size);
}
/* Both the HP and GNU assemblers under HP-UX provide a .comm directive
switch_to_section (bss_section);
assemble_name (stream, name);
- fprintf (stream, "\t.comm "HOST_WIDE_INT_PRINT_UNSIGNED"\n",
+ fprintf (stream, "\t.comm " HOST_WIDE_INT_PRINT_UNSIGNED"\n",
MAX (size, align / BITS_PER_UNIT));
}
#endif
ASM_OUTPUT_LABEL (stream, name);
- fprintf (stream, "\t.block "HOST_WIDE_INT_PRINT_UNSIGNED"\n", size);
+ fprintf (stream, "\t.block " HOST_WIDE_INT_PRINT_UNSIGNED"\n", size);
}
/* Returns 1 if the 6 operands specified in OPERANDS are suitable for
}
/* Return 1 if the given constant is 2, 4, or 8. These are the valid
+ constants for a MULT embedded inside a memory address. */
+int
+pa_mem_shadd_constant_p (int val)
+{
+ if (val == 2 || val == 4 || val == 8)
+ return 1;
+ else
+ return 0;
+}
+
+/* Return 1 if the given constant is 1, 2, or 3. These are the valid
constants for shadd instructions. */
int
pa_shadd_constant_p (int val)
{
- if (val == 2 || val == 4 || val == 8)
+ if (val == 1 || val == 2 || val == 3)
return 1;
else
return 0;
|| anchor_attr == PA_COMBINE_TYPE_FMPY))
{
/* Emit the new instruction and delete the old anchor. */
- emit_insn_before (gen_rtx_PARALLEL
- (VOIDmode,
- gen_rtvec (2, PATTERN (anchor),
- PATTERN (floater))),
- anchor);
+ rtvec vtemp = gen_rtvec (2, copy_rtx (PATTERN (anchor)),
+ copy_rtx (PATTERN (floater)));
+ rtx temp = gen_rtx_PARALLEL (VOIDmode, vtemp);
+ emit_insn_before (temp, anchor);
SET_INSN_DELETED (anchor);
/* Emit a special USE insn for FLOATER, then delete
the floating insn. */
- emit_insn_before (gen_rtx_USE (VOIDmode, floater), floater);
+ temp = copy_rtx (PATTERN (floater));
+ emit_insn_before (gen_rtx_USE (VOIDmode, temp), floater);
delete_insn (floater);
continue;
else if (floater
&& anchor_attr == PA_COMBINE_TYPE_UNCOND_BRANCH)
{
- rtx temp;
/* Emit the new_jump instruction and delete the old anchor. */
- temp
- = emit_jump_insn_before (gen_rtx_PARALLEL
- (VOIDmode,
- gen_rtvec (2, PATTERN (anchor),
- PATTERN (floater))),
- anchor);
+ rtvec vtemp = gen_rtvec (2, copy_rtx (PATTERN (anchor)),
+ copy_rtx (PATTERN (floater)));
+ rtx temp = gen_rtx_PARALLEL (VOIDmode, vtemp);
+ temp = emit_jump_insn_before (temp, anchor);
JUMP_LABEL (temp) = JUMP_LABEL (anchor);
SET_INSN_DELETED (anchor);
/* Emit a special USE insn for FLOATER, then delete
the floating insn. */
- emit_insn_before (gen_rtx_USE (VOIDmode, floater), floater);
+ temp = copy_rtx (PATTERN (floater));
+ emit_insn_before (gen_rtx_USE (VOIDmode, temp), floater);
delete_insn (floater);
continue;
}
return false;
}
-/* Update the data in CUM to advance over an argument
- of mode MODE and data type TYPE.
- (TYPE is null for libcalls where that information may not be available.) */
+/* Update the data in CUM to advance over argument ARG. */
static void
-pa_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
- const_tree type, bool named ATTRIBUTE_UNUSED)
+pa_function_arg_advance (cumulative_args_t cum_v,
+ const function_arg_info &arg)
{
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
- int arg_size = FUNCTION_ARG_SIZE (mode, type);
+ int arg_size = pa_function_arg_size (arg.mode, arg.type);
cum->nargs_prototype--;
cum->words += (arg_size
+ ((cum->words & 01)
- && type != NULL_TREE
+ && arg.type != NULL_TREE
&& arg_size > 1));
}
??? We might want to restructure this so that it looks more like other
ports. */
static rtx
-pa_function_arg (cumulative_args_t cum_v, machine_mode mode,
- const_tree type, bool named ATTRIBUTE_UNUSED)
+pa_function_arg (cumulative_args_t cum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+ tree type = arg.type;
+ machine_mode mode = arg.mode;
int max_arg_words = (TARGET_64BIT ? 8 : 4);
int alignment = 0;
int arg_size;
int gpr_reg_base;
rtx retval;
- if (mode == VOIDmode)
+ if (arg.end_marker_p ())
return NULL_RTX;
- arg_size = FUNCTION_ARG_SIZE (mode, type);
+ arg_size = pa_function_arg_size (mode, type);
/* If this arg would be passed partially or totally on the stack, then
this routine should return zero. pa_arg_partial_bytes will
then this routine should return zero. */
static int
-pa_arg_partial_bytes (cumulative_args_t cum_v, machine_mode mode,
- tree type, bool named ATTRIBUTE_UNUSED)
+pa_arg_partial_bytes (cumulative_args_t cum_v, const function_arg_info &arg)
{
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
unsigned int max_arg_words = 8;
if (!TARGET_64BIT)
return 0;
- if (FUNCTION_ARG_SIZE (mode, type) > 1 && (cum->words & 1))
+ if (pa_function_arg_size (arg.mode, arg.type) > 1 && (cum->words & 1))
offset = 1;
- if (cum->words + offset + FUNCTION_ARG_SIZE (mode, type) <= max_arg_words)
+ if (cum->words + offset + pa_function_arg_size (arg.mode, arg.type)
+ <= max_arg_words)
/* Arg fits fully into registers. */
return 0;
else if (cum->words + offset >= max_arg_words)
output_section_asm_op (data);
}
-/* Implement TARGET_ASM_INITIALIZE_SECTIONS */
+/* Implement TARGET_ASM_INIT_SECTIONS. */
static void
pa_som_asm_init_sections (void)
= get_unnamed_section (0, output_section_asm_op,
"\t.SPACE $PRIVATE$\n\t.SUBSPA $TM_CLONE_TABLE$");
- /* FIXME: HPUX ld generates incorrect GOT entries for "T" fixups
- which reference data within the $TEXT$ space (for example constant
+ /* HPUX ld generates incorrect GOT entries for "T" fixups which
+ reference data within the $TEXT$ space (for example constant
strings in the $LIT$ subspace).
The assemblers (GAS and HP as) both have problems with handling
- the difference of two symbols which is the other correct way to
+ the difference of two symbols. This is the other correct way to
reference constant data during PIC code generation.
- So, there's no way to reference constant data which is in the
- $TEXT$ space during PIC generation. Instead place all constant
- data into the $PRIVATE$ subspace (this reduces sharing, but it
- works correctly). */
- readonly_data_section = flag_pic ? data_section : som_readonly_data_section;
+ Thus, we can't put constant data needing relocation in the $TEXT$
+ space during PIC generation.
+
+ Previously, we placed all constant data into the $DATA$ subspace
+ when generating PIC code. This reduces sharing, but it works
+ correctly. Now we rely on pa_reloc_rw_mask() for section selection.
+ This puts constant data not needing relocation into the $TEXT$ space. */
+ readonly_data_section = som_readonly_data_section;
/* We must not have a reference to an external symbol defined in a
shared library in a readonly section, else the SOM linker will
/* On hpux10, the linker will give an error if we have a reference
in the read-only data section to a symbol defined in a shared
- library. Therefore, expressions that might require a reloc can
- not be placed in the read-only data section. */
+ library. Therefore, expressions that might require a reloc
+ cannot be placed in the read-only data section. */
static section *
pa_select_section (tree exp, int reloc,
&& DECL_INITIAL (exp)
&& (DECL_INITIAL (exp) == error_mark_node
|| TREE_CONSTANT (DECL_INITIAL (exp)))
- && !reloc)
+ && !(reloc & pa_reloc_rw_mask ()))
{
if (TARGET_SOM
&& DECL_ONE_ONLY (exp)
else
return readonly_data_section;
}
- else if (CONSTANT_CLASS_P (exp) && !reloc)
+ else if (CONSTANT_CLASS_P (exp)
+ && !(reloc & pa_reloc_rw_mask ()))
return readonly_data_section;
else if (TARGET_SOM
&& TREE_CODE (exp) == VAR_DECL
static int
pa_reloc_rw_mask (void)
{
- /* We force (const (plus (symbol) (const_int))) to memory when the
- const_int doesn't fit in a 14-bit integer. The SOM linker can't
- handle this construct in read-only memory and we want to avoid
- this for ELF. So, we always force an RTX needing relocation to
- the data section. */
- return 3;
+ if (flag_pic || (TARGET_SOM && !TARGET_HPUX_11))
+ return 3;
+
+ /* HP linker does not support global relocs in readonly memory. */
+ return TARGET_SOM ? 2 : 0;
}
static void
extern_symbol p = {decl, name};
vec_safe_push (extern_symbols, p);
}
+#endif
/* Output text required at the end of an assembler file.
This includes deferred plabels and .import directives for
all external symbols that were actually referenced. */
static void
-pa_hpux_file_end (void)
+pa_file_end (void)
{
+#ifdef ASM_OUTPUT_EXTERNAL_REAL
unsigned int i;
extern_symbol *p;
if (!NO_DEFERRED_PROFILE_COUNTERS)
output_deferred_profile_counters ();
+#endif
output_deferred_plabels ();
+#ifdef ASM_OUTPUT_EXTERNAL_REAL
for (i = 0; vec_safe_iterate (extern_symbols, i, &p); i++)
{
tree decl = p->decl;
}
vec_free (extern_symbols);
-}
#endif
-/* Return true if a change from mode FROM to mode TO for a register
- in register class RCLASS is invalid. */
+ if (NEED_INDICATE_EXEC_STACK)
+ file_end_indicate_exec_stack ();
+}
-bool
-pa_cannot_change_mode_class (machine_mode from, machine_mode to,
- enum reg_class rclass)
+/* Implement TARGET_CAN_CHANGE_MODE_CLASS. */
+
+static bool
+pa_can_change_mode_class (machine_mode from, machine_mode to,
+ reg_class_t rclass)
{
if (from == to)
+ return true;
+
+ if (GET_MODE_SIZE (from) == GET_MODE_SIZE (to))
+ return true;
+
+ /* Reject changes to/from modes with zero size. */
+ if (!GET_MODE_SIZE (from) || !GET_MODE_SIZE (to))
return false;
/* Reject changes to/from complex and vector modes. */
if (COMPLEX_MODE_P (from) || VECTOR_MODE_P (from)
|| COMPLEX_MODE_P (to) || VECTOR_MODE_P (to))
- return true;
-
- if (GET_MODE_SIZE (from) == GET_MODE_SIZE (to))
return false;
-
- /* There is no way to load QImode or HImode values directly from
- memory. SImode loads to the FP registers are not zero extended.
- On the 64-bit target, this conflicts with the definition of
- LOAD_EXTEND_OP. Thus, we can't allow changing between modes
- with different sizes in the floating-point registers. */
+
+ /* There is no way to load QImode or HImode values directly from memory
+ to a FP register. SImode loads to the FP registers are not zero
+ extended. On the 64-bit target, this conflicts with the definition
+ of LOAD_EXTEND_OP. Thus, we reject all mode changes in the FP registers
+ except for DImode to SImode on the 64-bit target. It is handled by
+ register renaming in pa_print_operand. */
if (MAYBE_FP_REG_CLASS_P (rclass))
- return true;
+ return TARGET_64BIT && from == DImode && to == SImode;
- /* HARD_REGNO_MODE_OK places modes with sizes larger than a word
+ /* TARGET_HARD_REGNO_MODE_OK places modes with sizes larger than a word
in specific sets of registers. Thus, we cannot allow changing
to a larger mode when it's larger than a word. */
if (GET_MODE_SIZE (to) > UNITS_PER_WORD
&& GET_MODE_SIZE (to) > GET_MODE_SIZE (from))
- return true;
+ return false;
- return false;
+ return true;
}
-/* Returns TRUE if it is a good idea to tie two pseudo registers
- when one has mode MODE1 and one has mode MODE2.
- If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
- for any hard reg, then this must be FALSE for correct output.
+/* Implement TARGET_MODES_TIEABLE_P.
We should return FALSE for QImode and HImode because these modes
are not ok in the floating-point registers. However, this prevents
tieing these modes to SImode and DImode in the general registers.
- So, this isn't a good idea. We rely on HARD_REGNO_MODE_OK and
- CANNOT_CHANGE_MODE_CLASS to prevent these modes from being used
+ So, this isn't a good idea. We rely on TARGET_HARD_REGNO_MODE_OK and
+ TARGET_CAN_CHANGE_MODE_CLASS to prevent these modes from being used
in the floating-point registers. */
-bool
+static bool
pa_modes_tieable_p (machine_mode mode1, machine_mode mode2)
{
/* Don't tie modes in different classes. */
\f
/* Length in units of the trampoline instruction code. */
-#define TRAMPOLINE_CODE_SIZE (TARGET_64BIT ? 24 : (TARGET_PA_20 ? 32 : 40))
+#define TRAMPOLINE_CODE_SIZE (TARGET_64BIT ? 24 : (TARGET_PA_20 ? 36 : 48))
/* Output assembler code for a block containing the constant parts
{
if (!TARGET_64BIT)
{
- fputs ("\tldw 36(%r22),%r21\n", f);
- fputs ("\tbb,>=,n %r21,30,.+16\n", f);
- if (ASSEMBLER_DIALECT == 0)
- fputs ("\tdepi 0,31,2,%r21\n", f);
- else
- fputs ("\tdepwi 0,31,2,%r21\n", f);
- fputs ("\tldw 4(%r21),%r19\n", f);
- fputs ("\tldw 0(%r21),%r21\n", f);
if (TARGET_PA_20)
{
- fputs ("\tbve (%r21)\n", f);
- fputs ("\tldw 40(%r22),%r29\n", f);
+ fputs ("\tmfia %r20\n", f);
+ fputs ("\tldw 48(%r20),%r22\n", f);
+ fputs ("\tcopy %r22,%r21\n", f);
+ fputs ("\tbb,>=,n %r22,30,.+16\n", f);
+ fputs ("\tdepwi 0,31,2,%r22\n", f);
+ fputs ("\tldw 0(%r22),%r21\n", f);
+ fputs ("\tldw 4(%r22),%r19\n", f);
+ fputs ("\tbve (%r21)\n", f);
+ fputs ("\tldw 52(%r1),%r29\n", f);
+ fputs ("\t.word 0\n", f);
fputs ("\t.word 0\n", f);
fputs ("\t.word 0\n", f);
}
else
{
+ if (ASSEMBLER_DIALECT == 0)
+ {
+ fputs ("\tbl .+8,%r20\n", f);
+ fputs ("\tdepi 0,31,2,%r20\n", f);
+ }
+ else
+ {
+ fputs ("\tb,l .+8,%r20\n", f);
+ fputs ("\tdepwi 0,31,2,%r20\n", f);
+ }
+ fputs ("\tldw 40(%r20),%r22\n", f);
+ fputs ("\tcopy %r22,%r21\n", f);
+ fputs ("\tbb,>=,n %r22,30,.+16\n", f);
+ if (ASSEMBLER_DIALECT == 0)
+ fputs ("\tdepi 0,31,2,%r22\n", f);
+ else
+ fputs ("\tdepwi 0,31,2,%r22\n", f);
+ fputs ("\tldw 0(%r22),%r21\n", f);
+ fputs ("\tldw 4(%r22),%r19\n", f);
fputs ("\tldsid (%r21),%r1\n", f);
fputs ("\tmtsp %r1,%sr0\n", f);
- fputs ("\tbe 0(%sr0,%r21)\n", f);
- fputs ("\tldw 40(%r22),%r29\n", f);
+ fputs ("\tbe 0(%sr0,%r21)\n", f);
+ fputs ("\tldw 44(%r20),%r29\n", f);
}
fputs ("\t.word 0\n", f);
fputs ("\t.word 0\n", f);
fputs ("\t.dword 0\n", f);
fputs ("\t.dword 0\n", f);
fputs ("\tmfia %r31\n", f);
- fputs ("\tldd 24(%r31),%r1\n", f);
- fputs ("\tldd 24(%r1),%r27\n", f);
- fputs ("\tldd 16(%r1),%r1\n", f);
- fputs ("\tbve (%r1)\n", f);
+ fputs ("\tldd 24(%r31),%r27\n", f);
fputs ("\tldd 32(%r31),%r31\n", f);
+ fputs ("\tldd 16(%r27),%r1\n", f);
+ fputs ("\tbve (%r1)\n", f);
+ fputs ("\tldd 24(%r27),%r27\n", f);
fputs ("\t.dword 0 ; fptr\n", f);
fputs ("\t.dword 0 ; static link\n", f);
}
FNADDR is an RTX for the address of the function's pure code.
CXT is an RTX for the static chain value for the function.
- Move the function address to the trampoline template at offset 36.
- Move the static chain value to trampoline template at offset 40.
- Move the trampoline address to trampoline template at offset 44.
- Move r19 to trampoline template at offset 48. The latter two
+ Move the function address to the trampoline template at offset 48.
+ Move the static chain value to trampoline template at offset 52.
+ Move the trampoline address to trampoline template at offset 56.
+ Move r19 to trampoline template at offset 60. The latter two
words create a plabel for the indirect call to the trampoline.
A similar sequence is used for the 64-bit port but the plabel is
if (!TARGET_64BIT)
{
- tmp = adjust_address (m_tramp, Pmode, 36);
+ tmp = adjust_address (m_tramp, Pmode, 48);
emit_move_insn (tmp, fnaddr);
- tmp = adjust_address (m_tramp, Pmode, 40);
+ tmp = adjust_address (m_tramp, Pmode, 52);
emit_move_insn (tmp, chain_value);
/* Create a fat pointer for the trampoline. */
- tmp = adjust_address (m_tramp, Pmode, 44);
+ tmp = adjust_address (m_tramp, Pmode, 56);
emit_move_insn (tmp, r_tramp);
- tmp = adjust_address (m_tramp, Pmode, 48);
+ tmp = adjust_address (m_tramp, Pmode, 60);
emit_move_insn (tmp, gen_rtx_REG (Pmode, 19));
/* fdc and fic only use registers for the address to flush,
}
#ifdef HAVE_ENABLE_EXECUTE_STACK
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
- LCT_NORMAL, VOIDmode, 1, XEXP (m_tramp, 0), Pmode);
+ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
+ LCT_NORMAL, VOIDmode, XEXP (m_tramp, 0), Pmode);
#endif
}
/* Perform any machine-specific adjustment in the address of the trampoline.
ADDR contains the address that was passed to pa_trampoline_init.
- Adjust the trampoline address to point to the plabel at offset 44. */
+ Adjust the trampoline address to point to the plabel at offset 56. */
static rtx
pa_trampoline_adjust_address (rtx addr)
{
if (!TARGET_64BIT)
- addr = memory_address (Pmode, plus_constant (Pmode, addr, 46));
+ addr = memory_address (Pmode, plus_constant (Pmode, addr, 58));
return addr;
}
&& !reload_in_progress
&& !reload_completed
&& !LEGITIMATE_64BIT_CONST_INT_P (INTVAL (x))
- && !pa_cint_ok_for_move (INTVAL (x)))
+ && !pa_cint_ok_for_move (UINTVAL (x)))
return false;
if (function_label_operand (x, mode))
if (!TARGET_DISABLE_INDEXING
&& GET_CODE (index) == MULT
- && MODE_OK_FOR_SCALED_INDEXING_P (mode)
+ /* Only accept base operands with the REG_POINTER flag prior to
+ reload on targets with non-equivalent space registers. */
+ && (TARGET_NO_SPACE_REGS
+ || (base == XEXP (x, 1)
+ && (reload_completed
+ || (reload_in_progress && HARD_REGISTER_P (base))
+ || REG_POINTER (base))))
&& REG_P (XEXP (index, 0))
&& GET_MODE (XEXP (index, 0)) == Pmode
+ && MODE_OK_FOR_SCALED_INDEXING_P (mode)
&& (strict ? STRICT_REG_OK_FOR_INDEX_P (XEXP (index, 0))
: REG_OK_FOR_INDEX_P (XEXP (index, 0)))
&& GET_CODE (XEXP (index, 1)) == CONST_INT
{
int idx, vlen = XVECLEN (body, 0);
+ if (!TARGET_SOM)
+ fputs ("\t.align 4\n", asm_out_file);
targetm.asm_out.internal_label (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
if (TARGET_GAS)
fputs ("\t.begin_brtab\n", asm_out_file);
fputs ("\t.end_brtab\n", asm_out_file);
}
+/* This is a helper function for the other atomic operations. This function
+ emits a loop that contains SEQ that iterates until a compare-and-swap
+ operation at the end succeeds. MEM is the memory to be modified. SEQ is
+ a set of instructions that takes a value from OLD_REG as an input and
+ produces a value in NEW_REG as an output. Before SEQ, OLD_REG will be
+ set to the current contents of MEM. After SEQ, a compare-and-swap will
+ attempt to update MEM with NEW_REG. The function returns true when the
+ loop was generated successfully. */
+
+static bool
+pa_expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq)
+{
+ machine_mode mode = GET_MODE (mem);
+ rtx_code_label *label;
+ rtx cmp_reg, success, oldval;
+
+ /* The loop we want to generate looks like
+
+ cmp_reg = mem;
+ label:
+ old_reg = cmp_reg;
+ seq;
+ (success, cmp_reg) = compare-and-swap(mem, old_reg, new_reg)
+ if (success)
+ goto label;
+
+ Note that we only do the plain load from memory once. Subsequent
+ iterations use the value loaded by the compare-and-swap pattern. */
+
+ label = gen_label_rtx ();
+ cmp_reg = gen_reg_rtx (mode);
+
+ emit_move_insn (cmp_reg, mem);
+ emit_label (label);
+ emit_move_insn (old_reg, cmp_reg);
+ if (seq)
+ emit_insn (seq);
+
+ success = NULL_RTX;
+ oldval = cmp_reg;
+ if (!expand_atomic_compare_and_swap (&success, &oldval, mem, old_reg,
+ new_reg, false, MEMMODEL_SYNC_SEQ_CST,
+ MEMMODEL_RELAXED))
+ return false;
+
+ if (oldval != cmp_reg)
+ emit_move_insn (cmp_reg, oldval);
+
+ /* Mark this jump predicted not taken. */
+ emit_cmp_and_jump_insns (success, const0_rtx, EQ, const0_rtx,
+ GET_MODE (success), 1, label,
+ profile_probability::guessed_never ());
+ return true;
+}
+
+/* This function tries to implement an atomic exchange operation using a
+ compare_and_swap loop. VAL is written to *MEM. The previous contents of
+ *MEM are returned, using TARGET if possible. No memory model is required
+ since a compare_and_swap loop is seq-cst. */
+
+rtx
+pa_maybe_emit_compare_and_swap_exchange_loop (rtx target, rtx mem, rtx val)
+{
+ machine_mode mode = GET_MODE (mem);
+
+ if (can_compare_and_swap_p (mode, true))
+ {
+ if (!target || !register_operand (target, mode))
+ target = gen_reg_rtx (mode);
+ if (pa_expand_compare_and_swap_loop (mem, target, val, NULL_RTX))
+ return target;
+ }
+
+ return NULL_RTX;
+}
+
+/* Implement TARGET_CALLEE_COPIES. The callee is responsible for copying
+ arguments passed by hidden reference in the 32-bit HP runtime. Users
+ can override this behavior for better compatibility with openmp at the
+ risk of library incompatibilities. Arguments are always passed by value
+ in the 64-bit HP runtime. */
+
+static bool
+pa_callee_copies (cumulative_args_t, const function_arg_info &)
+{
+ return !TARGET_CALLER_COPIES;
+}
+
+/* Implement TARGET_HARD_REGNO_NREGS. */
+
+static unsigned int
+pa_hard_regno_nregs (unsigned int regno ATTRIBUTE_UNUSED, machine_mode mode)
+{
+ return PA_HARD_REGNO_NREGS (regno, mode);
+}
+
+/* Implement TARGET_HARD_REGNO_MODE_OK. */
+
+static bool
+pa_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
+{
+ return PA_HARD_REGNO_MODE_OK (regno, mode);
+}
+
+/* Implement TARGET_STARTING_FRAME_OFFSET.
+
+ On the 32-bit ports, we reserve one slot for the previous frame
+ pointer and one fill slot. The fill slot is for compatibility
+ with HP compiled programs. On the 64-bit ports, we reserve one
+ slot for the previous frame pointer. */
+
+static HOST_WIDE_INT
+pa_starting_frame_offset (void)
+{
+ return 8;
+}
+
+/* Figure out the size in words of the function argument. The size
+ returned by this function should always be greater than zero because
+ we pass variable and zero sized objects by reference. */
+
+HOST_WIDE_INT
+pa_function_arg_size (machine_mode mode, const_tree type)
+{
+ HOST_WIDE_INT size;
+
+ size = mode != BLKmode ? GET_MODE_SIZE (mode) : int_size_in_bytes (type);
+ return CEIL (size, UNITS_PER_WORD);
+}
+
#include "gt-pa.h"