/* Subroutines for insn-output.c for HPPA.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
+#include "df.h"
/* Return nonzero if there is a bypass for the output of
OUT_INSN and the fp store IN_INSN. */
rtx set;
if (recog_memoized (in_insn) < 0
- || get_attr_type (in_insn) != TYPE_FPSTORE
+ || (get_attr_type (in_insn) != TYPE_FPSTORE
+ && get_attr_type (in_insn) != TYPE_FPSTORE_LOAD)
|| recog_memoized (out_insn) < 0)
return 0;
static int pa_adjust_cost (rtx, rtx, rtx, int);
static int pa_adjust_priority (rtx, int);
static int pa_issue_rate (void);
-static void pa_select_section (tree, int, unsigned HOST_WIDE_INT)
+static void pa_som_asm_init_sections (void) ATTRIBUTE_UNUSED;
+static section *pa_select_section (tree, int, unsigned HOST_WIDE_INT)
ATTRIBUTE_UNUSED;
static void pa_encode_section_info (tree, rtx, int);
static const char *pa_strip_name_encoding (const char *);
#endif
static void pa_init_builtins (void);
static rtx hppa_builtin_saveregs (void);
+static void hppa_va_start (tree, rtx);
static tree hppa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
static bool pa_scalar_mode_supported_p (enum machine_mode);
-static bool pa_commutative_p (rtx x, int outer_code);
+static bool pa_commutative_p (const_rtx x, int outer_code);
static void copy_fp_args (rtx) ATTRIBUTE_UNUSED;
static int length_fp_args (rtx) ATTRIBUTE_UNUSED;
-static struct deferred_plabel *get_plabel (rtx) ATTRIBUTE_UNUSED;
static inline void pa_file_start_level (void) ATTRIBUTE_UNUSED;
static inline void pa_file_start_space (int) ATTRIBUTE_UNUSED;
static inline void pa_file_start_file (int) ATTRIBUTE_UNUSED;
static void pa_hpux64_gas_file_start (void) ATTRIBUTE_UNUSED;
static void pa_hpux64_hpas_file_start (void) ATTRIBUTE_UNUSED;
static void output_deferred_plabels (void);
+static void output_deferred_profile_counters (void) ATTRIBUTE_UNUSED;
#ifdef ASM_OUTPUT_EXTERNAL_REAL
static void pa_hpux_file_end (void);
#endif
#endif
static rtx pa_struct_value_rtx (tree, int);
static bool pa_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
- tree, bool);
+ const_tree, bool);
static int pa_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
static struct machine_function * pa_init_machine_status (void);
+static enum reg_class pa_secondary_reload (bool, rtx, enum reg_class,
+ enum machine_mode,
+ secondary_reload_info *);
+static void pa_extra_live_on_entry (bitmap);
+/* The following extra sections are only used for SOM. */
+static GTY(()) section *som_readonly_data_section;
+static GTY(()) section *som_one_only_readonly_data_section;
+static GTY(()) section *som_one_only_data_section;
/* Save the operands last given to a compare for use when we
generate a scc or bcc insn. */
registers which were saved by the current function's prologue. */
static int gr_saved, fr_saved;
+/* Boolean indicating whether the return pointer was saved by the
+ current function's prologue. */
+static bool rp_saved;
+
static rtx find_addr_reg (rtx);
/* Keep track of the number of bytes we have output in the CODE subspace
#endif
#undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
#undef TARGET_PROMOTE_PROTOTYPES
-#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX pa_struct_value_rtx
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS hppa_builtin_saveregs
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START hppa_va_start
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR hppa_gimplify_va_arg_expr
#undef TARGET_CANNOT_FORCE_CONST_MEM
#define TARGET_CANNOT_FORCE_CONST_MEM pa_tls_referenced_p
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD pa_secondary_reload
+
+#undef TARGET_EXTRA_LIVE_ON_ENTRY
+#define TARGET_EXTRA_LIVE_ON_ENTRY pa_extra_live_on_entry
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
/* Parse the -mfixed-range= option string. */
pa_init_builtins (void)
{
#ifdef DONT_HAVE_FPUTC_UNLOCKED
- built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE;
- implicit_built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE;
+ built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] =
+ built_in_decls[(int) BUILT_IN_PUTC_UNLOCKED];
+ implicit_built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED]
+ = implicit_built_in_decls[(int) BUILT_IN_PUTC_UNLOCKED];
+#endif
+#if TARGET_HPUX_11
+ if (built_in_decls [BUILT_IN_FINITE])
+ set_user_assembler_name (built_in_decls [BUILT_IN_FINITE], "_Isfinite");
+ if (built_in_decls [BUILT_IN_FINITEF])
+ set_user_assembler_name (built_in_decls [BUILT_IN_FINITEF], "_Isfinitef");
#endif
}
/* Accept any constant that can be moved in one instruction into a
general register. */
int
-cint_ok_for_move (HOST_WIDE_INT intval)
+cint_ok_for_move (HOST_WIDE_INT ival)
{
/* OK if ldo, ldil, or zdepi, can be used. */
- return (CONST_OK_FOR_LETTER_P (intval, 'J')
- || CONST_OK_FOR_LETTER_P (intval, 'N')
- || CONST_OK_FOR_LETTER_P (intval, 'K'));
+ return (VAL_14_BITS_P (ival)
+ || ldil_cint_p (ival)
+ || zdepi_cint_p (ival));
}
\f
/* Return truth value of whether OP can be used as an operand in a
&& (TARGET_64BIT ? INT_14_BITS (op) : INT_11_BITS (op))));
}
+/* True iff the operand OP can be used as the destination operand of
+ an integer store. This also implies the operand could be used as
+ the source operand of an integer load. Symbolic, lo_sum and indexed
+ memory operands are not allowed. We accept reloading pseudos and
+ other memory operands. */
+int
+integer_store_memory_operand (rtx op, enum machine_mode mode)
+{
+ return ((reload_in_progress
+ && REG_P (op)
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber [REGNO (op)] < 0)
+ || (GET_CODE (op) == MEM
+ && (reload_in_progress || memory_address_p (mode, XEXP (op, 0)))
+ && !symbolic_memory_operand (op, VOIDmode)
+ && !IS_LO_SUM_DLT_ADDR_P (XEXP (op, 0))
+ && !IS_INDEX_ADDR_P (XEXP (op, 0))));
+}
+
+/* True iff ldil can be used to load this CONST_INT. The least
+ significant 11 bits of the value must be zero and the value must
+ not change sign when extended from 32 to 64 bits. */
+int
+ldil_cint_p (HOST_WIDE_INT ival)
+{
+ HOST_WIDE_INT x = ival & (((HOST_WIDE_INT) -1 << 31) | 0x7ff);
+
+ return x == 0 || x == ((HOST_WIDE_INT) -1 << 31);
+}
+
/* True iff zdepi can be used to generate this CONST_INT.
- zdepi first sign extends a 5 bit signed number to a given field
+ zdepi first sign extends a 5-bit signed number to a given field
length, then places this field anywhere in a zero. */
int
zdepi_cint_p (unsigned HOST_WIDE_INT x)
tmp_reg = ((reload_in_progress || reload_completed)
? reg : gen_reg_rtx (Pmode));
+ /* Force function labels into memory. */
+ if (function_label_operand (orig, mode))
+ orig = force_const_mem (mode, orig);
+
emit_move_insn (tmp_reg,
gen_rtx_PLUS (word_mode, pic_offset_table_rtx,
gen_rtx_HIGH (word_mode, orig)));
insn = emit_move_insn (reg, pic_ref);
/* Put a REG_EQUAL note on this insn, so that it can be optimized. */
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, REG_NOTES (insn));
+ set_unique_reg_note (insn, REG_EQUAL, orig);
return reg;
}
{
case TLS_MODEL_GLOBAL_DYNAMIC:
tmp = gen_reg_rtx (Pmode);
- emit_insn (gen_tgd_load (tmp, addr));
+ if (flag_pic)
+ emit_insn (gen_tgd_load_pic (tmp, addr));
+ else
+ emit_insn (gen_tgd_load (tmp, addr));
ret = hppa_tls_call (tmp);
break;
ret = gen_reg_rtx (Pmode);
tmp = gen_reg_rtx (Pmode);
start_sequence ();
- emit_insn (gen_tld_load (tmp, addr));
+ if (flag_pic)
+ emit_insn (gen_tld_load_pic (tmp, addr));
+ else
+ emit_insn (gen_tld_load (tmp, addr));
t1 = hppa_tls_call (tmp);
insn = get_insns ();
end_sequence ();
tmp = gen_reg_rtx (Pmode);
ret = gen_reg_rtx (Pmode);
emit_insn (gen_tp_load (tp));
- emit_insn (gen_tie_load (tmp, addr));
+ if (flag_pic)
+ emit_insn (gen_tie_load_pic (tmp, addr));
+ else
+ emit_insn (gen_tie_load (tmp, addr));
emit_move_insn (ret, gen_rtx_PLUS (Pmode, tp, tmp));
break;
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. */
- gcc_assert (!no_new_pseudos);
+ gcc_assert (can_create_pseudo_p ());
tem = copy_to_mode_reg (Pmode, XEXP (operand0, 0));
operand0 = replace_equiv_address (operand0, tem);
&& flag_pic)
{
rtx const_mem = force_const_mem (mode, operand1);
- operands[1] = legitimize_pic_address (XEXP (operands[1], 0),
+ operands[1] = legitimize_pic_address (XEXP (const_mem, 0),
mode, temp);
operands[1] = replace_equiv_address (const_mem, operands[1]);
emit_move_sequence (operands, mode, temp);
because PLUS uses an 11-bit immediate and the insn sequence
generated is not as efficient as the one using HIGH/LO_SUM. */
if (GET_CODE (operand1) == CONST_INT
+ && GET_MODE_BITSIZE (mode) <= BITS_PER_WORD
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& !insert)
{
}
}
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_EQUAL, op1, REG_NOTES (insn));
+ set_unique_reg_note (insn, REG_EQUAL, op1);
return 1;
}
case ADDR_EXPR:
return 1;
+ case POINTER_PLUS_EXPR:
case PLUS_EXPR:
case MINUS_EXPR:
reloc = reloc_needed (TREE_OPERAND (exp, 0));
supposed to allow to happen. */
gcc_assert (optype0 == REGOP || optype1 == REGOP);
+ /* Handle copies between general and floating registers. */
+
+ if (optype0 == REGOP && optype1 == REGOP
+ && FP_REG_P (operands[0]) ^ FP_REG_P (operands[1]))
+ {
+ if (FP_REG_P (operands[0]))
+ {
+ output_asm_insn ("{stws|stw} %1,-16(%%sp)", operands);
+ output_asm_insn ("{stws|stw} %R1,-12(%%sp)", operands);
+ return "{fldds|fldd} -16(%%sp),%0";
+ }
+ else
+ {
+ output_asm_insn ("{fstds|fstd} %1,-16(%%sp)", operands);
+ output_asm_insn ("{ldws|ldw} -16(%%sp),%0", operands);
+ return "{ldws|ldw} -12(%%sp),%R0";
+ }
+ }
+
/* Handle auto decrementing and incrementing loads and stores
specifically, since the structure of the function doesn't work
for them without major modification. Do it better when we learn
else if (GET_CODE (addr) == PLUS
&& GET_CODE (XEXP (addr, 0)) == MULT)
{
+ rtx xoperands[4];
rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0);
if (!reg_overlap_mentioned_p (high_reg, addr))
{
- rtx xoperands[3];
-
xoperands[0] = high_reg;
xoperands[1] = XEXP (addr, 1);
xoperands[2] = XEXP (XEXP (addr, 0), 0);
}
else
{
- rtx xoperands[3];
-
xoperands[0] = high_reg;
xoperands[1] = XEXP (addr, 1);
xoperands[2] = XEXP (XEXP (addr, 0), 0);
rtx tmpreg = gen_rtx_REG (Pmode, 1);
emit_move_insn (tmpreg, delta);
- emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg));
- dest = gen_rtx_MEM (word_mode, tmpreg);
- insn = emit_move_insn (dest, src);
+ insn = emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg));
if (DO_FRAME_NOTES)
{
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
- gen_rtx_SET (VOIDmode,
- gen_rtx_MEM (word_mode,
- gen_rtx_PLUS (word_mode, basereg,
- delta)),
- src),
+ gen_rtx_SET (VOIDmode, tmpreg,
+ gen_rtx_PLUS (Pmode, basereg, delta)),
REG_NOTES (insn));
+ RTX_FRAME_RELATED_P (insn) = 1;
}
+ dest = gen_rtx_MEM (word_mode, tmpreg);
+ insn = emit_move_insn (dest, src);
}
else
{
RTX_FRAME_RELATED_P (insn) = 1;
/* RTX_FRAME_RELATED_P must be set on each frame related set
- in a parallel with more than one element. Don't set
- RTX_FRAME_RELATED_P in the first set if reg is temporary
- register 1. The effect of this operation is recorded in
- the initial copy. */
- if (reg != 1)
- {
- RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 0)) = 1;
- RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 1)) = 1;
- }
- else
- {
- /* The first element of a PARALLEL is always processed if it is
- a SET. Thus, we need an expression list for this case. */
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
- gen_rtx_SET (VOIDmode, basereg,
- gen_rtx_PLUS (word_mode, basereg, delta)),
- REG_NOTES (insn));
- }
+ in a parallel with more than one element. */
+ RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 0)) = 1;
+ RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 1)) = 1;
}
}
emit_move_insn (tmpreg, delta);
insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
gen_rtx_PLUS (Pmode, tmpreg, basereg));
+ if (DO_FRAME_NOTES)
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (VOIDmode, tmpreg,
+ gen_rtx_PLUS (Pmode, basereg, delta)),
+ REG_NOTES (insn));
}
else
{
/* Account for space used by the callee general register saves. */
for (i = 18, j = frame_pointer_needed ? 4 : 3; i >= j; i--)
- if (regs_ever_live[i])
+ if (df_regs_ever_live_p (i))
size += UNITS_PER_WORD;
/* Account for space used by the callee floating point register saves. */
for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP)
- if (regs_ever_live[i]
- || (!TARGET_64BIT && regs_ever_live[i + 1]))
+ if (df_regs_ever_live_p (i)
+ || (!TARGET_64BIT && df_regs_ever_live_p (i + 1)))
{
freg_saved = 1;
to output the assembler directives which denote the start
of a function. */
fprintf (file, "\t.CALLINFO FRAME=" HOST_WIDE_INT_PRINT_DEC, actual_fsize);
- if (regs_ever_live[2])
- fputs (",CALLS,SAVE_RP", file);
- else
+ if (current_function_is_leaf)
fputs (",NO_CALLS", file);
+ else
+ fputs (",CALLS", file);
+ if (rp_saved)
+ fputs (",SAVE_RP", file);
/* The SAVE_SP flag is used to indicate that register %r3 is stored
at the beginning of the frame and that it is used as the frame
/* Save RP first. The calling conventions manual states RP will
always be stored into the caller's frame at sp - 20 or sp - 16
depending on which ABI is in use. */
- if (regs_ever_live[2] || current_function_calls_eh_return)
- store_reg (2, TARGET_64BIT ? -16 : -20, STACK_POINTER_REGNUM);
+ if (df_regs_ever_live_p (2) || current_function_calls_eh_return)
+ {
+ store_reg (2, TARGET_64BIT ? -16 : -20, STACK_POINTER_REGNUM);
+ rp_saved = true;
+ }
+ else
+ rp_saved = false;
/* Allocate the local frame and set up the frame pointer if needed. */
if (actual_fsize != 0)
frames. */
insn = emit_move_insn (tmpreg, frame_pointer_rtx);
if (DO_FRAME_NOTES)
- {
- /* We need to record the frame pointer save here since the
- new frame pointer is set in the following insn. */
- RTX_FRAME_RELATED_P (insn) = 1;
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
- gen_rtx_SET (VOIDmode,
- gen_rtx_MEM (word_mode, stack_pointer_rtx),
- frame_pointer_rtx),
- REG_NOTES (insn));
- }
+ RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
if (DO_FRAME_NOTES)
}
for (i = 18; i >= 4; i--)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
{
store_reg (i, offset, FRAME_POINTER_REGNUM);
offset += UNITS_PER_WORD;
}
for (i = 18; i >= 3; i--)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
{
/* If merge_sp_adjust_with_store is nonzero, then we can
optimize the first GR save. */
/* Now actually save the FP registers. */
for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP)
{
- if (regs_ever_live[i]
- || (! TARGET_64BIT && regs_ever_live[i + 1]))
+ if (df_regs_ever_live_p (i)
+ || (! TARGET_64BIT && df_regs_ever_live_p (i + 1)))
{
rtx addr, insn, reg;
addr = gen_rtx_MEM (DFmode, gen_rtx_POST_INC (DFmode, tmpreg));
/* We done with this subspace except possibly for some additional
debug information. Forget that we are in this subspace to ensure
that the next function is output in its own subspace. */
- forget_section ();
+ in_section = NULL;
+ cfun->machine->in_nsubspa = 2;
}
if (INSN_ADDRESSES_SET_P ())
/* Try to restore RP early to avoid load/use interlocks when
RP gets used in the return (bv) instruction. This appears to still
be necessary even when we schedule the prologue and epilogue. */
- if (regs_ever_live [2] || current_function_calls_eh_return)
+ if (rp_saved)
{
ret_off = TARGET_64BIT ? -16 : -20;
if (frame_pointer_needed)
}
for (i = 18; i >= 4; i--)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
{
load_reg (i, offset, FRAME_POINTER_REGNUM);
offset += UNITS_PER_WORD;
for (i = 18; i >= 3; i--)
{
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
{
/* Only for the first load.
merge_sp_adjust_with_load holds the register load
/* Actually do the restores now. */
for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP)
- if (regs_ever_live[i]
- || (! TARGET_64BIT && regs_ever_live[i + 1]))
+ if (df_regs_ever_live_p (i)
+ || (! TARGET_64BIT && df_regs_ever_live_p (i + 1)))
{
rtx src = gen_rtx_MEM (DFmode, gen_rtx_POST_INC (DFmode, tmpreg));
rtx dest = gen_rtx_REG (DFmode, i);
return get_hard_reg_initial_val (word_mode, PIC_OFFSET_TABLE_REGNUM);
}
+#ifndef NO_DEFERRED_PROFILE_COUNTERS
+#define NO_DEFERRED_PROFILE_COUNTERS 0
+#endif
+
+
+/* Vector of funcdef numbers. */
+static VEC(int,heap) *funcdef_nos;
+
+/* Output deferred profile counters. */
+static void
+output_deferred_profile_counters (void)
+{
+ unsigned int i;
+ int align, n;
+
+ if (VEC_empty (int, funcdef_nos))
+ return;
+
+ switch_to_section (data_section);
+ align = MIN (BIGGEST_ALIGNMENT, LONG_TYPE_SIZE);
+ ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+
+ for (i = 0; VEC_iterate (int, funcdef_nos, i, n); i++)
+ {
+ targetm.asm_out.internal_label (asm_out_file, "LP", n);
+ assemble_integer (const0_rtx, LONG_TYPE_SIZE / BITS_PER_UNIT, align, 1);
+ }
+
+ VEC_free (int, heap, funcdef_nos);
+}
+
void
hppa_profile_hook (int label_no)
{
emit_move_insn (gen_rtx_REG (word_mode, 26), gen_rtx_REG (word_mode, 2));
- /* The address of the function is loaded into %r25 with a instruction-
+ /* The address of the function is loaded into %r25 with an instruction-
relative sequence that avoids the use of relocations. The sequence
is split so that the load_offset_label_address instruction can
occupy the delay slot of the call to _mcount. */
emit_insn (gen_load_offset_label_address (gen_rtx_REG (SImode, 25),
reg, begin_label_rtx, label_rtx));
-#ifndef NO_PROFILE_COUNTERS
+#if !NO_DEFERRED_PROFILE_COUNTERS
{
rtx count_label_rtx, addr, r24;
char count_label_name[16];
+ VEC_safe_push (int, heap, funcdef_nos, label_no);
ASM_GENERATE_INTERNAL_LABEL (count_label_name, "LP", label_no);
count_label_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (count_label_name));
GEN_INT (0x00011820), NE, NULL_RTX, SImode, 1);
emit_jump_insn (gen_bne (label));
+ /* 0xe0400002 must be specified as -532676606 so that it won't be
+ rejected as an invalid immediate operand on 64-bit hosts. */
emit_cmp_insn (gen_rtx_MEM (SImode, plus_constant (ins, 12)),
- GEN_INT (0xe0400002), NE, NULL_RTX, SImode, 1);
+ GEN_INT (-532676606), NE, NULL_RTX, SImode, 1);
/* If there is no export stub then just use the value saved from
the return pointer register. */
return saved_rp;
}
-/* This is only valid once reload has completed because it depends on
- knowing exactly how much (if any) frame there is and...
-
- It's only valid if there is no frame marker to de-allocate and...
-
- It's only valid if %r2 hasn't been saved into the caller's frame
- (we're not profiling and %r2 isn't live anywhere). */
-int
-hppa_can_use_return_insn_p (void)
-{
- return (reload_completed
- && (compute_frame_size (get_frame_size (), 0) ? 0 : 1)
- && ! regs_ever_live[2]
- && ! frame_pointer_needed);
-}
-
void
emit_bcond_fp (enum rtx_code code, rtx operand0)
{
}
#undef aputs
-static struct deferred_plabel *
-get_plabel (rtx symbol)
+/* Search the deferred plabel list for SYMBOL and return its internal
+ label. If an entry for SYMBOL is not found, a new entry is created. */
+
+rtx
+get_deferred_plabel (rtx symbol)
{
const char *fname = XSTR (symbol, 0);
size_t i;
mark_referenced (id);
}
- return &deferred_plabels[i];
+ return deferred_plabels[i].internal_label;
}
static void
output_deferred_plabels (void)
{
size_t i;
- /* If we have deferred plabels, then we need to switch into the data
- section and align it to a 4 byte boundary before we output the
- deferred plabels. */
+
+ /* If we have some deferred plabels, then we need to switch into the
+ data or readonly data section, and align it to a 4 byte boundary
+ before outputting the deferred plabels. */
if (n_deferred_plabels)
{
- data_section ();
+ switch_to_section (flag_pic ? data_section : readonly_data_section);
ASM_OUTPUT_ALIGN (asm_out_file, TARGET_64BIT ? 3 : 2);
}
/* Now output the deferred plabels. */
for (i = 0; i < n_deferred_plabels; i++)
{
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ targetm.asm_out.internal_label (asm_out_file, "L",
CODE_LABEL_NUMBER (deferred_plabels[i].internal_label));
assemble_integer (deferred_plabels[i].symbol,
TARGET_64BIT ? 8 : 4, TARGET_64BIT ? 64 : 32, 1);
set_conv_libfunc (sfloat_optab, TFmode, SImode, "_U_Qfcnvxf_sgl_to_quad");
set_conv_libfunc (sfloat_optab, TFmode, DImode, "_U_Qfcnvxf_dbl_to_quad");
+ set_conv_libfunc (ufloat_optab, TFmode, SImode, "_U_Qfcnvxf_usgl_to_quad");
+ set_conv_libfunc (ufloat_optab, TFmode, DImode, "_U_Qfcnvxf_udbl_to_quad");
}
#endif
fputc ('\n', asm_out_file);
}
\f
-/* Return the class of any secondary reload register that is needed to
- move IN into a register in class CLASS using mode MODE.
-
- Profiling has showed this routine and its descendants account for
- a significant amount of compile time (~7%). So it has been
- optimized to reduce redundant computations and eliminate useless
- function calls.
-
- It might be worthwhile to try and make this a leaf function too. */
-
-enum reg_class
-secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in)
+static enum reg_class
+pa_secondary_reload (bool in_p, rtx x, enum reg_class class,
+ enum machine_mode mode, secondary_reload_info *sri)
{
- int regno, is_symbolic;
-
- /* Trying to load a constant into a FP register during PIC code
- generation will require %r1 as a scratch register. */
- if (flag_pic
- && GET_MODE_CLASS (mode) == MODE_INT
- && FP_REG_CLASS_P (class)
- && (GET_CODE (in) == CONST_INT || GET_CODE (in) == CONST_DOUBLE))
- return R1_REGS;
+ int is_symbolic, regno;
- /* Profiling showed the PA port spends about 1.3% of its compilation
- time in true_regnum from calls inside secondary_reload_class. */
+ /* Handle the easy stuff first. */
+ if (class == R1_REGS)
+ return NO_REGS;
- if (GET_CODE (in) == REG)
+ if (REG_P (x))
{
- regno = REGNO (in);
- if (regno >= FIRST_PSEUDO_REGISTER)
- regno = true_regnum (in);
+ regno = REGNO (x);
+ if (class == BASE_REG_CLASS && regno < FIRST_PSEUDO_REGISTER)
+ return NO_REGS;
}
- else if (GET_CODE (in) == SUBREG)
- regno = true_regnum (in);
else
regno = -1;
/* If we have something like (mem (mem (...)), we can safely assume the
inner MEM will end up in a general register after reloading, so there's
no need for a secondary reload. */
- if (GET_CODE (in) == MEM
- && GET_CODE (XEXP (in, 0)) == MEM)
+ if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == MEM)
return NO_REGS;
- /* Handle out of range displacement for integer mode loads/stores of
- FP registers. */
- if (((regno >= FIRST_PSEUDO_REGISTER || regno == -1)
- && GET_MODE_CLASS (mode) == MODE_INT
- && FP_REG_CLASS_P (class))
- || (class == SHIFT_REGS && (regno <= 0 || regno >= 32)))
- return GENERAL_REGS;
+ /* Trying to load a constant into a FP register during PIC code
+ generation requires %r1 as a scratch register. */
+ if (flag_pic
+ && (mode == SImode || mode == DImode)
+ && FP_REG_CLASS_P (class)
+ && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE))
+ {
+ sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1
+ : CODE_FOR_reload_indi_r1);
+ return NO_REGS;
+ }
+
+ /* Profiling showed the PA port spends about 1.3% of its compilation
+ time in true_regnum from calls inside pa_secondary_reload_class. */
+ if (regno >= FIRST_PSEUDO_REGISTER || GET_CODE (x) == SUBREG)
+ regno = true_regnum (x);
+
+ /* In order to allow 14-bit displacements in integer loads and stores,
+ we need to prevent reload from generating out of range integer mode
+ loads and stores to the floating point registers. Previously, we
+ used to call for a secondary reload and have emit_move_sequence()
+ fix the instruction sequence. However, reload occasionally wouldn't
+ generate the reload and we would end up with an invalid REG+D memory
+ address. So, now we use an intermediate general register for most
+ memory loads and stores. */
+ if ((regno >= FIRST_PSEUDO_REGISTER || regno == -1)
+ && GET_MODE_CLASS (mode) == MODE_INT
+ && FP_REG_CLASS_P (class))
+ {
+ /* Reload passes (mem:SI (reg/f:DI 30 %r30) when it wants to check
+ the secondary reload needed for a pseudo. It never passes a
+ REG+D address. */
+ if (GET_CODE (x) == MEM)
+ {
+ x = XEXP (x, 0);
+
+ /* We don't need an intermediate for indexed and LO_SUM DLT
+ memory addresses. When INT14_OK_STRICT is true, it might
+ appear that we could directly allow register indirect
+ memory addresses. However, this doesn't work because we
+ don't support SUBREGs in floating-point register copies
+ and reload doesn't tell us when it's going to use a SUBREG. */
+ if (IS_INDEX_ADDR_P (x)
+ || IS_LO_SUM_DLT_ADDR_P (x))
+ return NO_REGS;
+
+ /* Otherwise, we need an intermediate general register. */
+ return GENERAL_REGS;
+ }
+
+ /* Request a secondary reload with a general scratch register
+ for everthing else. ??? Could symbolic operands be handled
+ directly when generating non-pic PA 2.0 code? */
+ sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
+ return NO_REGS;
+ }
+
+ /* We need a secondary register (GPR) for copies between the SAR
+ and anything other than a general register. */
+ if (class == SHIFT_REGS && (regno <= 0 || regno >= 32))
+ {
+ sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
+ return NO_REGS;
+ }
/* A SAR<->FP register copy requires a secondary register (GPR) as
well as secondary memory. */
if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER
- && ((REGNO_REG_CLASS (regno) == SHIFT_REGS && FP_REG_CLASS_P (class))
- || (class == SHIFT_REGS && FP_REG_CLASS_P (REGNO_REG_CLASS (regno)))))
- return GENERAL_REGS;
+ && (REGNO_REG_CLASS (regno) == SHIFT_REGS
+ && FP_REG_CLASS_P (class)))
+ {
+ sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
+ return NO_REGS;
+ }
- if (GET_CODE (in) == HIGH)
- in = XEXP (in, 0);
+ /* Secondary reloads of symbolic operands require %r1 as a scratch
+ register when we're generating PIC code and when the operand isn't
+ readonly. */
+ if (GET_CODE (x) == HIGH)
+ x = XEXP (x, 0);
/* Profiling has showed GCC spends about 2.6% of its compilation
- time in symbolic_operand from calls inside secondary_reload_class.
-
- We use an inline copy and only compute its return value once to avoid
- useless work. */
- switch (GET_CODE (in))
+ time in symbolic_operand from calls inside pa_secondary_reload_class.
+ So, we use an inline copy to avoid useless work. */
+ switch (GET_CODE (x))
{
- rtx tmp;
+ rtx op;
case SYMBOL_REF:
+ is_symbolic = !SYMBOL_REF_TLS_MODEL (x);
+ break;
case LABEL_REF:
is_symbolic = 1;
break;
case CONST:
- tmp = XEXP (in, 0);
- is_symbolic = ((GET_CODE (XEXP (tmp, 0)) == SYMBOL_REF
- || GET_CODE (XEXP (tmp, 0)) == LABEL_REF)
- && GET_CODE (XEXP (tmp, 1)) == CONST_INT);
+ op = XEXP (x, 0);
+ is_symbolic = (((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ && !SYMBOL_REF_TLS_MODEL (XEXP (op, 0)))
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT);
break;
-
default:
is_symbolic = 0;
break;
}
- if (!flag_pic
- && is_symbolic
- && read_only_operand (in, VOIDmode))
- return NO_REGS;
-
- if (class != R1_REGS && is_symbolic)
- return R1_REGS;
+ if (is_symbolic && (flag_pic || !read_only_operand (x, VOIDmode)))
+ {
+ gcc_assert (mode == SImode || mode == DImode);
+ sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1
+ : CODE_FOR_reload_indi_r1);
+ }
return NO_REGS;
}
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. The argument pointer
+ is only marked as live on entry by df-scan when it is a fixed
+ register. It isn't a fixed register in the 64-bit runtime,
+ so we need to mark it here. */
+
+static void
+pa_extra_live_on_entry (bitmap regs)
+{
+ if (TARGET_64BIT)
+ bitmap_set_bit (regs, ARG_POINTER_REGNUM);
+}
+
+/* Implement EH_RETURN_HANDLER_RTX. The MEM needs to be volatile
+ to prevent it from being deleted. */
+
+rtx
+pa_eh_return_handler_rtx (void)
+{
+ rtx tmp;
+
+ tmp = gen_rtx_PLUS (word_mode, frame_pointer_rtx,
+ TARGET_64BIT ? GEN_INT (-16) : GEN_INT (-20));
+ tmp = gen_rtx_MEM (word_mode, tmp);
+ tmp->volatil = 1;
+ return tmp;
+}
+
/* In the 32-bit runtime, arguments larger than eight bytes are passed
by invisible reference. As a GCC extension, we also pass anything
with a zero or variable size by reference.
static bool
pa_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
- enum machine_mode mode, tree type,
+ enum machine_mode mode, const_tree type,
bool named ATTRIBUTE_UNUSED)
{
HOST_WIDE_INT size;
}
enum direction
-function_arg_padding (enum machine_mode mode, tree type)
+function_arg_padding (enum machine_mode mode, const_tree type)
{
if (mode == BLKmode
|| (TARGET_64BIT && type && AGGREGATE_TYPE_P (type)))
offset, 0, 0, OPTAB_LIB_WIDEN));
}
-void
+static void
hppa_va_start (tree valist, rtx nextarg)
{
nextarg = expand_builtin_saveregs ();
/* Args grow down. Not handled by generic routines. */
- u = fold_convert (valist_type, size_in_bytes (type));
- t = build (MINUS_EXPR, valist_type, valist, u);
+ u = fold_convert (sizetype, size_in_bytes (type));
+ u = fold_build1 (NEGATE_EXPR, sizetype, u);
+ t = build2 (POINTER_PLUS_EXPR, valist_type, valist, u);
/* Copied from va-pa.h, but we probably don't need to align to
word size, since we generate and preserve that invariant. */
- u = build_int_cst (valist_type, (size > 4 ? -8 : -4));
- t = build (BIT_AND_EXPR, valist_type, t, u);
+ u = size_int (size > 4 ? -8 : -4);
+ t = fold_convert (sizetype, t);
+ t = build2 (BIT_AND_EXPR, sizetype, t, u);
+ t = fold_convert (valist_type, t);
- t = build (MODIFY_EXPR, valist_type, valist, t);
+ t = build2 (MODIFY_EXPR, valist_type, valist, t);
ofs = (8 - size) % 4;
if (ofs != 0)
{
- u = fold_convert (valist_type, size_int (ofs));
- t = build (PLUS_EXPR, valist_type, t, u);
+ u = size_int (ofs);
+ t = build2 (POINTER_PLUS_EXPR, valist_type, t, u);
}
t = fold_convert (ptr, t);
return true;
return false;
+ case MODE_DECIMAL_FLOAT:
+ return false;
+
default:
gcc_unreachable ();
}
parameters. */
const char *
-output_cbranch (rtx *operands, int nullify, int length, int negated, rtx insn)
+output_cbranch (rtx *operands, int negated, rtx insn)
{
static char buf[100];
int useskip = 0;
- rtx xoperands[5];
+ int nullify = INSN_ANNULLED_BRANCH_P (insn);
+ int length = get_attr_length (insn);
+ int xdelay;
/* A conditional branch to the following instruction (e.g. the delay slot)
is asking for a disaster. This can happen when not optimizing and
zero for cmpb, we must ensure that we use cmpb for the comparison. */
if (GET_MODE (operands[1]) == DImode && operands[2] == const0_rtx)
operands[2] = gen_rtx_REG (DImode, 0);
+ if (GET_MODE (operands[2]) == DImode && operands[1] == const0_rtx)
+ operands[1] = gen_rtx_REG (DImode, 0);
/* If this is a long branch with its delay slot unfilled, set `nullify'
as it can nullify the delay slot and save a nop. */
with an unfilled delay slot. */
case 8:
/* Handle weird backwards branch with a filled delay slot
- with is nullified. */
+ which is nullified. */
if (dbr_sequence_length () != 0
&& ! forward_branch_p (insn)
&& nullify)
}
break;
- case 20:
- case 28:
- xoperands[0] = operands[0];
- xoperands[1] = operands[1];
- xoperands[2] = operands[2];
- xoperands[3] = operands[3];
-
+ default:
/* The reversed conditional branch must branch over one additional
- instruction if the delay slot is filled. If the delay slot
- is empty, the instruction after the reversed condition branch
- must be nullified. */
- nullify = dbr_sequence_length () == 0;
- xoperands[4] = nullify ? GEN_INT (length) : GEN_INT (length + 4);
+ instruction if the delay slot is filled and needs to be extracted
+ by output_lbranch. If the delay slot is empty or this is a
+ nullified forward branch, the instruction after the reversed
+ condition branch must be nullified. */
+ if (dbr_sequence_length () == 0
+ || (nullify && forward_branch_p (insn)))
+ {
+ nullify = 1;
+ xdelay = 0;
+ operands[4] = GEN_INT (length);
+ }
+ else
+ {
+ xdelay = 1;
+ operands[4] = GEN_INT (length + 4);
+ }
/* Create a reversed conditional branch which branches around
the following insns. */
}
}
- output_asm_insn (buf, xoperands);
- return output_lbranch (operands[0], insn);
-
- default:
- gcc_unreachable ();
+ output_asm_insn (buf, operands);
+ return output_lbranch (operands[0], insn, xdelay);
}
return buf;
}
-/* This routine handles long unconditional branches that exceed the
- maximum range of a simple branch instruction. */
+/* This routine handles output of long unconditional branches that
+ exceed the maximum range of a simple branch instruction. Since
+ we don't have a register available for the branch, we save register
+ %r1 in the frame marker, load the branch destination DEST into %r1,
+ execute the branch, and restore %r1 in the delay slot of the branch.
+
+ Since long branches may have an insn in the delay slot and the
+ delay slot is used to restore %r1, we in general need to extract
+ this insn and execute it before the branch. However, to facilitate
+ use of this function by conditional branches, we also provide an
+ option to not extract the delay insn so that it will be emitted
+ after the long branch. So, if there is an insn in the delay slot,
+ it is extracted if XDELAY is nonzero.
+
+ The lengths of the various long-branch sequences are 20, 16 and 24
+ bytes for the portable runtime, non-PIC and PIC cases, respectively. */
const char *
-output_lbranch (rtx dest, rtx insn)
+output_lbranch (rtx dest, rtx insn, int xdelay)
{
rtx xoperands[2];
xoperands[0] = dest;
/* First, free up the delay slot. */
- if (dbr_sequence_length () != 0)
+ if (xdelay && dbr_sequence_length () != 0)
{
/* We can't handle a jump in the delay slot. */
gcc_assert (GET_CODE (NEXT_INSN (insn)) != JUMP_INSN);
optimize, 0, NULL);
/* Now delete the delay insn. */
- PUT_CODE (NEXT_INSN (insn), NOTE);
- NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
+ SET_INSN_DELETED (NEXT_INSN (insn));
}
/* Output an insn to save %r1. The runtime documentation doesn't
for other purposes. */
if (TARGET_64BIT)
{
- if (actual_fsize == 0 && !regs_ever_live[2])
+ if (actual_fsize == 0 && !df_regs_ever_live_p (2))
/* Use the return pointer slot in the frame marker. */
output_asm_insn ("std %%r1,-16(%%r30)", xoperands);
else
}
else
{
- if (actual_fsize == 0 && !regs_ever_live[2])
+ if (actual_fsize == 0 && !df_regs_ever_live_p (2))
/* Use the return pointer slot in the frame marker. */
output_asm_insn ("stw %%r1,-20(%%r30)", xoperands);
else
{
xoperands[1] = gen_label_rtx ();
output_asm_insn ("addil L'%l0-%l1,%%r1", xoperands);
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[1]));
+ targetm.asm_out.internal_label (asm_out_file, "L",
+ CODE_LABEL_NUMBER (xoperands[1]));
output_asm_insn ("ldo R'%l0-%l1(%%r1),%%r1", xoperands);
}
else
/* Now restore the value of %r1 in the delay slot. */
if (TARGET_64BIT)
{
- if (actual_fsize == 0 && !regs_ever_live[2])
+ if (actual_fsize == 0 && !df_regs_ever_live_p (2))
return "ldd -16(%%r30),%%r1";
else
return "ldd -40(%%r30),%%r1";
}
else
{
- if (actual_fsize == 0 && !regs_ever_live[2])
+ if (actual_fsize == 0 && !df_regs_ever_live_p (2))
return "ldw -20(%%r30),%%r1";
else
return "ldw -12(%%r30),%%r1";
above. it returns the appropriate output template to emit the branch. */
const char *
-output_bb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length,
- int negated, rtx insn, int which)
+output_bb (rtx *operands ATTRIBUTE_UNUSED, int negated, rtx insn, int which)
{
static char buf[100];
int useskip = 0;
+ int nullify = INSN_ANNULLED_BRANCH_P (insn);
+ int length = get_attr_length (insn);
+ int xdelay;
/* A conditional branch to the following instruction (e.g. the delay slot) is
asking for a disaster. I do not think this can happen as this pattern
with an unfilled delay slot. */
case 8:
/* Handle weird backwards branch with a filled delay slot
- with is nullified. */
+ which is nullified. */
if (dbr_sequence_length () != 0
&& ! forward_branch_p (insn)
&& nullify)
}
else
{
- strcpy (buf, "{extrs,|extrw,s,}");
if (GET_MODE (operands[0]) == DImode)
strcpy (buf, "extrd,s,*");
+ else
+ strcpy (buf, "{extrs,|extrw,s,}");
if ((which == 0 && negated)
|| (which == 1 && ! negated))
strcat (buf, "<");
break;
default:
- gcc_unreachable ();
+ /* The reversed conditional branch must branch over one additional
+ instruction if the delay slot is filled and needs to be extracted
+ by output_lbranch. If the delay slot is empty or this is a
+ nullified forward branch, the instruction after the reversed
+ condition branch must be nullified. */
+ if (dbr_sequence_length () == 0
+ || (nullify && forward_branch_p (insn)))
+ {
+ nullify = 1;
+ xdelay = 0;
+ operands[4] = GEN_INT (length);
+ }
+ else
+ {
+ xdelay = 1;
+ operands[4] = GEN_INT (length + 4);
+ }
+
+ if (GET_MODE (operands[0]) == DImode)
+ strcpy (buf, "bb,*");
+ else
+ strcpy (buf, "bb,");
+ if ((which == 0 && negated)
+ || (which == 1 && !negated))
+ strcat (buf, "<");
+ else
+ strcat (buf, ">=");
+ if (nullify)
+ strcat (buf, ",n %0,%1,.+%4");
+ else
+ strcat (buf, " %0,%1,.+%4");
+ output_asm_insn (buf, operands);
+ return output_lbranch (negated ? operands[3] : operands[2],
+ insn, xdelay);
}
return buf;
}
branch. */
const char *
-output_bvb (rtx *operands ATTRIBUTE_UNUSED, int nullify, int length,
- int negated, rtx insn, int which)
+output_bvb (rtx *operands ATTRIBUTE_UNUSED, int negated, rtx insn, int which)
{
static char buf[100];
int useskip = 0;
+ int nullify = INSN_ANNULLED_BRANCH_P (insn);
+ int length = get_attr_length (insn);
+ int xdelay;
/* A conditional branch to the following instruction (e.g. the delay slot) is
asking for a disaster. I do not think this can happen as this pattern
with an unfilled delay slot. */
case 8:
/* Handle weird backwards branch with a filled delay slot
- with is nullified. */
+ which is nullified. */
if (dbr_sequence_length () != 0
&& ! forward_branch_p (insn)
&& nullify)
break;
default:
- gcc_unreachable ();
+ /* The reversed conditional branch must branch over one additional
+ instruction if the delay slot is filled and needs to be extracted
+ by output_lbranch. If the delay slot is empty or this is a
+ nullified forward branch, the instruction after the reversed
+ condition branch must be nullified. */
+ if (dbr_sequence_length () == 0
+ || (nullify && forward_branch_p (insn)))
+ {
+ nullify = 1;
+ xdelay = 0;
+ operands[4] = GEN_INT (length);
+ }
+ else
+ {
+ xdelay = 1;
+ operands[4] = GEN_INT (length + 4);
+ }
+
+ if (GET_MODE (operands[0]) == DImode)
+ strcpy (buf, "bb,*");
+ else
+ strcpy (buf, "{bvb,|bb,}");
+ if ((which == 0 && negated)
+ || (which == 1 && !negated))
+ strcat (buf, "<");
+ else
+ strcat (buf, ">=");
+ if (nullify)
+ strcat (buf, ",n {%0,.+%4|%0,%%sar,.+%4}");
+ else
+ strcat (buf, " {%0,.+%4|%0,%%sar,.+%4}");
+ output_asm_insn (buf, operands);
+ return output_lbranch (negated ? operands[3] : operands[2],
+ insn, xdelay);
}
return buf;
}
const char *
output_dbra (rtx *operands, rtx insn, int which_alternative)
{
+ int length = get_attr_length (insn);
/* A conditional branch to the following instruction (e.g. the delay slot) is
asking for a disaster. Be prepared! */
if (which_alternative == 0)
{
int nullify = INSN_ANNULLED_BRANCH_P (insn);
- int length = get_attr_length (insn);
+ int xdelay;
/* If this is a long branch with its delay slot unfilled, set `nullify'
as it can nullify the delay slot and save a nop. */
return "addi,%N2 %1,%0,%0\n\tb %3";
default:
- gcc_unreachable ();
+ /* The reversed conditional branch must branch over one additional
+ instruction if the delay slot is filled and needs to be extracted
+ by output_lbranch. If the delay slot is empty or this is a
+ nullified forward branch, the instruction after the reversed
+ condition branch must be nullified. */
+ if (dbr_sequence_length () == 0
+ || (nullify && forward_branch_p (insn)))
+ {
+ nullify = 1;
+ xdelay = 0;
+ operands[4] = GEN_INT (length);
+ }
+ else
+ {
+ xdelay = 1;
+ operands[4] = GEN_INT (length + 4);
+ }
+
+ if (nullify)
+ output_asm_insn ("addib,%N2,n %1,%0,.+%4", operands);
+ else
+ output_asm_insn ("addib,%N2 %1,%0,.+%4", operands);
+
+ return output_lbranch (operands[3], insn, xdelay);
}
}
output_asm_insn ("{fstws|fstw} %0,-16(%%r30)\n\tldw -16(%%r30),%4",
operands);
output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(%%r30)", operands);
- if (get_attr_length (insn) == 24)
+ if (length == 24)
return "{comb|cmpb},%S2 %%r0,%4,%3\n\t{fldws|fldw} -16(%%r30),%0";
- else
+ else if (length == 28)
return "{comclr|cmpclr},%B2 %%r0,%4,%%r0\n\tb %3\n\t{fldws|fldw} -16(%%r30),%0";
+ else
+ {
+ operands[5] = GEN_INT (length - 16);
+ output_asm_insn ("{comb|cmpb},%B2 %%r0,%4,.+%5", operands);
+ output_asm_insn ("{fldws|fldw} -16(%%r30),%0", operands);
+ return output_lbranch (operands[3], insn, 0);
+ }
}
/* Deal with gross reload from memory case. */
else
/* Reload loop counter from memory, the store back to memory
happens in the branch's delay slot. */
output_asm_insn ("ldw %0,%4", operands);
- if (get_attr_length (insn) == 12)
+ if (length == 12)
return "addib,%C2 %1,%4,%3\n\tstw %4,%0";
- else
+ else if (length == 16)
return "addi,%N2 %1,%4,%4\n\tb %3\n\tstw %4,%0";
+ else
+ {
+ operands[5] = GEN_INT (length - 4);
+ output_asm_insn ("addib,%N2 %1,%4,.+%5\n\tstw %4,%0", operands);
+ return output_lbranch (operands[3], insn, 0);
+ }
}
}
-/* Return the output template for emitting a dbra type insn.
+/* Return the output template for emitting a movb type insn.
Note it may perform some output operations on its own before
returning the final output string. */
output_movb (rtx *operands, rtx insn, int which_alternative,
int reverse_comparison)
{
+ int length = get_attr_length (insn);
/* A conditional branch to the following instruction (e.g. the delay slot) is
asking for a disaster. Be prepared! */
if (which_alternative == 0)
{
int nullify = INSN_ANNULLED_BRANCH_P (insn);
- int length = get_attr_length (insn);
+ int xdelay;
/* If this is a long branch with its delay slot unfilled, set `nullify'
as it can nullify the delay slot and save a nop. */
return "or,%N2 %1,%%r0,%0\n\tb %3";
default:
- gcc_unreachable ();
+ /* The reversed conditional branch must branch over one additional
+ instruction if the delay slot is filled and needs to be extracted
+ by output_lbranch. If the delay slot is empty or this is a
+ nullified forward branch, the instruction after the reversed
+ condition branch must be nullified. */
+ if (dbr_sequence_length () == 0
+ || (nullify && forward_branch_p (insn)))
+ {
+ nullify = 1;
+ xdelay = 0;
+ operands[4] = GEN_INT (length);
+ }
+ else
+ {
+ xdelay = 1;
+ operands[4] = GEN_INT (length + 4);
+ }
+
+ if (nullify)
+ output_asm_insn ("movb,%N2,n %1,%0,.+%4", operands);
+ else
+ output_asm_insn ("movb,%N2 %1,%0,.+%4", operands);
+
+ return output_lbranch (operands[3], insn, xdelay);
}
}
- /* Deal with gross reload from FP register case. */
+ /* Deal with gross reload for FP destination register case. */
else if (which_alternative == 1)
{
- /* Move loop counter from FP register to MEM then into a GR,
- increment the GR, store the GR into MEM, and finally reload
- the FP register from MEM from within the branch's delay slot. */
+ /* Move source register to MEM, perform the branch test, then
+ finally load the FP register from MEM from within the branch's
+ delay slot. */
output_asm_insn ("stw %1,-16(%%r30)", operands);
- if (get_attr_length (insn) == 12)
+ if (length == 12)
return "{comb|cmpb},%S2 %%r0,%1,%3\n\t{fldws|fldw} -16(%%r30),%0";
- else
+ else if (length == 16)
return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\t{fldws|fldw} -16(%%r30),%0";
+ else
+ {
+ operands[4] = GEN_INT (length - 4);
+ output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4", operands);
+ output_asm_insn ("{fldws|fldw} -16(%%r30),%0", operands);
+ return output_lbranch (operands[3], insn, 0);
+ }
}
/* Deal with gross reload from memory case. */
else if (which_alternative == 2)
{
/* Reload loop counter from memory, the store back to memory
happens in the branch's delay slot. */
- if (get_attr_length (insn) == 8)
+ if (length == 8)
return "{comb|cmpb},%S2 %%r0,%1,%3\n\tstw %1,%0";
- else
+ else if (length == 12)
return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\tstw %1,%0";
+ else
+ {
+ operands[4] = GEN_INT (length);
+ output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4\n\tstw %1,%0",
+ operands);
+ return output_lbranch (operands[3], insn, 0);
+ }
}
/* Handle SAR as a destination. */
else
{
- if (get_attr_length (insn) == 8)
+ if (length == 8)
return "{comb|cmpb},%S2 %%r0,%1,%3\n\tmtsar %r1";
- else
+ else if (length == 12)
return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\tmtsar %r1";
+ else
+ {
+ operands[4] = GEN_INT (length);
+ output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4\n\tmtsar %r1",
+ operands);
+ return output_lbranch (operands[3], insn, 0);
+ }
}
}
{
xoperands[1] = gen_label_rtx ();
output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ targetm.asm_out.internal_label (asm_out_file, "L",
CODE_LABEL_NUMBER (xoperands[1]));
output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
}
millicode symbol but not an arbitrary external
symbol when generating SOM output. */
xoperands[1] = gen_label_rtx ();
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ targetm.asm_out.internal_label (asm_out_file, "L",
CODE_LABEL_NUMBER (xoperands[1]));
output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
{
xoperands[1] = gen_label_rtx ();
output_asm_insn ("ldo %0-%1(%2),%2", xoperands);
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[1]));
+ targetm.asm_out.internal_label (asm_out_file, "L",
+ CODE_LABEL_NUMBER (xoperands[1]));
}
else
/* ??? This branch may not reach its target. */
output_asm_insn ("nop\n\tb,n %0", xoperands);
/* Delete the jump. */
- PUT_CODE (NEXT_INSN (insn), NOTE);
- NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
+ SET_INSN_DELETED (NEXT_INSN (insn));
return "";
}
call_dest = XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0);
call_decl = SYMBOL_REF_DECL (call_dest);
- local_call = call_decl && (*targetm.binds_local_p) (call_decl);
+ local_call = call_decl && targetm.binds_local_p (call_decl);
/* pc-relative branch. */
if (!TARGET_LONG_CALLS
/* long pc-relative branch sequence. */
else if ((TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
|| (TARGET_64BIT && !TARGET_GAS)
- || (TARGET_GAS && (TARGET_LONG_PIC_PCREL_CALL || local_call)))
+ || (TARGET_GAS && !TARGET_SOM
+ && (TARGET_LONG_PIC_PCREL_CALL || local_call)))
{
length += 20;
int delay_slot_filled = 0;
int seq_length = dbr_sequence_length ();
tree call_decl = SYMBOL_REF_DECL (call_dest);
- int local_call = call_decl && (*targetm.binds_local_p) (call_decl);
+ int local_call = call_decl && targetm.binds_local_p (call_decl);
rtx xoperands[2];
xoperands[0] = call_dest;
/* ??? As far as I can tell, the HP linker doesn't support the
long pc-relative sequence described in the 64-bit runtime
architecture. So, we use a slightly longer indirect call. */
- struct deferred_plabel *p = get_plabel (call_dest);
-
- xoperands[0] = p->internal_label;
+ xoperands[0] = get_deferred_plabel (call_dest);
xoperands[1] = gen_label_rtx ();
/* If this isn't a sibcall, we put the load of %r27 into the
optimize, 0, NULL);
/* Now delete the delay insn. */
- PUT_CODE (NEXT_INSN (insn), NOTE);
- NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
+ SET_INSN_DELETED (NEXT_INSN (insn));
delay_insn_deleted = 1;
}
they don't allow an instruction in the delay slot. */
if (!((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)
&& !(TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
- && !(TARGET_GAS && (TARGET_LONG_PIC_PCREL_CALL || local_call))
+ && !(TARGET_GAS && !TARGET_SOM
+ && (TARGET_LONG_PIC_PCREL_CALL || local_call))
&& !TARGET_64BIT)
indirect_call = 1;
NULL);
/* Now delete the delay insn. */
- PUT_CODE (NEXT_INSN (insn), NOTE);
- NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
+ SET_INSN_DELETED (NEXT_INSN (insn));
delay_insn_deleted = 1;
}
xoperands[1] = gen_label_rtx ();
output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ targetm.asm_out.internal_label (asm_out_file, "L",
CODE_LABEL_NUMBER (xoperands[1]));
output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
}
- else if (TARGET_GAS && (TARGET_LONG_PIC_PCREL_CALL || local_call))
+ else if (TARGET_GAS && !TARGET_SOM
+ && (TARGET_LONG_PIC_PCREL_CALL || local_call))
{
/* GAS currently can't generate the relocations that
are needed for the SOM linker under HP-UX using this
essentially an inline implementation of $$dyncall.
We don't actually try to call $$dyncall as this is
as difficult as calling the function itself. */
- struct deferred_plabel *p = get_plabel (call_dest);
-
- xoperands[0] = p->internal_label;
+ xoperands[0] = get_deferred_plabel (call_dest);
xoperands[1] = gen_label_rtx ();
/* Since the call is indirect, FP arguments in registers
{
xoperands[1] = gen_label_rtx ();
output_asm_insn ("ldo %0-%1(%%r2),%%r2", xoperands);
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[1]));
+ targetm.asm_out.internal_label (asm_out_file, "L",
+ CODE_LABEL_NUMBER (xoperands[1]));
}
else
output_asm_insn ("nop\n\tb,n %0", xoperands);
output_asm_insn ("b,n %0", xoperands);
/* Delete the jump. */
- PUT_CODE (NEXT_INSN (insn), NOTE);
- NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
+ SET_INSN_DELETED (NEXT_INSN (insn));
return "";
}
if (TARGET_FAST_INDIRECT_CALLS
|| (!TARGET_PORTABLE_RUNTIME
- && ((TARGET_PA_20 && distance < 7600000) || distance < 240000)))
+ && ((TARGET_PA_20 && !TARGET_SOM && distance < 7600000)
+ || distance < 240000)))
return 8;
if (flag_pic)
the remaining cases. */
if (attr_length_indirect_call (insn) == 8)
{
- /* The HP linker substitutes a BLE for millicode calls using
- the short PIC PCREL form. Thus, we must use %r31 as the
- link register when generating PA 1.x code. */
- if (TARGET_PA_20)
+ /* The HP linker sometimes substitutes a BLE for BL/B,L calls to
+ $$dyncall. Since BLE uses %r31 as the link register, the 22-bit
+ variant of the B,L instruction can't be used on the SOM target. */
+ if (TARGET_PA_20 && !TARGET_SOM)
return ".CALL\tARGW0=GR\n\tb,l $$dyncall,%%r2\n\tcopy %%r2,%%r31";
else
return ".CALL\tARGW0=GR\n\tbl $$dyncall,%%r31\n\tcopy %%r31,%%r2";
{
xoperands[0] = gen_label_rtx ();
output_asm_insn ("addil L'$$dyncall-%0,%%r1", xoperands);
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
- CODE_LABEL_NUMBER (xoperands[0]));
+ targetm.asm_out.internal_label (asm_out_file, "L",
+ CODE_LABEL_NUMBER (xoperands[0]));
output_asm_insn ("ldo R'$$dyncall-%0(%%r1),%%r1", xoperands);
}
else
static void
pa_encode_section_info (tree decl, rtx rtl, int first)
{
+ int old_referenced = 0;
+
+ if (!first && MEM_P (rtl) && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF)
+ old_referenced
+ = SYMBOL_REF_FLAGS (XEXP (rtl, 0)) & SYMBOL_FLAG_REFERENCED;
+
default_encode_section_info (decl, rtl, first);
if (first && TEXT_SPACE_P (decl))
if (TREE_CODE (decl) == FUNCTION_DECL)
hppa_encode_label (XEXP (rtl, 0));
}
+ else if (old_referenced)
+ SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= old_referenced;
}
/* This is sort of inverse to pa_encode_section_info. */
fprintf (file, "\t.EXIT\n\t.PROCEND\n");
+ if (TARGET_SOM && TARGET_GAS)
+ {
+ /* We done with this subspace except possibly for some additional
+ debug information. Forget that we are in this subspace to ensure
+ that the next function is output in its own subspace. */
+ in_section = NULL;
+ cfun->machine->in_nsubspa = 2;
+ }
+
if (TARGET_SOM && flag_pic && TREE_PUBLIC (function))
{
- data_section ();
+ switch_to_section (data_section);
output_asm_insn (".align 4", xoperands);
ASM_OUTPUT_LABEL (file, label);
output_asm_insn (".word P'%0", xoperands);
}
- else if (TARGET_SOM && TARGET_GAS)
- forget_section ();
current_thunk_number++;
nbytes = ((nbytes + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)
space register selection rules for memory addresses. Therefore, we
don't consider a + b == b + a, as this might be inside a MEM. */
static bool
-pa_commutative_p (rtx x, int outer_code)
+pa_commutative_p (const_rtx x, int outer_code)
{
return (COMMUTATIVE_P (x)
&& (TARGET_NO_SPACE_REGS
unsigned HOST_WIDE_INT size,
unsigned int align)
{
- bss_section ();
+ switch_to_section (bss_section);
fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT);
#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
align = max_common_align;
}
- bss_section ();
+ switch_to_section (bss_section);
assemble_name (stream, name);
fprintf (stream, "\t.comm "HOST_WIDE_INT_PRINT_UNSIGNED"\n",
unsigned HOST_WIDE_INT size,
unsigned int align)
{
- bss_section ();
+ switch_to_section (bss_section);
fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT);
#ifdef LOCAL_ASM_OP
/* Output an unconditional move and branch insn. */
const char *
-output_parallel_movb (rtx *operands, int length)
+output_parallel_movb (rtx *operands, rtx insn)
{
+ int length = get_attr_length (insn);
+
/* These are the cases in which we win. */
if (length == 4)
return "mov%I1b,tr %1,%0,%2";
- /* None of these cases wins, but they don't lose either. */
- if (dbr_sequence_length () == 0)
+ /* None of the following cases win, but they don't lose either. */
+ if (length == 8)
{
- /* Nothing in the delay slot, fake it by putting the combined
- insn (the copy or add) in the delay slot of a bl. */
- if (GET_CODE (operands[1]) == CONST_INT)
- return "b %2\n\tldi %1,%0";
+ if (dbr_sequence_length () == 0)
+ {
+ /* Nothing in the delay slot, fake it by putting the combined
+ insn (the copy or add) in the delay slot of a bl. */
+ if (GET_CODE (operands[1]) == CONST_INT)
+ return "b %2\n\tldi %1,%0";
+ else
+ return "b %2\n\tcopy %1,%0";
+ }
else
- return "b %2\n\tcopy %1,%0";
+ {
+ /* Something in the delay slot, but we've got a long branch. */
+ if (GET_CODE (operands[1]) == CONST_INT)
+ return "ldi %1,%0\n\tb %2";
+ else
+ return "copy %1,%0\n\tb %2";
+ }
}
+
+ if (GET_CODE (operands[1]) == CONST_INT)
+ output_asm_insn ("ldi %1,%0", operands);
else
- {
- /* Something in the delay slot, but we've got a long branch. */
- if (GET_CODE (operands[1]) == CONST_INT)
- return "ldi %1,%0\n\tb %2";
- else
- return "copy %1,%0\n\tb %2";
- }
+ output_asm_insn ("copy %1,%0", operands);
+ return output_lbranch (operands[2], insn, 1);
}
/* Output an unconditional add and branch insn. */
const char *
-output_parallel_addb (rtx *operands, int length)
+output_parallel_addb (rtx *operands, rtx insn)
{
+ int length = get_attr_length (insn);
+
/* To make life easy we want operand0 to be the shared input/output
operand and operand1 to be the readonly operand. */
if (operands[0] == operands[1])
if (length == 4)
return "add%I1b,tr %1,%0,%3";
- /* None of these cases win, but they don't lose either. */
- if (dbr_sequence_length () == 0)
+ /* None of the following cases win, but they don't lose either. */
+ if (length == 8)
{
- /* Nothing in the delay slot, fake it by putting the combined
- insn (the copy or add) in the delay slot of a bl. */
- return "b %3\n\tadd%I1 %1,%0,%0";
- }
- else
- {
- /* Something in the delay slot, but we've got a long branch. */
- return "add%I1 %1,%0,%0\n\tb %3";
+ if (dbr_sequence_length () == 0)
+ /* Nothing in the delay slot, fake it by putting the combined
+ insn (the copy or add) in the delay slot of a bl. */
+ return "b %3\n\tadd%I1 %1,%0,%0";
+ else
+ /* Something in the delay slot, but we've got a long branch. */
+ return "add%I1 %1,%0,%0\n\tb %3";
}
+
+ output_asm_insn ("add%I1 %1,%0,%0", operands);
+ return output_lbranch (operands[3], insn, 1);
}
/* Return nonzero if INSN (a jump insn) immediately follows a call
PATTERN (floater))),
anchor);
- PUT_CODE (anchor, NOTE);
- NOTE_LINE_NUMBER (anchor) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (anchor) = 0;
+ SET_INSN_DELETED (anchor);
/* Emit a special USE insn for FLOATER, then delete
the floating insn. */
anchor);
JUMP_LABEL (temp) = JUMP_LABEL (anchor);
- PUT_CODE (anchor, NOTE);
- NOTE_LINE_NUMBER (anchor) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (anchor) = 0;
+ SET_INSN_DELETED (anchor);
/* Emit a special USE insn for FLOATER, then delete
the floating insn. */
to match the HP Compiler ABI. */
rtx
-function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
+function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
{
enum machine_mode valmode;
- if (AGGREGATE_TYPE_P (valtype))
+ if (AGGREGATE_TYPE_P (valtype)
+ || TREE_CODE (valtype) == COMPLEX_TYPE
+ || TREE_CODE (valtype) == VECTOR_TYPE)
{
if (TARGET_64BIT)
{
}
if ((INTEGRAL_TYPE_P (valtype)
- && TYPE_PRECISION (valtype) < BITS_PER_WORD)
+ && GET_MODE_BITSIZE (TYPE_MODE (valtype)) < BITS_PER_WORD)
|| POINTER_TYPE_P (valtype))
valmode = word_mode;
else
this routine should return zero. pa_arg_partial_bytes will
handle arguments which are split between regs and stack slots if
the ABI mandates split arguments. */
- if (! TARGET_64BIT)
+ if (!TARGET_64BIT)
{
/* The 32-bit ABI does not split arguments. */
if (cum->words + arg_size > max_arg_words)
treatment. */
if (arg_size > 1
|| mode == BLKmode
- || (type && AGGREGATE_TYPE_P (type)))
+ || (type && (AGGREGATE_TYPE_P (type)
+ || TREE_CODE (type) == COMPLEX_TYPE
+ || TREE_CODE (type) == VECTOR_TYPE)))
{
/* Double-extended precision (80-bit), quad-precision (128-bit)
and aggregates including complex numbers are aligned on
objects. The data is right-justified and zero-extended
to 64 bits. This is opposite to the normal justification
used on big endian targets and requires special treatment.
- We now define BLOCK_REG_PADDING to pad these objects. */
- if (mode == BLKmode || (type && AGGREGATE_TYPE_P (type)))
+ We now define BLOCK_REG_PADDING to pad these objects.
+ Aggregates, complex and vector types are passed in the same
+ manner as structures. */
+ if (mode == BLKmode
+ || (type && (AGGREGATE_TYPE_P (type)
+ || TREE_CODE (type) == COMPLEX_TYPE
+ || TREE_CODE (type) == VECTOR_TYPE)))
{
rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (DImode, gpr_reg_base),
/* If we are doing soft-float with portable runtime, then there
is no need to worry about FP regs. */
&& !TARGET_SOFT_FLOAT
- /* The parameter must be some kind of float, else we can just
+ /* The parameter must be some kind of scalar float, else we just
pass it in integer registers. */
- && FLOAT_MODE_P (mode)
+ && GET_MODE_CLASS (mode) == MODE_FLOAT
/* The target function must not have a prototype. */
&& cum->nargs_prototype <= 0
/* libcalls do not need to pass items in both FP and general
&& !TARGET_GAS
&& !cum->incoming
&& cum->indirect
- && FLOAT_MODE_P (mode)))
+ && GET_MODE_CLASS (mode) == MODE_FLOAT))
{
retval
= gen_rtx_PARALLEL
&& !TARGET_64BIT
&& !TARGET_ELF32
&& cum->indirect)
- /* If the parameter is not a floating point parameter, then
- it belongs in GPRs. */
- || !FLOAT_MODE_P (mode)
+ /* If the parameter is not a scalar floating-point parameter,
+ then it belongs in GPRs. */
+ || GET_MODE_CLASS (mode) != MODE_FLOAT
/* Structure with single SFmode field belongs in GPR. */
|| (type && AGGREGATE_TYPE_P (type)))
retval = gen_rtx_REG (mode, gpr_reg_base);
}
-/* Return a string to output before text in the current function.
+/* A get_unnamed_section callback for switching to the text section.
This function is only used with SOM. Because we don't support
named subspaces, we can only create a new subspace or switch back
to the default text subspace. */
-const char *
-som_text_section_asm_op (void)
-{
- if (!TARGET_SOM)
- return "";
+static void
+som_output_text_section_asm_op (const void *data ATTRIBUTE_UNUSED)
+{
+ gcc_assert (TARGET_SOM);
if (TARGET_GAS)
{
- if (cfun && !cfun->machine->in_nsubspa)
+ if (cfun && cfun->machine && !cfun->machine->in_nsubspa)
{
/* We only want to emit a .nsubspa directive once at the
start of the function. */
if (cfun->decl
&& DECL_ONE_ONLY (cfun->decl)
&& !DECL_WEAK (cfun->decl))
- return
- "\t.SPACE $TEXT$\n\t.NSUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,SORT=24,COMDAT";
-
- return "\t.SPACE $TEXT$\n\t.NSUBSPA $CODE$";
+ {
+ output_section_asm_op ("\t.SPACE $TEXT$\n"
+ "\t.NSUBSPA $CODE$,QUAD=0,ALIGN=8,"
+ "ACCESS=44,SORT=24,COMDAT");
+ return;
+ }
}
else
{
/* There isn't a current function or the body of the current
function has been completed. So, we are changing to the
- text section to output debugging information. Do this in
- the default text section. We need to forget that we are
- in the text section so that the function text_section in
- varasm.c will call us the next time around. */
- forget_section ();
- }
+ text section to output debugging information. Thus, we
+ need to forget that we are in the text section so that
+ varasm.c will call us when text_section is selected again. */
+ gcc_assert (!cfun || !cfun->machine
+ || cfun->machine->in_nsubspa == 2);
+ in_section = NULL;
+ }
+ output_section_asm_op ("\t.SPACE $TEXT$\n\t.NSUBSPA $CODE$");
+ return;
}
+ output_section_asm_op ("\t.SPACE $TEXT$\n\t.SUBSPA $CODE$");
+}
- return "\t.SPACE $TEXT$\n\t.SUBSPA $CODE$";
+/* A get_unnamed_section callback for switching to comdat data
+ sections. This function is only used with SOM. */
+
+static void
+som_output_comdat_data_section_asm_op (const void *data)
+{
+ in_section = NULL;
+ output_section_asm_op (data);
+}
+
+/* Implement TARGET_ASM_INITIALIZE_SECTIONS */
+
+static void
+pa_som_asm_init_sections (void)
+{
+ text_section
+ = get_unnamed_section (0, som_output_text_section_asm_op, NULL);
+
+ /* SOM puts readonly data in the default $LIT$ subspace when PIC code
+ is not being generated. */
+ som_readonly_data_section
+ = get_unnamed_section (0, output_section_asm_op,
+ "\t.SPACE $TEXT$\n\t.SUBSPA $LIT$");
+
+ /* When secondary definitions are not supported, SOM makes readonly
+ data one-only by creating a new $LIT$ subspace in $TEXT$ with
+ the comdat flag. */
+ som_one_only_readonly_data_section
+ = get_unnamed_section (0, som_output_comdat_data_section_asm_op,
+ "\t.SPACE $TEXT$\n"
+ "\t.NSUBSPA $LIT$,QUAD=0,ALIGN=8,"
+ "ACCESS=0x2c,SORT=16,COMDAT");
+
+
+ /* When secondary definitions are not supported, SOM makes data one-only
+ by creating a new $DATA$ subspace in $PRIVATE$ with the comdat flag. */
+ som_one_only_data_section
+ = get_unnamed_section (SECTION_WRITE,
+ som_output_comdat_data_section_asm_op,
+ "\t.SPACE $PRIVATE$\n"
+ "\t.NSUBSPA $DATA$,QUAD=1,ALIGN=8,"
+ "ACCESS=31,SORT=24,COMDAT");
+
+ /* FIXME: HPUX ld generates incorrect GOT entries for "T" fixups
+ which reference data within the $TEXT$ space (for example constant
+ strings in the $LIT$ subspace).
+
+ The assemblers (GAS and HP as) both have problems with handling
+ the difference of two symbols which is the other correct way to
+ reference constant data during PIC code generation.
+
+ So, there's no way to reference constant data which is in the
+ $TEXT$ space during PIC generation. Instead place all constant
+ data into the $PRIVATE$ subspace (this reduces sharing, but it
+ works correctly). */
+ readonly_data_section = flag_pic ? data_section : som_readonly_data_section;
+
+ /* We must not have a reference to an external symbol defined in a
+ shared library in a readonly section, else the SOM linker will
+ complain.
+
+ So, we force exception information into the data section. */
+ exception_section = data_section;
}
/* On hpux10, the linker will give an error if we have a reference
library. Therefore, expressions that might require a reloc can
not be placed in the read-only data section. */
-static void
+static section *
pa_select_section (tree exp, int reloc,
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
if (TARGET_SOM
&& DECL_ONE_ONLY (exp)
&& !DECL_WEAK (exp))
- som_one_only_readonly_data_section ();
+ return som_one_only_readonly_data_section;
else
- readonly_data_section ();
+ return readonly_data_section;
}
else if (CONSTANT_CLASS_P (exp) && !reloc)
- readonly_data_section ();
+ return readonly_data_section;
else if (TARGET_SOM
&& TREE_CODE (exp) == VAR_DECL
&& DECL_ONE_ONLY (exp)
&& !DECL_WEAK (exp))
- som_one_only_data_section ();
+ return som_one_only_data_section;
else
- data_section ();
+ return data_section;
}
static void
/* Worker function for TARGET_RETURN_IN_MEMORY. */
bool
-pa_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
+pa_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
{
/* SOM ABI says that objects larger than 64 bits are returned in memory.
PA64 ABI says that objects larger than 128 bits are returned in memory.
unsigned int i;
extern_symbol *p;
+ if (!NO_DEFERRED_PROFILE_COUNTERS)
+ output_deferred_profile_counters ();
+
output_deferred_plabels ();
for (i = 0; VEC_iterate (extern_symbol, extern_symbols, i, p); i++)
}
#endif
+/* Return true if a change from mode FROM to mode TO for a register
+ in register class CLASS is invalid. */
+
+bool
+pa_cannot_change_mode_class (enum machine_mode from, enum machine_mode to,
+ enum reg_class class)
+{
+ if (from == 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. */
+ if (MAYBE_FP_REG_CLASS_P (class))
+ return true;
+
+ /* 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;
+}
+
+/* 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.
+
+ 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
+ in the floating-point registers. */
+
+bool
+pa_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2)
+{
+ /* Don't tie modes in different classes. */
+ if (GET_MODE_CLASS (mode1) != GET_MODE_CLASS (mode2))
+ return false;
+
+ return true;
+}
+
#include "gt-pa.h"