/* Subroutines for insn-output.c for SPARC.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com)
64-bit SPARC-V9 support by Michael Tiemann, Jim Wilson, and Doug Evans,
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 "target.h"
#include "target-def.h"
#include "cfglayout.h"
-#include "tree-gimple.h"
+#include "gimple.h"
#include "langhooks.h"
#include "params.h"
+#include "df.h"
/* Processor costs */
static const
0, /* shift penalty */
};
+static const
+struct processor_costs niagara2_costs = {
+ COSTS_N_INSNS (3), /* int load */
+ COSTS_N_INSNS (3), /* int signed load */
+ COSTS_N_INSNS (3), /* int zeroed load */
+ COSTS_N_INSNS (3), /* float load */
+ COSTS_N_INSNS (6), /* fmov, fneg, fabs */
+ COSTS_N_INSNS (6), /* fadd, fsub */
+ COSTS_N_INSNS (6), /* fcmp */
+ COSTS_N_INSNS (6), /* fmov, fmovr */
+ COSTS_N_INSNS (6), /* fmul */
+ COSTS_N_INSNS (19), /* fdivs */
+ COSTS_N_INSNS (33), /* fdivd */
+ COSTS_N_INSNS (19), /* fsqrts */
+ COSTS_N_INSNS (33), /* fsqrtd */
+ COSTS_N_INSNS (5), /* imul */
+ COSTS_N_INSNS (5), /* imulX */
+ 0, /* imul bit factor */
+ COSTS_N_INSNS (31), /* idiv, average of 12 - 41 cycle range */
+ COSTS_N_INSNS (31), /* idivX, average of 12 - 41 cycle range */
+ COSTS_N_INSNS (1), /* movcc/movr */
+ 0, /* shift penalty */
+};
+
const struct processor_costs *sparc_costs = &cypress_costs;
#ifdef HAVE_AS_RELAX_OPTION
static int num_gfregs;
/* The alias set for prologue/epilogue register save/restore. */
-static GTY(()) int sparc_sr_alias_set;
+static GTY(()) alias_set_type sparc_sr_alias_set;
/* The alias set for the structure return value. */
-static GTY(()) int struct_value_alias_set;
-
-/* Save the operands last given to a compare for use when we
- generate a scc or bcc insn. */
-rtx sparc_compare_op0, sparc_compare_op1, sparc_compare_emitted;
+static GTY(()) alias_set_type struct_value_alias_set;
/* Vector to say how input registers are mapped to output registers.
HARD_FRAME_POINTER_REGNUM cannot be remapped by this function to
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1};
-struct machine_function GTY(())
+struct GTY(()) machine_function
{
/* Some local-dynamic TLS symbol name. */
const char *some_ld_name;
static void sparc_output_addr_vec (rtx);
static void sparc_output_addr_diff_vec (rtx);
static void sparc_output_deferred_case_vectors (void);
+static bool sparc_legitimate_address_p (enum machine_mode, rtx, bool);
static rtx sparc_builtin_saveregs (void);
static int epilogue_renumber (rtx *, int);
static bool sparc_assemble_integer (rtx, unsigned int, int);
static tree sparc_handle_vis_mul8x16 (int, tree, tree, tree);
static void sparc_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
HOST_WIDE_INT, tree);
-static bool sparc_can_output_mi_thunk (tree, HOST_WIDE_INT,
- HOST_WIDE_INT, tree);
+static bool sparc_can_output_mi_thunk (const_tree, HOST_WIDE_INT,
+ HOST_WIDE_INT, const_tree);
static struct machine_function * sparc_init_machine_status (void);
static bool sparc_cannot_force_const_mem (rtx);
static rtx sparc_tls_get_addr (void);
static rtx sparc_tls_got (void);
static const char *get_some_local_dynamic_name (void);
static int get_some_local_dynamic_name_1 (rtx *, void *);
-static bool sparc_rtx_costs (rtx, int, int, int *);
-static bool sparc_promote_prototypes (tree);
+static bool sparc_rtx_costs (rtx, int, int, int *, bool);
+static bool sparc_promote_prototypes (const_tree);
static rtx sparc_struct_value_rtx (tree, int);
-static bool sparc_return_in_memory (tree, tree);
+static bool sparc_return_in_memory (const_tree, const_tree);
static bool sparc_strict_argument_naming (CUMULATIVE_ARGS *);
-static tree sparc_gimplify_va_arg (tree, tree, tree *, tree *);
+static void sparc_va_start (tree, rtx);
+static tree sparc_gimplify_va_arg (tree, tree, gimple_seq *, gimple_seq *);
static bool sparc_vector_mode_supported_p (enum machine_mode);
+static rtx sparc_legitimize_address (rtx, rtx, enum machine_mode);
static bool sparc_pass_by_reference (CUMULATIVE_ARGS *,
- enum machine_mode, tree, bool);
+ enum machine_mode, const_tree, bool);
static int sparc_arg_partial_bytes (CUMULATIVE_ARGS *,
enum machine_mode, tree, bool);
static void sparc_dwarf_handle_frame_unspec (const char *, rtx, int);
static void sparc_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
static void sparc_file_end (void);
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
-static const char *sparc_mangle_fundamental_type (tree);
+static const char *sparc_mangle_type (const_tree);
#endif
+\f
#ifdef SUBTARGET_ATTRIBUTE_TABLE
-const struct attribute_spec sparc_attribute_table[];
+/* Table of valid machine attributes. */
+static const struct attribute_spec sparc_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ SUBTARGET_ATTRIBUTE_TABLE,
+ { NULL, 0, 0, false, false, false, NULL }
+};
#endif
\f
/* Option handling. */
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS sparc_init_builtins
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS sparc_legitimize_address
+
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN sparc_expand_builtin
#undef TARGET_FOLD_BUILTIN
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS sparc_rtx_costs
#undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST hook_int_rtx_0
+#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
/* This is only needed for TARGET_ARCH64, but since PROMOTE_FUNCTION_MODE is a
no-op for TARGET_ARCH32 this is ok. Otherwise we'd need to add a runtime
test for this value. */
#undef TARGET_PROMOTE_FUNCTION_ARGS
-#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
/* This is only needed for TARGET_ARCH64, but since PROMOTE_FUNCTION_MODE is a
no-op for TARGET_ARCH32 this is ok. Otherwise we'd need to add a runtime
test for this value. */
#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 sparc_promote_prototypes
#undef TARGET_STRICT_ARGUMENT_NAMING
#define TARGET_STRICT_ARGUMENT_NAMING sparc_strict_argument_naming
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START sparc_va_start
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR sparc_gimplify_va_arg
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION sparc_handle_option
-#if TARGET_GNU_TLS
+#if TARGET_GNU_TLS && defined(HAVE_AS_SPARC_UA_PCREL)
#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
#define TARGET_ASM_OUTPUT_DWARF_DTPREL sparc_output_dwarf_dtprel
#endif
#define TARGET_ASM_FILE_END sparc_file_end
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
-#undef TARGET_MANGLE_FUNDAMENTAL_TYPE
-#define TARGET_MANGLE_FUNDAMENTAL_TYPE sparc_mangle_fundamental_type
+#undef TARGET_MANGLE_TYPE
+#define TARGET_MANGLE_TYPE sparc_mangle_type
#endif
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P sparc_legitimate_address_p
+
struct gcc_target targetm = TARGET_INITIALIZER;
/* Implement TARGET_HANDLE_OPTION. */
{
static struct code_model {
const char *const name;
- const int value;
+ const enum cmodel value;
} const cmodels[] = {
{ "32", CM_32 },
{ "medlow", CM_MEDLOW },
{ "medmid", CM_MEDMID },
{ "medany", CM_MEDANY },
{ "embmedany", CM_EMBMEDANY },
- { 0, 0 }
+ { NULL, (enum cmodel) 0 }
};
const struct code_model *cmodel;
/* Map TARGET_CPU_DEFAULT to value for -m{arch,tune}=. */
{ TARGET_CPU_ultrasparc, "ultrasparc" },
{ TARGET_CPU_ultrasparc3, "ultrasparc3" },
{ TARGET_CPU_niagara, "niagara" },
+ { TARGET_CPU_niagara2, "niagara2" },
{ 0, 0 }
};
const struct cpu_default *def;
{ "ultrasparc3", PROCESSOR_ULTRASPARC3, MASK_ISA, MASK_V9|MASK_DEPRECATED_V8_INSNS},
/* UltraSPARC T1 */
{ "niagara", PROCESSOR_NIAGARA, MASK_ISA, MASK_V9|MASK_DEPRECATED_V8_INSNS},
- { 0, 0, 0, 0 }
+ { "niagara2", PROCESSOR_NIAGARA, MASK_ISA, MASK_V9},
+ { 0, (enum processor_type) 0, 0, 0 }
};
const struct cpu_table *cpu;
const struct sparc_cpu_select *sel;
error ("-mcmodel= is not supported on 32 bit systems");
}
- fpu = TARGET_FPU; /* save current -mfpu status */
+ fpu = target_flags & MASK_FPU; /* save current -mfpu status */
/* Set the default CPU. */
for (def = &cpu_default[0]; def->name; ++def)
if (align_functions == 0
&& (sparc_cpu == PROCESSOR_ULTRASPARC
|| sparc_cpu == PROCESSOR_ULTRASPARC3
- || sparc_cpu == PROCESSOR_NIAGARA))
+ || sparc_cpu == PROCESSOR_NIAGARA
+ || sparc_cpu == PROCESSOR_NIAGARA2))
align_functions = 32;
/* Validate PCC_STRUCT_RETURN. */
case PROCESSOR_NIAGARA:
sparc_costs = &niagara_costs;
break;
+ case PROCESSOR_NIAGARA2:
+ sparc_costs = &niagara2_costs;
+ break;
};
#ifdef TARGET_DEFAULT_LONG_DOUBLE_128
if (!PARAM_SET_P (PARAM_SIMULTANEOUS_PREFETCHES))
set_param_value ("simultaneous-prefetches",
((sparc_cpu == PROCESSOR_ULTRASPARC
- || sparc_cpu == PROCESSOR_NIAGARA)
+ || sparc_cpu == PROCESSOR_NIAGARA
+ || sparc_cpu == PROCESSOR_NIAGARA2)
? 2
: (sparc_cpu == PROCESSOR_ULTRASPARC3
? 8 : 3)));
set_param_value ("l1-cache-line-size",
((sparc_cpu == PROCESSOR_ULTRASPARC
|| sparc_cpu == PROCESSOR_ULTRASPARC3
- || sparc_cpu == PROCESSOR_NIAGARA)
+ || sparc_cpu == PROCESSOR_NIAGARA
+ || sparc_cpu == PROCESSOR_NIAGARA2)
? 64 : 32));
}
\f
-#ifdef SUBTARGET_ATTRIBUTE_TABLE
-/* Table of valid machine attributes. */
-const struct attribute_spec sparc_attribute_table[] =
-{
- /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
- SUBTARGET_ATTRIBUTE_TABLE,
- { NULL, 0, 0, false, false, false, NULL }
-};
-#endif
-\f
/* Miscellaneous utilities. */
/* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move
if (pic_address_needs_scratch (operands[1]))
operands[1] = legitimize_pic_address (operands[1], mode, 0);
- if (GET_CODE (operands[1]) == LABEL_REF && mode == SImode)
+ /* VxWorks does not impose a fixed gap between segments; the run-time
+ gap can be different from the object-file gap. We therefore can't
+ assume X - _GLOBAL_OFFSET_TABLE_ is a link-time constant unless we
+ are absolutely sure that X is in the same segment as the GOT.
+ Unfortunately, the flexibility of linker scripts means that we
+ can't be sure of that in general, so assume that _G_O_T_-relative
+ accesses are never valid on VxWorks. */
+ if (GET_CODE (operands[1]) == LABEL_REF && !TARGET_VXWORKS_RTP)
{
- emit_insn (gen_movsi_pic_label_ref (operands[0], operands[1]));
- return true;
- }
+ if (mode == SImode)
+ {
+ emit_insn (gen_movsi_pic_label_ref (operands[0], operands[1]));
+ return true;
+ }
- if (GET_CODE (operands[1]) == LABEL_REF && mode == DImode)
- {
- gcc_assert (TARGET_ARCH64);
- emit_insn (gen_movdi_pic_label_ref (operands[0], operands[1]));
- return true;
+ if (mode == DImode)
+ {
+ gcc_assert (TARGET_ARCH64);
+ emit_insn (gen_movdi_pic_label_ref (operands[0], operands[1]));
+ return true;
+ }
}
if (symbolic_operand (operands[1], mode))
}
}
-/* X and Y are two things to compare using CODE. Emit the compare insn and
- return the rtx for the cc reg in the proper mode. */
+/* Emit the compare insn and return the CC reg for a CODE comparison
+ with operands X and Y. */
-rtx
-gen_compare_reg (enum rtx_code code)
+static rtx
+gen_compare_reg_1 (enum rtx_code code, rtx x, rtx y)
{
- rtx x = sparc_compare_op0;
- rtx y = sparc_compare_op1;
- enum machine_mode mode = SELECT_CC_MODE (code, x, y);
+ enum machine_mode mode;
rtx cc_reg;
- if (sparc_compare_emitted != NULL_RTX)
- {
- cc_reg = sparc_compare_emitted;
- sparc_compare_emitted = NULL_RTX;
- return cc_reg;
- }
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
+ return x;
+
+ mode = SELECT_CC_MODE (code, x, y);
/* ??? We don't have movcc patterns so we cannot generate pseudo regs for the
fcc regs (cse can't tell they're really call clobbered regs and will
else
cc_reg = gen_rtx_REG (mode, SPARC_ICC_REG);
- emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
- gen_rtx_COMPARE (mode, x, y)));
+ /* We shouldn't get there for TFmode if !TARGET_HARD_QUAD. If we do, this
+ will only result in an unrecognizable insn so no point in asserting. */
+ emit_insn (gen_rtx_SET (VOIDmode, cc_reg, gen_rtx_COMPARE (mode, x, y)));
return cc_reg;
}
+
+/* Emit the compare insn and return the CC reg for the comparison in CMP. */
+
+rtx
+gen_compare_reg (rtx cmp)
+{
+ return gen_compare_reg_1 (GET_CODE (cmp), XEXP (cmp, 0), XEXP (cmp, 1));
+}
+
/* This function is used for v9 only.
+ DEST is the target of the Scc insn.
CODE is the code for an Scc's comparison.
- OPERANDS[0] is the target of the Scc insn.
- OPERANDS[1] is the value we compare against const0_rtx (which hasn't
- been generated yet).
+ X and Y are the values we compare.
This function is needed to turn
IE: The instruction recognizer needs to see the mode of the comparison to
find the right instruction. We could use "gt:DI" right in the
- define_expand, but leaving it out allows us to handle DI, SI, etc.
+ define_expand, but leaving it out allows us to handle DI, SI, etc. */
- We refer to the global sparc compare operands sparc_compare_op0 and
- sparc_compare_op1. */
-
-int
-gen_v9_scc (enum rtx_code compare_code, register rtx *operands)
+static int
+gen_v9_scc (rtx dest, enum rtx_code compare_code, rtx x, rtx y)
{
if (! TARGET_ARCH64
- && (GET_MODE (sparc_compare_op0) == DImode
- || GET_MODE (operands[0]) == DImode))
+ && (GET_MODE (x) == DImode
+ || GET_MODE (dest) == DImode))
return 0;
/* Try to use the movrCC insns. */
if (TARGET_ARCH64
- && GET_MODE_CLASS (GET_MODE (sparc_compare_op0)) == MODE_INT
- && sparc_compare_op1 == const0_rtx
+ && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
+ && y == const0_rtx
&& v9_regcmp_p (compare_code))
{
- rtx op0 = sparc_compare_op0;
+ rtx op0 = x;
rtx temp;
/* Special case for op0 != 0. This can be done with one instruction if
- operands[0] == sparc_compare_op0. */
+ dest == x. */
if (compare_code == NE
- && GET_MODE (operands[0]) == DImode
- && rtx_equal_p (op0, operands[0]))
+ && GET_MODE (dest) == DImode
+ && rtx_equal_p (op0, dest))
{
- emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+ emit_insn (gen_rtx_SET (VOIDmode, dest,
gen_rtx_IF_THEN_ELSE (DImode,
gen_rtx_fmt_ee (compare_code, DImode,
op0, const0_rtx),
const1_rtx,
- operands[0])));
+ dest)));
return 1;
}
- if (reg_overlap_mentioned_p (operands[0], op0))
+ if (reg_overlap_mentioned_p (dest, op0))
{
- /* Handle the case where operands[0] == sparc_compare_op0.
+ /* Handle the case where dest == x.
We "early clobber" the result. */
- op0 = gen_reg_rtx (GET_MODE (sparc_compare_op0));
- emit_move_insn (op0, sparc_compare_op0);
+ op0 = gen_reg_rtx (GET_MODE (x));
+ emit_move_insn (op0, x);
}
- emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx));
+ emit_insn (gen_rtx_SET (VOIDmode, dest, const0_rtx));
if (GET_MODE (op0) != DImode)
{
temp = gen_reg_rtx (DImode);
}
else
temp = op0;
- emit_insn (gen_rtx_SET (VOIDmode, operands[0],
- gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+ emit_insn (gen_rtx_SET (VOIDmode, dest,
+ gen_rtx_IF_THEN_ELSE (GET_MODE (dest),
gen_rtx_fmt_ee (compare_code, DImode,
temp, const0_rtx),
const1_rtx,
- operands[0])));
+ dest)));
return 1;
}
else
{
- operands[1] = gen_compare_reg (compare_code);
+ x = gen_compare_reg_1 (compare_code, x, y);
+ y = const0_rtx;
- switch (GET_MODE (operands[1]))
- {
- case CCmode :
- case CCXmode :
- case CCFPEmode :
- case CCFPmode :
- break;
- default :
- gcc_unreachable ();
- }
- emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx));
- emit_insn (gen_rtx_SET (VOIDmode, operands[0],
- gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
+ gcc_assert (GET_MODE (x) != CC_NOOVmode
+ && GET_MODE (x) != CCX_NOOVmode);
+
+ emit_insn (gen_rtx_SET (VOIDmode, dest, const0_rtx));
+ emit_insn (gen_rtx_SET (VOIDmode, dest,
+ gen_rtx_IF_THEN_ELSE (GET_MODE (dest),
gen_rtx_fmt_ee (compare_code,
- GET_MODE (operands[1]),
- operands[1], const0_rtx),
- const1_rtx, operands[0])));
+ GET_MODE (x), x, y),
+ const1_rtx, dest)));
return 1;
}
}
+
+/* Emit an scc insn. For seq, sne, sgeu, and sltu, we can do this
+ without jumps using the addx/subx instructions. */
+
+bool
+emit_scc_insn (rtx operands[])
+{
+ rtx tem;
+ rtx x;
+ rtx y;
+ enum rtx_code code;
+
+ /* The quad-word fp compare library routines all return nonzero to indicate
+ true, which is different from the equivalent libgcc routines, so we must
+ handle them specially here. */
+ if (GET_MODE (operands[2]) == TFmode && ! TARGET_HARD_QUAD)
+ {
+ operands[1] = sparc_emit_float_lib_cmp (operands[2], operands[3],
+ GET_CODE (operands[1]));
+ operands[2] = XEXP (operands[1], 0);
+ operands[3] = XEXP (operands[1], 1);
+ }
+
+ code = GET_CODE (operands[1]);
+ x = operands[2];
+ y = operands[3];
+
+ /* For seq/sne on v9 we use the same code as v8 (the addx/subx method has
+ more applications). The exception to this is "reg != 0" which can
+ be done in one instruction on v9 (so we do it). */
+ if (code == EQ)
+ {
+ if (GET_MODE (x) == SImode)
+ {
+ rtx pat = gen_seqsi_special (operands[0], x, y);
+ emit_insn (pat);
+ return true;
+ }
+ else if (GET_MODE (x) == DImode)
+ {
+ rtx pat = gen_seqdi_special (operands[0], x, y);
+ emit_insn (pat);
+ return true;
+ }
+ }
+
+ if (code == NE)
+ {
+ if (GET_MODE (x) == SImode)
+ {
+ rtx pat = gen_snesi_special (operands[0], x, y);
+ emit_insn (pat);
+ return true;
+ }
+ else if (GET_MODE (x) == DImode)
+ {
+ rtx pat = gen_snedi_special (operands[0], x, y);
+ emit_insn (pat);
+ return true;
+ }
+ }
+
+ /* For the rest, on v9 we can use conditional moves. */
+
+ if (TARGET_V9)
+ {
+ if (gen_v9_scc (operands[0], code, x, y))
+ return true;
+ }
+
+ /* We can do LTU and GEU using the addx/subx instructions too. And
+ for GTU/LEU, if both operands are registers swap them and fall
+ back to the easy case. */
+ if (code == GTU || code == LEU)
+ {
+ if ((GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
+ && (GET_CODE (y) == REG || GET_CODE (y) == SUBREG))
+ {
+ tem = x;
+ x = y;
+ y = tem;
+ code = swap_condition (code);
+ }
+ }
+
+ if (code == LTU || code == GEU)
+ {
+ emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+ gen_rtx_fmt_ee (code, SImode,
+ gen_compare_reg_1 (code, x, y),
+ const0_rtx)));
+ return true;
+ }
+
+ /* Nope, do branches. */
+ return false;
+}
+
/* Emit a conditional jump insn for the v9 architecture using comparison code
CODE and jump target LABEL.
This function exists to take advantage of the v9 brxx insns. */
-void
+static void
emit_v9_brxx_insn (enum rtx_code code, rtx op0, rtx label)
{
- gcc_assert (sparc_compare_emitted == NULL_RTX);
emit_jump_insn (gen_rtx_SET (VOIDmode,
pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode,
pc_rtx)));
}
+void
+emit_conditional_branch_insn (rtx operands[])
+{
+ /* The quad-word fp compare library routines all return nonzero to indicate
+ true, which is different from the equivalent libgcc routines, so we must
+ handle them specially here. */
+ if (GET_MODE (operands[1]) == TFmode && ! TARGET_HARD_QUAD)
+ {
+ operands[0] = sparc_emit_float_lib_cmp (operands[1], operands[2],
+ GET_CODE (operands[0]));
+ operands[1] = XEXP (operands[0], 0);
+ operands[2] = XEXP (operands[0], 1);
+ }
+
+ if (TARGET_ARCH64 && operands[2] == const0_rtx
+ && GET_CODE (operands[1]) == REG
+ && GET_MODE (operands[1]) == DImode)
+ {
+ emit_v9_brxx_insn (GET_CODE (operands[0]), operands[1], operands[3]);
+ return;
+ }
+
+ operands[1] = gen_compare_reg (operands[0]);
+ operands[2] = const0_rtx;
+ operands[0] = gen_rtx_fmt_ee (GET_CODE (operands[0]), VOIDmode,
+ operands[1], operands[2]);
+ emit_jump_insn (gen_cbranchcc4 (operands[0], operands[1], operands[2],
+ operands[3]));
+}
+
+
/* Generate a DFmode part of a hard TFmode register.
REG is the TFmode hard register, LOW is 1 for the
low 64bit of the register and 0 otherwise.
{
case SImode:
func = "_Qp_itoq";
+ if (TARGET_ARCH64)
+ operands[1] = gen_rtx_SIGN_EXTEND (DImode, operands[1]);
break;
case DImode:
func = "_Qp_xtoq";
{
case SImode:
func = "_Qp_uitoq";
+ if (TARGET_ARCH64)
+ operands[1] = gen_rtx_ZERO_EXTEND (DImode, operands[1]);
break;
case DImode:
func = "_Qp_uxtoq";
/* If the function uses __builtin_eh_return, the eh_return machinery
occupies the delay slot. */
- if (current_function_calls_eh_return)
+ if (crtl->calls_eh_return)
return 0;
/* In the case of a true leaf function, anything can go into the slot. */
/* Return nonzero if ADDR is a valid memory address.
STRICT specifies whether strict register checking applies. */
-int
-legitimate_address_p (enum machine_mode mode, rtx addr, int strict)
+static bool
+sparc_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
{
rtx rs1 = NULL, rs2 = NULL, imm1 = NULL;
rtx temp;
if (flag_pic)
{
- current_function_uses_pic_offset_table = 1;
+ crtl->uses_pic_offset_table = 1;
return pic_offset_table_rtx;
}
{
rtx temp1, temp2, temp3, ret, o0, got, insn;
- gcc_assert (! no_new_pseudos);
+ gcc_assert (can_create_pseudo_p ());
if (GET_CODE (addr) == SYMBOL_REF)
switch (SYMBOL_REF_TLS_MODEL (addr))
legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
rtx reg)
{
- if (GET_CODE (orig) == SYMBOL_REF)
+ if (GET_CODE (orig) == SYMBOL_REF
+ /* See the comment in sparc_expand_move. */
+ || (TARGET_VXWORKS_RTP && GET_CODE (orig) == LABEL_REF))
{
rtx pic_ref, address;
rtx insn;
pic_ref = gen_const_mem (Pmode,
gen_rtx_PLUS (Pmode,
pic_offset_table_rtx, address));
- current_function_uses_pic_offset_table = 1;
+ crtl->uses_pic_offset_table = 1;
insn = emit_move_insn (reg, pic_ref);
/* Put a REG_EQUAL note on this insn, so that it can be optimized
by loop. */
/* ??? Why do we do this? */
/* Now movsi_pic_label_ref uses it, but we ought to be checking that
the register is live instead, in case it is eliminated. */
- current_function_uses_pic_offset_table = 1;
+ crtl->uses_pic_offset_table = 1;
return orig;
}
OLDX is the address as it was before break_out_memory_refs was called.
In some cases it is useful to look at this to decide what needs to be done.
- MODE is the mode of the operand pointed to by X. */
+ MODE is the mode of the operand pointed to by X.
+
+ On SPARC, change REG+N into REG+REG, and REG+(X*Y) into REG+REG. */
rtx
-legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, enum machine_mode mode)
+sparc_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode)
{
rtx orig_x = x;
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
force_operand (XEXP (x, 1), NULL_RTX));
- if (x != orig_x && legitimate_address_p (mode, x, FALSE))
+ if (x != orig_x && sparc_legitimate_address_p (mode, x, FALSE))
return x;
if (SPARC_SYMBOL_REF_TLS_P (x))
{
int orig_flag_pic = flag_pic;
+ if (TARGET_VXWORKS_RTP)
+ {
+ emit_insn (gen_vxworks_load_got ());
+ emit_use (pic_offset_table_rtx);
+ return;
+ }
+
/* If we haven't initialized the special PIC symbols, do so now. */
if (!pic_helper_symbol_name[0])
{
since setjmp/longjmp can cause life info to screw up.
??? In the case where we don't obey regdecls, this is not sufficient
since we may not fall out the bottom. */
- emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
+ emit_use (pic_offset_table_rtx);
+}
+
+/* Emit a call instruction with the pattern given by PAT. ADDR is the
+ address of the call target. */
+
+void
+sparc_emit_call_insn (rtx pat, rtx addr)
+{
+ rtx insn;
+
+ insn = emit_call_insn (pat);
+
+ /* The PIC register is live on entry to VxWorks PIC PLT entries. */
+ if (TARGET_VXWORKS_RTP
+ && flag_pic
+ && GET_CODE (addr) == SYMBOL_REF
+ && (SYMBOL_REF_DECL (addr)
+ ? !targetm.binds_local_p (SYMBOL_REF_DECL (addr))
+ : !SYMBOL_REF_LOCAL_P (addr)))
+ {
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
+ crtl->uses_pic_offset_table = 1;
+ }
}
\f
/* Return 1 if RTX is a MEM which is known to be aligned to at
hard register number, and one indexed by mode. */
/* The purpose of sparc_mode_class is to shrink the range of modes so that
- they all fit (as bit numbers) in a 32 bit word (again). Each real mode is
+ they all fit (as bit numbers) in a 32-bit word (again). Each real mode is
mapped into one sparc_mode_class mode. */
enum sparc_mode_class {
HOST_WIDE_INT
sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function_p)
{
- int outgoing_args_size = (current_function_outgoing_args_size
+ int outgoing_args_size = (crtl->outgoing_args_size
+ REG_PARM_STACK_SPACE (current_function_decl));
int n_regs = 0; /* N_REGS is the number of 4-byte regs saved thus far. */
int i;
if (TARGET_ARCH64)
{
for (i = 0; i < 8; i++)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
n_regs += 2;
}
else
{
for (i = 0; i < 8; i += 2)
- if ((regs_ever_live[i] && ! call_used_regs[i])
- || (regs_ever_live[i+1] && ! call_used_regs[i+1]))
+ if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
+ || (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]))
n_regs += 2;
}
for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2)
- if ((regs_ever_live[i] && ! call_used_regs[i])
- || (regs_ever_live[i+1] && ! call_used_regs[i+1]))
+ if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
+ || (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]))
n_regs += 2;
/* Set up values for use in prologue and epilogue. */
if (leaf_function_p
&& n_regs == 0
&& size == 0
- && current_function_outgoing_args_size == 0)
+ && crtl->outgoing_args_size == 0)
actual_fsize = apparent_fsize = 0;
else
{
.register being printed for them already. */
for (i = 2; i < 8; i++)
{
- if (regs_ever_live [i]
+ if (df_regs_ever_live_p (i)
&& ! sparc_hard_reg_printed [i])
{
sparc_hard_reg_printed [i] = 1;
{
for (i = low; i < high; i++)
{
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
{
mem = gen_rtx_MEM (DImode, plus_constant (base, offset));
set_mem_alias_set (mem, sparc_sr_alias_set);
{
for (i = low; i < high; i += 2)
{
- bool reg0 = regs_ever_live[i] && ! call_used_regs[i];
- bool reg1 = regs_ever_live[i+1] && ! call_used_regs[i+1];
+ bool reg0 = df_regs_ever_live_p (i) && ! call_used_regs[i];
+ bool reg1 = df_regs_ever_live_p (i+1) && ! call_used_regs[i+1];
enum machine_mode mode;
int regno;
rtx reg = gen_rtx_REG (Pmode, 1);
emit_move_insn (reg, GEN_INT (-actual_fsize));
insn = emit_insn (gen_stack_pointer_inc (reg));
- REG_NOTES (insn) =
- gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
- gen_stack_pointer_inc (GEN_INT (-actual_fsize)),
- REG_NOTES (insn));
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+ gen_stack_pointer_inc (GEN_INT (-actual_fsize)));
}
RTX_FRAME_RELATED_P (insn) = 1;
emit_save_or_restore_regs (SORR_SAVE);
/* Load the PIC register if needed. */
- if (flag_pic && current_function_uses_pic_offset_table)
+ if (flag_pic && crtl->uses_pic_offset_table)
load_pic_register (false);
}
semantics of restore/return. We simply output the jump to the
return address and the insn in the delay slot (if any). */
- gcc_assert (! current_function_calls_eh_return);
+ gcc_assert (! crtl->calls_eh_return);
return "jmp\t%%o7+%)%#";
}
combined with the 'restore' instruction or put in the delay slot of
the 'return' instruction. */
- if (current_function_calls_eh_return)
+ if (crtl->calls_eh_return)
{
/* If the function uses __builtin_eh_return, the eh_return
machinery occupies the delay slot. */
When a prototype says `char' or `short', really pass an `int'. */
static bool
-sparc_promote_prototypes (tree fntype ATTRIBUTE_UNUSED)
+sparc_promote_prototypes (const_tree fntype ATTRIBUTE_UNUSED)
{
return TARGET_ARCH32 ? true : false;
}
{
case MODE_FLOAT:
case MODE_COMPLEX_FLOAT:
+ case MODE_VECTOR_INT:
if (TARGET_ARCH64 && TARGET_FPU && named)
{
if (slotno >= SPARC_FP_ARG_MAX)
static void function_arg_record_value_3
(HOST_WIDE_INT, struct function_arg_record_value_parms *);
static void function_arg_record_value_2
- (tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
+ (const_tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
static void function_arg_record_value_1
- (tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
-static rtx function_arg_record_value (tree, enum machine_mode, int, int, int);
+ (const_tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
+static rtx function_arg_record_value (const_tree, enum machine_mode, int, int, int);
static rtx function_arg_union_value (int, enum machine_mode, int, int);
/* A subroutine of function_arg_record_value. Traverse the structure
recursively and determine how many registers will be required. */
static void
-function_arg_record_value_1 (tree type, HOST_WIDE_INT startbitpos,
+function_arg_record_value_1 (const_tree type, HOST_WIDE_INT startbitpos,
struct function_arg_record_value_parms *parms,
bool packed_p)
{
to make that happen. */
static void
-function_arg_record_value_2 (tree type, HOST_WIDE_INT startbitpos,
+function_arg_record_value_2 (const_tree type, HOST_WIDE_INT startbitpos,
struct function_arg_record_value_parms *parms,
bool packed_p)
{
REGBASE is the regno of the base register for the parameter array. */
static rtx
-function_arg_record_value (tree type, enum machine_mode mode,
+function_arg_record_value (const_tree type, enum machine_mode mode,
int slotno, int named, int regbase)
{
HOST_WIDE_INT typesize = int_size_in_bytes (type);
Return an expression valid as a return value for the two macros
FUNCTION_ARG and FUNCTION_VALUE.
- SIZE is the size in bytes of the vector.
- BASE_MODE is the argument's base machine mode.
+ SIZE is the size in bytes of the vector (at least 8 bytes).
REGNO is the FP hard register the vector will be passed in. */
static rtx
-function_arg_vector_value (int size, enum machine_mode base_mode, int regno)
+function_arg_vector_value (int size, int regno)
{
- unsigned short base_mode_size = GET_MODE_SIZE (base_mode);
- int nregs = size / base_mode_size, i;
+ int i, nregs = size / 8;
rtx regs;
regs = gen_rtx_PARALLEL (BLKmode, rtvec_alloc (nregs));
{
XVECEXP (regs, 0, i)
= gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (base_mode, regno),
- GEN_INT (base_mode_size * i));
- regno += base_mode_size / 4;
+ gen_rtx_REG (DImode, regno + 2*i),
+ GEN_INT (i*8));
}
return regs;
if (mode == BLKmode)
return function_arg_vector_value (size,
- TYPE_MODE (TREE_TYPE (type)),
SPARC_FP_ARG_FIRST + 2*slotno);
else
mclass = MODE_FLOAT;
static bool
sparc_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
- enum machine_mode mode, tree type,
+ enum machine_mode mode, const_tree type,
bool named ATTRIBUTE_UNUSED)
{
if (TARGET_ARCH32)
argument slot. */
enum direction
-function_arg_padding (enum machine_mode mode, tree type)
+function_arg_padding (enum machine_mode mode, const_tree type)
{
if (TARGET_ARCH64 && type != 0 && AGGREGATE_TYPE_P (type))
return upward;
Specify whether to return the return value in memory. */
static bool
-sparc_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
+sparc_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
{
if (TARGET_ARCH32)
/* Original SPARC 32-bit ABI says that structures and unions,
except that up to 32 bytes may be returned in registers. */
rtx
-function_value (tree type, enum machine_mode mode, int incoming_p)
+function_value (const_tree type, enum machine_mode mode, int incoming_p)
{
/* Beware that the two values are swapped here wrt function_arg. */
int regbase = (incoming_p
if (mode == BLKmode)
return function_arg_vector_value (size,
- TYPE_MODE (TREE_TYPE (type)),
SPARC_FP_ARG_FIRST);
else
mclass = MODE_FLOAT;
static rtx
sparc_builtin_saveregs (void)
{
- int first_reg = current_function_args_info.words;
+ int first_reg = crtl->args.info.words;
rtx address;
int regno;
/* Implement `va_start' for stdarg. */
-void
+static void
sparc_va_start (tree valist, rtx nextarg)
{
nextarg = expand_builtin_saveregs ();
/* Implement `va_arg' for stdarg. */
static tree
-sparc_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
+sparc_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p)
{
HOST_WIDE_INT size, rsize, align;
tree addr, incr;
size = int_size_in_bytes (type);
rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
align = 0;
-
+
if (TARGET_ARCH64)
{
/* For SPARC64, objects requiring 16-byte alignment get it. */
incr = valist;
if (align)
{
- incr = fold (build2 (PLUS_EXPR, ptr_type_node, incr,
- ssize_int (align - 1)));
- incr = fold (build2 (BIT_AND_EXPR, ptr_type_node, incr,
- ssize_int (-align)));
+ incr = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr,
+ size_int (align - 1));
+ incr = fold_convert (sizetype, incr);
+ incr = fold_build2 (BIT_AND_EXPR, sizetype, incr,
+ size_int (-align));
+ incr = fold_convert (ptr_type_node, incr);
}
gimplify_expr (&incr, pre_p, post_p, is_gimple_val, fb_rvalue);
addr = incr;
if (BYTES_BIG_ENDIAN && size < rsize)
- addr = fold (build2 (PLUS_EXPR, ptr_type_node, incr,
- ssize_int (rsize - size)));
+ addr = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr,
+ size_int (rsize - size));
if (indirect)
{
addr = fold_convert (build_pointer_type (ptrtype), addr);
addr = build_va_arg_indirect_ref (addr);
}
- /* If the address isn't aligned properly for the type,
- we may need to copy to a temporary.
- FIXME: This is inefficient. Usually we can do this
- in registers. */
- else if (align == 0
- && TYPE_ALIGN (type) > BITS_PER_WORD)
+
+ /* If the address isn't aligned properly for the type, we need a temporary.
+ FIXME: This is inefficient, usually we can do this in registers. */
+ else if (align == 0 && TYPE_ALIGN (type) > BITS_PER_WORD)
{
tree tmp = create_tmp_var (type, "va_arg_tmp");
tree dest_addr = build_fold_addr_expr (tmp);
-
- tree copy = build_function_call_expr
- (implicit_built_in_decls[BUILT_IN_MEMCPY],
- tree_cons (NULL_TREE, dest_addr,
- tree_cons (NULL_TREE, addr,
- tree_cons (NULL_TREE, size_int (rsize),
- NULL_TREE))));
-
+ tree copy = build_call_expr (implicit_built_in_decls[BUILT_IN_MEMCPY],
+ 3, dest_addr, addr, size_int (rsize));
+ TREE_ADDRESSABLE (tmp) = 1;
gimplify_and_add (copy, pre_p);
addr = dest_addr;
}
+
else
addr = fold_convert (ptrtype, addr);
- incr = fold (build2 (PLUS_EXPR, ptr_type_node, incr, ssize_int (rsize)));
- incr = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, valist, incr);
- gimplify_and_add (incr, post_p);
+ incr
+ = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr, size_int (rsize));
+ gimplify_assign (valist, incr, post_p);
return build_va_arg_indirect_ref (addr);
}
}
/* Emit a library call comparison between floating point X and Y.
- COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).
+ COMPARISON is the operator to compare with (EQ, NE, GT, etc).
+ Return the new operator to be used in the comparison sequence.
+
TARGET_ARCH64 uses _Qp_* functions, which use pointers to TFmode
values as arguments instead of the TFmode registers themselves,
that's why we cannot call emit_float_lib_cmp. */
-void
+
+rtx
sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison)
{
const char *qpfunc;
rtx slot0, slot1, result, tem, tem2;
enum machine_mode mode;
+ enum rtx_code new_comparison;
switch (comparison)
{
case EQ:
- qpfunc = (TARGET_ARCH64) ? "_Qp_feq" : "_Q_feq";
+ qpfunc = (TARGET_ARCH64 ? "_Qp_feq" : "_Q_feq");
break;
case NE:
- qpfunc = (TARGET_ARCH64) ? "_Qp_fne" : "_Q_fne";
+ qpfunc = (TARGET_ARCH64 ? "_Qp_fne" : "_Q_fne");
break;
case GT:
- qpfunc = (TARGET_ARCH64) ? "_Qp_fgt" : "_Q_fgt";
+ qpfunc = (TARGET_ARCH64 ? "_Qp_fgt" : "_Q_fgt");
break;
case GE:
- qpfunc = (TARGET_ARCH64) ? "_Qp_fge" : "_Q_fge";
+ qpfunc = (TARGET_ARCH64 ? "_Qp_fge" : "_Q_fge");
break;
case LT:
- qpfunc = (TARGET_ARCH64) ? "_Qp_flt" : "_Q_flt";
+ qpfunc = (TARGET_ARCH64 ? "_Qp_flt" : "_Q_flt");
break;
case LE:
- qpfunc = (TARGET_ARCH64) ? "_Qp_fle" : "_Q_fle";
+ qpfunc = (TARGET_ARCH64 ? "_Qp_fle" : "_Q_fle");
break;
case ORDERED:
case UNGE:
case UNLE:
case LTGT:
- qpfunc = (TARGET_ARCH64) ? "_Qp_cmp" : "_Q_cmp";
+ qpfunc = (TARGET_ARCH64 ? "_Qp_cmp" : "_Q_cmp");
break;
default:
if (TARGET_ARCH64)
{
- if (GET_CODE (x) != MEM)
+ if (MEM_P (x))
+ slot0 = x;
+ else
{
slot0 = assign_stack_temp (TFmode, GET_MODE_SIZE(TFmode), 0);
emit_move_insn (slot0, x);
}
- else
- slot0 = x;
- if (GET_CODE (y) != MEM)
+ if (MEM_P (y))
+ slot1 = y;
+ else
{
slot1 = assign_stack_temp (TFmode, GET_MODE_SIZE(TFmode), 0);
emit_move_insn (slot1, y);
}
- else
- slot1 = y;
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, qpfunc), LCT_NORMAL,
DImode, 2,
XEXP (slot0, 0), Pmode,
XEXP (slot1, 0), Pmode);
-
mode = DImode;
}
else
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, qpfunc), LCT_NORMAL,
SImode, 2,
x, TFmode, y, TFmode);
-
mode = SImode;
}
switch (comparison)
{
default:
- emit_cmp_insn (result, const0_rtx, NE, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_NE (VOIDmode, result, const0_rtx);
case ORDERED:
case UNORDERED:
- emit_cmp_insn (result, GEN_INT(3), comparison == UNORDERED ? EQ : NE,
- NULL_RTX, mode, 0);
- break;
+ new_comparison = (comparison == UNORDERED ? EQ : NE);
+ return gen_rtx_fmt_ee (new_comparison, VOIDmode, result, GEN_INT(3));
case UNGT:
case UNGE:
- emit_cmp_insn (result, const1_rtx,
- comparison == UNGT ? GT : NE, NULL_RTX, mode, 0);
- break;
+ new_comparison = (comparison == UNGT ? GT : NE);
+ return gen_rtx_fmt_ee (new_comparison, VOIDmode, result, const1_rtx);
case UNLE:
- emit_cmp_insn (result, const2_rtx, NE, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_NE (VOIDmode, result, const2_rtx);
case UNLT:
tem = gen_reg_rtx (mode);
if (TARGET_ARCH32)
emit_insn (gen_andsi3 (tem, result, const1_rtx));
else
emit_insn (gen_anddi3 (tem, result, const1_rtx));
- emit_cmp_insn (tem, const0_rtx, NE, NULL_RTX, mode, 0);
- break;
+ return gen_rtx_NE (VOIDmode, tem, const0_rtx);
case UNEQ:
case LTGT:
tem = gen_reg_rtx (mode);
emit_insn (gen_andsi3 (tem2, tem, const2_rtx));
else
emit_insn (gen_anddi3 (tem2, tem, const2_rtx));
- emit_cmp_insn (tem2, const0_rtx, comparison == UNEQ ? EQ : NE,
- NULL_RTX, mode, 0);
- break;
+ new_comparison = (comparison == UNEQ ? EQ : NE);
+ return gen_rtx_fmt_ee (new_comparison, VOIDmode, tem2, const0_rtx);
}
+
+ gcc_unreachable ();
}
/* Generate an unsigned DImode to FP conversion. This is the same code
{
static int last_order_nonleaf = 1;
- if (regs_ever_live[15] != last_order_nonleaf)
+ if (df_regs_ever_live_p (15) != last_order_nonleaf)
{
last_order_nonleaf = !last_order_nonleaf;
memcpy ((char *) reg_alloc_order,
return 1;
}
-/* Return 1 if reg is a pseudo, or is the first register in
- a hard register pair. This makes it a candidate for use in
+/* Return 1 if reg is a pseudo, or is the first register in
+ a hard register pair. This makes it suitable for use in
ldd and std insns. */
int
register_ok_for_ldd (rtx reg)
{
/* We might have been passed a SUBREG. */
- if (GET_CODE (reg) != REG)
+ if (!REG_P (reg))
return 0;
if (REGNO (reg) < FIRST_PSEUDO_REGISTER)
return (REGNO (reg) % 2 == 0);
- else
- return 1;
+
+ return 1;
+}
+
+/* Return 1 if OP is a memory whose address is known to be
+ aligned to 8-byte boundary, or a pseudo during reload.
+ This makes it suitable for use in ldd and std insns. */
+
+int
+memory_ok_for_ldd (rtx op)
+{
+ if (MEM_P (op))
+ {
+ /* In 64-bit mode, we assume that the address is word-aligned. */
+ if (TARGET_ARCH32 && !mem_min_alignment (op, 8))
+ return 0;
+
+ if ((reload_in_progress || reload_completed)
+ && !strict_memory_address_p (Pmode, XEXP (op, 0)))
+ return 0;
+ }
+ else if (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ {
+ if (!(reload_in_progress && reg_renumber [REGNO (op)] < 0))
+ return 0;
+ }
+ else
+ return 0;
+
+ return 1;
}
\f
/* Print operand X (an rtx) in assembler syntax to file FILE.
The call emitted is the same when sparc_std_struct_return is
present. */
if (! TARGET_ARCH64
- && current_function_returns_struct
+ && cfun->returns_struct
&& ! sparc_std_struct_return
&& (TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl)))
== INTEGER_CST)
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode, tramp))));
if (sparc_cpu != PROCESSOR_ULTRASPARC
&& sparc_cpu != PROCESSOR_ULTRASPARC3
- && sparc_cpu != PROCESSOR_NIAGARA)
+ && sparc_cpu != PROCESSOR_NIAGARA
+ && sparc_cpu != PROCESSOR_NIAGARA2)
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode,
plus_constant (tramp, 8)))));
if (sparc_cpu != PROCESSOR_ULTRASPARC
&& sparc_cpu != PROCESSOR_ULTRASPARC3
- && sparc_cpu != PROCESSOR_NIAGARA)
+ && sparc_cpu != PROCESSOR_NIAGARA
+ && sparc_cpu != PROCESSOR_NIAGARA2)
emit_insn (gen_flushdi (validize_mem (gen_rtx_MEM (DImode, plus_constant (tramp, 8)))));
/* Call __enable_execute_stack after writing onto the stack to make sure
static int
sparc_use_sched_lookahead (void)
{
- if (sparc_cpu == PROCESSOR_NIAGARA)
+ if (sparc_cpu == PROCESSOR_NIAGARA
+ || sparc_cpu == PROCESSOR_NIAGARA2)
return 0;
if (sparc_cpu == PROCESSOR_ULTRASPARC
|| sparc_cpu == PROCESSOR_ULTRASPARC3)
switch (sparc_cpu)
{
case PROCESSOR_NIAGARA:
+ case PROCESSOR_NIAGARA2:
default:
return 1;
case PROCESSOR_V9:
y = gen_rtx_REG (SImode, REGNO (x) + WORDS_BIG_ENDIAN);
if (flag_expensive_optimizations
- && REG_N_SETS (REGNO (y)) == 1)
+ && df && DF_REG_DEF_COUNT (REGNO (y)) == 1)
set_once = 1;
if (insn == 0)
char buf[32];
rtx lab, fun;
- ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
- lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
fun = gen_rtx_SYMBOL_REF (Pmode, MCOUNT_FUNCTION);
-
- emit_library_call (fun, LCT_NORMAL, VOIDmode, 1, lab, Pmode);
+ if (NO_PROFILE_COUNTERS)
+ {
+ emit_library_call (fun, LCT_NORMAL, VOIDmode, 0);
+ }
+ else
+ {
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
+ lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
+ emit_library_call (fun, LCT_NORMAL, VOIDmode, 1, lab, Pmode);
+ }
}
\f
#ifdef OBJECT_FORMAT_ELF
the sibling call right? Well, in the C++ case we can end up passing
the pointer to the struct return area to a constructor (which returns
void) and then nothing else happens. Such a sibling call would look
- valid without the added check here. */
+ valid without the added check here.
+
+ VxWorks PIC PLT entries require the global pointer to be initialized
+ on entry. We therefore can't emit sibling calls to them. */
static bool
sparc_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
return (decl
&& flag_delayed_branch
- && (TARGET_ARCH64 || ! current_function_returns_struct));
+ && (TARGET_ARCH64 || ! cfun->returns_struct)
+ && !(TARGET_VXWORKS_RTP
+ && flag_pic
+ && !targetm.binds_local_p (decl)));
}
\f
/* libfunc renaming. */
Expand builtin functions for sparc intrinsics. */
static rtx
-sparc_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
- enum machine_mode tmode, int ignore ATTRIBUTE_UNUSED)
-{
- tree arglist;
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+sparc_expand_builtin (tree exp, rtx target,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode tmode ATTRIBUTE_UNUSED,
+ int ignore ATTRIBUTE_UNUSED)
+{
+ tree arg;
+ call_expr_arg_iterator iter;
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
unsigned int icode = DECL_FUNCTION_CODE (fndecl);
rtx pat, op[4];
enum machine_mode mode[4];
int arg_count = 0;
- mode[arg_count] = tmode;
-
- if (target == 0
- || GET_MODE (target) != tmode
- || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
- op[arg_count] = gen_reg_rtx (tmode);
+ mode[0] = insn_data[icode].operand[0].mode;
+ if (!target
+ || GET_MODE (target) != mode[0]
+ || ! (*insn_data[icode].operand[0].predicate) (target, mode[0]))
+ op[0] = gen_reg_rtx (mode[0]);
else
- op[arg_count] = target;
+ op[0] = target;
- for (arglist = TREE_OPERAND (exp, 1); arglist;
- arglist = TREE_CHAIN (arglist))
+ FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
{
- tree arg = TREE_VALUE (arglist);
-
arg_count++;
mode[arg_count] = insn_data[icode].operand[arg_count].mode;
op[arg_count] = expand_normal (arg);
{
tree arg0, arg1, arg2;
tree rtype = TREE_TYPE (TREE_TYPE (fndecl));
-
+ enum insn_code icode = (enum insn_code) DECL_FUNCTION_CODE (fndecl);
- if (ignore && DECL_FUNCTION_CODE (fndecl) != CODE_FOR_alignaddrsi_vis
- && DECL_FUNCTION_CODE (fndecl) != CODE_FOR_alignaddrdi_vis)
- return build_int_cst (rtype, 0);
+ if (ignore
+ && icode != CODE_FOR_alignaddrsi_vis
+ && icode != CODE_FOR_alignaddrdi_vis)
+ return fold_convert (rtype, integer_zero_node);
- switch (DECL_FUNCTION_CODE (fndecl))
+ switch (icode)
{
case CODE_FOR_fexpand_vis:
arg0 = TREE_VALUE (arglist);
tree inner_type = TREE_TYPE (rtype);
tree elts0 = TREE_VECTOR_CST_ELTS (arg0);
tree elts1 = TREE_VECTOR_CST_ELTS (arg1);
- tree n_elts = sparc_handle_vis_mul8x16 (DECL_FUNCTION_CODE (fndecl),
- inner_type, elts0, elts1);
+ tree n_elts = sparc_handle_vis_mul8x16 (icode, inner_type, elts0,
+ elts1);
return build_vector (rtype, n_elts);
}
default:
break;
}
+
return NULL_TREE;
}
\f
-int
-sparc_extra_constraint_check (rtx op, int c, int strict)
-{
- int reload_ok_mem;
-
- if (TARGET_ARCH64
- && (c == 'T' || c == 'U'))
- return 0;
-
- switch (c)
- {
- case 'Q':
- return fp_sethi_p (op);
-
- case 'R':
- return fp_mov_p (op);
-
- case 'S':
- return fp_high_losum_p (op);
-
- case 'U':
- if (! strict
- || (GET_CODE (op) == REG
- && (REGNO (op) < FIRST_PSEUDO_REGISTER
- || reg_renumber[REGNO (op)] >= 0)))
- return register_ok_for_ldd (op);
-
- return 0;
-
- case 'W':
- case 'T':
- break;
-
- case 'Y':
- return const_zero_operand (op, GET_MODE (op));
-
- default:
- return 0;
- }
-
- /* Our memory extra constraints have to emulate the
- behavior of 'm' and 'o' in order for reload to work
- correctly. */
- if (GET_CODE (op) == MEM)
- {
- reload_ok_mem = 0;
- if ((TARGET_ARCH64 || mem_min_alignment (op, 8))
- && (! strict
- || strict_memory_address_p (Pmode, XEXP (op, 0))))
- reload_ok_mem = 1;
- }
- else
- {
- reload_ok_mem = (reload_in_progress
- && GET_CODE (op) == REG
- && REGNO (op) >= FIRST_PSEUDO_REGISTER
- && reg_renumber [REGNO (op)] < 0);
- }
-
- return reload_ok_mem;
-}
-
/* ??? This duplicates information provided to the compiler by the
??? scheduler description. Some day, teach genautomata to output
??? the latencies and then CSE will just use that. */
static bool
-sparc_rtx_costs (rtx x, int code, int outer_code, int *total)
+sparc_rtx_costs (rtx x, int code, int outer_code, int *total,
+ bool speed ATTRIBUTE_UNUSED)
{
enum machine_mode mode = GET_MODE (x);
bool float_mode_p = FLOAT_MODE_P (mode);
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
tree function)
{
- rtx this, insn, funexp;
+ rtx this_rtx, insn, funexp;
unsigned int int_arg_first;
reload_completed = 1;
epilogue_completed = 1;
- no_new_pseudos = 1;
- reset_block_changes ();
emit_note (NOTE_INSN_PROLOGUE_END);
/* Find the "this" pointer. Normally in %o0, but in ARCH64 if the function
returns a structure, the structure return pointer is there instead. */
if (TARGET_ARCH64 && aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
- this = gen_rtx_REG (Pmode, int_arg_first + 1);
+ this_rtx = gen_rtx_REG (Pmode, int_arg_first + 1);
else
- this = gen_rtx_REG (Pmode, int_arg_first);
+ this_rtx = gen_rtx_REG (Pmode, int_arg_first);
/* Add DELTA. When possible use a plain add, otherwise load it into
a register first. */
delta_rtx = scratch;
}
- /* THIS += DELTA. */
- emit_insn (gen_add2_insn (this, delta_rtx));
+ /* THIS_RTX += DELTA. */
+ emit_insn (gen_add2_insn (this_rtx, delta_rtx));
}
- /* Add the word at address (*THIS + VCALL_OFFSET). */
+ /* Add the word at address (*THIS_RTX + VCALL_OFFSET). */
if (vcall_offset)
{
rtx vcall_offset_rtx = GEN_INT (vcall_offset);
gcc_assert (vcall_offset < 0);
- /* SCRATCH = *THIS. */
- emit_move_insn (scratch, gen_rtx_MEM (Pmode, this));
+ /* SCRATCH = *THIS_RTX. */
+ emit_move_insn (scratch, gen_rtx_MEM (Pmode, this_rtx));
/* Prepare for adding VCALL_OFFSET. The difficulty is that we
may not have any available scratch register at this point. */
vcall_offset_rtx = GEN_INT (vcall_offset); /* cannot be 0 */
}
- /* SCRATCH = *(*THIS + VCALL_OFFSET). */
+ /* SCRATCH = *(*THIS_RTX + VCALL_OFFSET). */
emit_move_insn (scratch, gen_rtx_MEM (Pmode,
gen_rtx_PLUS (Pmode,
scratch,
vcall_offset_rtx)));
- /* THIS += *(*THIS + VCALL_OFFSET). */
- emit_insn (gen_add2_insn (this, scratch));
+ /* THIS_RTX += *(*THIS_RTX + VCALL_OFFSET). */
+ emit_insn (gen_add2_insn (this_rtx, scratch));
}
/* Generate a tail call to the target function. */
instruction scheduling worth while. Note that use_thunk calls
assemble_start_function and assemble_end_function. */
insn = get_insns ();
- insn_locators_initialize ();
+ insn_locators_alloc ();
shorten_branches (insn);
final_start_function (insn, file, 1);
final (insn, file, 1);
final_end_function ();
+ free_after_compilation (cfun);
reload_completed = 0;
epilogue_completed = 0;
- no_new_pseudos = 0;
}
/* Return true if sparc_output_mi_thunk would be able to output the
assembler code for the thunk function specified by the arguments
it is passed, and false otherwise. */
static bool
-sparc_can_output_mi_thunk (tree thunk_fndecl ATTRIBUTE_UNUSED,
+sparc_can_output_mi_thunk (const_tree thunk_fndecl ATTRIBUTE_UNUSED,
HOST_WIDE_INT delta ATTRIBUTE_UNUSED,
HOST_WIDE_INT vcall_offset,
- tree function ATTRIBUTE_UNUSED)
+ const_tree function ATTRIBUTE_UNUSED)
{
/* Bound the loop used in the default method above. */
return (vcall_offset >= -32768 || ! fixed_regs[5]);
static struct machine_function *
sparc_init_machine_status (void)
{
- return ggc_alloc_cleared (sizeof (struct machine_function));
+ return GGC_CNEW (struct machine_function);
}
/* Locate some local-dynamic symbol still in use by this function
}
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
-/* Implement TARGET_MANGLE_FUNDAMENTAL_TYPE. */
+/* Implement TARGET_MANGLE_TYPE. */
static const char *
-sparc_mangle_fundamental_type (tree type)
+sparc_mangle_type (const_tree type)
{
if (!TARGET_64BIT
&& TYPE_MAIN_VARIANT (type) == long_double_type_node
gen_rtx_AND (SImode, gen_rtx_NOT (SImode, mask),
res)));
- sparc_compare_op0 = resv;
- sparc_compare_op1 = val;
- cc = gen_compare_reg (NE);
-
+ cc = gen_compare_reg_1 (NE, resv, val);
emit_insn (gen_rtx_SET (VOIDmode, val, resv));
- sparc_compare_emitted = cc;
- emit_jump_insn (gen_bne (loop_label));
+ /* Use cbranchcc4 to separate the compare and branch! */
+ emit_jump_insn (gen_cbranchcc4 (gen_rtx_NE (VOIDmode, cc, const0_rtx),
+ cc, const0_rtx, loop_label));
emit_label (end_label);