/* Subroutines for insn-output.c for SPARC.
- Copyright (C) 1987-2019 Free Software Foundation, Inc.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com)
64-bit SPARC-V9 support by Michael Tiemann, Jim Wilson, and Doug Evans,
at Cygnus Support.
#include "gimplify.h"
#include "langhooks.h"
#include "reload.h"
-#include "params.h"
#include "tree-pass.h"
#include "context.h"
#include "builtins.h"
#include "tree-vector-builder.h"
+#include "opts.h"
/* This file should be included last. */
#include "target-def.h"
static rtx sparc_delegitimize_address (rtx);
static bool sparc_mode_dependent_address_p (const_rtx, addr_space_t);
static bool sparc_pass_by_reference (cumulative_args_t,
- machine_mode, const_tree, bool);
+ const function_arg_info &);
static void sparc_function_arg_advance (cumulative_args_t,
- machine_mode, const_tree, bool);
-static rtx sparc_function_arg_1 (cumulative_args_t,
- machine_mode, const_tree, bool, bool);
-static rtx sparc_function_arg (cumulative_args_t,
- machine_mode, const_tree, bool);
+ const function_arg_info &);
+static rtx sparc_function_arg (cumulative_args_t, const function_arg_info &);
static rtx sparc_function_incoming_arg (cumulative_args_t,
- machine_mode, const_tree, bool);
+ const function_arg_info &);
static pad_direction sparc_function_arg_padding (machine_mode, const_tree);
static unsigned int sparc_function_arg_boundary (machine_mode,
const_tree);
gcc_unreachable ();
};
- /* PARAM_SIMULTANEOUS_PREFETCHES is the number of prefetches that
+ /* param_simultaneous_prefetches is the number of prefetches that
can run at the same time. More important, it is the threshold
defining when additional prefetches will be dropped by the
hardware.
single-threaded program. Experimental results show that setting
this parameter to 32 works well when the number of threads is not
high. */
- maybe_set_param_value (PARAM_SIMULTANEOUS_PREFETCHES,
- ((sparc_cpu == PROCESSOR_ULTRASPARC
- || sparc_cpu == PROCESSOR_NIAGARA
- || sparc_cpu == PROCESSOR_NIAGARA2
- || sparc_cpu == PROCESSOR_NIAGARA3
- || sparc_cpu == PROCESSOR_NIAGARA4)
- ? 2
- : (sparc_cpu == PROCESSOR_ULTRASPARC3
- ? 8 : ((sparc_cpu == PROCESSOR_NIAGARA7
- || sparc_cpu == PROCESSOR_M8)
- ? 32 : 3))),
- global_options.x_param_values,
- global_options_set.x_param_values);
-
- /* PARAM_L1_CACHE_LINE_SIZE is the size of the L1 cache line, in
+ SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+ param_simultaneous_prefetches,
+ ((sparc_cpu == PROCESSOR_ULTRASPARC
+ || sparc_cpu == PROCESSOR_NIAGARA
+ || sparc_cpu == PROCESSOR_NIAGARA2
+ || sparc_cpu == PROCESSOR_NIAGARA3
+ || sparc_cpu == PROCESSOR_NIAGARA4)
+ ? 2
+ : (sparc_cpu == PROCESSOR_ULTRASPARC3
+ ? 8 : ((sparc_cpu == PROCESSOR_NIAGARA7
+ || sparc_cpu == PROCESSOR_M8)
+ ? 32 : 3))));
+
+ /* param_l1_cache_line_size is the size of the L1 cache line, in
bytes.
The Oracle SPARC Architecture (previously the UltraSPARC
L2 and L3, but only 32B are brought into the L1D$. (Assuming it
is a read_n prefetch, which is the only type which allocates to
the L1.) */
- maybe_set_param_value (PARAM_L1_CACHE_LINE_SIZE,
- (sparc_cpu == PROCESSOR_M8
- ? 64 : 32),
- global_options.x_param_values,
- global_options_set.x_param_values);
+ SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+ param_l1_cache_line_size,
+ (sparc_cpu == PROCESSOR_M8 ? 64 : 32));
- /* PARAM_L1_CACHE_SIZE is the size of the L1D$ (most SPARC chips use
+ /* param_l1_cache_size is the size of the L1D$ (most SPARC chips use
Hardvard level-1 caches) in kilobytes. Both UltraSPARC and
Niagara processors feature a L1D$ of 16KB. */
- maybe_set_param_value (PARAM_L1_CACHE_SIZE,
- ((sparc_cpu == PROCESSOR_ULTRASPARC
- || sparc_cpu == PROCESSOR_ULTRASPARC3
- || sparc_cpu == PROCESSOR_NIAGARA
- || sparc_cpu == PROCESSOR_NIAGARA2
- || sparc_cpu == PROCESSOR_NIAGARA3
- || sparc_cpu == PROCESSOR_NIAGARA4
- || sparc_cpu == PROCESSOR_NIAGARA7
- || sparc_cpu == PROCESSOR_M8)
- ? 16 : 64),
- global_options.x_param_values,
- global_options_set.x_param_values);
-
-
- /* PARAM_L2_CACHE_SIZE is the size fo the L2 in kilobytes. Note
+ SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+ param_l1_cache_size,
+ ((sparc_cpu == PROCESSOR_ULTRASPARC
+ || sparc_cpu == PROCESSOR_ULTRASPARC3
+ || sparc_cpu == PROCESSOR_NIAGARA
+ || sparc_cpu == PROCESSOR_NIAGARA2
+ || sparc_cpu == PROCESSOR_NIAGARA3
+ || sparc_cpu == PROCESSOR_NIAGARA4
+ || sparc_cpu == PROCESSOR_NIAGARA7
+ || sparc_cpu == PROCESSOR_M8)
+ ? 16 : 64));
+
+ /* param_l2_cache_size is the size fo the L2 in kilobytes. Note
that 512 is the default in params.def. */
- maybe_set_param_value (PARAM_L2_CACHE_SIZE,
- ((sparc_cpu == PROCESSOR_NIAGARA4
- || sparc_cpu == PROCESSOR_M8)
- ? 128 : (sparc_cpu == PROCESSOR_NIAGARA7
- ? 256 : 512)),
- global_options.x_param_values,
- global_options_set.x_param_values);
+ SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+ param_l2_cache_size,
+ ((sparc_cpu == PROCESSOR_NIAGARA4
+ || sparc_cpu == PROCESSOR_M8)
+ ? 128 : (sparc_cpu == PROCESSOR_NIAGARA7
+ ? 256 : 512)));
/* Disable save slot sharing for call-clobbered registers by default.
case UNGT:
case UNGE:
case UNEQ:
- case LTGT:
return CCFPmode;
case LT:
case LE:
case GT:
case GE:
+ case LTGT:
return CCFPEmode;
default:
}
\f
/* Global Offset Table support. */
-static GTY(()) rtx got_helper_rtx = NULL_RTX;
-static GTY(()) rtx got_register_rtx = NULL_RTX;
static GTY(()) rtx got_symbol_rtx = NULL_RTX;
+static GTY(()) rtx got_register_rtx = NULL_RTX;
+static GTY(()) rtx got_helper_rtx = NULL_RTX;
+
+static GTY(()) bool got_helper_needed = false;
/* Return the SYMBOL_REF for the Global Offset Table. */
return got_symbol_rtx;
}
-#ifdef HAVE_GAS_HIDDEN
-# define USE_HIDDEN_LINKONCE 1
-#else
-# define USE_HIDDEN_LINKONCE 0
-#endif
-
-static void
-get_pc_thunk_name (char name[32], unsigned int regno)
-{
- const char *reg_name = reg_names[regno];
-
- /* Skip the leading '%' as that cannot be used in a
- symbol name. */
- reg_name += 1;
-
- if (USE_HIDDEN_LINKONCE)
- sprintf (name, "__sparc_get_pc_thunk.%s", reg_name);
- else
- ASM_GENERATE_INTERNAL_LABEL (name, "LADDPC", regno);
-}
-
/* Wrapper around the load_pcrel_sym{si,di} patterns. */
static rtx
return insn;
}
+/* Output the load_pcrel_sym{si,di} patterns. */
+
+const char *
+output_load_pcrel_sym (rtx *operands)
+{
+ if (flag_delayed_branch)
+ {
+ output_asm_insn ("sethi\t%%hi(%a1-4), %0", operands);
+ output_asm_insn ("call\t%a2", operands);
+ output_asm_insn (" add\t%0, %%lo(%a1+4), %0", operands);
+ }
+ else
+ {
+ output_asm_insn ("sethi\t%%hi(%a1-8), %0", operands);
+ output_asm_insn ("add\t%0, %%lo(%a1-4), %0", operands);
+ output_asm_insn ("call\t%a2", operands);
+ output_asm_insn (" nop", NULL);
+ }
+
+ if (operands[2] == got_helper_rtx)
+ got_helper_needed = true;
+
+ return "";
+}
+
+#ifdef HAVE_GAS_HIDDEN
+# define USE_HIDDEN_LINKONCE 1
+#else
+# define USE_HIDDEN_LINKONCE 0
+#endif
+
/* Emit code to load the GOT register. */
void
load_got_register (void)
{
- if (!got_register_rtx)
- got_register_rtx = gen_rtx_REG (Pmode, GLOBAL_OFFSET_TABLE_REGNUM);
+ rtx insn;
if (TARGET_VXWORKS_RTP)
- emit_insn (gen_vxworks_load_got ());
+ {
+ if (!got_register_rtx)
+ got_register_rtx = pic_offset_table_rtx;
+
+ insn = gen_vxworks_load_got ();
+ }
else
{
+ if (!got_register_rtx)
+ got_register_rtx = gen_rtx_REG (Pmode, GLOBAL_OFFSET_TABLE_REGNUM);
+
/* The GOT symbol is subject to a PC-relative relocation so we need a
helper function to add the PC value and thus get the final value. */
if (!got_helper_rtx)
{
char name[32];
- get_pc_thunk_name (name, GLOBAL_OFFSET_TABLE_REGNUM);
+
+ /* Skip the leading '%' as that cannot be used in a symbol name. */
+ if (USE_HIDDEN_LINKONCE)
+ sprintf (name, "__sparc_get_pc_thunk.%s",
+ reg_names[REGNO (got_register_rtx)] + 1);
+ else
+ ASM_GENERATE_INTERNAL_LABEL (name, "LADDPC",
+ REGNO (got_register_rtx));
+
got_helper_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
}
- emit_insn (gen_load_pcrel_sym (got_register_rtx, sparc_got (),
- got_helper_rtx));
+ insn
+ = gen_load_pcrel_sym (got_register_rtx, sparc_got (), got_helper_rtx);
}
+
+ emit_insn (insn);
}
/* Ensure that we are not using patterns that are not OK with PIC. */
return true;
if (!HARD_REGISTER_P (pic_offset_table_rtx)
- && (HARD_REGISTER_P (x) || lra_in_progress)
+ && (HARD_REGISTER_P (x) || lra_in_progress || reload_in_progress)
&& ORIGINAL_REGNO (x) == REGNO (pic_offset_table_rtx))
return true;
save_global_or_fp_reg_p (unsigned int regno,
int leaf_function ATTRIBUTE_UNUSED)
{
- return !call_used_regs[regno] && df_regs_ever_live_p (regno);
+ return !call_used_or_fixed_reg_p (regno) && df_regs_ever_live_p (regno);
}
/* Return whether the return address register (%i7) is needed. */
save_local_or_in_reg_p (unsigned int regno, int leaf_function)
{
/* General case: call-saved registers live at some point. */
- if (!call_used_regs[regno] && df_regs_ever_live_p (regno))
+ if (!call_used_or_fixed_reg_p (regno) && df_regs_ever_live_p (regno))
return true;
/* Frame pointer register (%fp) if needed. */
return true;
/* GOT register (%l7) if needed. */
- if (regno == GLOBAL_OFFSET_TABLE_REGNUM && got_register_rtx)
+ if (got_register_rtx && regno == REGNO (got_register_rtx))
return true;
/* If the function accesses prior frames, the frame pointer and the return
Specify whether to pass the argument by reference. */
static bool
-sparc_pass_by_reference (cumulative_args_t cum ATTRIBUTE_UNUSED,
- machine_mode mode, const_tree type,
- bool named ATTRIBUTE_UNUSED)
+sparc_pass_by_reference (cumulative_args_t, const function_arg_info &arg)
{
+ tree type = arg.type;
+ machine_mode mode = arg.mode;
if (TARGET_ARCH32)
/* Original SPARC 32-bit ABI says that structures and unions,
and quad-precision floats are passed by reference.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
- MODE is the argument's machine mode.
- TYPE is the data type of the argument (as a tree).
- This is null for libcalls where that information may
- not be available.
- NAMED is true if this argument is a named parameter
- (otherwise it is an extra parameter matching an ellipsis).
+ ARG is a description of the argument.
INCOMING_P is false for TARGET_FUNCTION_ARG, true for
TARGET_FUNCTION_INCOMING_ARG. */
static rtx
-sparc_function_arg_1 (cumulative_args_t cum_v, machine_mode mode,
- const_tree type, bool named, bool incoming)
+sparc_function_arg_1 (cumulative_args_t cum_v, const function_arg_info &arg,
+ bool incoming)
{
const CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
const int regbase
= incoming ? SPARC_INCOMING_INT_ARG_FIRST : SPARC_OUTGOING_INT_ARG_FIRST;
int slotno, regno, padding;
+ tree type = arg.type;
+ machine_mode mode = arg.mode;
enum mode_class mclass = GET_MODE_CLASS (mode);
+ bool named = arg.named;
slotno
= function_arg_slotno (cum, mode, type, named, incoming, ®no, &padding);
/* Handle the TARGET_FUNCTION_ARG target hook. */
static rtx
-sparc_function_arg (cumulative_args_t cum, machine_mode mode,
- const_tree type, bool named)
+sparc_function_arg (cumulative_args_t cum, const function_arg_info &arg)
{
- return sparc_function_arg_1 (cum, mode, type, named, false);
+ return sparc_function_arg_1 (cum, arg, false);
}
/* Handle the TARGET_FUNCTION_INCOMING_ARG target hook. */
static rtx
-sparc_function_incoming_arg (cumulative_args_t cum, machine_mode mode,
- const_tree type, bool named)
+sparc_function_incoming_arg (cumulative_args_t cum,
+ const function_arg_info &arg)
{
- return sparc_function_arg_1 (cum, mode, type, named, true);
+ return sparc_function_arg_1 (cum, arg, true);
}
/* For sparc64, objects requiring 16 byte alignment are passed that way. */
}
/* Handle the TARGET_FUNCTION_ARG_ADVANCE hook.
- Update the data in CUM to advance over an argument
- of mode MODE and data type TYPE.
- TYPE is null for libcalls where that information may not be available. */
+ Update the data in CUM to advance over argument ARG. */
static void
-sparc_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
- const_tree type, bool named)
+sparc_function_arg_advance (cumulative_args_t cum_v,
+ const function_arg_info &arg)
{
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+ tree type = arg.type;
+ machine_mode mode = arg.mode;
int regno, padding;
/* We pass false for incoming here, it doesn't matter. */
- function_arg_slotno (cum, mode, type, named, false, ®no, &padding);
+ function_arg_slotno (cum, mode, type, arg.named, false, ®no, &padding);
/* If argument requires leading padding, add it. */
cum->words += padding;
sparc_file_end (void)
{
/* If we need to emit the special GOT helper function, do so now. */
- if (got_helper_rtx)
+ if (got_helper_needed)
{
const char *name = XSTR (got_helper_rtx, 0);
- const char *reg_name = reg_names[GLOBAL_OFFSET_TABLE_REGNUM];
#ifdef DWARF2_UNWIND_INFO
bool do_cfi;
#endif
#ifdef DWARF2_UNWIND_INFO
do_cfi = dwarf2out_do_cfi_asm ();
if (do_cfi)
- fprintf (asm_out_file, "\t.cfi_startproc\n");
+ output_asm_insn (".cfi_startproc", NULL);
#endif
if (flag_delayed_branch)
- fprintf (asm_out_file, "\tjmp\t%%o7+8\n\t add\t%%o7, %s, %s\n",
- reg_name, reg_name);
+ {
+ output_asm_insn ("jmp\t%%o7+8", NULL);
+ output_asm_insn (" add\t%%o7, %0, %0", &got_register_rtx);
+ }
else
- fprintf (asm_out_file, "\tadd\t%%o7, %s, %s\n\tjmp\t%%o7+8\n\t nop\n",
- reg_name, reg_name);
+ {
+ output_asm_insn ("add\t%%o7, %0, %0", &got_register_rtx);
+ output_asm_insn ("jmp\t%%o7+8", NULL);
+ output_asm_insn (" nop", NULL);
+ }
#ifdef DWARF2_UNWIND_INFO
if (do_cfi)
- fprintf (asm_out_file, "\t.cfi_endproc\n");
+ output_asm_insn (".cfi_endproc", NULL);
#endif
}
sparc_conditional_register_usage (void)
{
if (PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
- {
- fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
- call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
- }
+ fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
/* If the user has passed -f{fixed,call-{used,saved}}-g5 */
/* then honor it. */
if (TARGET_ARCH32 && fixed_regs[5])
edge entry_edge;
rtx_insn *seq;
- if (!crtl->uses_pic_offset_table)
+ /* In PIC mode, we need to always initialize the PIC register if optimization
+ is enabled, because we are called from IRA and LRA may later force things
+ to the constant pool for optimization purposes. */
+ if (!flag_pic || (!crtl->uses_pic_offset_table && !optimize))
return;
start_sequence ();