You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
#include "config.h"
#include "system.h"
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 *);
static rtx hppa_builtin_saveregs (void);
static tree hppa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
static bool pa_scalar_mode_supported_p (enum machine_mode);
+static bool pa_commutative_p (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
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 *);
+/* The following extra sections are only used for SOM. */
+static GTY(()) section *som_readonly_data_section;
+static GTY(()) section *som_one_only_readonly_data_section;
+static GTY(()) section *som_one_only_data_section;
+
/* Save the operands last given to a compare for use when we
generate a scc or bcc insn. */
rtx hppa_compare_op0, hppa_compare_op1;
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL pa_function_ok_for_sibcall
+#undef TARGET_COMMUTATIVE_P
+#define TARGET_COMMUTATIVE_P pa_commutative_p
+
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK pa_asm_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#undef TARGET_SCALAR_MODE_SUPPORTED_P
#define TARGET_SCALAR_MODE_SUPPORTED_P pa_scalar_mode_supported_p
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM pa_tls_referenced_p
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD pa_secondary_reload
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
/* Parse the -mfixed-range= option string. */
REG2 are either register names or register numbers. The effect
of this option is to mark the registers in the range from REG1 to
REG2 as ``fixed'' so they won't be used by the compiler. This is
- used, e.g., to ensure that kernel mode code doesn't use f32-f127. */
+ used, e.g., to ensure that kernel mode code doesn't use fr4-fr31. */
i = strlen (const_str);
str = (char *) alloca (i + 1);
dash = strchr (str, '-');
if (!dash)
{
- warning ("value of -mfixed-range must have form REG1-REG2");
+ warning (0, "value of -mfixed-range must have form REG1-REG2");
return;
}
*dash = '\0';
first = decode_reg_name (str);
if (first < 0)
{
- warning ("unknown register name: %s", str);
+ warning (0, "unknown register name: %s", str);
return;
}
last = decode_reg_name (dash + 1);
if (last < 0)
{
- warning ("unknown register name: %s", dash + 1);
+ warning (0, "unknown register name: %s", dash + 1);
return;
}
if (first > last)
{
- warning ("%s-%s is an empty range", str, dash + 1);
+ warning (0, "%s-%s is an empty range", str, dash + 1);
return;
}
if (flag_pic && TARGET_PORTABLE_RUNTIME)
{
- warning ("PIC code generation is not supported in the portable runtime model\n");
+ warning (0, "PIC code generation is not supported in the portable runtime model");
}
if (flag_pic && TARGET_FAST_INDIRECT_CALLS)
{
- warning ("PIC code generation is not compatible with fast indirect calls\n");
+ warning (0, "PIC code generation is not compatible with fast indirect calls");
}
if (! TARGET_GAS && write_symbols != NO_DEBUG)
{
- warning ("-g is only supported when using GAS on this processor,");
- warning ("-g option disabled");
+ warning (0, "-g is only supported when using GAS on this processor,");
+ warning (0, "-g option disabled");
write_symbols = NO_DEBUG;
}
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
}
{
rtx pic_ref = orig;
+ gcc_assert (!PA_SYMBOL_REF_TLS_P (orig));
+
/* Labels need special handling. */
if (pic_label_operand (orig, mode))
{
return pic_ref;
}
+static GTY(()) rtx gen_tls_tga;
+
+static rtx
+gen_tls_get_addr (void)
+{
+ if (!gen_tls_tga)
+ gen_tls_tga = init_one_libfunc ("__tls_get_addr");
+ return gen_tls_tga;
+}
+
+static rtx
+hppa_tls_call (rtx arg)
+{
+ rtx ret;
+
+ ret = gen_reg_rtx (Pmode);
+ emit_library_call_value (gen_tls_get_addr (), ret,
+ LCT_CONST, Pmode, 1, arg, Pmode);
+
+ return ret;
+}
+
+static rtx
+legitimize_tls_address (rtx addr)
+{
+ rtx ret, insn, tmp, t1, t2, tp;
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (addr);
+
+ switch (model)
+ {
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ tmp = gen_reg_rtx (Pmode);
+ emit_insn (gen_tgd_load (tmp, addr));
+ ret = hppa_tls_call (tmp);
+ break;
+
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ ret = gen_reg_rtx (Pmode);
+ tmp = gen_reg_rtx (Pmode);
+ start_sequence ();
+ emit_insn (gen_tld_load (tmp, addr));
+ t1 = hppa_tls_call (tmp);
+ insn = get_insns ();
+ end_sequence ();
+ t2 = gen_reg_rtx (Pmode);
+ emit_libcall_block (insn, t2, t1,
+ gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
+ UNSPEC_TLSLDBASE));
+ emit_insn (gen_tld_offset_load (ret, addr, t2));
+ break;
+
+ case TLS_MODEL_INITIAL_EXEC:
+ tp = gen_reg_rtx (Pmode);
+ tmp = gen_reg_rtx (Pmode);
+ ret = gen_reg_rtx (Pmode);
+ emit_insn (gen_tp_load (tp));
+ emit_insn (gen_tie_load (tmp, addr));
+ emit_move_insn (ret, gen_rtx_PLUS (Pmode, tp, tmp));
+ break;
+
+ case TLS_MODEL_LOCAL_EXEC:
+ tp = gen_reg_rtx (Pmode);
+ ret = gen_reg_rtx (Pmode);
+ emit_insn (gen_tp_load (tp));
+ emit_insn (gen_tle_load (ret, addr, tp));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ return ret;
+}
+
/* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address.
This macro is used in only one place: `memory_address' in explow.c.
&& !REG_POINTER (XEXP (x, 1)))
return gen_rtx_PLUS (Pmode, XEXP (x, 1), XEXP (x, 0));
- if (flag_pic)
+ if (PA_SYMBOL_REF_TLS_P (x))
+ return legitimize_tls_address (x);
+ else if (flag_pic)
return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode));
/* Strip off CONST. */
return gen_rtx_REG (mode, REGNO (orig));
}
+/* Return 1 if *X is a thread-local symbol. */
+
+static int
+pa_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+ return PA_SYMBOL_REF_TLS_P (*x);
+}
+
+/* Return 1 if X contains a thread-local symbol. */
+
+bool
+pa_tls_referenced_p (rtx x)
+{
+ if (!TARGET_HAVE_TLS)
+ return false;
+
+ return for_each_rtx (&x, &pa_tls_symbol_ref_1, 0);
+}
+
/* Emit insns to move operands[1] into operands[0].
Return 1 if we have written out everything that needs to be done to
if (scratch_reg && reload_in_progress && GET_CODE (operand0) == MEM
&& ((tem = find_replacement (&XEXP (operand0, 0)))
!= XEXP (operand0, 0)))
- operand0 = gen_rtx_MEM (GET_MODE (operand0), tem);
+ operand0 = replace_equiv_address (operand0, tem);
if (scratch_reg && reload_in_progress && GET_CODE (operand1) == MEM
&& ((tem = find_replacement (&XEXP (operand1, 0)))
!= XEXP (operand1, 0)))
- operand1 = gen_rtx_MEM (GET_MODE (operand1), tem);
+ operand1 = replace_equiv_address (operand1, tem);
/* Handle secondary reloads for loads/stores of FP registers from
REG+D addresses where D does not fit in 5 or 14 bits, including
else
emit_move_insn (scratch_reg, XEXP (operand1, 0));
emit_insn (gen_rtx_SET (VOIDmode, operand0,
- gen_rtx_MEM (mode, scratch_reg)));
+ replace_equiv_address (operand1, scratch_reg)));
return 1;
}
else if (scratch_reg
}
else
emit_move_insn (scratch_reg, XEXP (operand0, 0));
- emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_MEM (mode, scratch_reg),
+ emit_insn (gen_rtx_SET (VOIDmode,
+ replace_equiv_address (operand0, scratch_reg),
operand1));
return 1;
}
&& CONSTANT_P (operand1)
&& fp_reg_operand (operand0, mode))
{
- rtx xoperands[2];
+ rtx const_mem, xoperands[2];
/* SCRATCH_REG will hold an address and maybe the actual data. We want
it in WORD_MODE regardless of what mode it was originally given
/* Force the constant into memory and put the address of the
memory location into scratch_reg. */
+ const_mem = force_const_mem (mode, operand1);
xoperands[0] = scratch_reg;
- xoperands[1] = XEXP (force_const_mem (mode, operand1), 0);
+ xoperands[1] = XEXP (const_mem, 0);
emit_move_sequence (xoperands, Pmode, 0);
/* Now load the destination register. */
emit_insn (gen_rtx_SET (mode, operand0,
- gen_rtx_MEM (mode, scratch_reg)));
+ replace_equiv_address (const_mem, scratch_reg)));
return 1;
}
/* Handle secondary reloads for SAR. These occur when trying to load
OPERAND0. */
scratch_reg = force_mode (GET_MODE (operand0), scratch_reg);
- emit_move_insn (scratch_reg, gen_rtx_MEM (GET_MODE (operand0),
- scratch_reg));
+ emit_move_insn (scratch_reg,
+ replace_equiv_address (operand1, scratch_reg));
}
else
{
&& (reload_completed || reload_in_progress)
&& flag_pic)
{
- operands[1] = force_const_mem (mode, operand1);
- operands[1] = legitimize_pic_address (XEXP (operands[1], 0),
+ rtx const_mem = force_const_mem (mode, operand1);
+ operands[1] = legitimize_pic_address (XEXP (const_mem, 0),
mode, temp);
- operands[1] = gen_rtx_MEM (mode, operands[1]);
+ operands[1] = replace_equiv_address (const_mem, operands[1]);
emit_move_sequence (operands, mode, temp);
}
else
}
return 1;
}
+ else if (pa_tls_referenced_p (operand1))
+ {
+ rtx tmp = operand1;
+ rtx addend = NULL;
+
+ if (GET_CODE (tmp) == CONST && GET_CODE (XEXP (tmp, 0)) == PLUS)
+ {
+ addend = XEXP (XEXP (tmp, 0), 1);
+ tmp = XEXP (XEXP (tmp, 0), 0);
+ }
+
+ gcc_assert (GET_CODE (tmp) == SYMBOL_REF);
+ tmp = legitimize_tls_address (tmp);
+ if (addend)
+ {
+ tmp = gen_rtx_PLUS (mode, tmp, addend);
+ tmp = force_operand (tmp, operands[0]);
+ }
+ operands[1] = tmp;
+ }
else if (GET_CODE (operand1) != CONST_INT
|| !cint_ok_for_move (INTVAL (operand1)))
{
case CONSTRUCTOR:
{
- register tree link;
- for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
- if (TREE_VALUE (link) != 0)
- reloc |= reloc_needed (TREE_VALUE (link));
+ tree value;
+ unsigned HOST_WIDE_INT ix;
+
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), ix, value)
+ if (value)
+ reloc |= reloc_needed (value);
}
break;
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
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
{
/* The SAVE_SP flag is used to indicate that register %r3 is stored
at the beginning of the frame and that it is used as the frame
pointer for the frame. We do this because our current frame
- layout doesn't conform to that specified in the the HP runtime
+ layout doesn't conform to that specified in the HP runtime
documentation and we need a way to indicate to programs such as
GDB where %r3 is saved. The SAVE_SP flag was chosen because it
isn't used by HP compilers but is supported by the assembler.
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)
/* 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 ())
return get_hard_reg_initial_val (word_mode, PIC_OFFSET_TABLE_REGNUM);
}
+#ifndef NO_DEFERRED_PROFILE_COUNTERS
+#define NO_DEFERRED_PROFILE_COUNTERS 0
+#endif
+
+/* Define heap vector type for funcdef numbers. */
+DEF_VEC_I(int);
+DEF_VEC_ALLOC_I(int,heap);
+
+/* 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_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));
}
#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);
}
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;
+ /* Trying to load a constant into a FP register during PIC code
+ generation requires %r1 as a scratch register. */
+ if (flag_pic
+ && (mode == SImode || mode == DImode)
+ && FP_REG_CLASS_P (class)
+ && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE))
+ {
+ sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1
+ : CODE_FOR_reload_indi_r1);
+ return NO_REGS;
+ }
+
+ /* Profiling showed the PA port spends about 1.3% of its compilation
+ time in true_regnum from calls inside pa_secondary_reload_class. */
+ if (regno >= FIRST_PSEUDO_REGISTER || GET_CODE (x) == SUBREG)
+ regno = true_regnum (x);
+
/* Handle out of range displacement for integer mode loads/stores of
FP registers. */
if (((regno >= FIRST_PSEUDO_REGISTER || regno == -1)
&& GET_MODE_CLASS (mode) == MODE_INT
&& FP_REG_CLASS_P (class))
|| (class == SHIFT_REGS && (regno <= 0 || regno >= 32)))
- return GENERAL_REGS;
+ {
+ sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
+ return NO_REGS;
+ }
/* A SAR<->FP register copy requires a secondary register (GPR) as
well as secondary memory. */
if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER
&& ((REGNO_REG_CLASS (regno) == SHIFT_REGS && FP_REG_CLASS_P (class))
- || (class == SHIFT_REGS && FP_REG_CLASS_P (REGNO_REG_CLASS (regno)))))
- return GENERAL_REGS;
+ || (class == SHIFT_REGS
+ && FP_REG_CLASS_P (REGNO_REG_CLASS (regno)))))
+ {
+ sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
+ return NO_REGS;
+ }
- if (GET_CODE (in) == HIGH)
- in = XEXP (in, 0);
+ /* Secondary reloads of symbolic operands require %r1 as a scratch
+ register when we're generating PIC code and the operand isn't
+ readonly. */
+ if (GET_CODE (x) == HIGH)
+ x = XEXP (x, 0);
/* Profiling has showed GCC spends about 2.6% of its compilation
- time in symbolic_operand from calls inside secondary_reload_class.
-
- We use an inline copy and only compute its return value once to avoid
- useless work. */
- switch (GET_CODE (in))
+ time in symbolic_operand from calls inside pa_secondary_reload_class.
+ So, we use an inline copy to avoid useless work. */
+ switch (GET_CODE (x))
{
- rtx tmp;
+ rtx op;
case SYMBOL_REF:
+ is_symbolic = !SYMBOL_REF_TLS_MODEL (x);
+ break;
case LABEL_REF:
is_symbolic = 1;
break;
case CONST:
- tmp = XEXP (in, 0);
- is_symbolic = ((GET_CODE (XEXP (tmp, 0)) == SYMBOL_REF
- || GET_CODE (XEXP (tmp, 0)) == LABEL_REF)
- && GET_CODE (XEXP (tmp, 1)) == CONST_INT);
+ op = XEXP (x, 0);
+ is_symbolic = (((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ && !SYMBOL_REF_TLS_MODEL (XEXP (op, 0)))
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT);
break;
-
default:
is_symbolic = 0;
break;
}
- if (!flag_pic
- && is_symbolic
- && read_only_operand (in, VOIDmode))
- return NO_REGS;
-
- if (class != R1_REGS && is_symbolic)
- return R1_REGS;
+ if (is_symbolic && (flag_pic || !read_only_operand (x, VOIDmode)))
+ {
+ gcc_assert (mode == SImode || mode == DImode);
+ sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1
+ : CODE_FOR_reload_indi_r1);
+ }
return NO_REGS;
}
/* 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);
+ t = build2 (MINUS_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);
+ t = build2 (BIT_AND_EXPR, valist_type, t, u);
- 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);
+ t = build2 (PLUS_EXPR, valist_type, t, u);
}
t = fold_convert (ptr, t);
- t = build_fold_indirect_ref (t);
+ t = build_va_arg_indirect_ref (t);
if (indirect)
- t = build_fold_indirect_ref (t);
+ t = build_va_arg_indirect_ref (t);
return t;
}
return true;
return false;
+ case MODE_DECIMAL_FLOAT:
+ return false;
+
default:
gcc_unreachable ();
}
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. */
/* ??? 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
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
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";
static void
pa_encode_section_info (tree decl, rtx rtl, int first)
{
+ default_encode_section_info (decl, rtl, first);
+
if (first && TEXT_SPACE_P (decl))
{
SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
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)
return (decl && !TREE_PUBLIC (decl));
}
+/* ??? Addition is not commutative on the PA due to the weird implicit
+ space register selection rules for memory addresses. Therefore, we
+ don't consider a + b == b + a, as this might be inside a MEM. */
+static bool
+pa_commutative_p (rtx x, int outer_code)
+{
+ return (COMMUTATIVE_P (x)
+ && (TARGET_NO_SPACE_REGS
+ || (outer_code != UNKNOWN && outer_code != MEM)
+ || GET_CODE (x) != PLUS));
+}
+
/* Returns 1 if the 6 operands specified in OPERANDS are suitable for
use in fmpyadd instructions. */
int
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
max_common_align = TARGET_64BIT ? 128 : (size >= 4096 ? 256 : 64);
if (align > max_common_align)
{
- warning ("alignment (%u) for %s exceeds maximum alignment "
+ warning (0, "alignment (%u) for %s exceeds maximum alignment "
"for global common data. Using %u",
align / BITS_PER_UNIT, name, max_common_align / BITS_PER_UNIT);
align = max_common_align;
}
- 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
{
enum machine_mode valmode;
- /* Aggregates with a size less than or equal to 128 bits are returned
- in GR 28(-29). They are left justified. The pad bits are undefined.
- Larger aggregates are returned in memory. */
- if (TARGET_64BIT && AGGREGATE_TYPE_P (valtype))
+ if (AGGREGATE_TYPE_P (valtype)
+ || TREE_CODE (valtype) == COMPLEX_TYPE
+ || TREE_CODE (valtype) == VECTOR_TYPE)
{
- rtx loc[2];
- int i, offset = 0;
- int ub = int_size_in_bytes (valtype) <= UNITS_PER_WORD ? 1 : 2;
+ if (TARGET_64BIT)
+ {
+ /* Aggregates with a size less than or equal to 128 bits are
+ returned in GR 28(-29). They are left justified. The pad
+ bits are undefined. Larger aggregates are returned in
+ memory. */
+ rtx loc[2];
+ int i, offset = 0;
+ int ub = int_size_in_bytes (valtype) <= UNITS_PER_WORD ? 1 : 2;
+
+ for (i = 0; i < ub; i++)
+ {
+ loc[i] = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (DImode, 28 + i),
+ GEN_INT (offset));
+ offset += 8;
+ }
- for (i = 0; i < ub; i++)
+ return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (ub, loc));
+ }
+ else if (int_size_in_bytes (valtype) > UNITS_PER_WORD)
{
- loc[i] = gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (DImode, 28 + i),
- GEN_INT (offset));
- offset += 8;
+ /* Aggregates 5 to 8 bytes in size are returned in general
+ registers r28-r29 in the same manner as other non
+ floating-point objects. The data is right-justified and
+ zero-extended to 64 bits. This is opposite to the normal
+ justification used on big endian targets and requires
+ special treatment. */
+ rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (DImode, 28), const0_rtx);
+ return gen_rtx_PARALLEL (BLKmode, gen_rtvec (1, loc));
}
-
- return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (ub, loc));
}
if ((INTEGRAL_TYPE_P (valtype)
valmode = TYPE_MODE (valtype);
if (TREE_CODE (valtype) == REAL_TYPE
+ && !AGGREGATE_TYPE_P (valtype)
&& TYPE_MODE (valtype) != TFmode
&& !TARGET_SOFT_FLOAT)
return gen_rtx_REG (valmode, 32);
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)
+ We now define BLOCK_REG_PADDING to pad these objects.
+ Aggregates, complex and vector types are passed in the same
+ manner as structures. */
+ if (mode == BLKmode
+ || (type && (AGGREGATE_TYPE_P (type)
+ || TREE_CODE (type) == COMPLEX_TYPE
+ || TREE_CODE (type) == VECTOR_TYPE)))
{
rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (DImode, gpr_reg_base),
const0_rtx);
- return gen_rtx_PARALLEL (mode, gen_rtvec (1, loc));
+ return gen_rtx_PARALLEL (BLKmode, gen_rtvec (1, loc));
}
}
else
/* 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);
else
retval = gen_rtx_REG (mode, fpr_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->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->in_nsubspa == 2);
+ in_section = NULL;
}
+ output_section_asm_op ("\t.SPACE $TEXT$\n\t.NSUBSPA $CODE$");
+ return;
}
+ output_section_asm_op ("\t.SPACE $TEXT$\n\t.SUBSPA $CODE$");
+}
+
+/* A get_unnamed_section callback for switching to comdat data
+ sections. This function is only used with SOM. */
- return "\t.SPACE $TEXT$\n\t.SUBSPA $CODE$";
+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
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++)