#include "langhooks.h"
#include "reload.h"
#include "cfglayout.h"
+#include "cfgloop.h"
#include "sched-int.h"
#include "gimple.h"
#include "tree-flow.h"
/* Structure used to define the rs6000 stack */
typedef struct rs6000_stack {
+ int reload_completed; /* stack info won't change from here on */
int first_gp_reg_save; /* first callee saved GP register used */
int first_fp_reg_save; /* first callee saved FP register used */
int first_altivec_reg_save; /* first callee saved AltiVec register used */
int spe_padding_size;
HOST_WIDE_INT total_size; /* total bytes allocated for stack */
int spe_64bit_regs_used;
+ int savres_strategy;
} rs6000_stack_t;
/* A C structure for machine-specific, per-function data.
/* Target cpu type */
-enum processor_type rs6000_cpu;
struct rs6000_cpu_select rs6000_select[3] =
{
/* switch name, tune arch */
{ (const char *)0, "-mtune=", 1, 0 },
};
-/* Always emit branch hint bits. */
-static GTY(()) bool rs6000_always_hint;
-
-/* Schedule instructions for group formation. */
-static GTY(()) bool rs6000_sched_groups;
-
-/* Align branch targets. */
-static GTY(()) bool rs6000_align_branch_targets;
-
-/* Support for -msched-costly-dep option. */
-const char *rs6000_sched_costly_dep_str;
-enum rs6000_dependence_cost rs6000_sched_costly_dep;
+/* String variables to hold the various options. */
+static const char *rs6000_sched_insert_nops_str;
+static const char *rs6000_sched_costly_dep_str;
+static const char *rs6000_recip_name;
-/* Support for -minsert-sched-nops option. */
-const char *rs6000_sched_insert_nops_str;
-enum rs6000_nop_insertion rs6000_sched_insert_nops;
+#ifdef USING_ELFOS_H
+static const char *rs6000_abi_name;
+static const char *rs6000_sdata_name;
+#endif
/* Support targetm.vectorize.builtin_mask_for_load. */
static GTY(()) tree altivec_builtin_mask_for_load;
-/* Size of long double. */
-int rs6000_long_double_type_size;
-
-/* IEEE quad extended precision long double. */
-int rs6000_ieeequad;
-
-/* Nonzero to use AltiVec ABI. */
-int rs6000_altivec_abi;
-
-/* Nonzero if we want SPE SIMD instructions. */
-int rs6000_spe;
-
-/* Nonzero if we want SPE ABI extensions. */
-int rs6000_spe_abi;
-
-/* Nonzero if floating point operations are done in the GPRs. */
-int rs6000_float_gprs = 0;
-
-/* Nonzero if we want Darwin's struct-by-value-in-regs ABI. */
-int rs6000_darwin64_abi;
-
/* Set to nonzero once AIX common-mode calls have been defined. */
static GTY(()) int common_mode_defined;
/* Label number of label created for -mrelocatable, to call to so we can
get the address of the GOT section */
-int rs6000_pic_labelno;
+static int rs6000_pic_labelno;
#ifdef USING_ELFOS_H
-/* Which abi to adhere to */
-const char *rs6000_abi_name;
-
-/* Semantics of the small data area */
-enum rs6000_sdata_type rs6000_sdata = SDATA_DATA;
-
-/* Which small data model to use */
-const char *rs6000_sdata_name = (char *)0;
-
/* Counter for labels which are to be placed in .fixup. */
int fixuplabelno = 0;
#endif
-/* Bit size of immediate TLS offsets and string from which it is decoded. */
-int rs6000_tls_size = 32;
-const char *rs6000_tls_size_string;
-
-/* ABI enumeration available for subtarget to use. */
-enum rs6000_abi rs6000_current_abi;
-
/* Whether to use variant of AIX ABI for PowerPC64 Linux. */
int dot_symbols;
-/* Debug flags */
-const char *rs6000_debug_name;
-int rs6000_debug_stack; /* debug stack applications */
-int rs6000_debug_arg; /* debug argument handling */
-int rs6000_debug_reg; /* debug register classes */
-int rs6000_debug_addr; /* debug memory addressing */
-int rs6000_debug_cost; /* debug rtx_costs */
-
/* Specify the machine mode that pointers have. After generation of rtl, the
compiler makes no further distinction between pointers and any other objects
of this machine mode. The type is unsigned since not all things that
tree rs6000_builtin_types[RS6000_BTI_MAX];
tree rs6000_builtin_decls[RS6000_BUILTIN_COUNT];
-const char *rs6000_traceback_name;
-static enum {
- traceback_default = 0,
- traceback_none,
- traceback_part,
- traceback_full
-} rs6000_traceback;
-
/* Flag to say the TOC is initialized */
int toc_initialized;
char toc_label_name[10];
static GTY(()) section *sdata2_section;
static GTY(()) section *toc_section;
-/* Control alignment for fields within structures. */
-/* String from -malign-XXXXX. */
-int rs6000_alignment_flags;
-
-/* Code model for 64-bit linux. */
-enum rs6000_cmodel cmodel;
-
/* True for any options that were explicitly set. */
static struct {
bool aix_struct_ret; /* True if -maix-struct-ret was used. */
RECIP_LOW_PRECISION = (RECIP_ALL & ~(RECIP_DF_RSQRT | RECIP_V2DF_RSQRT))
};
-static unsigned int rs6000_recip_control;
-static const char *rs6000_recip_name;
-
/* -mrecip options. */
static struct
{
static bool rs6000_reg_live_or_pic_offset_p (int);
static tree rs6000_builtin_vectorized_libmass (tree, tree, tree);
static tree rs6000_builtin_vectorized_function (tree, tree, tree);
-static int rs6000_savres_strategy (rs6000_stack_t *, bool, int, int);
static void rs6000_restore_saved_cr (rtx, int);
static bool rs6000_output_addr_const_extra (FILE *, rtx);
static void rs6000_output_function_prologue (FILE *, HOST_WIDE_INT);
static void rs6000_option_init_struct (struct gcc_options *);
static void rs6000_option_default_params (void);
static bool rs6000_handle_option (size_t, const char *, int);
-static void rs6000_parse_tls_size_option (void);
+static int rs6000_loop_align_max_skip (rtx);
static void rs6000_parse_yes_no_option (const char *, const char *, int *);
static int first_altivec_reg_to_save (void);
static unsigned int compute_vrsave_mask (void);
const_tree, bool);
static rtx rs6000_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
const_tree, bool);
+static unsigned int rs6000_function_arg_boundary (enum machine_mode,
+ const_tree);
static void rs6000_move_block_from_reg (int regno, rtx x, int nregs);
static void setup_incoming_varargs (CUMULATIVE_ARGS *,
enum machine_mode, tree,
const int INSN_NOT_AVAILABLE = -1;
static enum machine_mode rs6000_eh_return_filter_mode (void);
static bool rs6000_can_eliminate (const int, const int);
+static void rs6000_conditional_register_usage (void);
static void rs6000_trampoline_init (rtx, tree, rtx);
/* Hash table stuff for keeping track of TOC entries. */
};
static GTY ((param_is (struct builtin_hash_struct))) htab_t builtin_hash_table;
+
+static bool rs6000_valid_attribute_p (tree, tree, tree, int);
+static void rs6000_function_specific_save (struct cl_target_option *);
+static void rs6000_function_specific_restore (struct cl_target_option *);
+static void rs6000_function_specific_print (FILE *, int,
+ struct cl_target_option *);
+static bool rs6000_can_inline_p (tree, tree);
+static void rs6000_set_current_function (tree);
+
\f
/* Default register names. */
char rs6000_reg_names[][8] =
#define TARGET_FUNCTION_ARG_ADVANCE rs6000_function_arg_advance
#undef TARGET_FUNCTION_ARG
#define TARGET_FUNCTION_ARG rs6000_function_arg
+#undef TARGET_FUNCTION_ARG_BOUNDARY
+#define TARGET_FUNCTION_ARG_BOUNDARY rs6000_function_arg_boundary
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION rs6000_handle_option
+#undef TARGET_ASM_LOOP_ALIGN_MAX_SKIP
+#define TARGET_ASM_LOOP_ALIGN_MAX_SKIP rs6000_loop_align_max_skip
+
#undef TARGET_OPTION_OVERRIDE
#define TARGET_OPTION_OVERRIDE rs6000_option_override
#undef TARGET_CAN_ELIMINATE
#define TARGET_CAN_ELIMINATE rs6000_can_eliminate
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE rs6000_conditional_register_usage
+
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT rs6000_trampoline_init
#undef TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE rs6000_function_value
+#undef TARGET_OPTION_VALID_ATTRIBUTE_P
+#define TARGET_OPTION_VALID_ATTRIBUTE_P rs6000_valid_attribute_p
+
+#undef TARGET_OPTION_SAVE
+#define TARGET_OPTION_SAVE rs6000_function_specific_save
+
+#undef TARGET_OPTION_RESTORE
+#define TARGET_OPTION_RESTORE rs6000_function_specific_restore
+
+#undef TARGET_OPTION_PRINT
+#define TARGET_OPTION_PRINT rs6000_function_specific_print
+
+#undef TARGET_CAN_INLINE_P
+#define TARGET_CAN_INLINE_P rs6000_can_inline_p
+
+#undef TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION rs6000_set_current_function
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
+
+/* Simplifications for entries below. */
+
+enum {
+ POWERPC_BASE_MASK = MASK_POWERPC | MASK_NEW_MNEMONICS,
+ POWERPC_7400_MASK = POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_ALTIVEC
+};
+
+/* Some OSs don't support saving the high part of 64-bit registers on context
+ switch. Other OSs don't support saving Altivec registers. On those OSs, we
+ don't touch the MASK_POWERPC64 or MASK_ALTIVEC settings; if the user wants
+ either, the user must explicitly specify them and we won't interfere with
+ the user's specification. */
+
+enum {
+ POWER_MASKS = MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING,
+ POWERPC_MASKS = (POWERPC_BASE_MASK | MASK_PPC_GPOPT | MASK_STRICT_ALIGN
+ | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_ALTIVEC
+ | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_MULHW
+ | MASK_DLMZB | MASK_CMPB | MASK_MFPGPR | MASK_DFP
+ | MASK_POPCNTD | MASK_VSX | MASK_ISEL | MASK_NO_UPDATE
+ | MASK_RECIP_PRECISION)
+};
+
+/* Masks for instructions set at various powerpc ISAs. */
+enum {
+ ISA_2_1_MASKS = MASK_MFCRF,
+ ISA_2_2_MASKS = (ISA_2_1_MASKS | MASK_POPCNTB),
+ ISA_2_4_MASKS = (ISA_2_2_MASKS | MASK_FPRND),
+
+ /* For ISA 2.05, do not add MFPGPR, since it isn't in ISA 2.06, and don't add
+ ALTIVEC, since in general it isn't a win on power6. In ISA 2.04, fsel,
+ fre, fsqrt, etc. were no longer documented as optional. Group masks by
+ server and embedded. */
+ ISA_2_5_MASKS_EMBEDDED = (ISA_2_2_MASKS | MASK_CMPB | MASK_RECIP_PRECISION
+ | MASK_PPC_GFXOPT | MASK_PPC_GPOPT),
+ ISA_2_5_MASKS_SERVER = (ISA_2_5_MASKS_EMBEDDED | MASK_DFP),
+
+ /* For ISA 2.06, don't add ISEL, since in general it isn't a win, but
+ altivec is a win so enable it. */
+ ISA_2_6_MASKS_EMBEDDED = (ISA_2_5_MASKS_EMBEDDED | MASK_POPCNTD),
+ ISA_2_6_MASKS_SERVER = (ISA_2_5_MASKS_SERVER | MASK_POPCNTD | MASK_ALTIVEC
+ | MASK_VSX)
+};
+
+/* This table occasionally claims that a processor does not support a
+ particular feature even though it does, but the feature is slower than the
+ alternative. Thus, it shouldn't be relied on as a complete description of
+ the processor's support.
+
+ Please keep this list in order, and don't forget to update the documentation
+ in invoke.texi when adding a new processor or flag. */
+
+struct rs6000_ptt
+{
+ const char *const name; /* Canonical processor name. */
+ const enum processor_type processor; /* Processor type enum value. */
+ const int target_enable; /* Target flags to enable. */
+};
+
+static struct rs6000_ptt const processor_target_table[] =
+{
+ {"401", PROCESSOR_PPC403, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+ {"403", PROCESSOR_PPC403,
+ POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_STRICT_ALIGN},
+ {"405", PROCESSOR_PPC405,
+ POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB},
+ {"405fp", PROCESSOR_PPC405,
+ POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
+ {"440", PROCESSOR_PPC440,
+ POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB},
+ {"440fp", PROCESSOR_PPC440,
+ POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
+ {"464", PROCESSOR_PPC440,
+ POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB},
+ {"464fp", PROCESSOR_PPC440,
+ POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
+ {"476", PROCESSOR_PPC476,
+ POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_PPC_GFXOPT | MASK_MFCRF
+ | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_MULHW | MASK_DLMZB},
+ {"476fp", PROCESSOR_PPC476,
+ POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_MFCRF | MASK_POPCNTB
+ | MASK_FPRND | MASK_CMPB | MASK_MULHW | MASK_DLMZB},
+ {"505", PROCESSOR_MPCCORE, POWERPC_BASE_MASK},
+ {"601", PROCESSOR_PPC601,
+ MASK_POWER | POWERPC_BASE_MASK | MASK_MULTIPLE | MASK_STRING},
+ {"602", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+ {"603", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+ {"603e", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+ {"604", PROCESSOR_PPC604, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+ {"604e", PROCESSOR_PPC604e, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+ {"620", PROCESSOR_PPC620,
+ POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
+ {"630", PROCESSOR_PPC630,
+ POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
+ {"740", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+ {"7400", PROCESSOR_PPC7400, POWERPC_7400_MASK},
+ {"7450", PROCESSOR_PPC7450, POWERPC_7400_MASK},
+ {"750", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+ {"801", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+ {"821", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+ {"823", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+ {"8540", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN
+ | MASK_ISEL},
+ /* 8548 has a dummy entry for now. */
+ {"8548", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN
+ | MASK_ISEL},
+ {"a2", PROCESSOR_PPCA2,
+ POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_POPCNTB
+ | MASK_CMPB | MASK_NO_UPDATE },
+ {"e300c2", PROCESSOR_PPCE300C2, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+ {"e300c3", PROCESSOR_PPCE300C3, POWERPC_BASE_MASK},
+ {"e500mc", PROCESSOR_PPCE500MC, POWERPC_BASE_MASK | MASK_PPC_GFXOPT
+ | MASK_ISEL},
+ {"e500mc64", PROCESSOR_PPCE500MC64, POWERPC_BASE_MASK | MASK_POWERPC64
+ | MASK_PPC_GFXOPT | MASK_ISEL},
+ {"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+ {"970", PROCESSOR_POWER4,
+ POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
+ {"cell", PROCESSOR_CELL,
+ POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
+ {"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS},
+ {"ec603e", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+ {"G3", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+ {"G4", PROCESSOR_PPC7450, POWERPC_7400_MASK},
+ {"G5", PROCESSOR_POWER4,
+ POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
+ {"titan", PROCESSOR_TITAN,
+ POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
+ {"power", PROCESSOR_POWER, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+ {"power2", PROCESSOR_POWER,
+ MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING},
+ {"power3", PROCESSOR_PPC630,
+ POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
+ {"power4", PROCESSOR_POWER4,
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+ | MASK_MFCRF},
+ {"power5", PROCESSOR_POWER5,
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+ | MASK_MFCRF | MASK_POPCNTB},
+ {"power5+", PROCESSOR_POWER5,
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+ | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND},
+ {"power6", PROCESSOR_POWER6,
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+ | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP
+ | MASK_RECIP_PRECISION},
+ {"power6x", PROCESSOR_POWER6,
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+ | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP
+ | MASK_MFPGPR | MASK_RECIP_PRECISION},
+ {"power7", PROCESSOR_POWER7, /* Don't add MASK_ISEL by default */
+ POWERPC_7400_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_MFCRF
+ | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP | MASK_POPCNTD
+ | MASK_VSX | MASK_RECIP_PRECISION},
+ {"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK},
+ {"powerpc64", PROCESSOR_POWERPC64,
+ POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
+ {"rios", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+ {"rios1", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+ {"rios2", PROCESSOR_RIOS2,
+ MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING},
+ {"rsc", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+ {"rsc1", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+ {"rs64", PROCESSOR_RS64A,
+ POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64}
+};
+
+/* Look up a processor name for -mcpu=xxx and -mtune=xxx. Return -1 if the
+ name is invalid. */
+
+static int
+rs6000_cpu_name_lookup (const char *name)
+{
+ size_t i;
+
+ if (name != NULL)
+ {
+ for (i = 0; i < ARRAY_SIZE (processor_target_table); i++)
+ if (! strcmp (name, processor_target_table[i].name))
+ return (int)i;
+ }
+
+ return -1;
+}
+
+\f
/* Return number of consecutive hard regs needed starting at reg REGNO
to hold something of mode MODE.
This is ordinarily the length in words of a value of mode MODE
}
}
+#define DEBUG_FMT_D "%-32s= %d\n"
+#define DEBUG_FMT_S "%-32s= %s\n"
+
/* Print various interesting information with -mdebug=reg. */
static void
rs6000_debug_reg_global (void)
{
+ static const char *const tf[2] = { "false", "true" };
const char *nl = (const char *)0;
int m;
char costly_num[20];
char nop_num[20];
const char *costly_str;
const char *nop_str;
+ const char *trace_str;
+ const char *abi_str;
+ const char *cmodel_str;
/* Map enum rs6000_vector to string. */
static const char *rs6000_debug_vector_unit[] = {
fputs ("\n", stderr);
}
+ if (rs6000_cpu_index >= 0)
+ fprintf (stderr, DEBUG_FMT_S, "cpu",
+ processor_target_table[rs6000_cpu_index].name);
+
+ if (rs6000_tune_index >= 0)
+ fprintf (stderr, DEBUG_FMT_S, "tune",
+ processor_target_table[rs6000_tune_index].name);
+
switch (rs6000_sched_costly_dep)
{
case max_dep_latency:
break;
}
+ fprintf (stderr, DEBUG_FMT_S, "sched_costly_dep", costly_str);
+
switch (rs6000_sched_insert_nops)
{
case sched_finish_regroup_exact:
break;
}
- fprintf (stderr,
- "always_hint = %s\n"
- "align_branch_targets = %s\n"
- "sched_restricted_insns_priority = %d\n"
- "sched_costly_dep = %s\n"
- "sched_insert_nops = %s\n\n",
- rs6000_always_hint ? "true" : "false",
- rs6000_align_branch_targets ? "true" : "false",
- (int)rs6000_sched_restricted_insns_priority,
- costly_str, nop_str);
+ fprintf (stderr, DEBUG_FMT_S, "sched_insert_nops", nop_str);
+
+ switch (rs6000_sdata)
+ {
+ default:
+ case SDATA_NONE:
+ break;
+
+ case SDATA_DATA:
+ fprintf (stderr, DEBUG_FMT_S, "sdata", "data");
+ break;
+
+ case SDATA_SYSV:
+ fprintf (stderr, DEBUG_FMT_S, "sdata", "sysv");
+ break;
+
+ case SDATA_EABI:
+ fprintf (stderr, DEBUG_FMT_S, "sdata", "eabi");
+ break;
+
+ }
+
+ switch (rs6000_traceback)
+ {
+ case traceback_default: trace_str = "default"; break;
+ case traceback_none: trace_str = "none"; break;
+ case traceback_part: trace_str = "part"; break;
+ case traceback_full: trace_str = "full"; break;
+ default: trace_str = "unknown"; break;
+ }
+
+ fprintf (stderr, DEBUG_FMT_S, "traceback", trace_str);
+
+ switch (rs6000_current_cmodel)
+ {
+ case CMODEL_SMALL: cmodel_str = "small"; break;
+ case CMODEL_MEDIUM: cmodel_str = "medium"; break;
+ case CMODEL_LARGE: cmodel_str = "large"; break;
+ default: cmodel_str = "unknown"; break;
+ }
+
+ fprintf (stderr, DEBUG_FMT_S, "cmodel", cmodel_str);
+
+ switch (rs6000_current_abi)
+ {
+ case ABI_NONE: abi_str = "none"; break;
+ case ABI_AIX: abi_str = "aix"; break;
+ case ABI_V4: abi_str = "V4"; break;
+ case ABI_DARWIN: abi_str = "darwin"; break;
+ default: abi_str = "unknown"; break;
+ }
+
+ fprintf (stderr, DEBUG_FMT_S, "abi", abi_str);
+
+ if (rs6000_altivec_abi)
+ fprintf (stderr, DEBUG_FMT_S, "altivec_abi", "true");
+
+ if (rs6000_spe_abi)
+ fprintf (stderr, DEBUG_FMT_S, "spe_abi", "true");
+
+ if (rs6000_darwin64_abi)
+ fprintf (stderr, DEBUG_FMT_S, "darwin64_abi", "true");
+
+ if (rs6000_float_gprs)
+ fprintf (stderr, DEBUG_FMT_S, "float_gprs", "true");
+
+ fprintf (stderr, DEBUG_FMT_S, "always_hint", tf[!!rs6000_always_hint]);
+ fprintf (stderr, DEBUG_FMT_S, "align_branch",
+ tf[!!rs6000_align_branch_targets]);
+ fprintf (stderr, DEBUG_FMT_D, "tls_size", rs6000_tls_size);
+ fprintf (stderr, DEBUG_FMT_D, "long_double_size",
+ rs6000_long_double_type_size);
+ fprintf (stderr, DEBUG_FMT_D, "sched_restricted_insns_priority",
+ (int)rs6000_sched_restricted_insns_priority);
}
/* Initialize the various global tables that are based on register size. */
static void
-rs6000_init_hard_regno_mode_ok (void)
+rs6000_init_hard_regno_mode_ok (bool global_init_p)
{
int r, m, c;
int align64;
if (rs6000_recip_control)
{
- if (!TARGET_FUSED_MADD)
- warning (0, "-mrecip requires -mfused-madd");
if (!flag_finite_math_only)
warning (0, "-mrecip requires -ffinite-math or -ffast-math");
if (flag_trapping_math)
warning (0, "-mrecip requires -fno-trapping-math or -ffast-math");
if (!flag_reciprocal_math)
warning (0, "-mrecip requires -freciprocal-math or -ffast-math");
- if (TARGET_FUSED_MADD && flag_finite_math_only && !flag_trapping_math
- && flag_reciprocal_math)
+ if (flag_finite_math_only && !flag_trapping_math && flag_reciprocal_math)
{
if (RS6000_RECIP_HAVE_RE_P (SFmode)
&& (rs6000_recip_control & RECIP_SF_DIV) != 0)
}
}
- if (TARGET_DEBUG_REG)
- rs6000_debug_reg_global ();
-
- if (TARGET_DEBUG_COST || TARGET_DEBUG_REG)
- fprintf (stderr,
- "SImode variable mult cost = %d\n"
- "SImode constant mult cost = %d\n"
- "SImode short constant mult cost = %d\n"
- "DImode multipliciation cost = %d\n"
- "SImode division cost = %d\n"
- "DImode division cost = %d\n"
- "Simple fp operation cost = %d\n"
- "DFmode multiplication cost = %d\n"
- "SFmode division cost = %d\n"
- "DFmode division cost = %d\n"
- "cache line size = %d\n"
- "l1 cache size = %d\n"
- "l2 cache size = %d\n"
- "simultaneous prefetches = %d\n"
- "\n",
- rs6000_cost->mulsi,
- rs6000_cost->mulsi_const,
- rs6000_cost->mulsi_const9,
- rs6000_cost->muldi,
- rs6000_cost->divsi,
- rs6000_cost->divdi,
- rs6000_cost->fp,
- rs6000_cost->dmul,
- rs6000_cost->sdiv,
- rs6000_cost->ddiv,
- rs6000_cost->cache_line_size,
- rs6000_cost->l1_cache_size,
- rs6000_cost->l2_cache_size,
- rs6000_cost->simultaneous_prefetches);
+ if (global_init_p || TARGET_DEBUG_TARGET)
+ {
+ if (TARGET_DEBUG_REG)
+ rs6000_debug_reg_global ();
+
+ if (TARGET_DEBUG_COST || TARGET_DEBUG_REG)
+ fprintf (stderr,
+ "SImode variable mult cost = %d\n"
+ "SImode constant mult cost = %d\n"
+ "SImode short constant mult cost = %d\n"
+ "DImode multipliciation cost = %d\n"
+ "SImode division cost = %d\n"
+ "DImode division cost = %d\n"
+ "Simple fp operation cost = %d\n"
+ "DFmode multiplication cost = %d\n"
+ "SFmode division cost = %d\n"
+ "DFmode division cost = %d\n"
+ "cache line size = %d\n"
+ "l1 cache size = %d\n"
+ "l2 cache size = %d\n"
+ "simultaneous prefetches = %d\n"
+ "\n",
+ rs6000_cost->mulsi,
+ rs6000_cost->mulsi_const,
+ rs6000_cost->mulsi_const9,
+ rs6000_cost->muldi,
+ rs6000_cost->divsi,
+ rs6000_cost->divdi,
+ rs6000_cost->fp,
+ rs6000_cost->dmul,
+ rs6000_cost->sdiv,
+ rs6000_cost->ddiv,
+ rs6000_cost->cache_line_size,
+ rs6000_cost->l1_cache_size,
+ rs6000_cost->l2_cache_size,
+ rs6000_cost->simultaneous_prefetches);
+ }
}
#if TARGET_MACHO
off. */
rs6000_altivec_abi = 1;
TARGET_ALTIVEC_VRSAVE = 1;
- if (DEFAULT_ABI == ABI_DARWIN)
- {
- if (MACHO_DYNAMIC_NO_PIC_P)
- {
- if (flag_pic)
- warning (0, "-mdynamic-no-pic overrides -fpic or -fPIC");
- flag_pic = 0;
- }
- else if (flag_pic == 1)
- {
- flag_pic = 2;
- }
- if (TARGET_64BIT)
+
+ if (DEFAULT_ABI == ABI_DARWIN
+ && TARGET_64BIT)
darwin_one_byte_bool = 1;
- }
+
if (TARGET_64BIT && ! TARGET_POWERPC64)
{
target_flags |= MASK_POWERPC64;
#define RS6000_DEFAULT_LONG_DOUBLE_SIZE 64
#endif
-/* Override command line options. Mostly we process the processor
- type and sometimes adjust other TARGET_ options. */
+/* Override command line options. Mostly we process the processor type and
+ sometimes adjust other TARGET_ options. */
-static void
-rs6000_option_override_internal (const char *default_cpu)
+static bool
+rs6000_option_override_internal (bool global_init_p)
{
- size_t i, j;
- struct rs6000_cpu_select *ptr;
+ bool ret = true;
+ const char *default_cpu = OPTION_TARGET_CPU_DEFAULT;
int set_masks;
-
- /* Simplifications for entries below. */
-
- enum {
- POWERPC_BASE_MASK = MASK_POWERPC | MASK_NEW_MNEMONICS,
- POWERPC_7400_MASK = POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_ALTIVEC
- };
-
- /* This table occasionally claims that a processor does not support
- a particular feature even though it does, but the feature is slower
- than the alternative. Thus, it shouldn't be relied on as a
- complete description of the processor's support.
-
- Please keep this list in order, and don't forget to update the
- documentation in invoke.texi when adding a new processor or
- flag. */
- static struct ptt
- {
- const char *const name; /* Canonical processor name. */
- const enum processor_type processor; /* Processor type enum value. */
- const int target_enable; /* Target flags to enable. */
- } const processor_target_table[]
- = {{"401", PROCESSOR_PPC403, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
- {"403", PROCESSOR_PPC403,
- POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_STRICT_ALIGN},
- {"405", PROCESSOR_PPC405,
- POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB},
- {"405fp", PROCESSOR_PPC405,
- POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
- {"440", PROCESSOR_PPC440,
- POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB},
- {"440fp", PROCESSOR_PPC440,
- POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
- {"464", PROCESSOR_PPC440,
- POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB},
- {"464fp", PROCESSOR_PPC440,
- POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
- {"476", PROCESSOR_PPC476,
- POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_PPC_GFXOPT | MASK_MFCRF
- | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_MULHW | MASK_DLMZB},
- {"476fp", PROCESSOR_PPC476,
- POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_MFCRF | MASK_POPCNTB
- | MASK_FPRND | MASK_CMPB | MASK_MULHW | MASK_DLMZB},
- {"505", PROCESSOR_MPCCORE, POWERPC_BASE_MASK},
- {"601", PROCESSOR_PPC601,
- MASK_POWER | POWERPC_BASE_MASK | MASK_MULTIPLE | MASK_STRING},
- {"602", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
- {"603", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
- {"603e", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
- {"604", PROCESSOR_PPC604, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
- {"604e", PROCESSOR_PPC604e, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
- {"620", PROCESSOR_PPC620,
- POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
- {"630", PROCESSOR_PPC630,
- POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
- {"740", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
- {"7400", PROCESSOR_PPC7400, POWERPC_7400_MASK},
- {"7450", PROCESSOR_PPC7450, POWERPC_7400_MASK},
- {"750", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
- {"801", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
- {"821", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
- {"823", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
- {"8540", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN
- | MASK_ISEL},
- /* 8548 has a dummy entry for now. */
- {"8548", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN
- | MASK_ISEL},
- {"a2", PROCESSOR_PPCA2,
- POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_POPCNTB
- | MASK_CMPB | MASK_NO_UPDATE },
- {"e300c2", PROCESSOR_PPCE300C2, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
- {"e300c3", PROCESSOR_PPCE300C3, POWERPC_BASE_MASK},
- {"e500mc", PROCESSOR_PPCE500MC, POWERPC_BASE_MASK | MASK_PPC_GFXOPT
- | MASK_ISEL},
- {"e500mc64", PROCESSOR_PPCE500MC64, POWERPC_BASE_MASK | MASK_POWERPC64
- | MASK_PPC_GFXOPT | MASK_ISEL},
- {"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
- {"970", PROCESSOR_POWER4,
- POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
- {"cell", PROCESSOR_CELL,
- POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
- {"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS},
- {"ec603e", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
- {"G3", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
- {"G4", PROCESSOR_PPC7450, POWERPC_7400_MASK},
- {"G5", PROCESSOR_POWER4,
- POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
- {"titan", PROCESSOR_TITAN,
- POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
- {"power", PROCESSOR_POWER, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
- {"power2", PROCESSOR_POWER,
- MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING},
- {"power3", PROCESSOR_PPC630,
- POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
- {"power4", PROCESSOR_POWER4,
- POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
- | MASK_MFCRF},
- {"power5", PROCESSOR_POWER5,
- POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
- | MASK_MFCRF | MASK_POPCNTB},
- {"power5+", PROCESSOR_POWER5,
- POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
- | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND},
- {"power6", PROCESSOR_POWER6,
- POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
- | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP
- | MASK_RECIP_PRECISION},
- {"power6x", PROCESSOR_POWER6,
- POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
- | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP
- | MASK_MFPGPR | MASK_RECIP_PRECISION},
- {"power7", PROCESSOR_POWER7, /* Don't add MASK_ISEL by default */
- POWERPC_7400_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_MFCRF
- | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP | MASK_POPCNTD
- | MASK_VSX | MASK_RECIP_PRECISION},
- {"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK},
- {"powerpc64", PROCESSOR_POWERPC64,
- POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
- {"rios", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
- {"rios1", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
- {"rios2", PROCESSOR_RIOS2,
- MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING},
- {"rsc", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
- {"rsc1", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
- {"rs64", PROCESSOR_RS64A,
- POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64}
- };
-
- const size_t ptt_size = ARRAY_SIZE (processor_target_table);
-
- /* Some OSs don't support saving the high part of 64-bit registers on
- context switch. Other OSs don't support saving Altivec registers.
- On those OSs, we don't touch the MASK_POWERPC64 or MASK_ALTIVEC
- settings; if the user wants either, the user must explicitly specify
- them and we won't interfere with the user's specification. */
-
- enum {
- POWER_MASKS = MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING,
- POWERPC_MASKS = (POWERPC_BASE_MASK | MASK_PPC_GPOPT | MASK_STRICT_ALIGN
- | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_ALTIVEC
- | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_MULHW
- | MASK_DLMZB | MASK_CMPB | MASK_MFPGPR | MASK_DFP
- | MASK_POPCNTD | MASK_VSX | MASK_ISEL | MASK_NO_UPDATE
- | MASK_RECIP_PRECISION)
- };
-
- /* Masks for instructions set at various powerpc ISAs. */
- enum {
- ISA_2_1_MASKS = MASK_MFCRF,
- ISA_2_2_MASKS = (ISA_2_1_MASKS | MASK_POPCNTB | MASK_FPRND),
-
- /* For ISA 2.05, do not add MFPGPR, since it isn't in ISA 2.06, and don't
- add ALTIVEC, since in general it isn't a win on power6. In ISA 2.04,
- fsel, fre, fsqrt, etc. were no longer documented as optional. Group
- masks by server and embedded. */
- ISA_2_5_MASKS_EMBEDDED = (ISA_2_2_MASKS | MASK_CMPB | MASK_RECIP_PRECISION
- | MASK_PPC_GFXOPT | MASK_PPC_GPOPT),
- ISA_2_5_MASKS_SERVER = (ISA_2_5_MASKS_EMBEDDED | MASK_DFP),
-
- /* For ISA 2.06, don't add ISEL, since in general it isn't a win, but
- altivec is a win so enable it. */
- ISA_2_6_MASKS_EMBEDDED = (ISA_2_5_MASKS_EMBEDDED | MASK_POPCNTD),
- ISA_2_6_MASKS_SERVER = (ISA_2_5_MASKS_SERVER | MASK_POPCNTD | MASK_ALTIVEC
- | MASK_VSX)
- };
+ int cpu_index;
+ int tune_index;
+ struct cl_target_option *main_target_opt
+ = ((global_init_p || target_option_default_node == NULL)
+ ? NULL : TREE_TARGET_OPTION (target_option_default_node));
/* Numerous experiment shows that IRA based loop pressure
calculation works better for RTL loop invariant motion on targets
with enough (>= 32) registers. It is an expensive optimization.
So it is on only for peak performance. */
- if (optimize >= 3)
+ if (optimize >= 3 && global_init_p)
flag_ira_loop_pressure = 1;
/* Set the pointer size. */
set_masks &= ~target_flags_explicit;
/* Identify the processor type. */
- rs6000_select[0].string = default_cpu;
- rs6000_cpu = TARGET_POWERPC64 ? PROCESSOR_DEFAULT64 : PROCESSOR_DEFAULT;
-
- for (i = 0; i < ARRAY_SIZE (rs6000_select); i++)
- {
- ptr = &rs6000_select[i];
- if (ptr->string != (char *)0 && ptr->string[0] != '\0')
- {
- for (j = 0; j < ptt_size; j++)
- if (! strcmp (ptr->string, processor_target_table[j].name))
- {
- if (ptr->set_tune_p)
- rs6000_cpu = processor_target_table[j].processor;
+ if (!default_cpu)
+ {
+ if (TARGET_POWERPC64)
+ default_cpu = "powerpc64";
+ else if (TARGET_POWERPC)
+ default_cpu = "powerpc";
+ }
+
+ /* 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
+ with -mtune on the command line. */
+ if (rs6000_cpu_index > 0)
+ cpu_index = rs6000_cpu_index;
+ else if (main_target_opt != NULL && main_target_opt->x_rs6000_cpu_index > 0)
+ rs6000_cpu_index = cpu_index = main_target_opt->x_rs6000_cpu_index;
+ else
+ rs6000_cpu_index = cpu_index = rs6000_cpu_name_lookup (default_cpu);
- if (ptr->set_arch_p)
- {
- target_flags &= ~set_masks;
- target_flags |= (processor_target_table[j].target_enable
- & set_masks);
- }
- break;
- }
+ if (rs6000_tune_index > 0)
+ tune_index = rs6000_tune_index;
+ else
+ rs6000_tune_index = tune_index = cpu_index;
- if (j == ptt_size)
- error ("bad value (%s) for %s switch", ptr->string, ptr->name);
- }
+ if (cpu_index >= 0)
+ {
+ target_flags &= ~set_masks;
+ target_flags |= (processor_target_table[cpu_index].target_enable
+ & set_masks);
}
+ rs6000_cpu = ((tune_index >= 0)
+ ? processor_target_table[tune_index].processor
+ : (TARGET_POWERPC64
+ ? PROCESSOR_DEFAULT64
+ : PROCESSOR_DEFAULT));
+
if (rs6000_cpu == PROCESSOR_PPCE300C2 || rs6000_cpu == PROCESSOR_PPCE300C3
|| rs6000_cpu == PROCESSOR_PPCE500MC || rs6000_cpu == PROCESSOR_PPCE500MC64)
{
if (TARGET_ALTIVEC)
error ("AltiVec not supported in this target");
if (TARGET_SPE)
- error ("Spe not supported in this target");
+ error ("SPE not supported in this target");
}
/* Disable Cell microcode if we are optimizing for the Cell
target_flags |= (ISA_2_5_MASKS_SERVER & ~target_flags_explicit);
else if (TARGET_CMPB)
target_flags |= (ISA_2_5_MASKS_EMBEDDED & ~target_flags_explicit);
- else if (TARGET_POPCNTB || TARGET_FPRND)
+ else if (TARGET_FPRND)
+ target_flags |= (ISA_2_4_MASKS & ~target_flags_explicit);
+ else if (TARGET_POPCNTB)
target_flags |= (ISA_2_2_MASKS & ~target_flags_explicit);
else if (TARGET_ALTIVEC)
target_flags |= (MASK_PPC_GFXOPT & ~target_flags_explicit);
if (rs6000_block_move_inline_limit < (TARGET_POWERPC64 ? 64 : 32))
rs6000_block_move_inline_limit = (TARGET_POWERPC64 ? 64 : 32);
- /* Set debug flags */
- if (rs6000_debug_name)
- {
- if (! strcmp (rs6000_debug_name, "all"))
- rs6000_debug_stack = rs6000_debug_arg = rs6000_debug_reg
- = rs6000_debug_addr = rs6000_debug_cost = 1;
- else if (! strcmp (rs6000_debug_name, "stack"))
- rs6000_debug_stack = 1;
- else if (! strcmp (rs6000_debug_name, "arg"))
- rs6000_debug_arg = 1;
- else if (! strcmp (rs6000_debug_name, "reg"))
- rs6000_debug_reg = 1;
- else if (! strcmp (rs6000_debug_name, "addr"))
- rs6000_debug_addr = 1;
- else if (! strcmp (rs6000_debug_name, "cost"))
- rs6000_debug_cost = 1;
- else
- error ("unknown -mdebug-%s switch", rs6000_debug_name);
-
+ if (global_init_p)
+ {
/* If the appropriate debug option is enabled, replace the target hooks
with debug versions that call the real version and then prints
debugging information. */
rs6000_mode_dependent_address_ptr
= rs6000_debug_mode_dependent_address;
}
- }
- if (rs6000_traceback_name)
- {
- if (! strncmp (rs6000_traceback_name, "full", 4))
- rs6000_traceback = traceback_full;
- else if (! strncmp (rs6000_traceback_name, "part", 4))
- rs6000_traceback = traceback_part;
- else if (! strncmp (rs6000_traceback_name, "no", 2))
- rs6000_traceback = traceback_none;
- else
- error ("unknown -mtraceback arg %qs; expecting %<full%>, %<partial%> or %<none%>",
- rs6000_traceback_name);
+ if (rs6000_veclibabi_name)
+ {
+ if (strcmp (rs6000_veclibabi_name, "mass") == 0)
+ rs6000_veclib_handler = rs6000_builtin_vectorized_libmass;
+ else
+ {
+ error ("unknown vectorization library ABI type (%s) for "
+ "-mveclibabi= switch", rs6000_veclibabi_name);
+ ret = false;
+ }
+ }
}
- if (rs6000_veclibabi_name)
+ if (!rs6000_explicit_options.long_double)
{
- if (strcmp (rs6000_veclibabi_name, "mass") == 0)
- rs6000_veclib_handler = rs6000_builtin_vectorized_libmass;
+ if (main_target_opt != NULL
+ && (main_target_opt->x_rs6000_long_double_type_size
+ != RS6000_DEFAULT_LONG_DOUBLE_SIZE))
+ error ("target attribute or pragma changes long double size");
else
- error ("unknown vectorization library ABI type (%s) for "
- "-mveclibabi= switch", rs6000_veclibabi_name);
+ rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
}
- if (!rs6000_explicit_options.long_double)
- rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
-
#ifndef POWERPC_LINUX
if (!rs6000_explicit_options.ieee)
rs6000_ieeequad = 1;
#endif
+ /* Disable VSX and Altivec silently if the user switched cpus to power7 in a
+ target attribute or pragma which automatically enables both options,
+ unless the altivec ABI was set. This is set by default for 64-bit, but
+ not for 32-bit. */
+ if (main_target_opt != NULL && !main_target_opt->x_rs6000_altivec_abi)
+ target_flags &= ~((MASK_VSX | MASK_ALTIVEC) & ~target_flags_explicit);
+
/* Enable Altivec ABI for AIX -maltivec. */
if (TARGET_XCOFF && (TARGET_ALTIVEC || TARGET_VSX))
- rs6000_altivec_abi = 1;
+ {
+ if (main_target_opt != NULL && !main_target_opt->x_rs6000_altivec_abi)
+ error ("target attribute or pragma changes AltiVec ABI");
+ else
+ rs6000_altivec_abi = 1;
+ }
/* The AltiVec ABI is the default for PowerPC-64 GNU/Linux. For
PowerPC-32 GNU/Linux, -maltivec implies the AltiVec ABI. It can
{
if (!rs6000_explicit_options.altivec_abi
&& (TARGET_64BIT || TARGET_ALTIVEC || TARGET_VSX))
- rs6000_altivec_abi = 1;
+ {
+ if (main_target_opt != NULL &&
+ !main_target_opt->x_rs6000_altivec_abi)
+ error ("target attribute or pragma changes AltiVec ABI");
+ else
+ rs6000_altivec_abi = 1;
+ }
/* Enable VRSAVE for AltiVec ABI, unless explicitly overridden. */
if (!rs6000_explicit_options.vrsave)
&& DEFAULT_ABI == ABI_DARWIN
&& TARGET_64BIT)
{
- rs6000_darwin64_abi = 1;
- /* Default to natural alignment, for better performance. */
- rs6000_alignment_flags = MASK_ALIGN_NATURAL;
+ if (main_target_opt != NULL && !main_target_opt->x_rs6000_darwin64_abi)
+ error ("target attribute or pragma changes darwin64 ABI");
+ else
+ {
+ rs6000_darwin64_abi = 1;
+ /* Default to natural alignment, for better performance. */
+ rs6000_alignment_flags = MASK_ALIGN_NATURAL;
+ }
}
/* Place FP constants in the constant pool instead of TOC
if (flag_section_anchors)
TARGET_NO_FP_IN_TOC = 1;
- /* Handle -mtls-size option. */
- rs6000_parse_tls_size_option ();
-
#ifdef SUBTARGET_OVERRIDE_OPTIONS
SUBTARGET_OVERRIDE_OPTIONS;
#endif
/* For the powerpc-eabispe configuration, we set all these by
default, so let's unset them if we manually set another
CPU that is not the E500. */
- if (!rs6000_explicit_options.spe_abi)
- rs6000_spe_abi = 0;
- if (!rs6000_explicit_options.spe)
- rs6000_spe = 0;
- if (!rs6000_explicit_options.float_gprs)
- rs6000_float_gprs = 0;
+ if (main_target_opt != NULL
+ && ((main_target_opt->x_rs6000_spe_abi != rs6000_spe_abi)
+ || (main_target_opt->x_rs6000_spe != rs6000_spe)
+ || (main_target_opt->x_rs6000_float_gprs != rs6000_float_gprs)))
+ error ("target attribute or pragma changes SPE ABI");
+ else
+ {
+ if (!rs6000_explicit_options.spe_abi)
+ rs6000_spe_abi = 0;
+ if (!rs6000_explicit_options.spe)
+ rs6000_spe = 0;
+ if (!rs6000_explicit_options.float_gprs)
+ rs6000_float_gprs = 0;
+ }
if (!(target_flags_explicit & MASK_ISEL))
target_flags &= ~MASK_ISEL;
}
|| rs6000_cpu == PROCESSOR_PPCE500MC
|| rs6000_cpu == PROCESSOR_PPCE500MC64);
- /* Allow debug switches to override the above settings. */
- if (TARGET_ALWAYS_HINT > 0)
+ /* Allow debug switches to override the above settings. These are set to -1
+ in rs6000.opt to indicate the user hasn't directly set the switch. */
+ if (TARGET_ALWAYS_HINT >= 0)
rs6000_always_hint = TARGET_ALWAYS_HINT;
- if (TARGET_SCHED_GROUPS > 0)
+ if (TARGET_SCHED_GROUPS >= 0)
rs6000_sched_groups = TARGET_SCHED_GROUPS;
- if (TARGET_ALIGN_BRANCH_TARGETS > 0)
+ if (TARGET_ALIGN_BRANCH_TARGETS >= 0)
rs6000_align_branch_targets = TARGET_ALIGN_BRANCH_TARGETS;
rs6000_sched_restricted_insns_priority
atoi (rs6000_sched_insert_nops_str));
}
+ if (global_init_p)
+ {
#ifdef TARGET_REGNAMES
- /* If the user desires alternate register names, copy in the
- alternate names now. */
- if (TARGET_REGNAMES)
- memcpy (rs6000_reg_names, alt_reg_names, sizeof (rs6000_reg_names));
+ /* If the user desires alternate register names, copy in the
+ alternate names now. */
+ if (TARGET_REGNAMES)
+ memcpy (rs6000_reg_names, alt_reg_names, sizeof (rs6000_reg_names));
#endif
- /* Set aix_struct_return last, after the ABI is determined.
- If -maix-struct-return or -msvr4-struct-return was explicitly
- used, don't override with the ABI default. */
- if (!rs6000_explicit_options.aix_struct_ret)
- aix_struct_return = (DEFAULT_ABI != ABI_V4 || DRAFT_V4_STRUCT_RET);
+ /* Set aix_struct_return last, after the ABI is determined.
+ If -maix-struct-return or -msvr4-struct-return was explicitly
+ used, don't override with the ABI default. */
+ if (!rs6000_explicit_options.aix_struct_ret)
+ aix_struct_return = (DEFAULT_ABI != ABI_V4 || DRAFT_V4_STRUCT_RET);
#if 0
- /* IBM XL compiler defaults to unsigned bitfields. */
- if (TARGET_XL_COMPAT)
- flag_signed_bitfields = 0;
+ /* IBM XL compiler defaults to unsigned bitfields. */
+ if (TARGET_XL_COMPAT)
+ flag_signed_bitfields = 0;
#endif
- if (TARGET_LONG_DOUBLE_128 && !TARGET_IEEEQUAD)
- REAL_MODE_FORMAT (TFmode) = &ibm_extended_format;
+ if (TARGET_LONG_DOUBLE_128 && !TARGET_IEEEQUAD)
+ REAL_MODE_FORMAT (TFmode) = &ibm_extended_format;
- if (TARGET_TOC)
- ASM_GENERATE_INTERNAL_LABEL (toc_label_name, "LCTOC", 1);
-
- /* We can only guarantee the availability of DI pseudo-ops when
- assembling for 64-bit targets. */
- if (!TARGET_64BIT)
- {
- targetm.asm_out.aligned_op.di = NULL;
- targetm.asm_out.unaligned_op.di = NULL;
- }
+ if (TARGET_TOC)
+ ASM_GENERATE_INTERNAL_LABEL (toc_label_name, "LCTOC", 1);
- /* Set branch target alignment, if not optimizing for size. */
- if (!optimize_size)
- {
- /* Cell wants to be aligned 8byte for dual issue. Titan wants to be
- aligned 8byte to avoid misprediction by the branch predictor. */
- if (rs6000_cpu == PROCESSOR_TITAN
- || rs6000_cpu == PROCESSOR_CELL)
+ /* We can only guarantee the availability of DI pseudo-ops when
+ assembling for 64-bit targets. */
+ if (!TARGET_64BIT)
{
- if (align_functions <= 0)
- align_functions = 8;
- if (align_jumps <= 0)
- align_jumps = 8;
- if (align_loops <= 0)
- align_loops = 8;
- }
- if (rs6000_align_branch_targets)
+ targetm.asm_out.aligned_op.di = NULL;
+ targetm.asm_out.unaligned_op.di = NULL;
+ }
+
+
+ /* Set branch target alignment, if not optimizing for size. */
+ if (!optimize_size)
{
- if (align_functions <= 0)
- align_functions = 16;
- if (align_jumps <= 0)
- align_jumps = 16;
- if (align_loops <= 0)
- align_loops = 16;
+ /* Cell wants to be aligned 8byte for dual issue. Titan wants to be
+ aligned 8byte to avoid misprediction by the branch predictor. */
+ if (rs6000_cpu == PROCESSOR_TITAN
+ || rs6000_cpu == PROCESSOR_CELL)
+ {
+ if (align_functions <= 0)
+ align_functions = 8;
+ if (align_jumps <= 0)
+ align_jumps = 8;
+ if (align_loops <= 0)
+ align_loops = 8;
+ }
+ if (rs6000_align_branch_targets)
+ {
+ if (align_functions <= 0)
+ align_functions = 16;
+ if (align_jumps <= 0)
+ align_jumps = 16;
+ if (align_loops <= 0)
+ {
+ can_override_loop_align = 1;
+ align_loops = 16;
+ }
+ }
+ if (align_jumps_max_skip <= 0)
+ align_jumps_max_skip = 15;
+ if (align_loops_max_skip <= 0)
+ align_loops_max_skip = 15;
}
- if (align_jumps_max_skip <= 0)
- align_jumps_max_skip = 15;
- if (align_loops_max_skip <= 0)
- align_loops_max_skip = 15;
- }
- /* Arrange to save and restore machine status around nested functions. */
- init_machine_status = rs6000_init_machine_status;
+ /* Arrange to save and restore machine status around nested functions. */
+ init_machine_status = rs6000_init_machine_status;
- /* We should always be splitting complex arguments, but we can't break
- Linux and Darwin ABIs at the moment. For now, only AIX is fixed. */
- if (DEFAULT_ABI != ABI_AIX)
- targetm.calls.split_complex_arg = NULL;
+ /* We should always be splitting complex arguments, but we can't break
+ Linux and Darwin ABIs at the moment. For now, only AIX is fixed. */
+ if (DEFAULT_ABI != ABI_AIX)
+ targetm.calls.split_complex_arg = NULL;
+ }
/* Initialize rs6000_cost with the appropriate target costs. */
if (optimize_size)
gcc_unreachable ();
}
- maybe_set_param_value (PARAM_SIMULTANEOUS_PREFETCHES,
- rs6000_cost->simultaneous_prefetches,
- global_options.x_param_values,
- global_options_set.x_param_values);
- maybe_set_param_value (PARAM_L1_CACHE_SIZE, rs6000_cost->l1_cache_size,
- global_options.x_param_values,
- global_options_set.x_param_values);
- maybe_set_param_value (PARAM_L1_CACHE_LINE_SIZE,
- rs6000_cost->cache_line_size,
- global_options.x_param_values,
- global_options_set.x_param_values);
- maybe_set_param_value (PARAM_L2_CACHE_SIZE, rs6000_cost->l2_cache_size,
- global_options.x_param_values,
- global_options_set.x_param_values);
-
- /* If using typedef char *va_list, signal that __builtin_va_start (&ap, 0)
- can be optimized to ap = __builtin_next_arg (0). */
- if (DEFAULT_ABI != ABI_V4)
- targetm.expand_builtin_va_start = NULL;
+ if (global_init_p)
+ {
+ maybe_set_param_value (PARAM_SIMULTANEOUS_PREFETCHES,
+ rs6000_cost->simultaneous_prefetches,
+ global_options.x_param_values,
+ global_options_set.x_param_values);
+ maybe_set_param_value (PARAM_L1_CACHE_SIZE, rs6000_cost->l1_cache_size,
+ global_options.x_param_values,
+ global_options_set.x_param_values);
+ maybe_set_param_value (PARAM_L1_CACHE_LINE_SIZE,
+ rs6000_cost->cache_line_size,
+ global_options.x_param_values,
+ global_options_set.x_param_values);
+ maybe_set_param_value (PARAM_L2_CACHE_SIZE, rs6000_cost->l2_cache_size,
+ global_options.x_param_values,
+ global_options_set.x_param_values);
+
+ /* If using typedef char *va_list, signal that
+ __builtin_va_start (&ap, 0) can be optimized to
+ ap = __builtin_next_arg (0). */
+ if (DEFAULT_ABI != ABI_V4)
+ targetm.expand_builtin_va_start = NULL;
+ }
/* Set up single/double float flags.
If TARGET_HARD_FLOAT is set, but neither single or double is set,
rs6000_single_float = rs6000_double_float = 1;
}
+ if (main_target_opt)
+ {
+ if (main_target_opt->x_rs6000_single_float != rs6000_single_float)
+ error ("target attribute or pragma changes single precision floating "
+ "point");
+ if (main_target_opt->x_rs6000_double_float != rs6000_double_float)
+ error ("target attribute or pragma changes double precision floating "
+ "point");
+ }
+
/* If not explicitly specified via option, decide whether to generate indexed
load/store instructions. */
if (TARGET_AVOID_XFORM == -1)
if (i == ARRAY_SIZE (recip_options))
{
- error ("Unknown option for -mrecip=%s", q);
+ error ("unknown option for -mrecip=%s", q);
invert = false;
mask = 0;
+ ret = false;
}
}
}
}
- rs6000_init_hard_regno_mode_ok ();
+ rs6000_init_hard_regno_mode_ok (global_init_p);
+
+ /* Save the initial options in case the user does function specific options */
+ if (global_init_p)
+ target_option_default_node = target_option_current_node
+ = build_target_option_node ();
+
+ return ret;
}
/* Implement TARGET_OPTION_OVERRIDE. On the RS/6000 this is used to
static void
rs6000_option_override (void)
{
- rs6000_option_override_internal (OPTION_TARGET_CPU_DEFAULT);
+ (void) rs6000_option_override_internal (true);
}
+\f
/* Implement targetm.vectorize.builtin_mask_for_load. */
static tree
rs6000_builtin_mask_for_load (void)
return 0;
}
+/* Implement LOOP_ALIGN. */
+int
+rs6000_loop_align (rtx label)
+{
+ basic_block bb;
+ int ninsns;
+
+ /* Don't override loop alignment if -falign-loops was specified. */
+ if (!can_override_loop_align)
+ return align_loops_log;
+
+ bb = BLOCK_FOR_INSN (label);
+ ninsns = num_loop_insns(bb->loop_father);
+
+ /* Align small loops to 32 bytes to fit in an icache sector, otherwise return default. */
+ if (ninsns > 4 && ninsns <= 8
+ && (rs6000_cpu == PROCESSOR_POWER4
+ || rs6000_cpu == PROCESSOR_POWER5
+ || rs6000_cpu == PROCESSOR_POWER6
+ || rs6000_cpu == PROCESSOR_POWER7))
+ return 5;
+ else
+ return align_loops_log;
+}
+
+/* Implement TARGET_LOOP_ALIGN_MAX_SKIP. */
+static int
+rs6000_loop_align_max_skip (rtx label)
+{
+ return (1 << rs6000_loop_align (label)) - 1;
+}
+
/* Implement targetm.vectorize.builtin_conversion.
Returns a decl of a function that implements conversion of an integer vector
into a floating-point vector, or vice-versa. DEST_TYPE is the
error ("unknown -m%s= option specified: '%s'", name, value);
}
-/* Validate and record the size specified with the -mtls-size option. */
-
-static void
-rs6000_parse_tls_size_option (void)
-{
- if (rs6000_tls_size_string == 0)
- return;
- else if (strcmp (rs6000_tls_size_string, "16") == 0)
- rs6000_tls_size = 16;
- else if (strcmp (rs6000_tls_size_string, "32") == 0)
- rs6000_tls_size = 32;
- else if (strcmp (rs6000_tls_size_string, "64") == 0)
- rs6000_tls_size = 64;
- else
- error ("bad value %qs for -mtls-size switch", rs6000_tls_size_string);
-}
-
/* Implement TARGET_OPTION_INIT_STRUCT. */
static void
{
enum fpu_type_t fpu_type = FPU_NONE;
int isel;
+ char *p, *q;
switch (code)
{
#if defined (HAVE_LD_LARGE_TOC) && defined (TARGET_USES_LINUX64_OPT)
case OPT_mcmodel_:
if (strcmp (arg, "small") == 0)
- cmodel = CMODEL_SMALL;
+ rs6000_current_cmodel = CMODEL_SMALL;
else if (strcmp (arg, "medium") == 0)
- cmodel = CMODEL_MEDIUM;
+ rs6000_current_cmodel = CMODEL_MEDIUM;
else if (strcmp (arg, "large") == 0)
- cmodel = CMODEL_LARGE;
+ rs6000_current_cmodel = CMODEL_LARGE;
else
{
error ("invalid option for -mcmodel: '%s'", arg);
break;
case OPT_mdebug_:
- rs6000_debug_name = arg;
+ p = ASTRDUP (arg);
+ rs6000_debug = 0;
+
+ while ((q = strtok (p, ",")) != NULL)
+ {
+ unsigned mask = 0;
+ bool invert;
+
+ p = NULL;
+ if (*q == '!')
+ {
+ invert = true;
+ q++;
+ }
+ else
+ invert = false;
+
+ if (! strcmp (q, "all"))
+ mask = MASK_DEBUG_ALL;
+ else if (! strcmp (q, "stack"))
+ mask = MASK_DEBUG_STACK;
+ else if (! strcmp (q, "arg"))
+ mask = MASK_DEBUG_ARG;
+ else if (! strcmp (q, "reg"))
+ mask = MASK_DEBUG_REG;
+ else if (! strcmp (q, "addr"))
+ mask = MASK_DEBUG_ADDR;
+ else if (! strcmp (q, "cost"))
+ mask = MASK_DEBUG_COST;
+ else if (! strcmp (q, "target"))
+ mask = MASK_DEBUG_TARGET;
+ else
+ error ("unknown -mdebug-%s switch", q);
+
+ if (invert)
+ rs6000_debug &= ~mask;
+ else
+ rs6000_debug |= mask;
+ }
break;
#ifdef TARGET_USES_SYSV4_OPT
break;
case OPT_mtls_size_:
- rs6000_tls_size_string = arg;
+ if (strcmp (arg, "16") == 0)
+ rs6000_tls_size = 16;
+ else if (strcmp (arg, "32") == 0)
+ rs6000_tls_size = 32;
+ else if (strcmp (arg, "64") == 0)
+ rs6000_tls_size = 64;
+ else
+ error ("bad value %qs for -mtls-size switch", arg);
break;
case OPT_mrelocatable:
else if (! strcmp (arg, "d64"))
{
rs6000_darwin64_abi = 1;
- warning (0, "Using darwin64 ABI");
+ warning (0, "using darwin64 ABI");
}
else if (! strcmp (arg, "d32"))
{
rs6000_darwin64_abi = 0;
- warning (0, "Using old darwin ABI");
+ warning (0, "using old darwin ABI");
}
else if (! strcmp (arg, "ibmlongdouble"))
{
rs6000_explicit_options.ieee = true;
rs6000_ieeequad = 0;
- warning (0, "Using IBM extended precision long double");
+ warning (0, "using IBM extended precision long double");
}
else if (! strcmp (arg, "ieeelongdouble"))
{
rs6000_explicit_options.ieee = true;
rs6000_ieeequad = 1;
- warning (0, "Using IEEE extended precision long double");
+ warning (0, "using IEEE extended precision long double");
}
else
case OPT_mcpu_:
rs6000_select[1].string = arg;
+ rs6000_cpu_index = rs6000_cpu_name_lookup (arg);
+ if (rs6000_cpu_index < 0)
+ error ("bad value (%s) for -mcpu", arg);
break;
case OPT_mtune_:
rs6000_select[2].string = arg;
+ rs6000_tune_index = rs6000_cpu_name_lookup (arg);
+ if (rs6000_tune_index < 0)
+ error ("bad value (%s) for -mtune", arg);
break;
case OPT_mtraceback_:
- rs6000_traceback_name = arg;
+ if (! strncmp (arg, "full", 4))
+ rs6000_traceback = traceback_full;
+ else if (! strncmp (arg, "part", 4))
+ rs6000_traceback = traceback_part;
+ else if (! strncmp (arg, "no", 2))
+ rs6000_traceback = traceback_none;
+ else
+ error ("unknown -mtraceback arg %qs; expecting %<full%>, "
+ "%<partial%> or %<none%>", arg);
break;
case OPT_mfloat_gprs_:
rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
if (value != 64 && value != 128)
{
- error ("Unknown switch -mlong-double-%s", arg);
+ error ("unknown switch -mlong-double-%s", arg);
rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
return false;
}
{
enum machine_mode mode = GET_MODE (vec);
enum machine_mode inner_mode = GET_MODE_INNER (mode);
- rtx mem, x;
+ rtx mem;
if (VECTOR_MEM_VSX_P (mode) && (mode == V2DFmode || mode == V2DImode))
{
/* Allocate mode-sized buffer. */
mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+ emit_move_insn (mem, vec);
+
/* Add offset to field within buffer matching vector element. */
- mem = adjust_address_nv (mem, mode, elt * GET_MODE_SIZE (inner_mode));
+ mem = adjust_address_nv (mem, inner_mode, elt * GET_MODE_SIZE (inner_mode));
- /* Store single field into mode-sized buffer. */
- x = gen_rtx_UNSPEC (VOIDmode,
- gen_rtvec (1, const0_rtx), UNSPEC_STVE);
- emit_insn (gen_rtx_PARALLEL (VOIDmode,
- gen_rtvec (2,
- gen_rtx_SET (VOIDmode,
- mem, vec),
- x)));
emit_move_insn (target, adjust_address_nv (mem, inner_mode, 0));
}
}
/* Change register usage conditional on target flags. */
-void
+static void
rs6000_conditional_register_usage (void)
{
int i;
+ if (TARGET_DEBUG_TARGET)
+ fprintf (stderr, "rs6000_conditional_register_usage called\n");
+
/* Set MQ register fixed (already call_used) if not POWER
architecture (RIOS1, RIOS2, RSC, and PPC601) so that it will not
be allocated. */
Quadword align Altivec vectors.
Quadword align large synthetic vector types. */
-int
-function_arg_boundary (enum machine_mode mode, const_tree type)
+static unsigned int
+rs6000_function_arg_boundary (enum machine_mode mode, const_tree type)
{
if (DEFAULT_ABI == ABI_V4
&& (GET_MODE_SIZE (mode) == 8
unsigned int align;
unsigned int parm_offset;
- align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
+ align = rs6000_function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
parm_offset = DEFAULT_ABI == ABI_V4 ? 2 : 6;
return nwords + (-(parm_offset + nwords) & align);
}
f_ovf = DECL_CHAIN (f_res);
f_sav = DECL_CHAIN (f_ovf);
- valist = build_va_arg_indirect_ref (valist);
+ valist = build_simple_mem_ref (valist);
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), unshare_expr (valist),
f_fpr, NULL_TREE);
unsigned HOST_WIDE_INT align, boundary;
tree valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
align = PARM_BOUNDARY / BITS_PER_UNIT;
- boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type);
+ boundary = rs6000_function_arg_boundary (TYPE_MODE (type), type);
if (boundary > MAX_SUPPORTED_STACK_ALIGNMENT)
boundary = MAX_SUPPORTED_STACK_ALIGNMENT;
boundary /= BITS_PER_UNIT;
{
tree t;
if (rs6000_builtin_decls[code])
- fatal_error ("internal error: builtin function to %s already processed.",
+ fatal_error ("internal error: builtin function to %s already processed",
name);
rs6000_builtin_decls[code] = t =
static const struct builtin_description bdesc_3arg[] =
{
- { MASK_ALTIVEC, CODE_FOR_altivec_vmaddfp, "__builtin_altivec_vmaddfp", ALTIVEC_BUILTIN_VMADDFP },
+ { MASK_ALTIVEC, CODE_FOR_fmav4sf4, "__builtin_altivec_vmaddfp", ALTIVEC_BUILTIN_VMADDFP },
{ MASK_ALTIVEC, CODE_FOR_altivec_vmhaddshs, "__builtin_altivec_vmhaddshs", ALTIVEC_BUILTIN_VMHADDSHS },
{ MASK_ALTIVEC, CODE_FOR_altivec_vmhraddshs, "__builtin_altivec_vmhraddshs", ALTIVEC_BUILTIN_VMHRADDSHS },
{ MASK_ALTIVEC, CODE_FOR_altivec_vmladduhm, "__builtin_altivec_vmladduhm", ALTIVEC_BUILTIN_VMLADDUHM},
{ MASK_ALTIVEC, CODE_FOR_altivec_vmsumshm, "__builtin_altivec_vmsumshm", ALTIVEC_BUILTIN_VMSUMSHM },
{ MASK_ALTIVEC, CODE_FOR_altivec_vmsumuhs, "__builtin_altivec_vmsumuhs", ALTIVEC_BUILTIN_VMSUMUHS },
{ MASK_ALTIVEC, CODE_FOR_altivec_vmsumshs, "__builtin_altivec_vmsumshs", ALTIVEC_BUILTIN_VMSUMSHS },
- { MASK_ALTIVEC, CODE_FOR_altivec_vnmsubfp, "__builtin_altivec_vnmsubfp", ALTIVEC_BUILTIN_VNMSUBFP },
+ { MASK_ALTIVEC, CODE_FOR_nfmsv4sf4, "__builtin_altivec_vnmsubfp", ALTIVEC_BUILTIN_VNMSUBFP },
{ MASK_ALTIVEC, CODE_FOR_altivec_vperm_v2df, "__builtin_altivec_vperm_2df", ALTIVEC_BUILTIN_VPERM_2DF },
{ MASK_ALTIVEC, CODE_FOR_altivec_vperm_v2di, "__builtin_altivec_vperm_2di", ALTIVEC_BUILTIN_VPERM_2DI },
{ MASK_ALTIVEC, CODE_FOR_altivec_vperm_v4sf, "__builtin_altivec_vperm_4sf", ALTIVEC_BUILTIN_VPERM_4SF },
{ MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_perm", ALTIVEC_BUILTIN_VEC_PERM },
{ MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sel", ALTIVEC_BUILTIN_VEC_SEL },
- { MASK_VSX, CODE_FOR_vsx_fmaddv2df4, "__builtin_vsx_xvmadddp", VSX_BUILTIN_XVMADDDP },
- { MASK_VSX, CODE_FOR_vsx_fmsubv2df4, "__builtin_vsx_xvmsubdp", VSX_BUILTIN_XVMSUBDP },
- { MASK_VSX, CODE_FOR_vsx_fnmaddv2df4, "__builtin_vsx_xvnmadddp", VSX_BUILTIN_XVNMADDDP },
- { MASK_VSX, CODE_FOR_vsx_fnmsubv2df4, "__builtin_vsx_xvnmsubdp", VSX_BUILTIN_XVNMSUBDP },
+ { MASK_VSX, CODE_FOR_fmav2df4, "__builtin_vsx_xvmadddp", VSX_BUILTIN_XVMADDDP },
+ { MASK_VSX, CODE_FOR_fmsv2df4, "__builtin_vsx_xvmsubdp", VSX_BUILTIN_XVMSUBDP },
+ { MASK_VSX, CODE_FOR_nfmav2df4, "__builtin_vsx_xvnmadddp", VSX_BUILTIN_XVNMADDDP },
+ { MASK_VSX, CODE_FOR_nfmsv2df4, "__builtin_vsx_xvnmsubdp", VSX_BUILTIN_XVNMSUBDP },
- { MASK_VSX, CODE_FOR_vsx_fmaddv4sf4, "__builtin_vsx_xvmaddsp", VSX_BUILTIN_XVMADDSP },
- { MASK_VSX, CODE_FOR_vsx_fmsubv4sf4, "__builtin_vsx_xvmsubsp", VSX_BUILTIN_XVMSUBSP },
- { MASK_VSX, CODE_FOR_vsx_fnmaddv4sf4, "__builtin_vsx_xvnmaddsp", VSX_BUILTIN_XVNMADDSP },
- { MASK_VSX, CODE_FOR_vsx_fnmsubv4sf4, "__builtin_vsx_xvnmsubsp", VSX_BUILTIN_XVNMSUBSP },
+ { MASK_VSX, CODE_FOR_fmav4sf4, "__builtin_vsx_xvmaddsp", VSX_BUILTIN_XVMADDSP },
+ { MASK_VSX, CODE_FOR_fmsv4sf4, "__builtin_vsx_xvmsubsp", VSX_BUILTIN_XVMSUBSP },
+ { MASK_VSX, CODE_FOR_nfmav4sf4, "__builtin_vsx_xvnmaddsp", VSX_BUILTIN_XVNMADDSP },
+ { MASK_VSX, CODE_FOR_nfmsv4sf4, "__builtin_vsx_xvnmsubsp", VSX_BUILTIN_XVNMSUBSP },
{ MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_msub", VSX_BUILTIN_VEC_MSUB },
{ MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_nmadd", VSX_BUILTIN_VEC_NMADD },
{ MASK_VSX, CODE_FOR_vsx_xxsldwi_v16qi, "__builtin_vsx_xxsldwi_16qi", VSX_BUILTIN_XXSLDWI_16QI },
{ MASK_VSX, CODE_FOR_nothing, "__builtin_vsx_xxsldwi", VSX_BUILTIN_VEC_XXSLDWI },
- { 0, CODE_FOR_paired_msub, "__builtin_paired_msub", PAIRED_BUILTIN_MSUB },
- { 0, CODE_FOR_paired_madd, "__builtin_paired_madd", PAIRED_BUILTIN_MADD },
+ { 0, CODE_FOR_fmsv2sf4, "__builtin_paired_msub", PAIRED_BUILTIN_MSUB },
+ { 0, CODE_FOR_fmav2sf4, "__builtin_paired_madd", PAIRED_BUILTIN_MADD },
{ 0, CODE_FOR_paired_madds0, "__builtin_paired_madds0", PAIRED_BUILTIN_MADDS0 },
{ 0, CODE_FOR_paired_madds1, "__builtin_paired_madds1", PAIRED_BUILTIN_MADDS1 },
- { 0, CODE_FOR_paired_nmsub, "__builtin_paired_nmsub", PAIRED_BUILTIN_NMSUB },
- { 0, CODE_FOR_paired_nmadd, "__builtin_paired_nmadd", PAIRED_BUILTIN_NMADD },
+ { 0, CODE_FOR_nfmsv2sf4, "__builtin_paired_nmsub", PAIRED_BUILTIN_NMSUB },
+ { 0, CODE_FOR_nfmav2sf4, "__builtin_paired_nmadd", PAIRED_BUILTIN_NMADD },
{ 0, CODE_FOR_paired_sum0, "__builtin_paired_sum0", PAIRED_BUILTIN_SUM0 },
{ 0, CODE_FOR_paired_sum1, "__builtin_paired_sum1", PAIRED_BUILTIN_SUM1 },
{ 0, CODE_FOR_selv2sf4, "__builtin_paired_selv2sf4", PAIRED_BUILTIN_SELV2SF4 },
rtx op2 = expand_normal (arg2);
rtx pat, addr;
enum machine_mode tmode = insn_data[icode].operand[0].mode;
+ enum machine_mode smode = insn_data[icode].operand[1].mode;
enum machine_mode mode1 = Pmode;
enum machine_mode mode2 = Pmode;
|| arg2 == error_mark_node)
return const0_rtx;
- if (! (*insn_data[icode].operand[1].predicate) (op0, tmode))
- op0 = copy_to_mode_reg (tmode, op0);
+ if (! (*insn_data[icode].operand[1].predicate) (op0, smode))
+ op0 = copy_to_mode_reg (smode, op0);
op2 = copy_to_mode_reg (mode2, op2);
return pic_offset_table_rtx;
}
\f
+static rs6000_stack_t stack_info;
+
/* Function to init struct machine_function.
This will be called, via a pointer variable,
from push_function_context. */
static struct machine_function *
rs6000_init_machine_status (void)
{
+ stack_info.reload_completed = 0;
return ggc_alloc_cleared_machine_function ();
}
\f
rtx target;
/* VSX/altivec have direct min/max insns. */
- if ((code == SMAX || code == SMIN) && VECTOR_UNIT_ALTIVEC_OR_VSX_P (mode))
+ if ((code == SMAX || code == SMIN)
+ && (VECTOR_UNIT_ALTIVEC_OR_VSX_P (mode)
+ || (mode == SFmode && VECTOR_UNIT_VSX_P (DFmode))))
{
emit_insn (gen_rtx_SET (VOIDmode,
dest,
}
\f
+/* Determine the strategy for savings/restoring registers. */
+
+enum {
+ SAVRES_MULTIPLE = 0x1,
+ SAVE_INLINE_FPRS = 0x2,
+ SAVE_INLINE_GPRS = 0x4,
+ REST_INLINE_FPRS = 0x8,
+ REST_INLINE_GPRS = 0x10,
+ SAVE_NOINLINE_GPRS_SAVES_LR = 0x20,
+ SAVE_NOINLINE_FPRS_SAVES_LR = 0x40,
+ REST_NOINLINE_FPRS_DOESNT_RESTORE_LR = 0x80
+};
+
+static int
+rs6000_savres_strategy (rs6000_stack_t *info,
+ bool using_static_chain_p)
+{
+ int strategy = 0;
+
+ if (TARGET_MULTIPLE
+ && !TARGET_POWERPC64
+ && !(TARGET_SPE_ABI && info->spe_64bit_regs_used)
+ && info->first_gp_reg_save < 31
+ && no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true))
+ strategy |= SAVRES_MULTIPLE;
+
+ if (crtl->calls_eh_return
+ || cfun->machine->ra_need_lr
+ || info->total_size > 32767)
+ strategy |= (SAVE_INLINE_FPRS | REST_INLINE_FPRS
+ | SAVE_INLINE_GPRS | REST_INLINE_GPRS);
+
+ if (info->first_fp_reg_save == 64
+ || FP_SAVE_INLINE (info->first_fp_reg_save)
+ /* The out-of-line FP routines use double-precision stores;
+ we can't use those routines if we don't have such stores. */
+ || (TARGET_HARD_FLOAT && !TARGET_DOUBLE_FLOAT)
+ || !no_global_regs_above (info->first_fp_reg_save, /*gpr=*/false))
+ strategy |= SAVE_INLINE_FPRS | REST_INLINE_FPRS;
+
+ if (info->first_gp_reg_save == 32
+ || GP_SAVE_INLINE (info->first_gp_reg_save)
+ || !((strategy & SAVRES_MULTIPLE)
+ || no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true)))
+ strategy |= SAVE_INLINE_GPRS | REST_INLINE_GPRS;
+
+ /* Don't bother to try to save things out-of-line if r11 is occupied
+ by the static chain. It would require too much fiddling and the
+ static chain is rarely used anyway. */
+ if (using_static_chain_p)
+ strategy |= SAVE_INLINE_FPRS | SAVE_INLINE_GPRS;
+
+ /* If we are going to use store multiple, then don't even bother
+ with the out-of-line routines, since the store-multiple
+ instruction will always be smaller. */
+ if ((strategy & SAVRES_MULTIPLE))
+ strategy |= SAVE_INLINE_GPRS;
+
+ /* The situation is more complicated with load multiple. We'd
+ prefer to use the out-of-line routines for restores, since the
+ "exit" out-of-line routines can handle the restore of LR and the
+ frame teardown. However if doesn't make sense to use the
+ out-of-line routine if that is the only reason we'd need to save
+ LR, and we can't use the "exit" out-of-line gpr restore if we
+ have saved some fprs; In those cases it is advantageous to use
+ load multiple when available. */
+ if ((strategy & SAVRES_MULTIPLE)
+ && (!info->lr_save_p
+ || info->first_fp_reg_save != 64))
+ strategy |= REST_INLINE_GPRS;
+
+ /* We can only use load multiple or the out-of-line routines to
+ restore if we've used store multiple or out-of-line routines
+ in the prologue, i.e. if we've saved all the registers from
+ first_gp_reg_save. Otherwise, we risk loading garbage. */
+ if ((strategy & (SAVE_INLINE_GPRS | SAVRES_MULTIPLE)) == SAVE_INLINE_GPRS)
+ strategy |= REST_INLINE_GPRS;
+
+ /* Saving CR interferes with the exit routines used on the SPE, so
+ just punt here. */
+ if (TARGET_SPE_ABI
+ && info->spe_64bit_regs_used
+ && info->cr_save_p)
+ strategy |= REST_INLINE_GPRS;
+
+#ifdef POWERPC_LINUX
+ if (TARGET_64BIT)
+ {
+ if (!(strategy & SAVE_INLINE_FPRS))
+ strategy |= SAVE_NOINLINE_FPRS_SAVES_LR;
+ else if (!(strategy & SAVE_INLINE_GPRS)
+ && info->first_fp_reg_save == 64)
+ strategy |= SAVE_NOINLINE_GPRS_SAVES_LR;
+ }
+#else
+ if (TARGET_AIX && !(strategy & REST_INLINE_FPRS))
+ strategy |= REST_NOINLINE_FPRS_DOESNT_RESTORE_LR;
+#endif
+ return strategy;
+}
+
/* Calculate the stack information for the current function. This is
complicated by having two separate calling sequences, the AIX calling
sequence and the V.4 calling sequence.
static rs6000_stack_t *
rs6000_stack_info (void)
{
- static rs6000_stack_t info;
- rs6000_stack_t *info_ptr = &info;
+#ifdef ENABLE_CHECKING
+ static rs6000_stack_t info_save;
+#endif
+ rs6000_stack_t *info_ptr = &stack_info;
int reg_size = TARGET_32BIT ? 4 : 8;
int ehrd_size;
int save_align;
int first_gp;
HOST_WIDE_INT non_fixed_size;
+ bool using_static_chain_p;
- memset (&info, 0, sizeof (info));
+#ifdef ENABLE_CHECKING
+ memcpy (&info_save, &stack_info, sizeof stack_info);
+#else
+ if (reload_completed && info_ptr->reload_completed)
+ return info_ptr;
+#endif
+
+ memset (&stack_info, 0, sizeof (stack_info));
+ info_ptr->reload_completed = reload_completed;
if (TARGET_SPE)
{
info_ptr->calls_p = (! current_function_is_leaf
|| cfun->machine->ra_needs_full_frame);
- /* Determine if we need to save the link register. */
- if ((DEFAULT_ABI == ABI_AIX
- && crtl->profile
- && !TARGET_PROFILE_KERNEL)
-#ifdef TARGET_RELOCATABLE
- || (TARGET_RELOCATABLE && (get_pool_size () != 0))
-#endif
- || (info_ptr->first_fp_reg_save != 64
- && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save))
- || (DEFAULT_ABI == ABI_V4 && cfun->calls_alloca)
- || info_ptr->calls_p
- || rs6000_ra_ever_killed ())
- {
- info_ptr->lr_save_p = 1;
- df_set_regs_ever_live (LR_REGNO, true);
- }
-
/* Determine if we need to save the condition code registers. */
if (df_regs_ever_live_p (CR2_REGNO)
|| df_regs_ever_live_p (CR3_REGNO)
info_ptr->total_size = RS6000_ALIGN (non_fixed_size + info_ptr->fixed_size,
ABI_STACK_BOUNDARY / BITS_PER_UNIT);
+ /* Determine if we need to save the link register. */
+ if (info_ptr->calls_p
+ || (DEFAULT_ABI == ABI_AIX
+ && crtl->profile
+ && !TARGET_PROFILE_KERNEL)
+ || (DEFAULT_ABI == ABI_V4 && cfun->calls_alloca)
+#ifdef TARGET_RELOCATABLE
+ || (TARGET_RELOCATABLE && (get_pool_size () != 0))
+#endif
+ || rs6000_ra_ever_killed ())
+ info_ptr->lr_save_p = 1;
+
+ using_static_chain_p = (cfun->static_chain_decl != NULL_TREE
+ && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
+ && call_used_regs[STATIC_CHAIN_REGNUM]);
+ info_ptr->savres_strategy = rs6000_savres_strategy (info_ptr,
+ using_static_chain_p);
+
+ if (!(info_ptr->savres_strategy & SAVE_INLINE_GPRS)
+ || !(info_ptr->savres_strategy & SAVE_INLINE_FPRS)
+ || !(info_ptr->savres_strategy & REST_INLINE_GPRS)
+ || !(info_ptr->savres_strategy & REST_INLINE_FPRS))
+ info_ptr->lr_save_p = 1;
+
+ if (info_ptr->lr_save_p)
+ df_set_regs_ever_live (LR_REGNO, true);
+
/* Determine if we need to allocate any stack frame:
For AIX we need to push the stack if a frame pointer is needed
if (! info_ptr->cr_save_p)
info_ptr->cr_save_offset = 0;
+#ifdef ENABLE_CHECKING
+ gcc_assert (!(reload_completed && info_save.reload_completed)
+ || memcmp (&info_save, &stack_info, sizeof stack_info) == 0);
+#endif
return info_ptr;
}
char buf[30];
rtx lab, tmp1, tmp2, got;
- ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
+ lab = gen_label_rtx ();
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (lab));
lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
if (flag_pic == 2)
got = gen_rtx_SYMBOL_REF (Pmode, toc_label_name);
tmp2 = gen_reg_rtx (Pmode);
}
emit_insn (gen_load_toc_v4_PIC_1 (lab));
- emit_move_insn (tmp1,
- gen_rtx_REG (Pmode, LR_REGNO));
+ emit_move_insn (tmp1, gen_rtx_REG (Pmode, LR_REGNO));
emit_insn (gen_load_toc_v4_PIC_3b (tmp2, tmp1, got, lab));
emit_insn (gen_load_toc_v4_PIC_3c (dest, tmp2, got, lab));
}
symL = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
emit_insn (gen_load_toc_v4_PIC_1 (symF));
- emit_move_insn (dest,
- gen_rtx_REG (Pmode, LR_REGNO));
+ emit_move_insn (dest, gen_rtx_REG (Pmode, LR_REGNO));
emit_insn (gen_load_toc_v4_PIC_2 (temp0, dest, symL, symF));
}
else
}
}
else if (DEFAULT_ABI == ABI_DARWIN)
- sorry ("Out-of-line save/restore routines not supported on Darwin");
+ sorry ("out-of-line save/restore routines not supported on Darwin");
sprintf (savres_routine_name, "%s%d%s", prefix, regno, suffix);
return (((crtl->calls_eh_return || df_regs_ever_live_p (reg))
&& (!call_used_regs[reg]
|| (reg == RS6000_PIC_OFFSET_TABLE_REGNUM
+ && !TARGET_SINGLE_PIC_BASE
&& TARGET_TOC && TARGET_MINIMAL_TOC)))
|| (reg == RS6000_PIC_OFFSET_TABLE_REGNUM
+ && !TARGET_SINGLE_PIC_BASE
&& ((DEFAULT_ABI == ABI_V4 && flag_pic != 0)
|| (DEFAULT_ABI == ABI_DARWIN && flag_pic))));
}
-enum {
- SAVRES_MULTIPLE = 0x1,
- SAVRES_INLINE_FPRS = 0x2,
- SAVRES_INLINE_GPRS = 0x4,
- SAVRES_NOINLINE_GPRS_SAVES_LR = 0x8,
- SAVRES_NOINLINE_FPRS_SAVES_LR = 0x10,
- SAVRES_NOINLINE_FPRS_DOESNT_RESTORE_LR = 0x20
-};
-
-/* Determine the strategy for savings/restoring registers. */
-
-static int
-rs6000_savres_strategy (rs6000_stack_t *info, bool savep,
- int using_static_chain_p, int sibcall)
-{
- bool using_multiple_p;
- bool common;
- bool savres_fprs_inline;
- bool savres_gprs_inline;
- bool noclobber_global_gprs
- = no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true);
- int strategy;
-
- using_multiple_p = (TARGET_MULTIPLE && ! TARGET_POWERPC64
- && (!TARGET_SPE_ABI
- || info->spe_64bit_regs_used == 0)
- && info->first_gp_reg_save < 31
- && noclobber_global_gprs);
- /* Don't bother to try to save things out-of-line if r11 is occupied
- by the static chain. It would require too much fiddling and the
- static chain is rarely used anyway. */
- common = (using_static_chain_p
- || sibcall
- || crtl->calls_eh_return
- || !info->lr_save_p
- || cfun->machine->ra_need_lr
- || info->total_size > 32767);
- savres_fprs_inline = (common
- || info->first_fp_reg_save == 64
- || !no_global_regs_above (info->first_fp_reg_save,
- /*gpr=*/false)
- /* The out-of-line FP routines use
- double-precision stores; we can't use those
- routines if we don't have such stores. */
- || (TARGET_HARD_FLOAT && !TARGET_DOUBLE_FLOAT)
- || FP_SAVE_INLINE (info->first_fp_reg_save));
- savres_gprs_inline = (common
- /* Saving CR interferes with the exit routines
- used on the SPE, so just punt here. */
- || (!savep
- && TARGET_SPE_ABI
- && info->spe_64bit_regs_used != 0
- && info->cr_save_p != 0)
- || info->first_gp_reg_save == 32
- || !noclobber_global_gprs
- || GP_SAVE_INLINE (info->first_gp_reg_save));
-
- if (savep)
- /* If we are going to use store multiple, then don't even bother
- with the out-of-line routines, since the store-multiple instruction
- will always be smaller. */
- savres_gprs_inline = savres_gprs_inline || using_multiple_p;
- else
- {
- /* The situation is more complicated with load multiple. We'd
- prefer to use the out-of-line routines for restores, since the
- "exit" out-of-line routines can handle the restore of LR and
- the frame teardown. But we can only use the out-of-line
- routines if we know that we've used store multiple or
- out-of-line routines in the prologue, i.e. if we've saved all
- the registers from first_gp_reg_save. Otherwise, we risk
- loading garbage from the stack. Furthermore, we can only use
- the "exit" out-of-line gpr restore if we haven't saved any
- fprs. */
- bool saved_all = !savres_gprs_inline || using_multiple_p;
-
- if (saved_all && info->first_fp_reg_save != 64)
- /* We can't use the exit routine; use load multiple if it's
- available. */
- savres_gprs_inline = savres_gprs_inline || using_multiple_p;
- }
-
- strategy = (using_multiple_p
- | (savres_fprs_inline << 1)
- | (savres_gprs_inline << 2));
-#ifdef POWERPC_LINUX
- if (TARGET_64BIT)
- {
- if (!savres_fprs_inline)
- strategy |= SAVRES_NOINLINE_FPRS_SAVES_LR;
- else if (!savres_gprs_inline && info->first_fp_reg_save == 64)
- strategy |= SAVRES_NOINLINE_GPRS_SAVES_LR;
- }
-#else
- if (TARGET_AIX && !savres_fprs_inline)
- strategy |= SAVRES_NOINLINE_FPRS_DOESNT_RESTORE_LR;
-#endif
- return strategy;
-}
-
/* Emit function prologue as insns. */
void
reg_size = 8;
}
- strategy = rs6000_savres_strategy (info, /*savep=*/true,
- /*static_chain_p=*/using_static_chain_p,
- /*sibcall=*/0);
+ strategy = info->savres_strategy;
using_store_multiple = strategy & SAVRES_MULTIPLE;
- saving_FPRs_inline = strategy & SAVRES_INLINE_FPRS;
- saving_GPRs_inline = strategy & SAVRES_INLINE_GPRS;
+ saving_FPRs_inline = strategy & SAVE_INLINE_FPRS;
+ saving_GPRs_inline = strategy & SAVE_INLINE_GPRS;
/* For V.4, update stack before we do any saving and set back pointer. */
if (! WORLD_SAVE_P (info)
gen_rtx_REG (Pmode, LR_REGNO));
RTX_FRAME_RELATED_P (insn) = 1;
- if (!(strategy & (SAVRES_NOINLINE_GPRS_SAVES_LR
- | SAVRES_NOINLINE_FPRS_SAVES_LR)))
+ if (!(strategy & (SAVE_NOINLINE_GPRS_SAVES_LR
+ | SAVE_NOINLINE_FPRS_SAVES_LR)))
{
addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
GEN_INT (info->lr_save_offset + sp_offset));
DFmode,
/*savep=*/true, /*gpr=*/false,
/*lr=*/(strategy
- & SAVRES_NOINLINE_FPRS_SAVES_LR)
+ & SAVE_NOINLINE_FPRS_SAVES_LR)
!= 0);
insn = emit_insn (par);
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
reg_mode,
/*savep=*/true, /*gpr=*/true,
/*lr=*/(strategy
- & SAVRES_NOINLINE_GPRS_SAVES_LR)
+ & SAVE_NOINLINE_GPRS_SAVES_LR)
!= 0);
insn = emit_insn (par);
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
insn = emit_insn (generate_set_vrsave (reg, info, 0));
}
+ if (TARGET_SINGLE_PIC_BASE)
+ return; /* Do not set PIC register */
+
/* If we are using RS6000_PIC_OFFSET_TABLE_REGNUM, we need to set it up. */
if ((TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0)
|| (DEFAULT_ABI == ABI_V4
/* Write .extern for any function we will call to save and restore
fp values. */
- if (info->first_fp_reg_save < 64
- && !FP_SAVE_INLINE (info->first_fp_reg_save))
+ if (info->first_fp_reg_save < 64)
{
char *name;
int regno = info->first_fp_reg_save - 32;
- name = rs6000_savres_routine_name (info, regno, /*savep=*/true,
- /*gpr=*/false, /*lr=*/false);
- fprintf (file, "\t.extern %s\n", name);
-
- name = rs6000_savres_routine_name (info, regno, /*savep=*/false,
- /*gpr=*/false, /*lr=*/true);
- fprintf (file, "\t.extern %s\n", name);
+ if ((info->savres_strategy & SAVE_INLINE_FPRS) == 0)
+ {
+ name = rs6000_savres_routine_name (info, regno, /*savep=*/true,
+ /*gpr=*/false, /*lr=*/false);
+ fprintf (file, "\t.extern %s\n", name);
+ }
+ if ((info->savres_strategy & REST_INLINE_FPRS) == 0)
+ {
+ name = rs6000_savres_routine_name (info, regno, /*savep=*/false,
+ /*gpr=*/false, /*lr=*/true);
+ fprintf (file, "\t.extern %s\n", name);
+ }
}
/* Write .extern for AIX common mode routines, if needed. */
reg_size = 8;
}
- strategy = rs6000_savres_strategy (info, /*savep=*/false,
- /*static_chain_p=*/0, sibcall);
+ strategy = info->savres_strategy;
using_load_multiple = strategy & SAVRES_MULTIPLE;
- restoring_FPRs_inline = strategy & SAVRES_INLINE_FPRS;
- restoring_GPRs_inline = strategy & SAVRES_INLINE_GPRS;
+ restoring_FPRs_inline = sibcall || (strategy & REST_INLINE_FPRS);
+ restoring_GPRs_inline = sibcall || (strategy & REST_INLINE_GPRS);
using_mtcr_multiple = (rs6000_cpu == PROCESSOR_PPC601
|| rs6000_cpu == PROCESSOR_PPC603
|| rs6000_cpu == PROCESSOR_PPC750
&& !frame_pointer_needed));
restore_lr = (info->lr_save_p
&& (restoring_FPRs_inline
- || (strategy & SAVRES_NOINLINE_FPRS_DOESNT_RESTORE_LR))
+ || (strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR))
&& (restoring_GPRs_inline
|| info->first_fp_reg_save < 64));
if (!sibcall)
{
rtvec p;
- bool lr = (strategy & SAVRES_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
+ bool lr = (strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
if (! restoring_FPRs_inline)
p = rtvec_alloc (4 + 64 - info->first_fp_reg_save);
else
return true;
case PLUS:
- if (mode == DFmode)
- {
- if (GET_CODE (XEXP (x, 0)) == MULT)
- {
- /* FNMA accounted in outer NEG. */
- if (outer_code == NEG)
- *total = rs6000_cost->dmul - rs6000_cost->fp;
- else
- *total = rs6000_cost->dmul;
- }
- else
- *total = rs6000_cost->fp;
- }
- else if (mode == SFmode)
- {
- /* FNMA accounted in outer NEG. */
- if (outer_code == NEG && GET_CODE (XEXP (x, 0)) == MULT)
- *total = 0;
- else
- *total = rs6000_cost->fp;
- }
- else
- *total = COSTS_N_INSNS (1);
- return false;
-
case MINUS:
- if (mode == DFmode)
- {
- if (GET_CODE (XEXP (x, 0)) == MULT
- || GET_CODE (XEXP (x, 1)) == MULT)
- {
- /* FNMA accounted in outer NEG. */
- if (outer_code == NEG)
- *total = rs6000_cost->dmul - rs6000_cost->fp;
- else
- *total = rs6000_cost->dmul;
- }
- else
- *total = rs6000_cost->fp;
- }
- else if (mode == SFmode)
- {
- /* FNMA accounted in outer NEG. */
- if (outer_code == NEG && GET_CODE (XEXP (x, 0)) == MULT)
- *total = 0;
- else
- *total = rs6000_cost->fp;
- }
+ if (FLOAT_MODE_P (mode))
+ *total = rs6000_cost->fp;
else
*total = COSTS_N_INSNS (1);
return false;
else
*total = rs6000_cost->mulsi_const;
}
- /* FMA accounted in outer PLUS/MINUS. */
- else if ((mode == DFmode || mode == SFmode)
- && (outer_code == PLUS || outer_code == MINUS))
- *total = 0;
- else if (mode == DFmode)
- *total = rs6000_cost->dmul;
else if (mode == SFmode)
*total = rs6000_cost->fp;
+ else if (FLOAT_MODE_P (mode))
+ *total = rs6000_cost->dmul;
else if (mode == DImode)
*total = rs6000_cost->muldi;
else
*total = rs6000_cost->mulsi;
return false;
+ case FMA:
+ if (mode == SFmode)
+ *total = rs6000_cost->fp;
+ else
+ *total = rs6000_cost->dmul;
+ break;
+
case DIV:
case MOD:
if (FLOAT_MODE_P (mode))
return reg;
}
-/* Generate a FMADD instruction:
- dst = (m1 * m2) + a
-
- generating different RTL based on the fused multiply/add switch. */
+/* Generate an FMA instruction. */
static void
-rs6000_emit_madd (rtx dst, rtx m1, rtx m2, rtx a)
+rs6000_emit_madd (rtx target, rtx m1, rtx m2, rtx a)
{
- enum machine_mode mode = GET_MODE (dst);
-
- if (!TARGET_FUSED_MADD)
- {
- /* For the simple ops, use the generator function, rather than assuming
- that the RTL is standard. */
- enum insn_code mcode = optab_handler (smul_optab, mode);
- enum insn_code acode = optab_handler (add_optab, mode);
- gen_2arg_fn_t gen_mul = (gen_2arg_fn_t) GEN_FCN (mcode);
- gen_2arg_fn_t gen_add = (gen_2arg_fn_t) GEN_FCN (acode);
- rtx mreg = gen_reg_rtx (mode);
+ enum machine_mode mode = GET_MODE (target);
+ rtx dst;
- gcc_assert (mcode != CODE_FOR_nothing && acode != CODE_FOR_nothing);
- emit_insn (gen_mul (mreg, m1, m2));
- emit_insn (gen_add (dst, mreg, a));
- }
+ dst = expand_ternary_op (mode, fma_optab, m1, m2, a, target, 0);
+ gcc_assert (dst != NULL);
- else
- emit_insn (gen_rtx_SET (VOIDmode, dst,
- gen_rtx_PLUS (mode,
- gen_rtx_MULT (mode, m1, m2),
- a)));
+ if (dst != target)
+ emit_move_insn (target, dst);
}
-/* Generate a FMSUB instruction:
- dst = (m1 * m2) - a
-
- generating different RTL based on the fused multiply/add switch. */
+/* Generate a FMSUB instruction: dst = fma(m1, m2, -a). */
static void
-rs6000_emit_msub (rtx dst, rtx m1, rtx m2, rtx a)
+rs6000_emit_msub (rtx target, rtx m1, rtx m2, rtx a)
{
- enum machine_mode mode = GET_MODE (dst);
+ enum machine_mode mode = GET_MODE (target);
+ rtx dst;
- if (!TARGET_FUSED_MADD
- || (mode == V4SFmode && VECTOR_UNIT_ALTIVEC_P (V4SFmode)))
+ /* Altivec does not support fms directly;
+ generate in terms of fma in that case. */
+ if (optab_handler (fms_optab, mode) != CODE_FOR_nothing)
+ dst = expand_ternary_op (mode, fms_optab, m1, m2, a, target, 0);
+ else
{
- /* For the simple ops, use the generator function, rather than assuming
- that the RTL is standard. */
- enum insn_code mcode = optab_handler (smul_optab, mode);
- enum insn_code scode = optab_handler (add_optab, mode);
- gen_2arg_fn_t gen_mul = (gen_2arg_fn_t) GEN_FCN (mcode);
- gen_2arg_fn_t gen_sub = (gen_2arg_fn_t) GEN_FCN (scode);
- rtx mreg = gen_reg_rtx (mode);
-
- gcc_assert (mcode != CODE_FOR_nothing && scode != CODE_FOR_nothing);
- emit_insn (gen_mul (mreg, m1, m2));
- emit_insn (gen_sub (dst, mreg, a));
+ a = expand_unop (mode, neg_optab, a, NULL_RTX, 0);
+ dst = expand_ternary_op (mode, fma_optab, m1, m2, a, target, 0);
}
+ gcc_assert (dst != NULL);
- else
- emit_insn (gen_rtx_SET (VOIDmode, dst,
- gen_rtx_MINUS (mode,
- gen_rtx_MULT (mode, m1, m2),
- a)));
+ if (dst != target)
+ emit_move_insn (target, dst);
}
-
-/* Generate a FNMSUB instruction:
- dst = - ((m1 * m2) - a)
-
- Which is equivalent to (except in the prescence of -0.0):
- dst = a - (m1 * m2)
-
- generating different RTL based on the fast-math and fused multiply/add
- switches. */
+
+/* Generate a FNMSUB instruction: dst = -fma(m1, m2, -a). */
static void
rs6000_emit_nmsub (rtx dst, rtx m1, rtx m2, rtx a)
{
enum machine_mode mode = GET_MODE (dst);
+ rtx r;
- if (!TARGET_FUSED_MADD)
- {
- /* For the simple ops, use the generator function, rather than assuming
- that the RTL is standard. */
- enum insn_code mcode = optab_handler (smul_optab, mode);
- enum insn_code scode = optab_handler (sub_optab, mode);
- gen_2arg_fn_t gen_mul = (gen_2arg_fn_t) GEN_FCN (mcode);
- gen_2arg_fn_t gen_sub = (gen_2arg_fn_t) GEN_FCN (scode);
- rtx mreg = gen_reg_rtx (mode);
+ /* This is a tad more complicated, since the fnma_optab is for
+ a different expression: fma(-m1, m2, a), which is the same
+ thing except in the case of signed zeros.
- gcc_assert (mcode != CODE_FOR_nothing && scode != CODE_FOR_nothing);
- emit_insn (gen_mul (mreg, m1, m2));
- emit_insn (gen_sub (dst, a, mreg));
- }
+ Fortunately we know that if FMA is supported that FNMSUB is
+ also supported in the ISA. Just expand it directly. */
- else
- {
- rtx m = gen_rtx_MULT (mode, m1, m2);
+ gcc_assert (optab_handler (fma_optab, mode) != CODE_FOR_nothing);
- if (!HONOR_SIGNED_ZEROS (mode))
- emit_insn (gen_rtx_SET (VOIDmode, dst, gen_rtx_MINUS (mode, a, m)));
-
- else
- emit_insn (gen_rtx_SET (VOIDmode, dst,
- gen_rtx_NEG (mode,
- gen_rtx_MINUS (mode, m, a))));
- }
+ r = gen_rtx_NEG (mode, a);
+ r = gen_rtx_FMA (mode, m1, m2, r);
+ r = gen_rtx_NEG (mode, r);
+ emit_insn (gen_rtx_SET (VOIDmode, dst, r));
}
/* Newton-Raphson approximation of floating point divide with just 2 passes
}
\f
+/* Mask options that we want to support inside of attribute((target)) and
+ #pragma GCC target operations. Note, we do not include things like
+ 64/32-bit, endianess, hard/soft floating point, etc. that would have
+ different calling sequences. */
+
+struct rs6000_opt_mask {
+ const char *name; /* option name */
+ int mask; /* mask to set */
+ bool invert; /* invert sense of mask */
+ bool valid_target; /* option is a target option */
+};
+
+static struct rs6000_opt_mask const rs6000_opt_masks[] =
+{
+ { "altivec", MASK_ALTIVEC, false, true },
+ { "cmpb", MASK_CMPB, false, true },
+ { "dlmzb", MASK_DLMZB, false, true },
+ { "fprnd", MASK_FPRND, false, true },
+ { "hard-dfp", MASK_DFP, false, true },
+ { "isel", MASK_ISEL, false, true },
+ { "mfcrf", MASK_MFCRF, false, true },
+ { "mfpgpr", MASK_MFPGPR, false, true },
+ { "mulhw", MASK_MULHW, false, true },
+ { "multiple", MASK_MULTIPLE, false, true },
+ { "update", MASK_NO_UPDATE, true , true },
+ { "popcntb", MASK_POPCNTB, false, true },
+ { "popcntd", MASK_POPCNTD, false, true },
+ { "powerpc-gfxopt", MASK_PPC_GFXOPT, false, true },
+ { "powerpc-gpopt", MASK_PPC_GPOPT, false, true },
+ { "recip-precision", MASK_RECIP_PRECISION, false, true },
+ { "string", MASK_STRING, false, true },
+ { "vsx", MASK_VSX, false, true },
+#ifdef MASK_64BIT
+#if TARGET_AIX_OS
+ { "aix64", MASK_64BIT, false, false },
+ { "aix32", MASK_64BIT, true, false },
+#else
+ { "64", MASK_64BIT, false, false },
+ { "32", MASK_64BIT, true, false },
+#endif
+#endif
+#ifdef MASK_EABI
+ { "eabi", MASK_EABI, false, false },
+#endif
+#ifdef MASK_LITTLE_ENDIAN
+ { "little", MASK_LITTLE_ENDIAN, false, false },
+ { "big", MASK_LITTLE_ENDIAN, true, false },
+#endif
+#ifdef MASK_RELOCATABLE
+ { "relocatable", MASK_RELOCATABLE, false, false },
+#endif
+#ifdef MASK_STRICT_ALIGN
+ { "strict-align", MASK_STRICT_ALIGN, false, false },
+#endif
+ { "power", MASK_POWER, false, false },
+ { "power2", MASK_POWER2, false, false },
+ { "powerpc", MASK_POWERPC, false, false },
+ { "soft-float", MASK_SOFT_FLOAT, false, false },
+ { "string", MASK_STRING, false, false },
+};
+
+/* Option variables that we want to support inside attribute((target)) and
+ #pragma GCC target operations. */
+
+struct rs6000_opt_var {
+ const char *name; /* option name */
+ size_t global_offset; /* offset of the option in global_options. */
+ size_t target_offset; /* offset of the option in target optiosn. */
+};
+
+static struct rs6000_opt_var const rs6000_opt_vars[] =
+{
+ { "friz",
+ offsetof (struct gcc_options, x_TARGET_FRIZ),
+ offsetof (struct cl_target_option, x_TARGET_FRIZ), },
+ { "avoid-indexed-addresses",
+ offsetof (struct gcc_options, x_TARGET_AVOID_XFORM),
+ offsetof (struct cl_target_option, x_TARGET_AVOID_XFORM) },
+ { "paired",
+ offsetof (struct gcc_options, x_rs6000_paired_float),
+ offsetof (struct cl_target_option, x_rs6000_paired_float), },
+ { "longcall",
+ offsetof (struct gcc_options, x_rs6000_default_long_calls),
+ offsetof (struct cl_target_option, x_rs6000_default_long_calls), },
+};
+
+/* Inner function to handle attribute((target("..."))) and #pragma GCC target
+ parsing. Return true if there were no errors. */
+
+static bool
+rs6000_inner_target_options (tree args, bool attr_p)
+{
+ bool ret = true;
+
+ if (args == NULL_TREE)
+ ;
+
+ else if (TREE_CODE (args) == STRING_CST)
+ {
+ char *p = ASTRDUP (TREE_STRING_POINTER (args));
+ char *q;
+
+ while ((q = strtok (p, ",")) != NULL)
+ {
+ bool error_p = false;
+ bool not_valid_p = false;
+ const char *cpu_opt = NULL;
+
+ p = NULL;
+ if (strncmp (q, "cpu=", 4) == 0)
+ {
+ int cpu_index = rs6000_cpu_name_lookup (q+4);
+ if (cpu_index >= 0)
+ rs6000_cpu_index = cpu_index;
+ else
+ {
+ error_p = true;
+ cpu_opt = q+4;
+ }
+ }
+ else if (strncmp (q, "tune=", 5) == 0)
+ {
+ int tune_index = rs6000_cpu_name_lookup (q+5);
+ if (tune_index >= 0)
+ rs6000_tune_index = tune_index;
+ else
+ {
+ error_p = true;
+ cpu_opt = q+5;
+ }
+ }
+ else
+ {
+ size_t i;
+ bool invert = false;
+ char *r = q;
+
+ error_p = true;
+ if (strncmp (r, "no-", 3) == 0)
+ {
+ invert = true;
+ r += 3;
+ }
+
+ for (i = 0; i < ARRAY_SIZE (rs6000_opt_masks); i++)
+ if (strcmp (r, rs6000_opt_masks[i].name) == 0)
+ {
+ int mask = rs6000_opt_masks[i].mask;
+
+ if (!rs6000_opt_masks[i].valid_target)
+ not_valid_p = true;
+ else
+ {
+ error_p = false;
+ target_flags_explicit |= mask;
+
+ if (rs6000_opt_masks[i].invert)
+ invert = !invert;
+
+ if (invert)
+ target_flags &= ~mask;
+ else
+ target_flags |= mask;
+ }
+ break;
+ }
+
+ if (error_p && !not_valid_p)
+ {
+ for (i = 0; i < ARRAY_SIZE (rs6000_opt_vars); i++)
+ if (strcmp (r, rs6000_opt_vars[i].name) == 0)
+ {
+ size_t j = rs6000_opt_vars[i].global_offset;
+ ((int *) &global_options)[j] = !invert;
+ error_p = false;
+ break;
+ }
+ }
+ }
+
+ if (error_p)
+ {
+ const char *eprefix, *esuffix;
+
+ ret = false;
+ if (attr_p)
+ {
+ eprefix = "__attribute__((__target__(";
+ esuffix = ")))";
+ }
+ else
+ {
+ eprefix = "#pragma GCC target ";
+ esuffix = "";
+ }
+
+ if (cpu_opt)
+ error ("invalid cpu \"%s\" for %s\"%s\"%s", cpu_opt, eprefix,
+ q, esuffix);
+ else if (not_valid_p)
+ error ("%s\"%s\"%s is not allowed", eprefix, q, esuffix);
+ else
+ error ("%s\"%s\"%s is invalid", eprefix, q, esuffix);
+ }
+ }
+ }
+
+ else if (TREE_CODE (args) == TREE_LIST)
+ {
+ do
+ {
+ tree value = TREE_VALUE (args);
+ if (value)
+ {
+ bool ret2 = rs6000_inner_target_options (value, attr_p);
+ if (!ret2)
+ ret = false;
+ }
+ args = TREE_CHAIN (args);
+ }
+ while (args != NULL_TREE);
+ }
+
+ else
+ gcc_unreachable ();
+
+ return ret;
+}
+
+/* Print out the target options as a list for -mdebug=target. */
+
+static void
+rs6000_debug_target_options (tree args, const char *prefix)
+{
+ if (args == NULL_TREE)
+ fprintf (stderr, "%s<NULL>", prefix);
+
+ else if (TREE_CODE (args) == STRING_CST)
+ {
+ char *p = ASTRDUP (TREE_STRING_POINTER (args));
+ char *q;
+
+ while ((q = strtok (p, ",")) != NULL)
+ {
+ p = NULL;
+ fprintf (stderr, "%s\"%s\"", prefix, q);
+ prefix = ", ";
+ }
+ }
+
+ else if (TREE_CODE (args) == TREE_LIST)
+ {
+ do
+ {
+ tree value = TREE_VALUE (args);
+ if (value)
+ {
+ rs6000_debug_target_options (value, prefix);
+ prefix = ", ";
+ }
+ args = TREE_CHAIN (args);
+ }
+ while (args != NULL_TREE);
+ }
+
+ else
+ gcc_unreachable ();
+
+ return;
+}
+
+\f
+/* Hook to validate attribute((target("..."))). */
+
+static bool
+rs6000_valid_attribute_p (tree fndecl,
+ tree ARG_UNUSED (name),
+ tree args,
+ int flags)
+{
+ struct cl_target_option cur_target;
+ bool ret;
+ tree old_optimize = build_optimization_node ();
+ tree new_target, new_optimize;
+ tree func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl);
+
+ gcc_assert ((fndecl != NULL_TREE) && (args != NULL_TREE));
+
+ if (TARGET_DEBUG_TARGET)
+ {
+ tree tname = DECL_NAME (fndecl);
+ fprintf (stderr, "\n==================== rs6000_valid_attribute_p:\n");
+ if (tname)
+ fprintf (stderr, "function: %.*s\n",
+ (int) IDENTIFIER_LENGTH (tname),
+ IDENTIFIER_POINTER (tname));
+ else
+ fprintf (stderr, "function: unknown\n");
+
+ fprintf (stderr, "args:");
+ rs6000_debug_target_options (args, " ");
+ fprintf (stderr, "\n");
+
+ if (flags)
+ fprintf (stderr, "flags: 0x%x\n", flags);
+
+ fprintf (stderr, "--------------------\n");
+ }
+
+ old_optimize = build_optimization_node ();
+ func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl);
+
+ /* If the function changed the optimization levels as well as setting target
+ options, start with the optimizations specified. */
+ if (func_optimize && func_optimize != old_optimize)
+ cl_optimization_restore (&global_options,
+ TREE_OPTIMIZATION (func_optimize));
+
+ /* The target attributes may also change some optimization flags, so update
+ the optimization options if necessary. */
+ cl_target_option_save (&cur_target, &global_options);
+ rs6000_cpu_index = rs6000_tune_index = -1;
+ ret = rs6000_inner_target_options (args, true);
+
+ /* Set up any additional state. */
+ if (ret)
+ {
+ ret = rs6000_option_override_internal (false);
+ new_target = build_target_option_node ();
+ }
+ else
+ new_target = NULL;
+
+ new_optimize = build_optimization_node ();
+
+ if (!new_target)
+ ret = false;
+
+ else if (fndecl)
+ {
+ DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
+
+ if (old_optimize != new_optimize)
+ DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = new_optimize;
+ }
+
+ cl_target_option_restore (&global_options, &cur_target);
+
+ if (old_optimize != new_optimize)
+ cl_optimization_restore (&global_options,
+ TREE_OPTIMIZATION (old_optimize));
+
+ return ret;
+}
+
+\f
+/* Hook to validate the current #pragma GCC target and set the state, and
+ update the macros based on what was changed. If ARGS is NULL, then
+ POP_TARGET is used to reset the options. */
+
+bool
+rs6000_pragma_target_parse (tree args, tree pop_target)
+{
+ tree cur_tree;
+ bool ret;
+
+ if (TARGET_DEBUG_TARGET)
+ {
+ fprintf (stderr, "\n==================== rs6000_pragma_target_parse\n");
+ fprintf (stderr, "args:");
+ rs6000_debug_target_options (args, " ");
+ fprintf (stderr, "\n");
+
+ if (pop_target)
+ {
+ fprintf (stderr, "pop_target:\n");
+ debug_tree (pop_target);
+ }
+ else
+ fprintf (stderr, "pop_target: <NULL>\n");
+
+ fprintf (stderr, "--------------------\n");
+ }
+
+ if (! args)
+ {
+ ret = true;
+ cur_tree = ((pop_target)
+ ? pop_target
+ : target_option_default_node);
+ cl_target_option_restore (&global_options,
+ TREE_TARGET_OPTION (cur_tree));
+ }
+ else
+ {
+ rs6000_cpu_index = rs6000_tune_index = -1;
+ ret = rs6000_inner_target_options (args, false);
+ cur_tree = build_target_option_node ();
+
+ if (!cur_tree)
+ ret = false;
+ }
+
+ if (cur_tree)
+ target_option_current_node = cur_tree;
+
+ return ret;
+}
+
+\f
+/* Remember the last target of rs6000_set_current_function. */
+static GTY(()) tree rs6000_previous_fndecl;
+
+/* Establish appropriate back-end context for processing the function
+ FNDECL. The argument might be NULL to indicate processing at top
+ level, outside of any function scope. */
+static void
+rs6000_set_current_function (tree fndecl)
+{
+ tree old_tree = (rs6000_previous_fndecl
+ ? DECL_FUNCTION_SPECIFIC_TARGET (rs6000_previous_fndecl)
+ : NULL_TREE);
+
+ tree new_tree = (fndecl
+ ? DECL_FUNCTION_SPECIFIC_TARGET (fndecl)
+ : NULL_TREE);
+
+ if (TARGET_DEBUG_TARGET)
+ {
+ bool print_final = false;
+ fprintf (stderr, "\n==================== rs6000_set_current_function");
+
+ if (fndecl)
+ fprintf (stderr, ", fndecl %s (%p)",
+ (DECL_NAME (fndecl)
+ ? IDENTIFIER_POINTER (DECL_NAME (fndecl))
+ : "<unknown>"), (void *)fndecl);
+
+ if (rs6000_previous_fndecl)
+ fprintf (stderr, ", prev_fndecl (%p)", (void *)rs6000_previous_fndecl);
+
+ fprintf (stderr, "\n");
+ if (new_tree)
+ {
+ fprintf (stderr, "\nnew fndecl target specific options:\n");
+ debug_tree (new_tree);
+ print_final = true;
+ }
+
+ if (old_tree)
+ {
+ fprintf (stderr, "\nold fndecl target specific options:\n");
+ debug_tree (old_tree);
+ print_final = true;
+ }
+
+ if (print_final)
+ fprintf (stderr, "--------------------\n");
+ }
+
+ /* Only change the context if the function changes. This hook is called
+ several times in the course of compiling a function, and we don't want to
+ slow things down too much or call target_reinit when it isn't safe. */
+ if (fndecl && fndecl != rs6000_previous_fndecl)
+ {
+ rs6000_previous_fndecl = fndecl;
+ if (old_tree == new_tree)
+ ;
+
+ else if (new_tree)
+ {
+ cl_target_option_restore (&global_options,
+ TREE_TARGET_OPTION (new_tree));
+ target_reinit ();
+ }
+
+ else if (old_tree)
+ {
+ struct cl_target_option *def
+ = TREE_TARGET_OPTION (target_option_current_node);
+
+ cl_target_option_restore (&global_options, def);
+ target_reinit ();
+ }
+ }
+}
+
+\f
+/* Save the current options */
+
+static void
+rs6000_function_specific_save (struct cl_target_option *ptr)
+{
+ ptr->rs6000_target_flags_explicit = target_flags_explicit;
+}
+
+/* Restore the current options */
+
+static void
+rs6000_function_specific_restore (struct cl_target_option *ptr)
+{
+ target_flags_explicit = ptr->rs6000_target_flags_explicit;
+ (void) rs6000_option_override_internal (false);
+}
+
+/* Print the current options */
+
+static void
+rs6000_function_specific_print (FILE *file, int indent,
+ struct cl_target_option *ptr)
+{
+ size_t i;
+ int flags = ptr->x_target_flags;
+
+ /* Print the various mask options. */
+ for (i = 0; i < ARRAY_SIZE (rs6000_opt_masks); i++)
+ if ((flags & rs6000_opt_masks[i].mask) != 0)
+ {
+ flags &= ~ rs6000_opt_masks[i].mask;
+ fprintf (file, "%*s-m%s%s\n", indent, "",
+ rs6000_opt_masks[i].invert ? "no-" : "",
+ rs6000_opt_masks[i].name);
+ }
+
+ /* Print the various options that are variables. */
+ for (i = 0; i < ARRAY_SIZE (rs6000_opt_vars); i++)
+ {
+ size_t j = rs6000_opt_vars[i].target_offset;
+ if (((signed char *) ptr)[j])
+ fprintf (file, "%*s-m%s\n", indent, "",
+ rs6000_opt_vars[i].name);
+ }
+}
+
+\f
+/* Hook to determine if one function can safely inline another. */
+
+static bool
+rs6000_can_inline_p (tree caller, tree callee)
+{
+ bool ret = false;
+ tree caller_tree = DECL_FUNCTION_SPECIFIC_TARGET (caller);
+ tree callee_tree = DECL_FUNCTION_SPECIFIC_TARGET (callee);
+
+ /* If callee has no option attributes, then it is ok to inline. */
+ if (!callee_tree)
+ ret = true;
+
+ /* If caller has no option attributes, but callee does then it is not ok to
+ inline. */
+ else if (!caller_tree)
+ ret = false;
+
+ else
+ {
+ struct cl_target_option *caller_opts = TREE_TARGET_OPTION (caller_tree);
+ struct cl_target_option *callee_opts = TREE_TARGET_OPTION (callee_tree);
+
+ /* Callee's options should a subset of the caller's, i.e. a vsx function
+ can inline an altivec function but a non-vsx function can't inline a
+ vsx function. */
+ if ((caller_opts->x_target_flags & callee_opts->x_target_flags)
+ == callee_opts->x_target_flags)
+ ret = true;
+ }
+
+ if (TARGET_DEBUG_TARGET)
+ fprintf (stderr, "rs6000_can_inline_p:, caller %s, callee %s, %s inline\n",
+ (DECL_NAME (caller)
+ ? IDENTIFIER_POINTER (DECL_NAME (caller))
+ : "<unknown>"),
+ (DECL_NAME (callee)
+ ? IDENTIFIER_POINTER (DECL_NAME (callee))
+ : "<unknown>"),
+ (ret ? "can" : "cannot"));
+
+ return ret;
+}
+\f
/* Allocate a stack temp and fixup the address so it meets the particular
memory requirements (either offetable or REG+REG addressing). */
addr = XEXP (x, 0);
if (! legitimate_indirect_address_p (addr, strict_p)
&& ! legitimate_indexed_address_p (addr, strict_p))
- x = replace_equiv_address (x, copy_addr_to_reg (addr));
-
- return x;
-}
-
-/* Expand 32-bit int -> floating point conversions. Return true if
- successful. */
-
-void
-rs6000_expand_convert_si_to_sfdf (rtx dest, rtx src, bool unsigned_p)
-{
- enum machine_mode dmode = GET_MODE (dest);
- rtx (*func_si) (rtx, rtx, rtx, rtx);
- rtx (*func_si_mem) (rtx, rtx);
- rtx (*func_di) (rtx, rtx);
- rtx reg, stack;
-
- gcc_assert (GET_MODE (src) == SImode);
-
- if (dmode == SFmode)
{
- if (unsigned_p)
+ if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC)
{
- gcc_assert (TARGET_FCFIDUS && TARGET_LFIWZX);
- func_si = gen_floatunssisf2_lfiwzx;
- func_si_mem = gen_floatunssisf2_lfiwzx_mem;
- func_di = gen_floatunsdisf2;
+ rtx reg = XEXP (addr, 0);
+ HOST_WIDE_INT size = GET_MODE_SIZE (GET_MODE (x));
+ rtx size_rtx = GEN_INT ((GET_CODE (addr) == PRE_DEC) ? -size : size);
+ gcc_assert (REG_P (reg));
+ emit_insn (gen_add3_insn (reg, reg, size_rtx));
+ addr = reg;
}
- else
+ else if (GET_CODE (addr) == PRE_MODIFY)
{
- gcc_assert (TARGET_FCFIDS && TARGET_LFIWAX);
- func_si = gen_floatsisf2_lfiwax;
- func_si_mem = gen_floatsisf2_lfiwax_mem;
- func_di = gen_floatdisf2;
+ rtx reg = XEXP (addr, 0);
+ rtx expr = XEXP (addr, 1);
+ gcc_assert (REG_P (reg));
+ gcc_assert (GET_CODE (expr) == PLUS);
+ emit_insn (gen_add3_insn (reg, XEXP (expr, 0), XEXP (expr, 1)));
+ addr = reg;
}
- }
- else if (dmode == DFmode)
- {
- if (unsigned_p)
- {
- gcc_assert (TARGET_FCFIDU && TARGET_LFIWZX);
- func_si = gen_floatunssidf2_lfiwzx;
- func_si_mem = gen_floatunssidf2_lfiwzx_mem;
- func_di = gen_floatunsdidf2;
- }
- else
- {
- gcc_assert (TARGET_FCFID && TARGET_LFIWAX);
- func_si = gen_floatsidf2_lfiwax;
- func_si_mem = gen_floatsidf2_lfiwax_mem;
- func_di = gen_floatdidf2;
- }
+ x = replace_equiv_address (x, copy_addr_to_reg (addr));
}
- else
- gcc_unreachable ();
-
- if (MEM_P (src))
- {
- src = rs6000_address_for_fpconvert (src);
- emit_insn (func_si_mem (dest, src));
- }
- else if (!TARGET_MFPGPR)
- {
- reg = gen_reg_rtx (DImode);
- stack = rs6000_allocate_stack_temp (SImode, false, true);
- emit_insn (func_si (dest, src, stack, reg));
- }
- else
- {
- if (!REG_P (src))
- src = force_reg (SImode, src);
- reg = convert_to_mode (DImode, src, unsigned_p);
- emit_insn (func_di (dest, reg));
- }
+ return x;
}
#include "gt-rs6000.h"