/* Subroutines used for code generation on IBM RS/6000.
- Copyright (C) 1991-2018 Free Software Foundation, Inc.
+ Copyright (C) 1991-2019 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
This file is part of GCC.
#include "case-cfn-macros.h"
#include "ppc-auxv.h"
#include "tree-ssa-propagate.h"
+#include "tree-vrp.h"
+#include "tree-ssanames.h"
/* This file should be included last. */
#include "target-def.h"
bool, bool);
#if TARGET_MACHO
static void macho_branch_islands (void);
+static tree get_prev_label (tree);
#endif
-static rtx rs6000_legitimize_reload_address (rtx, machine_mode, int, int,
- int, int *);
-static rtx rs6000_debug_legitimize_reload_address (rtx, machine_mode, int,
- int, int, int *);
static bool rs6000_mode_dependent_address (const_rtx);
static bool rs6000_debug_mode_dependent_address (const_rtx);
static bool rs6000_offsettable_memref_p (rtx, machine_mode, bool);
static bool rs6000_save_toc_in_prologue_p (void);
static rtx rs6000_internal_arg_pointer (void);
-rtx (*rs6000_legitimize_reload_address_ptr) (rtx, machine_mode, int, int,
- int, int *)
- = rs6000_legitimize_reload_address;
-
static bool (*rs6000_mode_dependent_address_ptr) (const_rtx)
= rs6000_mode_dependent_address;
/* Default register names. */
char rs6000_reg_names[][8] =
{
+ /* GPRs */
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", "10", "11", "12", "13", "14", "15",
+ "16", "17", "18", "19", "20", "21", "22", "23",
+ "24", "25", "26", "27", "28", "29", "30", "31",
+ /* FPRs */
"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15",
"16", "17", "18", "19", "20", "21", "22", "23",
"24", "25", "26", "27", "28", "29", "30", "31",
+ /* VRs */
"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "11", "12", "13", "14", "15",
"16", "17", "18", "19", "20", "21", "22", "23",
"24", "25", "26", "27", "28", "29", "30", "31",
- "mq", "lr", "ctr","ap",
+ /* lr ctr ca ap */
+ "lr", "ctr", "ca", "ap",
+ /* cr0..cr7 */
"0", "1", "2", "3", "4", "5", "6", "7",
- "ca",
- /* AltiVec registers. */
- "0", "1", "2", "3", "4", "5", "6", "7",
- "8", "9", "10", "11", "12", "13", "14", "15",
- "16", "17", "18", "19", "20", "21", "22", "23",
- "24", "25", "26", "27", "28", "29", "30", "31",
- "vrsave", "vscr",
- /* Soft frame pointer. */
- "sfp",
- /* HTM SPR registers. */
- "tfhar", "tfiar", "texasr"
+ /* vrsave vscr sfp */
+ "vrsave", "vscr", "sfp",
};
#ifdef TARGET_REGNAMES
static const char alt_reg_names[][8] =
{
- "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
- "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
- "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23",
- "%r24", "%r25", "%r26", "%r27", "%r28", "%r29", "%r30", "%r31",
- "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
- "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15",
- "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23",
- "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31",
- "mq", "lr", "ctr", "ap",
- "%cr0", "%cr1", "%cr2", "%cr3", "%cr4", "%cr5", "%cr6", "%cr7",
- "ca",
- /* AltiVec registers. */
- "%v0", "%v1", "%v2", "%v3", "%v4", "%v5", "%v6", "%v7",
+ /* GPRs */
+ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
+ "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23",
+ "%r24", "%r25", "%r26", "%r27", "%r28", "%r29", "%r30", "%r31",
+ /* FPRs */
+ "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
+ "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15",
+ "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23",
+ "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31",
+ /* VRs */
+ "%v0", "%v1", "%v2", "%v3", "%v4", "%v5", "%v6", "%v7",
"%v8", "%v9", "%v10", "%v11", "%v12", "%v13", "%v14", "%v15",
"%v16", "%v17", "%v18", "%v19", "%v20", "%v21", "%v22", "%v23",
"%v24", "%v25", "%v26", "%v27", "%v28", "%v29", "%v30", "%v31",
- "vrsave", "vscr",
- /* Soft frame pointer. */
- "sfp",
- /* HTM SPR registers. */
- "tfhar", "tfiar", "texasr"
+ /* lr ctr ca ap */
+ "lr", "ctr", "ca", "ap",
+ /* cr0..cr7 */
+ "%cr0", "%cr1", "%cr2", "%cr3", "%cr4", "%cr5", "%cr6", "%cr7",
+ /* vrsave vscr sfp */
+ "vrsave", "vscr", "sfp",
};
#endif
#define TARGET_REGISTER_MOVE_COST rs6000_register_move_cost
#undef TARGET_MEMORY_MOVE_COST
#define TARGET_MEMORY_MOVE_COST rs6000_memory_move_cost
+#undef TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS
+#define TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS \
+ rs6000_ira_change_pseudo_allocno_class
#undef TARGET_CANNOT_COPY_INSN_P
#define TARGET_CANNOT_COPY_INSN_P rs6000_cannot_copy_insn_p
#undef TARGET_RTX_COSTS
#undef TARGET_SETJMP_PRESERVES_NONVOLATILE_REGS_P
#define TARGET_SETJMP_PRESERVES_NONVOLATILE_REGS_P hook_bool_void_true
+
+#undef TARGET_MANGLE_DECL_ASSEMBLER_NAME
+#define TARGET_MANGLE_DECL_ASSEMBLER_NAME rs6000_mangle_decl_assembler_name
\f
/* Processor table. */
/* Implement TARGET_HARD_REGNO_CALL_PART_CLOBBERED. */
static bool
-rs6000_hard_regno_call_part_clobbered (unsigned int regno, machine_mode mode)
+rs6000_hard_regno_call_part_clobbered (rtx_insn *insn ATTRIBUTE_UNUSED,
+ unsigned int regno, machine_mode mode)
{
if (TARGET_32BIT
&& TARGET_POWERPC64
"f reg_class = %s\n"
"v reg_class = %s\n"
"wa reg_class = %s\n"
- "wb reg_class = %s\n"
"wd reg_class = %s\n"
"we reg_class = %s\n"
"wf reg_class = %s\n"
"wg reg_class = %s\n"
- "wh reg_class = %s\n"
"wi reg_class = %s\n"
- "wj reg_class = %s\n"
- "wk reg_class = %s\n"
- "wl reg_class = %s\n"
- "wm reg_class = %s\n"
- "wo reg_class = %s\n"
"wp reg_class = %s\n"
"wq reg_class = %s\n"
"wr reg_class = %s\n"
"ws reg_class = %s\n"
"wt reg_class = %s\n"
- "wu reg_class = %s\n"
"wv reg_class = %s\n"
"ww reg_class = %s\n"
"wx reg_class = %s\n"
- "wy reg_class = %s\n"
- "wz reg_class = %s\n"
"wA reg_class = %s\n"
- "wH reg_class = %s\n"
- "wI reg_class = %s\n"
- "wJ reg_class = %s\n"
- "wK reg_class = %s\n"
"\n",
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_d]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_f]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_v]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wa]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wb]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wd]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_we]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wf]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wg]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wh]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wi]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wj]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wk]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wl]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wm]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wo]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wp]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wq]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wr]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_ws]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wt]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wu]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wv]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_ww]],
reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wx]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wy]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wz]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wA]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wH]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wI]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wJ]],
- reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wK]]);
+ reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wA]]);
nl = "\n";
for (m = 0; m < NUM_MACHINE_MODES; ++m)
{
char options[80];
- strcpy (options, (TARGET_P9_FUSION) ? "power9" : "power8");
+ strcpy (options, "power8");
if (TARGET_P8_FUSION_SIGN)
strcat (options, ", sign");
for (r = 32; r < 64; ++r)
rs6000_regno_regclass[r] = FLOAT_REGS;
- for (r = 64; r < FIRST_PSEUDO_REGISTER; ++r)
+ for (r = 64; HARD_REGISTER_NUM_P (r); ++r)
rs6000_regno_regclass[r] = NO_REGS;
for (r = FIRST_ALTIVEC_REGNO; r <= LAST_ALTIVEC_REGNO; ++r)
rs6000_regno_regclass[CA_REGNO] = NO_REGS;
rs6000_regno_regclass[VRSAVE_REGNO] = VRSAVE_REGS;
rs6000_regno_regclass[VSCR_REGNO] = VRSAVE_REGS;
- rs6000_regno_regclass[TFHAR_REGNO] = SPR_REGS;
- rs6000_regno_regclass[TFIAR_REGNO] = SPR_REGS;
- rs6000_regno_regclass[TEXASR_REGNO] = SPR_REGS;
rs6000_regno_regclass[ARG_POINTER_REGNUM] = BASE_REGS;
rs6000_regno_regclass[FRAME_POINTER_REGNUM] = BASE_REGS;
below. */
gcc_assert ((int)VECTOR_NONE == 0);
memset ((void *) &rs6000_vector_unit[0], '\0', sizeof (rs6000_vector_unit));
- memset ((void *) &rs6000_vector_mem[0], '\0', sizeof (rs6000_vector_unit));
+ memset ((void *) &rs6000_vector_mem[0], '\0', sizeof (rs6000_vector_mem));
gcc_assert ((int)CODE_FOR_nothing == 0);
memset ((void *) ®_addr[0], '\0', sizeof (reg_addr));
wd - Preferred register class for V2DFmode.
wf - Preferred register class for V4SFmode.
wg - Float register for power6x move insns.
- wh - FP register for direct move instructions.
wi - FP or VSX register to hold 64-bit integers for VSX insns.
- wj - FP or VSX register to hold 64-bit integers for direct moves.
- wk - FP or VSX register to hold 64-bit doubles for direct moves.
- wl - Float register if we can do 32-bit signed int loads.
- wm - VSX register for ISA 2.07 direct move operations.
wn - always NO_REGS.
wr - GPR if 64-bit mode is permitted.
ws - Register class to do ISA 2.06 DF operations.
wt - VSX register for TImode in VSX registers.
- wu - Altivec register for ISA 2.07 VSX SF/SI load/stores.
wv - Altivec register for ISA 2.06 VSX DF/DI load/stores.
ww - Register class to do SF conversions in with VSX operations.
- wx - Float register if we can do 32-bit int stores.
- wy - Register class to do ISA 2.07 SF operations.
- wz - Float register if we can do 32-bit unsigned int loads.
- wH - Altivec register if SImode is allowed in VSX registers.
- wI - VSX register if SImode is allowed in VSX registers.
- wJ - VSX register if QImode/HImode are allowed in VSX registers.
- wK - Altivec register if QImode/HImode are allowed in VSX registers. */
+ wx - Float register if we can do 32-bit int stores. */
if (TARGET_HARD_FLOAT)
{
if (TARGET_MFPGPR) /* DFmode */
rs6000_constraints[RS6000_CONSTRAINT_wg] = FLOAT_REGS;
- if (TARGET_LFIWAX)
- rs6000_constraints[RS6000_CONSTRAINT_wl] = FLOAT_REGS; /* DImode */
-
- if (TARGET_DIRECT_MOVE)
- {
- rs6000_constraints[RS6000_CONSTRAINT_wh] = FLOAT_REGS;
- rs6000_constraints[RS6000_CONSTRAINT_wj] /* DImode */
- = rs6000_constraints[RS6000_CONSTRAINT_wi];
- rs6000_constraints[RS6000_CONSTRAINT_wk] /* DFmode */
- = rs6000_constraints[RS6000_CONSTRAINT_ws];
- rs6000_constraints[RS6000_CONSTRAINT_wm] = VSX_REGS;
- }
-
if (TARGET_POWERPC64)
{
rs6000_constraints[RS6000_CONSTRAINT_wr] = GENERAL_REGS;
}
if (TARGET_P8_VECTOR) /* SFmode */
- {
- rs6000_constraints[RS6000_CONSTRAINT_wu] = ALTIVEC_REGS;
- rs6000_constraints[RS6000_CONSTRAINT_wy] = VSX_REGS;
- rs6000_constraints[RS6000_CONSTRAINT_ww] = VSX_REGS;
- }
+ rs6000_constraints[RS6000_CONSTRAINT_ww] = VSX_REGS;
else if (TARGET_VSX)
rs6000_constraints[RS6000_CONSTRAINT_ww] = FLOAT_REGS;
if (TARGET_STFIWX)
rs6000_constraints[RS6000_CONSTRAINT_wx] = FLOAT_REGS; /* DImode */
- if (TARGET_LFIWZX)
- rs6000_constraints[RS6000_CONSTRAINT_wz] = FLOAT_REGS; /* DImode */
-
if (TARGET_FLOAT128_TYPE)
{
rs6000_constraints[RS6000_CONSTRAINT_wq] = VSX_REGS; /* KFmode */
rs6000_constraints[RS6000_CONSTRAINT_wp] = VSX_REGS; /* TFmode */
}
- if (TARGET_P9_VECTOR)
- {
- /* Support for new D-form instructions. */
- rs6000_constraints[RS6000_CONSTRAINT_wb] = ALTIVEC_REGS;
-
- /* Support for ISA 3.0 (power9) vectors. */
- rs6000_constraints[RS6000_CONSTRAINT_wo] = VSX_REGS;
- }
-
/* Support for new direct moves (ISA 3.0 + 64bit). */
if (TARGET_DIRECT_MOVE_128)
rs6000_constraints[RS6000_CONSTRAINT_we] = VSX_REGS;
- /* Support small integers in VSX registers. */
- if (TARGET_P8_VECTOR)
- {
- rs6000_constraints[RS6000_CONSTRAINT_wH] = ALTIVEC_REGS;
- rs6000_constraints[RS6000_CONSTRAINT_wI] = FLOAT_REGS;
- if (TARGET_P9_VECTOR)
- {
- rs6000_constraints[RS6000_CONSTRAINT_wJ] = FLOAT_REGS;
- rs6000_constraints[RS6000_CONSTRAINT_wK] = ALTIVEC_REGS;
- }
- }
-
/* Set up the reload helper and direct move functions. */
if (TARGET_VSX || TARGET_ALTIVEC)
{
}
/* Precalculate HARD_REGNO_NREGS. */
- for (r = 0; r < FIRST_PSEUDO_REGISTER; ++r)
+ for (r = 0; HARD_REGISTER_NUM_P (r); ++r)
for (m = 0; m < NUM_MACHINE_MODES; ++m)
rs6000_hard_regno_nregs[m][r]
- = rs6000_hard_regno_nregs_internal (r, (machine_mode)m);
+ = rs6000_hard_regno_nregs_internal (r, (machine_mode) m);
/* Precalculate TARGET_HARD_REGNO_MODE_OK. */
- for (r = 0; r < FIRST_PSEUDO_REGISTER; ++r)
+ for (r = 0; HARD_REGISTER_NUM_P (r); ++r)
for (m = 0; m < NUM_MACHINE_MODES; ++m)
- if (rs6000_hard_regno_mode_ok_uncached (r, (machine_mode)m))
- rs6000_hard_regno_mode_ok_p[m][r] = true;
+ rs6000_hard_regno_mode_ok_p[m][r]
+ = rs6000_hard_regno_mode_ok_uncached (r, (machine_mode) m);
/* Precalculate CLASS_MAX_NREGS sizes. */
for (c = 0; c < LIM_REG_CLASSES; ++c)
/* Don't override by the processor default if given explicitly. */
set_masks &= ~rs6000_isa_flags_explicit;
+ if (global_init_p && rs6000_dejagnu_cpu_index >= 0)
+ rs6000_cpu_index = rs6000_dejagnu_cpu_index;
+
/* Process the -mcpu=<xxx> and -mtune=<xxx> argument. If the user changed
the cpu in a target attribute or pragma, but did not specify a tuning
option, use the cpu for the tuning option rather than the option specified
if (!TARGET_HARD_FLOAT)
{
if (rs6000_isa_flags_explicit & OPTION_MASK_VSX)
- msg = N_("-mvsx requires hardware floating point");
+ msg = N_("%<-mvsx%> requires hardware floating point");
else
{
rs6000_isa_flags &= ~ OPTION_MASK_VSX;
}
}
else if (TARGET_AVOID_XFORM > 0)
- msg = N_("-mvsx needs indexed addressing");
+ msg = N_("%<-mvsx%> needs indexed addressing");
else if (!TARGET_ALTIVEC && (rs6000_isa_flags_explicit
& OPTION_MASK_ALTIVEC))
{
if (rs6000_isa_flags_explicit & OPTION_MASK_VSX)
- msg = N_("-mvsx and -mno-altivec are incompatible");
+ msg = N_("%<-mvsx%> and %<-mno-altivec%> are incompatible");
else
- msg = N_("-mno-altivec disables vsx");
+ msg = N_("%<-mno-altivec%> disables vsx");
}
if (msg)
if ((TARGET_QUAD_MEMORY || TARGET_QUAD_MEMORY_ATOMIC) && !TARGET_POWERPC64)
{
if ((rs6000_isa_flags_explicit & OPTION_MASK_QUAD_MEMORY) != 0)
- warning (0, N_("-mquad-memory requires 64-bit mode"));
+ warning (0, N_("%<-mquad-memory%> requires 64-bit mode"));
if ((rs6000_isa_flags_explicit & OPTION_MASK_QUAD_MEMORY_ATOMIC) != 0)
- warning (0, N_("-mquad-memory-atomic requires 64-bit mode"));
+ warning (0, N_("%<-mquad-memory-atomic%> requires 64-bit mode"));
rs6000_isa_flags &= ~(OPTION_MASK_QUAD_MEMORY
| OPTION_MASK_QUAD_MEMORY_ATOMIC);
if (TARGET_QUAD_MEMORY && !WORDS_BIG_ENDIAN)
{
if ((rs6000_isa_flags_explicit & OPTION_MASK_QUAD_MEMORY) != 0)
- warning (0, N_("-mquad-memory is not available in little endian "
+ warning (0, N_("%<-mquad-memory%> is not available in little endian "
"mode"));
rs6000_isa_flags &= ~OPTION_MASK_QUAD_MEMORY;
rs6000_isa_flags |= OPTION_MASK_SAVE_TOC_INDIRECT;
/* Enable power8 fusion if we are tuning for power8, even if we aren't
- generating power8 instructions. */
+ generating power8 instructions. Power9 does not optimize power8 fusion
+ cases. */
if (!(rs6000_isa_flags_explicit & OPTION_MASK_P8_FUSION))
- rs6000_isa_flags |= (processor_target_table[tune_index].target_enable
- & OPTION_MASK_P8_FUSION);
+ {
+ if (processor_target_table[tune_index].processor == PROCESSOR_POWER8)
+ rs6000_isa_flags |= OPTION_MASK_P8_FUSION;
+ else
+ rs6000_isa_flags &= ~OPTION_MASK_P8_FUSION;
+ }
/* Setting additional fusion flags turns on base fusion. */
if (!TARGET_P8_FUSION && TARGET_P8_FUSION_SIGN)
rs6000_isa_flags |= OPTION_MASK_P8_FUSION;
}
- /* Power9 fusion is a superset over power8 fusion. */
- if (TARGET_P9_FUSION && !TARGET_P8_FUSION)
- {
- if (rs6000_isa_flags_explicit & OPTION_MASK_P8_FUSION)
- {
- /* We prefer to not mention undocumented options in
- error messages. However, if users have managed to select
- power9-fusion without selecting power8-fusion, they
- already know about undocumented flags. */
- error ("%qs requires %qs", "-mpower9-fusion", "-mpower8-fusion");
- rs6000_isa_flags &= ~OPTION_MASK_P9_FUSION;
- }
- else
- rs6000_isa_flags |= OPTION_MASK_P8_FUSION;
- }
-
- /* Enable power9 fusion if we are tuning for power9, even if we aren't
- generating power9 instructions. */
- if (!(rs6000_isa_flags_explicit & OPTION_MASK_P9_FUSION))
- rs6000_isa_flags |= (processor_target_table[tune_index].target_enable
- & OPTION_MASK_P9_FUSION);
-
/* Power8 does not fuse sign extended loads with the addis. If we are
optimizing at high levels for speed, convert a sign extended load into a
zero extending load, and an explicit sign extension. */
if (main_target_opt != NULL
&& (main_target_opt->x_rs6000_long_double_type_size
!= default_long_double_size))
- error ("target attribute or pragma changes long double size");
+ error ("target attribute or pragma changes %<long double%> size");
else
rs6000_long_double_type_size = default_long_double_size;
}
else if (rs6000_long_double_type_size == 128)
rs6000_long_double_type_size = FLOAT_PRECISION_TFmode;
+ else if (global_options_set.x_rs6000_ieeequad)
+ {
+ if (global_options.x_rs6000_ieeequad)
+ error ("%qs requires %qs", "-mabi=ieeelongdouble", "-mlong-double-128");
+ else
+ error ("%qs requires %qs", "-mabi=ibmlongdouble", "-mlong-double-128");
+ }
/* Set -mabi=ieeelongdouble on some old targets. In the future, power server
systems will also set long double to be IEEE 128-bit. AIX and Darwin
if (!global_options_set.x_rs6000_ieeequad)
rs6000_ieeequad = TARGET_IEEEQUAD_DEFAULT;
- else if (rs6000_ieeequad != TARGET_IEEEQUAD_DEFAULT && TARGET_LONG_DOUBLE_128)
+ else
{
- static bool warned_change_long_double;
- if (!warned_change_long_double)
+ if (global_options.x_rs6000_ieeequad
+ && (!TARGET_POPCNTD || !TARGET_VSX))
+ error ("%qs requires full ISA 2.06 support", "-mabi=ieeelongdouble");
+
+ if (rs6000_ieeequad != TARGET_IEEEQUAD_DEFAULT && TARGET_LONG_DOUBLE_128)
{
- warned_change_long_double = true;
- if (TARGET_IEEEQUAD)
- warning (OPT_Wpsabi, "Using IEEE extended precision long double");
- else
- warning (OPT_Wpsabi, "Using IBM extended precision long double");
+ static bool warned_change_long_double;
+ if (!warned_change_long_double)
+ {
+ warned_change_long_double = true;
+ if (TARGET_IEEEQUAD)
+ warning (OPT_Wpsabi, "Using IEEE extended precision "
+ "%<long double%>");
+ else
+ warning (OPT_Wpsabi, "Using IBM extended precision "
+ "%<long double%>");
+ }
}
}
if (!TARGET_VSX)
{
if ((rs6000_isa_flags_explicit & OPTION_MASK_FLOAT128_KEYWORD) != 0)
- error ("%qs requires VSX support", "-mfloat128");
+ error ("%qs requires VSX support", "%<-mfloat128%>");
TARGET_FLOAT128_TYPE = 0;
rs6000_isa_flags &= ~(OPTION_MASK_FLOAT128_KEYWORD
else if (!TARGET_FLOAT128_TYPE)
{
TARGET_FLOAT128_TYPE = 1;
- warning (0, "The -mfloat128 option may not be fully supported");
+ warning (0, "The %<-mfloat128%> option may not be fully supported");
}
}
&& (rs6000_isa_flags & ISA_3_0_MASKS_IEEE) != ISA_3_0_MASKS_IEEE)
{
if ((rs6000_isa_flags_explicit & OPTION_MASK_FLOAT128_HW) != 0)
- error ("%qs requires full ISA 3.0 support", "-mfloat128-hardware");
+ error ("%qs requires full ISA 3.0 support", "%<-mfloat128-hardware%>");
rs6000_isa_flags &= ~OPTION_MASK_FLOAT128_HW;
}
if (TARGET_FLOAT128_HW && !TARGET_64BIT)
{
if ((rs6000_isa_flags_explicit & OPTION_MASK_FLOAT128_HW) != 0)
- error ("%qs requires %qs", "-mfloat128-hardware", "-m64");
+ error ("%qs requires %qs", "%<-mfloat128-hardware%>", "-m64");
rs6000_isa_flags &= ~OPTION_MASK_FLOAT128_HW;
}
+ /* -mpcrel requires the prefixed load/store support on FUTURE systems. */
+ if (!TARGET_FUTURE && TARGET_PCREL)
+ {
+ if ((rs6000_isa_flags_explicit & OPTION_MASK_PCREL) != 0)
+ error ("%qs requires %qs", "-mpcrel", "-mcpu=future");
+
+ rs6000_isa_flags &= ~OPTION_MASK_PCREL;
+ }
+
/* Print the options after updating the defaults. */
if (TARGET_DEBUG_REG || TARGET_DEBUG_TARGET)
rs6000_print_isa_options (stderr, 0, "after defaults", rs6000_isa_flags);
= rs6000_debug_can_change_mode_class;
rs6000_preferred_reload_class_ptr
= rs6000_debug_preferred_reload_class;
- rs6000_legitimize_reload_address_ptr
- = rs6000_debug_legitimize_reload_address;
rs6000_mode_dependent_address_ptr
= rs6000_debug_mode_dependent_address;
}
&& rs6000_tune != PROCESSOR_POWER7
&& rs6000_tune != PROCESSOR_POWER8
&& rs6000_tune != PROCESSOR_POWER9
+ && rs6000_tune != PROCESSOR_FUTURE
&& rs6000_tune != PROCESSOR_PPCA2
&& rs6000_tune != PROCESSOR_CELL
&& rs6000_tune != PROCESSOR_PPC476);
|| rs6000_tune == PROCESSOR_POWER7
|| rs6000_tune == PROCESSOR_POWER8
|| rs6000_tune == PROCESSOR_POWER9
+ || rs6000_tune == PROCESSOR_FUTURE
|| rs6000_tune == PROCESSOR_PPCE500MC
|| rs6000_tune == PROCESSOR_PPCE500MC64
|| rs6000_tune == PROCESSOR_PPCE5500
break;
case PROCESSOR_POWER9:
+ case PROCESSOR_FUTURE:
rs6000_cost = &power9_cost;
break;
/* Default CPU string for rs6000*_file_start functions. */
static const char *rs6000_default_cpu;
+#ifdef USING_ELFOS_H
+static const char *rs6000_machine;
+
+static const char *
+rs6000_machine_from_flags (void)
+{
+ if ((rs6000_isa_flags & (ISA_FUTURE_MASKS_SERVER & ~ISA_3_0_MASKS_SERVER))
+ != 0)
+ return "future";
+ if ((rs6000_isa_flags & (ISA_3_0_MASKS_SERVER & ~ISA_2_7_MASKS_SERVER)) != 0)
+ return "power9";
+ if ((rs6000_isa_flags & (ISA_2_7_MASKS_SERVER & ~ISA_2_6_MASKS_SERVER)) != 0)
+ return "power8";
+ if ((rs6000_isa_flags & (ISA_2_6_MASKS_SERVER & ~ISA_2_5_MASKS_SERVER)) != 0)
+ return "power7";
+ if ((rs6000_isa_flags & (ISA_2_5_MASKS_SERVER & ~ISA_2_4_MASKS)) != 0)
+ return "power6";
+ if ((rs6000_isa_flags & (ISA_2_4_MASKS & ~ISA_2_1_MASKS)) != 0)
+ return "power5";
+ if ((rs6000_isa_flags & ISA_2_1_MASKS) != 0)
+ return "power4";
+ if ((rs6000_isa_flags & OPTION_MASK_POWERPC64) != 0)
+ return "ppc64";
+ return "ppc";
+}
+
+static void
+emit_asm_machine (void)
+{
+ fprintf (asm_out_file, "\t.machine %s\n", rs6000_machine);
+}
+#endif
+
/* Do anything needed at the start of the asm file. */
static void
}
#ifdef USING_ELFOS_H
+ rs6000_machine = rs6000_machine_from_flags ();
if (!(rs6000_default_cpu && rs6000_default_cpu[0])
&& !global_options_set.x_rs6000_cpu_index)
- {
- fputs ("\t.machine ", asm_out_file);
- if ((rs6000_isa_flags & OPTION_MASK_MODULO) != 0)
- fputs ("power9\n", asm_out_file);
- else if ((rs6000_isa_flags & OPTION_MASK_DIRECT_MOVE) != 0)
- fputs ("power8\n", asm_out_file);
- else if ((rs6000_isa_flags & OPTION_MASK_POPCNTD) != 0)
- fputs ("power7\n", asm_out_file);
- else if ((rs6000_isa_flags & OPTION_MASK_CMPB) != 0)
- fputs ("power6\n", asm_out_file);
- else if ((rs6000_isa_flags & OPTION_MASK_POPCNTB) != 0)
- fputs ("power5\n", asm_out_file);
- else if ((rs6000_isa_flags & OPTION_MASK_MFCRF) != 0)
- fputs ("power4\n", asm_out_file);
- else if ((rs6000_isa_flags & OPTION_MASK_POWERPC64) != 0)
- fputs ("ppc64\n", asm_out_file);
- else
- fputs ("ppc\n", asm_out_file);
- }
+ emit_asm_machine ();
#endif
if (DEFAULT_ABI == ABI_ELFv2)
return 0;
}
-/* Return the number of instructions it takes to form a constant in an
- integer register. */
+/* Helper for num_insns_constant. Calculate number of instructions to
+ load VALUE to a single gpr using combinations of addi, addis, ori,
+ oris and sldi instructions. */
-int
-num_insns_constant_wide (HOST_WIDE_INT value)
+static int
+num_insns_constant_gpr (HOST_WIDE_INT value)
{
/* signed constant loadable with addi */
if (((unsigned HOST_WIDE_INT) value + 0x8000) < 0x10000)
high >>= 1;
if (low == 0)
- return num_insns_constant_wide (high) + 1;
+ return num_insns_constant_gpr (high) + 1;
else if (high == 0)
- return num_insns_constant_wide (low) + 1;
+ return num_insns_constant_gpr (low) + 1;
else
- return (num_insns_constant_wide (high)
- + num_insns_constant_wide (low) + 1);
+ return (num_insns_constant_gpr (high)
+ + num_insns_constant_gpr (low) + 1);
}
else
return 2;
}
+/* Helper for num_insns_constant. Allow constants formed by the
+ num_insns_constant_gpr sequences, plus li -1, rldicl/rldicr/rlwinm,
+ and handle modes that require multiple gprs. */
+
+static int
+num_insns_constant_multi (HOST_WIDE_INT value, machine_mode mode)
+{
+ int nregs = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ int total = 0;
+ while (nregs-- > 0)
+ {
+ HOST_WIDE_INT low = sext_hwi (value, BITS_PER_WORD);
+ int insns = num_insns_constant_gpr (low);
+ if (insns > 2
+ /* We won't get more than 2 from num_insns_constant_gpr
+ except when TARGET_POWERPC64 and mode is DImode or
+ wider, so the register mode must be DImode. */
+ && rs6000_is_valid_and_mask (GEN_INT (low), DImode))
+ insns = 2;
+ total += insns;
+ value >>= BITS_PER_WORD;
+ }
+ return total;
+}
+
+/* Return the number of instructions it takes to form a constant in as
+ many gprs are needed for MODE. */
+
int
num_insns_constant (rtx op, machine_mode mode)
{
- HOST_WIDE_INT low, high;
+ HOST_WIDE_INT val;
switch (GET_CODE (op))
{
case CONST_INT:
- if ((INTVAL (op) >> 31) != 0 && (INTVAL (op) >> 31) != -1
- && rs6000_is_valid_and_mask (op, mode))
- return 2;
- else
- return num_insns_constant_wide (INTVAL (op));
+ val = INTVAL (op);
+ break;
case CONST_WIDE_INT:
{
- int i;
- int ins = CONST_WIDE_INT_NUNITS (op) - 1;
- for (i = 0; i < CONST_WIDE_INT_NUNITS (op); i++)
- ins += num_insns_constant_wide (CONST_WIDE_INT_ELT (op, i));
- return ins;
+ int insns = 0;
+ for (int i = 0; i < CONST_WIDE_INT_NUNITS (op); i++)
+ insns += num_insns_constant_multi (CONST_WIDE_INT_ELT (op, i),
+ DImode);
+ return insns;
}
- case CONST_DOUBLE:
+ case CONST_DOUBLE:
+ {
+ const struct real_value *rv = CONST_DOUBLE_REAL_VALUE (op);
+
if (mode == SFmode || mode == SDmode)
{
long l;
- if (DECIMAL_FLOAT_MODE_P (mode))
- REAL_VALUE_TO_TARGET_DECIMAL32
- (*CONST_DOUBLE_REAL_VALUE (op), l);
+ if (mode == SDmode)
+ REAL_VALUE_TO_TARGET_DECIMAL32 (*rv, l);
else
- REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (op), l);
- return num_insns_constant_wide ((HOST_WIDE_INT) l);
+ REAL_VALUE_TO_TARGET_SINGLE (*rv, l);
+ /* See the first define_split in rs6000.md handling a
+ const_double_operand. */
+ val = l;
+ mode = SImode;
}
-
- long l[2];
- if (DECIMAL_FLOAT_MODE_P (mode))
- REAL_VALUE_TO_TARGET_DECIMAL64 (*CONST_DOUBLE_REAL_VALUE (op), l);
- else
- REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (op), l);
- high = l[WORDS_BIG_ENDIAN == 0];
- low = l[WORDS_BIG_ENDIAN != 0];
-
- if (TARGET_32BIT)
- return (num_insns_constant_wide (low)
- + num_insns_constant_wide (high));
- else
+ else if (mode == DFmode || mode == DDmode)
{
- if ((high == 0 && low >= 0)
- || (high == -1 && low < 0))
- return num_insns_constant_wide (low);
+ long l[2];
- else if (rs6000_is_valid_and_mask (op, mode))
- return 2;
+ if (mode == DDmode)
+ REAL_VALUE_TO_TARGET_DECIMAL64 (*rv, l);
+ else
+ REAL_VALUE_TO_TARGET_DOUBLE (*rv, l);
- else if (low == 0)
- return num_insns_constant_wide (high) + 1;
+ /* See the second (32-bit) and third (64-bit) define_split
+ in rs6000.md handling a const_double_operand. */
+ val = (unsigned HOST_WIDE_INT) l[WORDS_BIG_ENDIAN ? 0 : 1] << 32;
+ val |= l[WORDS_BIG_ENDIAN ? 1 : 0] & 0xffffffffUL;
+ mode = DImode;
+ }
+ else if (mode == TFmode || mode == TDmode
+ || mode == KFmode || mode == IFmode)
+ {
+ long l[4];
+ int insns;
+ if (mode == TDmode)
+ REAL_VALUE_TO_TARGET_DECIMAL128 (*rv, l);
else
- return (num_insns_constant_wide (high)
- + num_insns_constant_wide (low) + 1);
+ REAL_VALUE_TO_TARGET_LONG_DOUBLE (*rv, l);
+
+ val = (unsigned HOST_WIDE_INT) l[WORDS_BIG_ENDIAN ? 0 : 3] << 32;
+ val |= l[WORDS_BIG_ENDIAN ? 1 : 2] & 0xffffffffUL;
+ insns = num_insns_constant_multi (val, DImode);
+ val = (unsigned HOST_WIDE_INT) l[WORDS_BIG_ENDIAN ? 2 : 1] << 32;
+ val |= l[WORDS_BIG_ENDIAN ? 3 : 0] & 0xffffffffUL;
+ insns += num_insns_constant_multi (val, DImode);
+ return insns;
}
+ else
+ gcc_unreachable ();
+ }
+ break;
default:
gcc_unreachable ();
}
+
+ return num_insns_constant_multi (val, mode);
}
/* Interpret element ELT of the CONST_VECTOR OP as an integer value.
else if (mode == V2DImode)
{
- if (GET_CODE (CONST_VECTOR_ELT (op, 0)) != CONST_INT
- || GET_CODE (CONST_VECTOR_ELT (op, 1)) != CONST_INT)
+ if (!CONST_INT_P (CONST_VECTOR_ELT (op, 0))
+ || !CONST_INT_P (CONST_VECTOR_ELT (op, 1)))
return false;
if (zero_constant (op, mode))
{
rtx element0 = XVECEXP (vals, 0, 0);
if (MEM_P (element0))
- element0 = rs6000_address_for_fpconvert (element0);
+ element0 = rs6000_force_indexed_or_indirect_mem (element0);
else
element0 = force_reg (SImode, element0);
if (TARGET_P9_VECTOR)
{
if (MEM_P (element0))
- element0 = rs6000_address_for_fpconvert (element0);
+ element0 = rs6000_force_indexed_or_indirect_mem (element0);
emit_insn (gen_vsx_splat_v4sf (target, element0));
}
default:
break;
case E_V1TImode:
- gcc_assert (INTVAL (elt) == 0 && inner_mode == TImode);
emit_move_insn (target, gen_lowpart (TImode, vec));
break;
case E_V2DFmode:
switch (mode)
{
+ case E_V1TImode:
+ emit_move_insn (target, gen_lowpart (TImode, vec));
+ return;
+
case E_V2DFmode:
emit_insn (gen_vsx_extract_v2df_var (target, vec, elt));
return;
}
}
- gcc_assert (CONST_INT_P (elt));
-
/* Allocate mode-sized buffer. */
mem = assign_stack_temp (mode, GET_MODE_SIZE (mode));
emit_move_insn (mem, vec);
+ if (CONST_INT_P (elt))
+ {
+ int modulo_elt = INTVAL (elt) % GET_MODE_NUNITS (mode);
- /* Add offset to field within buffer matching vector element. */
- mem = adjust_address_nv (mem, inner_mode,
- INTVAL (elt) * GET_MODE_SIZE (inner_mode));
-
- emit_move_insn (target, adjust_address_nv (mem, inner_mode, 0));
-}
-
-/* Helper function to return the register number of a RTX. */
-static inline int
-regno_or_subregno (rtx op)
-{
- if (REG_P (op))
- return REGNO (op);
- else if (SUBREG_P (op))
- return subreg_regno (op);
+ /* Add offset to field within buffer matching vector element. */
+ mem = adjust_address_nv (mem, inner_mode,
+ modulo_elt * GET_MODE_SIZE (inner_mode));
+ emit_move_insn (target, adjust_address_nv (mem, inner_mode, 0));
+ }
else
- gcc_unreachable ();
+ {
+ unsigned int ele_size = GET_MODE_SIZE (inner_mode);
+ rtx num_ele_m1 = GEN_INT (GET_MODE_NUNITS (mode) - 1);
+ rtx new_addr = gen_reg_rtx (Pmode);
+
+ elt = gen_rtx_AND (Pmode, elt, num_ele_m1);
+ if (ele_size > 1)
+ elt = gen_rtx_MULT (Pmode, elt, GEN_INT (ele_size));
+ new_addr = gen_rtx_PLUS (Pmode, XEXP (mem, 0), elt);
+ new_addr = change_address (mem, inner_mode, new_addr);
+ emit_move_insn (target, new_addr);
+ }
}
/* Adjust a memory address (MEM) of a vector type to point to a scalar field
{
rtx op1 = XEXP (new_addr, 1);
addr_mask_type addr_mask;
- int scalar_regno = regno_or_subregno (scalar_reg);
+ unsigned int scalar_regno = reg_or_subregno (scalar_reg);
- gcc_assert (scalar_regno < FIRST_PSEUDO_REGISTER);
+ gcc_assert (HARD_REGISTER_NUM_P (scalar_regno));
if (INT_REGNO_P (scalar_regno))
addr_mask = reg_addr[scalar_mode].addr_mask[RELOAD_REG_GPR];
rtx tmp_altivec)
{
machine_mode mode = GET_MODE (src);
- machine_mode scalar_mode = GET_MODE (dest);
+ machine_mode scalar_mode = GET_MODE_INNER (GET_MODE (src));
unsigned scalar_size = GET_MODE_SIZE (scalar_mode);
int byte_shift = exact_log2 (scalar_size);
systems. */
if (MEM_P (src))
{
+ int num_elements = GET_MODE_NUNITS (mode);
+ rtx num_ele_m1 = GEN_INT (num_elements - 1);
+
+ emit_insn (gen_anddi3 (element, element, num_ele_m1));
gcc_assert (REG_P (tmp_gpr));
emit_move_insn (dest, rs6000_adjust_vec_address (dest, src, element,
tmp_gpr, scalar_mode));
else if (REG_P (src) || SUBREG_P (src))
{
- int bit_shift = byte_shift + 3;
+ int num_elements = GET_MODE_NUNITS (mode);
+ int bits_in_element = mode_to_bits (GET_MODE_INNER (mode));
+ int bit_shift = 7 - exact_log2 (num_elements);
rtx element2;
- int dest_regno = regno_or_subregno (dest);
- int src_regno = regno_or_subregno (src);
- int element_regno = regno_or_subregno (element);
+ unsigned int dest_regno = reg_or_subregno (dest);
+ unsigned int src_regno = reg_or_subregno (src);
+ unsigned int element_regno = reg_or_subregno (element);
gcc_assert (REG_P (tmp_gpr));
{
if (!BYTES_BIG_ENDIAN)
{
- rtx num_ele_m1 = GEN_INT (GET_MODE_NUNITS (mode) - 1);
+ rtx num_ele_m1 = GEN_INT (num_elements - 1);
emit_insn (gen_anddi3 (tmp_gpr, element, num_ele_m1));
emit_insn (gen_subdi3 (tmp_gpr, num_ele_m1, tmp_gpr));
emit_insn (gen_vsx_vslo_v2di (tmp_altivec_di, src_v2di,
tmp_altivec));
emit_move_insn (tmp_gpr_di, tmp_altivec_di);
- emit_insn (gen_ashrdi3 (tmp_gpr_di, tmp_gpr_di,
- GEN_INT (64 - (8 * scalar_size))));
+ emit_insn (gen_lshrdi3 (tmp_gpr_di, tmp_gpr_di,
+ GEN_INT (64 - bits_in_element)));
return;
}
if (DEFAULT_ABI != ABI_V4)
return 0;
- if (GET_CODE (op) == SYMBOL_REF)
+ if (SYMBOL_REF_P (op))
sym_ref = op;
else if (GET_CODE (op) != CONST
|| GET_CODE (XEXP (op, 0)) != PLUS
- || GET_CODE (XEXP (XEXP (op, 0), 0)) != SYMBOL_REF
- || GET_CODE (XEXP (XEXP (op, 0), 1)) != CONST_INT)
+ || !SYMBOL_REF_P (XEXP (XEXP (op, 0), 0))
+ || !CONST_INT_P (XEXP (XEXP (op, 0), 1)))
return 0;
else
regno0 = REGNO (op0);
regno1 = REGNO (op1);
- if (regno0 >= FIRST_PSEUDO_REGISTER || regno1 >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno0) || !HARD_REGISTER_NUM_P (regno1))
return false;
if (INT_REGNO_P (regno0))
Accept direct, indexed, offset, lo_sum and tocref. Since this is
a constraint function we know the operand has satisfied a suitable
- memory predicate. Also accept some odd rtl generated by reload
- (see rs6000_legitimize_reload_address for various forms). It is
- important that reload rtl be accepted by appropriate constraints
- but not by the operand predicate.
+ memory predicate.
Offsetting a lo_sum should not be allowed, except where we know by
- alignment that a 32k boundary is not crossed, but see the ???
- comment in rs6000_legitimize_reload_address. Note that by
+ alignment that a 32k boundary is not crossed. Note that by
"offsetting" here we mean a further offset to access parts of the
MEM. It's fine to have a lo_sum where the inner address is offset
from a sym, since the same sym+offset will appear in the high part
{
int regnum;
- if (GET_CODE (op) == REG)
+ if (REG_P (op))
regnum = REGNO (op);
else if (GET_CODE (op) == PLUS
- && GET_CODE (XEXP (op, 0)) == REG
- && GET_CODE (XEXP (op, 1)) == CONST_INT)
+ && REG_P (XEXP (op, 0))
+ && CONST_INT_P (XEXP (op, 1)))
regnum = REGNO (XEXP (op, 0));
else
tree decl;
unsigned HOST_WIDE_INT dsize, dalign, lsb, mask;
- if (GET_CODE (op) != SYMBOL_REF)
+ if (!SYMBOL_REF_P (op))
return false;
/* ISA 3.0 vector d-form addressing is restricted, don't allow
rtx base, offset;
split_const (op, &base, &offset);
- return (GET_CODE (base) == SYMBOL_REF
+ return (SYMBOL_REF_P (base)
&& CONSTANT_POOL_ADDRESS_P (base)
&& ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (base), Pmode));
}
{
return (DEFAULT_ABI == ABI_V4
&& !flag_pic && !TARGET_TOC
- && (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST)
+ && (SYMBOL_REF_P (x) || GET_CODE (x) == CONST)
&& small_data_operand (x, mode));
}
return virtual_stack_registers_memory_p (x);
if (legitimate_constant_pool_address_p (x, mode, strict || lra_in_progress))
return true;
- if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ if (!CONST_INT_P (XEXP (x, 1)))
return false;
offset = INTVAL (XEXP (x, 1));
bool
legitimate_indirect_address_p (rtx x, int strict)
{
- return GET_CODE (x) == REG && INT_REG_OK_FOR_BASE_P (x, strict);
+ return REG_P (x) && INT_REG_OK_FOR_BASE_P (x, strict);
}
bool
macho_lo_sum_memory_operand (rtx x, machine_mode mode)
{
if (!TARGET_MACHO || !flag_pic
- || mode != SImode || GET_CODE (x) != MEM)
+ || mode != SImode || !MEM_P (x))
return false;
x = XEXP (x, 0);
if (GET_CODE (x) != LO_SUM)
return false;
- if (GET_CODE (XEXP (x, 0)) != REG)
+ if (!REG_P (XEXP (x, 0)))
return false;
if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), 0))
return false;
{
if (GET_CODE (x) != LO_SUM)
return false;
- if (GET_CODE (XEXP (x, 0)) != REG)
+ if (!REG_P (XEXP (x, 0)))
return false;
if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict))
return false;
recognizes some LO_SUM addresses as valid although this
function says opposite. In most cases, LRA through different
transformations can generate correct code for address reloads.
- It can not manage only some LO_SUM cases. So we need to add
- code analogous to one in rs6000_legitimize_reload_address for
- LOW_SUM here saying that some addresses are still valid. */
+ It cannot manage only some LO_SUM cases. So we need to add
+ code here saying that some addresses are still valid. */
large_toc_ok = (lra_in_progress && TARGET_CMODEL != CMODEL_SMALL
&& small_toc_ref (x, VOIDmode));
if (TARGET_TOC && ! large_toc_ok)
else
return force_reg (Pmode, x);
}
- if (GET_CODE (x) == SYMBOL_REF)
+ if (SYMBOL_REF_P (x))
{
enum tls_model model = SYMBOL_REF_TLS_MODEL (x);
if (model != 0)
}
if (GET_CODE (x) == PLUS
- && GET_CODE (XEXP (x, 0)) == REG
- && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && REG_P (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1))
&& ((unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1)) + 0x8000)
>= 0x10000 - extra))
{
return plus_constant (Pmode, sum, low_int);
}
else if (GET_CODE (x) == PLUS
- && GET_CODE (XEXP (x, 0)) == REG
- && GET_CODE (XEXP (x, 1)) != CONST_INT
+ && REG_P (XEXP (x, 0))
+ && !CONST_INT_P (XEXP (x, 1))
&& GET_MODE_NUNITS (mode) == 1
&& (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
|| (/* ??? Assume floating point reg based on mode? */
)
&& TARGET_32BIT
&& TARGET_NO_TOC
- && ! flag_pic
- && GET_CODE (x) != CONST_INT
- && GET_CODE (x) != CONST_WIDE_INT
- && GET_CODE (x) != CONST_DOUBLE
+ && !flag_pic
+ && !CONST_INT_P (x)
+ && !CONST_WIDE_INT_P (x)
+ && !CONST_DOUBLE_P (x)
&& CONSTANT_P (x)
&& GET_MODE_NUNITS (mode) == 1
&& (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
return gen_rtx_LO_SUM (Pmode, reg, x);
}
else if (TARGET_TOC
- && GET_CODE (x) == SYMBOL_REF
+ && SYMBOL_REF_P (x)
&& constant_pool_expr_p (x)
&& ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), Pmode))
return create_TOC_reference (x, NULL_RTX);
output_addr_const (file, x);
if (TARGET_ELF)
fputs ("@dtprel+0x8000", file);
- else if (TARGET_XCOFF && GET_CODE (x) == SYMBOL_REF)
+ else if (TARGET_XCOFF && SYMBOL_REF_P (x))
{
switch (SYMBOL_REF_TLS_MODEL (x))
{
static bool
rs6000_real_tls_symbol_ref_p (rtx x)
{
- return (GET_CODE (x) == SYMBOL_REF
+ return (SYMBOL_REF_P (x)
&& SYMBOL_REF_TLS_MODEL (x) >= TLS_MODEL_REAL);
}
{
rtx x, y, offset;
+ if (GET_CODE (orig_x) == UNSPEC && XINT (orig_x, 1) == UNSPEC_FUSION_GPR)
+ orig_x = XVECEXP (orig_x, 0, 0);
+
orig_x = delegitimize_mem_from_attrs (orig_x);
+
x = orig_x;
if (MEM_P (x))
x = XEXP (x, 0);
y = x;
- if (TARGET_CMODEL != CMODEL_SMALL
- && GET_CODE (y) == LO_SUM)
+ if (TARGET_CMODEL != CMODEL_SMALL && GET_CODE (y) == LO_SUM)
y = XEXP (y, 1);
offset = NULL_RTX;
y = XEXP (y, 0);
}
- if (GET_CODE (y) == UNSPEC
- && XINT (y, 1) == UNSPEC_TOCREL)
+ if (GET_CODE (y) == UNSPEC && XINT (y, 1) == UNSPEC_TOCREL)
{
y = XVECEXP (y, 0, 0);
/* Do not associate thread-local symbols with the original
constant pool symbol. */
if (TARGET_XCOFF
- && GET_CODE (y) == SYMBOL_REF
+ && SYMBOL_REF_P (y)
&& CONSTANT_POOL_ADDRESS_P (y)
&& rs6000_real_tls_symbol_ref_p (get_pool_constant (y)))
return orig_x;
&& GET_CODE (XEXP (orig_x, 1)) == CONST)
{
y = XEXP (XEXP (orig_x, 1), 0);
- if (GET_CODE (y) == UNSPEC
- && XINT (y, 1) == UNSPEC_MACHOPIC_OFFSET)
+ if (GET_CODE (y) == UNSPEC && XINT (y, 1) == UNSPEC_MACHOPIC_OFFSET)
return XVECEXP (y, 0, 0);
}
{
if (GET_CODE (x) == UNSPEC)
return true;
- if (GET_CODE (x) == SYMBOL_REF
+ if (SYMBOL_REF_P (x)
&& CONSTANT_POOL_ADDRESS_P (x))
{
rtx c = get_pool_constant (x);
return false;
}
-
/* Implement the TARGET_LEGITIMATE_COMBINED_INSN hook. */
static bool
return dest;
}
+/* Output arg setup instructions for a !TARGET_TLS_MARKERS
+ __tls_get_addr call. */
+
+void
+rs6000_output_tlsargs (rtx *operands)
+{
+ /* Set up operands for output_asm_insn, without modifying OPERANDS. */
+ rtx op[3];
+
+ /* The set dest of the call, ie. r3, which is also the first arg reg. */
+ op[0] = operands[0];
+ /* The TLS symbol from global_tlsarg stashed as CALL operand 2. */
+ op[1] = XVECEXP (operands[2], 0, 0);
+ if (XINT (operands[2], 1) == UNSPEC_TLSGD)
+ {
+ /* The GOT register. */
+ op[2] = XVECEXP (operands[2], 0, 1);
+ if (TARGET_CMODEL != CMODEL_SMALL)
+ output_asm_insn ("addis %0,%2,%1@got@tlsgd@ha\n\t"
+ "addi %0,%0,%1@got@tlsgd@l", op);
+ else
+ output_asm_insn ("addi %0,%2,%1@got@tlsgd", op);
+ }
+ else if (XINT (operands[2], 1) == UNSPEC_TLSLD)
+ {
+ if (TARGET_CMODEL != CMODEL_SMALL)
+ output_asm_insn ("addis %0,%1,%&@got@tlsld@ha\n\t"
+ "addi %0,%0,%&@got@tlsld@l", op);
+ else
+ output_asm_insn ("addi %0,%1,%&@got@tlsld", op);
+ }
+ else
+ gcc_unreachable ();
+}
+
+/* Passes the tls arg value for global dynamic and local dynamic
+ emit_library_call_value in rs6000_legitimize_tls_address to
+ rs6000_call_aix and rs6000_call_sysv. This is used to emit the
+ marker relocs put on __tls_get_addr calls. */
+static rtx global_tlsarg;
+
/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute
this (thread-local) address. */
}
else
{
- rtx r3, got, tga, tmp1, tmp2, call_insn;
+ rtx got, tga, tmp1, tmp2;
/* We currently use relocations like @got@tlsgd for tls, which
means the linker will handle allocation of tls entries, placing
if (model == TLS_MODEL_GLOBAL_DYNAMIC)
{
+ rtx arg = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, addr, got),
+ UNSPEC_TLSGD);
tga = rs6000_tls_get_addr ();
- emit_library_call_value (tga, dest, LCT_CONST, Pmode,
- const0_rtx, Pmode);
-
- r3 = gen_rtx_REG (Pmode, 3);
- if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
+ global_tlsarg = arg;
+ if (TARGET_TLS_MARKERS)
{
- if (TARGET_64BIT)
- insn = gen_tls_gd_aix64 (r3, got, addr, tga, const0_rtx);
- else
- insn = gen_tls_gd_aix32 (r3, got, addr, tga, const0_rtx);
+ rtx argreg = gen_rtx_REG (Pmode, 3);
+ emit_insn (gen_rtx_SET (argreg, arg));
+ emit_library_call_value (tga, dest, LCT_CONST, Pmode,
+ argreg, Pmode);
}
- else if (DEFAULT_ABI == ABI_V4)
- insn = gen_tls_gd_sysvsi (r3, got, addr, tga, const0_rtx);
else
- gcc_unreachable ();
- call_insn = last_call_insn ();
- PATTERN (call_insn) = insn;
- if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn),
- pic_offset_table_rtx);
+ emit_library_call_value (tga, dest, LCT_CONST, Pmode);
+ global_tlsarg = NULL_RTX;
+
+ /* Make a note so that the result of this call can be CSEd. */
+ rtvec vec = gen_rtvec (1, copy_rtx (arg));
+ rtx uns = gen_rtx_UNSPEC (Pmode, vec, UNSPEC_TLS_GET_ADDR);
+ set_unique_reg_note (get_last_insn (), REG_EQUAL, uns);
}
else if (model == TLS_MODEL_LOCAL_DYNAMIC)
{
+ rtx arg = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got), UNSPEC_TLSLD);
tga = rs6000_tls_get_addr ();
tmp1 = gen_reg_rtx (Pmode);
- emit_library_call_value (tga, tmp1, LCT_CONST, Pmode,
- const0_rtx, Pmode);
-
- r3 = gen_rtx_REG (Pmode, 3);
- if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
+ global_tlsarg = arg;
+ if (TARGET_TLS_MARKERS)
{
- if (TARGET_64BIT)
- insn = gen_tls_ld_aix64 (r3, got, tga, const0_rtx);
- else
- insn = gen_tls_ld_aix32 (r3, got, tga, const0_rtx);
+ rtx argreg = gen_rtx_REG (Pmode, 3);
+ emit_insn (gen_rtx_SET (argreg, arg));
+ emit_library_call_value (tga, tmp1, LCT_CONST, Pmode,
+ argreg, Pmode);
}
- else if (DEFAULT_ABI == ABI_V4)
- insn = gen_tls_ld_sysvsi (r3, got, tga, const0_rtx);
else
- gcc_unreachable ();
- call_insn = last_call_insn ();
- PATTERN (call_insn) = insn;
- if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn),
- pic_offset_table_rtx);
+ emit_library_call_value (tga, tmp1, LCT_CONST, Pmode);
+ global_tlsarg = NULL_RTX;
+
+ /* Make a note so that the result of this call can be CSEd. */
+ rtvec vec = gen_rtvec (1, copy_rtx (arg));
+ rtx uns = gen_rtx_UNSPEC (Pmode, vec, UNSPEC_TLS_GET_ADDR);
+ set_unique_reg_note (get_last_insn (), REG_EQUAL, uns);
if (rs6000_tls_size == 16)
{
/* A TLS symbol in the TOC cannot contain a sum. */
if (GET_CODE (x) == CONST
&& GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
+ && SYMBOL_REF_P (XEXP (XEXP (x, 0), 0))
&& SYMBOL_REF_TLS_MODEL (XEXP (XEXP (x, 0), 0)) != 0)
return true;
&& GET_MODE_SIZE (mode) <= POWERPC64_TOC_POINTER_ALIGNMENT));
}
-/* Our implementation of LEGITIMIZE_RELOAD_ADDRESS. Returns a value to
- replace the input X, or the original X if no replacement is called for.
- The output parameter *WIN is 1 if the calling macro should goto WIN,
- 0 if it should not.
-
- For RS/6000, we wish to handle large displacements off a base
- register by splitting the addend across an addiu/addis and the mem insn.
- This cuts number of extra insns needed from 3 to 1.
-
- On Darwin, we use this to generate code for floating point constants.
- A movsf_low is generated so we wind up with 2 instructions rather than 3.
- The Darwin code is inside #if TARGET_MACHO because only then are the
- machopic_* functions defined. */
-static rtx
-rs6000_legitimize_reload_address (rtx x, machine_mode mode,
- int opnum, int type,
- int ind_levels ATTRIBUTE_UNUSED, int *win)
-{
- bool reg_offset_p = reg_offset_addressing_ok_p (mode);
- bool quad_offset_p = mode_supports_dq_form (mode);
-
- /* Nasty hack for vsx_splat_v2df/v2di load from mem, which takes a
- DFmode/DImode MEM. Ditto for ISA 3.0 vsx_splat_v4sf/v4si. */
- if (reg_offset_p
- && opnum == 1
- && ((mode == DFmode && recog_data.operand_mode[0] == V2DFmode)
- || (mode == DImode && recog_data.operand_mode[0] == V2DImode)
- || (mode == SFmode && recog_data.operand_mode[0] == V4SFmode
- && TARGET_P9_VECTOR)
- || (mode == SImode && recog_data.operand_mode[0] == V4SImode
- && TARGET_P9_VECTOR)))
- reg_offset_p = false;
-
- /* We must recognize output that we have already generated ourselves. */
- if (GET_CODE (x) == PLUS
- && GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
- && GET_CODE (XEXP (x, 1)) == CONST_INT)
- {
- if (TARGET_DEBUG_ADDR)
- {
- fprintf (stderr, "\nlegitimize_reload_address push_reload #1:\n");
- debug_rtx (x);
- }
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- *win = 1;
- return x;
- }
-
- /* Likewise for (lo_sum (high ...) ...) output we have generated. */
- if (GET_CODE (x) == LO_SUM
- && GET_CODE (XEXP (x, 0)) == HIGH)
- {
- if (TARGET_DEBUG_ADDR)
- {
- fprintf (stderr, "\nlegitimize_reload_address push_reload #2:\n");
- debug_rtx (x);
- }
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- *win = 1;
- return x;
- }
-
-#if TARGET_MACHO
- if (DEFAULT_ABI == ABI_DARWIN && flag_pic
- && GET_CODE (x) == LO_SUM
- && GET_CODE (XEXP (x, 0)) == PLUS
- && XEXP (XEXP (x, 0), 0) == pic_offset_table_rtx
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == HIGH
- && XEXP (XEXP (XEXP (x, 0), 1), 0) == XEXP (x, 1)
- && machopic_operand_p (XEXP (x, 1)))
- {
- /* Result of previous invocation of this function on Darwin
- floating point constant. */
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- *win = 1;
- return x;
- }
-#endif
-
- if (TARGET_CMODEL != CMODEL_SMALL
- && reg_offset_p
- && !quad_offset_p
- && small_toc_ref (x, VOIDmode))
- {
- rtx hi = gen_rtx_HIGH (Pmode, copy_rtx (x));
- x = gen_rtx_LO_SUM (Pmode, hi, x);
- if (TARGET_DEBUG_ADDR)
- {
- fprintf (stderr, "\nlegitimize_reload_address push_reload #3:\n");
- debug_rtx (x);
- }
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- *win = 1;
- return x;
- }
-
- if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x, 0))
- && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER
- && INT_REG_OK_FOR_BASE_P (XEXP (x, 0), 1)
- && CONST_INT_P (XEXP (x, 1))
- && reg_offset_p
- && (quad_offset_p || !VECTOR_MODE_P (mode) || VECTOR_MEM_NONE_P (mode)))
- {
- HOST_WIDE_INT val = INTVAL (XEXP (x, 1));
- HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000;
- HOST_WIDE_INT high
- = (((val - low) & 0xffffffff) ^ 0x80000000) - 0x80000000;
-
- /* Check for 32-bit overflow or quad addresses with one of the
- four least significant bits set. */
- if (high + low != val
- || (quad_offset_p && (low & 0xf)))
- {
- *win = 0;
- return x;
- }
-
- /* Reload the high part into a base reg; leave the low part
- in the mem directly. */
-
- x = gen_rtx_PLUS (GET_MODE (x),
- gen_rtx_PLUS (GET_MODE (x), XEXP (x, 0),
- GEN_INT (high)),
- GEN_INT (low));
-
- if (TARGET_DEBUG_ADDR)
- {
- fprintf (stderr, "\nlegitimize_reload_address push_reload #4:\n");
- debug_rtx (x);
- }
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- *win = 1;
- return x;
- }
-
- if (GET_CODE (x) == SYMBOL_REF
- && reg_offset_p
- && !quad_offset_p
- && (!VECTOR_MODE_P (mode) || VECTOR_MEM_NONE_P (mode))
-#if TARGET_MACHO
- && DEFAULT_ABI == ABI_DARWIN
- && (flag_pic || MACHO_DYNAMIC_NO_PIC_P)
- && machopic_symbol_defined_p (x)
-#else
- && DEFAULT_ABI == ABI_V4
- && !flag_pic
-#endif
- /* Don't do this for TFmode or TDmode, since the result isn't offsettable.
- The same goes for DImode without 64-bit gprs and DFmode and DDmode
- without fprs.
- ??? Assume floating point reg based on mode? This assumption is
- violated by eg. powerpc-linux -m32 compile of gcc.dg/pr28796-2.c
- where reload ends up doing a DFmode load of a constant from
- mem using two gprs. Unfortunately, at this point reload
- hasn't yet selected regs so poking around in reload data
- won't help and even if we could figure out the regs reliably,
- we'd still want to allow this transformation when the mem is
- naturally aligned. Since we say the address is good here, we
- can't disable offsets from LO_SUMs in mem_operand_gpr.
- FIXME: Allow offset from lo_sum for other modes too, when
- mem is sufficiently aligned.
-
- Also disallow this if the type can go in VMX/Altivec registers, since
- those registers do not have d-form (reg+offset) address modes. */
- && !reg_addr[mode].scalar_in_vmx_p
- && mode != TFmode
- && mode != TDmode
- && mode != IFmode
- && mode != KFmode
- && (mode != TImode || !TARGET_VSX)
- && mode != PTImode
- && (mode != DImode || TARGET_POWERPC64)
- && ((mode != DFmode && mode != DDmode) || TARGET_POWERPC64
- || TARGET_HARD_FLOAT))
- {
-#if TARGET_MACHO
- if (flag_pic)
- {
- rtx offset = machopic_gen_offset (x);
- x = gen_rtx_LO_SUM (GET_MODE (x),
- gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
- gen_rtx_HIGH (Pmode, offset)), offset);
- }
- else
-#endif
- x = gen_rtx_LO_SUM (GET_MODE (x),
- gen_rtx_HIGH (Pmode, x), x);
-
- if (TARGET_DEBUG_ADDR)
- {
- fprintf (stderr, "\nlegitimize_reload_address push_reload #5:\n");
- debug_rtx (x);
- }
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- *win = 1;
- return x;
- }
-
- /* Reload an offset address wrapped by an AND that represents the
- masking of the lower bits. Strip the outer AND and let reload
- convert the offset address into an indirect address. For VSX,
- force reload to create the address with an AND in a separate
- register, because we can't guarantee an altivec register will
- be used. */
- if (VECTOR_MEM_ALTIVEC_P (mode)
- && GET_CODE (x) == AND
- && GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
- && GET_CODE (XEXP (x, 1)) == CONST_INT
- && INTVAL (XEXP (x, 1)) == -16)
- {
- x = XEXP (x, 0);
- *win = 1;
- return x;
- }
-
- if (TARGET_TOC
- && reg_offset_p
- && !quad_offset_p
- && GET_CODE (x) == SYMBOL_REF
- && use_toc_relative_ref (x, mode))
- {
- x = create_TOC_reference (x, NULL_RTX);
- if (TARGET_CMODEL != CMODEL_SMALL)
- {
- if (TARGET_DEBUG_ADDR)
- {
- fprintf (stderr, "\nlegitimize_reload_address push_reload #6:\n");
- debug_rtx (x);
- }
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- }
- *win = 1;
- return x;
- }
- *win = 0;
- return x;
-}
-
-/* Debug version of rs6000_legitimize_reload_address. */
-static rtx
-rs6000_debug_legitimize_reload_address (rtx x, machine_mode mode,
- int opnum, int type,
- int ind_levels, int *win)
-{
- rtx ret = rs6000_legitimize_reload_address (x, mode, opnum, type,
- ind_levels, win);
- fprintf (stderr,
- "\nrs6000_legitimize_reload_address: mode = %s, opnum = %d, "
- "type = %d, ind_levels = %d, win = %d, original addr:\n",
- GET_MODE_NAME (mode), opnum, type, ind_levels, *win);
- debug_rtx (x);
-
- if (x == ret)
- fprintf (stderr, "Same address returned\n");
- else if (!ret)
- fprintf (stderr, "NULL returned\n");
- else
- {
- fprintf (stderr, "New address:\n");
- debug_rtx (ret);
- }
-
- return ret;
-}
-
/* TARGET_LEGITIMATE_ADDRESS_P recognizes an RTL expression
that is a valid memory address for an instruction.
The MODE argument is the machine mode for the MEM expression
/* If this is an unaligned stvx/ldvx type address, discard the outer AND. */
if (VECTOR_MEM_ALTIVEC_P (mode)
&& GET_CODE (x) == AND
- && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && CONST_INT_P (XEXP (x, 1))
&& INTVAL (XEXP (x, 1)) == -16)
x = XEXP (x, 0);
if (! reg_ok_strict
&& reg_offset_p
&& GET_CODE (x) == PLUS
- && GET_CODE (XEXP (x, 0)) == REG
+ && REG_P (XEXP (x, 0))
&& (XEXP (x, 0) == virtual_stack_vars_rtx
|| XEXP (x, 0) == arg_pointer_rtx)
- && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ && CONST_INT_P (XEXP (x, 1)))
return 1;
if (rs6000_legitimate_offset_address_p (mode, x, reg_ok_strict, false))
return 1;
been rejected as illegitimate. */
if (XEXP (addr, 0) != virtual_stack_vars_rtx
&& XEXP (addr, 0) != arg_pointer_rtx
- && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ && CONST_INT_P (XEXP (addr, 1)))
{
unsigned HOST_WIDE_INT val = INTVAL (XEXP (addr, 1));
return val + 0x8000 >= 0x10000 - (TARGET_POWERPC64 ? 8 : 12);
{
case PROCESSOR_POWER8:
case PROCESSOR_POWER9:
+ case PROCESSOR_FUTURE:
if (DECIMAL_FLOAT_MODE_P (mode))
return 1;
if (VECTOR_MODE_P (mode))
if (TARGET_DEBUG_TARGET)
fprintf (stderr, "rs6000_conditional_register_usage called\n");
- /* Set MQ register fixed (already call_used) so that it will not be
- allocated. */
- fixed_regs[64] = 1;
-
/* 64-bit AIX and Linux reserve GPR13 for thread-private data. */
if (TARGET_64BIT)
fixed_regs[13] = call_used_regs[13]
static void
rs6000_eliminate_indexed_memrefs (rtx operands[2])
{
- if (GET_CODE (operands[0]) == MEM
- && GET_CODE (XEXP (operands[0], 0)) != REG
+ if (MEM_P (operands[0])
+ && !REG_P (XEXP (operands[0], 0))
&& ! legitimate_constant_pool_address_p (XEXP (operands[0], 0),
GET_MODE (operands[0]), false))
operands[0]
= replace_equiv_address (operands[0],
copy_addr_to_reg (XEXP (operands[0], 0)));
- if (GET_CODE (operands[1]) == MEM
- && GET_CODE (XEXP (operands[1], 0)) != REG
+ if (MEM_P (operands[1])
+ && !REG_P (XEXP (operands[1], 0))
&& ! legitimate_constant_pool_address_p (XEXP (operands[1], 0),
GET_MODE (operands[1]), false))
operands[1]
if (MEM_P (source))
{
- gcc_assert (REG_P (dest) || GET_CODE (dest) == SUBREG);
+ gcc_assert (REG_P (dest) || SUBREG_P (dest));
rs6000_emit_le_vsx_load (dest, source, mode);
}
else
static bool
rs6000_emit_move_si_sf_subreg (rtx dest, rtx source, machine_mode mode)
{
- if (TARGET_DIRECT_MOVE_64BIT && !lra_in_progress && !reload_completed
+ if (TARGET_DIRECT_MOVE_64BIT && !reload_completed
&& (!SUBREG_P (dest) || !sf_subreg_operand (dest, mode))
&& SUBREG_P (source) && sf_subreg_operand (source, mode))
{
debug_rtx (source);
}
- /* Sanity checks. Check that we get CONST_DOUBLE only when we should. */
+ /* Check that we get CONST_WIDE_INT only when we should. */
if (CONST_WIDE_INT_P (operands[1])
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
- {
- /* This should be fixed with the introduction of CONST_WIDE_INT. */
- gcc_unreachable ();
- }
+ gcc_unreachable ();
#ifdef HAVE_AS_GNU_ATTRIBUTE
/* If we use a long double type, set the flags in .gnu_attribute that say
/* Check if GCC is setting up a block move that will end up using FP
registers as temporaries. We must make sure this is acceptable. */
- if (GET_CODE (operands[0]) == MEM
- && GET_CODE (operands[1]) == MEM
+ if (MEM_P (operands[0])
+ && MEM_P (operands[1])
&& mode == DImode
&& (rs6000_slow_unaligned_access (DImode, MEM_ALIGN (operands[0]))
|| rs6000_slow_unaligned_access (DImode, MEM_ALIGN (operands[1])))
return;
}
- if (can_create_pseudo_p () && GET_CODE (operands[0]) == MEM
+ if (can_create_pseudo_p () && MEM_P (operands[0])
&& !gpc_reg_operand (operands[1], mode))
operands[1] = force_reg (mode, operands[1]);
tmp = XEXP (XEXP (tmp, 0), 0);
}
- gcc_assert (GET_CODE (tmp) == SYMBOL_REF);
+ gcc_assert (SYMBOL_REF_P (tmp));
model = SYMBOL_REF_TLS_MODEL (tmp);
gcc_assert (model != 0);
/* 128-bit constant floating-point values on Darwin should really be loaded
as two parts. However, this premature splitting is a problem when DFmode
values can go into Altivec registers. */
- if (FLOAT128_IBM_P (mode) && !reg_addr[DFmode].scalar_in_vmx_p
- && GET_CODE (operands[1]) == CONST_DOUBLE)
+ if (TARGET_MACHO && CONST_DOUBLE_P (operands[1]) && FLOAT128_IBM_P (mode)
+ && !reg_addr[DFmode].scalar_in_vmx_p)
{
rs6000_emit_move (simplify_gen_subreg (DFmode, operands[0], mode, 0),
simplify_gen_subreg (DFmode, operands[1], mode, 0),
p1:SD) if p1 is not of floating point class and p0 is spilled as
we can have no analogous movsd_store for this. */
if (lra_in_progress && mode == DDmode
- && REG_P (operands[0]) && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER
+ && REG_P (operands[0]) && !HARD_REGISTER_P (operands[0])
&& reg_preferred_class (REGNO (operands[0])) == NO_REGS
- && GET_CODE (operands[1]) == SUBREG && REG_P (SUBREG_REG (operands[1]))
+ && SUBREG_P (operands[1]) && REG_P (SUBREG_REG (operands[1]))
&& GET_MODE (SUBREG_REG (operands[1])) == SDmode)
{
enum reg_class cl;
int regno = REGNO (SUBREG_REG (operands[1]));
- if (regno >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno))
{
cl = reg_preferred_class (regno);
regno = reg_renumber[regno];
}
if (lra_in_progress
&& mode == SDmode
- && REG_P (operands[0]) && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER
+ && REG_P (operands[0]) && !HARD_REGISTER_P (operands[0])
&& reg_preferred_class (REGNO (operands[0])) == NO_REGS
&& (REG_P (operands[1])
- || (GET_CODE (operands[1]) == SUBREG
- && REG_P (SUBREG_REG (operands[1])))))
+ || (SUBREG_P (operands[1]) && REG_P (SUBREG_REG (operands[1])))))
{
- int regno = REGNO (GET_CODE (operands[1]) == SUBREG
- ? SUBREG_REG (operands[1]) : operands[1]);
+ int regno = reg_or_subregno (operands[1]);
enum reg_class cl;
- if (regno >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno))
{
cl = reg_preferred_class (regno);
gcc_assert (cl != NO_REGS);
p:DD)) if p0 is not of floating point class and p1 is spilled as
we can have no analogous movsd_load for this. */
if (lra_in_progress && mode == DDmode
- && GET_CODE (operands[0]) == SUBREG && REG_P (SUBREG_REG (operands[0]))
+ && SUBREG_P (operands[0]) && REG_P (SUBREG_REG (operands[0]))
&& GET_MODE (SUBREG_REG (operands[0])) == SDmode
- && REG_P (operands[1]) && REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER
+ && REG_P (operands[1]) && !HARD_REGISTER_P (operands[1])
&& reg_preferred_class (REGNO (operands[1])) == NO_REGS)
{
enum reg_class cl;
int regno = REGNO (SUBREG_REG (operands[0]));
- if (regno >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno))
{
cl = reg_preferred_class (regno);
regno = reg_renumber[regno];
if (lra_in_progress
&& mode == SDmode
&& (REG_P (operands[0])
- || (GET_CODE (operands[0]) == SUBREG
- && REG_P (SUBREG_REG (operands[0]))))
- && REG_P (operands[1]) && REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER
+ || (SUBREG_P (operands[0]) && REG_P (SUBREG_REG (operands[0]))))
+ && REG_P (operands[1]) && !HARD_REGISTER_P (operands[1])
&& reg_preferred_class (REGNO (operands[1])) == NO_REGS)
{
- int regno = REGNO (GET_CODE (operands[0]) == SUBREG
- ? SUBREG_REG (operands[0]) : operands[0]);
+ int regno = reg_or_subregno (operands[0]);
enum reg_class cl;
- if (regno >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno))
{
cl = reg_preferred_class (regno);
gcc_assert (cl != NO_REGS);
case E_HImode:
case E_QImode:
if (CONSTANT_P (operands[1])
- && GET_CODE (operands[1]) != CONST_INT)
+ && !CONST_INT_P (operands[1]))
operands[1] = force_const_mem (mode, operands[1]);
break;
if (TARGET_ELF
&& mode == Pmode
&& DEFAULT_ABI == ABI_V4
- && (GET_CODE (operands[1]) == SYMBOL_REF
+ && (SYMBOL_REF_P (operands[1])
|| GET_CODE (operands[1]) == CONST)
&& small_data_operand (operands[1], mode))
{
&& mode == Pmode
&& CONSTANT_P (operands[1])
&& GET_CODE (operands[1]) != HIGH
- && GET_CODE (operands[1]) != CONST_INT)
+ && !CONST_INT_P (operands[1]))
{
rtx target = (!can_create_pseudo_p ()
? operands[0]
/* If this is a function address on -mcall-aixdesc,
convert it to the address of the descriptor. */
if (DEFAULT_ABI == ABI_AIX
- && GET_CODE (operands[1]) == SYMBOL_REF
+ && SYMBOL_REF_P (operands[1])
&& XSTR (operands[1], 0)[0] == '.')
{
const char *name = XSTR (operands[1], 0);
and we have put it in the TOC, we just need to make a TOC-relative
reference to it. */
if (TARGET_TOC
- && GET_CODE (operands[1]) == SYMBOL_REF
+ && SYMBOL_REF_P (operands[1])
&& use_toc_relative_ref (operands[1], mode))
operands[1] = create_TOC_reference (operands[1], operands[0]);
else if (mode == Pmode
&& CONSTANT_P (operands[1])
&& GET_CODE (operands[1]) != HIGH
- && ((GET_CODE (operands[1]) != CONST_INT
- && ! easy_fp_constant (operands[1], mode))
- || (GET_CODE (operands[1]) == CONST_INT
- && (num_insns_constant (operands[1], mode)
- > (TARGET_CMODEL != CMODEL_SMALL ? 3 : 2)))
- || (GET_CODE (operands[0]) == REG
- && FP_REGNO_P (REGNO (operands[0]))))
+ && ((REG_P (operands[0])
+ && FP_REGNO_P (REGNO (operands[0])))
+ || !CONST_INT_P (operands[1])
+ || (num_insns_constant (operands[1], mode)
+ > (TARGET_CMODEL != CMODEL_SMALL ? 3 : 2)))
&& !toc_relative_expr_p (operands[1], false, NULL, NULL)
&& (TARGET_CMODEL == CMODEL_SMALL
|| can_create_pseudo_p ()
&& GET_CODE (XEXP (operands[1], 0)) == PLUS
&& add_operand (XEXP (XEXP (operands[1], 0), 1), mode)
&& (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == LABEL_REF
- || GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == SYMBOL_REF)
+ || SYMBOL_REF_P (XEXP (XEXP (operands[1], 0), 0)))
&& ! side_effects_p (operands[0]))
{
rtx sym =
operands[1] = force_const_mem (mode, operands[1]);
if (TARGET_TOC
- && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
+ && SYMBOL_REF_P (XEXP (operands[1], 0))
&& use_toc_relative_ref (XEXP (operands[1], 0), mode))
{
rtx tocref = create_TOC_reference (XEXP (operands[1], 0),
/* Above, we may have called force_const_mem which may have returned
an invalid address. If we can, fix this up; otherwise, reload will
have to deal with it. */
- if (GET_CODE (operands[1]) == MEM)
+ if (MEM_P (operands[1]))
operands[1] = validize_mem (operands[1]);
emit_insn (gen_rtx_SET (operands[0], operands[1]));
init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
rtx libname ATTRIBUTE_UNUSED, int incoming,
int libcall, int n_named_args,
- tree fndecl ATTRIBUTE_UNUSED,
+ tree fndecl,
machine_mode return_mode ATTRIBUTE_UNUSED)
{
static CUMULATIVE_ARGS zero_cumulative;
&& lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))
&& !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype))))
cum->call_cookie |= CALL_LONG;
+ else if (DEFAULT_ABI != ABI_DARWIN)
+ {
+ bool is_local = (fndecl
+ && !DECL_EXTERNAL (fndecl)
+ && !DECL_WEAK (fndecl)
+ && (*targetm.binds_local_p) (fndecl));
+ if (is_local)
+ ;
+ else if (flag_plt)
+ {
+ if (fntype
+ && lookup_attribute ("noplt", TYPE_ATTRIBUTES (fntype)))
+ cum->call_cookie |= CALL_LONG;
+ }
+ else
+ {
+ if (!(fntype
+ && lookup_attribute ("plt", TYPE_ATTRIBUTES (fntype))))
+ cum->call_cookie |= CALL_LONG;
+ }
+ }
if (TARGET_DEBUG_ARG)
{
if (elt_mode == TDmode && (cum->fregno % 2) == 1)
cum->fregno++;
- if (USE_FP_FOR_ARG_P (cum, elt_mode))
+ if (USE_FP_FOR_ARG_P (cum, elt_mode)
+ && !(TARGET_AIX && !TARGET_ELF
+ && type != NULL && AGGREGATE_TYPE_P (type)))
{
rtx rvec[GP_ARG_NUM_REG + AGGR_ARG_NUM_REG + 1];
rtx r, off;
{
warned = true;
inform (input_location,
- "the ABI of passing homogeneous float aggregates"
+ "the ABI of passing homogeneous %<float%> aggregates"
" has changed in GCC 5");
}
}
align_words = rs6000_parm_start (mode, type, cum->words);
- if (USE_FP_FOR_ARG_P (cum, elt_mode))
+ if (USE_FP_FOR_ARG_P (cum, elt_mode)
+ && !(TARGET_AIX && !TARGET_ELF
+ && type != NULL && AGGREGATE_TYPE_P (type)))
{
unsigned long n_fpreg = (GET_MODE_SIZE (elt_mode) + 7) >> 3;
{
rtx reg_save_area
= assign_stack_local (BLKmode, gpr_size + fpr_size, 64);
- gcc_assert (GET_CODE (reg_save_area) == MEM);
+ gcc_assert (MEM_P (reg_save_area));
reg_save_area = XEXP (reg_save_area, 0);
if (GET_CODE (reg_save_area) == PLUS)
{
gcc_assert (XEXP (reg_save_area, 0)
== virtual_stack_vars_rtx);
- gcc_assert (GET_CODE (XEXP (reg_save_area, 1)) == CONST_INT);
+ gcc_assert (CONST_INT_P (XEXP (reg_save_area, 1)));
offset += INTVAL (XEXP (reg_save_area, 1));
}
else
return 0;
if (icode == CODE_FOR_rs6000_mffsl
- && rs6000_isa_flags_explicit & OPTION_MASK_SOFT_FLOAT)
+ && rs6000_isa_flags & OPTION_MASK_SOFT_FLOAT)
{
- error ("__builtin_mffsl() not supported with -msoft-float");
+ error ("%<__builtin_mffsl%> not supported with %<-msoft-float%>");
return const0_rtx;
}
if (arg0 == error_mark_node || arg1 == error_mark_node)
return const0_rtx;
- if (GET_CODE (op0) != CONST_INT
+ if (!CONST_INT_P (op0)
|| INTVAL (op0) > 255
|| INTVAL (op0) < 0)
{
/* Builtin not supported on this processor. */
return 0;
- if (rs6000_isa_flags_explicit & OPTION_MASK_SOFT_FLOAT)
+ if (rs6000_isa_flags & OPTION_MASK_SOFT_FLOAT)
{
- error ("__builtin_mtfsb0 and __builtin_mtfsb1 not supported with -msoft-float");
+ error ("%<__builtin_mtfsb0%> and %<__builtin_mtfsb1%> not supported with "
+ "%<-msoft-float%>");
return const0_rtx;
}
/* Builtin not supported on this processor. */
return 0;
- if (rs6000_isa_flags_explicit & OPTION_MASK_SOFT_FLOAT)
+ if (rs6000_isa_flags & OPTION_MASK_SOFT_FLOAT)
{
- error ("__builtin_set_fpscr_rn not supported with -msoft-float");
+ error ("%<__builtin_set_fpscr_rn%> not supported with %<-msoft-float%>");
return const0_rtx;
}
compile time if the argument is a variable. The least significant two
bits of the argument, regardless of type, are used to set the rounding
mode. All other bits are ignored. */
- if (GET_CODE (op0) == CONST_INT && !const_0_to_3_operand(op0, VOIDmode))
+ if (CONST_INT_P (op0) && !const_0_to_3_operand(op0, VOIDmode))
{
error ("Argument must be a value between 0 and 3.");
return const0_rtx;
if (TARGET_32BIT)
/* Builtin not supported in 32-bit mode. */
fatal_error (input_location,
- "__builtin_set_fpscr_drn is not supported in 32-bit mode.");
+ "%<__builtin_set_fpscr_drn%> is not supported "
+ "in 32-bit mode");
- if (rs6000_isa_flags_explicit & OPTION_MASK_SOFT_FLOAT)
+ if (rs6000_isa_flags & OPTION_MASK_SOFT_FLOAT)
{
- error ("__builtin_set_fpscr_drn not supported with -msoft-float");
+ error ("%<__builtin_set_fpscr_drn%> not supported with %<-msoft-float%>");
return const0_rtx;
}
compile time if the argument is a variable. The least significant two
bits of the argument, regardless of type, are used to set the rounding
mode. All other bits are ignored. */
- if (GET_CODE (op0) == CONST_INT && !const_0_to_7_operand(op0, VOIDmode))
+ if (CONST_INT_P (op0) && !const_0_to_7_operand(op0, VOIDmode))
{
error ("Argument must be a value between 0 and 7.");
return const0_rtx;
|| icode == CODE_FOR_altivec_vspltisw)
{
/* Only allow 5-bit *signed* literals. */
- if (GET_CODE (op0) != CONST_INT
+ if (!CONST_INT_P (op0)
|| INTVAL (op0) > 15
|| INTVAL (op0) < -16)
{
return TEXASRU_SPR;
}
-/* Return the appropriate SPR regno associated with the given builtin. */
-static inline HOST_WIDE_INT
-htm_spr_regno (enum rs6000_builtins code)
-{
- if (code == HTM_BUILTIN_GET_TFHAR
- || code == HTM_BUILTIN_SET_TFHAR)
- return TFHAR_REGNO;
- else if (code == HTM_BUILTIN_GET_TFIAR
- || code == HTM_BUILTIN_SET_TFIAR)
- return TFIAR_REGNO;
- gcc_assert (code == HTM_BUILTIN_GET_TEXASR
- || code == HTM_BUILTIN_SET_TEXASR
- || code == HTM_BUILTIN_GET_TEXASRU
- || code == HTM_BUILTIN_SET_TEXASRU);
- return TEXASR_REGNO;
-}
-
/* Return the correct ICODE value depending on whether we are
setting or reading the HTM SPRs. */
static inline enum insn_code
{
machine_mode mode = (TARGET_POWERPC64) ? DImode : SImode;
op[nopnds++] = gen_rtx_CONST_INT (mode, htm_spr_num (fcode));
- op[nopnds++] = gen_rtx_REG (mode, htm_spr_regno (fcode));
}
/* If this builtin accesses a CR, then pass in a scratch
CR as the last operand. */
if (!(attr & RS6000_BTC_VOID))
expected_nopnds += 1;
if (uses_spr)
- expected_nopnds += 2;
+ expected_nopnds += 1;
gcc_assert (nopnds == expected_nopnds
&& nopnds <= MAX_HTM_OPERANDS);
if (TREE_CODE (arg2) != INTEGER_CST
|| wi::geu_p (wi::to_wide (arg2), 16))
{
- error ("argument 3 must be in the range 0..15");
+ error ("argument 3 must be in the range [0, 15]");
return CONST0_RTX (tmode);
}
}
if (!tree_fits_uhwi_p (arg)
|| (elt = tree_to_uhwi (arg), elt > max))
{
- error ("selector must be an integer constant in the range 0..%wi", max);
+ error ("selector must be an integer constant in the range [0, %wi]", max);
return 0;
}
op0 = expand_normal (arg0);
op1 = expand_normal (arg1);
- /* Call get_element_number to validate arg1 if it is a constant. */
if (TREE_CODE (arg1) == INTEGER_CST)
- (void) get_element_number (TREE_TYPE (arg0), arg1);
+ {
+ unsigned HOST_WIDE_INT elt;
+ unsigned HOST_WIDE_INT size = TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg0));
+ unsigned int truncated_selector;
+ /* Even if !tree_fits_uhwi_p (arg1)), TREE_INT_CST_LOW (arg0)
+ returns low-order bits of INTEGER_CST for modulo indexing. */
+ elt = TREE_INT_CST_LOW (arg1);
+ truncated_selector = elt % size;
+ op1 = GEN_INT (truncated_selector);
+ }
tmode = TYPE_MODE (TREE_TYPE (TREE_TYPE (arg0)));
mode0 = TYPE_MODE (TREE_TYPE (arg0));
if (TREE_CODE (arg1) != INTEGER_CST || TREE_INT_CST_LOW (arg1) > 12)
{
- error ("second argument to %qs must be 0..12", "vec_vextract4b");
+ error ("second argument to %qs must be [0, 12]", "vec_vextract4b");
return expand_call (exp, target, false);
}
break;
if (TREE_CODE (arg2) != INTEGER_CST || TREE_INT_CST_LOW (arg2) > 12)
{
- error ("third argument to %qs must be 0..12", "vec_vinsert4b");
+ error ("third argument to %qs must be [0, 12]", "vec_vinsert4b");
return expand_call (exp, target, false);
}
break;
error ("builtin function %qs requires ISA 3.0 IEEE 128-bit floating point",
name);
else if ((fnmask & RS6000_BTM_FLOAT128) != 0)
- error ("builtin function %qs requires the %qs option", name, "-mfloat128");
+ error ("builtin function %qs requires the %qs option", name,
+ "%<-mfloat128%>");
else if ((fnmask & (RS6000_BTM_POPCNTD | RS6000_BTM_POWERPC64))
== (RS6000_BTM_POPCNTD | RS6000_BTM_POWERPC64))
error ("builtin function %qs requires the %qs (or newer), and "
gsi_replace (gsi, g, true);
}
+/* Helper function to map V2DF and V4SF types to their
+ integral equivalents (V2DI and V4SI). */
+tree map_to_integral_tree_type (tree input_tree_type)
+{
+ if (INTEGRAL_TYPE_P (TREE_TYPE (input_tree_type)))
+ return input_tree_type;
+ else
+ {
+ if (types_compatible_p (TREE_TYPE (input_tree_type),
+ TREE_TYPE (V2DF_type_node)))
+ return V2DI_type_node;
+ else if (types_compatible_p (TREE_TYPE (input_tree_type),
+ TREE_TYPE (V4SF_type_node)))
+ return V4SI_type_node;
+ else
+ gcc_unreachable ();
+ }
+}
+
/* Helper function to handle the vector merge[hl] built-ins. The
implementation difference between h and l versions for this code are in
the values used when building of the permute vector for high word versus
float types, the permute type needs to map to the V2 or V4 type that
matches size. */
tree permute_type;
- if (INTEGRAL_TYPE_P (TREE_TYPE (lhs_type)))
- permute_type = lhs_type;
- else
- {
- if (types_compatible_p (TREE_TYPE (lhs_type),
- TREE_TYPE (V2DF_type_node)))
- permute_type = V2DI_type_node;
- else if (types_compatible_p (TREE_TYPE (lhs_type),
- TREE_TYPE (V4SF_type_node)))
- permute_type = V4SI_type_node;
- else
- gcc_unreachable ();
- }
+ permute_type = map_to_integral_tree_type (lhs_type);
tree_vector_builder elts (permute_type, VECTOR_CST_NELTS (arg0), 1);
for (int i = 0; i < midpoint; i++)
gsi_replace (gsi, g, true);
}
+/* Helper function to handle the vector merge[eo] built-ins. */
+static void
+fold_mergeeo_helper (gimple_stmt_iterator *gsi, gimple *stmt, int use_odd)
+{
+ tree arg0 = gimple_call_arg (stmt, 0);
+ tree arg1 = gimple_call_arg (stmt, 1);
+ tree lhs = gimple_call_lhs (stmt);
+ tree lhs_type = TREE_TYPE (lhs);
+ int n_elts = TYPE_VECTOR_SUBPARTS (lhs_type);
+
+ /* The permute_type will match the lhs for integral types. For double and
+ float types, the permute type needs to map to the V2 or V4 type that
+ matches size. */
+ tree permute_type;
+ permute_type = map_to_integral_tree_type (lhs_type);
+
+ tree_vector_builder elts (permute_type, VECTOR_CST_NELTS (arg0), 1);
+
+ /* Build the permute vector. */
+ for (int i = 0; i < n_elts / 2; i++)
+ {
+ elts.safe_push (build_int_cst (TREE_TYPE (permute_type),
+ 2*i + use_odd));
+ elts.safe_push (build_int_cst (TREE_TYPE (permute_type),
+ 2*i + use_odd + n_elts));
+ }
+
+ tree permute = elts.build ();
+
+ gimple *g = gimple_build_assign (lhs, VEC_PERM_EXPR, arg0, arg1, permute);
+ gimple_set_location (g, gimple_location (stmt));
+ gsi_replace (gsi, g, true);
+}
+
/* Fold a machine-dependent built-in in GIMPLE. (For folding into
a constant, use rs6000_fold_builtin.) */
enum rs6000_builtins fn_code
= (enum rs6000_builtins) DECL_FUNCTION_CODE (fndecl);
tree arg0, arg1, lhs, temp;
+ enum tree_code bcode;
gimple *g;
size_t uns_fncode = (size_t) fn_code;
case P8V_BUILTIN_VADDUDM:
case ALTIVEC_BUILTIN_VADDFP:
case VSX_BUILTIN_XVADDDP:
+ bcode = PLUS_EXPR;
+ do_binary:
arg0 = gimple_call_arg (stmt, 0);
arg1 = gimple_call_arg (stmt, 1);
lhs = gimple_call_lhs (stmt);
- g = gimple_build_assign (lhs, PLUS_EXPR, arg0, arg1);
+ if (INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (lhs)))
+ && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (TREE_TYPE (lhs))))
+ {
+ /* Ensure the binary operation is performed in a type
+ that wraps if it is integral type. */
+ gimple_seq stmts = NULL;
+ tree type = unsigned_type_for (TREE_TYPE (lhs));
+ tree uarg0 = gimple_build (&stmts, VIEW_CONVERT_EXPR,
+ type, arg0);
+ tree uarg1 = gimple_build (&stmts, VIEW_CONVERT_EXPR,
+ type, arg1);
+ tree res = gimple_build (&stmts, gimple_location (stmt), bcode,
+ type, uarg0, uarg1);
+ gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ g = gimple_build_assign (lhs, VIEW_CONVERT_EXPR,
+ build1 (VIEW_CONVERT_EXPR,
+ TREE_TYPE (lhs), res));
+ gsi_replace (gsi, g, true);
+ return true;
+ }
+ g = gimple_build_assign (lhs, bcode, arg0, arg1);
gimple_set_location (g, gimple_location (stmt));
gsi_replace (gsi, g, true);
return true;
case P8V_BUILTIN_VSUBUDM:
case ALTIVEC_BUILTIN_VSUBFP:
case VSX_BUILTIN_XVSUBDP:
- arg0 = gimple_call_arg (stmt, 0);
- arg1 = gimple_call_arg (stmt, 1);
- lhs = gimple_call_lhs (stmt);
- g = gimple_build_assign (lhs, MINUS_EXPR, arg0, arg1);
- gimple_set_location (g, gimple_location (stmt));
- gsi_replace (gsi, g, true);
- return true;
+ bcode = MINUS_EXPR;
+ goto do_binary;
case VSX_BUILTIN_XVMULSP:
case VSX_BUILTIN_XVMULDP:
arg0 = gimple_call_arg (stmt, 0);
case ALTIVEC_BUILTIN_VSRAH:
case ALTIVEC_BUILTIN_VSRAW:
case P8V_BUILTIN_VSRAD:
- arg0 = gimple_call_arg (stmt, 0);
- arg1 = gimple_call_arg (stmt, 1);
- lhs = gimple_call_lhs (stmt);
- g = gimple_build_assign (lhs, RSHIFT_EXPR, arg0, arg1);
- gimple_set_location (g, gimple_location (stmt));
- gsi_replace (gsi, g, true);
- return true;
+ {
+ arg0 = gimple_call_arg (stmt, 0);
+ arg1 = gimple_call_arg (stmt, 1);
+ lhs = gimple_call_lhs (stmt);
+ tree arg1_type = TREE_TYPE (arg1);
+ tree unsigned_arg1_type = unsigned_type_for (TREE_TYPE (arg1));
+ tree unsigned_element_type = unsigned_type_for (TREE_TYPE (arg1_type));
+ location_t loc = gimple_location (stmt);
+ /* Force arg1 into the range valid matching the arg0 type. */
+ /* Build a vector consisting of the max valid bit-size values. */
+ int n_elts = VECTOR_CST_NELTS (arg1);
+ tree element_size = build_int_cst (unsigned_element_type,
+ 128 / n_elts);
+ tree_vector_builder elts (unsigned_arg1_type, n_elts, 1);
+ for (int i = 0; i < n_elts; i++)
+ elts.safe_push (element_size);
+ tree modulo_tree = elts.build ();
+ /* Modulo the provided shift value against that vector. */
+ gimple_seq stmts = NULL;
+ tree unsigned_arg1 = gimple_build (&stmts, VIEW_CONVERT_EXPR,
+ unsigned_arg1_type, arg1);
+ tree new_arg1 = gimple_build (&stmts, loc, TRUNC_MOD_EXPR,
+ unsigned_arg1_type, unsigned_arg1,
+ modulo_tree);
+ gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ /* And finally, do the shift. */
+ g = gimple_build_assign (lhs, RSHIFT_EXPR, arg0, new_arg1);
+ gimple_set_location (g, loc);
+ gsi_replace (gsi, g, true);
+ return true;
+ }
/* Flavors of vector shift left.
builtin_altivec_vsl{b,h,w} -> vsl{b,h,w}. */
case ALTIVEC_BUILTIN_VSLB:
arg0 = gimple_call_arg (stmt, 0);
arg1 = gimple_call_arg (stmt, 1);
lhs = gimple_call_lhs (stmt);
+ tree arg1_type = TREE_TYPE (arg1);
+ tree unsigned_arg1_type = unsigned_type_for (TREE_TYPE (arg1));
+ tree unsigned_element_type = unsigned_type_for (TREE_TYPE (arg1_type));
+ location_t loc = gimple_location (stmt);
gimple_seq stmts = NULL;
/* Convert arg0 to unsigned. */
tree arg0_unsigned
= gimple_build (&stmts, VIEW_CONVERT_EXPR,
unsigned_type_for (TREE_TYPE (arg0)), arg0);
+ /* Force arg1 into the range valid matching the arg0 type. */
+ /* Build a vector consisting of the max valid bit-size values. */
+ int n_elts = VECTOR_CST_NELTS (arg1);
+ tree element_size = build_int_cst (unsigned_element_type,
+ 128 / n_elts);
+ tree_vector_builder elts (unsigned_arg1_type, n_elts, 1);
+ for (int i = 0; i < n_elts; i++)
+ elts.safe_push (element_size);
+ tree modulo_tree = elts.build ();
+ /* Modulo the provided shift value against that vector. */
+ tree unsigned_arg1 = gimple_build (&stmts, VIEW_CONVERT_EXPR,
+ unsigned_arg1_type, arg1);
+ tree new_arg1 = gimple_build (&stmts, loc, TRUNC_MOD_EXPR,
+ unsigned_arg1_type, unsigned_arg1,
+ modulo_tree);
+ /* Do the shift. */
tree res
= gimple_build (&stmts, RSHIFT_EXPR,
- TREE_TYPE (arg0_unsigned), arg0_unsigned, arg1);
+ TREE_TYPE (arg0_unsigned), arg0_unsigned, new_arg1);
/* Convert result back to the lhs type. */
res = gimple_build (&stmts, VIEW_CONVERT_EXPR, TREE_TYPE (lhs), res);
gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
arg1_type, temp_addr,
build_int_cst (arg1_type, -16));
gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ if (!is_gimple_mem_ref_addr (aligned_addr))
+ {
+ tree t = make_ssa_name (TREE_TYPE (aligned_addr));
+ gimple *g = gimple_build_assign (t, aligned_addr);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ aligned_addr = t;
+ }
/* Use the build2 helper to set up the mem_ref. The MEM_REF could also
take an offset, but since we've already incorporated the offset
above, here we just pass in a zero. */
arg2_type, temp_addr,
build_int_cst (arg2_type, -16));
gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ if (!is_gimple_mem_ref_addr (aligned_addr))
+ {
+ tree t = make_ssa_name (TREE_TYPE (aligned_addr));
+ gimple *g = gimple_build_assign (t, aligned_addr);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ aligned_addr = t;
+ }
/* The desired gimple result should be similar to:
MEM[(__vector floatD.1407 *)_1] = vf1D.2697; */
gimple *g
case VSX_BUILTIN_LXVD2X_V2DF:
case VSX_BUILTIN_LXVD2X_V2DI:
{
- arg0 = gimple_call_arg (stmt, 0); // offset
- arg1 = gimple_call_arg (stmt, 1); // address
- lhs = gimple_call_lhs (stmt);
- location_t loc = gimple_location (stmt);
- /* Since arg1 may be cast to a different type, just use ptr_type_node
- here instead of trying to enforce TBAA on pointer types. */
- tree arg1_type = ptr_type_node;
- tree lhs_type = TREE_TYPE (lhs);
- /* In GIMPLE the type of the MEM_REF specifies the alignment. The
- required alignment (power) is 4 bytes regardless of data type. */
- tree align_ltype = build_aligned_type (lhs_type, 4);
- /* POINTER_PLUS_EXPR wants the offset to be of type 'sizetype'. Create
- the tree using the value from arg0. The resulting type will match
- the type of arg1. */
- gimple_seq stmts = NULL;
- tree temp_offset = gimple_convert (&stmts, loc, sizetype, arg0);
- tree temp_addr = gimple_build (&stmts, loc, POINTER_PLUS_EXPR,
+ arg0 = gimple_call_arg (stmt, 0); // offset
+ arg1 = gimple_call_arg (stmt, 1); // address
+ lhs = gimple_call_lhs (stmt);
+ location_t loc = gimple_location (stmt);
+ /* Since arg1 may be cast to a different type, just use ptr_type_node
+ here instead of trying to enforce TBAA on pointer types. */
+ tree arg1_type = ptr_type_node;
+ tree lhs_type = TREE_TYPE (lhs);
+ /* In GIMPLE the type of the MEM_REF specifies the alignment. The
+ required alignment (power) is 4 bytes regardless of data type. */
+ tree align_ltype = build_aligned_type (lhs_type, 4);
+ /* POINTER_PLUS_EXPR wants the offset to be of type 'sizetype'. Create
+ the tree using the value from arg0. The resulting type will match
+ the type of arg1. */
+ gimple_seq stmts = NULL;
+ tree temp_offset = gimple_convert (&stmts, loc, sizetype, arg0);
+ tree temp_addr = gimple_build (&stmts, loc, POINTER_PLUS_EXPR,
arg1_type, arg1, temp_offset);
- gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
- /* Use the build2 helper to set up the mem_ref. The MEM_REF could also
- take an offset, but since we've already incorporated the offset
- above, here we just pass in a zero. */
- gimple *g;
- g = gimple_build_assign (lhs, build2 (MEM_REF, align_ltype, temp_addr,
- build_int_cst (arg1_type, 0)));
- gimple_set_location (g, loc);
- gsi_replace (gsi, g, true);
- return true;
+ gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ if (!is_gimple_mem_ref_addr (temp_addr))
+ {
+ tree t = make_ssa_name (TREE_TYPE (temp_addr));
+ gimple *g = gimple_build_assign (t, temp_addr);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ temp_addr = t;
+ }
+ /* Use the build2 helper to set up the mem_ref. The MEM_REF could also
+ take an offset, but since we've already incorporated the offset
+ above, here we just pass in a zero. */
+ gimple *g;
+ g = gimple_build_assign (lhs, build2 (MEM_REF, align_ltype, temp_addr,
+ build_int_cst (arg1_type, 0)));
+ gimple_set_location (g, loc);
+ gsi_replace (gsi, g, true);
+ return true;
}
/* unaligned Vector stores. */
case VSX_BUILTIN_STXVD2X_V2DF:
case VSX_BUILTIN_STXVD2X_V2DI:
{
- arg0 = gimple_call_arg (stmt, 0); /* Value to be stored. */
- arg1 = gimple_call_arg (stmt, 1); /* Offset. */
- tree arg2 = gimple_call_arg (stmt, 2); /* Store-to address. */
- location_t loc = gimple_location (stmt);
- tree arg0_type = TREE_TYPE (arg0);
- /* Use ptr_type_node (no TBAA) for the arg2_type. */
- tree arg2_type = ptr_type_node;
- /* In GIMPLE the type of the MEM_REF specifies the alignment. The
- required alignment (power) is 4 bytes regardless of data type. */
- tree align_stype = build_aligned_type (arg0_type, 4);
- /* POINTER_PLUS_EXPR wants the offset to be of type 'sizetype'. Create
- the tree using the value from arg1. */
- gimple_seq stmts = NULL;
- tree temp_offset = gimple_convert (&stmts, loc, sizetype, arg1);
- tree temp_addr = gimple_build (&stmts, loc, POINTER_PLUS_EXPR,
+ arg0 = gimple_call_arg (stmt, 0); /* Value to be stored. */
+ arg1 = gimple_call_arg (stmt, 1); /* Offset. */
+ tree arg2 = gimple_call_arg (stmt, 2); /* Store-to address. */
+ location_t loc = gimple_location (stmt);
+ tree arg0_type = TREE_TYPE (arg0);
+ /* Use ptr_type_node (no TBAA) for the arg2_type. */
+ tree arg2_type = ptr_type_node;
+ /* In GIMPLE the type of the MEM_REF specifies the alignment. The
+ required alignment (power) is 4 bytes regardless of data type. */
+ tree align_stype = build_aligned_type (arg0_type, 4);
+ /* POINTER_PLUS_EXPR wants the offset to be of type 'sizetype'. Create
+ the tree using the value from arg1. */
+ gimple_seq stmts = NULL;
+ tree temp_offset = gimple_convert (&stmts, loc, sizetype, arg1);
+ tree temp_addr = gimple_build (&stmts, loc, POINTER_PLUS_EXPR,
arg2_type, arg2, temp_offset);
- gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
- gimple *g;
- g = gimple_build_assign (build2 (MEM_REF, align_stype, temp_addr,
- build_int_cst (arg2_type, 0)), arg0);
- gimple_set_location (g, loc);
- gsi_replace (gsi, g, true);
- return true;
+ gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ if (!is_gimple_mem_ref_addr (temp_addr))
+ {
+ tree t = make_ssa_name (TREE_TYPE (temp_addr));
+ gimple *g = gimple_build_assign (t, temp_addr);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ temp_addr = t;
+ }
+ gimple *g;
+ g = gimple_build_assign (build2 (MEM_REF, align_stype, temp_addr,
+ build_int_cst (arg2_type, 0)), arg0);
+ gimple_set_location (g, loc);
+ gsi_replace (gsi, g, true);
+ return true;
}
/* Vector Fused multiply-add (fma). */
case ALTIVEC_BUILTIN_VSPLTISH:
case ALTIVEC_BUILTIN_VSPLTISW:
{
- int size;
-
- if (fn_code == ALTIVEC_BUILTIN_VSPLTISB)
- size = 8;
- else if (fn_code == ALTIVEC_BUILTIN_VSPLTISH)
- size = 16;
- else
- size = 32;
-
- arg0 = gimple_call_arg (stmt, 0);
- lhs = gimple_call_lhs (stmt);
-
- /* Only fold the vec_splat_*() if the lower bits of arg 0 is a
- 5-bit signed constant in range -16 to +15. */
- if (TREE_CODE (arg0) != INTEGER_CST
- || !IN_RANGE (sext_hwi(TREE_INT_CST_LOW (arg0), size),
- -16, 15))
- return false;
- gimple_seq stmts = NULL;
- location_t loc = gimple_location (stmt);
- tree splat_value = gimple_convert (&stmts, loc,
- TREE_TYPE (TREE_TYPE (lhs)), arg0);
- gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
- tree splat_tree = build_vector_from_val (TREE_TYPE (lhs), splat_value);
- g = gimple_build_assign (lhs, splat_tree);
- gimple_set_location (g, gimple_location (stmt));
- gsi_replace (gsi, g, true);
- return true;
- }
+ arg0 = gimple_call_arg (stmt, 0);
+ lhs = gimple_call_lhs (stmt);
+
+ /* Only fold the vec_splat_*() if the lower bits of arg 0 is a
+ 5-bit signed constant in range -16 to +15. */
+ if (TREE_CODE (arg0) != INTEGER_CST
+ || !IN_RANGE (TREE_INT_CST_LOW (arg0), -16, 15))
+ return false;
+ gimple_seq stmts = NULL;
+ location_t loc = gimple_location (stmt);
+ tree splat_value = gimple_convert (&stmts, loc,
+ TREE_TYPE (TREE_TYPE (lhs)), arg0);
+ gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+ tree splat_tree = build_vector_from_val (TREE_TYPE (lhs), splat_value);
+ g = gimple_build_assign (lhs, splat_tree);
+ gimple_set_location (g, gimple_location (stmt));
+ gsi_replace (gsi, g, true);
+ return true;
+ }
/* Flavors of vec_splat. */
/* a = vec_splat (b, 0x3) becomes a = { b[3],b[3],b[3],...}; */
case VSX_BUILTIN_VEC_MERGEL_V2DI:
case VSX_BUILTIN_XXMRGLW_4SF:
case VSX_BUILTIN_VEC_MERGEL_V2DF:
- fold_mergehl_helper (gsi, stmt, 1);
- return true;
+ fold_mergehl_helper (gsi, stmt, 1);
+ return true;
/* vec_mergeh (integrals). */
case ALTIVEC_BUILTIN_VMRGHH:
case ALTIVEC_BUILTIN_VMRGHW:
case VSX_BUILTIN_VEC_MERGEH_V2DI:
case VSX_BUILTIN_XXMRGHW_4SF:
case VSX_BUILTIN_VEC_MERGEH_V2DF:
- fold_mergehl_helper (gsi, stmt, 0);
- return true;
+ fold_mergehl_helper (gsi, stmt, 0);
+ return true;
+
+ /* Flavors of vec_mergee. */
+ case P8V_BUILTIN_VMRGEW_V4SI:
+ case P8V_BUILTIN_VMRGEW_V2DI:
+ case P8V_BUILTIN_VMRGEW_V4SF:
+ case P8V_BUILTIN_VMRGEW_V2DF:
+ fold_mergeeo_helper (gsi, stmt, 0);
+ return true;
+ /* Flavors of vec_mergeo. */
+ case P8V_BUILTIN_VMRGOW_V4SI:
+ case P8V_BUILTIN_VMRGOW_V2DI:
+ case P8V_BUILTIN_VMRGOW_V4SF:
+ case P8V_BUILTIN_VMRGOW_V2DF:
+ fold_mergeeo_helper (gsi, stmt, 1);
+ return true;
/* d = vec_pack (a, b) */
case P8V_BUILTIN_VPKUDUM:
case ALTIVEC_BUILTIN_VPKUHUM:
case ALTIVEC_BUILTIN_VPKUWUM:
{
- arg0 = gimple_call_arg (stmt, 0);
- arg1 = gimple_call_arg (stmt, 1);
- lhs = gimple_call_lhs (stmt);
- gimple *g = gimple_build_assign (lhs, VEC_PACK_TRUNC_EXPR, arg0, arg1);
- gimple_set_location (g, gimple_location (stmt));
- gsi_replace (gsi, g, true);
- return true;
+ arg0 = gimple_call_arg (stmt, 0);
+ arg1 = gimple_call_arg (stmt, 1);
+ lhs = gimple_call_lhs (stmt);
+ gimple *g = gimple_build_assign (lhs, VEC_PACK_TRUNC_EXPR, arg0, arg1);
+ gimple_set_location (g, gimple_location (stmt));
+ gsi_replace (gsi, g, true);
+ return true;
}
- /* d = vec_unpackh (a) */
- /* Note that the UNPACK_{HI,LO}_EXPR used in the gimple_build_assign call
- in this code is sensitive to endian-ness, and needs to be inverted to
- handle both LE and BE targets. */
+ /* d = vec_unpackh (a) */
+ /* Note that the UNPACK_{HI,LO}_EXPR used in the gimple_build_assign call
+ in this code is sensitive to endian-ness, and needs to be inverted to
+ handle both LE and BE targets. */
case ALTIVEC_BUILTIN_VUPKHSB:
case ALTIVEC_BUILTIN_VUPKHSH:
case P8V_BUILTIN_VUPKHSW:
{
- arg0 = gimple_call_arg (stmt, 0);
- lhs = gimple_call_lhs (stmt);
- if (BYTES_BIG_ENDIAN)
- g = gimple_build_assign (lhs, VEC_UNPACK_HI_EXPR, arg0);
- else
- g = gimple_build_assign (lhs, VEC_UNPACK_LO_EXPR, arg0);
- gimple_set_location (g, gimple_location (stmt));
- gsi_replace (gsi, g, true);
- return true;
+ arg0 = gimple_call_arg (stmt, 0);
+ lhs = gimple_call_lhs (stmt);
+ if (BYTES_BIG_ENDIAN)
+ g = gimple_build_assign (lhs, VEC_UNPACK_HI_EXPR, arg0);
+ else
+ g = gimple_build_assign (lhs, VEC_UNPACK_LO_EXPR, arg0);
+ gimple_set_location (g, gimple_location (stmt));
+ gsi_replace (gsi, g, true);
+ return true;
}
- /* d = vec_unpackl (a) */
+ /* d = vec_unpackl (a) */
case ALTIVEC_BUILTIN_VUPKLSB:
case ALTIVEC_BUILTIN_VUPKLSH:
case P8V_BUILTIN_VUPKLSW:
{
- arg0 = gimple_call_arg (stmt, 0);
- lhs = gimple_call_lhs (stmt);
- if (BYTES_BIG_ENDIAN)
- g = gimple_build_assign (lhs, VEC_UNPACK_LO_EXPR, arg0);
- else
- g = gimple_build_assign (lhs, VEC_UNPACK_HI_EXPR, arg0);
- gimple_set_location (g, gimple_location (stmt));
- gsi_replace (gsi, g, true);
- return true;
+ arg0 = gimple_call_arg (stmt, 0);
+ lhs = gimple_call_lhs (stmt);
+ if (BYTES_BIG_ENDIAN)
+ g = gimple_build_assign (lhs, VEC_UNPACK_LO_EXPR, arg0);
+ else
+ g = gimple_build_assign (lhs, VEC_UNPACK_HI_EXPR, arg0);
+ gimple_set_location (g, gimple_location (stmt));
+ gsi_replace (gsi, g, true);
+ return true;
}
/* There is no gimple type corresponding with pixel, so just return. */
case ALTIVEC_BUILTIN_VUPKHPX:
{
/* unsigned 1 argument functions. */
case CRYPTO_BUILTIN_VSBOX:
+ case CRYPTO_BUILTIN_VSBOX_BE:
case P8V_BUILTIN_VGBBD:
case MISC_BUILTIN_CDTBCD:
case MISC_BUILTIN_CBCDTD:
case ALTIVEC_BUILTIN_VMULOUH:
case P8V_BUILTIN_VMULOUW:
case CRYPTO_BUILTIN_VCIPHER:
+ case CRYPTO_BUILTIN_VCIPHER_BE:
case CRYPTO_BUILTIN_VCIPHERLAST:
+ case CRYPTO_BUILTIN_VCIPHERLAST_BE:
case CRYPTO_BUILTIN_VNCIPHER:
+ case CRYPTO_BUILTIN_VNCIPHER_BE:
case CRYPTO_BUILTIN_VNCIPHERLAST:
+ case CRYPTO_BUILTIN_VNCIPHERLAST_BE:
case CRYPTO_BUILTIN_VPMSUMB:
case CRYPTO_BUILTIN_VPMSUMH:
case CRYPTO_BUILTIN_VPMSUMW:
registers_ok_for_quad_peep (rtx reg1, rtx reg2)
{
/* We might have been passed a SUBREG. */
- if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG)
+ if (!REG_P (reg1) || !REG_P (reg2))
return 0;
/* We might have been passed non floating point registers. */
if (GET_CODE (addr1) == PLUS)
{
/* If not a REG, return zero. */
- if (GET_CODE (XEXP (addr1, 0)) != REG)
+ if (!REG_P (XEXP (addr1, 0)))
return 0;
else
{
reg1 = REGNO (XEXP (addr1, 0));
/* The offset must be constant! */
- if (GET_CODE (XEXP (addr1, 1)) != CONST_INT)
+ if (!CONST_INT_P (XEXP (addr1, 1)))
return 0;
offset1 = INTVAL (XEXP (addr1, 1));
}
}
- else if (GET_CODE (addr1) != REG)
+ else if (!REG_P (addr1))
return 0;
else
{
if (GET_CODE (addr2) == PLUS)
{
/* If not a REG, return zero. */
- if (GET_CODE (XEXP (addr2, 0)) != REG)
+ if (!REG_P (XEXP (addr2, 0)))
return 0;
else
{
reg2 = REGNO (XEXP (addr2, 0));
/* The offset must be constant. */
- if (GET_CODE (XEXP (addr2, 1)) != CONST_INT)
+ if (!CONST_INT_P (XEXP (addr2, 1)))
return 0;
offset2 = INTVAL (XEXP (addr2, 1));
}
}
- else if (GET_CODE (addr2) != REG)
+ else if (!REG_P (addr2))
return 0;
else
{
HOST_WIDE_INT regno;
enum reg_class rclass;
- if (GET_CODE (reg) == SUBREG)
+ if (SUBREG_P (reg))
reg = SUBREG_REG (reg);
if (!REG_P (reg))
return NO_REG_TYPE;
regno = REGNO (reg);
- if (regno >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno))
{
if (!lra_in_progress && !reload_completed)
return PSEUDO_REG_TYPE;
regno = true_regnum (reg);
- if (regno < 0 || regno >= FIRST_PSEUDO_REGISTER)
+ if (regno < 0 || !HARD_REGISTER_NUM_P (regno))
return PSEUDO_REG_TYPE;
}
case AND:
and_arg = XEXP (addr, 0);
if (GET_MODE_SIZE (mode) != 16
- || GET_CODE (XEXP (addr, 1)) != CONST_INT
+ || !CONST_INT_P (XEXP (addr, 1))
|| INTVAL (XEXP (addr, 1)) != -16)
{
fail_msg = "bad Altivec AND #1";
/* Allow subreg of memory before/during reload. */
bool memory_p = (MEM_P (x)
- || (!reload_completed && GET_CODE (x) == SUBREG
+ || (!reload_completed && SUBREG_P (x)
&& MEM_P (SUBREG_REG (x))));
sri->icode = CODE_FOR_nothing;
if (!done_p && reg_addr[mode].scalar_in_vmx_p
&& !mode_supports_vmx_dform (mode)
&& (rclass == VSX_REGS || rclass == ALTIVEC_REGS)
- && (memory_p || (GET_CODE (x) == CONST_DOUBLE)))
+ && (memory_p || CONST_DOUBLE_P (x)))
{
ret = FLOAT_REGS;
default_p = false;
rtx cc_clobber;
rtvec rv;
- if (regno < 0 || regno >= FIRST_PSEUDO_REGISTER || !MEM_P (mem)
+ if (regno < 0 || !HARD_REGISTER_NUM_P (regno) || !MEM_P (mem)
|| !base_reg_operand (scratch, GET_MODE (scratch)))
rs6000_secondary_reload_fail (__LINE__, reg, mem, scratch, store_p);
if ((addr_mask & RELOAD_REG_PRE_INCDEC) == 0)
{
- emit_insn (gen_add2_insn (op_reg, GEN_INT (GET_MODE_SIZE (mode))));
+ int delta = GET_MODE_SIZE (mode);
+ if (GET_CODE (addr) == PRE_DEC)
+ delta = -delta;
+ emit_insn (gen_add2_insn (op_reg, GEN_INT (delta)));
new_addr = op_reg;
}
break;
op1 = XEXP (addr, 1);
if ((addr_mask & RELOAD_REG_AND_M16) == 0)
{
- if (REG_P (op0) || GET_CODE (op0) == SUBREG)
+ if (REG_P (op0) || SUBREG_P (op0))
op_reg = op0;
else if (GET_CODE (op1) == PLUS)
debug_rtx (scratch);
}
- gcc_assert (regno >= 0 && regno < FIRST_PSEUDO_REGISTER);
- gcc_assert (GET_CODE (mem) == MEM);
+ gcc_assert (regno >= 0 && HARD_REGISTER_NUM_P (regno));
+ gcc_assert (MEM_P (mem));
rclass = REGNO_REG_CLASS (regno);
gcc_assert (rclass == GENERAL_REGS || rclass == BASE_REGS);
addr = XEXP (mem, 0);
&& GET_CODE (XEXP (addr, 1)) == PLUS
&& XEXP (XEXP (addr, 1), 0) == XEXP (addr, 0));
scratch_or_premodify = XEXP (addr, 0);
- if (!HARD_REGISTER_P (scratch_or_premodify))
- /* If we have a pseudo here then reload will have arranged
- to have it replaced, but only in the original insn.
- Use the replacement here too. */
- scratch_or_premodify = find_replacement (&XEXP (addr, 0));
-
- /* RTL emitted by rs6000_secondary_reload_gpr uses RTL
- expressions from the original insn, without unsharing them.
- Any RTL that points into the original insn will of course
- have register replacements applied. That is why we don't
- need to look for replacements under the PLUS. */
addr = XEXP (addr, 1);
}
gcc_assert (GET_CODE (addr) == PLUS || GET_CODE (addr) == LO_SUM);
return NO_REGS;
}
- if (GET_MODE_CLASS (mode) == MODE_INT && rclass == NON_SPECIAL_REGS)
+ if (GET_MODE_CLASS (mode) == MODE_INT && rclass == GEN_OR_FLOAT_REGS)
return GENERAL_REGS;
return rclass;
On Darwin, pic addresses require a load from memory, which
needs a base register. */
if (rclass != BASE_REGS
- && (GET_CODE (in) == SYMBOL_REF
+ && (SYMBOL_REF_P (in)
|| GET_CODE (in) == HIGH
|| GET_CODE (in) == LABEL_REF
|| GET_CODE (in) == CONST))
return BASE_REGS;
}
- if (GET_CODE (in) == REG)
+ if (REG_P (in))
{
regno = REGNO (in);
- if (regno >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno))
{
regno = true_regnum (in);
- if (regno >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno))
regno = -1;
}
}
- else if (GET_CODE (in) == SUBREG)
+ else if (SUBREG_P (in))
{
regno = true_regnum (in);
- if (regno >= FIRST_PSEUDO_REGISTER)
+ if (!HARD_REGISTER_NUM_P (regno))
regno = -1;
}
else
/* Constants, memory, and FP registers can go into FP registers. */
if ((regno == -1 || FP_REGNO_P (regno))
- && (rclass == FLOAT_REGS || rclass == NON_SPECIAL_REGS))
+ && (rclass == FLOAT_REGS || rclass == GEN_OR_FLOAT_REGS))
return (mode != SDmode || lra_in_progress) ? NO_REGS : GENERAL_REGS;
/* Memory, and AltiVec registers can go into AltiVec registers. */
/* Constants. */
else if (dest_regno >= 0
- && (GET_CODE (src) == CONST_INT
- || GET_CODE (src) == CONST_WIDE_INT
- || GET_CODE (src) == CONST_DOUBLE
+ && (CONST_INT_P (src)
+ || CONST_WIDE_INT_P (src)
+ || CONST_DOUBLE_P (src)
|| GET_CODE (src) == CONST_VECTOR))
{
if (dest_gpr_p)
reg = XEXP (op, 0);
- gcc_assert (GET_CODE (reg) == REG && CR_REGNO_P (REGNO (reg)));
+ if (!REG_P (reg) || !CR_REGNO_P (REGNO (reg)))
+ return -1;
cc_mode = GET_MODE (reg);
cc_regnum = REGNO (reg);
/* When generating a sCOND operation, only positive conditions are
allowed. */
- gcc_assert (!scc_p
- || code == EQ || code == GT || code == LT || code == UNORDERED
- || code == GTU || code == LTU);
+ if (scc_p)
+ switch (code)
+ {
+ case EQ:
+ case GT:
+ case LT:
+ case UNORDERED:
+ case GTU:
+ case LTU:
+ break;
+ default:
+ return -1;
+ }
switch (code)
{
return scc_p ? base_bit + 3 : base_bit + 1;
default:
- gcc_unreachable ();
+ return -1;
}
}
\f
return ggc_cleared_alloc<machine_function> ();
}
\f
-#define INT_P(X) (GET_CODE (X) == CONST_INT && GET_MODE (X) == VOIDmode)
+#define INT_P(X) (CONST_INT_P (X) && GET_MODE (X) == VOIDmode)
/* Write out a function code label. */
case 'D':
/* Like 'J' but get to the GT bit only. */
- gcc_assert (REG_P (x));
+ if (!REG_P (x) || !CR_REGNO_P (REGNO (x)))
+ {
+ output_operand_lossage ("invalid %%D value");
+ return;
+ }
/* Bit 1 is GT bit. */
i = 4 * (REGNO (x) - CR0_REGNO) + 1;
case 'E':
/* X is a CR register. Print the number of the EQ bit of the CR */
- if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+ if (!REG_P (x) || !CR_REGNO_P (REGNO (x)))
output_operand_lossage ("invalid %%E value");
else
fprintf (file, "%d", 4 * (REGNO (x) - CR0_REGNO) + 2);
case 'f':
/* X is a CR register. Print the shift count needed to move it
to the high-order four bits. */
- if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+ if (!REG_P (x) || !CR_REGNO_P (REGNO (x)))
output_operand_lossage ("invalid %%f value");
else
fprintf (file, "%d", 4 * (REGNO (x) - CR0_REGNO));
case 'F':
/* Similar, but print the count for the rotate in the opposite
direction. */
- if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+ if (!REG_P (x) || !CR_REGNO_P (REGNO (x)))
output_operand_lossage ("invalid %%F value");
else
fprintf (file, "%d", 32 - 4 * (REGNO (x) - CR0_REGNO));
case 'G':
/* X is a constant integer. If it is negative, print "m",
otherwise print "z". This is to make an aze or ame insn. */
- if (GET_CODE (x) != CONST_INT)
+ if (!CONST_INT_P (x))
output_operand_lossage ("invalid %%G value");
else if (INTVAL (x) >= 0)
putc ('z', file);
if (GET_CODE (x) == CONST)
{
if (GET_CODE (XEXP (x, 0)) != PLUS
- || (GET_CODE (XEXP (XEXP (x, 0), 0)) != SYMBOL_REF
+ || (!SYMBOL_REF_P (XEXP (XEXP (x, 0), 0))
&& GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF)
- || GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
+ || !CONST_INT_P (XEXP (XEXP (x, 0), 1)))
output_operand_lossage ("invalid %%K value");
}
print_operand_address (file, x);
case 'P':
/* The operand must be an indirect memory reference. The result
is the register name. */
- if (GET_CODE (x) != MEM || GET_CODE (XEXP (x, 0)) != REG
+ if (!MEM_P (x) || !REG_P (XEXP (x, 0))
|| REGNO (XEXP (x, 0)) >= 32)
output_operand_lossage ("invalid %%P value");
else
case 'R':
/* X is a CR register. Print the mask for `mtcrf'. */
- if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+ if (!REG_P (x) || !CR_REGNO_P (REGNO (x)))
output_operand_lossage ("invalid %%R value");
else
fprintf (file, "%d", 128 >> (REGNO (x) - CR0_REGNO));
case 't':
/* Like 'J' but get to the OVERFLOW/UNORDERED bit. */
- gcc_assert (REG_P (x) && GET_MODE (x) == CCmode);
+ if (!REG_P (x) || !CR_REGNO_P (REGNO (x)))
+ {
+ output_operand_lossage ("invalid %%t value");
+ return;
+ }
/* Bit 3 is OV bit. */
i = 4 * (REGNO (x) - CR0_REGNO) + 3;
case 'T':
/* Print the symbolic name of a branch target register. */
- if (GET_CODE (x) != REG || (REGNO (x) != LR_REGNO
- && REGNO (x) != CTR_REGNO))
+ if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+ x = XVECEXP (x, 0, 0);
+ if (!REG_P (x) || (REGNO (x) != LR_REGNO
+ && REGNO (x) != CTR_REGNO))
output_operand_lossage ("invalid %%T value");
else if (REGNO (x) == LR_REGNO)
fputs ("lr", file);
fputs ("lge", file); /* 5 */
break;
default:
- gcc_unreachable ();
+ output_operand_lossage ("invalid %%V value");
}
break;
case 'x':
/* X is a FPR or Altivec register used in a VSX context. */
- if (GET_CODE (x) != REG || !VSX_REGNO_P (REGNO (x)))
+ if (!REG_P (x) || !VSX_REGNO_P (REGNO (x)))
output_operand_lossage ("invalid %%x value");
else
{
return;
case 'z':
+ if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+ x = XVECEXP (x, 0, 1);
/* X is a SYMBOL_REF. Write out the name preceded by a
period and without any trailing data in brackets. Used for function
names. If we are configured for System V (or the embedded ABI) on
the PowerPC, do not emit the period, since those systems do not use
TOCs and the like. */
- gcc_assert (GET_CODE (x) == SYMBOL_REF);
+ if (!SYMBOL_REF_P (x))
+ {
+ output_operand_lossage ("invalid %%z value");
+ return;
+ }
/* For macho, check to see if we need a stub. */
if (TARGET_MACHO)
if (VECTOR_MEM_ALTIVEC_OR_VSX_P (GET_MODE (x))
&& GET_CODE (tmp) == AND
- && GET_CODE (XEXP (tmp, 1)) == CONST_INT
+ && CONST_INT_P (XEXP (tmp, 1))
&& INTVAL (XEXP (tmp, 1)) == -16)
tmp = XEXP (tmp, 0);
else if (VECTOR_MEM_VSX_P (GET_MODE (x))
else
output_address (GET_MODE (x), XEXP (x, 0));
}
+ else if (toc_relative_expr_p (x, false,
+ &tocrel_base_oac, &tocrel_offset_oac))
+ /* This hack along with a corresponding hack in
+ rs6000_output_addr_const_extra arranges to output addends
+ where the assembler expects to find them. eg.
+ (plus (unspec [(symbol_ref ("x")) (reg 2)] tocrel) 4)
+ without this hack would be output as "x@toc+4". We
+ want "x+4@toc". */
+ output_addr_const (file, CONST_CAST_RTX (tocrel_base_oac));
+ else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD)
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+ output_addr_const (file, XVECEXP (x, 0, 1));
else
- {
- if (toc_relative_expr_p (x, false, &tocrel_base_oac, &tocrel_offset_oac))
- /* This hack along with a corresponding hack in
- rs6000_output_addr_const_extra arranges to output addends
- where the assembler expects to find them. eg.
- (plus (unspec [(symbol_ref ("x")) (reg 2)] tocrel) 4)
- without this hack would be output as "x@toc+4". We
- want "x+4@toc". */
- output_addr_const (file, CONST_CAST_RTX (tocrel_base_oac));
- else
- output_addr_const (file, x);
- }
+ output_addr_const (file, x);
return;
case '&':
{
if (REG_P (x))
fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
- else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST
+ else if (SYMBOL_REF_P (x) || GET_CODE (x) == CONST
|| GET_CODE (x) == LABEL_REF)
{
output_addr_const (file, x);
reg_names[ REGNO (XEXP (x, 1)) ]);
}
else if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0))
- && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ && CONST_INT_P (XEXP (x, 1)))
fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%s)",
INTVAL (XEXP (x, 1)), reg_names[ REGNO (XEXP (x, 0)) ]);
#if TARGET_MACHO
fprintf (file, "(%s)", reg_names[REGNO (XVECEXP (tocrel_base_oac, 0, 1))]);
}
else
- gcc_unreachable ();
+ output_addr_const (file, x);
}
\f
/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA. */
switch (XINT (x, 1))
{
case UNSPEC_TOCREL:
- gcc_checking_assert (GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF
+ gcc_checking_assert (SYMBOL_REF_P (XVECEXP (x, 0, 0))
&& REG_P (XVECEXP (x, 0, 1))
&& REGNO (XVECEXP (x, 0, 1)) == TOC_REGISTER);
output_addr_const (file, XVECEXP (x, 0, 0));
/* Remove initial .'s to turn a -mcall-aixdesc function
address into the address of the descriptor, not the function
itself. */
- else if (GET_CODE (x) == SYMBOL_REF
+ else if (SYMBOL_REF_P (x)
&& XSTR (x, 0)[0] == '.'
&& DEFAULT_ABI == ABI_AIX)
{
return default_assemble_integer (x, size, aligned_p);
}
+/* Return a template string for assembly to emit when making an
+ external call. FUNOP is the call mem argument operand number. */
+
+static const char *
+rs6000_call_template_1 (rtx *operands, unsigned int funop, bool sibcall)
+{
+ /* -Wformat-overflow workaround, without which gcc thinks that %u
+ might produce 10 digits. */
+ gcc_assert (funop <= MAX_RECOG_OPERANDS);
+
+ char arg[12];
+ arg[0] = 0;
+ if (TARGET_TLS_MARKERS && GET_CODE (operands[funop + 1]) == UNSPEC)
+ {
+ if (XINT (operands[funop + 1], 1) == UNSPEC_TLSGD)
+ sprintf (arg, "(%%%u@tlsgd)", funop + 1);
+ else if (XINT (operands[funop + 1], 1) == UNSPEC_TLSLD)
+ sprintf (arg, "(%%&@tlsld)");
+ else
+ gcc_unreachable ();
+ }
+
+ /* The magic 32768 offset here corresponds to the offset of
+ r30 in .got2, as given by LCTOC1. See sysv4.h:toc_section. */
+ char z[11];
+ sprintf (z, "%%z%u%s", funop,
+ (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic == 2
+ ? "+32768" : ""));
+
+ static char str[32]; /* 2 spare */
+ if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
+ sprintf (str, "b%s %s%s%s", sibcall ? "" : "l", z, arg,
+ sibcall ? "" : "\n\tnop");
+ else if (DEFAULT_ABI == ABI_V4)
+ sprintf (str, "b%s %s%s%s", sibcall ? "" : "l", z, arg,
+ flag_pic ? "@plt" : "");
+#if TARGET_MACHO
+ /* If/when we remove the mlongcall opt, we can share the AIX/ELGv2 case. */
+ else if (DEFAULT_ABI == ABI_DARWIN)
+ {
+ /* The cookie is in operand func+2. */
+ gcc_checking_assert (GET_CODE (operands[funop + 2]) == CONST_INT);
+ int cookie = INTVAL (operands[funop + 2]);
+ if (cookie & CALL_LONG)
+ {
+ tree funname = get_identifier (XSTR (operands[funop], 0));
+ tree labelname = get_prev_label (funname);
+ gcc_checking_assert (labelname && !sibcall);
+
+ /* "jbsr foo, L42" is Mach-O for "Link as 'bl foo' if a 'bl'
+ instruction will reach 'foo', otherwise link as 'bl L42'".
+ "L42" should be a 'branch island', that will do a far jump to
+ 'foo'. Branch islands are generated in
+ macho_branch_islands(). */
+ sprintf (str, "jbsr %%z%u,%.10s", funop,
+ IDENTIFIER_POINTER (labelname));
+ }
+ else
+ /* Same as AIX or ELFv2, except to keep backwards compat, no nop
+ after the call. */
+ sprintf (str, "b%s %s%s", sibcall ? "" : "l", z, arg);
+ }
+#endif
+ else
+ gcc_unreachable ();
+ return str;
+}
+
+const char *
+rs6000_call_template (rtx *operands, unsigned int funop)
+{
+ return rs6000_call_template_1 (operands, funop, false);
+}
+
+const char *
+rs6000_sibcall_template (rtx *operands, unsigned int funop)
+{
+ return rs6000_call_template_1 (operands, funop, true);
+}
+
+/* As above, for indirect calls. */
+
+static const char *
+rs6000_indirect_call_template_1 (rtx *operands, unsigned int funop,
+ bool sibcall)
+{
+ /* -Wformat-overflow workaround, without which gcc thinks that %u
+ might produce 10 digits. Note that -Wformat-overflow will not
+ currently warn here for str[], so do not rely on a warning to
+ ensure str[] is correctly sized. */
+ gcc_assert (funop <= MAX_RECOG_OPERANDS);
+
+ /* Currently, funop is either 0 or 1. The maximum string is always
+ a !speculate 64-bit __tls_get_addr call.
+
+ ABI_AIX:
+ . 9 ld 2,%3\n\t
+ . 27 .reloc .,R_PPC64_TLSGD,%2\n\t
+ . 29 .reloc .,R_PPC64_PLTSEQ,%z1\n\t
+ . 9 crset 2\n\t
+ . 27 .reloc .,R_PPC64_TLSGD,%2\n\t
+ . 30 .reloc .,R_PPC64_PLTCALL,%z1\n\t
+ . 10 beq%T1l-\n\t
+ . 10 ld 2,%4(1)
+ .---
+ .151
+
+ ABI_ELFv2:
+ . 27 .reloc .,R_PPC64_TLSGD,%2\n\t
+ . 29 .reloc .,R_PPC64_PLTSEQ,%z1\n\t
+ . 9 crset 2\n\t
+ . 27 .reloc .,R_PPC64_TLSGD,%2\n\t
+ . 30 .reloc .,R_PPC64_PLTCALL,%z1\n\t
+ . 10 beq%T1l-\n\t
+ . 10 ld 2,%3(1)
+ .---
+ .142
+
+ ABI_V4:
+ . 27 .reloc .,R_PPC64_TLSGD,%2\n\t
+ . 35 .reloc .,R_PPC64_PLTSEQ,%z1+32768\n\t
+ . 9 crset 2\n\t
+ . 27 .reloc .,R_PPC64_TLSGD,%2\n\t
+ . 36 .reloc .,R_PPC64_PLTCALL,%z1+32768\n\t
+ . 8 beq%T1l-
+ .---
+ .141 */
+ static char str[160]; /* 8 spare */
+ char *s = str;
+ const char *ptrload = TARGET_64BIT ? "d" : "wz";
+
+ if (DEFAULT_ABI == ABI_AIX)
+ s += sprintf (s,
+ "l%s 2,%%%u\n\t",
+ ptrload, funop + 2);
+
+ /* We don't need the extra code to stop indirect call speculation if
+ calling via LR. */
+ bool speculate = (TARGET_MACHO
+ || rs6000_speculate_indirect_jumps
+ || (REG_P (operands[funop])
+ && REGNO (operands[funop]) == LR_REGNO));
+
+ if (TARGET_PLTSEQ && GET_CODE (operands[funop]) == UNSPEC)
+ {
+ const char *rel64 = TARGET_64BIT ? "64" : "";
+ char tls[29];
+ tls[0] = 0;
+ if (TARGET_TLS_MARKERS && GET_CODE (operands[funop + 1]) == UNSPEC)
+ {
+ if (XINT (operands[funop + 1], 1) == UNSPEC_TLSGD)
+ sprintf (tls, ".reloc .,R_PPC%s_TLSGD,%%%u\n\t",
+ rel64, funop + 1);
+ else if (XINT (operands[funop + 1], 1) == UNSPEC_TLSLD)
+ sprintf (tls, ".reloc .,R_PPC%s_TLSLD,%%&\n\t",
+ rel64);
+ else
+ gcc_unreachable ();
+ }
+
+ const char *addend = (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT
+ && flag_pic == 2 ? "+32768" : "");
+ if (!speculate)
+ {
+ s += sprintf (s,
+ "%s.reloc .,R_PPC%s_PLTSEQ,%%z%u%s\n\t",
+ tls, rel64, funop, addend);
+ s += sprintf (s, "crset 2\n\t");
+ }
+ s += sprintf (s,
+ "%s.reloc .,R_PPC%s_PLTCALL,%%z%u%s\n\t",
+ tls, rel64, funop, addend);
+ }
+ else if (!speculate)
+ s += sprintf (s, "crset 2\n\t");
+
+ if (DEFAULT_ABI == ABI_AIX)
+ {
+ if (speculate)
+ sprintf (s,
+ "b%%T%ul\n\t"
+ "l%s 2,%%%u(1)",
+ funop, ptrload, funop + 3);
+ else
+ sprintf (s,
+ "beq%%T%ul-\n\t"
+ "l%s 2,%%%u(1)",
+ funop, ptrload, funop + 3);
+ }
+ else if (DEFAULT_ABI == ABI_ELFv2)
+ {
+ if (speculate)
+ sprintf (s,
+ "b%%T%ul\n\t"
+ "l%s 2,%%%u(1)",
+ funop, ptrload, funop + 2);
+ else
+ sprintf (s,
+ "beq%%T%ul-\n\t"
+ "l%s 2,%%%u(1)",
+ funop, ptrload, funop + 2);
+ }
+ else
+ {
+ if (speculate)
+ sprintf (s,
+ "b%%T%u%s",
+ funop, sibcall ? "" : "l");
+ else
+ sprintf (s,
+ "beq%%T%u%s-%s",
+ funop, sibcall ? "" : "l", sibcall ? "\n\tb $" : "");
+ }
+ return str;
+}
+
+const char *
+rs6000_indirect_call_template (rtx *operands, unsigned int funop)
+{
+ return rs6000_indirect_call_template_1 (operands, funop, false);
+}
+
+const char *
+rs6000_indirect_sibcall_template (rtx *operands, unsigned int funop)
+{
+ return rs6000_indirect_call_template_1 (operands, funop, true);
+}
+
+#if HAVE_AS_PLTSEQ
+/* Output indirect call insns.
+ WHICH is 0 for tocsave, 1 for plt16_ha, 2 for plt16_lo, 3 for mtctr. */
+const char *
+rs6000_pltseq_template (rtx *operands, int which)
+{
+ const char *rel64 = TARGET_64BIT ? "64" : "";
+ char tls[28];
+ tls[0] = 0;
+ if (TARGET_TLS_MARKERS && GET_CODE (operands[3]) == UNSPEC)
+ {
+ if (XINT (operands[3], 1) == UNSPEC_TLSGD)
+ sprintf (tls, ".reloc .,R_PPC%s_TLSGD,%%3\n\t",
+ rel64);
+ else if (XINT (operands[3], 1) == UNSPEC_TLSLD)
+ sprintf (tls, ".reloc .,R_PPC%s_TLSLD,%%&\n\t",
+ rel64);
+ else
+ gcc_unreachable ();
+ }
+
+ gcc_assert (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4);
+ static char str[96]; /* 15 spare */
+ const char *off = WORDS_BIG_ENDIAN ? "+2" : "";
+ const char *addend = (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT
+ && flag_pic == 2 ? "+32768" : "");
+ switch (which)
+ {
+ case 0:
+ sprintf (str,
+ "%s.reloc .,R_PPC%s_PLTSEQ,%%z2\n\t"
+ "st%s",
+ tls, rel64, TARGET_64BIT ? "d 2,24(1)" : "w 2,12(1)");
+ break;
+ case 1:
+ if (DEFAULT_ABI == ABI_V4 && !flag_pic)
+ sprintf (str,
+ "%s.reloc .%s,R_PPC%s_PLT16_HA,%%z2\n\t"
+ "lis %%0,0",
+ tls, off, rel64);
+ else
+ sprintf (str,
+ "%s.reloc .%s,R_PPC%s_PLT16_HA,%%z2%s\n\t"
+ "addis %%0,%%1,0",
+ tls, off, rel64, addend);
+ break;
+ case 2:
+ sprintf (str,
+ "%s.reloc .%s,R_PPC%s_PLT16_LO%s,%%z2%s\n\t"
+ "l%s %%0,0(%%1)",
+ tls, off, rel64, TARGET_64BIT ? "_DS" : "", addend,
+ TARGET_64BIT ? "d" : "wz");
+ break;
+ case 3:
+ sprintf (str,
+ "%s.reloc .,R_PPC%s_PLTSEQ,%%z2%s\n\t"
+ "mtctr %%1",
+ tls, rel64, addend);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return str;
+}
+#endif
+
#if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
/* Emit an assembler directive to set symbol visibility for DECL to
VISIBILITY_TYPE. */
/* If we have an unsigned compare, make sure we don't have a signed value as
an immediate. */
- if (comp_mode == CCUNSmode && GET_CODE (op1) == CONST_INT
+ if (comp_mode == CCUNSmode && CONST_INT_P (op1)
&& INTVAL (op1) < 0)
{
op0 = copy_rtx_if_shared (op0);
would treat EQ different to UNORDERED, we can't do it. */
if (HONOR_INFINITIES (compare_mode)
&& code != GT && code != UNGE
- && (GET_CODE (op1) != CONST_DOUBLE
+ && (!CONST_DOUBLE_P (op1)
|| real_isinf (CONST_DOUBLE_REAL_VALUE (op1)))
/* Constructs of the form (a OP b ? a : b) are safe. */
&& ((! rtx_equal_p (op0, false_cond) && ! rtx_equal_p (op1, false_cond))
emit_insn (TARGET_32BIT
? (TARGET_POWERPC64
? gen_movdi_si_update (breg, breg, delta_rtx, nsrc)
- : gen_movsi_update (breg, breg, delta_rtx, nsrc))
+ : gen_movsi_si_update (breg, breg, delta_rtx, nsrc))
: gen_movdi_di_update (breg, breg, delta_rtx, nsrc));
used_update = true;
}
static bool
save_reg_p (int reg)
{
- /* We need to mark the PIC offset register live for the same conditions
- as it is set up, or otherwise it won't be saved before we clobber it. */
-
if (reg == RS6000_PIC_OFFSET_TABLE_REGNUM && !TARGET_SINGLE_PIC_BASE)
{
/* When calling eh_return, we must return true for all the cases
where conditional_register_usage marks the PIC offset reg
- call used. */
+ call used or fixed. */
+ if (crtl->calls_eh_return
+ && ((DEFAULT_ABI == ABI_V4 && flag_pic)
+ || (DEFAULT_ABI == ABI_DARWIN && flag_pic)
+ || (TARGET_TOC && TARGET_MINIMAL_TOC)))
+ return true;
+
+ /* We need to mark the PIC offset register live for the same
+ conditions as it is set up in rs6000_emit_prologue, or
+ otherwise it won't be saved before we clobber it. */
if (TARGET_TOC && TARGET_MINIMAL_TOC
- && (crtl->calls_eh_return
- || df_regs_ever_live_p (reg)
- || !constant_pool_empty_p ()))
+ && !constant_pool_empty_p ())
return true;
- if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_DARWIN)
- && flag_pic)
+ if (DEFAULT_ABI == ABI_V4
+ && (flag_pic == 1 || (flag_pic && TARGET_SECURE_PLT))
+ && df_regs_ever_live_p (RS6000_PIC_OFFSET_TABLE_REGNUM))
+ return true;
+
+ if (DEFAULT_ABI == ABI_DARWIN
+ && flag_pic && crtl->uses_pic_offset_table)
return true;
}
if (save_reg_p (first_reg))
break;
-#if TARGET_MACHO
- if (flag_pic
- && crtl->uses_pic_offset_table
- && first_reg > RS6000_PIC_OFFSET_TABLE_REGNUM)
- return RS6000_PIC_OFFSET_TABLE_REGNUM;
-#endif
-
return first_reg;
}
fprintf (stderr, "\tsave-strategy = %04x\n", info->savres_strategy);
+ if (info->abi == ABI_DARWIN)
+ fprintf (stderr, "\tWORLD_SAVE_P = %5d\n", WORLD_SAVE_P(info));
+
fprintf (stderr, "\n");
}
if (TARGET_DEBUG_ADDR)
{
- if (GET_CODE (symbol) == SYMBOL_REF)
+ if (SYMBOL_REF_P (symbol))
fprintf (stderr, "\ncreate_TOC_reference, (symbol_ref %s)\n",
XSTR (symbol, 0));
else
size_rtx = tmp_reg;
}
- if (Pmode == SImode)
+ if (TARGET_32BIT)
insn = emit_insn (gen_movsi_update_stack (stack_pointer_rtx,
stack_pointer_rtx,
size_rtx,
orig_sp));
else
- insn = emit_insn (gen_movdi_di_update_stack (stack_pointer_rtx,
- stack_pointer_rtx,
- size_rtx,
- orig_sp));
+ insn = emit_insn (gen_movdi_update_stack (stack_pointer_rtx,
+ stack_pointer_rtx,
+ size_rtx,
+ orig_sp));
rtx par = PATTERN (insn);
gcc_assert (GET_CODE (par) == PARALLEL);
rtx set = XVECEXP (par, 0, 0);
emit_insn (insn);
emit_insn (gen_cond_trap (LTU, stack_reg, tmp_reg, const0_rtx));
}
- else if (GET_CODE (stack_limit_rtx) == SYMBOL_REF
+ else if (SYMBOL_REF_P (stack_limit_rtx)
&& TARGET_32BIT
&& DEFAULT_ABI == ABI_V4
&& !flag_pic)
if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
{
if (!epiloguep || call_used_regs [i])
- clobs[nclobs++] = gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_REG (V4SImode, i));
+ clobs[nclobs++] = gen_hard_reg_clobber (V4SImode, i);
else
{
rtx reg = gen_rtx_REG (V4SImode, i);
if (!(sel & SAVRES_SAVE) && (sel & SAVRES_LR))
RTVEC_ELT (p, offset++) = ret_rtx;
- RTVEC_ELT (p, offset++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, LR_REGNO));
+ RTVEC_ELT (p, offset++) = gen_hard_reg_clobber (Pmode, LR_REGNO);
sym = rs6000_savres_routine_sym (info, sel);
RTVEC_ELT (p, offset++) = gen_rtx_USE (VOIDmode, sym);
if ((sel & SAVRES_REG) == SAVRES_VR)
{
/* Vector regs are saved/restored using [reg+reg] addressing. */
- RTVEC_ELT (p, offset++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, use_reg));
+ RTVEC_ELT (p, offset++) = gen_hard_reg_clobber (Pmode, use_reg);
RTVEC_ELT (p, offset++)
= gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 0));
}
then the arg pointer is used. */
if (cfun->machine->split_stack_arg_pointer != NULL_RTX
&& (!REG_P (cfun->machine->split_stack_arg_pointer)
- || (REGNO (cfun->machine->split_stack_arg_pointer)
- < FIRST_PSEUDO_REGISTER)))
+ || HARD_REGISTER_P (cfun->machine->split_stack_arg_pointer)))
return true;
/* Unfortunately we also need to do some code scanning, since
sz += LAST_ALTIVEC_REGNO - info->first_altivec_reg_save + 1;
p = rtvec_alloc (sz);
j = 0;
- RTVEC_ELT (p, j++) = gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_REG (SImode,
- LR_REGNO));
+ RTVEC_ELT (p, j++) = gen_hard_reg_clobber (SImode, LR_REGNO);
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode,
gen_rtx_SYMBOL_REF (Pmode,
"*save_world"));
}
/* If we need to save CR, put it into r12 or r11. Choose r12 except when
- r12 will be needed by out-of-line gpr restore. */
+ r12 will be needed by out-of-line gpr save. */
cr_save_regno = ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
&& !(strategy & (SAVE_INLINE_GPRS
| SAVE_NOINLINE_GPRS_SAVES_LR))
rs6000_output_function_prologue (FILE *file)
{
if (!cfun->is_thunk)
- rs6000_output_savres_externs (file);
+ {
+ rs6000_output_savres_externs (file);
+#ifdef USING_ELFOS_H
+ const char *curr_machine = rs6000_machine_from_flags ();
+ if (rs6000_machine != curr_machine)
+ {
+ rs6000_machine = curr_machine;
+ emit_asm_machine ();
+ }
+#endif
+ }
/* ELFv2 ABI r2 setup code and local entry point. This must follow
immediately after the global entry point label. */
/* Reload CR from REG. */
static void
-restore_saved_cr (rtx reg, int using_mfcr_multiple, bool exit_func)
+restore_saved_cr (rtx reg, bool using_mfcr_multiple, bool exit_func)
{
int count = 0;
int i;
/* Emit function epilogue as insns. */
void
-rs6000_emit_epilogue (int sibcall)
-{
- rs6000_stack_t *info;
- int restoring_GPRs_inline;
- int restoring_FPRs_inline;
- int using_load_multiple;
- int using_mtcr_multiple;
- int use_backchain_to_restore_sp;
- int restore_lr;
- int strategy;
+rs6000_emit_epilogue (enum epilogue_type epilogue_type)
+{
HOST_WIDE_INT frame_off = 0;
rtx sp_reg_rtx = gen_rtx_REG (Pmode, 1);
rtx frame_reg_rtx = sp_reg_rtx;
machine_mode fp_reg_mode = TARGET_HARD_FLOAT ? DFmode : SFmode;
int fp_reg_size = 8;
int i;
- bool exit_func;
unsigned ptr_regno;
- info = rs6000_stack_info ();
+ rs6000_stack_t *info = rs6000_stack_info ();
+
+ if (epilogue_type == EPILOGUE_TYPE_NORMAL && crtl->calls_eh_return)
+ epilogue_type = EPILOGUE_TYPE_EH_RETURN;
+
+ int strategy = info->savres_strategy;
+ bool using_load_multiple = !!(strategy & REST_MULTIPLE);
+ bool restoring_GPRs_inline = !!(strategy & REST_INLINE_GPRS);
+ bool restoring_FPRs_inline = !!(strategy & REST_INLINE_FPRS);
+ if (epilogue_type == EPILOGUE_TYPE_SIBCALL)
+ {
+ restoring_GPRs_inline = true;
+ restoring_FPRs_inline = true;
+ }
+
+ bool using_mtcr_multiple = (rs6000_tune == PROCESSOR_PPC601
+ || rs6000_tune == PROCESSOR_PPC603
+ || rs6000_tune == PROCESSOR_PPC750
+ || optimize_size);
- strategy = info->savres_strategy;
- using_load_multiple = strategy & REST_MULTIPLE;
- restoring_FPRs_inline = sibcall || (strategy & REST_INLINE_FPRS);
- restoring_GPRs_inline = sibcall || (strategy & REST_INLINE_GPRS);
- using_mtcr_multiple = (rs6000_tune == PROCESSOR_PPC601
- || rs6000_tune == PROCESSOR_PPC603
- || rs6000_tune == PROCESSOR_PPC750
- || optimize_size);
/* Restore via the backchain when we have a large frame, since this
is more efficient than an addis, addi pair. The second condition
here will not trigger at the moment; We don't actually need a
frame pointer for alloca, but the generic parts of the compiler
give us one anyway. */
- use_backchain_to_restore_sp = (info->total_size + (info->lr_save_p
- ? info->lr_save_offset
- : 0) > 32767
- || (cfun->calls_alloca
- && !frame_pointer_needed));
- restore_lr = (info->lr_save_p
+ bool use_backchain_to_restore_sp
+ = (info->total_size + (info->lr_save_p ? info->lr_save_offset : 0) > 32767
+ || (cfun->calls_alloca && !frame_pointer_needed));
+
+ bool restore_lr = (info->lr_save_p
&& (restoring_FPRs_inline
|| (strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR))
&& (restoring_GPRs_inline
if (WORLD_SAVE_P (info))
{
- int i, j;
- char rname[30];
- const char *alloc_rname;
- rtvec p;
+ gcc_assert (epilogue_type != EPILOGUE_TYPE_SIBCALL);
/* eh_rest_world_r10 will return to the location saved in the LR
stack slot (which is not likely to be our caller.)
The exception-handling stuff that was here in 2.95 is no
longer necessary. */
+ rtvec p;
p = rtvec_alloc (9
+ 32 - info->first_gp_reg_save
+ LAST_ALTIVEC_REGNO + 1 - info->first_altivec_reg_save
+ 63 + 1 - info->first_fp_reg_save);
- strcpy (rname, ((crtl->calls_eh_return) ?
- "*eh_rest_world_r10" : "*rest_world"));
- alloc_rname = ggc_strdup (rname);
+ const char *rname;
+ switch (epilogue_type)
+ {
+ case EPILOGUE_TYPE_NORMAL:
+ rname = ggc_strdup ("*rest_world");
+ break;
- j = 0;
+ case EPILOGUE_TYPE_EH_RETURN:
+ rname = ggc_strdup ("*eh_rest_world_r10");
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ int j = 0;
RTVEC_ELT (p, j++) = ret_rtx;
RTVEC_ELT (p, j++)
- = gen_rtx_USE (VOIDmode, gen_rtx_SYMBOL_REF (Pmode, alloc_rname));
+ = gen_rtx_USE (VOIDmode, gen_rtx_SYMBOL_REF (Pmode, rname));
/* The instruction pattern requires a clobber here;
it is shared with the restVEC helper. */
- RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 11));
+ RTVEC_ELT (p, j++) = gen_hard_reg_clobber (Pmode, 11);
{
/* CR register traditionally saved as CR2. */
}
}
+ int i;
for (i = 0; i < 32 - info->first_gp_reg_save; i++)
{
rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
&& save_reg_p (info->first_fp_reg_save + i))
cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
}
- RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 0));
- RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 12));
- RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 7));
- RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 8));
+ RTVEC_ELT (p, j++) = gen_hard_reg_clobber (Pmode, 0);
+ RTVEC_ELT (p, j++) = gen_hard_reg_clobber (SImode, 12);
+ RTVEC_ELT (p, j++) = gen_hard_reg_clobber (SImode, 7);
+ RTVEC_ELT (p, j++) = gen_hard_reg_clobber (SImode, 8);
RTVEC_ELT (p, j++)
= gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, 10));
insn = emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
}
else if (info->push_p
&& DEFAULT_ABI != ABI_V4
- && !crtl->calls_eh_return)
+ && epilogue_type != EPILOGUE_TYPE_EH_RETURN)
{
/* Prevent reordering memory accesses against stack pointer restore. */
if (cfun->calls_alloca
function will deallocate the stack, so we don't need to worry
about the unwinder restoring cr from an invalid stack frame
location. */
- exit_func = (!restoring_FPRs_inline
- || (!restoring_GPRs_inline
- && info->first_fp_reg_save == 64));
+ bool exit_func = (!restoring_FPRs_inline
+ || (!restoring_GPRs_inline
+ && info->first_fp_reg_save == 64));
/* In the ELFv2 ABI we need to restore all call-saved CR fields from
*separate* slots if the routine calls __builtin_eh_return, so
restore_saved_lr (0, exit_func);
/* Load exception handler data registers, if needed. */
- if (crtl->calls_eh_return)
+ if (epilogue_type == EPILOGUE_TYPE_EH_RETURN)
{
unsigned int i, regno;
RTX_FRAME_RELATED_P (insn) = 1;
}
- if (crtl->calls_eh_return)
+ if (epilogue_type == EPILOGUE_TYPE_EH_RETURN)
{
rtx sa = EH_RETURN_STACKADJ_RTX;
emit_insn (gen_add3_insn (sp_reg_rtx, sp_reg_rtx, sa));
}
- if (!sibcall && restoring_FPRs_inline)
+ if (epilogue_type != EPILOGUE_TYPE_SIBCALL && restoring_FPRs_inline)
{
if (cfa_restores)
{
emit_jump_insn (targetm.gen_simple_return ());
}
- if (!sibcall && !restoring_FPRs_inline)
+ if (epilogue_type != EPILOGUE_TYPE_SIBCALL && !restoring_FPRs_inline)
{
bool lr = (strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
rtvec p = rtvec_alloc (3 + !!lr + 64 - info->first_fp_reg_save);
int elt = 0;
RTVEC_ELT (p, elt++) = ret_rtx;
if (lr)
- RTVEC_ELT (p, elt++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, LR_REGNO));
+ RTVEC_ELT (p, elt++) = gen_hard_reg_clobber (Pmode, LR_REGNO);
/* We have to restore more than two FP registers, so branch to the
restore function. It will return to our caller. */
if (cfa_restores)
{
- if (sibcall)
+ if (epilogue_type == EPILOGUE_TYPE_SIBCALL)
/* Ensure the cfa_restores are hung off an insn that won't
be reordered above other restores. */
emit_insn (gen_blockage ());
length fields that follow. However, if you omit the optional
fields, the assembler outputs zeros for all optional fields
anyways, giving each variable length field is minimum length
- (as defined in sys/debug.h). Thus we can not use the .tbtab
+ (as defined in sys/debug.h). Thus we cannot use the .tbtab
pseudo-op at all. */
/* An all-zero word flags the start of the tbtab, for debuggers
use language_string.
C is 0. Fortran is 1. Ada is 3. C++ is 9.
Java is 13. Objective-C is 14. Objective-C++ isn't assigned
- a number, so for now use 9. LTO, Go and JIT aren't assigned numbers
- either, so for now use 0. */
+ a number, so for now use 9. LTO, Go, D, and JIT aren't assigned
+ numbers either, so for now use 0. */
if (lang_GNU_C ()
|| ! strcmp (language_string, "GNU GIMPLE")
|| ! strcmp (language_string, "GNU Go")
+ || ! strcmp (language_string, "GNU D")
|| ! strcmp (language_string, "libgccjit"))
i = 0;
else if (! strcmp (language_string, "GNU F77")
rtx parameter = DECL_INCOMING_RTL (decl);
machine_mode mode = GET_MODE (parameter);
- if (GET_CODE (parameter) == REG)
+ if (REG_P (parameter))
{
if (SCALAR_FLOAT_MODE_P (mode))
{
if (global_regs[29])
{
- error ("%qs uses register r29", "-fsplit-stack");
+ error ("%qs uses register r29", "%<-fsplit-stack%>");
inform (DECL_SOURCE_LOCATION (global_regs_decl[29]),
"conflicts with %qD", global_regs_decl[29]);
}
allocate = info->total_size;
if (allocate > (unsigned HOST_WIDE_INT) 1 << 31)
{
- sorry ("Stack frame larger than 2G is not supported for -fsplit-stack");
+ sorry ("Stack frame larger than 2G is not supported for "
+ "%<-fsplit-stack%>");
return;
}
if (morestack_ref == NULL_RTX)
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
tree function)
{
+ const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
rtx this_rtx, funexp;
rtx_insn *insn;
assemble_start_function and assemble_end_function. */
insn = get_insns ();
shorten_branches (insn);
+ assemble_start_function (thunk_fndecl, fnname);
final_start_function (insn, file, 1);
final (insn, file, 1);
final_end_function ();
+ assemble_end_function (thunk_fndecl, fnname);
reload_completed = 0;
epilogue_completed = 0;
}
case CONST_DOUBLE:
- if (mode != VOIDmode)
- return real_hash (CONST_DOUBLE_REAL_VALUE (k)) * result;
- flen = 2;
- break;
+ return real_hash (CONST_DOUBLE_REAL_VALUE (k)) * result;
case CODE_LABEL:
fidx = 3;
fprintf (file, "%d\n", ((*found)->labelno));
#ifdef HAVE_AS_TLS
- if (TARGET_XCOFF && GET_CODE (x) == SYMBOL_REF
+ if (TARGET_XCOFF && SYMBOL_REF_P (x)
&& (SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_GLOBAL_DYNAMIC
|| SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC))
{
/* Handle FP constants specially. Note that if we have a minimal
TOC, things we put here aren't actually in the TOC, so we can allow
FP constants. */
- if (GET_CODE (x) == CONST_DOUBLE &&
- (GET_MODE (x) == TFmode || GET_MODE (x) == TDmode
- || GET_MODE (x) == IFmode || GET_MODE (x) == KFmode))
+ if (CONST_DOUBLE_P (x)
+ && (GET_MODE (x) == TFmode || GET_MODE (x) == TDmode
+ || GET_MODE (x) == IFmode || GET_MODE (x) == KFmode))
{
long k[4];
return;
}
}
- else if (GET_CODE (x) == CONST_DOUBLE &&
- (GET_MODE (x) == DFmode || GET_MODE (x) == DDmode))
+ else if (CONST_DOUBLE_P (x)
+ && (GET_MODE (x) == DFmode || GET_MODE (x) == DDmode))
{
long k[2];
return;
}
}
- else if (GET_CODE (x) == CONST_DOUBLE &&
- (GET_MODE (x) == SFmode || GET_MODE (x) == SDmode))
+ else if (CONST_DOUBLE_P (x)
+ && (GET_MODE (x) == SFmode || GET_MODE (x) == SDmode))
{
long l;
return;
}
}
- else if (GET_MODE (x) == VOIDmode && GET_CODE (x) == CONST_INT)
+ else if (GET_MODE (x) == VOIDmode && CONST_INT_P (x))
{
unsigned HOST_WIDE_INT low;
HOST_WIDE_INT high;
if (GET_CODE (x) == CONST)
{
gcc_assert (GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT);
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1)));
base = XEXP (XEXP (x, 0), 0);
offset = INTVAL (XEXP (XEXP (x, 0), 1));
output_addr_const (file, x);
#if HAVE_AS_TLS
- if (TARGET_XCOFF && GET_CODE (base) == SYMBOL_REF)
+ if (TARGET_XCOFF && SYMBOL_REF_P (base))
{
switch (SYMBOL_REF_TLS_MODEL (base))
{
some cycles later. */
/* Separate a load from a narrower, dependent store. */
- if ((rs6000_sched_groups || rs6000_tune == PROCESSOR_POWER9)
+ if ((rs6000_sched_groups || rs6000_tune == PROCESSOR_POWER9
+ || rs6000_tune == PROCESSOR_FUTURE)
&& GET_CODE (PATTERN (insn)) == SET
&& GET_CODE (PATTERN (dep_insn)) == SET
- && GET_CODE (XEXP (PATTERN (insn), 1)) == MEM
- && GET_CODE (XEXP (PATTERN (dep_insn), 0)) == MEM
+ && MEM_P (XEXP (PATTERN (insn), 1))
+ && MEM_P (XEXP (PATTERN (dep_insn), 0))
&& (GET_MODE_SIZE (GET_MODE (XEXP (PATTERN (insn), 1)))
> GET_MODE_SIZE (GET_MODE (XEXP (PATTERN (dep_insn), 0)))))
return cost + 14;
|| rs6000_tune == PROCESSOR_POWER7
|| rs6000_tune == PROCESSOR_POWER8
|| rs6000_tune == PROCESSOR_POWER9
+ || rs6000_tune == PROCESSOR_FUTURE
|| rs6000_tune == PROCESSOR_CELL)
&& recog_memoized (dep_insn)
&& (INSN_CODE (dep_insn) >= 0))
case PROCESSOR_POWER8:
return 7;
case PROCESSOR_POWER9:
+ case PROCESSOR_FUTURE:
return 6;
default:
return 1;
if (tie_operand (pat, VOIDmode))
return false;
- if (GET_CODE (pat) == MEM)
+ if (MEM_P (pat))
{
*mem_ref = pat;
return true;
/* Return a reference suitable for calling a function with the
longcall attribute. */
-rtx
-rs6000_longcall_ref (rtx call_ref)
+static rtx
+rs6000_longcall_ref (rtx call_ref, rtx arg)
{
- const char *call_name;
- tree node;
-
- if (GET_CODE (call_ref) != SYMBOL_REF)
- return call_ref;
-
/* System V adds '.' to the internal name, so skip them. */
- call_name = XSTR (call_ref, 0);
+ const char *call_name = XSTR (call_ref, 0);
if (*call_name == '.')
{
while (*call_name == '.')
call_name++;
- node = get_identifier (call_name);
+ tree node = get_identifier (call_name);
call_ref = gen_rtx_SYMBOL_REF (VOIDmode, IDENTIFIER_POINTER (node));
}
+ if (TARGET_PLTSEQ)
+ {
+ rtx base = const0_rtx;
+ int regno;
+ if (DEFAULT_ABI == ABI_ELFv2)
+ {
+ base = gen_rtx_REG (Pmode, TOC_REGISTER);
+ regno = 12;
+ }
+ else
+ {
+ if (flag_pic)
+ base = gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM);
+ regno = 11;
+ }
+ /* Reg must match that used by linker PLT stubs. For ELFv2, r12
+ may be used by a function global entry point. For SysV4, r11
+ is used by __glink_PLTresolve lazy resolver entry. */
+ rtx reg = gen_rtx_REG (Pmode, regno);
+ rtx hi = gen_rtx_UNSPEC (Pmode, gen_rtvec (3, base, call_ref, arg),
+ UNSPEC_PLT16_HA);
+ rtx lo = gen_rtx_UNSPEC (Pmode, gen_rtvec (3, reg, call_ref, arg),
+ UNSPEC_PLT16_LO);
+ emit_insn (gen_rtx_SET (reg, hi));
+ emit_insn (gen_rtx_SET (reg, lo));
+ return reg;
+ }
+
return force_reg (Pmode, call_ref);
}
\f
{
while (GET_CODE (addr) == PLUS)
{
- if (GET_CODE (XEXP (addr, 0)) == REG
+ if (REG_P (XEXP (addr, 0))
&& REGNO (XEXP (addr, 0)) != 0)
addr = XEXP (addr, 0);
- else if (GET_CODE (XEXP (addr, 1)) == REG
+ else if (REG_P (XEXP (addr, 1))
&& REGNO (XEXP (addr, 1)) != 0)
addr = XEXP (addr, 1);
else if (CONSTANT_P (XEXP (addr, 0)))
else
gcc_unreachable ();
}
- gcc_assert (GET_CODE (addr) == REG && REGNO (addr) != 0);
+ gcc_assert (REG_P (addr) && REGNO (addr) != 0);
return addr;
}
}
else
{
- strcat (tmp_buf, ":\nlis r12,hi16(");
+ strcat (tmp_buf, ":\n\tlis r12,hi16(");
strcat (tmp_buf, name_buf);
strcat (tmp_buf, ")\n\tori r12,r12,lo16(");
strcat (tmp_buf, name_buf);
return NULL_TREE;
}
-/* INSN is either a function call or a millicode call. It may have an
- unconditional jump in its delay slot.
-
- CALL_DEST is the routine we are calling. */
-
-char *
-output_call (rtx_insn *insn, rtx *operands, int dest_operand_number,
- int cookie_operand_number)
-{
- static char buf[256];
- if (darwin_emit_branch_islands
- && GET_CODE (operands[dest_operand_number]) == SYMBOL_REF
- && (INTVAL (operands[cookie_operand_number]) & CALL_LONG))
- {
- tree labelname;
- tree funname = get_identifier (XSTR (operands[dest_operand_number], 0));
-
- if (no_previous_def (funname))
- {
- rtx label_rtx = gen_label_rtx ();
- char *label_buf, temp_buf[256];
- ASM_GENERATE_INTERNAL_LABEL (temp_buf, "L",
- CODE_LABEL_NUMBER (label_rtx));
- label_buf = temp_buf[0] == '*' ? temp_buf + 1 : temp_buf;
- labelname = get_identifier (label_buf);
- add_compiler_branch_island (labelname, funname, insn_line (insn));
- }
- else
- labelname = get_prev_label (funname);
-
- /* "jbsr foo, L42" is Mach-O for "Link as 'bl foo' if a 'bl'
- instruction will reach 'foo', otherwise link as 'bl L42'".
- "L42" should be a 'branch island', that will do a far jump to
- 'foo'. Branch islands are generated in
- macho_branch_islands(). */
- sprintf (buf, "jbsr %%z%d,%.246s",
- dest_operand_number, IDENTIFIER_POINTER (labelname));
- }
- else
- sprintf (buf, "bl %%z%d", dest_operand_number);
- return buf;
-}
-
/* Generate PIC and indirect symbol stubs. */
void
unsigned int length;
char *symbol_name, *lazy_ptr_name;
char *local_label_0;
- static int label = 0;
+ static unsigned label = 0;
/* Lose our funky encoding stuff so it doesn't contaminate the stub. */
symb = (*targetm.strip_name_encoding) (symb);
fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
label++;
- local_label_0 = XALLOCAVEC (char, sizeof ("\"L00000000000$spb\""));
- sprintf (local_label_0, "\"L%011d$spb\"", label);
+ local_label_0 = XALLOCAVEC (char, 16);
+ sprintf (local_label_0, "L%u$spb", label);
fprintf (file, "\tmflr r0\n");
if (TARGET_LINK_STACK)
rs6000_machopic_legitimize_pic_address (XEXP (XEXP (orig, 0), 1),
Pmode, reg);
- if (GET_CODE (offset) == CONST_INT)
+ if (CONST_INT_P (offset))
{
if (SMALL_INT (offset))
return plus_constant (Pmode, base, INTVAL (offset));
rs6000_xcoff_output_readwrite_section_asm_op,
&xcoff_private_data_section_name);
+ read_only_private_data_section
+ = get_unnamed_section (0, rs6000_xcoff_output_readonly_section_asm_op,
+ &xcoff_private_rodata_section_name);
+
tls_data_section
= get_unnamed_section (SECTION_TLS,
rs6000_xcoff_output_tls_section_asm_op,
rs6000_xcoff_output_tls_section_asm_op,
&xcoff_private_data_section_name);
- read_only_private_data_section
- = get_unnamed_section (0, rs6000_xcoff_output_readonly_section_asm_op,
- &xcoff_private_data_section_name);
-
toc_section
= get_unnamed_section (0, rs6000_xcoff_output_toc_section_asm_op, NULL);
main_input_filename, ".bss_");
rs6000_gen_section_name (&xcoff_private_data_section_name,
main_input_filename, ".rw_");
+ rs6000_gen_section_name (&xcoff_private_rodata_section_name,
+ main_input_filename, ".rop_");
rs6000_gen_section_name (&xcoff_read_only_section_name,
main_input_filename, ".ro_");
rs6000_gen_section_name (&xcoff_tls_data_section_name,
if (!MEM_P (rtl))
return;
symbol = XEXP (rtl, 0);
- if (GET_CODE (symbol) != SYMBOL_REF)
+ if (!SYMBOL_REF_P (symbol))
return;
flags = SYMBOL_REF_FLAGS (symbol);
return false;
case MULT:
- if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ if (CONST_INT_P (XEXP (x, 1))
&& satisfies_constraint_I (XEXP (x, 1)))
{
if (INTVAL (XEXP (x, 1)) >= -256
case UDIV:
case UMOD:
- if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ if (CONST_INT_P (XEXP (x, 1))
&& exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
{
if (code == DIV || code == MOD)
case SIGN_EXTEND:
case ZERO_EXTEND:
- if (GET_CODE (XEXP (x, 0)) == MEM)
+ if (MEM_P (XEXP (x, 0)))
*total = 0;
else
*total = COSTS_N_INSNS (1);
reg_class_t from, reg_class_t to)
{
int ret;
+ reg_class_t rclass;
if (TARGET_DEBUG_COST)
dbg_cost_ctrl++;
+ /* If we have VSX, we can easily move between FPR or Altivec registers,
+ otherwise we can only easily move within classes.
+ Do this first so we give best-case answers for union classes
+ containing both gprs and vsx regs. */
+ HARD_REG_SET to_vsx, from_vsx;
+ COPY_HARD_REG_SET (to_vsx, reg_class_contents[to]);
+ AND_HARD_REG_SET (to_vsx, reg_class_contents[VSX_REGS]);
+ COPY_HARD_REG_SET (from_vsx, reg_class_contents[from]);
+ AND_HARD_REG_SET (from_vsx, reg_class_contents[VSX_REGS]);
+ if (!hard_reg_set_empty_p (to_vsx)
+ && !hard_reg_set_empty_p (from_vsx)
+ && (TARGET_VSX
+ || hard_reg_set_intersect_p (to_vsx, from_vsx)))
+ {
+ int reg = FIRST_FPR_REGNO;
+ if (TARGET_VSX
+ || (TEST_HARD_REG_BIT (to_vsx, FIRST_ALTIVEC_REGNO)
+ && TEST_HARD_REG_BIT (from_vsx, FIRST_ALTIVEC_REGNO)))
+ reg = FIRST_ALTIVEC_REGNO;
+ ret = 2 * hard_regno_nregs (reg, mode);
+ }
+
/* Moves from/to GENERAL_REGS. */
- if (reg_classes_intersect_p (to, GENERAL_REGS)
- || reg_classes_intersect_p (from, GENERAL_REGS))
+ else if ((rclass = from, reg_classes_intersect_p (to, GENERAL_REGS))
+ || (rclass = to, reg_classes_intersect_p (from, GENERAL_REGS)))
{
- reg_class_t rclass = from;
-
- if (! reg_classes_intersect_p (to, GENERAL_REGS))
- rclass = to;
-
if (rclass == FLOAT_REGS || rclass == ALTIVEC_REGS || rclass == VSX_REGS)
- ret = (rs6000_memory_move_cost (mode, rclass, false)
- + rs6000_memory_move_cost (mode, GENERAL_REGS, false));
+ {
+ if (TARGET_DIRECT_MOVE)
+ {
+ /* Keep the cost for direct moves above that for within
+ a register class even if the actual processor cost is
+ comparable. We do this because a direct move insn
+ can't be a nop, whereas with ideal register
+ allocation a move within the same class might turn
+ out to be a nop. */
+ if (rs6000_tune == PROCESSOR_POWER9
+ || rs6000_tune == PROCESSOR_FUTURE)
+ ret = 3 * hard_regno_nregs (FIRST_GPR_REGNO, mode);
+ else
+ ret = 4 * hard_regno_nregs (FIRST_GPR_REGNO, mode);
+ /* SFmode requires a conversion when moving between gprs
+ and vsx. */
+ if (mode == SFmode)
+ ret += 2;
+ }
+ else
+ ret = (rs6000_memory_move_cost (mode, rclass, false)
+ + rs6000_memory_move_cost (mode, GENERAL_REGS, false));
+ }
/* It's more expensive to move CR_REGS than CR0_REGS because of the
shift. */
|| rs6000_tune == PROCESSOR_POWER7
|| rs6000_tune == PROCESSOR_POWER8
|| rs6000_tune == PROCESSOR_POWER9)
- && reg_classes_intersect_p (rclass, LINK_OR_CTR_REGS))
- ret = 6 * hard_regno_nregs (0, mode);
+ && reg_class_subset_p (rclass, SPECIAL_REGS))
+ ret = 6 * hard_regno_nregs (FIRST_GPR_REGNO, mode);
else
/* A move will cost one instruction per GPR moved. */
- ret = 2 * hard_regno_nregs (0, mode);
+ ret = 2 * hard_regno_nregs (FIRST_GPR_REGNO, mode);
}
- /* If we have VSX, we can easily move between FPR or Altivec registers. */
- else if (VECTOR_MEM_VSX_P (mode)
- && reg_classes_intersect_p (to, VSX_REGS)
- && reg_classes_intersect_p (from, VSX_REGS))
- ret = 2 * hard_regno_nregs (FIRST_FPR_REGNO, mode);
-
- /* Moving between two similar registers is just one instruction. */
- else if (reg_classes_intersect_p (to, from))
- ret = (FLOAT128_2REG_P (mode)) ? 4 : 2;
-
/* Everything else has to go through GENERAL_REGS. */
else
ret = (rs6000_register_move_cost (mode, GENERAL_REGS, to)
{
if (dbg_cost_ctrl == 1)
fprintf (stderr,
- "rs6000_register_move_cost:, ret=%d, mode=%s, from=%s, to=%s\n",
+ "rs6000_register_move_cost: ret=%d, mode=%s, from=%s, to=%s\n",
ret, GET_MODE_NAME (mode), reg_class_names[from],
reg_class_names[to]);
dbg_cost_ctrl--;
return ret;
}
+/* Implement TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS.
+
+ The register allocator chooses GEN_OR_VSX_REGS for the allocno
+ class if GENERAL_REGS and VSX_REGS cost is lower than the memory
+ cost. This happens a lot when TARGET_DIRECT_MOVE makes the register
+ move cost between GENERAL_REGS and VSX_REGS low.
+
+ It might seem reasonable to use a union class. After all, if usage
+ of vsr is low and gpr high, it might make sense to spill gpr to vsr
+ rather than memory. However, in cases where register pressure of
+ both is high, like the cactus_adm spec test, allowing
+ GEN_OR_VSX_REGS as the allocno class results in bad decisions in
+ the first scheduling pass. This is partly due to an allocno of
+ GEN_OR_VSX_REGS wrongly contributing to the GENERAL_REGS pressure
+ class, which gives too high a pressure for GENERAL_REGS and too low
+ for VSX_REGS. So, force a choice of the subclass here.
+
+ The best class is also the union if GENERAL_REGS and VSX_REGS have
+ the same cost. In that case we do use GEN_OR_VSX_REGS as the
+ allocno class, since trying to narrow down the class by regno mode
+ is prone to error. For example, SImode is allowed in VSX regs and
+ in some cases (eg. gcc.target/powerpc/p9-xxbr-3.c do_bswap32_vect)
+ it would be wrong to choose an allocno of GENERAL_REGS based on
+ SImode. */
+
+static reg_class_t
+rs6000_ira_change_pseudo_allocno_class (int regno ATTRIBUTE_UNUSED,
+ reg_class_t allocno_class,
+ reg_class_t best_class)
+{
+ switch (allocno_class)
+ {
+ case GEN_OR_VSX_REGS:
+ /* best_class must be a subset of allocno_class. */
+ gcc_checking_assert (best_class == GEN_OR_VSX_REGS
+ || best_class == GEN_OR_FLOAT_REGS
+ || best_class == VSX_REGS
+ || best_class == ALTIVEC_REGS
+ || best_class == FLOAT_REGS
+ || best_class == GENERAL_REGS
+ || best_class == BASE_REGS);
+ /* Use best_class but choose wider classes when copying from the
+ wider class to best_class is cheap. This mimics IRA choice
+ of allocno class. */
+ if (best_class == BASE_REGS)
+ return GENERAL_REGS;
+ if (TARGET_VSX
+ && (best_class == FLOAT_REGS || best_class == ALTIVEC_REGS))
+ return VSX_REGS;
+ return best_class;
+
+ default:
+ break;
+ }
+
+ return allocno_class;
+}
+
/* Returns a code for a target-specific builtin that implements
reciprocal of the function, or NULL_TREE if not available. */
numbering) are what we need. */
if (!BYTES_BIG_ENDIAN
&& icode == CODE_FOR_altivec_vpkuwum_direct
- && ((GET_CODE (op0) == REG
+ && ((REG_P (op0)
&& GET_MODE (op0) != V4SImode)
- || (GET_CODE (op0) == SUBREG
+ || (SUBREG_P (op0)
&& GET_MODE (XEXP (op0, 0)) != V4SImode)))
continue;
if (!BYTES_BIG_ENDIAN
&& icode == CODE_FOR_altivec_vpkuhum_direct
- && ((GET_CODE (op0) == REG
+ && ((REG_P (op0)
&& GET_MODE (op0) != V8HImode)
- || (GET_CODE (op0) == SUBREG
+ || (SUBREG_P (op0)
&& GET_MODE (XEXP (op0, 0)) != V8HImode)))
continue;
}
/* Compute register pressure classes. We implement the target hook to avoid
- IRA picking something like NON_SPECIAL_REGS as a pressure class, which can
+ IRA picking something like GEN_OR_FLOAT_REGS as a pressure class, which can
lead to incorrect estimates of number of available registers and therefor
increased register pressure/spill. */
static int
unsigned int
rs6000_dbx_register_number (unsigned int regno, unsigned int format)
{
- /* Except for the above, we use the internal number for non-DWARF
- debug information, and also for .eh_frame. */
- if ((format == 0 && write_symbols != DWARF2_DEBUG) || format == 2)
- return regno;
-
/* On some platforms, we use the standard DWARF register
numbering for .debug_info and .debug_frame. */
+ if ((format == 0 && write_symbols == DWARF2_DEBUG) || format == 1)
+ {
#ifdef RS6000_USE_DWARF_NUMBERING
- if (regno <= 63)
+ if (regno <= 31)
+ return regno;
+ if (FP_REGNO_P (regno))
+ return regno - FIRST_FPR_REGNO + 32;
+ if (ALTIVEC_REGNO_P (regno))
+ return regno - FIRST_ALTIVEC_REGNO + 1124;
+ if (regno == LR_REGNO)
+ return 108;
+ if (regno == CTR_REGNO)
+ return 109;
+ if (regno == CA_REGNO)
+ return 101; /* XER */
+ /* Special handling for CR for .debug_frame: rs6000_emit_prologue has
+ translated any combination of CR2, CR3, CR4 saves to a save of CR2.
+ The actual code emitted saves the whole of CR, so we map CR2_REGNO
+ to the DWARF reg for CR. */
+ if (format == 1 && regno == CR2_REGNO)
+ return 64;
+ if (CR_REGNO_P (regno))
+ return regno - CR0_REGNO + 86;
+ if (regno == VRSAVE_REGNO)
+ return 356;
+ if (regno == VSCR_REGNO)
+ return 67;
+
+ /* These do not make much sense. */
+ if (regno == FRAME_POINTER_REGNUM)
+ return 111;
+ if (regno == ARG_POINTER_REGNUM)
+ return 67;
+ if (regno == 64)
+ return 100;
+
+ gcc_unreachable ();
+#endif
+ }
+
+ /* We use the GCC 7 (and before) internal number for non-DWARF debug
+ information, and also for .eh_frame. */
+ /* Translate the regnos to their numbers in GCC 7 (and before). */
+ if (regno <= 31)
return regno;
+ if (FP_REGNO_P (regno))
+ return regno - FIRST_FPR_REGNO + 32;
+ if (ALTIVEC_REGNO_P (regno))
+ return regno - FIRST_ALTIVEC_REGNO + 77;
if (regno == LR_REGNO)
- return 108;
+ return 65;
if (regno == CTR_REGNO)
- return 109;
- /* Special handling for CR for .debug_frame: rs6000_emit_prologue has
- translated any combination of CR2, CR3, CR4 saves to a save of CR2.
- The actual code emitted saves the whole of CR, so we map CR2_REGNO
- to the DWARF reg for CR. */
- if (format == 1 && regno == CR2_REGNO)
- return 64;
- if (CR_REGNO_P (regno))
- return regno - CR0_REGNO + 86;
+ return 66;
if (regno == CA_REGNO)
- return 101; /* XER */
- if (ALTIVEC_REGNO_P (regno))
- return regno - FIRST_ALTIVEC_REGNO + 1124;
+ return 76; /* XER */
+ if (CR_REGNO_P (regno))
+ return regno - CR0_REGNO + 68;
if (regno == VRSAVE_REGNO)
- return 356;
+ return 109;
if (regno == VSCR_REGNO)
+ return 110;
+
+ if (regno == FRAME_POINTER_REGNUM)
+ return 111;
+ if (regno == ARG_POINTER_REGNUM)
return 67;
-#endif
- return regno;
+ if (regno == 64)
+ return 64;
+
+ gcc_unreachable ();
}
/* target hook eh_return_filter_mode */
{ "float128", OPTION_MASK_FLOAT128_KEYWORD, false, true },
{ "float128-hardware", OPTION_MASK_FLOAT128_HW, false, true },
{ "fprnd", OPTION_MASK_FPRND, false, true },
+ { "future", OPTION_MASK_FUTURE, false, true },
{ "hard-dfp", OPTION_MASK_DFP, false, true },
{ "htm", OPTION_MASK_HTM, false, true },
{ "isel", OPTION_MASK_ISEL, false, true },
{ "modulo", OPTION_MASK_MODULO, false, true },
{ "mulhw", OPTION_MASK_MULHW, false, true },
{ "multiple", OPTION_MASK_MULTIPLE, false, true },
+ { "pcrel", OPTION_MASK_PCREL, false, true },
{ "popcntb", OPTION_MASK_POPCNTB, false, true },
{ "popcntd", OPTION_MASK_POPCNTD, false, true },
{ "power8-fusion", OPTION_MASK_P8_FUSION, false, true },
{ "power8-fusion-sign", OPTION_MASK_P8_FUSION_SIGN, false, true },
{ "power8-vector", OPTION_MASK_P8_VECTOR, false, true },
- { "power9-fusion", OPTION_MASK_P9_FUSION, false, true },
{ "power9-minmax", OPTION_MASK_P9_MINMAX, false, true },
{ "power9-misc", OPTION_MASK_P9_MISC, false, true },
{ "power9-vector", OPTION_MASK_P9_VECTOR, false, true },
#ifndef TARGET_LIBC_PROVIDES_HWCAP_IN_TCB
error_at (DECL_SOURCE_LOCATION (default_node->decl),
- "target_clones attribute needs GLIBC (2.23 and newer) that "
+ "%<target_clones%> attribute needs GLIBC (2.23 and newer) that "
"exports hardware capability bits");
#else
/* Build result decl and add to function_decl. */
tree t = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, ptr_type_node);
+ DECL_CONTEXT (t) = decl;
DECL_ARTIFICIAL (t) = 1;
DECL_IGNORED_P (t) = 1;
DECL_RESULT (decl) = t;
return stack;
}
-/* Given a memory reference, if it is not a reg or reg+reg addressing, convert
- to such a form to deal with memory reference instructions like STFIWX that
- only take reg+reg addressing. */
+/* Given a memory reference, if it is not a reg or reg+reg addressing,
+ convert to such a form to deal with memory reference instructions
+ like STFIWX and LDBRX that only take reg+reg addressing. */
rtx
-rs6000_address_for_fpconvert (rtx x)
+rs6000_force_indexed_or_indirect_mem (rtx x)
{
- rtx addr;
+ machine_mode mode = GET_MODE (x);
gcc_assert (MEM_P (x));
- addr = XEXP (x, 0);
- if (can_create_pseudo_p ()
- && ! legitimate_indirect_address_p (addr, reload_completed)
- && ! legitimate_indexed_address_p (addr, reload_completed))
+ if (can_create_pseudo_p () && !indexed_or_indirect_operand (x, mode))
{
+ rtx addr = XEXP (x, 0);
if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC)
{
rtx reg = XEXP (addr, 0);
addr = reg;
}
- x = replace_equiv_address (x, copy_addr_to_reg (addr));
+ x = replace_equiv_address (x, force_reg (Pmode, addr));
}
return x;
if (TARGET_ELF && tls_referenced_p (x))
return false;
- return ((GET_CODE (x) != CONST_DOUBLE && GET_CODE (x) != CONST_VECTOR)
- || GET_MODE (x) == VOIDmode
- || (TARGET_POWERPC64 && mode == DImode)
- || easy_fp_constant (x, mode)
- || easy_vector_constant (x, mode));
+ if (CONST_DOUBLE_P (x))
+ return easy_fp_constant (x, mode);
+
+ if (GET_CODE (x) == CONST_VECTOR)
+ return easy_vector_constant (x, mode);
+
+ return true;
}
\f
/* Expand code to perform a call under the AIX or ELFv2 ABI. */
void
-rs6000_call_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
+rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
{
- const bool direct_call_p
- = GET_CODE (func_desc) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (func_desc);
+ rtx func = func_desc;
rtx toc_reg = gen_rtx_REG (Pmode, TOC_REGNUM);
rtx toc_load = NULL_RTX;
rtx toc_restore = NULL_RTX;
rtx call[4];
int n_call;
rtx insn;
+ bool is_pltseq_longcall;
+
+ if (global_tlsarg)
+ tlsarg = global_tlsarg;
/* Handle longcall attributes. */
- if (INTVAL (cookie) & CALL_LONG)
- func_desc = rs6000_longcall_ref (func_desc);
+ is_pltseq_longcall = false;
+ if ((INTVAL (cookie) & CALL_LONG) != 0
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ func = rs6000_longcall_ref (func_desc, tlsarg);
+ if (TARGET_PLTSEQ)
+ is_pltseq_longcall = true;
+ }
/* Handle indirect calls. */
- if (GET_CODE (func_desc) != SYMBOL_REF
- || (DEFAULT_ABI == ABI_AIX && !SYMBOL_REF_FUNCTION_P (func_desc)))
+ if (!SYMBOL_REF_P (func)
+ || (DEFAULT_ABI == ABI_AIX && !SYMBOL_REF_FUNCTION_P (func)))
{
/* Save the TOC into its reserved slot before the call,
and prepare to restore it after the call. */
- rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
rtx stack_toc_offset = GEN_INT (RS6000_TOC_SAVE_SLOT);
- rtx stack_toc_mem = gen_frame_mem (Pmode,
- gen_rtx_PLUS (Pmode, stack_ptr,
- stack_toc_offset));
rtx stack_toc_unspec = gen_rtx_UNSPEC (Pmode,
gen_rtvec (1, stack_toc_offset),
UNSPEC_TOCSLOT);
cfun->machine->save_toc_in_prologue = true;
else
{
+ rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+ rtx stack_toc_mem = gen_frame_mem (Pmode,
+ gen_rtx_PLUS (Pmode, stack_ptr,
+ stack_toc_offset));
MEM_VOLATILE_P (stack_toc_mem) = 1;
- emit_move_insn (stack_toc_mem, toc_reg);
+ if (is_pltseq_longcall)
+ {
+ /* Use USPEC_PLTSEQ here to emit every instruction in an
+ inline PLT call sequence with a reloc, enabling the
+ linker to edit the sequence back to a direct call
+ when that makes sense. */
+ rtvec v = gen_rtvec (3, toc_reg, func_desc, tlsarg);
+ rtx mark_toc_reg = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ emit_insn (gen_rtx_SET (stack_toc_mem, mark_toc_reg));
+ }
+ else
+ emit_move_insn (stack_toc_mem, toc_reg);
}
if (DEFAULT_ABI == ABI_ELFv2)
/* A function pointer in the ELFv2 ABI is just a plain address, but
the ABI requires it to be loaded into r12 before the call. */
func_addr = gen_rtx_REG (Pmode, 12);
- emit_move_insn (func_addr, func_desc);
+ if (!rtx_equal_p (func_addr, func))
+ emit_move_insn (func_addr, func);
abi_reg = func_addr;
+ /* Indirect calls via CTR are strongly preferred over indirect
+ calls via LR, so move the address there. Needed to mark
+ this insn for linker plt sequence editing too. */
+ func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+ if (is_pltseq_longcall)
+ {
+ rtvec v = gen_rtvec (3, abi_reg, func_desc, tlsarg);
+ rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ emit_insn (gen_rtx_SET (func_addr, mark_func));
+ v = gen_rtvec (2, func_addr, func_desc);
+ func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ }
+ else
+ emit_move_insn (func_addr, abi_reg);
}
else
{
not have any executable code. */
/* Load up address of the actual function. */
- func_desc = force_reg (Pmode, func_desc);
+ func = force_reg (Pmode, func);
func_addr = gen_reg_rtx (Pmode);
- emit_move_insn (func_addr, gen_rtx_MEM (Pmode, func_desc));
+ emit_move_insn (func_addr, gen_rtx_MEM (Pmode, func));
+
+ /* Indirect calls via CTR are strongly preferred over indirect
+ calls via LR, so move the address there. */
+ rtx ctr_reg = gen_rtx_REG (Pmode, CTR_REGNO);
+ emit_move_insn (ctr_reg, func_addr);
+ func_addr = ctr_reg;
/* Prepare to load the TOC of the called function. Note that the
TOC load must happen immediately before the actual call so
comment in frob_update_context. */
rtx func_toc_offset = GEN_INT (GET_MODE_SIZE (Pmode));
rtx func_toc_mem = gen_rtx_MEM (Pmode,
- gen_rtx_PLUS (Pmode, func_desc,
+ gen_rtx_PLUS (Pmode, func,
func_toc_offset));
toc_load = gen_rtx_USE (VOIDmode, func_toc_mem);
originally direct, the 3rd word has not been written since no
trampoline has been built, so we ought not to load it, lest we
override a static chain value. */
- if (!direct_call_p
+ if (!(GET_CODE (func_desc) == SYMBOL_REF
+ && SYMBOL_REF_FUNCTION_P (func_desc))
&& TARGET_POINTERS_TO_NESTED_FUNCTIONS
&& !chain_already_loaded (get_current_sequence ()->next->last))
{
rtx sc_reg = gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM);
rtx func_sc_offset = GEN_INT (2 * GET_MODE_SIZE (Pmode));
rtx func_sc_mem = gen_rtx_MEM (Pmode,
- gen_rtx_PLUS (Pmode, func_desc,
+ gen_rtx_PLUS (Pmode, func,
func_sc_offset));
emit_move_insn (sc_reg, func_sc_mem);
abi_reg = sc_reg;
assume the TOC register is set; for non-local calls, the
PLT stub needs the TOC register. */
abi_reg = toc_reg;
- func_addr = func_desc;
+ func_addr = func;
}
/* Create the call. */
- call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), flag);
+ call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), tlsarg);
if (value != NULL_RTX)
call[0] = gen_rtx_SET (value, call[0]);
n_call = 1;
if (toc_restore)
call[n_call++] = toc_restore;
- call[n_call++] = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, LR_REGNO));
+ call[n_call++] = gen_hard_reg_clobber (Pmode, LR_REGNO);
insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (n_call, call));
insn = emit_call_insn (insn);
/* Expand code to perform a sibling call under the AIX or ELFv2 ABI. */
void
-rs6000_sibcall_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
+rs6000_sibcall_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
{
rtx call[2];
rtx insn;
gcc_assert (INTVAL (cookie) == 0);
+ if (global_tlsarg)
+ tlsarg = global_tlsarg;
+
/* Create the call. */
- call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_desc), flag);
+ call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_desc), tlsarg);
if (value != NULL_RTX)
call[0] = gen_rtx_SET (value, call[0]);
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), gen_rtx_REG (Pmode, TOC_REGNUM));
}
+/* Expand code to perform a call under the SYSV4 ABI. */
+
+void
+rs6000_call_sysv (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
+{
+ rtx func = func_desc;
+ rtx func_addr;
+ rtx call[4];
+ rtx insn;
+ rtx abi_reg = NULL_RTX;
+ int n;
+
+ if (global_tlsarg)
+ tlsarg = global_tlsarg;
+
+ /* Handle longcall attributes. */
+ if ((INTVAL (cookie) & CALL_LONG) != 0
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ func = rs6000_longcall_ref (func_desc, tlsarg);
+ /* If the longcall was implemented as an inline PLT call using
+ PLT unspecs then func will be REG:r11. If not, func will be
+ a pseudo reg. The inline PLT call sequence supports lazy
+ linking (and longcalls to functions in dlopen'd libraries).
+ The other style of longcalls don't. The lazy linking entry
+ to the dynamic symbol resolver requires r11 be the function
+ address (as it is for linker generated PLT stubs). Ensure
+ r11 stays valid to the bctrl by marking r11 used by the call. */
+ if (TARGET_PLTSEQ)
+ abi_reg = func;
+ }
+
+ /* Handle indirect calls. */
+ if (GET_CODE (func) != SYMBOL_REF)
+ {
+ func = force_reg (Pmode, func);
+
+ /* Indirect calls via CTR are strongly preferred over indirect
+ calls via LR, so move the address there. That can't be left
+ to reload because we want to mark every instruction in an
+ inline PLT call sequence with a reloc, enabling the linker to
+ edit the sequence back to a direct call when that makes sense. */
+ func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+ if (abi_reg)
+ {
+ rtvec v = gen_rtvec (3, func, func_desc, tlsarg);
+ rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ emit_insn (gen_rtx_SET (func_addr, mark_func));
+ v = gen_rtvec (2, func_addr, func_desc);
+ func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ }
+ else
+ emit_move_insn (func_addr, func);
+ }
+ else
+ func_addr = func;
+
+ /* Create the call. */
+ call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), tlsarg);
+ if (value != NULL_RTX)
+ call[0] = gen_rtx_SET (value, call[0]);
+
+ call[1] = gen_rtx_USE (VOIDmode, cookie);
+ n = 2;
+ if (TARGET_SECURE_PLT
+ && flag_pic
+ && GET_CODE (func_addr) == SYMBOL_REF
+ && !SYMBOL_REF_LOCAL_P (func_addr))
+ call[n++] = gen_rtx_USE (VOIDmode, pic_offset_table_rtx);
+
+ call[n++] = gen_hard_reg_clobber (Pmode, LR_REGNO);
+
+ insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (n, call));
+ insn = emit_call_insn (insn);
+ if (abi_reg)
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), abi_reg);
+}
+
+/* Expand code to perform a sibling call under the SysV4 ABI. */
+
+void
+rs6000_sibcall_sysv (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
+{
+ rtx func = func_desc;
+ rtx func_addr;
+ rtx call[3];
+ rtx insn;
+ rtx abi_reg = NULL_RTX;
+
+ if (global_tlsarg)
+ tlsarg = global_tlsarg;
+
+ /* Handle longcall attributes. */
+ if ((INTVAL (cookie) & CALL_LONG) != 0
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ func = rs6000_longcall_ref (func_desc, tlsarg);
+ /* If the longcall was implemented as an inline PLT call using
+ PLT unspecs then func will be REG:r11. If not, func will be
+ a pseudo reg. The inline PLT call sequence supports lazy
+ linking (and longcalls to functions in dlopen'd libraries).
+ The other style of longcalls don't. The lazy linking entry
+ to the dynamic symbol resolver requires r11 be the function
+ address (as it is for linker generated PLT stubs). Ensure
+ r11 stays valid to the bctr by marking r11 used by the call. */
+ if (TARGET_PLTSEQ)
+ abi_reg = func;
+ }
+
+ /* Handle indirect calls. */
+ if (GET_CODE (func) != SYMBOL_REF)
+ {
+ func = force_reg (Pmode, func);
+
+ /* Indirect sibcalls must go via CTR. That can't be left to
+ reload because we want to mark every instruction in an inline
+ PLT call sequence with a reloc, enabling the linker to edit
+ the sequence back to a direct call when that makes sense. */
+ func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+ if (abi_reg)
+ {
+ rtvec v = gen_rtvec (3, func, func_desc, tlsarg);
+ rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ emit_insn (gen_rtx_SET (func_addr, mark_func));
+ v = gen_rtvec (2, func_addr, func_desc);
+ func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ }
+ else
+ emit_move_insn (func_addr, func);
+ }
+ else
+ func_addr = func;
+
+ /* Create the call. */
+ call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), tlsarg);
+ if (value != NULL_RTX)
+ call[0] = gen_rtx_SET (value, call[0]);
+
+ call[1] = gen_rtx_USE (VOIDmode, cookie);
+ call[2] = simple_return_rtx;
+
+ insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (3, call));
+ insn = emit_call_insn (insn);
+ if (abi_reg)
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), abi_reg);
+}
+
+#if TARGET_MACHO
+
+/* Expand code to perform a call under the Darwin ABI.
+ Modulo handling of mlongcall, this is much the same as sysv.
+ if/when the longcall optimisation is removed, we could drop this
+ code and use the sysv case (taking care to avoid the tls stuff).
+
+ We can use this for sibcalls too, if needed. */
+
+void
+rs6000_call_darwin_1 (rtx value, rtx func_desc, rtx tlsarg,
+ rtx cookie, bool sibcall)
+{
+ rtx func = func_desc;
+ rtx func_addr;
+ rtx call[3];
+ rtx insn;
+ int cookie_val = INTVAL (cookie);
+ bool make_island = false;
+
+ /* Handle longcall attributes, there are two cases for Darwin:
+ 1) Newer linkers are capable of synthesising any branch islands needed.
+ 2) We need a helper branch island synthesised by the compiler.
+ The second case has mostly been retired and we don't use it for m64.
+ In fact, it's is an optimisation, we could just indirect as sysv does..
+ ... however, backwards compatibility for now.
+ If we're going to use this, then we need to keep the CALL_LONG bit set,
+ so that we can pick up the special insn form later. */
+ if ((cookie_val & CALL_LONG) != 0
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ if (darwin_emit_branch_islands && TARGET_32BIT)
+ make_island = true; /* Do nothing yet, retain the CALL_LONG flag. */
+ else
+ {
+ /* The linker is capable of doing this, but the user explicitly
+ asked for -mlongcall, so we'll do the 'normal' version. */
+ func = rs6000_longcall_ref (func_desc, NULL_RTX);
+ cookie_val &= ~CALL_LONG; /* Handled, zap it. */
+ }
+ }
+
+ /* Handle indirect calls. */
+ if (GET_CODE (func) != SYMBOL_REF)
+ {
+ func = force_reg (Pmode, func);
+
+ /* Indirect calls via CTR are strongly preferred over indirect
+ calls via LR, and are required for indirect sibcalls, so move
+ the address there. */
+ func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+ emit_move_insn (func_addr, func);
+ }
+ else
+ func_addr = func;
+
+ /* Create the call. */
+ call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), tlsarg);
+ if (value != NULL_RTX)
+ call[0] = gen_rtx_SET (value, call[0]);
+
+ call[1] = gen_rtx_USE (VOIDmode, GEN_INT (cookie_val));
+
+ if (sibcall)
+ call[2] = simple_return_rtx;
+ else
+ call[2] = gen_hard_reg_clobber (Pmode, LR_REGNO);
+
+ insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (3, call));
+ insn = emit_call_insn (insn);
+ /* Now we have the debug info in the insn, we can set up the branch island
+ if we're using one. */
+ if (make_island)
+ {
+ tree funname = get_identifier (XSTR (func_desc, 0));
+
+ if (no_previous_def (funname))
+ {
+ rtx label_rtx = gen_label_rtx ();
+ char *label_buf, temp_buf[256];
+ ASM_GENERATE_INTERNAL_LABEL (temp_buf, "L",
+ CODE_LABEL_NUMBER (label_rtx));
+ label_buf = temp_buf[0] == '*' ? temp_buf + 1 : temp_buf;
+ tree labelname = get_identifier (label_buf);
+ add_compiler_branch_island (labelname, funname,
+ insn_line ((const rtx_insn*)insn));
+ }
+ }
+}
+#endif
+
+void
+rs6000_call_darwin (rtx value ATTRIBUTE_UNUSED, rtx func_desc ATTRIBUTE_UNUSED,
+ rtx tlsarg ATTRIBUTE_UNUSED, rtx cookie ATTRIBUTE_UNUSED)
+{
+#if TARGET_MACHO
+ rs6000_call_darwin_1 (value, func_desc, tlsarg, cookie, false);
+#else
+ gcc_unreachable();
+#endif
+}
+
+
+void
+rs6000_sibcall_darwin (rtx value ATTRIBUTE_UNUSED, rtx func_desc ATTRIBUTE_UNUSED,
+ rtx tlsarg ATTRIBUTE_UNUSED, rtx cookie ATTRIBUTE_UNUSED)
+{
+#if TARGET_MACHO
+ rs6000_call_darwin_1 (value, func_desc, tlsarg, cookie, true);
+#else
+ gcc_unreachable();
+#endif
+}
+
+
/* Return whether we need to always update the saved TOC pointer when we update
the stack pointer. */
return (cfun && cfun->machine && cfun->machine->save_toc_in_prologue);
}
+/* Return whether we should generate PC-relative code for FNDECL. */
+bool
+rs6000_fndecl_pcrel_p (const_tree fndecl)
+{
+ if (DEFAULT_ABI != ABI_ELFv2)
+ return false;
+
+ struct cl_target_option *opts = target_opts_for_fn (fndecl);
+
+ return ((opts->x_rs6000_isa_flags & OPTION_MASK_PCREL) != 0
+ && TARGET_CMODEL == CMODEL_MEDIUM);
+}
+
+/* Return whether we should generate PC-relative code for *FN. */
+bool
+rs6000_pcrel_p (struct function *fn)
+{
+ if (DEFAULT_ABI != ABI_ELFv2)
+ return false;
+
+ /* Optimize usual case. */
+ if (fn == cfun)
+ return ((rs6000_isa_flags & OPTION_MASK_PCREL) != 0
+ && TARGET_CMODEL == CMODEL_MEDIUM);
+
+ return rs6000_fndecl_pcrel_p (fn->decl);
+}
+
#ifdef HAVE_GAS_HIDDEN
# define USE_HIDDEN_LINKONCE 1
#else
rtx bool_rtx;
/* Optimize AND of 0/0xffffffff and IOR/XOR of 0. */
- if (op2 && GET_CODE (op2) == CONST_INT
+ if (op2 && CONST_INT_P (op2)
&& (mode == SImode || (mode == DImode && TARGET_POWERPC64))
&& !complement_final_p && !complement_op1_p && !complement_op2_p)
{
op2_hi_lo[hi] = op2_hi_lo[lo] = NULL_RTX;
else
{
- if (GET_CODE (operands[2]) != CONST_INT)
+ if (!CONST_INT_P (operands[2]))
{
op2_hi_lo[hi] = gen_highpart_mode (SImode, DImode, operands[2]);
op2_hi_lo[lo] = gen_lowpart (SImode, operands[2]);
{
/* Split large IOR/XOR operations. */
if ((code == IOR || code == XOR)
- && GET_CODE (op2_hi_lo[i]) == CONST_INT
+ && CONST_INT_P (op2_hi_lo[i])
&& !complement_final_p
&& !complement_op1_p
&& !complement_op2_p
/* Emit a D-form load or store instruction that is the second instruction
of a fusion sequence. */
-void
-emit_fusion_load_store (rtx load_store_reg, rtx addis_reg, rtx offset,
- const char *insn_str)
+static void
+emit_fusion_load (rtx load_reg, rtx addis_reg, rtx offset, const char *insn_str)
{
rtx fuse_ops[10];
char insn_template[80];
- fuse_ops[0] = load_store_reg;
+ fuse_ops[0] = load_reg;
fuse_ops[1] = addis_reg;
if (CONST_INT_P (offset) && satisfies_constraint_I (offset))
emit_fusion_addis (target, addis_value);
/* Emit the D-form load instruction. */
- emit_fusion_load_store (target, target, load_offset, load_str);
+ emit_fusion_load (target, target, load_offset, load_str);
return "";
}
\f
-/* Return true if the peephole2 can combine a load/store involving a
- combination of an addis instruction and the memory operation. This was
- added to the ISA 3.0 (power9) hardware. */
-
-bool
-fusion_p9_p (rtx addis_reg, /* register set via addis. */
- rtx addis_value, /* addis value. */
- rtx dest, /* destination (memory or register). */
- rtx src) /* source (register or memory). */
-{
- rtx addr, mem, offset;
- machine_mode mode = GET_MODE (src);
-
- /* Validate arguments. */
- if (!base_reg_operand (addis_reg, GET_MODE (addis_reg)))
- return false;
-
- if (!fusion_gpr_addis (addis_value, GET_MODE (addis_value)))
- return false;
-
- /* Ignore extend operations that are part of the load. */
- if (GET_CODE (src) == FLOAT_EXTEND || GET_CODE (src) == ZERO_EXTEND)
- src = XEXP (src, 0);
-
- /* Test for memory<-register or register<-memory. */
- if (fpr_reg_operand (src, mode) || int_reg_operand (src, mode))
- {
- if (!MEM_P (dest))
- return false;
-
- mem = dest;
- }
-
- else if (MEM_P (src))
- {
- if (!fpr_reg_operand (dest, mode) && !int_reg_operand (dest, mode))
- return false;
-
- mem = src;
- }
-
- else
- return false;
-
- addr = XEXP (mem, 0); /* either PLUS or LO_SUM. */
- if (GET_CODE (addr) == PLUS)
- {
- if (!rtx_equal_p (addis_reg, XEXP (addr, 0)))
- return false;
-
- return satisfies_constraint_I (XEXP (addr, 1));
- }
-
- else if (GET_CODE (addr) == LO_SUM)
- {
- if (!rtx_equal_p (addis_reg, XEXP (addr, 0)))
- return false;
-
- offset = XEXP (addr, 1);
- if (TARGET_XCOFF || (TARGET_ELF && TARGET_POWERPC64))
- return small_toc_ref (offset, GET_MODE (offset));
-
- else if (TARGET_ELF && !TARGET_POWERPC64)
- return CONSTANT_P (offset);
- }
-
- return false;
-}
-
-/* During the peephole2 pass, adjust and expand the insns for an extended fusion
- load sequence.
-
- The operands are:
- operands[0] register set with addis
- operands[1] value set via addis
- operands[2] target register being loaded
- operands[3] D-form memory reference using operands[0].
-
- This is similar to the fusion introduced with power8, except it scales to
- both loads/stores and does not require the result register to be the same as
- the base register. At the moment, we only do this if register set with addis
- is dead. */
-
-void
-expand_fusion_p9_load (rtx *operands)
-{
- rtx tmp_reg = operands[0];
- rtx addis_value = operands[1];
- rtx target = operands[2];
- rtx orig_mem = operands[3];
- rtx new_addr, new_mem, orig_addr, offset, set, clobber, insn;
- enum rtx_code plus_or_lo_sum;
- machine_mode target_mode = GET_MODE (target);
- machine_mode extend_mode = target_mode;
- machine_mode ptr_mode = Pmode;
- enum rtx_code extend = UNKNOWN;
-
- if (GET_CODE (orig_mem) == FLOAT_EXTEND || GET_CODE (orig_mem) == ZERO_EXTEND)
- {
- extend = GET_CODE (orig_mem);
- orig_mem = XEXP (orig_mem, 0);
- target_mode = GET_MODE (orig_mem);
- }
-
- gcc_assert (MEM_P (orig_mem));
-
- orig_addr = XEXP (orig_mem, 0);
- plus_or_lo_sum = GET_CODE (orig_addr);
- gcc_assert (plus_or_lo_sum == PLUS || plus_or_lo_sum == LO_SUM);
-
- offset = XEXP (orig_addr, 1);
- new_addr = gen_rtx_fmt_ee (plus_or_lo_sum, ptr_mode, addis_value, offset);
- new_mem = replace_equiv_address_nv (orig_mem, new_addr, false);
-
- if (extend != UNKNOWN)
- new_mem = gen_rtx_fmt_e (extend, extend_mode, new_mem);
-
- new_mem = gen_rtx_UNSPEC (extend_mode, gen_rtvec (1, new_mem),
- UNSPEC_FUSION_P9);
-
- set = gen_rtx_SET (target, new_mem);
- clobber = gen_rtx_CLOBBER (VOIDmode, tmp_reg);
- insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber));
- emit_insn (insn);
-
- return;
-}
-
-/* During the peephole2 pass, adjust and expand the insns for an extended fusion
- store sequence.
-
- The operands are:
- operands[0] register set with addis
- operands[1] value set via addis
- operands[2] target D-form memory being stored to
- operands[3] register being stored
-
- This is similar to the fusion introduced with power8, except it scales to
- both loads/stores and does not require the result register to be the same as
- the base register. At the moment, we only do this if register set with addis
- is dead. */
-
-void
-expand_fusion_p9_store (rtx *operands)
-{
- rtx tmp_reg = operands[0];
- rtx addis_value = operands[1];
- rtx orig_mem = operands[2];
- rtx src = operands[3];
- rtx new_addr, new_mem, orig_addr, offset, set, clobber, insn, new_src;
- enum rtx_code plus_or_lo_sum;
- machine_mode target_mode = GET_MODE (orig_mem);
- machine_mode ptr_mode = Pmode;
-
- gcc_assert (MEM_P (orig_mem));
-
- orig_addr = XEXP (orig_mem, 0);
- plus_or_lo_sum = GET_CODE (orig_addr);
- gcc_assert (plus_or_lo_sum == PLUS || plus_or_lo_sum == LO_SUM);
-
- offset = XEXP (orig_addr, 1);
- new_addr = gen_rtx_fmt_ee (plus_or_lo_sum, ptr_mode, addis_value, offset);
- new_mem = replace_equiv_address_nv (orig_mem, new_addr, false);
-
- new_src = gen_rtx_UNSPEC (target_mode, gen_rtvec (1, src),
- UNSPEC_FUSION_P9);
-
- set = gen_rtx_SET (new_mem, new_src);
- clobber = gen_rtx_CLOBBER (VOIDmode, tmp_reg);
- insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber));
- emit_insn (insn);
-
- return;
-}
-
-/* Return a string to fuse an addis instruction with a load using extended
- fusion. The address that is used is the logical address that was formed
- during peephole2: (lo_sum (high) (low-part))
-
- The code is complicated, so we call output_asm_insn directly, and just
- return "". */
-
-const char *
-emit_fusion_p9_load (rtx reg, rtx mem, rtx tmp_reg)
-{
- machine_mode mode = GET_MODE (reg);
- rtx hi;
- rtx lo;
- rtx addr;
- const char *load_string;
- int r;
-
- if (GET_CODE (mem) == FLOAT_EXTEND || GET_CODE (mem) == ZERO_EXTEND)
- {
- mem = XEXP (mem, 0);
- mode = GET_MODE (mem);
- }
-
- if (GET_CODE (reg) == SUBREG)
- {
- gcc_assert (SUBREG_BYTE (reg) == 0);
- reg = SUBREG_REG (reg);
- }
-
- if (!REG_P (reg))
- fatal_insn ("emit_fusion_p9_load, bad reg #1", reg);
-
- r = REGNO (reg);
- if (FP_REGNO_P (r))
- {
- if (mode == SFmode)
- load_string = "lfs";
- else if (mode == DFmode || mode == DImode)
- load_string = "lfd";
- else
- gcc_unreachable ();
- }
- else if (ALTIVEC_REGNO_P (r) && TARGET_P9_VECTOR)
- {
- if (mode == SFmode)
- load_string = "lxssp";
- else if (mode == DFmode || mode == DImode)
- load_string = "lxsd";
- else
- gcc_unreachable ();
- }
- else if (INT_REGNO_P (r))
- {
- switch (mode)
- {
- case E_QImode:
- load_string = "lbz";
- break;
- case E_HImode:
- load_string = "lhz";
- break;
- case E_SImode:
- case E_SFmode:
- load_string = "lwz";
- break;
- case E_DImode:
- case E_DFmode:
- if (!TARGET_POWERPC64)
- gcc_unreachable ();
- load_string = "ld";
- break;
- default:
- gcc_unreachable ();
- }
- }
- else
- fatal_insn ("emit_fusion_p9_load, bad reg #2", reg);
-
- if (!MEM_P (mem))
- fatal_insn ("emit_fusion_p9_load not MEM", mem);
-
- addr = XEXP (mem, 0);
- fusion_split_address (addr, &hi, &lo);
-
- /* Emit the addis instruction. */
- emit_fusion_addis (tmp_reg, hi);
-
- /* Emit the D-form load instruction. */
- emit_fusion_load_store (reg, tmp_reg, lo, load_string);
-
- return "";
-}
-
-/* Return a string to fuse an addis instruction with a store using extended
- fusion. The address that is used is the logical address that was formed
- during peephole2: (lo_sum (high) (low-part))
-
- The code is complicated, so we call output_asm_insn directly, and just
- return "". */
-
-const char *
-emit_fusion_p9_store (rtx mem, rtx reg, rtx tmp_reg)
-{
- machine_mode mode = GET_MODE (reg);
- rtx hi;
- rtx lo;
- rtx addr;
- const char *store_string;
- int r;
-
- if (GET_CODE (reg) == SUBREG)
- {
- gcc_assert (SUBREG_BYTE (reg) == 0);
- reg = SUBREG_REG (reg);
- }
-
- if (!REG_P (reg))
- fatal_insn ("emit_fusion_p9_store, bad reg #1", reg);
-
- r = REGNO (reg);
- if (FP_REGNO_P (r))
- {
- if (mode == SFmode)
- store_string = "stfs";
- else if (mode == DFmode)
- store_string = "stfd";
- else
- gcc_unreachable ();
- }
- else if (ALTIVEC_REGNO_P (r) && TARGET_P9_VECTOR)
- {
- if (mode == SFmode)
- store_string = "stxssp";
- else if (mode == DFmode || mode == DImode)
- store_string = "stxsd";
- else
- gcc_unreachable ();
- }
- else if (INT_REGNO_P (r))
- {
- switch (mode)
- {
- case E_QImode:
- store_string = "stb";
- break;
- case E_HImode:
- store_string = "sth";
- break;
- case E_SImode:
- case E_SFmode:
- store_string = "stw";
- break;
- case E_DImode:
- case E_DFmode:
- if (!TARGET_POWERPC64)
- gcc_unreachable ();
- store_string = "std";
- break;
- default:
- gcc_unreachable ();
- }
- }
- else
- fatal_insn ("emit_fusion_p9_store, bad reg #2", reg);
-
- if (!MEM_P (mem))
- fatal_insn ("emit_fusion_p9_store not MEM", mem);
-
- addr = XEXP (mem, 0);
- fusion_split_address (addr, &hi, &lo);
-
- /* Emit the addis instruction. */
- emit_fusion_addis (tmp_reg, hi);
-
- /* Emit the D-form load instruction. */
- emit_fusion_load_store (reg, tmp_reg, lo, store_string);
-
- return "";
-}
-
#ifdef RS6000_GLIBC_ATOMIC_FENV
/* Function declarations for rs6000_atomic_assign_expand_fenv. */
static tree atomic_hold_decl, atomic_clear_decl, atomic_update_decl;
}
#endif
+\f
+/* On 64-bit Linux and Freebsd systems, possibly switch the long double library
+ function names from <foo>l to <foo>f128 if the default long double type is
+ IEEE 128-bit. Typically, with the C and C++ languages, the standard math.h
+ include file switches the names on systems that support long double as IEEE
+ 128-bit, but that doesn't work if the user uses __builtin_<foo>l directly.
+ In the future, glibc will export names like __ieee128_sinf128 and we can
+ switch to using those instead of using sinf128, which pollutes the user's
+ namespace.
+
+ This will switch the names for Fortran math functions as well (which doesn't
+ use math.h). However, Fortran needs other changes to the compiler and
+ library before you can switch the real*16 type at compile time.
+
+ We use the TARGET_MANGLE_DECL_ASSEMBLER_NAME hook to change this name. We
+ only do this if the default is that long double is IBM extended double, and
+ the user asked for IEEE 128-bit. */
+
+static tree
+rs6000_mangle_decl_assembler_name (tree decl, tree id)
+{
+ if (!TARGET_IEEEQUAD_DEFAULT && TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128
+ && TREE_CODE (decl) == FUNCTION_DECL && DECL_IS_BUILTIN (decl) )
+ {
+ size_t len = IDENTIFIER_LENGTH (id);
+ const char *name = IDENTIFIER_POINTER (id);
+
+ if (name[len - 1] == 'l')
+ {
+ bool uses_ieee128_p = false;
+ tree type = TREE_TYPE (decl);
+ machine_mode ret_mode = TYPE_MODE (type);
+
+ /* See if the function returns a IEEE 128-bit floating point type or
+ complex type. */
+ if (ret_mode == TFmode || ret_mode == TCmode)
+ uses_ieee128_p = true;
+ else
+ {
+ function_args_iterator args_iter;
+ tree arg;
+
+ /* See if the function passes a IEEE 128-bit floating point type
+ or complex type. */
+ FOREACH_FUNCTION_ARGS (type, arg, args_iter)
+ {
+ machine_mode arg_mode = TYPE_MODE (arg);
+ if (arg_mode == TFmode || arg_mode == TCmode)
+ {
+ uses_ieee128_p = true;
+ break;
+ }
+ }
+ }
+
+ /* If we passed or returned an IEEE 128-bit floating point type,
+ change the name. */
+ if (uses_ieee128_p)
+ {
+ char *name2 = (char *) alloca (len + 4);
+ memcpy (name2, name, len - 1);
+ strcpy (name2 + len - 1, "f128");
+ id = get_identifier (name2);
+ }
+ }
+ }
+
+ return id;
+}
+
\f
struct gcc_target targetm = TARGET_INITIALIZER;