/* Subroutines for insn-output.c for HPPA.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003 Free Software Foundation, Inc.
+ 2002, 2003, 2004 Free Software Foundation, Inc.
Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c
This file is part of GCC.
#include "reload.h"
#include "integrate.h"
#include "function.h"
-#include "obstack.h"
#include "toplev.h"
#include "ggc.h"
#include "recog.h"
#include "target.h"
#include "target-def.h"
-static int hppa_use_dfa_pipeline_interface (void);
-
-#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
-#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE hppa_use_dfa_pipeline_interface
-
-static int
-hppa_use_dfa_pipeline_interface (void)
-{
- return 1;
-}
-
/* Return nonzero if there is a bypass for the output of
OUT_INSN and the fp store IN_INSN. */
int
#endif
#endif
+static void copy_reg_pointer (rtx, rtx);
+static void fix_range (const char *);
static int hppa_address_cost (rtx);
static bool hppa_rtx_costs (rtx, int, int, int *);
static inline rtx force_mode (enum machine_mode, rtx);
static int forward_branch_p (rtx);
static int shadd_constant_p (int);
static void compute_zdepwi_operands (unsigned HOST_WIDE_INT, unsigned *);
-static int compute_movstr_length (rtx);
-static int compute_clrstr_length (rtx);
+static int compute_movmem_length (rtx);
+static int compute_clrmem_length (rtx);
static bool pa_assemble_integer (rtx, unsigned int, int);
static void remove_useless_addtr_insns (int);
-static void store_reg (int, int, int);
-static void store_reg_modify (int, int, int);
-static void load_reg (int, int, int);
-static void set_reg_plus_d (int, int, int, int);
+static void store_reg (int, HOST_WIDE_INT, int);
+static void store_reg_modify (int, int, HOST_WIDE_INT);
+static void load_reg (int, HOST_WIDE_INT, int);
+static void set_reg_plus_d (int, int, HOST_WIDE_INT, int);
static void pa_output_function_prologue (FILE *, HOST_WIDE_INT);
static void update_total_code_bytes (int);
static void pa_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void pa_asm_out_destructor (rtx, int);
#endif
static void pa_init_builtins (void);
+static rtx hppa_builtin_saveregs (void);
+static tree hppa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
static void copy_fp_args (rtx) ATTRIBUTE_UNUSED;
static int length_fp_args (rtx) ATTRIBUTE_UNUSED;
static struct deferred_plabel *get_plabel (const char *)
#ifdef HPUX_LONG_DOUBLE_LIBRARY
static void pa_hpux_init_libfuncs (void);
#endif
+static rtx pa_struct_value_rtx (tree, int);
+static bool pa_pass_by_reference (CUMULATIVE_ARGS *ca, enum machine_mode,
+ tree, bool);
+static struct machine_function * pa_init_machine_status (void);
+
/* Save the operands last given to a compare for use when we
generate a scc or bcc insn. */
rtx hppa_compare_op0, hppa_compare_op1;
enum cmp_type hppa_branch_type;
-/* Which cpu we are scheduling for. */
-enum processor_type pa_cpu;
-
-/* String to hold which cpu we are scheduling for. */
-const char *pa_cpu_string;
-
/* Which architecture we are generating code for. */
enum architecture_type pa_arch;
/* String to hold which architecture we are generating code for. */
const char *pa_arch_string;
+/* String used with the -mfixed-range= option. */
+const char *pa_fixed_range_string;
+
+/* Which cpu we are scheduling for. */
+enum processor_type pa_cpu;
+
+/* String to hold which cpu we are scheduling for. */
+const char *pa_cpu_string;
+
/* Counts for the number of callee-saved general and floating point
registers which were saved by the current function's prologue. */
static int gr_saved, fr_saved;
#define TARGET_INIT_LIBFUNCS pa_hpux_init_libfuncs
#endif
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX pa_struct_value_rtx
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY pa_return_in_memory
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE pa_pass_by_reference
+
+#undef TARGET_EXPAND_BUILTIN_SAVEREGS
+#define TARGET_EXPAND_BUILTIN_SAVEREGS hppa_builtin_saveregs
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR hppa_gimplify_va_arg_expr
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
+/* Parse the -mfixed-range= option string. */
+
+static void
+fix_range (const char *const_str)
+{
+ int i, first, last;
+ char *str, *dash, *comma;
+
+ /* str must be of the form REG1'-'REG2{,REG1'-'REG} where REG1 and
+ REG2 are either register names or register numbers. The effect
+ of this option is to mark the registers in the range from REG1 to
+ REG2 as ``fixed'' so they won't be used by the compiler. This is
+ used, e.g., to ensure that kernel mode code doesn't use f32-f127. */
+
+ i = strlen (const_str);
+ str = (char *) alloca (i + 1);
+ memcpy (str, const_str, i + 1);
+
+ while (1)
+ {
+ dash = strchr (str, '-');
+ if (!dash)
+ {
+ warning ("value of -mfixed-range must have form REG1-REG2");
+ return;
+ }
+ *dash = '\0';
+
+ comma = strchr (dash + 1, ',');
+ if (comma)
+ *comma = '\0';
+
+ first = decode_reg_name (str);
+ if (first < 0)
+ {
+ warning ("unknown register name: %s", str);
+ return;
+ }
+
+ last = decode_reg_name (dash + 1);
+ if (last < 0)
+ {
+ warning ("unknown register name: %s", dash + 1);
+ return;
+ }
+
+ *dash = '-';
+
+ if (first > last)
+ {
+ warning ("%s-%s is an empty range", str, dash + 1);
+ return;
+ }
+
+ for (i = first; i <= last; ++i)
+ fixed_regs[i] = call_used_regs[i] = 1;
+
+ if (!comma)
+ break;
+
+ *comma = ',';
+ str = comma + 1;
+ }
+
+ /* Check if all floating point registers have been fixed. */
+ for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
+ if (!fixed_regs[i])
+ break;
+
+ if (i > FP_REG_LAST)
+ target_flags |= MASK_DISABLE_FPREGS;
+}
+
void
override_options (void)
{
warning ("unknown -mschedule= option (%s).\nValid options are 700, 7100, 7100LC, 7200, 7300, and 8000\n", pa_cpu_string);
}
- /* Set the instruction set architecture. */
+ /* Set the instruction architecture. */
if (pa_arch_string && ! strcmp (pa_arch_string, "1.0"))
{
pa_arch_string = "1.0";
warning ("unknown -march= option (%s).\nValid options are 1.0, 1.1, and 2.0\n", pa_arch_string);
}
+ if (pa_fixed_range_string)
+ fix_range (pa_fixed_range_string);
+
/* Unconditional branches in the delay slot are not compatible with dwarf2
call frame information. There is no benefit in using this optimization
on PA8000 and later processors. */
targetm.asm_out.unaligned_op.si = NULL;
targetm.asm_out.unaligned_op.di = NULL;
}
+
+ init_machine_status = pa_init_machine_status;
}
static void
#endif
}
+/* Function to init struct machine_function.
+ This will be called, via a pointer variable,
+ from push_function_context. */
+
+static struct machine_function *
+pa_init_machine_status (void)
+{
+ return ggc_alloc_cleared (sizeof (machine_function));
+}
+
+/* If FROM is a probable pointer register, mark TO as a probable
+ pointer register with the same pointer alignment as FROM. */
+
+static void
+copy_reg_pointer (rtx to, rtx from)
+{
+ if (REG_POINTER (from))
+ mark_reg_pointer (to, REGNO_POINTER_ALIGN (REGNO (from)));
+}
+
/* Return nonzero only if OP is a register of mode MODE,
or CONST0_RTX. */
int
|| GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF);
}
-/* Return 1 if the operand is either a register or a memory operand that is
- not symbolic. */
-
-int
-reg_or_nonsymb_mem_operand (rtx op, enum machine_mode mode)
-{
- if (register_operand (op, mode))
- return 1;
-
- if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
- return 1;
-
- return 0;
-}
-
/* Return 1 if the operand is either a register, zero, or a memory operand
that is not symbolic. */
if (op == CONST0_RTX (mode))
return 1;
- if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
- return 1;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
- return 0;
+ if (GET_CODE (op) != MEM)
+ return 0;
+
+ /* Until problems with management of the REG_POINTER flag are resolved,
+ we need to delay creating move insns with unscaled indexed addresses
+ until CSE is not expected. */
+ if (!TARGET_NO_SPACE_REGS
+ && !cse_not_expected
+ && GET_CODE (XEXP (op, 0)) == PLUS
+ && REG_P (XEXP (XEXP (op, 0), 0))
+ && REG_P (XEXP (XEXP (op, 0), 1)))
+ return 0;
+
+ return (!symbolic_memory_operand (op, mode)
+ && memory_address_p (mode, XEXP (op, 0)));
}
/* Return 1 if the operand is a register operand or a non-symbolic memory
if (reload_completed
&& memory_operand (op, mode)
- && ! symbolic_memory_operand (op, mode))
+ && !symbolic_memory_operand (op, mode))
return 1;
return 0;
|| CONST_OK_FOR_LETTER_P (intval, 'K'));
}
-/* Accept anything that can be moved in one instruction into a general
- register. */
+/* Return 1 iff OP is an indexed memory operand. */
int
-move_operand (rtx op, enum machine_mode mode)
+indexed_memory_operand (rtx op, enum machine_mode mode)
+{
+ if (GET_MODE (op) != mode)
+ return 0;
+
+ /* Before reload, a (SUBREG (MEM...)) forces reloading into a register. */
+ if (reload_completed && GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+
+ if (GET_CODE (op) != MEM || symbolic_memory_operand (op, mode))
+ return 0;
+
+ op = XEXP (op, 0);
+
+ return (memory_address_p (mode, op) && IS_INDEX_ADDR_P (op));
+}
+
+/* Accept anything that can be used as a destination operand for a
+ move instruction. We don't accept indexed memory operands since
+ they are supported only for floating point stores. */
+int
+move_dest_operand (rtx op, enum machine_mode mode)
{
if (register_operand (op, mode))
return 1;
- if (GET_CODE (op) == CONSTANT_P_RTX)
+ if (GET_MODE (op) != mode)
+ return 0;
+
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+
+ if (GET_CODE (op) != MEM || symbolic_memory_operand (op, mode))
+ return 0;
+
+ op = XEXP (op, 0);
+
+ return (memory_address_p (mode, op)
+ && !IS_INDEX_ADDR_P (op)
+ && !IS_LO_SUM_DLT_ADDR_P (op));
+}
+
+/* Accept anything that can be used as a source operand for a move
+ instruction. */
+int
+move_src_operand (rtx op, enum machine_mode mode)
+{
+ if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT)
return cint_ok_for_move (INTVAL (op));
+ if (GET_MODE (op) != mode)
+ return 0;
+
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
+
if (GET_CODE (op) != MEM)
return 0;
- op = XEXP (op, 0);
+ /* Until problems with management of the REG_POINTER flag are resolved,
+ we need to delay creating move insns with unscaled indexed addresses
+ until CSE is not expected. */
+ if (!TARGET_NO_SPACE_REGS
+ && !cse_not_expected
+ && GET_CODE (XEXP (op, 0)) == PLUS
+ && REG_P (XEXP (XEXP (op, 0), 0))
+ && REG_P (XEXP (XEXP (op, 0), 1)))
+ return 0;
- /* We consider a LO_SUM DLT reference a move_operand now since it has
- been merged into the normal movsi/movdi patterns. */
- if (GET_CODE (op) == LO_SUM
- && GET_CODE (XEXP (op, 0)) == REG
- && REG_OK_FOR_BASE_P (XEXP (op, 0))
- && GET_CODE (XEXP (op, 1)) == UNSPEC
- && GET_MODE (op) == Pmode)
- return 1;
+ return memory_address_p (mode, XEXP (op, 0));
+}
- /* Since move_operand is only used for source operands, we can always
- allow scaled indexing! */
- if (! TARGET_DISABLE_INDEXING
- && GET_CODE (op) == PLUS
- && ((GET_CODE (XEXP (op, 0)) == MULT
- && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
- && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT
- && INTVAL (XEXP (XEXP (op, 0), 1))
- == (HOST_WIDE_INT) GET_MODE_SIZE (mode)
- && GET_CODE (XEXP (op, 1)) == REG)
- || (GET_CODE (XEXP (op, 1)) == MULT
- &&GET_CODE (XEXP (XEXP (op, 1), 0)) == REG
- && GET_CODE (XEXP (XEXP (op, 1), 1)) == CONST_INT
- && INTVAL (XEXP (XEXP (op, 1), 1))
- == (HOST_WIDE_INT) GET_MODE_SIZE (mode)
- && GET_CODE (XEXP (op, 0)) == REG)))
- return 1;
+/* Accept anything that can be used as the source operand for a prefetch
+ instruction. */
+int
+prefetch_operand (rtx op, enum machine_mode mode)
+{
+ if (GET_CODE (op) != MEM)
+ return 0;
- return memory_address_p (mode, op);
+ /* Until problems with management of the REG_POINTER flag are resolved,
+ we need to delay creating prefetch insns with unscaled indexed addresses
+ until CSE is not expected. */
+ if (!TARGET_NO_SPACE_REGS
+ && !cse_not_expected
+ && GET_CODE (XEXP (op, 0)) == PLUS
+ && REG_P (XEXP (XEXP (op, 0), 0))
+ && REG_P (XEXP (XEXP (op, 0), 1)))
+ return 0;
+
+ return memory_address_p (mode, XEXP (op, 0));
}
/* Accept REG and any CONST_INT that can be moved in one instruction into a
if (register_operand (op, mode))
return 1;
- if (GET_CODE (op) == CONST_INT)
- return cint_ok_for_move (INTVAL (op));
-
- return 0;
+ return (GET_CODE (op) == CONST_INT && cint_ok_for_move (INTVAL (op)));
}
int
So instead we just emit the raw set, which avoids the movXX
expanders completely. */
+ mark_reg_pointer (reg, BITS_PER_UNIT);
emit_insn (gen_rtx_SET (VOIDmode, reg, orig));
current_function_uses_pic_offset_table = 1;
return reg;
gen_rtx_LO_SUM (Pmode, tmp_reg,
gen_rtx_UNSPEC (Pmode,
gen_rtvec (1, orig),
- 0)));
+ UNSPEC_DLTIND14R)));
current_function_uses_pic_offset_table = 1;
MEM_NOTRAP_P (pic_ref) = 1;
RTX_UNCHANGING_P (pic_ref) = 1;
+ mark_reg_pointer (reg, BITS_PER_UNIT);
insn = emit_move_insn (reg, pic_ref);
/* Put a REG_EQUAL note on this insn, so that it can be optimized. */
orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
base == reg ? 0 : reg);
}
- else abort ();
+ else
+ abort ();
+
if (GET_CODE (orig) == CONST_INT)
{
if (INT_14_BITS (orig))
pic_ref = gen_rtx_PLUS (Pmode, base, orig);
/* Likewise, should we set special REG_NOTEs here? */
}
+
return pic_ref;
}
{
rtx orig = x;
+ /* We need to canonicalize the order of operands in unscaled indexed
+ addresses since the code that checks if an address is valid doesn't
+ always try both orders. */
+ if (!TARGET_NO_SPACE_REGS
+ && GET_CODE (x) == PLUS
+ && GET_MODE (x) == Pmode
+ && REG_P (XEXP (x, 0))
+ && REG_P (XEXP (x, 1))
+ && REG_POINTER (XEXP (x, 0))
+ && !REG_POINTER (XEXP (x, 1)))
+ return gen_rtx_PLUS (Pmode, XEXP (x, 1), XEXP (x, 0));
+
if (flag_pic)
return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode));
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))
- && (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == 'o'
+ && (OBJECT_P (XEXP (x, 1))
|| GET_CODE (XEXP (x, 1)) == SUBREG)
&& GET_CODE (XEXP (x, 1)) != CONST)
{
register rtx operand1 = operands[1];
register rtx tem;
+ /* We can only handle indexed addresses in the destination operand
+ of floating point stores. Thus, we need to break out indexed
+ addresses from the destination operand. */
+ if (GET_CODE (operand0) == MEM && IS_INDEX_ADDR_P (XEXP (operand0, 0)))
+ {
+ /* This is only safe up to the beginning of life analysis. */
+ if (no_new_pseudos)
+ abort ();
+
+ tem = copy_to_mode_reg (Pmode, XEXP (operand0, 0));
+ operand0 = replace_equiv_address (operand0, tem);
+ }
+
+ /* On targets with non-equivalent space registers, break out unscaled
+ indexed addresses from the source operand before the final CSE.
+ We have to do this because the REG_POINTER flag is not correctly
+ carried through various optimization passes and CSE may substitute
+ a pseudo without the pointer set for one with the pointer set. As
+ a result, we loose various opportunities to create insns with
+ unscaled indexed addresses. */
+ if (!TARGET_NO_SPACE_REGS
+ && !cse_not_expected
+ && GET_CODE (operand1) == MEM
+ && GET_CODE (XEXP (operand1, 0)) == PLUS
+ && REG_P (XEXP (XEXP (operand1, 0), 0))
+ && REG_P (XEXP (XEXP (operand1, 0), 1)))
+ operand1
+ = replace_equiv_address (operand1,
+ copy_to_mode_reg (Pmode, XEXP (operand1, 0)));
+
if (scratch_reg
&& reload_in_progress && GET_CODE (operand0) == REG
&& REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
&& ((tem = find_replacement (&XEXP (operand0, 0)))
!= XEXP (operand0, 0)))
operand0 = gen_rtx_MEM (GET_MODE (operand0), tem);
+
if (scratch_reg && reload_in_progress && GET_CODE (operand1) == MEM
&& ((tem = find_replacement (&XEXP (operand1, 0)))
!= XEXP (operand1, 0)))
operand1 = gen_rtx_MEM (GET_MODE (operand1), tem);
/* Handle secondary reloads for loads/stores of FP registers from
- REG+D addresses where D does not fit in 5 bits, including
+ REG+D addresses where D does not fit in 5 or 14 bits, including
(subreg (mem (addr))) cases. */
- if (fp_reg_operand (operand0, mode)
+ if (scratch_reg
+ && fp_reg_operand (operand0, mode)
&& ((GET_CODE (operand1) == MEM
- && ! memory_address_p (DFmode, XEXP (operand1, 0)))
+ && !memory_address_p ((GET_MODE_SIZE (mode) == 4 ? SFmode : DFmode),
+ XEXP (operand1, 0)))
|| ((GET_CODE (operand1) == SUBREG
&& GET_CODE (XEXP (operand1, 0)) == MEM
- && !memory_address_p (DFmode, XEXP (XEXP (operand1, 0), 0)))))
- && scratch_reg)
+ && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+ ? SFmode : DFmode),
+ XEXP (XEXP (operand1, 0), 0))))))
{
if (GET_CODE (operand1) == SUBREG)
operand1 = XEXP (operand1, 0);
if (!memory_address_p (Pmode, XEXP (operand1, 0)))
{
emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1));
- emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)),
- Pmode,
- XEXP (XEXP (operand1, 0), 0),
- scratch_reg));
+ emit_move_insn (scratch_reg,
+ gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)),
+ Pmode,
+ XEXP (XEXP (operand1, 0), 0),
+ scratch_reg));
}
else
emit_move_insn (scratch_reg, XEXP (operand1, 0));
gen_rtx_MEM (mode, scratch_reg)));
return 1;
}
- else if (fp_reg_operand (operand1, mode)
+ else if (scratch_reg
+ && fp_reg_operand (operand1, mode)
&& ((GET_CODE (operand0) == MEM
- && ! memory_address_p (DFmode, XEXP (operand0, 0)))
+ && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+ ? SFmode : DFmode),
+ XEXP (operand0, 0)))
|| ((GET_CODE (operand0) == SUBREG)
&& GET_CODE (XEXP (operand0, 0)) == MEM
- && !memory_address_p (DFmode, XEXP (XEXP (operand0, 0), 0))))
- && scratch_reg)
+ && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+ ? SFmode : DFmode),
+ XEXP (XEXP (operand0, 0), 0)))))
{
if (GET_CODE (operand0) == SUBREG)
operand0 = XEXP (operand0, 0);
/* Handle secondary reloads for loads of FP registers from constant
expressions by forcing the constant into memory.
- use scratch_reg to hold the address of the memory location.
+ Use scratch_reg to hold the address of the memory location.
The proper fix is to change PREFERRED_RELOAD_CLASS to return
NO_REGS when presented with a const_int and a register class
containing only FP registers. Doing so unfortunately creates
more problems than it solves. Fix this for 2.5. */
- else if (fp_reg_operand (operand0, mode)
+ else if (scratch_reg
&& CONSTANT_P (operand1)
- && scratch_reg)
+ && fp_reg_operand (operand0, mode))
{
rtx xoperands[2];
}
/* Handle secondary reloads for SAR. These occur when trying to load
the SAR from memory, FP register, or with a constant. */
- else if (GET_CODE (operand0) == REG
+ else if (scratch_reg
+ && GET_CODE (operand0) == REG
&& REGNO (operand0) < FIRST_PSEUDO_REGISTER
&& REGNO_REG_CLASS (REGNO (operand0)) == SHIFT_REGS
&& (GET_CODE (operand1) == MEM
|| GET_CODE (operand1) == CONST_INT
|| (GET_CODE (operand1) == REG
- && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1)))))
- && scratch_reg)
+ && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1))))))
{
/* D might not fit in 14 bits either; for such cases load D into
scratch reg. */
emit_move_insn (operand0, scratch_reg);
return 1;
}
- /* Handle most common case: storing into a register. */
+ /* Handle the most common case: storing into a register. */
else if (register_operand (operand0, mode))
{
if (register_operand (operand1, mode)
/* Only `general_operands' can come here, so MEM is ok. */
|| GET_CODE (operand1) == MEM)
{
- /* Run this case quickly. */
+ /* Various sets are created during RTL generation which don't
+ have the REG_POINTER flag correctly set. After the CSE pass,
+ instruction recognition can fail if we don't consistently
+ set this flag when performing register copies. This should
+ also improve the opportunities for creating insns that use
+ unscaled indexing. */
+ if (REG_P (operand0) && REG_P (operand1))
+ {
+ if (REG_POINTER (operand1)
+ && !REG_POINTER (operand0)
+ && !HARD_REGISTER_P (operand0))
+ copy_reg_pointer (operand0, operand1);
+ else if (REG_POINTER (operand0)
+ && !REG_POINTER (operand1)
+ && !HARD_REGISTER_P (operand1))
+ copy_reg_pointer (operand1, operand0);
+ }
+
+ /* When MEMs are broken out, the REG_POINTER flag doesn't
+ get set. In some cases, we can set the REG_POINTER flag
+ from the declaration for the MEM. */
+ if (REG_P (operand0)
+ && GET_CODE (operand1) == MEM
+ && !REG_POINTER (operand0))
+ {
+ tree decl = MEM_EXPR (operand1);
+
+ /* Set the register pointer flag and register alignment
+ if the declaration for this memory reference is a
+ pointer type. Fortran indirect argument references
+ are ignored. */
+ if (decl
+ && !(flag_argument_noalias > 1
+ && TREE_CODE (decl) == INDIRECT_REF
+ && TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL))
+ {
+ tree type;
+
+ /* If this is a COMPONENT_REF, use the FIELD_DECL from
+ tree operand 1. */
+ if (TREE_CODE (decl) == COMPONENT_REF)
+ decl = TREE_OPERAND (decl, 1);
+
+ type = TREE_TYPE (decl);
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ type = get_inner_array_type (type);
+
+ if (POINTER_TYPE_P (type))
+ {
+ int align;
+
+ type = TREE_TYPE (type);
+ /* Using TYPE_ALIGN_OK is rather conservative as
+ only the ada frontend actually sets it. */
+ align = (TYPE_ALIGN_OK (type) ? TYPE_ALIGN (type)
+ : BITS_PER_UNIT);
+ mark_reg_pointer (operand0, align);
+ }
+ }
+ }
+
emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1));
return 1;
}
operands[1] = force_const_mem (mode, operand1);
operands[1] = legitimize_pic_address (XEXP (operands[1], 0),
mode, temp);
+ operands[1] = gen_rtx_MEM (mode, operands[1]);
emit_move_sequence (operands, mode, temp);
}
else
{
operands[1] = legitimize_pic_address (operand1, mode, temp);
+ if (REG_P (operand0) && REG_P (operands[1]))
+ copy_reg_pointer (operand0, operands[1]);
emit_insn (gen_rtx_SET (VOIDmode, operand0, operands[1]));
}
}
Don't mark hard registers though. That loses. */
if (GET_CODE (operand0) == REG
&& REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
- REG_POINTER (operand0) = 1;
+ mark_reg_pointer (operand0, BITS_PER_UNIT);
if (REGNO (temp) >= FIRST_PSEUDO_REGISTER)
- REG_POINTER (temp) = 1;
+ mark_reg_pointer (temp, BITS_PER_UNIT);
+
if (ishighonly)
set = gen_rtx_SET (mode, operand0, temp);
else
return 1;
}
else if (GET_CODE (operand1) != CONST_INT
- || ! cint_ok_for_move (INTVAL (operand1)))
+ || !cint_ok_for_move (INTVAL (operand1)))
{
- rtx extend = NULL_RTX;
- rtx temp;
+ rtx insn, temp;
+ rtx op1 = operand1;
+ HOST_WIDE_INT value = 0;
+ HOST_WIDE_INT insv = 0;
+ int insert = 0;
+
+ if (GET_CODE (operand1) == CONST_INT)
+ value = INTVAL (operand1);
- if (TARGET_64BIT && GET_CODE (operand1) == CONST_INT
+ if (TARGET_64BIT
+ && GET_CODE (operand1) == CONST_INT
&& HOST_BITS_PER_WIDE_INT > 32
&& GET_MODE_BITSIZE (GET_MODE (operand0)) > 32)
{
- HOST_WIDE_INT val = INTVAL (operand1);
HOST_WIDE_INT nval;
/* Extract the low order 32 bits of the value and sign extend.
can use the original value as-is. If the new value is
different, we use it and insert the most-significant 32-bits
of the original value into the final result. */
- nval = ((val & (((HOST_WIDE_INT) 2 << 31) - 1))
+ nval = ((value & (((HOST_WIDE_INT) 2 << 31) - 1))
^ ((HOST_WIDE_INT) 1 << 31)) - ((HOST_WIDE_INT) 1 << 31);
- if (val != nval)
+ if (value != nval)
{
#if HOST_BITS_PER_WIDE_INT > 32
- extend = GEN_INT (val >> 32);
+ insv = value >= 0 ? value >> 32 : ~(~value >> 32);
#endif
+ insert = 1;
+ value = nval;
operand1 = GEN_INT (nval);
}
}
if (reload_in_progress || reload_completed)
- temp = operand0;
+ temp = scratch_reg ? scratch_reg : operand0;
else
temp = gen_reg_rtx (mode);
because PLUS uses an 11-bit immediate and the insn sequence
generated is not as efficient as the one using HIGH/LO_SUM. */
if (GET_CODE (operand1) == CONST_INT
- && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && !insert)
{
/* Directly break constant into high and low parts. This
provides better optimization opportunities because various
We use a 14-bit signed low part except when the addition
of 0x4000 to the high part might change the sign of the
high part. */
- HOST_WIDE_INT value = INTVAL (operand1);
HOST_WIDE_INT low = value & 0x3fff;
HOST_WIDE_INT high = value & ~ 0x3fff;
operands[1] = gen_rtx_LO_SUM (mode, temp, operand1);
}
- emit_move_insn (operands[0], operands[1]);
+ insn = emit_move_insn (operands[0], operands[1]);
+
+ /* Now insert the most significant 32 bits of the value
+ into the register. When we don't have a second register
+ available, it could take up to nine instructions to load
+ a 64-bit integer constant. Prior to reload, we force
+ constants that would take more than three instructions
+ to load to the constant pool. During and after reload,
+ we have to handle all possible values. */
+ if (insert)
+ {
+ /* Use a HIGH/LO_SUM/INSV sequence if we have a second
+ register and the value to be inserted is outside the
+ range that can be loaded with three depdi instructions. */
+ if (temp != operand0 && (insv >= 16384 || insv < -16384))
+ {
+ operand1 = GEN_INT (insv);
+
+ emit_insn (gen_rtx_SET (VOIDmode, temp,
+ gen_rtx_HIGH (mode, operand1)));
+ emit_move_insn (temp, gen_rtx_LO_SUM (mode, temp, operand1));
+ emit_insn (gen_insv (operand0, GEN_INT (32),
+ const0_rtx, temp));
+ }
+ else
+ {
+ int len = 5, pos = 27;
+
+ /* Insert the bits using the depdi instruction. */
+ while (pos >= 0)
+ {
+ HOST_WIDE_INT v5 = ((insv & 31) ^ 16) - 16;
+ HOST_WIDE_INT sign = v5 < 0;
+
+ /* Left extend the insertion. */
+ insv = (insv >= 0 ? insv >> len : ~(~insv >> len));
+ while (pos > 0 && (insv & 1) == sign)
+ {
+ insv = (insv >= 0 ? insv >> 1 : ~(~insv >> 1));
+ len += 1;
+ pos -= 1;
+ }
- if (extend != NULL_RTX)
- emit_insn (gen_insv (operands[0], GEN_INT (32), const0_rtx,
- extend));
+ emit_insn (gen_insv (operand0, GEN_INT (len),
+ GEN_INT (pos), GEN_INT (v5)));
+
+ len = pos > 0 && pos < 5 ? pos : 5;
+ pos -= len;
+ }
+ }
+ }
+
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_EQUAL, op1, REG_NOTES (insn));
return 1;
}
\f
/* Return the best assembler insn template
- for moving operands[1] into operands[0] as a fullword. */
+ for moving operands[1] into operands[0] as a fullword. */
const char *
singlemove_string (rtx *operands)
{
OPERANDS[3] is a register for temporary storage.
OPERANDS[4] is the size as a CONST_INT
OPERANDS[5] is the alignment safe to use, as a CONST_INT.
- OPERANDS[6] is another temporary register. */
+ OPERANDS[6] is another temporary register. */
const char *
output_block_move (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
count insns rather than emit them. */
static int
-compute_movstr_length (rtx insn)
+compute_movmem_length (rtx insn)
{
rtx pat = PATTERN (insn);
unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 7), 0));
count insns rather than emit them. */
static int
-compute_clrstr_length (rtx insn)
+compute_clrmem_length (rtx insn)
{
rtx pat = PATTERN (insn);
unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 4), 0));
{
int i;
int chars_output;
- unsigned char partial_output[16]; /* Max space 4 chars can occupy. */
+ unsigned char partial_output[16]; /* Max space 4 chars can occupy. */
/* The HP assembler can only take strings of 256 characters at one
time. This is a limitation on input line length, *not* the
/* Global variables set by output_function_prologue(). */
/* Size of frame. Need to know this to emit return insns from
leaf procedures. */
-static int actual_fsize;
-static int local_fsize, save_fregs;
+static HOST_WIDE_INT actual_fsize, local_fsize;
+static int save_fregs;
/* Emit RTL to store REG at the memory location specified by BASE+DISP.
Handle case where DISP > 8k by using the add_high_const patterns.
in %r1. There is code in expand_hppa_{prologue,epilogue} that knows this.*/
static void
-store_reg (int reg, int disp, int base)
+store_reg (int reg, HOST_WIDE_INT disp, int base)
{
rtx insn, dest, src, basereg;
dest = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
insn = emit_move_insn (dest, src);
}
+ else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
+ {
+ rtx delta = GEN_INT (disp);
+ rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
+ emit_move_insn (tmpreg, delta);
+ emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg));
+ dest = gen_rtx_MEM (word_mode, tmpreg);
+ insn = emit_move_insn (dest, src);
+ if (DO_FRAME_NOTES)
+ {
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (VOIDmode,
+ gen_rtx_MEM (word_mode,
+ gen_rtx_PLUS (word_mode, basereg,
+ delta)),
+ src),
+ REG_NOTES (insn));
+ }
+ }
else
{
rtx delta = GEN_INT (disp);
rtx high = gen_rtx_PLUS (Pmode, basereg, gen_rtx_HIGH (Pmode, delta));
rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
emit_move_insn (tmpreg, high);
dest = gen_rtx_MEM (word_mode, gen_rtx_LO_SUM (Pmode, tmpreg, delta));
insn = emit_move_insn (dest, src);
add MOD to BASE. MOD must be <= 8k. */
static void
-store_reg_modify (int base, int reg, int mod)
+store_reg_modify (int base, int reg, HOST_WIDE_INT mod)
{
rtx insn, basereg, srcreg, delta;
- if (! VAL_14_BITS_P (mod))
+ if (!VAL_14_BITS_P (mod))
abort ();
basereg = gen_rtx_REG (Pmode, base);
There is code in expand_hppa_{prologue,epilogue} that knows about this. */
static void
-set_reg_plus_d (int reg, int base, int disp, int note)
+set_reg_plus_d (int reg, int base, HOST_WIDE_INT disp, int note)
{
rtx insn;
insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
plus_constant (gen_rtx_REG (Pmode, base), disp));
}
+ else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
+ {
+ rtx basereg = gen_rtx_REG (Pmode, base);
+ rtx delta = GEN_INT (disp);
+ rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
+ emit_move_insn (tmpreg, delta);
+ insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
+ gen_rtx_PLUS (Pmode, tmpreg, basereg));
+ }
else
{
rtx basereg = gen_rtx_REG (Pmode, base);
rtx delta = GEN_INT (disp);
+ rtx tmpreg = gen_rtx_REG (Pmode, 1);
- emit_move_insn (gen_rtx_REG (Pmode, 1),
+ emit_move_insn (tmpreg,
gen_rtx_PLUS (Pmode, basereg,
gen_rtx_HIGH (Pmode, delta)));
insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
- gen_rtx_LO_SUM (Pmode, gen_rtx_REG (Pmode, 1),
- delta));
+ gen_rtx_LO_SUM (Pmode, tmpreg, delta));
}
if (DO_FRAME_NOTES && note)
RTX_FRAME_RELATED_P (insn) = 1;
}
-int
-compute_frame_size (int size, int *fregs_live)
+HOST_WIDE_INT
+compute_frame_size (HOST_WIDE_INT size, int *fregs_live)
{
int freg_saved = 0;
int i, j;
/* hppa_expand_prologue does the dirty work now. We just need
to output the assembler directives which denote the start
of a function. */
- fprintf (file, "\t.CALLINFO FRAME=%d", actual_fsize);
+ fprintf (file, "\t.CALLINFO FRAME=" HOST_WIDE_INT_PRINT_DEC, actual_fsize);
if (regs_ever_live[2])
fputs (",CALLS,SAVE_RP", file);
else
hppa_expand_prologue (void)
{
int merge_sp_adjust_with_store = 0;
- int size = get_frame_size ();
- int i, offset;
+ HOST_WIDE_INT size = get_frame_size ();
+ HOST_WIDE_INT offset;
+ int i;
rtx insn, tmpreg;
gr_saved = 0;
So instead use stwm to store at *sp and post-increment the
stack pointer as an atomic operation. Then increment sp to
finish allocating the new frame. */
- int adjust1 = 8192 - 64;
- int adjust2 = actual_fsize - adjust1;
+ HOST_WIDE_INT adjust1 = 8192 - 64;
+ HOST_WIDE_INT adjust2 = actual_fsize - adjust1;
store_reg_modify (STACK_POINTER_REGNUM, 1, adjust1);
set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM,
Handle case where DISP > 8k by using the add_high_const patterns. */
static void
-load_reg (int reg, int disp, int base)
+load_reg (int reg, HOST_WIDE_INT disp, int base)
{
- rtx src, dest, basereg;
+ rtx dest = gen_rtx_REG (word_mode, reg);
+ rtx basereg = gen_rtx_REG (Pmode, base);
+ rtx src;
- dest = gen_rtx_REG (word_mode, reg);
- basereg = gen_rtx_REG (Pmode, base);
if (VAL_14_BITS_P (disp))
+ src = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
+ else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
{
- src = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
- emit_move_insn (dest, src);
+ rtx delta = GEN_INT (disp);
+ rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
+ emit_move_insn (tmpreg, delta);
+ if (TARGET_DISABLE_INDEXING)
+ {
+ emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg));
+ src = gen_rtx_MEM (word_mode, tmpreg);
+ }
+ else
+ src = gen_rtx_MEM (word_mode, gen_rtx_PLUS (Pmode, tmpreg, basereg));
}
else
{
rtx delta = GEN_INT (disp);
rtx high = gen_rtx_PLUS (Pmode, basereg, gen_rtx_HIGH (Pmode, delta));
rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
emit_move_insn (tmpreg, high);
src = gen_rtx_MEM (word_mode, gen_rtx_LO_SUM (Pmode, tmpreg, delta));
- emit_move_insn (dest, src);
}
+
+ emit_move_insn (dest, src);
}
/* Update the total code bytes output to the text section. */
fputs ("\t.EXIT\n\t.PROCEND\n", file);
+ if (TARGET_SOM && TARGET_GAS)
+ {
+ /* We done with this subspace except possibly for some additional
+ debug information. Forget that we are in this subspace to ensure
+ that the next function is output in its own subspace. */
+ forget_section ();
+ }
+
if (INSN_ADDRESSES_SET_P ())
{
insn = get_last_nonnote_insn ();
hppa_expand_epilogue (void)
{
rtx tmpreg;
- int offset, i;
+ HOST_WIDE_INT offset;
+ HOST_WIDE_INT ret_off = 0;
+ int i;
int merge_sp_adjust_with_load = 0;
- int ret_off = 0;
/* We will use this often. */
tmpreg = gen_rtx_REG (word_mode, 1);
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 label_rtx = gen_label_rtx ();
rtx begin_label_rtx, call_insn;
char begin_label_name[16];
ASM_GENERATE_INTERNAL_LABEL (begin_label_name, FUNC_BEGIN_PROLOG_LABEL,
label_no);
- begin_label_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (begin_label_name));
+ begin_label_rtx = gen_rtx_SYMBOL_REF (SImode, ggc_strdup (begin_label_name));
if (TARGET_64BIT)
emit_move_insn (arg_pointer_rtx,
emit_move_insn (gen_rtx_REG (word_mode, 26), gen_rtx_REG (word_mode, 2));
+ /* The address of the function is loaded into %r25 with a instruction-
+ relative sequence that avoids the use of relocations. The sequence
+ is split so that the load_offset_label_address instruction can
+ occupy the delay slot of the call to _mcount. */
+ if (TARGET_PA_20)
+ emit_insn (gen_lcla2 (reg, label_rtx));
+ else
+ emit_insn (gen_lcla1 (reg, label_rtx));
+
+ emit_insn (gen_load_offset_label_address (gen_rtx_REG (SImode, 25),
+ reg, begin_label_rtx, label_rtx));
+
#ifndef NO_PROFILE_COUNTERS
{
rtx count_label_rtx, addr, r24;
r24 = gen_rtx_REG (Pmode, 24);
emit_move_insn (r24, addr);
- /* %r25 is set from within the output pattern. */
call_insn =
- emit_call_insn (gen_call_profiler (gen_rtx_SYMBOL_REF (Pmode, "_mcount"),
- GEN_INT (TARGET_64BIT ? 24 : 12),
- begin_label_rtx));
+ emit_call_insn (gen_call (gen_rtx_MEM (Pmode,
+ gen_rtx_SYMBOL_REF (Pmode,
+ "_mcount")),
+ GEN_INT (TARGET_64BIT ? 24 : 12)));
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), r24);
}
#else
- /* %r25 is set from within the output pattern. */
+
call_insn =
- emit_call_insn (gen_call_profiler (gen_rtx_SYMBOL_REF (Pmode, "_mcount"),
- GEN_INT (TARGET_64BIT ? 16 : 8),
- begin_label_rtx));
+ emit_call_insn (gen_call (gen_rtx_MEM (Pmode,
+ gen_rtx_SYMBOL_REF (Pmode,
+ "_mcount")),
+ GEN_INT (TARGET_64BIT ? 16 : 8)));
+
#endif
+ use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), gen_rtx_REG (SImode, 25));
+ use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), gen_rtx_REG (SImode, 26));
+
/* Indicate the _mcount call cannot throw, nor will it execute a
non-local goto. */
REG_NOTES (call_insn)
= gen_rtx_EXPR_LIST (REG_EH_REGION, constm1_rtx, REG_NOTES (call_insn));
-
- if (flag_pic)
- {
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx);
- if (TARGET_64BIT)
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
-
- emit_move_insn (pic_offset_table_rtx, hppa_pic_save_rtx ());
- }
}
/* Fetch the return address for the frame COUNT steps up from
&& GET_CODE (XEXP (XVECEXP (pat, 0, 0), 1)) == MEM
&& GET_MODE (XEXP (XVECEXP (pat, 0, 0), 0)) == BLKmode
&& GET_MODE (XEXP (XVECEXP (pat, 0, 0), 1)) == BLKmode)
- return compute_movstr_length (insn) - 4;
+ return compute_movmem_length (insn) - 4;
/* Block clear pattern. */
else if (GET_CODE (insn) == INSN
&& GET_CODE (pat) == PARALLEL
&& GET_CODE (XEXP (XVECEXP (pat, 0, 0), 0)) == MEM
&& XEXP (XVECEXP (pat, 0, 0), 1) == const0_rtx
&& GET_MODE (XEXP (XVECEXP (pat, 0, 0), 0)) == BLKmode)
- return compute_clrstr_length (insn) - 4;
+ return compute_clrmem_length (insn) - 4;
/* Conditional branch with an unfilled delay slot. */
else if (GET_CODE (insn) == JUMP_INSN && ! simplejump_p (insn))
{
fputs (",ma", file);
break;
case PLUS:
- if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
- || GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
+ if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == REG)
+ {
+ if (ASSEMBLER_DIALECT == 0)
+ fputs ("x", file);
+ }
+ else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
+ || GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
{
if (ASSEMBLER_DIALECT == 0)
fputs ("x,s", file);
base = XEXP (XEXP (x, 0), 0);
fprintf (file, "%d(%s)", size, reg_names [REGNO (base)]);
break;
- default:
- if (GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT)
+ case PLUS:
+ if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT)
fprintf (file, "%s(%s)",
reg_names [REGNO (XEXP (XEXP (XEXP (x, 0), 0), 0))],
reg_names [REGNO (XEXP (XEXP (x, 0), 1))]);
- else if (GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
+ else if (GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
fprintf (file, "%s(%s)",
reg_names [REGNO (XEXP (XEXP (XEXP (x, 0), 1), 0))],
reg_names [REGNO (XEXP (XEXP (x, 0), 0))]);
+ else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == REG)
+ {
+ /* Because the REG_POINTER flag can get lost during reload,
+ GO_IF_LEGITIMATE_ADDRESS canonicalizes the order of the
+ index and base registers in the combined move patterns. */
+ rtx base = XEXP (XEXP (x, 0), 1);
+ rtx index = XEXP (XEXP (x, 0), 0);
+
+ fprintf (file, "%s(%s)",
+ reg_names [REGNO (index)], reg_names [REGNO (base)]);
+ }
else
output_address (XEXP (x, 0));
break;
+ default:
+ output_address (XEXP (x, 0));
+ break;
}
}
else
set_optab_libfunc (ge_optab, TFmode, "_U_Qfge");
set_optab_libfunc (lt_optab, TFmode, "_U_Qflt");
set_optab_libfunc (le_optab, TFmode, "_U_Qfle");
+ set_optab_libfunc (unord_optab, TFmode, "_U_Qfunord");
set_conv_libfunc (sext_optab, TFmode, SFmode, "_U_Qfcnvff_sgl_to_quad");
set_conv_libfunc (sext_optab, TFmode, DFmode, "_U_Qfcnvff_dbl_to_quad");
emit_move_insn (gen_rtx_REG (SImode, 26), operands[1]);
emit
- (gen_rtx
- (PARALLEL, VOIDmode,
+ (gen_rtx_PARALLEL
+ (VOIDmode,
gen_rtvec (6, gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, 29),
gen_rtx_fmt_ee (unsignedp ? UDIV : DIV,
SImode,
return NO_REGS;
}
+/* In the 32-bit runtime, arguments larger than eight bytes are passed
+ by invisible reference. As a GCC extension, we also pass anything
+ with a zero or variable size by reference.
+
+ The 64-bit runtime does not describe passing any types by invisible
+ reference. The internals of GCC can't currently handle passing
+ empty structures, and zero or variable length arrays when they are
+ not passed entirely on the stack or by reference. Thus, as a GCC
+ extension, we pass these types by reference. The HP compiler doesn't
+ support these types, so hopefully there shouldn't be any compatibility
+ issues. This may have to be revisited when HP releases a C99 compiler
+ or updates the ABI. */
+
+static bool
+pa_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
+ enum machine_mode mode, tree type,
+ bool named ATTRIBUTE_UNUSED)
+{
+ HOST_WIDE_INT size;
+
+ if (type)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ if (TARGET_64BIT)
+ return size <= 0;
+ else
+ return size <= 0 || size > 8;
+}
+
enum direction
function_arg_padding (enum machine_mode mode, tree type)
{
to determine if stdargs or varargs is used and fill in an initial
va_list. A pointer to this constructor is returned. */
-struct rtx_def *
+static rtx
hppa_builtin_saveregs (void)
{
rtx offset, dest;
std_expand_builtin_va_start (valist, nextarg);
}
-rtx
-hppa_va_arg (tree valist, tree type)
+static tree
+hppa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
{
- HOST_WIDE_INT size = int_size_in_bytes (type);
- HOST_WIDE_INT ofs;
- tree t, ptr, pptr;
-
if (TARGET_64BIT)
{
- /* Every argument in PA64 is supposed to be passed by value
- (including large structs). However, as a GCC extension, we
- pass zero and variable sized arguments by reference. Empty
- structures are a GCC extension not supported by the HP
- compilers. Thus, passing them by reference isn't likely
- to conflict with the ABI. For variable sized arguments,
- GCC doesn't have the infrastructure to allocate these to
- registers. */
-
- /* Arguments with a size greater than 8 must be aligned 0 MOD 16. */
-
- if (size > UNITS_PER_WORD)
- {
- t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (2 * UNITS_PER_WORD - 1, 0));
- t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
- build_int_2 (-2 * UNITS_PER_WORD, -1));
- t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
- TREE_SIDE_EFFECTS (t) = 1;
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- }
-
- if (size > 0)
- return std_expand_builtin_va_arg (valist, type);
- else
- {
- ptr = build_pointer_type (type);
-
- /* Args grow upward. */
- t = build (POSTINCREMENT_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
- TREE_SIDE_EFFECTS (t) = 1;
-
- pptr = build_pointer_type (ptr);
- t = build1 (NOP_EXPR, pptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
-
- t = build1 (INDIRECT_REF, ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
- }
+ /* Args grow upward. We can use the generic routines. */
+ return std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
}
else /* !TARGET_64BIT */
{
- ptr = build_pointer_type (type);
+ tree ptr = build_pointer_type (type);
+ tree valist_type;
+ tree t, u;
+ unsigned int size, ofs;
+ bool indirect;
- /* "Large" and variable sized types are passed by reference. */
- if (size > 8 || size <= 0)
+ indirect = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+ if (indirect)
{
- /* Args grow downward. */
- t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
- TREE_SIDE_EFFECTS (t) = 1;
-
- pptr = build_pointer_type (ptr);
- t = build1 (NOP_EXPR, pptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
-
- t = build1 (INDIRECT_REF, ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
+ type = ptr;
+ ptr = build_pointer_type (type);
}
- else
- {
- t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (-size, -1));
+ size = int_size_in_bytes (type);
+ valist_type = TREE_TYPE (valist);
- /* Copied from va-pa.h, but we probably don't need to align to
- word size, since we generate and preserve that invariant. */
- t = build (BIT_AND_EXPR, TREE_TYPE (valist), t,
- build_int_2 ((size > 4 ? -8 : -4), -1));
+ /* Args grow down. Not handled by generic routines. */
- t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
- TREE_SIDE_EFFECTS (t) = 1;
+ u = fold_convert (valist_type, size_in_bytes (type));
+ t = build (MINUS_EXPR, valist_type, valist, u);
- ofs = (8 - size) % 4;
- if (ofs)
- {
- t = build (PLUS_EXPR, TREE_TYPE (valist), t,
- build_int_2 (ofs, 0));
- TREE_SIDE_EFFECTS (t) = 1;
- }
+ /* Copied from va-pa.h, but we probably don't need to align to
+ word size, since we generate and preserve that invariant. */
+ u = build_int_2 ((size > 4 ? -8 : -4), -1);
+ u = fold_convert (valist_type, u);
+ t = build (BIT_AND_EXPR, valist_type, t, u);
+
+ t = build (MODIFY_EXPR, valist_type, valist, t);
- t = build1 (NOP_EXPR, ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
+ ofs = (8 - size) % 4;
+ if (ofs != 0)
+ {
+ u = fold_convert (valist_type, size_int (ofs));
+ t = build (PLUS_EXPR, valist_type, t, u);
}
- }
- /* Calculate! */
- return expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL);
-}
+ t = fold_convert (ptr, t);
+ t = build_fold_indirect_ref (t);
+ if (indirect)
+ t = build_fold_indirect_ref (t);
+ return t;
+ }
+}
/* This routine handles all the normal conditional branch sequences we
might need to generate. It handles compare immediate vs compare
abort ();
final_scan_insn (NEXT_INSN (insn), asm_out_file,
- optimize, 0, 0);
+ optimize, 0, 0, NULL);
/* Now delete the delay insn. */
PUT_CODE (NEXT_INSN (insn), NOTE);
else
{
/* Reload loop counter from memory, the store back to memory
- happens in the branch's delay slot. */
+ happens in the branch's delay slot. */
output_asm_insn ("ldw %0,%4", operands);
if (get_attr_length (insn) == 12)
return "addib,%C2 %1,%4,%3\n\tstw %4,%0";
else if (which_alternative == 2)
{
/* Reload loop counter from memory, the store back to memory
- happens in the branch's delay slot. */
+ happens in the branch's delay slot. */
if (get_attr_length (insn) == 8)
return "{comb|cmpb},%S2 %%r0,%1,%3\n\tstw %1,%0";
else
if (get_attr_length (insn) == 8)
return "{comb|cmpb},%S2 %%r0,%1,%3\n\tmtsar %r1";
else
- return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tbl %3\n\tmtsar %r1";
+ return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\tmtsar %r1";
}
}
&& !sibcall)
{
final_scan_insn (NEXT_INSN (insn), asm_out_file,
- optimize, 0, 0);
+ optimize, 0, 0, NULL);
/* Now delete the delay insn. */
PUT_CODE (NEXT_INSN (insn), NOTE);
/* A non-jump insn in the delay slot. By definition we can
emit this insn before the call (and in fact before argument
relocating. */
- final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0);
+ final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0,
+ NULL);
/* Now delete the delay insn. */
PUT_CODE (NEXT_INSN (insn), NOTE);
No need to check target flags as the length uniquely identifies
the remaining cases. */
if (attr_length_indirect_call (insn) == 8)
- return ".CALL\tARGW0=GR\n\t{bl|b,l} $$dyncall,%%r31\n\tcopy %%r31,%%r2";
+ {
+ /* The HP linker substitutes a BLE for millicode calls using
+ the short PIC PCREL form. Thus, we must use %r31 as the
+ link register when generating PA 1.x code. */
+ if (TARGET_PA_20)
+ return ".CALL\tARGW0=GR\n\tb,l $$dyncall,%%r2\n\tcopy %%r2,%%r31";
+ else
+ return ".CALL\tARGW0=GR\n\tbl $$dyncall,%%r31\n\tcopy %%r31,%%r2";
+ }
/* Long millicode call, but we are not generating PIC or portable runtime
code. */
fprintf (file, "\t.align 4\n");
ASM_OUTPUT_LABEL (file, label);
fprintf (file, "\t.word P'%s\n", fname);
- function_section (thunk_fndecl);
}
+ else if (TARGET_SOM && TARGET_GAS)
+ forget_section ();
current_thunk_number++;
nbytes = ((nbytes + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)
}
#endif
+/* This function places uninitialized global data in the bss section.
+ The ASM_OUTPUT_ALIGNED_BSS macro needs to be defined to call this
+ function on the SOM port to prevent uninitialized global data from
+ being placed in the data section. */
+
+void
+pa_asm_output_aligned_bss (FILE *stream,
+ const char *name,
+ unsigned HOST_WIDE_INT size,
+ unsigned int align)
+{
+ bss_section ();
+ fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT);
+
+#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
+ ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
+#endif
+
+#ifdef ASM_OUTPUT_SIZE_DIRECTIVE
+ ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+#endif
+
+ 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);
+}
+
+/* Both the HP and GNU assemblers under HP-UX provide a .comm directive
+ that doesn't allow the alignment of global common storage to be directly
+ specified. The SOM linker aligns common storage based on the rounded
+ value of the NUM_BYTES parameter in the .comm directive. It's not
+ possible to use the .align directive as it doesn't affect the alignment
+ of the label associated with a .comm directive. */
+
+void
+pa_asm_output_aligned_common (FILE *stream,
+ const char *name,
+ unsigned HOST_WIDE_INT size,
+ unsigned int align)
+{
+ bss_section ();
+
+ assemble_name (stream, name);
+ fprintf (stream, "\t.comm "HOST_WIDE_INT_PRINT_UNSIGNED"\n",
+ MAX (size, align / BITS_PER_UNIT));
+}
+
+/* We can't use .comm for local common storage as the SOM linker effectively
+ treats the symbol as universal and uses the same storage for local symbols
+ with the same name in different object files. The .block directive
+ reserves an uninitialized block of storage. However, it's not common
+ storage. Fortunately, GCC never requests common storage with the same
+ name in any given translation unit. */
+
+void
+pa_asm_output_aligned_local (FILE *stream,
+ const char *name,
+ unsigned HOST_WIDE_INT size,
+ unsigned int align)
+{
+ bss_section ();
+ fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT);
+
+#ifdef LOCAL_ASM_OP
+ fprintf (stream, "%s", LOCAL_ASM_OP);
+ assemble_name (stream, name);
+ fprintf (stream, "\n");
+#endif
+
+ ASM_OUTPUT_LABEL (stream, name);
+ fprintf (stream, "\t.block "HOST_WIDE_INT_PRINT_UNSIGNED"\n", size);
+}
+
/* Returns 1 if the 6 operands specified in OPERANDS are suitable for
use in fmpysub instructions. */
int
return (GET_CODE (op) == CONST_INT && shadd_constant_p (INTVAL (op)));
}
-/* Return 1 if OP is valid as a base register in a reg + reg address. */
+/* Return 1 if OP is valid as a base or index register in a
+ REG+REG address. */
int
-basereg_operand (rtx op, enum machine_mode mode)
+borx_reg_operand (rtx op, enum machine_mode mode)
{
- /* cse will create some unscaled indexed addresses, however; it
- generally isn't a win on the PA, so avoid creating unscaled
- indexed addresses until after cse is finished. */
- if (!cse_not_expected)
+ if (GET_CODE (op) != REG)
return 0;
- /* Allow any register when TARGET_NO_SPACE_REGS is in effect since
- we don't have to worry about the braindamaged implicit space
- register selection from the basereg. */
- if (TARGET_NO_SPACE_REGS)
- return (GET_CODE (op) == REG);
+ /* We must reject virtual registers as the only expressions that
+ can be instantiated are REG and REG+CONST. */
+ if (op == virtual_incoming_args_rtx
+ || op == virtual_stack_vars_rtx
+ || op == virtual_stack_dynamic_rtx
+ || op == virtual_outgoing_args_rtx
+ || op == virtual_cfa_rtx)
+ return 0;
/* While it's always safe to index off the frame pointer, it's not
- always profitable, particularly when the frame pointer is being
- eliminated. */
- if (! flag_omit_frame_pointer && op == frame_pointer_rtx)
- return 1;
+ profitable to do so when the frame pointer is being eliminated. */
+ if (!reload_completed
+ && flag_omit_frame_pointer
+ && !current_function_calls_alloca
+ && op == frame_pointer_rtx)
+ return 0;
- return (GET_CODE (op) == REG
- && REG_POINTER (op)
- && register_operand (op, mode));
+ return register_operand (op, mode);
}
/* Return 1 if this operand is anything other than a hard register. */
markers disables output of the branch table to readonly memory,
and any alignment directives that might be needed. Possibly,
the begin_brtab insn should be output before the label for the
- table. This doesn matter at the moment since the tables are
+ table. This doesn't matter at the moment since the tables are
always output in the text section. */
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
the mode is SF or DF. Then the value is returned in fr4 (32).
This must perform the same promotions as PROMOTE_MODE, else
- PROMOTE_FUNCTION_RETURN will not work correctly.
+ TARGET_PROMOTE_FUNCTION_RETURN will not work correctly.
Small structures must be returned in a PARALLEL on PA64 in order
to match the HP Compiler ABI. */
/* Structures 5 to 8 bytes in size are passed in the general
registers in the same manner as other non floating-point
objects. The data is right-justified and zero-extended
- to 64 bits.
-
- This is magic. Normally, using a PARALLEL results in left
- justified data on a big-endian target. However, using a
- single double-word register provides the required right
- justification for 5 to 8 byte structures. This has nothing
- to do with the direction of padding specified for the argument.
- It has to do with how the data is widened and shifted into
- and from the register.
-
- Aside from adding load_multiple and store_multiple patterns,
- this is the only way that I have found to obtain right
- justification of BLKmode data when it has a size greater
- than one word. Splitting the operation into two SImode loads
- or returning a DImode REG results in left justified data. */
+ to 64 bits. This is opposite to the normal justification
+ used on big endian targets and requires special treatment.
+ We now define BLOCK_REG_PADDING to pad these objects. */
if (mode == BLKmode)
{
rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
|| GET_CODE (op) == LEU));
}
+#ifndef ONE_ONLY_TEXT_SECTION_ASM_OP
+#define ONE_ONLY_TEXT_SECTION_ASM_OP ""
+#endif
+
+#ifndef NEW_TEXT_SECTION_ASM_OP
+#define NEW_TEXT_SECTION_ASM_OP ""
+#endif
+
+#ifndef DEFAULT_TEXT_SECTION_ASM_OP
+#define DEFAULT_TEXT_SECTION_ASM_OP ""
+#endif
+
+/* Select and return a TEXT_SECTION_ASM_OP for the current function.
+
+ This function is only used with SOM. Because we don't support
+ named subspaces, we can only create a new subspace or switch back
+ into the default text subspace. */
+const char *
+som_text_section_asm_op (void)
+{
+ if (TARGET_SOM && TARGET_GAS)
+ {
+ if (cfun && !cfun->machine->in_nsubspa)
+ {
+ /* We only want to emit a .nsubspa directive once at the
+ start of the function. */
+ cfun->machine->in_nsubspa = 1;
+
+ /* Create a new subspace for the text. This provides
+ better stub placement and one-only functions. */
+ if (cfun->decl
+ && DECL_ONE_ONLY (cfun->decl)
+ && !DECL_WEAK (cfun->decl))
+ return ONE_ONLY_TEXT_SECTION_ASM_OP;
+
+ return NEW_TEXT_SECTION_ASM_OP;
+ }
+ else
+ {
+ /* There isn't a current function or the body of the current
+ function has been completed. So, we are changing to the
+ text section to output debugging information. Do this in
+ the default text section. We need to forget that we are
+ in the text section so that text_section will call us the
+ next time around. */
+ forget_section ();
+ }
+ }
+
+ return DEFAULT_TEXT_SECTION_ASM_OP;
+}
+
/* 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
&& (DECL_INITIAL (exp) == error_mark_node
|| TREE_CONSTANT (DECL_INITIAL (exp)))
&& !reloc)
- readonly_data_section ();
+ {
+ if (TARGET_SOM
+ && DECL_ONE_ONLY (exp)
+ && !DECL_WEAK (exp))
+ one_only_readonly_data_section ();
+ else
+ readonly_data_section ();
+ }
else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c'
- && !(TREE_CODE (exp) == STRING_CST && flag_writable_strings)
&& !reloc)
readonly_data_section ();
+ else if (TARGET_SOM
+ && TREE_CODE (exp) == VAR_DECL
+ && DECL_ONE_ONLY (exp)
+ && !DECL_WEAK (exp)
+ && DECL_INITIAL (exp))
+ one_only_data_section ();
else
data_section ();
}
fputs (",DATA\n", stream);
}
}
+
+/* Worker function for TARGET_STRUCT_VALUE_RTX. */
+
+static rtx
+pa_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+ int incoming ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (Pmode, PA_STRUCT_VALUE_REGNUM);
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+bool
+pa_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
+{
+ /* SOM ABI says that objects larger than 64 bits are returned in memory.
+ PA64 ABI says that objects larger than 128 bits are returned in memory.
+ Note, int_size_in_bytes can return -1 if the size of the object is
+ variable or larger than the maximum value that can be expressed as
+ a HOST_WIDE_INT. It can also return zero for an empty type. The
+ simplest way to handle variable and empty types is to pass them in
+ memory. This avoids problems in defining the boundaries of argument
+ slots, allocating registers, etc. */
+ return (int_size_in_bytes (type) > (TARGET_64BIT ? 16 : 8)
+ || int_size_in_bytes (type) <= 0);
+}
+
#include "gt-pa.h"