/* Subroutines used for code generation on IBM RS/6000.
Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
This file is part of GCC.
#include "cfglayout.h"
#include "sched-int.h"
#include "tree-gimple.h"
+#include "intl.h"
#if TARGET_XCOFF
#include "xcoffout.h" /* get declarations of xcoff_*_section_name */
#endif
+#if TARGET_MACHO
+#include "gstab.h" /* for N_SLINE */
+#endif
#ifndef TARGET_NO_PROTOTYPE
#define TARGET_NO_PROTOTYPE 0
#endif
-#define EASY_VECTOR_15(n) ((n) >= -16 && (n) <= 15)
-#define EASY_VECTOR_15_ADD_SELF(n) ((n) >= 0x10 && (n) <= 0x1e \
- && !((n) & 1))
-
#define min(A,B) ((A) < (B) ? (A) : (B))
#define max(A,B) ((A) > (B) ? (A) : (B))
int toc_save_p; /* true if the TOC needs to be saved */
int push_p; /* true if we need to allocate stack space */
int calls_p; /* true if the function makes any calls */
- int world_save_p; /* true if we're saving *everything*:
+ int world_save_p; /* true if we're saving *everything*:
r13-r31, cr, f14-f31, vrsave, v20-v31 */
enum rs6000_abi abi; /* which ABI to use */
int gp_save_offset; /* offset to save GP regs from initial SP */
const char *rs6000_sched_insert_nops_str;
enum rs6000_nop_insertion rs6000_sched_insert_nops;
+/* Support targetm.vectorize.builtin_mask_for_load. */
+static GTY(()) tree altivec_builtin_mask_for_load;
+
/* Size of long double */
const char *rs6000_long_double_size_string;
int rs6000_long_double_type_size;
/* 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;
+
/* String from -mfloat-gprs=. */
const char *rs6000_float_gprs_string;
/* Call distance, overridden by -mlongcall and #pragma longcall(1).
The only place that looks at this is rs6000_set_default_type_attributes;
everywhere else should rely on the presence or absence of a longcall
- attribute on the function declaration. */
+ attribute on the function declaration. Exception: init_cumulative_args
+ looks at it too, for libcalls. */
int rs6000_default_long_calls;
const char *rs6000_longcall_switch;
/* Target cpu costs. */
struct processor_costs {
- const int mulsi; /* cost of SImode multiplication. */
+ const int mulsi; /* cost of SImode multiplication. */
const int mulsi_const; /* cost of SImode multiplication by constant. */
const int mulsi_const9; /* cost of SImode mult by short constant. */
- const int muldi; /* cost of DImode multiplication. */
- const int divsi; /* cost of SImode division. */
- const int divdi; /* cost of DImode division. */
- const int fp; /* cost of simple SFmode and DFmode insns. */
- const int dmul; /* cost of DFmode multiplication (and fmadd). */
- const int sdiv; /* cost of SFmode division (fdivs). */
- const int ddiv; /* cost of DFmode division (fdiv). */
+ const int muldi; /* cost of DImode multiplication. */
+ const int divsi; /* cost of SImode division. */
+ const int divdi; /* cost of DImode division. */
+ const int fp; /* cost of simple SFmode and DFmode insns. */
+ const int dmul; /* cost of DFmode multiplication (and fmadd). */
+ const int sdiv; /* cost of SFmode division (fdivs). */
+ const int ddiv; /* cost of DFmode division (fdiv). */
};
const struct processor_costs *rs6000_cost;
\f
static bool rs6000_function_ok_for_sibcall (tree, tree);
-static int num_insns_constant_wide (HOST_WIDE_INT);
-static void validate_condition_mode (enum rtx_code, enum machine_mode);
static rtx rs6000_generate_compare (enum rtx_code);
static void rs6000_maybe_dead (rtx);
static void rs6000_emit_stack_tie (void);
static int toc_hash_eq (const void *, const void *);
static int constant_pool_expr_1 (rtx, int *, int *);
static bool constant_pool_expr_p (rtx);
-static bool toc_relative_expr_p (rtx);
static bool legitimate_small_data_p (enum machine_mode, rtx);
static bool legitimate_indexed_address_p (rtx, int);
-static bool legitimate_indirect_address_p (rtx, int);
-static bool macho_lo_sum_memory_operand (rtx x, enum machine_mode mode);
static bool legitimate_lo_sum_address_p (enum machine_mode, rtx, int);
static struct machine_function * rs6000_init_machine_status (void);
static bool rs6000_assemble_integer (rtx, unsigned int, int);
#endif
#if TARGET_XCOFF
static void rs6000_xcoff_asm_globalize_label (FILE *, const char *);
-static void rs6000_xcoff_asm_named_section (const char *, unsigned int);
+static void rs6000_xcoff_asm_named_section (const char *, unsigned int, tree);
static void rs6000_xcoff_select_section (tree, int, unsigned HOST_WIDE_INT);
static void rs6000_xcoff_unique_section (tree, int);
static void rs6000_xcoff_select_rtx_section (enum machine_mode, rtx,
static int pad_groups (FILE *, int, rtx, rtx);
static void rs6000_sched_finish (FILE *, int);
static int rs6000_use_sched_lookahead (void);
+static tree rs6000_builtin_mask_for_load (void);
static void rs6000_init_builtins (void);
static rtx rs6000_expand_unop_builtin (enum insn_code, tree, rtx);
static rtx altivec_expand_dst_builtin (tree, rtx, bool *);
static rtx altivec_expand_abs_builtin (enum insn_code, tree, rtx);
static rtx altivec_expand_predicate_builtin (enum insn_code,
- const char *, tree, rtx);
+ const char *, tree, rtx);
static rtx altivec_expand_lv_builtin (enum insn_code, tree, rtx);
static rtx altivec_expand_stv_builtin (enum insn_code, tree);
static void rs6000_parse_abi_options (void);
static void rs6000_parse_alignment_option (void);
static void rs6000_parse_tls_size_option (void);
static void rs6000_parse_yes_no_option (const char *, const char *, int *);
+static void rs6000_parse_float_gprs_option (void);
static int first_altivec_reg_to_save (void);
static unsigned int compute_vrsave_mask (void);
-static void compute_save_world_info(rs6000_stack_t *info_ptr);
+static void compute_save_world_info (rs6000_stack_t *info_ptr);
static void is_altivec_return_reg (rtx, void *);
static rtx generate_set_vrsave (rtx, rs6000_stack_t *, int);
int easy_vector_constant (rtx, enum machine_mode);
-static int easy_vector_same (rtx, enum machine_mode);
-static int easy_vector_splat_const (int, enum machine_mode);
static bool is_ev64_opaque_type (tree);
static rtx rs6000_dwarf_register_span (rtx);
static rtx rs6000_legitimize_tls_address (rtx, enum tls_model);
static rtx rs6000_tls_get_addr (void);
static rtx rs6000_got_sym (void);
-static inline int rs6000_tls_symbol_ref_1 (rtx *, void *);
+static int rs6000_tls_symbol_ref_1 (rtx *, void *);
static const char *rs6000_get_some_local_dynamic_name (void);
static int rs6000_get_some_local_dynamic_name_1 (rtx *, void *);
static rtx rs6000_complex_function_value (enum machine_mode);
static rtx rs6000_spe_function_arg (CUMULATIVE_ARGS *,
enum machine_mode, tree);
+static void rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *,
+ HOST_WIDE_INT);
+static void rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *,
+ tree, HOST_WIDE_INT);
+static void rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *,
+ HOST_WIDE_INT,
+ rtx[], int *);
+static void rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *,
+ tree, HOST_WIDE_INT,
+ rtx[], int *);
+static rtx rs6000_darwin64_record_arg (CUMULATIVE_ARGS *, tree, int, bool);
static rtx rs6000_mixed_function_arg (enum machine_mode, tree, int);
static void rs6000_move_block_from_reg (int regno, rtx x, int nregs);
static void setup_incoming_varargs (CUMULATIVE_ARGS *,
int *, int);
static bool rs6000_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
+static int rs6000_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+ tree, bool);
+static const char *invalid_arg_for_unprototyped_fn (tree, tree, tree);
#if TARGET_MACHO
static void macho_branch_islands (void);
static void add_compiler_branch_island (tree, tree, int);
static int no_previous_def (tree function_name);
static tree get_prev_label (tree function_name);
+static void rs6000_darwin_file_start (void);
#endif
static tree rs6000_build_builtin_va_list (void);
static tree rs6000_gimplify_va_arg (tree, tree, tree *, tree *);
static bool rs6000_must_pass_in_stack (enum machine_mode, tree);
static bool rs6000_vector_mode_supported_p (enum machine_mode);
+static int get_vec_cmp_insn (enum rtx_code, enum machine_mode,
+ enum machine_mode);
+static rtx rs6000_emit_vector_compare (enum rtx_code, rtx, rtx,
+ enum machine_mode);
+static int get_vsel_insn (enum machine_mode);
+static void rs6000_emit_vector_select (rtx, rtx, rtx, rtx);
+
+const int INSN_NOT_AVAILABLE = -1;
static enum machine_mode rs6000_eh_return_filter_mode (void);
/* Hash table stuff for keeping track of TOC entries. */
/* The VRSAVE bitmask puts bit %v0 as the most significant bit. */
#define ALTIVEC_REG_BIT(REGNO) (0x80000000 >> ((REGNO) - FIRST_ALTIVEC_REGNO))
-
-/* Return 1 for a symbol ref for a thread-local storage symbol. */
-#define RS6000_SYMBOL_REF_TLS_P(RTX) \
- (GET_CODE (RTX) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (RTX) != 0)
\f
/* Initialize the GCC target structure. */
#undef TARGET_ATTRIBUTE_TABLE
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD rs6000_use_sched_lookahead
+#undef TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD
+#define TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD rs6000_builtin_mask_for_load
+
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS rs6000_init_builtins
#define TARGET_MUST_PASS_IN_STACK rs6000_must_pass_in_stack
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE rs6000_pass_by_reference
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES rs6000_arg_partial_bytes
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list
#undef TARGET_VECTOR_MODE_SUPPORTED_P
#define TARGET_VECTOR_MODE_SUPPORTED_P rs6000_vector_mode_supported_p
+#undef TARGET_INVALID_ARG_FOR_UNPROTOTYPED_FN
+#define TARGET_INVALID_ARG_FOR_UNPROTOTYPED_FN invalid_arg_for_unprototyped_fn
+
+/* MPC604EUM 3.5.2 Weak Consistency between Multiple Processors
+ The PowerPC architecture requires only weak consistency among
+ processors--that is, memory accesses between processors need not be
+ sequentially consistent and memory accesses among processors can occur
+ in any order. The ability to order memory accesses weakly provides
+ opportunities for more efficient use of the system bus. Unless a
+ dependency exists, the 604e allows read operations to precede store
+ operations. */
+#undef TARGET_RELAXED_ORDERING
+#define TARGET_RELAXED_ORDERING true
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
{"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_PPC_GFXOPT},
+ /* 8548 has a dummy entry for now. */
+ {"8548", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
{"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
{"970", PROCESSOR_POWER4,
POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
rs6000_init_hard_regno_mode_ok ();
- set_masks = POWER_MASKS | POWERPC_MASKS | MASK_SOFT_FLOAT;
+ set_masks = POWER_MASKS | POWERPC_MASKS | MASK_SOFT_FLOAT;
#ifdef OS_MISSING_POWERPC64
if (OS_MISSING_POWERPC64)
set_masks &= ~MASK_POWERPC64;
set_masks &= ~MASK_ALTIVEC;
#endif
- /* Don't override these by the processor default if given explicitly. */
- set_masks &= ~(target_flags_explicit
- & (MASK_MULTIPLE | MASK_STRING | MASK_SOFT_FLOAT));
+ /* Don't override by the processor default if given explicitly. */
+ set_masks &= ~target_flags_explicit;
/* Identify the processor type. */
rs6000_select[0].string = default_cpu;
else if (! strncmp (rs6000_traceback_name, "no", 2))
rs6000_traceback = traceback_none;
else
- error ("unknown -mtraceback arg `%s'; expecting `full', `partial' or `none'",
+ error ("unknown -mtraceback arg %qs; expecting %<full%>, %<partial%> or %<none%>",
rs6000_traceback_name);
}
rs6000_altivec_vrsave = 1;
}
+ /* Set the Darwin64 ABI as default for 64-bit Darwin. */
+ if (DEFAULT_ABI == ABI_DARWIN && TARGET_64BIT)
+ {
+ rs6000_darwin64_abi = 1;
+ /* Setting to empty string is same as "-mone-byte-bool". */
+#if TARGET_MACHO
+ darwin_one_byte_bool = "";
+#endif
+ /* Default to natural alignment, for better performance. */
+ rs6000_alignment_flags = MASK_ALIGN_NATURAL;
+ }
+
/* Handle -mabi= options. */
rs6000_parse_abi_options ();
/* Handle -malign-XXXXX option. */
rs6000_parse_alignment_option ();
+ rs6000_parse_float_gprs_option ();
+
/* Handle generic -mFOO=YES/NO options. */
rs6000_parse_yes_no_option ("vrsave", rs6000_altivec_vrsave_string,
&rs6000_altivec_vrsave);
rs6000_parse_yes_no_option ("isel", rs6000_isel_string,
&rs6000_isel);
rs6000_parse_yes_no_option ("spe", rs6000_spe_string, &rs6000_spe);
- rs6000_parse_yes_no_option ("float-gprs", rs6000_float_gprs_string,
- &rs6000_float_gprs);
/* Handle -mtls-size option. */
rs6000_parse_tls_size_option ();
#ifdef SUBSUBTARGET_OVERRIDE_OPTIONS
SUBSUBTARGET_OVERRIDE_OPTIONS;
#endif
+#ifdef SUB3TARGET_OVERRIDE_OPTIONS
+ SUB3TARGET_OVERRIDE_OPTIONS;
+#endif
if (TARGET_E500)
{
/* No SPE means 64-bit long doubles, even if an E500. */
if (rs6000_spe_string != 0
- && !strcmp (rs6000_spe_string, "no"))
+ && !strcmp (rs6000_spe_string, "no"))
rs6000_long_double_type_size = 64;
}
else if (rs6000_select[1].string != NULL)
while (base[-1] != 'm') base--;
if (*rs6000_longcall_switch != '\0')
- error ("invalid option `%s'", base);
+ error ("invalid option %qs", base);
rs6000_default_long_calls = (base[0] != 'n');
}
while (base[-1] != 'm') base--;
if (*rs6000_warn_altivec_long_switch != '\0')
- error ("invalid option `%s'", base);
+ error ("invalid option %qs", base);
rs6000_warn_altivec_long = (base[0] != 'n');
}
if (rs6000_sched_costly_dep_str)
{
if (! strcmp (rs6000_sched_costly_dep_str, "no"))
- rs6000_sched_costly_dep = no_dep_costly;
+ rs6000_sched_costly_dep = no_dep_costly;
else if (! strcmp (rs6000_sched_costly_dep_str, "all"))
- rs6000_sched_costly_dep = all_deps_costly;
+ rs6000_sched_costly_dep = all_deps_costly;
else if (! strcmp (rs6000_sched_costly_dep_str, "true_store_to_load"))
- rs6000_sched_costly_dep = true_store_to_load_dep_costly;
+ rs6000_sched_costly_dep = true_store_to_load_dep_costly;
else if (! strcmp (rs6000_sched_costly_dep_str, "store_to_load"))
- rs6000_sched_costly_dep = store_to_load_dep_costly;
+ rs6000_sched_costly_dep = store_to_load_dep_costly;
else
- rs6000_sched_costly_dep = atoi (rs6000_sched_costly_dep_str);
+ rs6000_sched_costly_dep = atoi (rs6000_sched_costly_dep_str);
}
/* Handle -minsert-sched-nops option. */
if (rs6000_sched_insert_nops_str)
{
if (! strcmp (rs6000_sched_insert_nops_str, "no"))
- rs6000_sched_insert_nops = sched_finish_none;
+ rs6000_sched_insert_nops = sched_finish_none;
else if (! strcmp (rs6000_sched_insert_nops_str, "pad"))
- rs6000_sched_insert_nops = sched_finish_pad_groups;
+ rs6000_sched_insert_nops = sched_finish_pad_groups;
else if (! strcmp (rs6000_sched_insert_nops_str, "regroup_exact"))
- rs6000_sched_insert_nops = sched_finish_regroup_exact;
+ rs6000_sched_insert_nops = sched_finish_regroup_exact;
else
- rs6000_sched_insert_nops = atoi (rs6000_sched_insert_nops_str);
+ rs6000_sched_insert_nops = atoi (rs6000_sched_insert_nops_str);
}
#ifdef TARGET_REGNAMES
targetm.asm_out.unaligned_op.di = NULL;
}
- /* Set maximum branch target alignment at two instructions, eight bytes. */
- align_jumps_max_skip = 8;
- align_loops_max_skip = 8;
+ /* Set branch target alignment, if not optimizing for size. */
+ if (!optimize_size)
+ {
+ if (rs6000_sched_groups)
+ {
+ if (align_functions <= 0)
+ align_functions = 16;
+ if (align_jumps <= 0)
+ align_jumps = 16;
+ if (align_loops <= 0)
+ 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;
+ }
/* Arrange to save and restore machine status around nested functions. */
init_machine_status = rs6000_init_machine_status;
}
}
+/* Implement targetm.vectorize.builtin_mask_for_load. */
+static tree
+rs6000_builtin_mask_for_load (void)
+{
+ if (TARGET_ALTIVEC)
+ return altivec_builtin_mask_for_load;
+ else
+ return 0;
+}
+
/* Handle generic options of the form -mfoo=yes/no.
NAME is the option name.
VALUE is the option value.
error ("not configured for ABI: '%s'", rs6000_abi_string);
}
+ /* These are here for testing during development only, do not
+ document in the manual please. */
+ else if (! strcmp (rs6000_abi_string, "d64"))
+ {
+ rs6000_darwin64_abi = 1;
+ warning ("Using darwin64 ABI");
+ }
+ else if (! strcmp (rs6000_abi_string, "d32"))
+ {
+ rs6000_darwin64_abi = 0;
+ warning ("Using old darwin ABI");
+ }
+
else if (! strcmp (rs6000_abi_string, "no-spe"))
rs6000_spe_abi = 0;
else
error ("unknown ABI specified: '%s'", rs6000_abi_string);
}
+/* Handle -mfloat-gprs= options. */
+static void
+rs6000_parse_float_gprs_option (void)
+{
+ if (rs6000_float_gprs_string == 0)
+ return;
+ else if (! strcmp (rs6000_float_gprs_string, "yes")
+ || ! strcmp (rs6000_float_gprs_string, "single"))
+ rs6000_float_gprs = 1;
+ else if (! strcmp (rs6000_float_gprs_string, "double"))
+ rs6000_float_gprs = 2;
+ else if (! strcmp (rs6000_float_gprs_string, "no"))
+ rs6000_float_gprs = 0;
+ else
+ error ("invalid option for -mfloat-gprs");
+}
+
/* Handle -malign-XXXXXX options. */
static void
rs6000_parse_alignment_option (void)
if (rs6000_alignment_string == 0)
return;
else if (! strcmp (rs6000_alignment_string, "power"))
- rs6000_alignment_flags = MASK_ALIGN_POWER;
+ {
+ /* On 64-bit Darwin, power alignment is ABI-incompatible with
+ some C library functions, so warn about it. The flag may be
+ useful for performance studies from time to time though, so
+ don't disable it entirely. */
+ if (DEFAULT_ABI == ABI_DARWIN && TARGET_64BIT)
+ warning ("-malign-power is not supported for 64-bit Darwin;"
+ " it is incompatible with the installed C and C++ libraries");
+ rs6000_alignment_flags = MASK_ALIGN_POWER;
+ }
else if (! strcmp (rs6000_alignment_string, "natural"))
rs6000_alignment_flags = MASK_ALIGN_NATURAL;
else
else if (strcmp (rs6000_tls_size_string, "64") == 0)
rs6000_tls_size = 64;
else
- error ("bad value `%s' for -mtls-size switch", rs6000_tls_size_string);
+ error ("bad value %qs for -mtls-size switch", rs6000_tls_size_string);
}
void
if (*start == '\0')
putc ('\n', file);
}
+
+ if (DEFAULT_ABI == ABI_AIX || (TARGET_ELF && flag_pic == 2))
+ {
+ toc_section ();
+ text_section ();
+ }
}
+
\f
/* Return nonzero if this function is known to have a null epilogue. */
return 0;
}
-/* Returns 1 always. */
-
-int
-any_operand (rtx op ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return 1;
-}
-
-/* Returns 1 always. */
-
-int
-any_parallel_operand (rtx op ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return 1;
-}
-
-/* Returns 1 if op is the count register. */
-
-int
-count_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != REG)
- return 0;
-
- if (REGNO (op) == COUNT_REGISTER_REGNUM)
- return 1;
-
- if (REGNO (op) > FIRST_PSEUDO_REGISTER)
- return 1;
-
- return 0;
-}
-
-/* Returns 1 if op is an altivec register. */
-
-int
-altivec_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-
- return (register_operand (op, mode)
- && (GET_CODE (op) != REG
- || REGNO (op) > FIRST_PSEUDO_REGISTER
- || ALTIVEC_REGNO_P (REGNO (op))));
-}
-
-int
-xer_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != REG)
- return 0;
-
- if (XER_REGNO_P (REGNO (op)))
- return 1;
-
- return 0;
-}
-
-/* Return 1 if OP is a signed 8-bit constant. Int multiplication
- by such constants completes more quickly. */
-
-int
-s8bit_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return ( GET_CODE (op) == CONST_INT
- && (INTVAL (op) >= -128 && INTVAL (op) <= 127));
-}
-
-/* Return 1 if OP is a constant that can fit in a D field. */
-
-int
-short_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == CONST_INT
- && CONST_OK_FOR_LETTER_P (INTVAL (op), 'I'));
-}
-
-/* Similar for an unsigned D field. */
-
-int
-u_short_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == CONST_INT
- && CONST_OK_FOR_LETTER_P (INTVAL (op) & GET_MODE_MASK (mode), 'K'));
-}
-
-/* Return 1 if OP is a CONST_INT that cannot fit in a signed D field. */
-
-int
-non_short_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == CONST_INT
- && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x8000) >= 0x10000);
-}
-
-/* Returns 1 if OP is a CONST_INT that is a positive value
- and an exact power of 2. */
-
-int
-exact_log2_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == CONST_INT
- && INTVAL (op) > 0
- && exact_log2 (INTVAL (op)) >= 0);
-}
-
-/* Returns 1 if OP is a register that is not special (i.e., not MQ,
- ctr, or lr). */
-
-int
-gpc_reg_operand (rtx op, enum machine_mode mode)
-{
- return (register_operand (op, mode)
- && (GET_CODE (op) != REG
- || (REGNO (op) >= ARG_POINTER_REGNUM
- && !XER_REGNO_P (REGNO (op)))
- || REGNO (op) < MQ_REGNO));
-}
-
-/* Returns 1 if OP is either a pseudo-register or a register denoting a
- CR field. */
-
-int
-cc_reg_operand (rtx op, enum machine_mode mode)
-{
- return (register_operand (op, mode)
- && (GET_CODE (op) != REG
- || REGNO (op) >= FIRST_PSEUDO_REGISTER
- || CR_REGNO_P (REGNO (op))));
-}
-
-/* Returns 1 if OP is either a pseudo-register or a register denoting a
- CR field that isn't CR0. */
-
-int
-cc_reg_not_cr0_operand (rtx op, enum machine_mode mode)
-{
- return (register_operand (op, mode)
- && (GET_CODE (op) != REG
- || REGNO (op) >= FIRST_PSEUDO_REGISTER
- || CR_REGNO_NOT_CR0_P (REGNO (op))));
-}
-
-/* Returns 1 if OP is either a constant integer valid for a D-field or
- a non-special register. If a register, it must be in the proper
- mode unless MODE is VOIDmode. */
-
-int
-reg_or_short_operand (rtx op, enum machine_mode mode)
-{
- return short_cint_operand (op, mode) || gpc_reg_operand (op, mode);
-}
-
-/* Similar, except check if the negation of the constant would be
- valid for a D-field. Don't allow a constant zero, since all the
- patterns that call this predicate use "addic r1,r2,-constant" on
- a constant value to set a carry when r2 is greater or equal to
- "constant". That doesn't work for zero. */
-
-int
-reg_or_neg_short_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == CONST_INT)
- return CONST_OK_FOR_LETTER_P (INTVAL (op), 'P') && INTVAL (op) != 0;
-
- return gpc_reg_operand (op, mode);
-}
-
-/* Returns 1 if OP is either a constant integer valid for a DS-field or
- a non-special register. If a register, it must be in the proper
- mode unless MODE is VOIDmode. */
-
-int
-reg_or_aligned_short_operand (rtx op, enum machine_mode mode)
-{
- if (gpc_reg_operand (op, mode))
- return 1;
- else if (short_cint_operand (op, mode) && !(INTVAL (op) & 3))
- return 1;
-
- return 0;
-}
-
-
-/* Return 1 if the operand is either a register or an integer whose
- high-order 16 bits are zero. */
-
-int
-reg_or_u_short_operand (rtx op, enum machine_mode mode)
-{
- return u_short_cint_operand (op, mode) || gpc_reg_operand (op, mode);
-}
-
-/* Return 1 is the operand is either a non-special register or ANY
- constant integer. */
-
-int
-reg_or_cint_operand (rtx op, enum machine_mode mode)
-{
- return (GET_CODE (op) == CONST_INT || gpc_reg_operand (op, mode));
-}
-
-/* Return 1 is the operand is either a non-special register or ANY
- 32-bit signed constant integer. */
-
-int
-reg_or_arith_cint_operand (rtx op, enum machine_mode mode)
-{
- return (gpc_reg_operand (op, mode)
- || (GET_CODE (op) == CONST_INT
-#if HOST_BITS_PER_WIDE_INT != 32
- && ((unsigned HOST_WIDE_INT) (INTVAL (op) + 0x80000000)
- < (unsigned HOST_WIDE_INT) 0x100000000ll)
-#endif
- ));
-}
-
-/* Return 1 is the operand is either a non-special register or a 32-bit
- signed constant integer valid for 64-bit addition. */
-
-int
-reg_or_add_cint64_operand (rtx op, enum machine_mode mode)
-{
- return (gpc_reg_operand (op, mode)
- || (GET_CODE (op) == CONST_INT
-#if HOST_BITS_PER_WIDE_INT == 32
- && INTVAL (op) < 0x7fff8000
-#else
- && ((unsigned HOST_WIDE_INT) (INTVAL (op) + 0x80008000)
- < 0x100000000ll)
-#endif
- ));
-}
-
-/* Return 1 is the operand is either a non-special register or a 32-bit
- signed constant integer valid for 64-bit subtraction. */
-
-int
-reg_or_sub_cint64_operand (rtx op, enum machine_mode mode)
-{
- return (gpc_reg_operand (op, mode)
- || (GET_CODE (op) == CONST_INT
-#if HOST_BITS_PER_WIDE_INT == 32
- && (- INTVAL (op)) < 0x7fff8000
-#else
- && ((unsigned HOST_WIDE_INT) ((- INTVAL (op)) + 0x80008000)
- < 0x100000000ll)
-#endif
- ));
-}
-
-/* Return 1 is the operand is either a non-special register or ANY
- 32-bit unsigned constant integer. */
-
-int
-reg_or_logical_cint_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == CONST_INT)
- {
- if (GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT)
- {
- if (GET_MODE_BITSIZE (mode) <= 32)
- abort ();
-
- if (INTVAL (op) < 0)
- return 0;
- }
-
- return ((INTVAL (op) & GET_MODE_MASK (mode)
- & (~ (unsigned HOST_WIDE_INT) 0xffffffff)) == 0);
- }
- else if (GET_CODE (op) == CONST_DOUBLE)
- {
- if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
- || mode != DImode)
- abort ();
-
- return CONST_DOUBLE_HIGH (op) == 0;
- }
- else
- return gpc_reg_operand (op, mode);
-}
-
-/* Return 1 if the operand is an operand that can be loaded via the GOT. */
-
-int
-got_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == SYMBOL_REF
- || GET_CODE (op) == CONST
- || GET_CODE (op) == LABEL_REF);
-}
-
-/* Return 1 if the operand is a simple references that can be loaded via
- the GOT (labels involving addition aren't allowed). */
-
-int
-got_no_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF);
-}
-
/* Return the number of instructions it takes to form a constant in an
integer register. */
-static int
+int
num_insns_constant_wide (HOST_WIDE_INT value)
{
/* signed constant loadable with {cal|addi} */
#if HOST_BITS_PER_WIDE_INT == 64
if ((INTVAL (op) >> 31) != 0 && (INTVAL (op) >> 31) != -1
&& mask64_operand (op, mode))
- return 2;
+ return 2;
else
#endif
return num_insns_constant_wide (INTVAL (op));
abort ();
}
-/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a
- register with one instruction per word. We only do this if we can
- safely read CONST_DOUBLE_{LOW,HIGH}. */
+/* Returns the constant for the splat instruction, if exists. */
int
-easy_fp_constant (rtx op, enum machine_mode mode)
+easy_vector_splat_const (int cst, enum machine_mode mode)
{
- if (GET_CODE (op) != CONST_DOUBLE
- || GET_MODE (op) != mode
- || (GET_MODE_CLASS (mode) != MODE_FLOAT && mode != DImode))
- return 0;
-
- /* Consider all constants with -msoft-float to be easy. */
- if ((TARGET_SOFT_FLOAT || !TARGET_FPRS)
- && mode != DImode)
- return 1;
-
- /* If we are using V.4 style PIC, consider all constants to be hard. */
- if (flag_pic && DEFAULT_ABI == ABI_V4)
- return 0;
-
-#ifdef TARGET_RELOCATABLE
- /* Similarly if we are using -mrelocatable, consider all constants
- to be hard. */
- if (TARGET_RELOCATABLE)
- return 0;
-#endif
-
- if (mode == TFmode)
+ switch (mode)
{
- long k[4];
- REAL_VALUE_TYPE rv;
+ case V4SImode:
+ if (EASY_VECTOR_15 (cst)
+ || EASY_VECTOR_15_ADD_SELF (cst))
+ return cst;
+ if ((cst & 0xffff) != ((cst >> 16) & 0xffff))
+ break;
+ cst = cst >> 16;
+ /* Fall thru */
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_LONG_DOUBLE (rv, k);
+ case V8HImode:
+ if (EASY_VECTOR_15 (cst)
+ || EASY_VECTOR_15_ADD_SELF (cst))
+ return cst;
+ if ((cst & 0xff) != ((cst >> 8) & 0xff))
+ break;
+ cst = cst >> 8;
+ /* Fall thru */
- return (num_insns_constant_wide ((HOST_WIDE_INT) k[0]) == 1
- && num_insns_constant_wide ((HOST_WIDE_INT) k[1]) == 1
- && num_insns_constant_wide ((HOST_WIDE_INT) k[2]) == 1
- && num_insns_constant_wide ((HOST_WIDE_INT) k[3]) == 1);
+ case V16QImode:
+ if (EASY_VECTOR_15 (cst)
+ || EASY_VECTOR_15_ADD_SELF (cst))
+ return cst;
+ default:
+ break;
}
-
- else if (mode == DFmode)
- {
- long k[2];
- REAL_VALUE_TYPE rv;
-
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
-
- return (num_insns_constant_wide ((HOST_WIDE_INT) k[0]) == 1
- && num_insns_constant_wide ((HOST_WIDE_INT) k[1]) == 1);
- }
-
- else if (mode == SFmode)
- {
- long l;
- REAL_VALUE_TYPE rv;
-
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_SINGLE (rv, l);
-
- return num_insns_constant_wide (l) == 1;
- }
-
- else if (mode == DImode)
- return ((TARGET_POWERPC64
- && GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_LOW (op) == 0)
- || (num_insns_constant (op, DImode) <= 2));
-
- else if (mode == SImode)
- return 1;
- else
- abort ();
-}
-
-/* Returns the constant for the splat instruction, if exists. */
-
-static int
-easy_vector_splat_const (int cst, enum machine_mode mode)
-{
- switch (mode)
- {
- case V4SImode:
- if (EASY_VECTOR_15 (cst)
- || EASY_VECTOR_15_ADD_SELF (cst))
- return cst;
- if ((cst & 0xffff) != ((cst >> 16) & 0xffff))
- break;
- cst = cst >> 16;
- case V8HImode:
- if (EASY_VECTOR_15 (cst)
- || EASY_VECTOR_15_ADD_SELF (cst))
- return cst;
- if ((cst & 0xff) != ((cst >> 8) & 0xff))
- break;
- cst = cst >> 8;
- case V16QImode:
- if (EASY_VECTOR_15 (cst)
- || EASY_VECTOR_15_ADD_SELF (cst))
- return cst;
- default:
- break;
- }
- return 0;
-}
-
+ return 0;
+}
/* Return nonzero if all elements of a vector have the same value. */
-static int
+int
easy_vector_same (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
int units, i, cst;
return 0;
}
-/* Return 1 if the operand is a CONST_INT and can be put into a
- register without using memory. */
-
-int
-easy_vector_constant (rtx op, enum machine_mode mode)
-{
- int cst, cst2;
-
- if (GET_CODE (op) != CONST_VECTOR
- || (!TARGET_ALTIVEC
- && !TARGET_SPE))
- return 0;
-
- if (zero_constant (op, mode)
- && ((TARGET_ALTIVEC && ALTIVEC_VECTOR_MODE (mode))
- || (TARGET_SPE && SPE_VECTOR_MODE (mode))))
- return 1;
-
- if (GET_MODE_CLASS (mode) != MODE_VECTOR_INT)
- return 0;
-
- if (TARGET_SPE && mode == V1DImode)
- return 0;
-
- cst = INTVAL (CONST_VECTOR_ELT (op, 0));
- cst2 = INTVAL (CONST_VECTOR_ELT (op, 1));
-
- /* Limit SPE vectors to 15 bits signed. These we can generate with:
- li r0, CONSTANT1
- evmergelo r0, r0, r0
- li r0, CONSTANT2
-
- I don't know how efficient it would be to allow bigger constants,
- considering we'll have an extra 'ori' for every 'li'. I doubt 5
- instructions is better than a 64-bit memory load, but I don't
- have the e500 timing specs. */
- if (TARGET_SPE && mode == V2SImode
- && cst >= -0x7fff && cst <= 0x7fff
- && cst2 >= -0x7fff && cst2 <= 0x7fff)
- return 1;
-
- if (TARGET_ALTIVEC
- && easy_vector_same (op, mode))
- {
- cst = easy_vector_splat_const (cst, mode);
- if (EASY_VECTOR_15_ADD_SELF (cst)
- || EASY_VECTOR_15 (cst))
- return 1;
- }
- return 0;
-}
-
-/* Same as easy_vector_constant but only for EASY_VECTOR_15_ADD_SELF. */
-
-int
-easy_vector_constant_add_self (rtx op, enum machine_mode mode)
-{
- int cst;
- if (TARGET_ALTIVEC
- && GET_CODE (op) == CONST_VECTOR
- && easy_vector_same (op, mode))
- {
- cst = easy_vector_splat_const (INTVAL (CONST_VECTOR_ELT (op, 0)), mode);
- if (EASY_VECTOR_15_ADD_SELF (cst))
- return 1;
- }
- return 0;
-}
-
/* Generate easy_vector_constant out of a easy_vector_constant_add_self. */
rtx
else if (EASY_VECTOR_15_ADD_SELF (cst))
return "#";
cst = cst >> 16;
+ /* Fall thru */
+
case V8HImode:
if (EASY_VECTOR_15 (cst))
{
else if (EASY_VECTOR_15_ADD_SELF (cst))
return "#";
cst = cst >> 8;
+ /* Fall thru */
+
case V16QImode:
if (EASY_VECTOR_15 (cst))
{
}
else if (EASY_VECTOR_15_ADD_SELF (cst))
return "#";
+
default:
abort ();
}
pattern of V1DI, V4HI, and V2SF.
FIXME: We should probably return # and add post reload
- splitters for these, but this way is so easy ;-).
- */
+ splitters for these, but this way is so easy ;-). */
operands[1] = GEN_INT (cst);
operands[2] = GEN_INT (cst2);
if (cst == cst2)
abort ();
}
-/* Return 1 if the operand is the constant 0. This works for scalars
- as well as vectors. */
-int
-zero_constant (rtx op, enum machine_mode mode)
-{
- return op == CONST0_RTX (mode);
-}
-
-/* Return 1 if the operand is 0.0. */
-int
-zero_fp_constant (rtx op, enum machine_mode mode)
-{
- return GET_MODE_CLASS (mode) == MODE_FLOAT && op == CONST0_RTX (mode);
-}
-
-/* Return 1 if the operand is in volatile memory. Note that during
- the RTL generation phase, memory_operand does not return TRUE for
- volatile memory references. So this function allows us to
- recognize volatile references where its safe. */
-
-int
-volatile_mem_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) != MEM)
- return 0;
-
- if (!MEM_VOLATILE_P (op))
- return 0;
-
- if (mode != GET_MODE (op))
- return 0;
-
- if (reload_completed)
- return memory_operand (op, mode);
-
- if (reload_in_progress)
- return strict_memory_address_p (mode, XEXP (op, 0));
-
- return memory_address_p (mode, XEXP (op, 0));
-}
-
-/* Return 1 if the operand is an offsettable memory operand. */
-
-int
-offsettable_mem_operand (rtx op, enum machine_mode mode)
-{
- return ((GET_CODE (op) == MEM)
- && offsettable_address_p (reload_completed || reload_in_progress,
- mode, XEXP (op, 0)));
-}
-
-/* Return 1 if the operand is either an easy FP constant (see above) or
- memory. */
-
-int
-mem_or_easy_const_operand (rtx op, enum machine_mode mode)
-{
- return memory_operand (op, mode) || easy_fp_constant (op, mode);
-}
-
-/* Return 1 if the operand is either a non-special register or an item
- that can be used as the operand of a `mode' add insn. */
-
-int
-add_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == CONST_INT)
- return (CONST_OK_FOR_LETTER_P (INTVAL (op), 'I')
- || CONST_OK_FOR_LETTER_P (INTVAL (op), 'L'));
-
- return gpc_reg_operand (op, mode);
-}
-
-/* Return 1 if OP is a constant but not a valid add_operand. */
-
-int
-non_add_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == CONST_INT
- && !CONST_OK_FOR_LETTER_P (INTVAL (op), 'I')
- && !CONST_OK_FOR_LETTER_P (INTVAL (op), 'L'));
-}
-
-/* Return 1 if the operand is a non-special register or a constant that
- can be used as the operand of an OR or XOR insn on the RS/6000. */
-
-int
-logical_operand (rtx op, enum machine_mode mode)
-{
- HOST_WIDE_INT opl, oph;
-
- if (gpc_reg_operand (op, mode))
- return 1;
-
- if (GET_CODE (op) == CONST_INT)
- {
- opl = INTVAL (op) & GET_MODE_MASK (mode);
-
-#if HOST_BITS_PER_WIDE_INT <= 32
- if (GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT && opl < 0)
- return 0;
-#endif
- }
- else if (GET_CODE (op) == CONST_DOUBLE)
- {
- if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
- abort ();
-
- opl = CONST_DOUBLE_LOW (op);
- oph = CONST_DOUBLE_HIGH (op);
- if (oph != 0)
- return 0;
- }
- else
- return 0;
-
- return ((opl & ~ (unsigned HOST_WIDE_INT) 0xffff) == 0
- || (opl & ~ (unsigned HOST_WIDE_INT) 0xffff0000) == 0);
-}
-
-/* Return 1 if C is a constant that is not a logical operand (as
- above), but could be split into one. */
-
-int
-non_logical_cint_operand (rtx op, enum machine_mode mode)
-{
- return ((GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE)
- && ! logical_operand (op, mode)
- && reg_or_logical_cint_operand (op, mode));
-}
-
-/* Return 1 if C is a constant that can be encoded in a 32-bit mask on the
- RS/6000. It is if there are no more than two 1->0 or 0->1 transitions.
- Reject all ones and all zeros, since these should have been optimized
- away and confuse the making of MB and ME. */
-
-int
-mask_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- HOST_WIDE_INT c, lsb;
-
- if (GET_CODE (op) != CONST_INT)
- return 0;
-
- c = INTVAL (op);
-
- /* Fail in 64-bit mode if the mask wraps around because the upper
- 32-bits of the mask will all be 1s, contrary to GCC's internal view. */
- if (TARGET_POWERPC64 && (c & 0x80000001) == 0x80000001)
- return 0;
-
- /* We don't change the number of transitions by inverting,
- so make sure we start with the LS bit zero. */
- if (c & 1)
- c = ~c;
-
- /* Reject all zeros or all ones. */
- if (c == 0)
- return 0;
-
- /* Find the first transition. */
- lsb = c & -c;
-
- /* Invert to look for a second transition. */
- c = ~c;
-
- /* Erase first transition. */
- c &= -lsb;
-
- /* Find the second transition (if any). */
- lsb = c & -c;
-
- /* Match if all the bits above are 1's (or c is zero). */
- return c == -lsb;
-}
-
-/* Return 1 for the PowerPC64 rlwinm corner case. */
-
-int
-mask_operand_wrap (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- HOST_WIDE_INT c, lsb;
-
- if (GET_CODE (op) != CONST_INT)
- return 0;
-
- c = INTVAL (op);
-
- if ((c & 0x80000001) != 0x80000001)
- return 0;
-
- c = ~c;
- if (c == 0)
- return 0;
-
- lsb = c & -c;
- c = ~c;
- c &= -lsb;
- lsb = c & -c;
- return c == -lsb;
-}
-
-/* Return 1 if the operand is a constant that is a PowerPC64 mask.
- It is if there are no more than one 1->0 or 0->1 transitions.
- Reject all zeros, since zero should have been optimized away and
- confuses the making of MB and ME. */
-
-int
-mask64_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) == CONST_INT)
- {
- HOST_WIDE_INT c, lsb;
-
- c = INTVAL (op);
-
- /* Reject all zeros. */
- if (c == 0)
- return 0;
-
- /* We don't change the number of transitions by inverting,
- so make sure we start with the LS bit zero. */
- if (c & 1)
- c = ~c;
-
- /* Find the transition, and check that all bits above are 1's. */
- lsb = c & -c;
-
- /* Match if all the bits above are 1's (or c is zero). */
- return c == -lsb;
- }
- return 0;
-}
-
-/* Like mask64_operand, but allow up to three transitions. This
- predicate is used by insn patterns that generate two rldicl or
- rldicr machine insns. */
-
int
-mask64_2_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+mask64_1or2_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED,
+ bool allow_one)
{
if (GET_CODE (op) == CONST_INT)
{
HOST_WIDE_INT c, lsb;
-
+ bool one_ok;
+
c = INTVAL (op);
/* Disallow all zeros. */
if (c == 0)
return 0;
+ /* We can use a single rlwinm insn if no upper bits of C are set
+ AND there are zero, one or two transitions in the _whole_ of
+ C. */
+ one_ok = !(c & ~(HOST_WIDE_INT)0xffffffff);
+
/* We don't change the number of transitions by inverting,
so make sure we start with the LS bit zero. */
if (c & 1)
/* Erase second transition. */
c &= -lsb;
+ if (one_ok && !(allow_one || c))
+ return 0;
+
/* Find the third transition (if any). */
lsb = c & -c;
#endif
}
-/* Return 1 if the operand is either a non-special register or a constant
- that can be used as the operand of a PowerPC64 logical AND insn. */
+/* Return TRUE if OP is an invalid SUBREG operation on the e500. */
-int
-and64_operand (rtx op, enum machine_mode mode)
+bool
+invalid_e500_subreg (rtx op, enum machine_mode mode)
{
- if (fixed_regs[CR0_REGNO]) /* CR0 not available, don't do andi./andis. */
- return (gpc_reg_operand (op, mode) || mask64_operand (op, mode));
-
- return (logical_operand (op, mode) || mask64_operand (op, mode));
-}
-
-/* Like the above, but also match constants that can be implemented
- with two rldicl or rldicr insns. */
+ /* Reject (subreg:SI (reg:DF)). */
+ if (GET_CODE (op) == SUBREG
+ && mode == SImode
+ && REG_P (SUBREG_REG (op))
+ && GET_MODE (SUBREG_REG (op)) == DFmode)
+ return true;
-int
-and64_2_operand (rtx op, enum machine_mode mode)
-{
- if (fixed_regs[CR0_REGNO]) /* CR0 not available, don't do andi./andis. */
- return gpc_reg_operand (op, mode) || mask64_2_operand (op, mode);
+ /* Reject (subreg:DF (reg:DI)). */
+ if (GET_CODE (op) == SUBREG
+ && mode == DFmode
+ && REG_P (SUBREG_REG (op))
+ && GET_MODE (SUBREG_REG (op)) == DImode)
+ return true;
- return logical_operand (op, mode) || mask64_2_operand (op, mode);
+ return false;
}
-/* Return 1 if the operand is either a non-special register or a
- constant that can be used as the operand of an RS/6000 logical AND insn. */
+/* Darwin, AIX increases natural record alignment to doubleword if the first
+ field is an FP double while the FP fields remain word aligned. */
-int
-and_operand (rtx op, enum machine_mode mode)
+unsigned int
+rs6000_special_round_type_align (tree type, int computed, int specified)
{
- if (fixed_regs[CR0_REGNO]) /* CR0 not available, don't do andi./andis. */
- return (gpc_reg_operand (op, mode) || mask_operand (op, mode));
+ tree field = TYPE_FIELDS (type);
- return (logical_operand (op, mode) || mask_operand (op, mode));
-}
+ /* Skip all non field decls */
+ while (field != NULL && TREE_CODE (field) != FIELD_DECL)
+ field = TREE_CHAIN (field);
-/* Return 1 if the operand is a general register or memory operand. */
+ if (field == NULL || field == type || DECL_MODE (field) != DFmode)
+ return MAX (computed, specified);
-int
-reg_or_mem_operand (rtx op, enum machine_mode mode)
-{
- return (gpc_reg_operand (op, mode)
- || memory_operand (op, mode)
- || macho_lo_sum_memory_operand (op, mode)
- || volatile_mem_operand (op, mode));
+ return MAX (MAX (computed, specified), 64);
}
-/* Return 1 if the operand is a general register or memory operand without
- pre_inc or pre_dec which produces invalid form of PowerPC lwa
- instruction. */
+/* Return 1 for an operand in small memory on V.4/eabi. */
int
-lwa_operand (rtx op, enum machine_mode mode)
+small_data_operand (rtx op ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
{
- rtx inner = op;
-
- if (reload_completed && GET_CODE (inner) == SUBREG)
- inner = SUBREG_REG (inner);
+#if TARGET_ELF
+ rtx sym_ref;
- return gpc_reg_operand (inner, mode)
- || (memory_operand (inner, mode)
- && GET_CODE (XEXP (inner, 0)) != PRE_INC
- && GET_CODE (XEXP (inner, 0)) != PRE_DEC
- && (GET_CODE (XEXP (inner, 0)) != PLUS
- || GET_CODE (XEXP (XEXP (inner, 0), 1)) != CONST_INT
- || INTVAL (XEXP (XEXP (inner, 0), 1)) % 4 == 0));
-}
+ if (rs6000_sdata == SDATA_NONE || rs6000_sdata == SDATA_DATA)
+ return 0;
-/* Return 1 if the operand, used inside a MEM, is a SYMBOL_REF. */
-
-int
-symbol_ref_operand (rtx op, enum machine_mode mode)
-{
- if (mode != VOIDmode && GET_MODE (op) != mode)
- return 0;
-
- return (GET_CODE (op) == SYMBOL_REF
- && (DEFAULT_ABI != ABI_AIX || SYMBOL_REF_FUNCTION_P (op)));
-}
-
-/* Return 1 if the operand, used inside a MEM, is a valid first argument
- to CALL. This is a SYMBOL_REF, a pseudo-register, LR or CTR. */
-
-int
-call_operand (rtx op, enum machine_mode mode)
-{
- if (mode != VOIDmode && GET_MODE (op) != mode)
- return 0;
-
- return (GET_CODE (op) == SYMBOL_REF
- || (GET_CODE (op) == REG
- && (REGNO (op) == LINK_REGISTER_REGNUM
- || REGNO (op) == COUNT_REGISTER_REGNUM
- || REGNO (op) >= FIRST_PSEUDO_REGISTER)));
-}
-
-/* Return 1 if the operand is a SYMBOL_REF for a function known to be in
- this file. */
-
-int
-current_file_function_operand (rtx op,
- enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == SYMBOL_REF
- && (DEFAULT_ABI != ABI_AIX || SYMBOL_REF_FUNCTION_P (op))
- && (SYMBOL_REF_LOCAL_P (op)
- || (op == XEXP (DECL_RTL (current_function_decl), 0))));
-}
-
-/* Return 1 if this operand is a valid input for a move insn. */
-
-int
-input_operand (rtx op, enum machine_mode mode)
-{
- /* Memory is always valid. */
- if (memory_operand (op, mode))
- return 1;
-
- /* For floating-point, easy constants are valid. */
- if (GET_MODE_CLASS (mode) == MODE_FLOAT
- && CONSTANT_P (op)
- && easy_fp_constant (op, mode))
- return 1;
-
- /* Allow any integer constant. */
- if (GET_MODE_CLASS (mode) == MODE_INT
- && (GET_CODE (op) == CONST_INT
- || GET_CODE (op) == CONST_DOUBLE))
- return 1;
-
- /* Allow easy vector constants. */
- if (GET_CODE (op) == CONST_VECTOR
- && easy_vector_constant (op, mode))
- return 1;
-
- /* For floating-point or multi-word mode, the only remaining valid type
- is a register. */
- if (GET_MODE_CLASS (mode) == MODE_FLOAT
- || GET_MODE_SIZE (mode) > UNITS_PER_WORD)
- return register_operand (op, mode);
-
- /* The only cases left are integral modes one word or smaller (we
- do not get called for MODE_CC values). These can be in any
- register. */
- if (register_operand (op, mode))
- return 1;
-
- /* A SYMBOL_REF referring to the TOC is valid. */
- if (legitimate_constant_pool_address_p (op))
- return 1;
-
- /* A constant pool expression (relative to the TOC) is valid */
- if (toc_relative_expr_p (op))
- return 1;
-
- /* V.4 allows SYMBOL_REFs and CONSTs that are in the small data region
- to be valid. */
- if (DEFAULT_ABI == ABI_V4
- && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST)
- && small_data_operand (op, Pmode))
- return 1;
-
- return 0;
-}
-
-
-/* Darwin, AIX increases natural record alignment to doubleword if the first
- field is an FP double while the FP fields remain word aligned. */
-
-unsigned int
-rs6000_special_round_type_align (tree type, int computed, int specified)
-{
- tree field = TYPE_FIELDS (type);
-
- /* Skip all the static variables only if ABI is greater than
- 1 or equal to 0. */
- while (field != NULL && TREE_CODE (field) == VAR_DECL)
- field = TREE_CHAIN (field);
-
- if (field == NULL || field == type || DECL_MODE (field) != DFmode)
- return MAX (computed, specified);
-
- return MAX (MAX (computed, specified), 64);
-}
-
-/* Return 1 for an operand in small memory on V.4/eabi. */
-
-int
-small_data_operand (rtx op ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-#if TARGET_ELF
- rtx sym_ref;
-
- if (rs6000_sdata == SDATA_NONE || rs6000_sdata == SDATA_DATA)
- return 0;
-
- if (DEFAULT_ABI != ABI_V4)
- return 0;
+ if (DEFAULT_ABI != ABI_V4)
+ return 0;
if (GET_CODE (op) == SYMBOL_REF)
sym_ref = op;
HOST_WIDE_INT summand;
/* We have to be careful here, because it is the referenced address
- that must be 32k from _SDA_BASE_, not just the symbol. */
+ that must be 32k from _SDA_BASE_, not just the symbol. */
summand = INTVAL (XEXP (sum, 1));
if (summand < 0 || (unsigned HOST_WIDE_INT) summand > g_switch_value)
- return 0;
+ return 0;
sym_ref = XEXP (sum, 0);
}
return (off % 4) == 0;
}
+/* Return true if the operand is an indirect or indexed memory operand. */
+
+int
+indexed_or_indirect_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ rtx addr;
+ if (!memory_operand (op, mode))
+ return 0;
+
+ addr = XEXP (op, 0);
+ if (GET_CODE (addr) == REG)
+ return 1;
+ if (GET_CODE (addr) == PLUS
+ && GET_CODE (XEXP (addr, 0)) == REG
+ && GET_CODE (XEXP (addr, 1)) == REG)
+ return 1;
+ return 0;
+}
+
/* Return true if either operand is a general purpose register. */
bool
static int
constant_pool_expr_1 (rtx op, int *have_sym, int *have_toc)
{
- switch (GET_CODE(op))
+ switch (GET_CODE (op))
{
case SYMBOL_REF:
if (RS6000_SYMBOL_REF_TLS_P (op))
return constant_pool_expr_1 (op, &have_sym, &have_toc) && have_sym;
}
-static bool
+bool
toc_relative_expr_p (rtx op)
{
int have_sym = 0;
return SPE_CONST_OFFSET_OK (offset);
case DFmode:
+ if (TARGET_E500_DOUBLE)
+ return SPE_CONST_OFFSET_OK (offset);
+
case DImode:
+ /* On e500v2, we may have:
+
+ (subreg:DF (mem:DI (plus (reg) (const_int))) 0).
+
+ Which gets addressed with evldd instructions. */
+ if (TARGET_E500_DOUBLE)
+ return SPE_CONST_OFFSET_OK (offset);
+
if (mode == DFmode || !TARGET_POWERPC64)
extra = 4;
else if (offset & 3)
if (GET_CODE (x) != PLUS)
return false;
+
op0 = XEXP (x, 0);
op1 = XEXP (x, 1);
&& INT_REG_OK_FOR_INDEX_P (op0, strict)));
}
-static inline bool
+inline bool
legitimate_indirect_address_p (rtx x, int strict)
{
return GET_CODE (x) == REG && INT_REG_OK_FOR_BASE_P (x, strict);
}
-static bool
+bool
macho_lo_sum_memory_operand (rtx x, enum machine_mode mode)
{
- if (!TARGET_MACHO || !flag_pic
- || mode != SImode || GET_CODE(x) != MEM)
- return false;
- x = XEXP (x, 0);
+ if (!TARGET_MACHO || !flag_pic
+ || mode != SImode || GET_CODE (x) != MEM)
+ return false;
+ x = XEXP (x, 0);
if (GET_CODE (x) != LO_SUM)
return false;
return false;
if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict))
return false;
+ /* Restrict addressing for DI because of our SUBREG hackery. */
+ if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode))
+ return false;
x = XEXP (x, 1);
if (TARGET_ELF || TARGET_MACHO)
return false;
if (GET_MODE_NUNITS (mode) != 1)
return false;
- if (GET_MODE_BITSIZE (mode) > 64)
+ if (GET_MODE_BITSIZE (mode) > 64
+ || (GET_MODE_BITSIZE (mode) > 32 && !TARGET_POWERPC64
+ && !(TARGET_HARD_FLOAT && TARGET_FPRS && mode == DFmode)))
return false;
return CONSTANT_P (x);
&& GET_MODE_NUNITS (mode) == 1
&& ((TARGET_HARD_FLOAT && TARGET_FPRS)
|| TARGET_POWERPC64
- || (mode != DFmode && mode != TFmode))
+ || (((mode != DImode && mode != DFmode) || TARGET_E500_DOUBLE)
+ && mode != TFmode))
&& (TARGET_POWERPC64 || mode != DImode)
&& mode != TImode)
{
reg = force_reg (Pmode, x);
return reg;
}
- else if (SPE_VECTOR_MODE (mode))
+ else if (SPE_VECTOR_MODE (mode)
+ || (TARGET_E500_DOUBLE && (mode == DFmode
+ || mode == DImode)))
{
+ if (mode == DImode)
+ return NULL_RTX;
/* We accept [reg + reg] and [reg + OFFSET]. */
if (GET_CODE (x) == PLUS)
- {
- rtx op1 = XEXP (x, 0);
- rtx op2 = XEXP (x, 1);
+ {
+ rtx op1 = XEXP (x, 0);
+ rtx op2 = XEXP (x, 1);
- op1 = force_reg (Pmode, op1);
+ op1 = force_reg (Pmode, op1);
- if (GET_CODE (op2) != REG
- && (GET_CODE (op2) != CONST_INT
- || !SPE_CONST_OFFSET_OK (INTVAL (op2))))
- op2 = force_reg (Pmode, op2);
+ if (GET_CODE (op2) != REG
+ && (GET_CODE (op2) != CONST_INT
+ || !SPE_CONST_OFFSET_OK (INTVAL (op2))))
+ op2 = force_reg (Pmode, op2);
- return gen_rtx_PLUS (Pmode, op1, op2);
- }
+ return gen_rtx_PLUS (Pmode, op1, op2);
+ }
return force_reg (Pmode, x);
}
return dest;
}
-/* Return 1 if X is a SYMBOL_REF for a TLS symbol. This is used in
- instruction definitions. */
-
-int
-rs6000_tls_symbol_ref (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return RS6000_SYMBOL_REF_TLS_P (x);
-}
-
/* Return 1 if X contains a thread-local symbol. */
bool
/* Return 1 if *X is a thread-local symbol. This is the same as
rs6000_tls_symbol_ref except for the type of the unused argument. */
-static inline int
+static int
rs6000_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
{
return RS6000_SYMBOL_REF_TLS_P (*x);
machopic_function_base_name() defined. */
rtx
rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
- int opnum, int type, int ind_levels ATTRIBUTE_UNUSED, int *win)
+ int opnum, int type,
+ int ind_levels ATTRIBUTE_UNUSED, int *win)
{
/* We must recognize output that we have already generated ourselves. */
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 1)) == CONST_INT)
{
push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
- opnum, (enum reload_type)type);
+ BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
+ opnum, (enum reload_type)type);
*win = 1;
return x;
}
/* Result of previous invocation of this function on Darwin
floating point constant. */
push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
- opnum, (enum reload_type)type);
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
+ opnum, (enum reload_type)type);
*win = 1;
return x;
}
#endif
+
+ /* Force ld/std non-word aligned offset into base register by wrapping
+ in offset 0. */
+ if (GET_CODE (x) == PLUS
+ && GET_CODE (XEXP (x, 0)) == REG
+ && REGNO (XEXP (x, 0)) < 32
+ && REG_MODE_OK_FOR_BASE_P (XEXP (x, 0), mode)
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && (INTVAL (XEXP (x, 1)) & 3) != 0
+ && !ALTIVEC_VECTOR_MODE (mode)
+ && GET_MODE_SIZE (mode) >= UNITS_PER_WORD
+ && TARGET_POWERPC64)
+ {
+ x = gen_rtx_PLUS (GET_MODE (x), x, GEN_INT (0));
+ push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
+ BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
+ opnum, (enum reload_type) type);
+ *win = 1;
+ return x;
+ }
+
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == REG
&& REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER
&& REG_MODE_OK_FOR_BASE_P (XEXP (x, 0), mode)
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& !SPE_VECTOR_MODE (mode)
+ && !(TARGET_E500_DOUBLE && (mode == DFmode
+ || mode == DImode))
&& !ALTIVEC_VECTOR_MODE (mode))
{
HOST_WIDE_INT val = INTVAL (XEXP (x, 1));
HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000;
HOST_WIDE_INT high
- = (((val - low) & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ = (((val - low) & 0xffffffff) ^ 0x80000000) - 0x80000000;
/* Check for 32-bit overflow. */
if (high + low != val)
- {
+ {
*win = 0;
return x;
}
/* Reload the high part into a base reg; leave the low part
- in the mem directly. */
+ in the mem directly. */
x = gen_rtx_PLUS (GET_MODE (x),
- gen_rtx_PLUS (GET_MODE (x), XEXP (x, 0),
- GEN_INT (high)),
- GEN_INT (low));
+ gen_rtx_PLUS (GET_MODE (x), XEXP (x, 0),
+ GEN_INT (high)),
+ GEN_INT (low));
push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
- opnum, (enum reload_type)type);
+ BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
+ opnum, (enum reload_type)type);
*win = 1;
return x;
}
+
#if TARGET_MACHO
if (GET_CODE (x) == SYMBOL_REF
&& DEFAULT_ABI == ABI_DARWIN
&& !ALTIVEC_VECTOR_MODE (mode)
&& (flag_pic || MACHO_DYNAMIC_NO_PIC_P)
- /* Don't do this for TFmode, since the result isn't offsettable. */
- && mode != TFmode)
+ /* Don't do this for TFmode, since the result isn't offsettable.
+ The same goes for DImode without 64-bit gprs. */
+ && mode != TFmode
+ && (mode != DImode || TARGET_POWERPC64))
{
if (flag_pic)
{
}
else
x = gen_rtx_LO_SUM (GET_MODE (x),
- gen_rtx_HIGH (Pmode, x), x);
+ gen_rtx_HIGH (Pmode, x), x);
push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
return x;
}
#endif
+
if (TARGET_TOC
&& constant_pool_expr_p (x)
&& ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), mode))
int
rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
{
+ /* If this is an unaligned stvx/ldvx type address, discard the outer AND. */
+ if (TARGET_ALTIVEC
+ && ALTIVEC_VECTOR_MODE (mode)
+ && GET_CODE (x) == AND
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) == -16)
+ x = XEXP (x, 0);
+
if (RS6000_SYMBOL_REF_TLS_P (x))
return 0;
if (legitimate_indirect_address_p (x, reg_ok_strict))
if ((GET_CODE (x) == PRE_INC || GET_CODE (x) == PRE_DEC)
&& !ALTIVEC_VECTOR_MODE (mode)
&& !SPE_VECTOR_MODE (mode)
+ /* Restrict addressing for DI because of our SUBREG hackery. */
+ && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode))
&& TARGET_UPDATE
&& legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict))
return 1;
&& GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == REG
&& (XEXP (x, 0) == virtual_stack_vars_rtx
- || XEXP (x, 0) == arg_pointer_rtx)
+ || XEXP (x, 0) == arg_pointer_rtx)
&& GET_CODE (XEXP (x, 1)) == CONST_INT)
return 1;
if (rs6000_legitimate_offset_address_p (mode, x, reg_ok_strict))
&& mode != TFmode
&& ((TARGET_HARD_FLOAT && TARGET_FPRS)
|| TARGET_POWERPC64
- || (mode != DFmode && mode != TFmode))
+ || ((mode != DFmode || TARGET_E500_DOUBLE) && mode != TFmode))
&& (TARGET_POWERPC64 || mode != DImode)
&& legitimate_indexed_address_p (x, reg_ok_strict))
return 1;
if (FP_REGNO_P (regno))
return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD;
+ if (TARGET_E500_DOUBLE && mode == DFmode)
+ return 1;
+
if (SPE_SIMD_REGNO_P (regno) && TARGET_SPE && SPE_VECTOR_MODE (mode))
return (GET_MODE_SIZE (mode) + UNITS_PER_SPE_WORD - 1) / UNITS_PER_SPE_WORD;
if (TARGET_SOFT_FLOAT || !TARGET_FPRS)
for (i = 32; i < 64; i++)
fixed_regs[i] = call_used_regs[i]
- = call_really_used_regs[i] = 1;
+ = call_really_used_regs[i] = 1;
if (DEFAULT_ABI == ABI_V4
&& PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
{
global_regs[SPEFSCR_REGNO] = 1;
fixed_regs[FIXED_SCRATCH]
- = call_used_regs[FIXED_SCRATCH]
+ = call_used_regs[FIXED_SCRATCH]
= call_really_used_regs[FIXED_SCRATCH] = 1;
}
if (mode == QImode || mode == HImode)
{
if (dest == NULL)
- dest = gen_reg_rtx (mode);
+ dest = gen_reg_rtx (mode);
emit_insn (gen_rtx_SET (VOIDmode, dest, source));
return dest;
}
{
if (GET_CODE (operands[0]) == MEM
&& GET_CODE (XEXP (operands[0], 0)) != REG
+ && ! legitimate_constant_pool_address_p (XEXP (operands[0], 0))
&& ! reload_in_progress)
operands[0]
= replace_equiv_address (operands[0],
if (GET_CODE (operands[1]) == MEM
&& GET_CODE (XEXP (operands[1], 0)) != REG
+ && ! legitimate_constant_pool_address_p (XEXP (operands[1], 0))
&& ! reload_in_progress)
operands[1]
= replace_equiv_address (operands[1],
return;
}
- if (!no_new_pseudos)
- {
- if (GET_CODE (operands[1]) == MEM && optimize > 0
- && (mode == QImode || mode == HImode || mode == SImode)
- && GET_MODE_SIZE (mode) < GET_MODE_SIZE (word_mode))
- {
- rtx reg = gen_reg_rtx (word_mode);
-
- emit_insn (gen_rtx_SET (word_mode, reg,
- gen_rtx_ZERO_EXTEND (word_mode,
- operands[1])));
- operands[1] = gen_lowpart (mode, reg);
- }
- if (GET_CODE (operands[0]) != REG)
- operands[1] = force_reg (mode, operands[1]);
- }
+ if (!no_new_pseudos && GET_CODE (operands[0]) == MEM
+ && !gpc_reg_operand (operands[1], mode))
+ operands[1] = force_reg (mode, operands[1]);
if (mode == SFmode && ! TARGET_POWERPC
&& TARGET_HARD_FLOAT && TARGET_FPRS
operands[1], mode, operands[0]);
if (operands[0] != operands[1])
emit_insn (gen_rtx_SET (VOIDmode,
- operands[0], operands[1]));
+ operands[0], operands[1]));
return;
}
#endif
- if (mode == DImode)
- {
- emit_insn (gen_macho_high_di (target, operands[1]));
- emit_insn (gen_macho_low_di (operands[0], target, operands[1]));
- }
- else
- {
- emit_insn (gen_macho_high (target, operands[1]));
- emit_insn (gen_macho_low (operands[0], target, operands[1]));
- }
+ emit_insn (gen_macho_high (target, operands[1]));
+ emit_insn (gen_macho_low (operands[0], target, operands[1]));
return;
}
{
operands[1]
= gen_const_mem (mode,
- create_TOC_reference (XEXP (operands[1], 0)));
+ create_TOC_reference (XEXP (operands[1], 0)));
set_mem_alias_set (operands[1], get_TOC_alias_set ());
}
}
static bool
rs6000_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
+ /* In the darwin64 abi, try to use registers for larger structs
+ if possible. */
+ if (rs6000_darwin64_abi
+ && TREE_CODE (type) == RECORD_TYPE
+ && int_size_in_bytes (type) > 0)
+ {
+ CUMULATIVE_ARGS valcum;
+ rtx valret;
+
+ valcum.words = 0;
+ valcum.fregno = FP_ARG_MIN_REG;
+ valcum.vregno = ALTIVEC_ARG_MIN_REG;
+ /* Do a trial code generation as if this were going to be passed
+ as an argument; if any part goes in memory, we return NULL. */
+ valret = rs6000_darwin64_record_arg (&valcum, type, 1, true);
+ if (valret)
+ return false;
+ /* Otherwise fall through to more conventional ABI rules. */
+ }
+
if (AGGREGATE_TYPE_P (type)
&& (TARGET_AIX_STRUCT_RET
|| (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8))
return true;
+
+ /* Allow -maltivec -mabi=no-altivec without warning. Altivec vector
+ modes only exist for GCC vector types if -maltivec. */
+ if (TARGET_32BIT && !TARGET_ALTIVEC_ABI
+ && ALTIVEC_VECTOR_MODE (TYPE_MODE (type)))
+ return false;
+
+ /* Return synthetic vectors in memory. */
+ if (TREE_CODE (type) == VECTOR_TYPE
+ && int_size_in_bytes (type) > (TARGET_ALTIVEC_ABI ? 16 : 8))
+ {
+ static bool warned_for_return_big_vectors = false;
+ if (!warned_for_return_big_vectors)
+ {
+ warning ("GCC vector returned by reference: "
+ "non-standard ABI extension with no compatibility guarantee");
+ warned_for_return_big_vectors = true;
+ }
+ return true;
+ }
+
if (DEFAULT_ABI == ABI_V4 && TYPE_MODE (type) == TFmode)
return true;
+
return false;
}
cum->nargs_prototype = n_named_args;
/* Check for a longcall attribute. */
- if (fntype
- && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))
- && !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype)))
- cum->call_cookie = CALL_LONG;
+ if ((!fntype && rs6000_default_long_calls)
+ || (fntype
+ && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))
+ && !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype))))
+ cum->call_cookie |= CALL_LONG;
if (TARGET_DEBUG_ARG)
{
cum->prototype, cum->nargs_prototype);
}
- if (fntype
- && !TARGET_ALTIVEC
- && TARGET_ALTIVEC_ABI
- && ALTIVEC_VECTOR_MODE (TYPE_MODE (TREE_TYPE (fntype))))
- {
- error ("Cannot return value in vector register because"
- " altivec instructions are disabled, use -maltivec"
- " to enable them.");
- }
+ if (fntype
+ && !TARGET_ALTIVEC
+ && TARGET_ALTIVEC_ABI
+ && ALTIVEC_VECTOR_MODE (TYPE_MODE (TREE_TYPE (fntype))))
+ {
+ error ("Cannot return value in vector register because"
+ " altivec instructions are disabled, use -maltivec"
+ " to enable them.");
+ }
}
\f
/* Return true if TYPE must be passed on the stack and not in registers. */
{
/* GCC used to pass structures of the same size as integer types as
if they were in fact integers, ignoring FUNCTION_ARG_PADDING.
- ie. Structures of size 1 or 2 (or 4 when TARGET_64BIT) were
+ i.e. Structures of size 1 or 2 (or 4 when TARGET_64BIT) were
passed padded downward, except that -mstrict-align further
muddied the water in that multi-component structures of 2 and 4
bytes in size were passed padded upward.
of an argument with the specified mode and type. If it is not defined,
PARM_BOUNDARY is used for all arguments.
- V.4 wants long longs to be double word aligned. */
+ V.4 wants long longs to be double word aligned.
+ Doubleword align SPE vectors.
+ Quadword align Altivec vectors.
+ Quadword align large synthetic vector types. */
int
-function_arg_boundary (enum machine_mode mode, tree type ATTRIBUTE_UNUSED)
+function_arg_boundary (enum machine_mode mode, tree type)
{
if (DEFAULT_ABI == ABI_V4 && GET_MODE_SIZE (mode) == 8)
return 64;
- else if (SPE_VECTOR_MODE (mode))
+ else if (SPE_VECTOR_MODE (mode)
+ || (type && TREE_CODE (type) == VECTOR_TYPE
+ && int_size_in_bytes (type) >= 8
+ && int_size_in_bytes (type) < 16))
return 64;
- else if (ALTIVEC_VECTOR_MODE (mode))
+ else if (ALTIVEC_VECTOR_MODE (mode)
+ || (type && TREE_CODE (type) == VECTOR_TYPE
+ && int_size_in_bytes (type) >= 16))
+ return 128;
+ else if (rs6000_darwin64_abi && mode == BLKmode
+ && type && TYPE_ALIGN (type) > 64)
return 128;
else
return PARM_BOUNDARY;
}
+/* For a function parm of MODE and TYPE, return the starting word in
+ the parameter area. NWORDS of the parameter area are already used. */
+
+static unsigned int
+rs6000_parm_start (enum machine_mode mode, tree type, unsigned int nwords)
+{
+ unsigned int align;
+ unsigned int parm_offset;
+
+ align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
+ parm_offset = DEFAULT_ABI == ABI_V4 ? 2 : 6;
+ return nwords + (-(parm_offset + nwords) & align);
+}
+
/* Compute the size (in words) of a function argument. */
static unsigned long
return (size + 7) >> 3;
}
\f
+/* Use this to flush pending int fields. */
+
+static void
+rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *cum,
+ HOST_WIDE_INT bitpos)
+{
+ unsigned int startbit, endbit;
+ int intregs, intoffset;
+ enum machine_mode mode;
+
+ if (cum->intoffset == -1)
+ return;
+
+ intoffset = cum->intoffset;
+ cum->intoffset = -1;
+
+ if (intoffset % BITS_PER_WORD != 0)
+ {
+ mode = mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD,
+ MODE_INT, 0);
+ if (mode == BLKmode)
+ {
+ /* We couldn't find an appropriate mode, which happens,
+ e.g., in packed structs when there are 3 bytes to load.
+ Back intoffset back to the beginning of the word in this
+ case. */
+ intoffset = intoffset & -BITS_PER_WORD;
+ }
+ }
+
+ startbit = intoffset & -BITS_PER_WORD;
+ endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
+ intregs = (endbit - startbit) / BITS_PER_WORD;
+ cum->words += intregs;
+}
+
+/* The darwin64 ABI calls for us to recurse down through structs,
+ looking for elements passed in registers. Unfortunately, we have
+ to track int register count here also because of misalignments
+ in powerpc alignment mode. */
+
+static void
+rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *cum,
+ tree type,
+ HOST_WIDE_INT startbitpos)
+{
+ tree f;
+
+ for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
+ if (TREE_CODE (f) == FIELD_DECL)
+ {
+ HOST_WIDE_INT bitpos = startbitpos;
+ tree ftype = TREE_TYPE (f);
+ enum machine_mode mode = TYPE_MODE (ftype);
+
+ if (DECL_SIZE (f) != 0
+ && host_integerp (bit_position (f), 1))
+ bitpos += int_bit_position (f);
+
+ /* ??? FIXME: else assume zero offset. */
+
+ if (TREE_CODE (ftype) == RECORD_TYPE)
+ rs6000_darwin64_record_arg_advance_recurse (cum, ftype, bitpos);
+ else if (USE_FP_FOR_ARG_P (cum, mode, ftype))
+ {
+ rs6000_darwin64_record_arg_advance_flush (cum, bitpos);
+ cum->fregno += (GET_MODE_SIZE (mode) + 7) >> 3;
+ cum->words += (GET_MODE_SIZE (mode) + 7) >> 3;
+ }
+ else if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, 1))
+ {
+ rs6000_darwin64_record_arg_advance_flush (cum, bitpos);
+ cum->vregno++;
+ cum->words += 2;
+ }
+ else if (cum->intoffset == -1)
+ cum->intoffset = bitpos;
+ }
+}
+
/* Update the data in CUM to advance over an argument
of mode MODE and data type TYPE.
(TYPE is null for libcalls where that information may not be available.)
void
function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named)
+ tree type, int named, int depth)
{
- cum->nargs_prototype--;
+ int size;
- if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
+ /* Only tick off an argument if we're not recursing. */
+ if (depth == 0)
+ cum->nargs_prototype--;
+
+ if (TARGET_ALTIVEC_ABI
+ && (ALTIVEC_VECTOR_MODE (mode)
+ || (type && TREE_CODE (type) == VECTOR_TYPE
+ && int_size_in_bytes (type) == 16)))
{
bool stack = false;
if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named))
- {
+ {
cum->vregno++;
if (!TARGET_ALTIVEC)
error ("Cannot pass argument in vector register because"
stack = true;
if (stack)
- {
+ {
int align;
/* Vector parameters must be 16-byte aligned. This places
&& !cum->stdarg
&& cum->sysv_gregno <= GP_ARG_MAX_REG)
cum->sysv_gregno++;
+
+ else if (rs6000_darwin64_abi
+ && mode == BLKmode
+ && TREE_CODE (type) == RECORD_TYPE
+ && (size = int_size_in_bytes (type)) > 0)
+ {
+ /* Variable sized types have size == -1 and are
+ treated as if consisting entirely of ints.
+ Pad to 16 byte boundary if needed. */
+ if (TYPE_ALIGN (type) >= 2 * BITS_PER_WORD
+ && (cum->words % 2) != 0)
+ cum->words++;
+ /* For varargs, we can just go up by the size of the struct. */
+ if (!named)
+ cum->words += (size + 7) / 8;
+ else
+ {
+ /* It is tempting to say int register count just goes up by
+ sizeof(type)/8, but this is wrong in a case such as
+ { int; double; int; } [powerpc alignment]. We have to
+ grovel through the fields for these too. */
+ cum->intoffset = 0;
+ rs6000_darwin64_record_arg_advance_recurse (cum, type, 0);
+ rs6000_darwin64_record_arg_advance_flush (cum,
+ size * BITS_PER_UNIT);
+ }
+ }
else if (DEFAULT_ABI == ABI_V4)
{
if (TARGET_HARD_FLOAT && TARGET_FPRS
else
{
if (mode == DFmode)
- cum->words += cum->words & 1;
+ cum->words += cum->words & 1;
cum->words += rs6000_arg_size (mode, type);
}
}
else
{
int n_words = rs6000_arg_size (mode, type);
- int align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
+ int start_words = cum->words;
+ int align_words = rs6000_parm_start (mode, type, start_words);
- /* The simple alignment calculation here works because
- function_arg_boundary / PARM_BOUNDARY will only be 1 or 2.
- If we ever want to handle alignments larger than 8 bytes for
- 32-bit or 16 bytes for 64-bit, then we'll need to take into
- account the offset to the start of the parm save area. */
- align &= cum->words;
- cum->words += align + n_words;
+ cum->words = align_words + n_words;
if (GET_MODE_CLASS (mode) == MODE_FLOAT
&& TARGET_HARD_FLOAT && TARGET_FPRS)
cum->words, cum->fregno);
fprintf (stderr, "nargs = %4d, proto = %d, mode = %4s, ",
cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode));
- fprintf (stderr, "named = %d, align = %d\n", named, align);
+ fprintf (stderr, "named = %d, align = %d, depth = %d\n",
+ named, align_words - start_words, depth);
}
}
}
-/* Determine where to put a SIMD argument on the SPE. */
+static rtx
+spe_build_register_parallel (enum machine_mode mode, int gregno)
+{
+ rtx r1, r3;
+
+ if (mode == DFmode)
+ {
+ r1 = gen_rtx_REG (DImode, gregno);
+ r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx);
+ return gen_rtx_PARALLEL (mode, gen_rtvec (1, r1));
+ }
+ else if (mode == DCmode)
+ {
+ r1 = gen_rtx_REG (DImode, gregno);
+ r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx);
+ r3 = gen_rtx_REG (DImode, gregno + 2);
+ r3 = gen_rtx_EXPR_LIST (VOIDmode, r3, GEN_INT (8));
+ return gen_rtx_PARALLEL (mode, gen_rtvec (2, r1, r3));
+ }
+ abort ();
+ return NULL_RTX;
+}
+/* Determine where to put a SIMD argument on the SPE. */
static rtx
rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type)
{
+ int gregno = cum->sysv_gregno;
+
+ /* On E500 v2, double arithmetic is done on the full 64-bit GPR, but
+ are passed and returned in a pair of GPRs for ABI compatibility. */
+ if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DCmode))
+ {
+ int n_words = rs6000_arg_size (mode, type);
+
+ /* Doubles go in an odd/even register pair (r5/r6, etc). */
+ if (mode == DFmode)
+ gregno += (1 - gregno) & 1;
+
+ /* Multi-reg args are not split between registers and stack. */
+ if (gregno + n_words - 1 > GP_ARG_MAX_REG)
+ return NULL_RTX;
+
+ return spe_build_register_parallel (mode, gregno);
+ }
if (cum->stdarg)
{
- int gregno = cum->sysv_gregno;
int n_words = rs6000_arg_size (mode, type);
/* SPE vectors are put in odd registers. */
}
else
{
- if (cum->sysv_gregno <= GP_ARG_MAX_REG)
- return gen_rtx_REG (mode, cum->sysv_gregno);
+ if (gregno <= GP_ARG_MAX_REG)
+ return gen_rtx_REG (mode, gregno);
else
return NULL_RTX;
}
}
-/* Determine where to place an argument in 64-bit mode with 32-bit ABI. */
+/* A subroutine of rs6000_darwin64_record_arg. Assign the bits of the
+ structure between cum->intoffset and bitpos to integer registers. */
-static rtx
-rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words)
+static void
+rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *cum,
+ HOST_WIDE_INT bitpos, rtx rvec[], int *k)
{
- int n_units;
- int i, k;
- rtx rvec[GP_ARG_NUM_REG + 1];
+ enum machine_mode mode;
+ unsigned int regno;
+ unsigned int startbit, endbit;
+ int this_regno, intregs, intoffset;
+ rtx reg;
- if (align_words >= GP_ARG_NUM_REG)
- return NULL_RTX;
+ if (cum->intoffset == -1)
+ return;
+
+ intoffset = cum->intoffset;
+ cum->intoffset = -1;
+
+ /* If this is the trailing part of a word, try to only load that
+ much into the register. Otherwise load the whole register. Note
+ that in the latter case we may pick up unwanted bits. It's not a
+ problem at the moment but may wish to revisit. */
+
+ if (intoffset % BITS_PER_WORD != 0)
+ {
+ mode = mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD,
+ MODE_INT, 0);
+ if (mode == BLKmode)
+ {
+ /* We couldn't find an appropriate mode, which happens,
+ e.g., in packed structs when there are 3 bytes to load.
+ Back intoffset back to the beginning of the word in this
+ case. */
+ intoffset = intoffset & -BITS_PER_WORD;
+ mode = word_mode;
+ }
+ }
+ else
+ mode = word_mode;
+
+ startbit = intoffset & -BITS_PER_WORD;
+ endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
+ intregs = (endbit - startbit) / BITS_PER_WORD;
+ this_regno = cum->words + intoffset / BITS_PER_WORD;
+
+ if (intregs > 0 && intregs > GP_ARG_NUM_REG - this_regno)
+ cum->use_stack = 1;
+
+ intregs = MIN (intregs, GP_ARG_NUM_REG - this_regno);
+ if (intregs <= 0)
+ return;
+
+ intoffset /= BITS_PER_UNIT;
+ do
+ {
+ regno = GP_ARG_MIN_REG + this_regno;
+ reg = gen_rtx_REG (mode, regno);
+ rvec[(*k)++] =
+ gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset));
+
+ this_regno += 1;
+ intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1;
+ mode = word_mode;
+ intregs -= 1;
+ }
+ while (intregs > 0);
+}
+
+/* Recursive workhorse for the following. */
+
+static void
+rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type,
+ HOST_WIDE_INT startbitpos, rtx rvec[],
+ int *k)
+{
+ tree f;
+
+ for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
+ if (TREE_CODE (f) == FIELD_DECL)
+ {
+ HOST_WIDE_INT bitpos = startbitpos;
+ tree ftype = TREE_TYPE (f);
+ enum machine_mode mode = TYPE_MODE (ftype);
+
+ if (DECL_SIZE (f) != 0
+ && host_integerp (bit_position (f), 1))
+ bitpos += int_bit_position (f);
+
+ /* ??? FIXME: else assume zero offset. */
+
+ if (TREE_CODE (ftype) == RECORD_TYPE)
+ rs6000_darwin64_record_arg_recurse (cum, ftype, bitpos, rvec, k);
+ else if (cum->named && USE_FP_FOR_ARG_P (cum, mode, ftype))
+ {
+#if 0
+ switch (mode)
+ {
+ case SCmode: mode = SFmode; break;
+ case DCmode: mode = DFmode; break;
+ case TCmode: mode = TFmode; break;
+ default: break;
+ }
+#endif
+ rs6000_darwin64_record_arg_flush (cum, bitpos, rvec, k);
+ rvec[(*k)++]
+ = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode, cum->fregno++),
+ GEN_INT (bitpos / BITS_PER_UNIT));
+ if (mode == TFmode)
+ cum->fregno++;
+ }
+ else if (cum->named && USE_ALTIVEC_FOR_ARG_P (cum, mode, ftype, 1))
+ {
+ rs6000_darwin64_record_arg_flush (cum, bitpos, rvec, k);
+ rvec[(*k)++]
+ = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode, cum->vregno++),
+ GEN_INT (bitpos / BITS_PER_UNIT));
+ }
+ else if (cum->intoffset == -1)
+ cum->intoffset = bitpos;
+ }
+}
+
+/* For the darwin64 ABI, we want to construct a PARALLEL consisting of
+ the register(s) to be used for each field and subfield of a struct
+ being passed by value, along with the offset of where the
+ register's value may be found in the block. FP fields go in FP
+ register, vector fields go in vector registers, and everything
+ else goes in int registers, packed as in memory.
+
+ This code is also used for function return values. RETVAL indicates
+ whether this is the case.
+
+ Much of this is taken from the Sparc V9 port, which has a similar
+ calling convention. */
+
+static rtx
+rs6000_darwin64_record_arg (CUMULATIVE_ARGS *orig_cum, tree type,
+ int named, bool retval)
+{
+ rtx rvec[FIRST_PSEUDO_REGISTER];
+ int k = 1, kbase = 1;
+ HOST_WIDE_INT typesize = int_size_in_bytes (type);
+ /* This is a copy; modifications are not visible to our caller. */
+ CUMULATIVE_ARGS copy_cum = *orig_cum;
+ CUMULATIVE_ARGS *cum = ©_cum;
+
+ /* Pad to 16 byte boundary if needed. */
+ if (!retval && TYPE_ALIGN (type) >= 2 * BITS_PER_WORD
+ && (cum->words % 2) != 0)
+ cum->words++;
+
+ cum->intoffset = 0;
+ cum->use_stack = 0;
+ cum->named = named;
+
+ /* Put entries into rvec[] for individual FP and vector fields, and
+ for the chunks of memory that go in int regs. Note we start at
+ element 1; 0 is reserved for an indication of using memory, and
+ may or may not be filled in below. */
+ rs6000_darwin64_record_arg_recurse (cum, type, 0, rvec, &k);
+ rs6000_darwin64_record_arg_flush (cum, typesize * BITS_PER_UNIT, rvec, &k);
+
+ /* If any part of the struct went on the stack put all of it there.
+ This hack is because the generic code for
+ FUNCTION_ARG_PARTIAL_NREGS cannot handle cases where the register
+ parts of the struct are not at the beginning. */
+ if (cum->use_stack)
+ {
+ if (retval)
+ return NULL_RTX; /* doesn't go in registers at all */
+ kbase = 0;
+ rvec[0] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
+ }
+ if (k > 1 || cum->use_stack)
+ return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (k - kbase, &rvec[kbase]));
+ else
+ return NULL_RTX;
+}
+
+/* Determine where to place an argument in 64-bit mode with 32-bit ABI. */
+
+static rtx
+rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words)
+{
+ int n_units;
+ int i, k;
+ rtx rvec[GP_ARG_NUM_REG + 1];
+
+ if (align_words >= GP_ARG_NUM_REG)
+ return NULL_RTX;
n_units = rs6000_arg_size (mode, type);
In any case, the code to store the whole arg to memory is often
more efficient than code to store pieces, and we know that space
is available in the right place for the whole arg. */
+ /* FIXME: This should be fixed since the conversion to
+ TARGET_ARG_PARTIAL_BYTES. */
rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
i = 0;
This is null for libcalls where that information may
not be available.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
- the preceding args and about the function being called.
+ the preceding args and about the function being called. It is
+ not modified in this routine.
NAMED is nonzero if this argument is a named parameter
(otherwise it is an extra parameter matching an ellipsis).
with MODE and TYPE set to that of the pointer to the arg, not the arg
itself. */
-struct rtx_def *
+rtx
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named)
{
return GEN_INT (cum->call_cookie);
}
+ if (rs6000_darwin64_abi && mode == BLKmode
+ && TREE_CODE (type) == RECORD_TYPE)
+ {
+ rtx rslt = rs6000_darwin64_record_arg (cum, type, named, false);
+ if (rslt != NULL_RTX)
+ return rslt;
+ /* Else fall through to usual handling. */
+ }
+
if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named))
if (TARGET_64BIT && ! cum->prototype)
{
- /* Vector parameters get passed in vector register
- and also in GPRs or memory, in absence of prototype. */
- int align_words;
- rtx slot;
- align_words = (cum->words + 1) & ~1;
-
- if (align_words >= GP_ARG_NUM_REG)
- {
- slot = NULL_RTX;
- }
- else
- {
- slot = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
- }
- return gen_rtx_PARALLEL (mode,
- gen_rtvec (2,
- gen_rtx_EXPR_LIST (VOIDmode,
- slot, const0_rtx),
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (mode, cum->vregno),
- const0_rtx)));
+ /* Vector parameters get passed in vector register
+ and also in GPRs or memory, in absence of prototype. */
+ int align_words;
+ rtx slot;
+ align_words = (cum->words + 1) & ~1;
+
+ if (align_words >= GP_ARG_NUM_REG)
+ {
+ slot = NULL_RTX;
+ }
+ else
+ {
+ slot = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
+ }
+ return gen_rtx_PARALLEL (mode,
+ gen_rtvec (2,
+ gen_rtx_EXPR_LIST (VOIDmode,
+ slot, const0_rtx),
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode, cum->vregno),
+ const0_rtx)));
}
else
return gen_rtx_REG (mode, cum->vregno);
- else if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
+ else if (TARGET_ALTIVEC_ABI
+ && (ALTIVEC_VECTOR_MODE (mode)
+ || (type && TREE_CODE (type) == VECTOR_TYPE
+ && int_size_in_bytes (type) == 16)))
{
if (named || abi == ABI_V4)
return NULL_RTX;
return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words);
}
}
- else if (TARGET_SPE_ABI && TARGET_SPE && SPE_VECTOR_MODE (mode))
+ else if (TARGET_SPE_ABI && TARGET_SPE
+ && (SPE_VECTOR_MODE (mode)
+ || (TARGET_E500_DOUBLE && (mode == DFmode
+ || mode == DCmode))))
return rs6000_spe_function_arg (cum, mode, type);
+
else if (abi == ABI_V4)
{
if (TARGET_HARD_FLOAT && TARGET_FPRS
}
else
{
- int align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
- int align_words = cum->words + (cum->words & align);
+ int align_words = rs6000_parm_start (mode, type, cum->words);
if (USE_FP_FOR_ARG_P (cum, mode, type))
{
needs_psave = (type
&& (cum->nargs_prototype <= 0
|| (DEFAULT_ABI == ABI_AIX
- && TARGET_XL_CALL
+ && TARGET_XL_COMPAT
&& align_words >= GP_ARG_NUM_REG)));
if (!needs_psave && mode == fmode)
include the portion actually in registers here. */
enum machine_mode rmode = TARGET_32BIT ? SImode : DImode;
rtx off;
+ int i=0;
+ if (align_words + n_words > GP_ARG_NUM_REG
+ && (TARGET_32BIT && TARGET_POWERPC64))
+ /* Not all of the arg fits in gprs. Say that it
+ goes in memory too, using a magic NULL_RTX
+ component. Also see comment in
+ rs6000_mixed_function_arg for why the normal
+ function_arg_partial_nregs scheme doesn't work
+ in this case. */
+ rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX,
+ const0_rtx);
do
{
r = gen_rtx_REG (rmode,
GP_ARG_MIN_REG + align_words);
- off = GEN_INT (k * GET_MODE_SIZE (rmode));
+ off = GEN_INT (i++ * GET_MODE_SIZE (rmode));
rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off);
}
while (++align_words < GP_ARG_NUM_REG && --n_words != 0);
if (TARGET_32BIT && TARGET_POWERPC64)
return rs6000_mixed_function_arg (mode, type, align_words);
+ if (mode == BLKmode)
+ mode = Pmode;
+
return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
}
else
the number of registers used. For args passed entirely in registers
or entirely in memory, zero. When an arg is described by a PARALLEL,
perhaps using more than one register type, this function returns the
- number of registers used by the first element of the PARALLEL. */
+ number of bytes of registers used by the PARALLEL. */
-int
-function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named)
+static int
+rs6000_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, bool named)
{
int ret = 0;
- int align;
- int parm_offset;
int align_words;
if (DEFAULT_ABI == ABI_V4)
&& cum->nargs_prototype >= 0)
return 0;
- align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
- parm_offset = TARGET_32BIT ? 2 : 0;
- align_words = cum->words + ((parm_offset - cum->words) & align);
+ /* In this complicated case we just disable the partial_nregs code. */
+ if (rs6000_darwin64_abi && mode == BLKmode
+ && TREE_CODE (type) == RECORD_TYPE
+ && int_size_in_bytes (type) > 0)
+ return 0;
+
+ align_words = rs6000_parm_start (mode, type, cum->words);
if (USE_FP_FOR_ARG_P (cum, mode, type)
/* If we are passing this arg in gprs as well, then this function
&& !(type
&& (cum->nargs_prototype <= 0
|| (DEFAULT_ABI == ABI_AIX
- && TARGET_XL_CALL
+ && TARGET_XL_COMPAT
&& align_words >= GP_ARG_NUM_REG))))
{
if (cum->fregno + ((GET_MODE_SIZE (mode) + 7) >> 3) > FP_ARG_MAX_REG + 1)
&& GP_ARG_NUM_REG < align_words + rs6000_arg_size (mode, type))
ret = GP_ARG_NUM_REG - align_words;
+ ret *= (TARGET_32BIT ? 4 : 8);
+
if (ret != 0 && TARGET_DEBUG_ARG)
- fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);
+ fprintf (stderr, "rs6000_arg_partial_bytes: %d\n", ret);
return ret;
}
static bool
rs6000_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- tree type, bool named ATTRIBUTE_UNUSED)
+ enum machine_mode mode, tree type,
+ bool named ATTRIBUTE_UNUSED)
{
- if ((DEFAULT_ABI == ABI_V4
- && ((type && AGGREGATE_TYPE_P (type))
- || mode == TFmode))
- || (TARGET_32BIT && !TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
- || (type && int_size_in_bytes (type) < 0))
+ if (DEFAULT_ABI == ABI_V4 && mode == TFmode)
{
if (TARGET_DEBUG_ARG)
- fprintf (stderr, "function_arg_pass_by_reference\n");
+ fprintf (stderr, "function_arg_pass_by_reference: V4 long double\n");
+ return 1;
+ }
+ if (!type)
+ return 0;
+
+ if (DEFAULT_ABI == ABI_V4 && AGGREGATE_TYPE_P (type))
+ {
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr, "function_arg_pass_by_reference: V4 aggregate\n");
+ return 1;
+ }
+
+ if (int_size_in_bytes (type) < 0)
+ {
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr, "function_arg_pass_by_reference: variable size\n");
+ return 1;
+ }
+
+ /* Allow -maltivec -mabi=no-altivec without warning. Altivec vector
+ modes only exist for GCC vector types if -maltivec. */
+ if (TARGET_32BIT && !TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
+ {
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr, "function_arg_pass_by_reference: AltiVec\n");
+ return 1;
+ }
+
+ /* Pass synthetic vectors in memory. */
+ if (TREE_CODE (type) == VECTOR_TYPE
+ && int_size_in_bytes (type) > (TARGET_ALTIVEC_ABI ? 16 : 8))
+ {
+ static bool warned_for_pass_big_vectors = false;
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr, "function_arg_pass_by_reference: synthetic vector\n");
+ if (!warned_for_pass_big_vectors)
+ {
+ warning ("GCC vector passed by reference: "
+ "non-standard ABI extension with no compatibility guarantee");
+ warned_for_pass_big_vectors = true;
+ }
return 1;
}
+
return 0;
}
if (nregs == 0)
return;
- for (i = 0; i < nregs; i++)
+ for (i = 0; i < nregs; i++)
{
- rtx tem = adjust_address_nv (x, reg_mode, i*GET_MODE_SIZE(reg_mode));
+ rtx tem = adjust_address_nv (x, reg_mode, i * GET_MODE_SIZE (reg_mode));
if (reload_completed)
- {
- if (! strict_memory_address_p (reg_mode, XEXP (tem, 0)))
- tem = NULL_RTX;
- else
- tem = simplify_gen_subreg (reg_mode, x, BLKmode,
- i * GET_MODE_SIZE(reg_mode));
- }
+ {
+ if (! strict_memory_address_p (reg_mode, XEXP (tem, 0)))
+ tem = NULL_RTX;
+ else
+ tem = simplify_gen_subreg (reg_mode, x, BLKmode,
+ i * GET_MODE_SIZE (reg_mode));
+ }
else
tem = replace_equiv_address (tem, XEXP (tem, 0));
if (tem == NULL_RTX)
- abort ();
+ abort ();
emit_move_insn (tem, gen_rtx_REG (reg_mode, regno + i));
}
}
-
\f
/* Perform any needed actions needed for a function that is receiving a
variable number of arguments.
static void
setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int *pretend_size ATTRIBUTE_UNUSED, int no_rtl)
+ tree type, int *pretend_size ATTRIBUTE_UNUSED,
+ int no_rtl)
{
CUMULATIVE_ARGS next_cum;
int reg_size = TARGET_32BIT ? 4 : 8;
/* Skip the last named argument. */
next_cum = *cum;
- function_arg_advance (&next_cum, mode, type, 1);
+ function_arg_advance (&next_cum, mode, type, 1, 0);
if (DEFAULT_ABI == ABI_V4)
{
if (! no_rtl && first_reg_offset < GP_ARG_NUM_REG)
{
mem = gen_rtx_MEM (BLKmode,
- plus_constant (save_area,
+ plus_constant (save_area,
first_reg_offset * reg_size)),
set_mem_alias_set (mem, set);
set_mem_align (mem, BITS_PER_WORD);
rs6000_move_block_from_reg (GP_ARG_MIN_REG + first_reg_offset, mem,
- GP_ARG_NUM_REG - first_reg_offset);
+ GP_ARG_NUM_REG - first_reg_offset);
}
/* Save FP registers if needed. */
rtx lab = gen_label_rtx ();
int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) * 8);
- emit_jump_insn (gen_rtx_SET (VOIDmode,
- pc_rtx,
- gen_rtx_IF_THEN_ELSE (VOIDmode,
+ emit_jump_insn
+ (gen_rtx_SET (VOIDmode,
+ pc_rtx,
+ gen_rtx_IF_THEN_ELSE (VOIDmode,
gen_rtx_NE (VOIDmode, cr1,
- const0_rtx),
+ const0_rtx),
gen_rtx_LABEL_REF (VOIDmode, lab),
pc_rtx)));
while (fregno <= FP_ARG_V4_MAX_REG)
{
mem = gen_rtx_MEM (DFmode, plus_constant (save_area, off));
- set_mem_alias_set (mem, set);
+ set_mem_alias_set (mem, set);
+ set_mem_align (mem, GET_MODE_ALIGNMENT (DFmode));
emit_move_insn (mem, gen_rtx_REG (DFmode, fregno));
fregno++;
off += 8;
f_ovf = TREE_CHAIN (f_res);
f_sav = TREE_CHAIN (f_ovf);
- valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
+ valist = build_va_arg_indirect_ref (valist);
gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
/* Count number of gp and fp argument registers used. */
words = current_function_args_info.words;
- n_gpr = current_function_args_info.sysv_gregno - GP_ARG_MIN_REG;
- n_fpr = current_function_args_info.fregno - FP_ARG_MIN_REG;
+ n_gpr = MIN (current_function_args_info.sysv_gregno - GP_ARG_MIN_REG,
+ GP_ARG_NUM_REG);
+ n_fpr = MIN (current_function_args_info.fregno - FP_ARG_MIN_REG,
+ FP_ARG_NUM_REG);
if (TARGET_DEBUG_ARG)
fprintf (stderr, "va_start: words = "HOST_WIDE_INT_PRINT_DEC", n_gpr = "
if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
{
t = rs6000_gimplify_va_arg (valist, ptrtype, pre_p, post_p);
- return build_fold_indirect_ref (t);
+ return build_va_arg_indirect_ref (t);
}
if (DEFAULT_ABI != ABI_V4)
f_ovf = TREE_CHAIN (f_res);
f_sav = TREE_CHAIN (f_ovf);
- valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
+ valist = build_va_arg_indirect_ref (valist);
gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
}
addr = fold_convert (ptrtype, addr);
- return build_fold_indirect_ref (addr);
+ return build_va_arg_indirect_ref (addr);
}
/* Builtins. */
{ 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_altivec_vperm_4sf, "__builtin_altivec_vperm_4sf", ALTIVEC_BUILTIN_VPERM_4SF },
- { MASK_ALTIVEC, CODE_FOR_altivec_vperm_4si, "__builtin_altivec_vperm_4si", ALTIVEC_BUILTIN_VPERM_4SI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vperm_8hi, "__builtin_altivec_vperm_8hi", ALTIVEC_BUILTIN_VPERM_8HI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vperm_16qi, "__builtin_altivec_vperm_16qi", ALTIVEC_BUILTIN_VPERM_16QI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vsel_4sf, "__builtin_altivec_vsel_4sf", ALTIVEC_BUILTIN_VSEL_4SF },
- { MASK_ALTIVEC, CODE_FOR_altivec_vsel_4si, "__builtin_altivec_vsel_4si", ALTIVEC_BUILTIN_VSEL_4SI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vsel_8hi, "__builtin_altivec_vsel_8hi", ALTIVEC_BUILTIN_VSEL_8HI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vsel_16qi, "__builtin_altivec_vsel_16qi", ALTIVEC_BUILTIN_VSEL_16QI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_16qi, "__builtin_altivec_vsldoi_16qi", ALTIVEC_BUILTIN_VSLDOI_16QI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_8hi, "__builtin_altivec_vsldoi_8hi", ALTIVEC_BUILTIN_VSLDOI_8HI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_4si, "__builtin_altivec_vsldoi_4si", ALTIVEC_BUILTIN_VSLDOI_4SI },
- { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_4sf, "__builtin_altivec_vsldoi_4sf", ALTIVEC_BUILTIN_VSLDOI_4SF },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vperm_v4sf, "__builtin_altivec_vperm_4sf", ALTIVEC_BUILTIN_VPERM_4SF },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vperm_v4si, "__builtin_altivec_vperm_4si", ALTIVEC_BUILTIN_VPERM_4SI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vperm_v8hi, "__builtin_altivec_vperm_8hi", ALTIVEC_BUILTIN_VPERM_8HI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vperm_v16qi, "__builtin_altivec_vperm_16qi", ALTIVEC_BUILTIN_VPERM_16QI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vsel_v4sf, "__builtin_altivec_vsel_4sf", ALTIVEC_BUILTIN_VSEL_4SF },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vsel_v4si, "__builtin_altivec_vsel_4si", ALTIVEC_BUILTIN_VSEL_4SI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vsel_v8hi, "__builtin_altivec_vsel_8hi", ALTIVEC_BUILTIN_VSEL_8HI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vsel_v16qi, "__builtin_altivec_vsel_16qi", ALTIVEC_BUILTIN_VSEL_16QI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_v16qi, "__builtin_altivec_vsldoi_16qi", ALTIVEC_BUILTIN_VSLDOI_16QI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_v8hi, "__builtin_altivec_vsldoi_8hi", ALTIVEC_BUILTIN_VSLDOI_8HI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_v4si, "__builtin_altivec_vsldoi_4si", ALTIVEC_BUILTIN_VSLDOI_4SI },
+ { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_v4sf, "__builtin_altivec_vsldoi_4sf", ALTIVEC_BUILTIN_VSLDOI_4SF },
};
/* DST operations: void foo (void *, const int, const char). */
{ MASK_ALTIVEC, CODE_FOR_altivec_vadduws, "__builtin_altivec_vadduws", ALTIVEC_BUILTIN_VADDUWS },
{ MASK_ALTIVEC, CODE_FOR_altivec_vaddsws, "__builtin_altivec_vaddsws", ALTIVEC_BUILTIN_VADDSWS },
{ MASK_ALTIVEC, CODE_FOR_andv4si3, "__builtin_altivec_vand", ALTIVEC_BUILTIN_VAND },
- { MASK_ALTIVEC, CODE_FOR_altivec_vandc, "__builtin_altivec_vandc", ALTIVEC_BUILTIN_VANDC },
+ { MASK_ALTIVEC, CODE_FOR_andcv4si3, "__builtin_altivec_vandc", ALTIVEC_BUILTIN_VANDC },
{ MASK_ALTIVEC, CODE_FOR_altivec_vavgub, "__builtin_altivec_vavgub", ALTIVEC_BUILTIN_VAVGUB },
{ MASK_ALTIVEC, CODE_FOR_altivec_vavgsb, "__builtin_altivec_vavgsb", ALTIVEC_BUILTIN_VAVGSB },
{ MASK_ALTIVEC, CODE_FOR_altivec_vavguh, "__builtin_altivec_vavguh", ALTIVEC_BUILTIN_VAVGUH },
{ MASK_ALTIVEC, CODE_FOR_altivec_vmulosb, "__builtin_altivec_vmulosb", ALTIVEC_BUILTIN_VMULOSB },
{ MASK_ALTIVEC, CODE_FOR_altivec_vmulouh, "__builtin_altivec_vmulouh", ALTIVEC_BUILTIN_VMULOUH },
{ MASK_ALTIVEC, CODE_FOR_altivec_vmulosh, "__builtin_altivec_vmulosh", ALTIVEC_BUILTIN_VMULOSH },
- { MASK_ALTIVEC, CODE_FOR_altivec_vnor, "__builtin_altivec_vnor", ALTIVEC_BUILTIN_VNOR },
+ { MASK_ALTIVEC, CODE_FOR_altivec_norv4si3, "__builtin_altivec_vnor", ALTIVEC_BUILTIN_VNOR },
{ MASK_ALTIVEC, CODE_FOR_iorv4si3, "__builtin_altivec_vor", ALTIVEC_BUILTIN_VOR },
{ MASK_ALTIVEC, CODE_FOR_altivec_vpkuhum, "__builtin_altivec_vpkuhum", ALTIVEC_BUILTIN_VPKUHUM },
{ MASK_ALTIVEC, CODE_FOR_altivec_vpkuwum, "__builtin_altivec_vpkuwum", ALTIVEC_BUILTIN_VPKUWUM },
If you think this is disgusting, look at the specs for the
AltiVec predicates. */
- switch (cr6_form_int)
- {
- case 0:
- emit_insn (gen_cr6_test_for_zero (target));
- break;
- case 1:
- emit_insn (gen_cr6_test_for_zero_reverse (target));
- break;
- case 2:
- emit_insn (gen_cr6_test_for_lt (target));
- break;
- case 3:
- emit_insn (gen_cr6_test_for_lt_reverse (target));
- break;
- default:
- error ("argument 1 of __builtin_altivec_predicate is out of range");
- break;
- }
+ switch (cr6_form_int)
+ {
+ case 0:
+ emit_insn (gen_cr6_test_for_zero (target));
+ break;
+ case 1:
+ emit_insn (gen_cr6_test_for_zero_reverse (target));
+ break;
+ case 2:
+ emit_insn (gen_cr6_test_for_lt (target));
+ break;
+ case 3:
+ emit_insn (gen_cr6_test_for_lt_reverse (target));
+ break;
+ default:
+ error ("argument 1 of __builtin_altivec_predicate is out of range");
+ break;
+ }
return target;
}
|| arg2 == error_mark_node)
return const0_rtx;
- if (icode == CODE_FOR_altivec_vsldoi_4sf
- || icode == CODE_FOR_altivec_vsldoi_4si
- || icode == CODE_FOR_altivec_vsldoi_8hi
- || icode == CODE_FOR_altivec_vsldoi_16qi)
+ if (icode == CODE_FOR_altivec_vsldoi_v4sf
+ || icode == CODE_FOR_altivec_vsldoi_v4si
+ || icode == CODE_FOR_altivec_vsldoi_v8hi
+ || icode == CODE_FOR_altivec_vsldoi_v16qi)
{
/* Only allow 4-bit unsigned literals. */
STRIP_NOPS (arg2);
switch (fcode)
{
case ALTIVEC_BUILTIN_LD_INTERNAL_16qi:
- icode = CODE_FOR_altivec_lvx_16qi;
+ icode = CODE_FOR_altivec_lvx_v16qi;
break;
case ALTIVEC_BUILTIN_LD_INTERNAL_8hi:
- icode = CODE_FOR_altivec_lvx_8hi;
+ icode = CODE_FOR_altivec_lvx_v8hi;
break;
case ALTIVEC_BUILTIN_LD_INTERNAL_4si:
- icode = CODE_FOR_altivec_lvx_4si;
+ icode = CODE_FOR_altivec_lvx_v4si;
break;
case ALTIVEC_BUILTIN_LD_INTERNAL_4sf:
- icode = CODE_FOR_altivec_lvx_4sf;
+ icode = CODE_FOR_altivec_lvx_v4sf;
break;
default:
*expandedp = false;
switch (fcode)
{
case ALTIVEC_BUILTIN_ST_INTERNAL_16qi:
- icode = CODE_FOR_altivec_stvx_16qi;
+ icode = CODE_FOR_altivec_stvx_v16qi;
break;
case ALTIVEC_BUILTIN_ST_INTERNAL_8hi:
- icode = CODE_FOR_altivec_stvx_8hi;
+ icode = CODE_FOR_altivec_stvx_v8hi;
break;
case ALTIVEC_BUILTIN_ST_INTERNAL_4si:
- icode = CODE_FOR_altivec_stvx_4si;
+ icode = CODE_FOR_altivec_stvx_v4si;
break;
case ALTIVEC_BUILTIN_ST_INTERNAL_4sf:
- icode = CODE_FOR_altivec_stvx_4sf;
+ icode = CODE_FOR_altivec_stvx_v4sf;
break;
default:
*expandedp = false;
if (TREE_CODE (arg2) != INTEGER_CST
|| TREE_INT_CST_LOW (arg2) & ~0x3)
{
- error ("argument to `%s' must be a 2-bit unsigned literal", d->name);
+ error ("argument to %qs must be a 2-bit unsigned literal", d->name);
return const0_rtx;
}
if (! (*insn_data[d->icode].operand[0].predicate) (op0, mode0))
- op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0));
+ op0 = copy_to_mode_reg (Pmode, op0);
if (! (*insn_data[d->icode].operand[1].predicate) (op1, mode1))
op1 = copy_to_mode_reg (mode1, op1);
case ALTIVEC_BUILTIN_COMPILETIME_ERROR:
arg0 = TREE_VALUE (arglist);
while (TREE_CODE (arg0) == NOP_EXPR || TREE_CODE (arg0) == ADDR_EXPR
- || TREE_CODE (arg0) == ARRAY_REF)
+ || TREE_CODE (arg0) == ARRAY_REF)
arg0 = TREE_OPERAND (arg0, 0);
- error ("invalid parameter combination for `%s' AltiVec intrinsic",
+ error ("invalid parameter combination for %qs AltiVec intrinsic",
TREE_STRING_POINTER (arg0));
return const0_rtx;
dp = (struct builtin_description_predicates *) bdesc_altivec_preds;
for (i = 0; i < ARRAY_SIZE (bdesc_altivec_preds); i++, dp++)
if (dp->code == fcode)
- return altivec_expand_predicate_builtin (dp->icode, dp->opcode, arglist, target);
+ return altivec_expand_predicate_builtin (dp->icode, dp->opcode,
+ arglist, target);
/* LV* are funky. We initialized them differently. */
switch (fcode)
{
case ALTIVEC_BUILTIN_LVSL:
return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsl,
- arglist, target);
+ arglist, target);
case ALTIVEC_BUILTIN_LVSR:
return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsr,
- arglist, target);
+ arglist, target);
case ALTIVEC_BUILTIN_LVEBX:
return altivec_expand_lv_builtin (CODE_FOR_altivec_lvebx,
- arglist, target);
+ arglist, target);
case ALTIVEC_BUILTIN_LVEHX:
return altivec_expand_lv_builtin (CODE_FOR_altivec_lvehx,
- arglist, target);
+ arglist, target);
case ALTIVEC_BUILTIN_LVEWX:
return altivec_expand_lv_builtin (CODE_FOR_altivec_lvewx,
- arglist, target);
+ arglist, target);
case ALTIVEC_BUILTIN_LVXL:
return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl,
- arglist, target);
+ arglist, target);
case ALTIVEC_BUILTIN_LVX:
return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx,
- arglist, target);
+ arglist, target);
default:
break;
/* Fall through. */
static rtx
rs6000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- int ignore ATTRIBUTE_UNUSED)
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ int ignore ATTRIBUTE_UNUSED)
{
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
tree arglist = TREE_OPERAND (exp, 1);
rtx ret;
bool success;
+ if (fcode == ALTIVEC_BUILTIN_MASK_FOR_LOAD
+ || fcode == ALTIVEC_BUILTIN_MASK_FOR_STORE)
+ {
+ int icode = (int) CODE_FOR_altivec_lvsr;
+ enum machine_mode tmode = insn_data[icode].operand[0].mode;
+ enum machine_mode mode = insn_data[icode].operand[1].mode;
+ tree arg;
+ rtx op, addr, pat;
+
+ if (!TARGET_ALTIVEC)
+ abort ();
+
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+ abort ();
+ op = expand_expr (arg, NULL_RTX, Pmode, EXPAND_NORMAL);
+ addr = memory_address (mode, op);
+ if (fcode == ALTIVEC_BUILTIN_MASK_FOR_STORE)
+ op = addr;
+ else
+ {
+ /* For the load case need to negate the address. */
+ op = gen_reg_rtx (GET_MODE (addr));
+ emit_insn (gen_rtx_SET (VOIDmode, op,
+ gen_rtx_NEG (GET_MODE (addr), addr)));
+ }
+ op = gen_rtx_MEM (mode, op);
+
+ if (target == 0
+ || GET_MODE (target) != tmode
+ || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+ target = gen_reg_rtx (tmode);
+
+ /*pat = gen_altivec_lvsr (target, op);*/
+ pat = GEN_FCN (icode) (target, op);
+ if (!pat)
+ return 0;
+ emit_insn (pat);
+
+ return target;
+ }
+
if (TARGET_ALTIVEC)
{
ret = altivec_expand_builtin (exp, target, &success);
def_builtin (d->mask, d->name, type, d->code);
}
+
+ if (TARGET_ALTIVEC)
+ {
+ tree decl;
+
+ /* Initialize target builtin that implements
+ targetm.vectorize.builtin_mask_for_load. */
+
+ decl = lang_hooks.builtin_function ("__builtin_altivec_mask_for_load",
+ v16qi_ftype_long_pcvoid,
+ ALTIVEC_BUILTIN_MASK_FOR_LOAD,
+ BUILT_IN_MD, NULL, NULL_TREE);
+ /* Record the decl. Will be used by rs6000_builtin_mask_for_load. */
+ altivec_builtin_mask_for_load = decl;
+ }
}
static void
= build_function_type_list (V8HI_type_node,
V8HI_type_node, V8HI_type_node,
V8HI_type_node, NULL_TREE);
- tree v4si_ftype_v8hi_v8hi_v4si
+ tree v4si_ftype_v8hi_v8hi_v4si
= build_function_type_list (V4SI_type_node,
V8HI_type_node, V8HI_type_node,
V4SI_type_node, NULL_TREE);
- tree v4si_ftype_v16qi_v16qi_v4si
+ tree v4si_ftype_v16qi_v16qi_v4si
= build_function_type_list (V4SI_type_node,
V16QI_type_node, V16QI_type_node,
V4SI_type_node, NULL_TREE);
type = v16qi_ftype_v16qi_v16qi_v16qi;
break;
default:
- abort();
+ abort ();
}
}
else if (mode0 == mode1 && mode1 == mode2 && mode3 == V16QImode)
- {
+ {
switch (mode0)
{
case V4SImode:
type = v16qi_ftype_v16qi_v16qi_v16qi;
break;
default:
- abort();
+ abort ();
}
}
else if (mode0 == V4SImode && mode1 == V16QImode && mode2 == V16QImode
mode1 = insn_data[d->icode].operand[1].mode;
if (mode0 == V4SImode && mode1 == QImode)
- type = v4si_ftype_int;
+ type = v4si_ftype_int;
else if (mode0 == V8HImode && mode1 == QImode)
- type = v8hi_ftype_int;
+ type = v8hi_ftype_int;
else if (mode0 == V16QImode && mode1 == QImode)
- type = v16qi_ftype_int;
+ type = v16qi_ftype_int;
else if (mode0 == V4SFmode && mode1 == V4SFmode)
type = v4sf_ftype_v4sf;
else if (mode0 == V8HImode && mode1 == V16QImode)
}
/* Standard AIX/Darwin/64-bit SVR4 quad floating point routines. */
- set_optab_libfunc (add_optab, TFmode, "_xlqadd");
- set_optab_libfunc (sub_optab, TFmode, "_xlqsub");
- set_optab_libfunc (smul_optab, TFmode, "_xlqmul");
- set_optab_libfunc (sdiv_optab, TFmode, "_xlqdiv");
+ set_optab_libfunc (add_optab, TFmode, "__gcc_qadd");
+ set_optab_libfunc (sub_optab, TFmode, "__gcc_qsub");
+ set_optab_libfunc (smul_optab, TFmode, "__gcc_qmul");
+ set_optab_libfunc (sdiv_optab, TFmode, "__gcc_qdiv");
}
else
{
rtx orig_dest = operands[0];
rtx bytes_rtx = operands[1];
rtx align_rtx = operands[2];
- int constp = (GET_CODE (bytes_rtx) == CONST_INT);
- int align;
- int bytes;
+ bool constp = (GET_CODE (bytes_rtx) == CONST_INT);
+ HOST_WIDE_INT align;
+ HOST_WIDE_INT bytes;
int offset;
int clear_bytes;
+ int clear_step;
/* If this is not a fixed size move, just call memcpy */
if (! constp)
if (bytes <= 0)
return 1;
- if (bytes > (TARGET_POWERPC64 && align >= 32 ? 64 : 32))
- return 0;
+ /* Use the builtin memset after a point, to avoid huge code bloat.
+ When optimize_size, avoid any significant code bloat; calling
+ memset is about 4 instructions, so allow for one instruction to
+ load zero and three to do clearing. */
+ if (TARGET_ALTIVEC && align >= 128)
+ clear_step = 16;
+ else if (TARGET_POWERPC64 && align >= 32)
+ clear_step = 8;
+ else
+ clear_step = 4;
- if (optimize_size && bytes > 16)
+ if (optimize_size && bytes > 3 * clear_step)
+ return 0;
+ if (! optimize_size && bytes > 8 * clear_step)
return 0;
for (offset = 0; bytes > 0; offset += clear_bytes, bytes -= clear_bytes)
{
- rtx (*mov) (rtx, rtx);
enum machine_mode mode = BLKmode;
rtx dest;
- if (bytes >= 8 && TARGET_POWERPC64
- /* 64-bit loads and stores require word-aligned
- displacements. */
- && (align >= 64 || (!STRICT_ALIGNMENT && align >= 32)))
+ if (bytes >= 16 && TARGET_ALTIVEC && align >= 128)
+ {
+ clear_bytes = 16;
+ mode = V4SImode;
+ }
+ else if (bytes >= 8 && TARGET_POWERPC64
+ /* 64-bit loads and stores require word-aligned
+ displacements. */
+ && (align >= 64 || (!STRICT_ALIGNMENT && align >= 32)))
{
clear_bytes = 8;
mode = DImode;
- mov = gen_movdi;
}
- else if (bytes >= 4 && !STRICT_ALIGNMENT)
+ else if (bytes >= 4 && (align >= 32 || !STRICT_ALIGNMENT))
{ /* move 4 bytes */
clear_bytes = 4;
mode = SImode;
- mov = gen_movsi;
}
- else if (bytes == 2 && !STRICT_ALIGNMENT)
+ else if (bytes == 2 && (align >= 16 || !STRICT_ALIGNMENT))
{ /* move 2 bytes */
clear_bytes = 2;
mode = HImode;
- mov = gen_movhi;
}
else /* move 1 byte at a time */
{
clear_bytes = 1;
mode = QImode;
- mov = gen_movqi;
}
dest = adjust_address (orig_dest, mode, offset);
- emit_insn ((*mov) (dest, const0_rtx));
+ emit_move_insn (dest, CONST0_RTX (mode));
}
return 1;
enum machine_mode mode = BLKmode;
rtx src, dest;
- if (TARGET_STRING
+ /* Altivec first, since it will be faster than a string move
+ when it applies, and usually not significantly larger. */
+ if (TARGET_ALTIVEC && bytes >= 16 && align >= 128)
+ {
+ move_bytes = 16;
+ mode = V4SImode;
+ gen_func.mov = gen_movv4si;
+ }
+ else if (TARGET_STRING
&& bytes > 24 /* move up to 32 bytes at a time */
&& ! fixed_regs[5]
&& ! fixed_regs[6]
move_bytes = (bytes > 8) ? 8 : bytes;
gen_func.movmemsi = gen_movmemsi_2reg;
}
- else if (bytes >= 4 && !STRICT_ALIGNMENT)
+ else if (bytes >= 4 && (align >= 32 || !STRICT_ALIGNMENT))
{ /* move 4 bytes */
move_bytes = 4;
mode = SImode;
gen_func.mov = gen_movsi;
}
- else if (bytes == 2 && !STRICT_ALIGNMENT)
+ else if (bytes == 2 && (align >= 16 || !STRICT_ALIGNMENT))
{ /* move 2 bytes */
move_bytes = 2;
mode = HImode;
rtx dest_reg = copy_addr_to_reg (XEXP (dest, 0));
dest = replace_equiv_address (dest, dest_reg);
}
- set_mem_size (dest, GEN_INT (move_bytes));
-
- emit_insn ((*gen_func.movmemsi) (dest, src,
- GEN_INT (move_bytes & 31),
- align_rtx));
- }
- }
-
- return 1;
-}
-
-\f
-/* Return 1 if OP is suitable for a save_world call in prologue. It is
- known to be a PARALLEL. */
-int
-save_world_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int index;
- int i;
- rtx elt;
- int count = XVECLEN (op, 0);
-
- if (count != 55)
- return 0;
-
- index = 0;
- if (GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
- || GET_CODE (XVECEXP (op, 0, index++)) != USE)
- return 0;
-
- for (i=1; i <= 18; i++)
- {
- elt = XVECEXP (op, 0, index++);
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_DEST (elt)) != MEM
- || ! memory_operand (SET_DEST (elt), DFmode)
- || GET_CODE (SET_SRC (elt)) != REG
- || GET_MODE (SET_SRC (elt)) != DFmode)
- return 0;
- }
-
- for (i=1; i <= 12; i++)
- {
- elt = XVECEXP (op, 0, index++);
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_DEST (elt)) != MEM
- || GET_CODE (SET_SRC (elt)) != REG
- || GET_MODE (SET_SRC (elt)) != V4SImode)
- return 0;
- }
-
- for (i=1; i <= 19; i++)
- {
- elt = XVECEXP (op, 0, index++);
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_DEST (elt)) != MEM
- || ! memory_operand (SET_DEST (elt), Pmode)
- || GET_CODE (SET_SRC (elt)) != REG
- || GET_MODE (SET_SRC (elt)) != Pmode)
- return 0;
- }
-
- elt = XVECEXP (op, 0, index++);
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_DEST (elt)) != MEM
- || ! memory_operand (SET_DEST (elt), Pmode)
- || GET_CODE (SET_SRC (elt)) != REG
- || REGNO (SET_SRC (elt)) != CR2_REGNO
- || GET_MODE (SET_SRC (elt)) != Pmode)
- return 0;
-
- if (GET_CODE (XVECEXP (op, 0, index++)) != USE
- || GET_CODE (XVECEXP (op, 0, index++)) != USE
- || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER)
- return 0;
- return 1;
-}
-
-/* Return 1 if OP is suitable for a save_world call in prologue. It is
- known to be a PARALLEL. */
-int
-restore_world_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int index;
- int i;
- rtx elt;
- int count = XVECLEN (op, 0);
-
- if (count != 59)
- return 0;
-
- index = 0;
- if (GET_CODE (XVECEXP (op, 0, index++)) != RETURN
- || GET_CODE (XVECEXP (op, 0, index++)) != USE
- || GET_CODE (XVECEXP (op, 0, index++)) != USE
- || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER)
- return 0;
-
- elt = XVECEXP (op, 0, index++);
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_SRC (elt)) != MEM
- || ! memory_operand (SET_SRC (elt), Pmode)
- || GET_CODE (SET_DEST (elt)) != REG
- || REGNO (SET_DEST (elt)) != CR2_REGNO
- || GET_MODE (SET_DEST (elt)) != Pmode)
- return 0;
-
- for (i=1; i <= 19; i++)
- {
- elt = XVECEXP (op, 0, index++);
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_SRC (elt)) != MEM
- || ! memory_operand (SET_SRC (elt), Pmode)
- || GET_CODE (SET_DEST (elt)) != REG
- || GET_MODE (SET_DEST (elt)) != Pmode)
- return 0;
- }
-
- for (i=1; i <= 12; i++)
- {
- elt = XVECEXP (op, 0, index++);
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_SRC (elt)) != MEM
- || GET_CODE (SET_DEST (elt)) != REG
- || GET_MODE (SET_DEST (elt)) != V4SImode)
- return 0;
- }
-
- for (i=1; i <= 18; i++)
- {
- elt = XVECEXP (op, 0, index++);
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_SRC (elt)) != MEM
- || ! memory_operand (SET_SRC (elt), DFmode)
- || GET_CODE (SET_DEST (elt)) != REG
- || GET_MODE (SET_DEST (elt)) != DFmode)
- return 0;
- }
-
- if (GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
- || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
- || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
- || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
- || GET_CODE (XVECEXP (op, 0, index++)) != USE)
- return 0;
- return 1;
-}
-
-\f
-/* Return 1 if OP is a load multiple operation. It is known to be a
- PARALLEL and the first section will be tested. */
-
-int
-load_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int count = XVECLEN (op, 0);
- unsigned int dest_regno;
- rtx src_addr;
- int i;
-
- /* Perform a quick check so we don't blow up below. */
- if (count <= 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
- return 0;
-
- dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
- src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
-
- for (i = 1; i < count; i++)
- {
- rtx elt = XVECEXP (op, 0, i);
-
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_DEST (elt)) != REG
- || GET_MODE (SET_DEST (elt)) != SImode
- || REGNO (SET_DEST (elt)) != dest_regno + i
- || GET_CODE (SET_SRC (elt)) != MEM
- || GET_MODE (SET_SRC (elt)) != SImode
- || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
- || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
- || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
- || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
- return 0;
- }
-
- return 1;
-}
-
-/* Similar, but tests for store multiple. Here, the second vector element
- is a CLOBBER. It will be tested later. */
-
-int
-store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int count = XVECLEN (op, 0) - 1;
- unsigned int src_regno;
- rtx dest_addr;
- int i;
-
- /* Perform a quick check so we don't blow up below. */
- if (count <= 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
- return 0;
-
- src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
- dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
-
- for (i = 1; i < count; i++)
- {
- rtx elt = XVECEXP (op, 0, i + 1);
-
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_SRC (elt)) != REG
- || GET_MODE (SET_SRC (elt)) != SImode
- || REGNO (SET_SRC (elt)) != src_regno + i
- || GET_CODE (SET_DEST (elt)) != MEM
- || GET_MODE (SET_DEST (elt)) != SImode
- || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
- || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
- || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
- || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
- return 0;
- }
-
- return 1;
-}
-
-/* Return a string to perform a load_multiple operation.
- operands[0] is the vector.
- operands[1] is the source address.
- operands[2] is the first destination register. */
-
-const char *
-rs6000_output_load_multiple (rtx operands[3])
-{
- /* We have to handle the case where the pseudo used to contain the address
- is assigned to one of the output registers. */
- int i, j;
- int words = XVECLEN (operands[0], 0);
- rtx xop[10];
-
- if (XVECLEN (operands[0], 0) == 1)
- return "{l|lwz} %2,0(%1)";
-
- for (i = 0; i < words; i++)
- if (refers_to_regno_p (REGNO (operands[2]) + i,
- REGNO (operands[2]) + i + 1, operands[1], 0))
- {
- if (i == words-1)
- {
- xop[0] = GEN_INT (4 * (words-1));
- xop[1] = operands[1];
- xop[2] = operands[2];
- output_asm_insn ("{lsi|lswi} %2,%1,%0\n\t{l|lwz} %1,%0(%1)", xop);
- return "";
- }
- else if (i == 0)
- {
- xop[0] = GEN_INT (4 * (words-1));
- xop[1] = operands[1];
- xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
- output_asm_insn ("{cal %1,4(%1)|addi %1,%1,4}\n\t{lsi|lswi} %2,%1,%0\n\t{l|lwz} %1,-4(%1)", xop);
- return "";
- }
- else
- {
- for (j = 0; j < words; j++)
- if (j != i)
- {
- xop[0] = GEN_INT (j * 4);
- xop[1] = operands[1];
- xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + j);
- output_asm_insn ("{l|lwz} %2,%0(%1)", xop);
- }
- xop[0] = GEN_INT (i * 4);
- xop[1] = operands[1];
- output_asm_insn ("{l|lwz} %1,%0(%1)", xop);
- return "";
- }
- }
-
- return "{lsi|lswi} %2,%1,%N0";
-}
-
-/* Return 1 for a parallel vrsave operation. */
-
-int
-vrsave_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int count = XVECLEN (op, 0);
- unsigned int dest_regno, src_regno;
- int i;
-
- if (count <= 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC_VOLATILE)
- return 0;
-
- dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
- src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
-
- if (dest_regno != VRSAVE_REGNO
- && src_regno != VRSAVE_REGNO)
- return 0;
-
- for (i = 1; i < count; i++)
- {
- rtx elt = XVECEXP (op, 0, i);
-
- if (GET_CODE (elt) != CLOBBER
- && GET_CODE (elt) != SET)
- return 0;
- }
-
- return 1;
-}
-
-/* Return 1 for an PARALLEL suitable for mfcr. */
-
-int
-mfcr_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int count = XVECLEN (op, 0);
- int i;
-
- /* Perform a quick check so we don't blow up below. */
- if (count < 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC
- || XVECLEN (SET_SRC (XVECEXP (op, 0, 0)), 0) != 2)
- return 0;
-
- for (i = 0; i < count; i++)
- {
- rtx exp = XVECEXP (op, 0, i);
- rtx unspec;
- int maskval;
- rtx src_reg;
-
- src_reg = XVECEXP (SET_SRC (exp), 0, 0);
-
- if (GET_CODE (src_reg) != REG
- || GET_MODE (src_reg) != CCmode
- || ! CR_REGNO_P (REGNO (src_reg)))
- return 0;
-
- if (GET_CODE (exp) != SET
- || GET_CODE (SET_DEST (exp)) != REG
- || GET_MODE (SET_DEST (exp)) != SImode
- || ! INT_REGNO_P (REGNO (SET_DEST (exp))))
- return 0;
- unspec = SET_SRC (exp);
- maskval = 1 << (MAX_CR_REGNO - REGNO (src_reg));
-
- if (GET_CODE (unspec) != UNSPEC
- || XINT (unspec, 1) != UNSPEC_MOVESI_FROM_CR
- || XVECLEN (unspec, 0) != 2
- || XVECEXP (unspec, 0, 0) != src_reg
- || GET_CODE (XVECEXP (unspec, 0, 1)) != CONST_INT
- || INTVAL (XVECEXP (unspec, 0, 1)) != maskval)
- return 0;
- }
- return 1;
-}
-
-/* Return 1 for an PARALLEL suitable for mtcrf. */
-
-int
-mtcrf_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int count = XVECLEN (op, 0);
- int i;
- rtx src_reg;
-
- /* Perform a quick check so we don't blow up below. */
- if (count < 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC
- || XVECLEN (SET_SRC (XVECEXP (op, 0, 0)), 0) != 2)
- return 0;
- src_reg = XVECEXP (SET_SRC (XVECEXP (op, 0, 0)), 0, 0);
-
- if (GET_CODE (src_reg) != REG
- || GET_MODE (src_reg) != SImode
- || ! INT_REGNO_P (REGNO (src_reg)))
- return 0;
-
- for (i = 0; i < count; i++)
- {
- rtx exp = XVECEXP (op, 0, i);
- rtx unspec;
- int maskval;
-
- if (GET_CODE (exp) != SET
- || GET_CODE (SET_DEST (exp)) != REG
- || GET_MODE (SET_DEST (exp)) != CCmode
- || ! CR_REGNO_P (REGNO (SET_DEST (exp))))
- return 0;
- unspec = SET_SRC (exp);
- maskval = 1 << (MAX_CR_REGNO - REGNO (SET_DEST (exp)));
-
- if (GET_CODE (unspec) != UNSPEC
- || XINT (unspec, 1) != UNSPEC_MOVESI_TO_CR
- || XVECLEN (unspec, 0) != 2
- || XVECEXP (unspec, 0, 0) != src_reg
- || GET_CODE (XVECEXP (unspec, 0, 1)) != CONST_INT
- || INTVAL (XVECEXP (unspec, 0, 1)) != maskval)
- return 0;
- }
- return 1;
-}
-
-/* Return 1 for an PARALLEL suitable for lmw. */
-
-int
-lmw_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int count = XVECLEN (op, 0);
- unsigned int dest_regno;
- rtx src_addr;
- unsigned int base_regno;
- HOST_WIDE_INT offset;
- int i;
-
- /* Perform a quick check so we don't blow up below. */
- if (count <= 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
- return 0;
-
- dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
- src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
-
- if (dest_regno > 31
- || count != 32 - (int) dest_regno)
- return 0;
-
- if (legitimate_indirect_address_p (src_addr, 0))
- {
- offset = 0;
- base_regno = REGNO (src_addr);
- if (base_regno == 0)
- return 0;
- }
- else if (rs6000_legitimate_offset_address_p (SImode, src_addr, 0))
- {
- offset = INTVAL (XEXP (src_addr, 1));
- base_regno = REGNO (XEXP (src_addr, 0));
- }
- else
- return 0;
-
- for (i = 0; i < count; i++)
- {
- rtx elt = XVECEXP (op, 0, i);
- rtx newaddr;
- rtx addr_reg;
- HOST_WIDE_INT newoffset;
-
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_DEST (elt)) != REG
- || GET_MODE (SET_DEST (elt)) != SImode
- || REGNO (SET_DEST (elt)) != dest_regno + i
- || GET_CODE (SET_SRC (elt)) != MEM
- || GET_MODE (SET_SRC (elt)) != SImode)
- return 0;
- newaddr = XEXP (SET_SRC (elt), 0);
- if (legitimate_indirect_address_p (newaddr, 0))
- {
- newoffset = 0;
- addr_reg = newaddr;
- }
- else if (rs6000_legitimate_offset_address_p (SImode, newaddr, 0))
- {
- addr_reg = XEXP (newaddr, 0);
- newoffset = INTVAL (XEXP (newaddr, 1));
- }
- else
- return 0;
- if (REGNO (addr_reg) != base_regno
- || newoffset != offset + 4 * i)
- return 0;
- }
-
- return 1;
-}
-
-/* Return 1 for an PARALLEL suitable for stmw. */
-
-int
-stmw_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- int count = XVECLEN (op, 0);
- unsigned int src_regno;
- rtx dest_addr;
- unsigned int base_regno;
- HOST_WIDE_INT offset;
- int i;
-
- /* Perform a quick check so we don't blow up below. */
- if (count <= 1
- || GET_CODE (XVECEXP (op, 0, 0)) != SET
- || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
- || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
- return 0;
-
- src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
- dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
-
- if (src_regno > 31
- || count != 32 - (int) src_regno)
- return 0;
-
- if (legitimate_indirect_address_p (dest_addr, 0))
- {
- offset = 0;
- base_regno = REGNO (dest_addr);
- if (base_regno == 0)
- return 0;
- }
- else if (rs6000_legitimate_offset_address_p (SImode, dest_addr, 0))
- {
- offset = INTVAL (XEXP (dest_addr, 1));
- base_regno = REGNO (XEXP (dest_addr, 0));
- }
- else
- return 0;
-
- for (i = 0; i < count; i++)
- {
- rtx elt = XVECEXP (op, 0, i);
- rtx newaddr;
- rtx addr_reg;
- HOST_WIDE_INT newoffset;
+ set_mem_size (dest, GEN_INT (move_bytes));
- if (GET_CODE (elt) != SET
- || GET_CODE (SET_SRC (elt)) != REG
- || GET_MODE (SET_SRC (elt)) != SImode
- || REGNO (SET_SRC (elt)) != src_regno + i
- || GET_CODE (SET_DEST (elt)) != MEM
- || GET_MODE (SET_DEST (elt)) != SImode)
- return 0;
- newaddr = XEXP (SET_DEST (elt), 0);
- if (legitimate_indirect_address_p (newaddr, 0))
- {
- newoffset = 0;
- addr_reg = newaddr;
- }
- else if (rs6000_legitimate_offset_address_p (SImode, newaddr, 0))
- {
- addr_reg = XEXP (newaddr, 0);
- newoffset = INTVAL (XEXP (newaddr, 1));
+ emit_insn ((*gen_func.movmemsi) (dest, src,
+ GEN_INT (move_bytes & 31),
+ align_rtx));
}
- else
- return 0;
- if (REGNO (addr_reg) != base_regno
- || newoffset != offset + 4 * i)
- return 0;
}
return 1;
}
+
+\f
+/* Return a string to perform a load_multiple operation.
+ operands[0] is the vector.
+ operands[1] is the source address.
+ operands[2] is the first destination register. */
+
+const char *
+rs6000_output_load_multiple (rtx operands[3])
+{
+ /* We have to handle the case where the pseudo used to contain the address
+ is assigned to one of the output registers. */
+ int i, j;
+ int words = XVECLEN (operands[0], 0);
+ rtx xop[10];
+
+ if (XVECLEN (operands[0], 0) == 1)
+ return "{l|lwz} %2,0(%1)";
+
+ for (i = 0; i < words; i++)
+ if (refers_to_regno_p (REGNO (operands[2]) + i,
+ REGNO (operands[2]) + i + 1, operands[1], 0))
+ {
+ if (i == words-1)
+ {
+ xop[0] = GEN_INT (4 * (words-1));
+ xop[1] = operands[1];
+ xop[2] = operands[2];
+ output_asm_insn ("{lsi|lswi} %2,%1,%0\n\t{l|lwz} %1,%0(%1)", xop);
+ return "";
+ }
+ else if (i == 0)
+ {
+ xop[0] = GEN_INT (4 * (words-1));
+ xop[1] = operands[1];
+ xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
+ output_asm_insn ("{cal %1,4(%1)|addi %1,%1,4}\n\t{lsi|lswi} %2,%1,%0\n\t{l|lwz} %1,-4(%1)", xop);
+ return "";
+ }
+ else
+ {
+ for (j = 0; j < words; j++)
+ if (j != i)
+ {
+ xop[0] = GEN_INT (j * 4);
+ xop[1] = operands[1];
+ xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + j);
+ output_asm_insn ("{l|lwz} %2,%0(%1)", xop);
+ }
+ xop[0] = GEN_INT (i * 4);
+ xop[1] = operands[1];
+ output_asm_insn ("{l|lwz} %1,%0(%1)", xop);
+ return "";
+ }
+ }
+
+ return "{lsi|lswi} %2,%1,%N0";
+}
+
\f
/* A validation routine: say whether CODE, a condition code, and MODE
match. The other alternatives either don't make sense or should
never be generated. */
-static void
+void
validate_condition_mode (enum rtx_code code, enum machine_mode mode)
{
if ((GET_RTX_CLASS (code) != RTX_COMPARE
abort ();
}
-/* Return 1 if OP is a comparison operation that is valid for a branch insn.
- We only check the opcode against the mode of the CC value here. */
-
-int
-branch_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- enum rtx_code code = GET_CODE (op);
- enum machine_mode cc_mode;
-
- if (!COMPARISON_P (op))
- return 0;
-
- cc_mode = GET_MODE (XEXP (op, 0));
- if (GET_MODE_CLASS (cc_mode) != MODE_CC)
- return 0;
-
- validate_condition_mode (code, cc_mode);
-
- return 1;
-}
-
-/* Return 1 if OP is a comparison operation that is valid for a branch
- insn and which is true if the corresponding bit in the CC register
- is set. */
-
-int
-branch_positive_comparison_operator (rtx op, enum machine_mode mode)
-{
- enum rtx_code code;
-
- if (! branch_comparison_operator (op, mode))
- return 0;
-
- code = GET_CODE (op);
- return (code == EQ || code == LT || code == GT
- || code == LTU || code == GTU
- || code == UNORDERED);
-}
-
-/* Return 1 if OP is a comparison operation that is valid for an scc
- insn: it must be a positive comparison. */
-
-int
-scc_comparison_operator (rtx op, enum machine_mode mode)
-{
- return branch_positive_comparison_operator (op, mode);
-}
-
-int
-trap_comparison_operator (rtx op, enum machine_mode mode)
-{
- if (mode != VOIDmode && mode != GET_MODE (op))
- return 0;
- return COMPARISON_P (op);
-}
-
-int
-boolean_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- enum rtx_code code = GET_CODE (op);
- return (code == AND || code == IOR || code == XOR);
-}
-
-int
-boolean_or_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- enum rtx_code code = GET_CODE (op);
- return (code == IOR || code == XOR);
-}
-
-int
-min_max_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- enum rtx_code code = GET_CODE (op);
- return (code == SMIN || code == SMAX || code == UMIN || code == UMAX);
-}
\f
/* Return 1 if ANDOP is a mask that has no bits on that are not in the
mask required to convert the result of a rotate insn into a shift
return 0;
else
{
- reg1 = REGNO (XEXP (addr1, 0));
+ reg1 = REGNO (XEXP (addr1, 0));
/* The offset must be constant! */
if (GET_CODE (XEXP (addr1, 1)) != CONST_INT)
- return 0;
- offset1 = INTVAL (XEXP (addr1, 1));
+ return 0;
+ offset1 = INTVAL (XEXP (addr1, 1));
}
}
else if (GET_CODE (addr1) != REG)
if (TARGET_ELF || (DEFAULT_ABI == ABI_DARWIN
#if TARGET_MACHO
- && MACHOPIC_INDIRECT
+ && MACHOPIC_INDIRECT
#endif
- ))
+ ))
{
/* We cannot copy a symbolic operand directly into anything
- other than BASE_REGS for TARGET_ELF. So indicate that a
- register from BASE_REGS is needed as an intermediate
- register.
+ other than BASE_REGS for TARGET_ELF. So indicate that a
+ register from BASE_REGS is needed as an intermediate
+ register.
On Darwin, pic addresses require a load from memory, which
needs a base register. */
if (class != BASE_REGS
- && (GET_CODE (in) == SYMBOL_REF
- || GET_CODE (in) == HIGH
- || GET_CODE (in) == LABEL_REF
- || GET_CODE (in) == CONST))
- return BASE_REGS;
+ && (GET_CODE (in) == SYMBOL_REF
+ || GET_CODE (in) == HIGH
+ || GET_CODE (in) == LABEL_REF
+ || GET_CODE (in) == CONST))
+ return BASE_REGS;
}
if (GET_CODE (in) == REG)
\f
/* Return the GOT register. */
-struct rtx_def *
+rtx
rs6000_got_register (rtx value ATTRIBUTE_UNUSED)
{
/* The second flow pass currently (June 1999) can't update
case 'B':
/* If the low-order bit is zero, write 'r'; otherwise, write 'l'
for 64-bit mask direction. */
- putc (((INT_LOWPART(x) & 1) == 0 ? 'r' : 'l'), file);
+ putc (((INT_LOWPART (x) & 1) == 0 ? 'r' : 'l'), file);
return;
/* %c is output_addr_const if a CONSTANT_ADDRESS_P, otherwise
/* Bit 1 is EQ bit. */
i = 4 * (REGNO (x) - CR0_REGNO) + 2;
- /* If we want bit 31, write a shift count of zero, not 32. */
- fprintf (file, "%d", i == 31 ? 0 : i + 1);
+ fprintf (file, "%d", i);
return;
case 'E':
/* Write second word of DImode or DFmode reference. Works on register
or non-indexed memory only. */
if (GET_CODE (x) == REG)
- fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+ fputs (reg_names[REGNO (x) + 1], file);
else if (GET_CODE (x) == MEM)
{
/* Handle possible auto-increment. Since it is pre-increment and
|| REGNO (XEXP (x, 0)) >= 32)
output_operand_lossage ("invalid %%P value");
else
- fprintf (file, "%s", reg_names[REGNO (XEXP (x, 0))]);
+ fputs (reg_names[REGNO (XEXP (x, 0))], file);
return;
case 'q':
/* This outputs the logical code corresponding to a boolean
expression. The expression may have one or both operands
negated (if one, only the first one). For condition register
- logical operations, it will also treat the negated
- CR codes as NOTs, but not handle NOTs of them. */
+ logical operations, it will also treat the negated
+ CR codes as NOTs, but not handle NOTs of them. */
{
const char *const *t = 0;
const char *s;
case 'Y':
/* Like 'L', for third word of TImode */
if (GET_CODE (x) == REG)
- fprintf (file, "%s", reg_names[REGNO (x) + 2]);
+ fputs (reg_names[REGNO (x) + 2], file);
else if (GET_CODE (x) == MEM)
{
if (GET_CODE (XEXP (x, 0)) == PRE_INC
if (GET_CODE (x) != SYMBOL_REF)
abort ();
- /* Mark the decl as referenced so that cgraph will output the function. */
+ /* Mark the decl as referenced so that cgraph will output the
+ function. */
if (SYMBOL_REF_DECL (x))
- mark_decl_referenced (SYMBOL_REF_DECL (x));
+ mark_decl_referenced (SYMBOL_REF_DECL (x));
/* For macho, check to see if we need a stub. */
if (TARGET_MACHO)
case 'Z':
/* Like 'L', for last word of TImode. */
if (GET_CODE (x) == REG)
- fprintf (file, "%s", reg_names[REGNO (x) + 3]);
+ fputs (reg_names[REGNO (x) + 3], file);
else if (GET_CODE (x) == MEM)
{
if (GET_CODE (XEXP (x, 0)) == PRE_INC
/* Fall through. Must be [reg+reg]. */
}
+ if (TARGET_ALTIVEC
+ && GET_CODE (tmp) == AND
+ && GET_CODE (XEXP (tmp, 1)) == CONST_INT
+ && INTVAL (XEXP (tmp, 1)) == -16)
+ tmp = XEXP (tmp, 0);
if (GET_CODE (tmp) == REG)
fprintf (file, "0,%s", reg_names[REGNO (tmp)]);
else if (GET_CODE (tmp) == PLUS && GET_CODE (XEXP (tmp, 1)) == REG)
INTVAL (XEXP (x, 1)), reg_names[ REGNO (XEXP (x, 0)) ]);
#if TARGET_ELF
else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG
- && CONSTANT_P (XEXP (x, 1)))
+ && CONSTANT_P (XEXP (x, 1)))
{
output_addr_const (file, XEXP (x, 1));
fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
#endif
#if TARGET_MACHO
else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG
- && CONSTANT_P (XEXP (x, 1)))
+ && CONSTANT_P (XEXP (x, 1)))
{
fprintf (file, "lo16(");
output_addr_const (file, XEXP (x, 1));
{
#ifdef RELOCATABLE_NEEDS_FIXUP
/* Special handling for SI values. */
- if (size == 4 && aligned_p)
+ if (RELOCATABLE_NEEDS_FIXUP && size == 4 && aligned_p)
{
extern int in_toc_section (void);
static int recurse = 0;
&& TREE_CODE (decl) == FUNCTION_DECL)
{
static const char * const visibility_types[] = {
- NULL, "internal", "hidden", "protected"
+ NULL, "internal", "hidden", "protected"
};
const char *name, *type;
if (rs6000_compare_fp_p)
comp_mode = CCFPmode;
else if (code == GTU || code == LTU
- || code == GEU || code == LEU)
+ || code == GEU || code == LEU)
+ comp_mode = CCUNSmode;
+ else if ((code == EQ || code == NE)
+ && GET_CODE (rs6000_compare_op0) == SUBREG
+ && GET_CODE (rs6000_compare_op1) == SUBREG
+ && SUBREG_PROMOTED_UNSIGNED_P (rs6000_compare_op0)
+ && SUBREG_PROMOTED_UNSIGNED_P (rs6000_compare_op1))
+ /* These are unsigned values, perhaps there will be a later
+ ordering compare that can be shared with this one.
+ Unfortunately we cannot detect the signedness of the operands
+ for non-subregs. */
comp_mode = CCUNSmode;
else
comp_mode = CCmode;
if ((TARGET_E500 && !TARGET_FPRS && TARGET_HARD_FLOAT)
&& rs6000_compare_fp_p)
{
- rtx cmp, or1, or2, or_result, compare_result2;
+ rtx cmp, or_result, compare_result2;
+ enum machine_mode op_mode = GET_MODE (rs6000_compare_op0);
+
+ if (op_mode == VOIDmode)
+ op_mode = GET_MODE (rs6000_compare_op1);
/* Note: The E500 comparison instructions set the GT bit (x +
1), on success. This explains the mess. */
switch (code)
{
case EQ: case UNEQ: case NE: case LTGT:
- cmp = flag_finite_math_only
- ? gen_tstsfeq_gpr (compare_result, rs6000_compare_op0,
- rs6000_compare_op1)
- : gen_cmpsfeq_gpr (compare_result, rs6000_compare_op0,
- rs6000_compare_op1);
+ if (op_mode == SFmode)
+ cmp = flag_unsafe_math_optimizations
+ ? gen_tstsfeq_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1)
+ : gen_cmpsfeq_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1);
+ else if (op_mode == DFmode)
+ cmp = flag_unsafe_math_optimizations
+ ? gen_tstdfeq_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1)
+ : gen_cmpdfeq_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1);
+ else abort ();
break;
case GT: case GTU: case UNGT: case UNGE: case GE: case GEU:
- cmp = flag_finite_math_only
- ? gen_tstsfgt_gpr (compare_result, rs6000_compare_op0,
- rs6000_compare_op1)
- : gen_cmpsfgt_gpr (compare_result, rs6000_compare_op0,
- rs6000_compare_op1);
+ if (op_mode == SFmode)
+ cmp = flag_unsafe_math_optimizations
+ ? gen_tstsfgt_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1)
+ : gen_cmpsfgt_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1);
+ else if (op_mode == DFmode)
+ cmp = flag_unsafe_math_optimizations
+ ? gen_tstdfgt_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1)
+ : gen_cmpdfgt_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1);
+ else abort ();
break;
case LT: case LTU: case UNLT: case UNLE: case LE: case LEU:
- cmp = flag_finite_math_only
- ? gen_tstsflt_gpr (compare_result, rs6000_compare_op0,
- rs6000_compare_op1)
- : gen_cmpsflt_gpr (compare_result, rs6000_compare_op0,
- rs6000_compare_op1);
+ if (op_mode == SFmode)
+ cmp = flag_unsafe_math_optimizations
+ ? gen_tstsflt_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1)
+ : gen_cmpsflt_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1);
+ else if (op_mode == DFmode)
+ cmp = flag_unsafe_math_optimizations
+ ? gen_tstdflt_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1)
+ : gen_cmpdflt_gpr (compare_result, rs6000_compare_op0,
+ rs6000_compare_op1);
+ else abort ();
break;
- default:
- abort ();
+ default:
+ abort ();
}
/* Synthesize LE and GE from LT/GT || EQ. */
default: abort ();
}
- or1 = gen_reg_rtx (SImode);
- or2 = gen_reg_rtx (SImode);
- or_result = gen_reg_rtx (CCEQmode);
compare_result2 = gen_reg_rtx (CCFPmode);
/* Do the EQ. */
- cmp = flag_finite_math_only
- ? gen_tstsfeq_gpr (compare_result2, rs6000_compare_op0,
- rs6000_compare_op1)
- : gen_cmpsfeq_gpr (compare_result2, rs6000_compare_op0,
- rs6000_compare_op1);
+ if (op_mode == SFmode)
+ cmp = flag_unsafe_math_optimizations
+ ? gen_tstsfeq_gpr (compare_result2, rs6000_compare_op0,
+ rs6000_compare_op1)
+ : gen_cmpsfeq_gpr (compare_result2, rs6000_compare_op0,
+ rs6000_compare_op1);
+ else if (op_mode == DFmode)
+ cmp = flag_unsafe_math_optimizations
+ ? gen_tstdfeq_gpr (compare_result2, rs6000_compare_op0,
+ rs6000_compare_op1)
+ : gen_cmpdfeq_gpr (compare_result2, rs6000_compare_op0,
+ rs6000_compare_op1);
+ else abort ();
emit_insn (cmp);
- or1 = gen_rtx_GT (SImode, compare_result, const0_rtx);
- or2 = gen_rtx_GT (SImode, compare_result2, const0_rtx);
-
/* OR them together. */
- cmp = gen_rtx_SET (VOIDmode, or_result,
- gen_rtx_COMPARE (CCEQmode,
- gen_rtx_IOR (SImode, or1, or2),
- const_true_rtx));
+ or_result = gen_reg_rtx (CCFPmode);
+ cmp = gen_e500_cr_ior_compare (or_result, compare_result,
+ compare_result2);
compare_result = or_result;
code = EQ;
}
emit_insn (cmp);
}
else
- emit_insn (gen_rtx_SET (VOIDmode, compare_result,
- gen_rtx_COMPARE (comp_mode,
- rs6000_compare_op0,
- rs6000_compare_op1)));
+ {
+ /* Generate XLC-compatible TFmode compare as PARALLEL with extra
+ CLOBBERs to match cmptf_internal2 pattern. */
+ if (comp_mode == CCFPmode && TARGET_XL_COMPAT
+ && GET_MODE (rs6000_compare_op0) == TFmode
+ && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_DARWIN)
+ && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128)
+ emit_insn (gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (9,
+ gen_rtx_SET (VOIDmode,
+ compare_result,
+ gen_rtx_COMPARE (comp_mode,
+ rs6000_compare_op0,
+ rs6000_compare_op1)),
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
+ gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)))));
+ else
+ emit_insn (gen_rtx_SET (VOIDmode, compare_result,
+ gen_rtx_COMPARE (comp_mode,
+ rs6000_compare_op0,
+ rs6000_compare_op1)));
+ }
/* Some kinds of FP comparisons need an OR operation;
under flag_finite_math_only we don't bother. */
if (rs6000_compare_fp_p
- && ! flag_finite_math_only
- && ! (TARGET_HARD_FLOAT && TARGET_E500 && !TARGET_FPRS)
+ && !flag_finite_math_only
+ && !(TARGET_HARD_FLOAT && TARGET_E500 && !TARGET_FPRS)
&& (code == LE || code == GE
|| code == UNEQ || code == LTGT
|| code == UNGT || code == UNLT))
abort ();
if (cond_code == NE)
- emit_insn (gen_e500_flip_eq_bit (t, t));
+ emit_insn (gen_e500_flip_gt_bit (t, t));
- emit_insn (gen_move_from_CR_eq_bit (result, t));
+ emit_insn (gen_move_from_CR_gt_bit (result, t));
return;
}
{
if (abs (prob) > REG_BR_PROB_BASE / 20
&& ((prob > 0) ^ need_longbranch))
- pred = "+";
+ pred = "+";
else
pred = "-";
}
return string;
}
-/* Return the string to flip the EQ bit on a CR. */
+/* Return the string to flip the GT bit on a CR. */
char *
-output_e500_flip_eq_bit (rtx dst, rtx src)
+output_e500_flip_gt_bit (rtx dst, rtx src)
{
static char string[64];
int a, b;
|| GET_CODE (src) != REG || ! CR_REGNO_P (REGNO (src)))
abort ();
- /* EQ bit. */
- a = 4 * (REGNO (dst) - CR0_REGNO) + 2;
- b = 4 * (REGNO (src) - CR0_REGNO) + 2;
+ /* GT bit. */
+ a = 4 * (REGNO (dst) - CR0_REGNO) + 1;
+ b = 4 * (REGNO (src) - CR0_REGNO) + 1;
sprintf (string, "crnot %d,%d", a, b);
return string;
}
+/* Return insn index for the vector compare instruction for given CODE,
+ and DEST_MODE, OP_MODE. Return INSN_NOT_AVAILABLE if valid insn is
+ not available. */
+
+static int
+get_vec_cmp_insn (enum rtx_code code,
+ enum machine_mode dest_mode,
+ enum machine_mode op_mode)
+{
+ if (!TARGET_ALTIVEC)
+ return INSN_NOT_AVAILABLE;
+
+ switch (code)
+ {
+ case EQ:
+ if (dest_mode == V16QImode && op_mode == V16QImode)
+ return UNSPEC_VCMPEQUB;
+ if (dest_mode == V8HImode && op_mode == V8HImode)
+ return UNSPEC_VCMPEQUH;
+ if (dest_mode == V4SImode && op_mode == V4SImode)
+ return UNSPEC_VCMPEQUW;
+ if (dest_mode == V4SImode && op_mode == V4SFmode)
+ return UNSPEC_VCMPEQFP;
+ break;
+ case GE:
+ if (dest_mode == V4SImode && op_mode == V4SFmode)
+ return UNSPEC_VCMPGEFP;
+ case GT:
+ if (dest_mode == V16QImode && op_mode == V16QImode)
+ return UNSPEC_VCMPGTSB;
+ if (dest_mode == V8HImode && op_mode == V8HImode)
+ return UNSPEC_VCMPGTSH;
+ if (dest_mode == V4SImode && op_mode == V4SImode)
+ return UNSPEC_VCMPGTSW;
+ if (dest_mode == V4SImode && op_mode == V4SFmode)
+ return UNSPEC_VCMPGTFP;
+ break;
+ case GTU:
+ if (dest_mode == V16QImode && op_mode == V16QImode)
+ return UNSPEC_VCMPGTUB;
+ if (dest_mode == V8HImode && op_mode == V8HImode)
+ return UNSPEC_VCMPGTUH;
+ if (dest_mode == V4SImode && op_mode == V4SImode)
+ return UNSPEC_VCMPGTUW;
+ break;
+ default:
+ break;
+ }
+ return INSN_NOT_AVAILABLE;
+}
+
+/* Emit vector compare for operands OP0 and OP1 using code RCODE.
+ DMODE is expected destination mode. This is a recursive function. */
+
+static rtx
+rs6000_emit_vector_compare (enum rtx_code rcode,
+ rtx op0, rtx op1,
+ enum machine_mode dmode)
+{
+ int vec_cmp_insn;
+ rtx mask;
+ enum machine_mode dest_mode;
+ enum machine_mode op_mode = GET_MODE (op1);
+
+#ifdef ENABLE_CHECKING
+ if (!TARGET_ALTIVEC)
+ abort ();
+
+ if (GET_MODE (op0) != GET_MODE (op1))
+ abort ();
+#endif
+
+ /* Floating point vector compare instructions uses destination V4SImode.
+ Move destination to appropriate mode later. */
+ if (dmode == V4SFmode)
+ dest_mode = V4SImode;
+ else
+ dest_mode = dmode;
+
+ mask = gen_reg_rtx (dest_mode);
+ vec_cmp_insn = get_vec_cmp_insn (rcode, dest_mode, op_mode);
+
+ if (vec_cmp_insn == INSN_NOT_AVAILABLE)
+ {
+ bool swap_operands = false;
+ bool try_again = false;
+ switch (rcode)
+ {
+ case LT:
+ rcode = GT;
+ swap_operands = true;
+ try_again = true;
+ break;
+ case LTU:
+ rcode = GTU;
+ swap_operands = true;
+ try_again = true;
+ break;
+ case NE:
+ /* Treat A != B as ~(A==B). */
+ {
+ enum insn_code nor_code;
+ rtx eq_rtx = rs6000_emit_vector_compare (EQ, op0, op1,
+ dest_mode);
+
+ nor_code = one_cmpl_optab->handlers[(int)dest_mode].insn_code;
+ if (nor_code == CODE_FOR_nothing)
+ abort ();
+ emit_insn (GEN_FCN (nor_code) (mask, eq_rtx));
+
+ if (dmode != dest_mode)
+ {
+ rtx temp = gen_reg_rtx (dest_mode);
+ convert_move (temp, mask, 0);
+ return temp;
+ }
+ return mask;
+ }
+ break;
+ case GE:
+ case GEU:
+ case LE:
+ case LEU:
+ /* Try GT/GTU/LT/LTU OR EQ */
+ {
+ rtx c_rtx, eq_rtx;
+ enum insn_code ior_code;
+ enum rtx_code new_code;
+
+ if (rcode == GE)
+ new_code = GT;
+ else if (rcode == GEU)
+ new_code = GTU;
+ else if (rcode == LE)
+ new_code = LT;
+ else if (rcode == LEU)
+ new_code = LTU;
+ else
+ abort ();
+
+ c_rtx = rs6000_emit_vector_compare (new_code,
+ op0, op1, dest_mode);
+ eq_rtx = rs6000_emit_vector_compare (EQ, op0, op1,
+ dest_mode);
+
+ ior_code = ior_optab->handlers[(int)dest_mode].insn_code;
+ if (ior_code == CODE_FOR_nothing)
+ abort ();
+ emit_insn (GEN_FCN (ior_code) (mask, c_rtx, eq_rtx));
+ if (dmode != dest_mode)
+ {
+ rtx temp = gen_reg_rtx (dest_mode);
+ convert_move (temp, mask, 0);
+ return temp;
+ }
+ return mask;
+ }
+ break;
+ default:
+ abort ();
+ }
+
+ if (try_again)
+ {
+ vec_cmp_insn = get_vec_cmp_insn (rcode, dest_mode, op_mode);
+ if (vec_cmp_insn == INSN_NOT_AVAILABLE)
+ /* You only get two chances. */
+ abort ();
+ }
+
+ if (swap_operands)
+ {
+ rtx tmp;
+ tmp = op0;
+ op0 = op1;
+ op1 = tmp;
+ }
+ }
+
+ emit_insn (gen_rtx_fmt_ee (SET,
+ VOIDmode,
+ mask,
+ gen_rtx_fmt_Ei (UNSPEC, dest_mode,
+ gen_rtvec (2, op0, op1),
+ vec_cmp_insn)));
+ if (dmode != dest_mode)
+ {
+ rtx temp = gen_reg_rtx (dest_mode);
+ convert_move (temp, mask, 0);
+ return temp;
+ }
+ return mask;
+}
+
+/* Return vector select instruction for MODE. Return INSN_NOT_AVAILABLE, if
+ valid insn doesn exist for given mode. */
+
+static int
+get_vsel_insn (enum machine_mode mode)
+{
+ switch (mode)
+ {
+ case V4SImode:
+ return UNSPEC_VSEL4SI;
+ break;
+ case V4SFmode:
+ return UNSPEC_VSEL4SF;
+ break;
+ case V8HImode:
+ return UNSPEC_VSEL8HI;
+ break;
+ case V16QImode:
+ return UNSPEC_VSEL16QI;
+ break;
+ default:
+ return INSN_NOT_AVAILABLE;
+ break;
+ }
+ return INSN_NOT_AVAILABLE;
+}
+
+/* Emit vector select insn where DEST is destination using
+ operands OP1, OP2 and MASK. */
+
+static void
+rs6000_emit_vector_select (rtx dest, rtx op1, rtx op2, rtx mask)
+{
+ rtx t, temp;
+ enum machine_mode dest_mode = GET_MODE (dest);
+ int vsel_insn_index = get_vsel_insn (GET_MODE (dest));
+
+ temp = gen_reg_rtx (dest_mode);
+
+ t = gen_rtx_fmt_ee (SET, VOIDmode, temp,
+ gen_rtx_fmt_Ei (UNSPEC, dest_mode,
+ gen_rtvec (3, op1, op2, mask),
+ vsel_insn_index));
+ emit_insn (t);
+ emit_move_insn (dest, temp);
+ return;
+}
+
+/* Emit vector conditional expression.
+ DEST is destination. OP1 and OP2 are two VEC_COND_EXPR operands.
+ CC_OP0 and CC_OP1 are the two operands for the relation operation COND. */
+
+int
+rs6000_emit_vector_cond_expr (rtx dest, rtx op1, rtx op2,
+ rtx cond, rtx cc_op0, rtx cc_op1)
+{
+ enum machine_mode dest_mode = GET_MODE (dest);
+ enum rtx_code rcode = GET_CODE (cond);
+ rtx mask;
+
+ if (!TARGET_ALTIVEC)
+ return 0;
+
+ /* Get the vector mask for the given relational operations. */
+ mask = rs6000_emit_vector_compare (rcode, cc_op0, cc_op1, dest_mode);
+
+ rs6000_emit_vector_select (dest, op1, op2, mask);
+
+ return 1;
+}
+
/* Emit a conditional move: move TRUE_COND to DEST if OP of the
operands of the last comparison is nonzero/true, FALSE_COND if it
is zero/false. Return 0 if the hardware has no such operation. */
enum machine_mode compare_mode = GET_MODE (op0);
enum machine_mode result_mode = GET_MODE (dest);
rtx temp;
+ bool is_against_zero;
/* These modes should always match. */
if (GET_MODE (op1) != compare_mode
&& GET_MODE_CLASS (compare_mode) == MODE_FLOAT)
return 0;
+ is_against_zero = op1 == CONST0_RTX (compare_mode);
+
+ /* A floating-point subtract might overflow, underflow, or produce
+ an inexact result, thus changing the floating-point flags, so it
+ can't be generated if we care about that. It's safe if one side
+ of the construct is zero, since then no subtract will be
+ generated. */
+ if (GET_MODE_CLASS (compare_mode) == MODE_FLOAT
+ && flag_trapping_math && ! is_against_zero)
+ return 0;
+
/* Eliminate half of the comparisons by switching operands, this
makes the remaining code simpler. */
if (code == UNLT || code == UNGT || code == UNORDERED || code == NE
|| (! rtx_equal_p (op0, true_cond)
&& ! rtx_equal_p (op1, true_cond))))
return 0;
+
/* At this point we know we can use fsel. */
/* Reduce the comparison to a comparison against zero. */
- temp = gen_reg_rtx (compare_mode);
- emit_insn (gen_rtx_SET (VOIDmode, temp,
- gen_rtx_MINUS (compare_mode, op0, op1)));
- op0 = temp;
- op1 = CONST0_RTX (compare_mode);
+ if (! is_against_zero)
+ {
+ temp = gen_reg_rtx (compare_mode);
+ emit_insn (gen_rtx_SET (VOIDmode, temp,
+ gen_rtx_MINUS (compare_mode, op0, op1)));
+ op0 = temp;
+ op1 = CONST0_RTX (compare_mode);
+ }
/* If we don't care about NaNs we can reduce some of the comparisons
down to faster ones. */
bool used_update = false;
if (MEM_P (src) && INT_REGNO_P (reg))
- {
- rtx breg;
+ {
+ rtx breg;
if (GET_CODE (XEXP (src, 0)) == PRE_INC
|| GET_CODE (XEXP (src, 0)) == PRE_DEC)
{
rtx delta_rtx;
breg = XEXP (XEXP (src, 0), 0);
- delta_rtx = GET_CODE (XEXP (src, 0)) == PRE_INC
- ? GEN_INT (GET_MODE_SIZE (GET_MODE (src)))
- : GEN_INT (-GET_MODE_SIZE (GET_MODE (src)));
+ delta_rtx = (GET_CODE (XEXP (src, 0)) == PRE_INC
+ ? GEN_INT (GET_MODE_SIZE (GET_MODE (src)))
+ : GEN_INT (-GET_MODE_SIZE (GET_MODE (src))));
emit_insn (TARGET_32BIT
? gen_addsi3 (breg, breg, delta_rtx)
: gen_adddi3 (breg, breg, delta_rtx));
src = newsrc;
}
- /* We have now address involving an base register only.
- If we use one of the registers to address memory,
- we have change that register last. */
-
- breg = (GET_CODE (XEXP (src, 0)) == PLUS
- ? XEXP (XEXP (src, 0), 0)
- : XEXP (src, 0));
-
- if (!REG_P (breg))
- abort();
+ breg = XEXP (src, 0);
+ if (GET_CODE (breg) == PLUS || GET_CODE (breg) == LO_SUM)
+ breg = XEXP (breg, 0);
- if (REGNO (breg) >= REGNO (dst)
+ /* If the base register we are using to address memory is
+ also a destination reg, then change that register last. */
+ if (REG_P (breg)
+ && REGNO (breg) >= REGNO (dst)
&& REGNO (breg) < REGNO (dst) + nregs)
j = REGNO (breg) - REGNO (dst);
- }
+ }
if (GET_CODE (dst) == MEM && INT_REGNO_P (reg))
{
{
rtx delta_rtx;
breg = XEXP (XEXP (dst, 0), 0);
- delta_rtx = GET_CODE (XEXP (dst, 0)) == PRE_INC
- ? GEN_INT (GET_MODE_SIZE (GET_MODE (dst)))
- : GEN_INT (-GET_MODE_SIZE (GET_MODE (dst)));
+ delta_rtx = (GET_CODE (XEXP (dst, 0)) == PRE_INC
+ ? GEN_INT (GET_MODE_SIZE (GET_MODE (dst)))
+ : GEN_INT (-GET_MODE_SIZE (GET_MODE (dst))));
/* We have to update the breg before doing the store.
Use store with update, if available. */
{
rtx nsrc = simplify_gen_subreg (reg_mode, src, mode, 0);
emit_insn (TARGET_32BIT
- ? gen_movsi_update (breg, breg, delta_rtx, nsrc)
- : gen_movdi_update (breg, breg, delta_rtx, nsrc));
+ ? (TARGET_POWERPC64
+ ? gen_movdi_si_update (breg, breg, delta_rtx, nsrc)
+ : gen_movsi_update (breg, breg, delta_rtx, nsrc))
+ : gen_movdi_di_update (breg, breg, delta_rtx, nsrc));
used_update = true;
}
else
if (j == nregs)
j = 0;
- /* If compiler already emited move of first word by
+ /* If compiler already emitted move of first word by
store with update, no need to do anything. */
if (j == 0 && used_update)
continue;
}
/* For a very restricted set of circumstances, we can cut down the
- size of prologs/epilogs by calling our own save/restore-the-world
- routines. */
+ size of prologues/epilogues by calling our own save/restore-the-world
+ routines. */
static void
-compute_save_world_info(rs6000_stack_t *info_ptr)
-{
- info_ptr->world_save_p =
- (DEFAULT_ABI == ABI_DARWIN)
- && ! (current_function_calls_setjmp && flag_exceptions)
- && info_ptr->first_fp_reg_save == FIRST_SAVED_FP_REGNO
- && info_ptr->first_gp_reg_save == FIRST_SAVED_GP_REGNO
- && info_ptr->first_altivec_reg_save == FIRST_SAVED_ALTIVEC_REGNO
- && info_ptr->cr_save_p;
+compute_save_world_info (rs6000_stack_t *info_ptr)
+{
+ info_ptr->world_save_p = 1;
+ info_ptr->world_save_p
+ = (WORLD_SAVE_P (info_ptr)
+ && DEFAULT_ABI == ABI_DARWIN
+ && ! (current_function_calls_setjmp && flag_exceptions)
+ && info_ptr->first_fp_reg_save == FIRST_SAVED_FP_REGNO
+ && info_ptr->first_gp_reg_save == FIRST_SAVED_GP_REGNO
+ && info_ptr->first_altivec_reg_save == FIRST_SAVED_ALTIVEC_REGNO
+ && info_ptr->cr_save_p);
/* This will not work in conjunction with sibcalls. Make sure there
are none. (This check is expensive, but seldom executed.) */
- if ( info_ptr->world_save_p )
+ if (WORLD_SAVE_P (info_ptr))
{
rtx insn;
for ( insn = get_last_insn_anywhere (); insn; insn = PREV_INSN (insn))
- if ( GET_CODE (insn) == CALL_INSN
- && SIBLING_CALL_P (insn))
- {
- info_ptr->world_save_p = 0;
- break;
- }
+ if ( GET_CODE (insn) == CALL_INSN
+ && SIBLING_CALL_P (insn))
+ {
+ info_ptr->world_save_p = 0;
+ break;
+ }
}
- if (info_ptr->world_save_p)
+ if (WORLD_SAVE_P (info_ptr))
{
/* Even if we're not touching VRsave, make sure there's room on the
stack for it, if it looks like we're calling SAVE_WORLD, which
- will attempt to save it. */
+ will attempt to save it. */
info_ptr->vrsave_size = 4;
/* "Save" the VRsave register too if we're saving the world. */
if (info_ptr->vrsave_mask == 0)
- info_ptr->vrsave_mask = compute_vrsave_mask ();
+ info_ptr->vrsave_mask = compute_vrsave_mask ();
/* Because the Darwin register save/restore routines only handle
- F14 .. F31 and V20 .. V31 as per the ABI, perform a consistancy
- check and abort if there's something worng. */
+ F14 .. F31 and V20 .. V31 as per the ABI, perform a consistency
+ check and abort if there's something worng. */
if (info_ptr->first_fp_reg_save < FIRST_SAVED_FP_REGNO
- || info_ptr->first_altivec_reg_save < FIRST_SAVED_ALTIVEC_REGNO)
- abort ();
+ || info_ptr->first_altivec_reg_save < FIRST_SAVED_ALTIVEC_REGNO)
+ abort ();
}
return;
}
+---------------------------------------+
| Save area for VRSAVE register (Z) | 8+P+A+V+L+X+W+Y
+---------------------------------------+
- | SPE: area for 64-bit GP registers |
- +---------------------------------------+
- | SPE alignment padding |
- +---------------------------------------+
+ | SPE: area for 64-bit GP registers |
+ +---------------------------------------+
+ | SPE alignment padding |
+ +---------------------------------------+
| saved CR (C) | 8+P+A+V+L+X+W+Y+Z
+---------------------------------------+
| Save area for GP registers (G) | 8+P+A+V+L+X+W+Y+Z+C
rs6000_stack_t *info_ptr = &info;
int reg_size = TARGET_32BIT ? 4 : 8;
int ehrd_size;
+ int save_align;
HOST_WIDE_INT non_fixed_size;
/* Zero all fields portably. */
{
/* Cache value so we don't rescan instruction chain over and over. */
if (cfun->machine->insn_chain_scanned_p == 0)
- {
- cfun->machine->insn_chain_scanned_p = 1;
- info_ptr->spe_64bit_regs_used = (int) spe_func_has_64bit_regs_p ();
- }
+ cfun->machine->insn_chain_scanned_p
+ = spe_func_has_64bit_regs_p () + 1;
+ info_ptr->spe_64bit_regs_used = cfun->machine->insn_chain_scanned_p - 1;
}
/* Select which calling sequence. */
info_ptr->cr_save_offset = info_ptr->gp_save_offset - info_ptr->cr_size;
if (TARGET_SPE_ABI && info_ptr->spe_64bit_regs_used != 0)
- {
- /* Align stack so SPE GPR save area is aligned on a
- double-word boundary. */
- if (info_ptr->spe_gp_size != 0)
- info_ptr->spe_padding_size
- = 8 - (-info_ptr->cr_save_offset % 8);
- else
- info_ptr->spe_padding_size = 0;
-
- info_ptr->spe_gp_save_offset
- = info_ptr->cr_save_offset
- - info_ptr->spe_padding_size
- - info_ptr->spe_gp_size;
-
- /* Adjust for SPE case. */
- info_ptr->toc_save_offset
- = info_ptr->spe_gp_save_offset - info_ptr->toc_size;
- }
+ {
+ /* Align stack so SPE GPR save area is aligned on a
+ double-word boundary. */
+ if (info_ptr->spe_gp_size != 0)
+ info_ptr->spe_padding_size
+ = 8 - (-info_ptr->cr_save_offset % 8);
+ else
+ info_ptr->spe_padding_size = 0;
+
+ info_ptr->spe_gp_save_offset
+ = info_ptr->cr_save_offset
+ - info_ptr->spe_padding_size
+ - info_ptr->spe_gp_size;
+
+ /* Adjust for SPE case. */
+ info_ptr->toc_save_offset
+ = info_ptr->spe_gp_save_offset - info_ptr->toc_size;
+ }
else if (TARGET_ALTIVEC_ABI)
{
info_ptr->vrsave_save_offset
break;
}
+ save_align = (TARGET_ALTIVEC_ABI || DEFAULT_ABI == ABI_DARWIN) ? 16 : 8;
info_ptr->save_size = RS6000_ALIGN (info_ptr->fp_size
+ info_ptr->gp_size
+ info_ptr->altivec_size
+ info_ptr->lr_size
+ info_ptr->vrsave_size
+ info_ptr->toc_size,
- (TARGET_ALTIVEC_ABI || ABI_DARWIN)
- ? 16 : 8);
+ save_align);
non_fixed_size = (info_ptr->vars_size
+ info_ptr->parm_size
{
rtx i;
+ /* FIXME: This should be implemented with attributes...
+
+ (set_attr "spe64" "true")....then,
+ if (get_spe64(insn)) return true;
+
+ It's the only reliable way to do the stuff below. */
+
i = PATTERN (insn);
- if (GET_CODE (i) == SET
- && SPE_VECTOR_MODE (GET_MODE (SET_SRC (i))))
- return true;
+ if (GET_CODE (i) == SET)
+ {
+ enum machine_mode mode = GET_MODE (SET_SRC (i));
+
+ if (SPE_VECTOR_MODE (mode))
+ return true;
+ if (TARGET_E500_DOUBLE && mode == DFmode)
+ return true;
+ }
}
}
if (decl)
{
if (TARGET_ALTIVEC_VRSAVE)
- {
+ {
for (type = TYPE_ARG_TYPES (TREE_TYPE (decl));
type; type = TREE_CHAIN (type))
{
if (TREE_CODE (TREE_VALUE (type)) == VECTOR_TYPE)
return false;
}
- }
+ }
if (DEFAULT_ABI == ABI_DARWIN
|| (*targetm.binds_local_p) (decl))
{
: gen_reg_rtx (Pmode));
rtx symF;
- /* possibly create the toc section */
- if (! toc_initialized)
- {
- toc_section ();
- function_section (current_function_decl);
- }
-
if (fromprolog)
{
rtx symL;
rtx insn;
rtx stack_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
rtx tmp_reg = gen_rtx_REG (Pmode, 0);
- rtx todec = GEN_INT (-size);
+ rtx todec = gen_int_mode (-size, Pmode);
+
+ if (INTVAL (todec) != -size)
+ {
+ warning ("stack frame too large");
+ emit_insn (gen_trap ());
+ return;
+ }
if (current_function_limit_stack)
{
if (size > 32767)
{
/* Need a note here so that try_split doesn't get confused. */
- if (get_last_insn() == NULL_RTX)
+ if (get_last_insn () == NULL_RTX)
emit_note (NOTE_INSN_DELETED);
insn = emit_move_insn (tmp_reg, todec);
try_split (PATTERN (insn), insn, 0);
insn = emit_insn (TARGET_32BIT
? gen_movsi_update (stack_reg, stack_reg,
todec, stack_reg)
- : gen_movdi_update (stack_reg, stack_reg,
+ : gen_movdi_di_update (stack_reg, stack_reg,
todec, stack_reg));
}
else
/* Some cases that need register indexed addressing. */
if ((TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
+ || (TARGET_E500_DOUBLE && mode == DFmode)
|| (TARGET_SPE_ABI
&& SPE_VECTOR_MODE (mode)
&& !SPE_CONST_OFFSET_OK (offset)))
{
/* Whomever calls us must make sure r11 is available in the
- flow path of instructions in the prologue. */
+ flow path of instructions in the prologue. */
offset_rtx = gen_rtx_REG (Pmode, 11);
emit_move_insn (offset_rtx, int_rtx);
int_rtx = GEN_INT (offset);
- if (TARGET_SPE_ABI && SPE_VECTOR_MODE (mode))
+ if ((TARGET_SPE_ABI && SPE_VECTOR_MODE (mode))
+ || (TARGET_E500_DOUBLE && mode == DFmode))
{
offset_rtx = gen_rtx_REG (Pmode, FIXED_SCRATCH);
emit_move_insn (offset_rtx, int_rtx);
}
/* Handle world saves specially here. */
- if (info->world_save_p)
+ if (WORLD_SAVE_P (info))
{
int i, j, sz;
rtx treg;
/* save_world expects lr in r0. */
if (info->lr_save_p)
- {
- insn = emit_move_insn (gen_rtx_REG (Pmode, 0),
- gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
- RTX_FRAME_RELATED_P (insn) = 1;
- }
+ {
+ insn = emit_move_insn (gen_rtx_REG (Pmode, 0),
+ gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
/* The SAVE_WORLD and RESTORE_WORLD routines make a number of
- assumptions about the offsets of various bits of the stack
- frame. Abort if things aren't what they should be. */
+ assumptions about the offsets of various bits of the stack
+ frame. Abort if things aren't what they should be. */
if (info->gp_save_offset != -220
- || info->fp_save_offset != -144
- || info->lr_save_offset != 8
- || info->cr_save_offset != 4
- || !info->push_p
- || !info->lr_save_p
- || (current_function_calls_eh_return && info->ehrd_offset != -432)
- || (info->vrsave_save_offset != -224
- || info->altivec_save_offset != (-224 -16 -192)))
- abort ();
+ || info->fp_save_offset != -144
+ || info->lr_save_offset != 8
+ || info->cr_save_offset != 4
+ || !info->push_p
+ || !info->lr_save_p
+ || (current_function_calls_eh_return && info->ehrd_offset != -432)
+ || (info->vrsave_save_offset != -224
+ || info->altivec_save_offset != (-224 -16 -192)))
+ abort ();
treg = gen_rtx_REG (SImode, 11);
emit_move_insn (treg, GEN_INT (-info->total_size));
/* SAVE_WORLD takes the caller's LR in R0 and the frame size
- in R11. It also clobbers R12, so beware! */
+ in R11. It also clobbers R12, so beware! */
/* Preserve CR2 for save_world prologues */
sz = 6;
p = rtvec_alloc (sz);
j = 0;
RTVEC_ELT (p, j++) = gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_REG (Pmode,
- LINK_REGISTER_REGNUM));
+ gen_rtx_REG (Pmode,
+ LINK_REGISTER_REGNUM));
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode,
- gen_rtx_SYMBOL_REF (Pmode,
- "*save_world"));
+ gen_rtx_SYMBOL_REF (Pmode,
+ "*save_world"));
/* We do floats first so that the instruction pattern matches
- properly. */
- for (i = 0; i < 64 - info->first_fp_reg_save; i++)
- {
- rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->fp_save_offset
- + sp_offset + 8 * i));
- rtx mem = gen_rtx_MEM (DFmode, addr);
- set_mem_alias_set (mem, rs6000_sr_alias_set);
-
- RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
- }
+ properly. */
+ for (i = 0; i < 64 - info->first_fp_reg_save; i++)
+ {
+ rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->fp_save_offset
+ + sp_offset + 8 * i));
+ rtx mem = gen_rtx_MEM (DFmode, addr);
+ set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+ RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
+ }
for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++)
- {
- rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->altivec_save_offset
- + sp_offset + 16 * i));
- rtx mem = gen_rtx_MEM (V4SImode, addr);
- set_mem_alias_set (mem, rs6000_sr_alias_set);
-
- RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
- }
+ {
+ rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->altivec_save_offset
+ + sp_offset + 16 * i));
+ rtx mem = gen_rtx_MEM (V4SImode, addr);
+ set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+ RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
+ }
for (i = 0; i < 32 - info->first_gp_reg_save; i++)
- {
- rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->gp_save_offset
- + sp_offset + reg_size * i));
- rtx mem = gen_rtx_MEM (reg_mode, addr);
- set_mem_alias_set (mem, rs6000_sr_alias_set);
-
- RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
- }
-
- {
- /* CR register traditionally saved as CR2. */
- rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->cr_save_offset
- + sp_offset));
- rtx mem = gen_rtx_MEM (reg_mode, addr);
- set_mem_alias_set (mem, rs6000_sr_alias_set);
-
- RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
- }
+ {
+ rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->gp_save_offset
+ + sp_offset + reg_size * i));
+ rtx mem = gen_rtx_MEM (reg_mode, addr);
+ set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+ RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
+ }
+
+ {
+ /* CR register traditionally saved as CR2. */
+ rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->cr_save_offset
+ + sp_offset));
+ rtx mem = gen_rtx_MEM (reg_mode, addr);
+ set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+ RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
+ }
/* Prevent any attempt to delete the setting of r0 and treg! */
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 0));
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode, treg);
insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
- NULL_RTX, NULL_RTX);
+ NULL_RTX, NULL_RTX);
if (current_function_calls_eh_return)
- {
- unsigned int i;
- for (i = 0; ; ++i)
- {
- unsigned int regno = EH_RETURN_DATA_REGNO (i);
- if (regno == INVALID_REGNUM)
- break;
- emit_frame_save (frame_reg_rtx, frame_ptr_rtx, reg_mode, regno,
- info->ehrd_offset + sp_offset
- + reg_size * (int) i,
- info->total_size);
- }
- }
+ {
+ unsigned int i;
+ for (i = 0; ; ++i)
+ {
+ unsigned int regno = EH_RETURN_DATA_REGNO (i);
+ if (regno == INVALID_REGNUM)
+ break;
+ emit_frame_save (frame_reg_rtx, frame_ptr_rtx, reg_mode, regno,
+ info->ehrd_offset + sp_offset
+ + reg_size * (int) i,
+ info->total_size);
+ }
+ }
}
/* Save AltiVec registers if needed. */
- if (! info->world_save_p && TARGET_ALTIVEC_ABI && info->altivec_size != 0)
+ if (!WORLD_SAVE_P (info) && TARGET_ALTIVEC_ABI && info->altivec_size != 0)
{
int i;
epilogue. */
if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
- && ! info->world_save_p && info->vrsave_mask != 0)
+ && !WORLD_SAVE_P (info) && info->vrsave_mask != 0)
{
rtx reg, mem, vrsave;
int offset;
- /* Get VRSAVE onto a GPR. */
- reg = gen_rtx_REG (SImode, 12);
+ /* Get VRSAVE onto a GPR. Note that ABI_V4 might be using r12
+ as frame_reg_rtx and r11 as the static chain pointer for
+ nested functions. */
+ reg = gen_rtx_REG (SImode, 0);
vrsave = gen_rtx_REG (SImode, VRSAVE_REGNO);
if (TARGET_MACHO)
emit_insn (gen_get_vrsave_internal (reg));
}
/* If we use the link register, get it into r0. */
- if (! info->world_save_p && info->lr_save_p)
+ if (!WORLD_SAVE_P (info) && info->lr_save_p)
{
insn = emit_move_insn (gen_rtx_REG (Pmode, 0),
gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
}
/* If we need to save CR, put it into r12. */
- if (! info->world_save_p && info->cr_save_p && frame_reg_rtx != frame_ptr_rtx)
+ if (!WORLD_SAVE_P (info) && info->cr_save_p && frame_reg_rtx != frame_ptr_rtx)
{
rtx set;
/* Do any required saving of fpr's. If only one or two to save, do
it ourselves. Otherwise, call function. */
- if (! info->world_save_p && saving_FPRs_inline)
+ if (!WORLD_SAVE_P (info) && saving_FPRs_inline)
{
int i;
for (i = 0; i < 64 - info->first_fp_reg_save; i++)
info->fp_save_offset + sp_offset + 8 * i,
info->total_size);
}
- else if (! info->world_save_p && info->first_fp_reg_save != 64)
+ else if (!WORLD_SAVE_P (info) && info->first_fp_reg_save != 64)
{
int i;
char rname[30];
/* Save GPRs. This is done as a PARALLEL if we are using
the store-multiple instructions. */
- if (! info->world_save_p && using_store_multiple)
+ if (!WORLD_SAVE_P (info) && using_store_multiple)
{
rtvec p;
int i;
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
NULL_RTX, NULL_RTX);
}
- else if (! info->world_save_p)
+ else if (!WORLD_SAVE_P (info))
{
int i;
for (i = 0; i < 32 - info->first_gp_reg_save; i++)
/* ??? There's no need to emit actual instructions here, but it's the
easiest way to get the frame unwind information emitted. */
- if (! info->world_save_p && current_function_calls_eh_return)
+ if (!WORLD_SAVE_P (info) && current_function_calls_eh_return)
{
unsigned int i, regno;
}
/* Save lr if we used it. */
- if (! info->world_save_p && info->lr_save_p)
+ if (!WORLD_SAVE_P (info) && info->lr_save_p)
{
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
GEN_INT (info->lr_save_offset + sp_offset));
}
/* Save CR if we use any that must be preserved. */
- if (! info->world_save_p && info->cr_save_p)
+ if (!WORLD_SAVE_P (info) && info->cr_save_p)
{
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
GEN_INT (info->cr_save_offset + sp_offset));
/* Update stack and set back pointer unless this is V.4,
for which it was done previously. */
- if (! info->world_save_p && info->push_p
+ if (!WORLD_SAVE_P (info) && info->push_p
&& !(DEFAULT_ABI == ABI_V4 || current_function_calls_eh_return))
rs6000_emit_allocate_stack (info->total_size, FALSE);
if ((TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0)
|| (DEFAULT_ABI == ABI_V4 && flag_pic == 1
&& regs_ever_live[RS6000_PIC_OFFSET_TABLE_REGNUM]))
- {
- /* If emit_load_toc_table will use the link register, we need to save
- it. We use R12 for this purpose because emit_load_toc_table
- can use register 0. This allows us to use a plain 'blr' to return
- from the procedure more often. */
- int save_LR_around_toc_setup = (TARGET_ELF
- && DEFAULT_ABI != ABI_AIX
- && flag_pic
- && ! info->lr_save_p
- && EXIT_BLOCK_PTR->pred != NULL);
- if (save_LR_around_toc_setup)
- {
- rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM);
+ {
+ /* If emit_load_toc_table will use the link register, we need to save
+ it. We use R12 for this purpose because emit_load_toc_table
+ can use register 0. This allows us to use a plain 'blr' to return
+ from the procedure more often. */
+ int save_LR_around_toc_setup = (TARGET_ELF
+ && DEFAULT_ABI != ABI_AIX
+ && flag_pic
+ && ! info->lr_save_p
+ && EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0);
+ if (save_LR_around_toc_setup)
+ {
+ rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM);
- insn = emit_move_insn (frame_ptr_rtx, lr);
- rs6000_maybe_dead (insn);
- RTX_FRAME_RELATED_P (insn) = 1;
+ insn = emit_move_insn (frame_ptr_rtx, lr);
+ rs6000_maybe_dead (insn);
+ RTX_FRAME_RELATED_P (insn) = 1;
- rs6000_emit_load_toc_table (TRUE);
+ rs6000_emit_load_toc_table (TRUE);
- insn = emit_move_insn (lr, frame_ptr_rtx);
- rs6000_maybe_dead (insn);
- RTX_FRAME_RELATED_P (insn) = 1;
- }
- else
- rs6000_emit_load_toc_table (TRUE);
- }
+ insn = emit_move_insn (lr, frame_ptr_rtx);
+ rs6000_maybe_dead (insn);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ else
+ rs6000_emit_load_toc_table (TRUE);
+ }
#if TARGET_MACHO
if (DEFAULT_ABI == ABI_DARWIN
|| rs6000_cpu == PROCESSOR_PPC750
|| optimize_size);
- if (info->world_save_p)
+ if (WORLD_SAVE_P (info))
{
int i, j;
char rname[30];
rtvec p;
/* eh_rest_world_r10 will return to the location saved in the LR
- stack slot (which is not likely to be our caller.)
- Input: R10 -- stack adjustment. Clobbers R0, R11, R12, R7, R8.
- rest_world is similar, except any R10 parameter is ignored.
- The exception-handling stuff that was here in 2.95 is no
- longer necessary. */
+ stack slot (which is not likely to be our caller.)
+ Input: R10 -- stack adjustment. Clobbers R0, R11, R12, R7, R8.
+ rest_world is similar, except any R10 parameter is ignored.
+ The exception-handling stuff that was here in 2.95 is no
+ longer necessary. */
p = rtvec_alloc (9
+ 1
+ 32 - info->first_gp_reg_save
- + LAST_ALTIVEC_REGNO + 1 - info->first_altivec_reg_save
- + 63 + 1 - info->first_fp_reg_save);
+ + LAST_ALTIVEC_REGNO + 1 - info->first_altivec_reg_save
+ + 63 + 1 - info->first_fp_reg_save);
- strcpy (rname, (current_function_calls_eh_return) ?
- "*eh_rest_world_r10" : "*rest_world");
+ strcpy (rname, ((current_function_calls_eh_return) ?
+ "*eh_rest_world_r10" : "*rest_world"));
alloc_rname = ggc_strdup (rname);
j = 0;
RTVEC_ELT (p, j++) = gen_rtx_RETURN (VOIDmode);
RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode,
- gen_rtx_REG (Pmode,
- LINK_REGISTER_REGNUM));
+ gen_rtx_REG (Pmode,
+ LINK_REGISTER_REGNUM));
RTVEC_ELT (p, j++)
- = gen_rtx_USE (VOIDmode, gen_rtx_SYMBOL_REF (Pmode, alloc_rname));
+ = gen_rtx_USE (VOIDmode, gen_rtx_SYMBOL_REF (Pmode, alloc_rname));
/* The instruction pattern requires a clobber here;
- it is shared with the restVEC helper. */
+ it is shared with the restVEC helper. */
RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 11));
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 11));
{
- /* CR register traditionally saved as CR2. */
- rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->cr_save_offset));
- rtx mem = gen_rtx_MEM (reg_mode, addr);
- set_mem_alias_set (mem, rs6000_sr_alias_set);
-
- RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+ /* CR register traditionally saved as CR2. */
+ rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->cr_save_offset));
+ rtx mem = gen_rtx_MEM (reg_mode, addr);
+ set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+ RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
}
for (i = 0; i < 32 - info->first_gp_reg_save; i++)
- {
- rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->gp_save_offset
- + reg_size * i));
- rtx mem = gen_rtx_MEM (reg_mode, addr);
- set_mem_alias_set (mem, rs6000_sr_alias_set);
-
- RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
- }
+ {
+ rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->gp_save_offset
+ + reg_size * i));
+ rtx mem = gen_rtx_MEM (reg_mode, addr);
+ set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+ RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+ }
for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++)
- {
- rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->altivec_save_offset
- + 16 * i));
- rtx mem = gen_rtx_MEM (V4SImode, addr);
- set_mem_alias_set (mem, rs6000_sr_alias_set);
-
- RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
- }
+ {
+ rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->altivec_save_offset
+ + 16 * i));
+ rtx mem = gen_rtx_MEM (V4SImode, addr);
+ set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+ RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+ }
for (i = 0; info->first_fp_reg_save + i <= 63; i++)
- {
- rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->fp_save_offset
- + 8 * i));
- rtx mem = gen_rtx_MEM (DFmode, addr);
- set_mem_alias_set (mem, rs6000_sr_alias_set);
-
- RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
- }
+ {
+ rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->fp_save_offset
+ + 8 * i));
+ rtx mem = gen_rtx_MEM (DFmode, addr);
+ set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+ RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+ }
RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 0));
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 0));
RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 12));
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 12));
RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 7));
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 7));
RTVEC_ELT (p, j++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 8));
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 8));
RTVEC_ELT (p, j++)
- = gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, 10));
+ = gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, 10));
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
return;
|| current_function_calls_eh_return)
{
if (frame_reg_rtx != sp_reg_rtx)
- rs6000_emit_stack_tie ();
+ rs6000_emit_stack_tie ();
if (use_backchain_to_restore_sp)
{
else
{
size_t i;
- for (i = 0; i < sizeof(HOST_WIDE_INT)/sizeof(unsigned); i++)
+ for (i = 0; i < sizeof (HOST_WIDE_INT) / sizeof (unsigned); i++)
result = result * 613 + (unsigned) (XWINT (k, fidx)
>> CHAR_BIT * i);
}
to whether or not an object is a vtable. */
#define VTABLE_NAME_P(NAME) \
- (strncmp ("_vt.", name, strlen("_vt.")) == 0 \
+ (strncmp ("_vt.", name, strlen ("_vt.")) == 0 \
|| strncmp ("_ZTV", name, strlen ("_ZTV")) == 0 \
|| strncmp ("_ZTT", name, strlen ("_ZTT")) == 0 \
|| strncmp ("_ZTI", name, strlen ("_ZTI")) == 0 \
void * * found;
/* Create toc_hash_table. This can't be done at OVERRIDE_OPTIONS
- time because GGC is not initialized at that point. */
+ time because GGC is not initialized at that point. */
if (toc_hash_table == NULL)
toc_hash_table = htab_create_ggc (1021, toc_hash_function,
toc_hash_eq, NULL);
}
#else
{
- low = INTVAL (x) & 0xffffffff;
- high = (HOST_WIDE_INT) INTVAL (x) >> 32;
+ low = INTVAL (x) & 0xffffffff;
+ high = (HOST_WIDE_INT) INTVAL (x) >> 32;
}
#endif
void
rs6000_gen_section_name (char **buf, const char *filename,
- const char *section_desc)
+ const char *section_desc)
{
const char *q, *after_last_slash, *last_period = 0;
char *p;
for (q = after_last_slash; *q; q++)
{
if (q == last_period)
- {
+ {
strcpy (p, section_desc);
p += strlen (section_desc);
break;
- }
+ }
else if (ISALNUM (*q))
- *p++ = *q;
+ *p++ = *q;
}
if (last_period == 0)
if (rs6000_sched_groups)
{
if (is_microcoded_insn (insn))
- return 0;
+ return 0;
else if (is_cracked_insn (insn))
- return more > 2 ? more - 2 : 0;
+ return more > 2 ? more - 2 : 0;
}
return more - 1;
a dependency LINK or INSN on DEP_INSN. COST is the current cost. */
static int
-rs6000_adjust_cost (rtx insn, rtx link, rtx dep_insn ATTRIBUTE_UNUSED,
- int cost)
+rs6000_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
{
if (! recog_memoized (insn))
return 0;
{
/* Data dependency; DEP_INSN writes a register that INSN reads
some cycles later. */
+
+ /* Separate a load from a narrower, dependent store. */
+ if (rs6000_sched_groups
+ && GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (PATTERN (dep_insn)) == SET
+ && GET_CODE (XEXP (PATTERN (insn), 1)) == MEM
+ && GET_CODE (XEXP (PATTERN (dep_insn), 0)) == MEM
+ && (GET_MODE_SIZE (GET_MODE (XEXP (PATTERN (insn), 1)))
+ > GET_MODE_SIZE (GET_MODE (XEXP (PATTERN (dep_insn), 0)))))
+ return cost + 14;
+
switch (get_attr_type (insn))
{
case TYPE_JMPREG:
|| type == TYPE_LOAD_UX
|| type == TYPE_STORE_UX
|| type == TYPE_MFCR)
- return true;
+ return true;
}
return false;
{
enum attr_type type = get_attr_type (insn);
if (type == TYPE_LOAD_U || type == TYPE_STORE_U
- || type == TYPE_FPLOAD_U || type == TYPE_FPSTORE_U
- || type == TYPE_FPLOAD_UX || type == TYPE_FPSTORE_UX
- || type == TYPE_LOAD_EXT || type == TYPE_DELAYED_CR
- || type == TYPE_COMPARE || type == TYPE_DELAYED_COMPARE
- || type == TYPE_IMUL_COMPARE || type == TYPE_LMUL_COMPARE
- || type == TYPE_IDIV || type == TYPE_LDIV
- || type == TYPE_INSERT_WORD)
- return true;
+ || type == TYPE_FPLOAD_U || type == TYPE_FPSTORE_U
+ || type == TYPE_FPLOAD_UX || type == TYPE_FPSTORE_UX
+ || type == TYPE_LOAD_EXT || type == TYPE_DELAYED_CR
+ || type == TYPE_COMPARE || type == TYPE_DELAYED_COMPARE
+ || type == TYPE_IMUL_COMPARE || type == TYPE_LMUL_COMPARE
+ || type == TYPE_IDIV || type == TYPE_LDIV
+ || type == TYPE_INSERT_WORD)
+ return true;
}
return false;
&& rs6000_sched_restricted_insns_priority)
{
- /* Prioritize insns that can be dispatched only in the first dispatch slot. */
+ /* Prioritize insns that can be dispatched only in the first
+ dispatch slot. */
if (rs6000_sched_restricted_insns_priority == 1)
/* Attach highest priority to insn. This means that in
haifa-sched.c:ready_sort(), dispatch-slot restriction considerations
return current_sched_info->sched_max_insns_priority;
else if (rs6000_sched_restricted_insns_priority == 2)
/* Increase priority of insn by a minimal amount. This means that in
- haifa-sched.c:ready_sort(), only 'priority' (critical path) considerations
- precede dispatch-slot restriction considerations. */
+ haifa-sched.c:ready_sort(), only 'priority' (critical path)
+ considerations precede dispatch-slot restriction considerations. */
return (priority + 1);
}
costly by the given target. */
static bool
-rs6000_is_costly_dependence (rtx insn, rtx next, rtx link, int cost, int distance)
+rs6000_is_costly_dependence (rtx insn, rtx next, rtx link, int cost,
+ int distance)
{
/* If the flag is not enbled - no dependence is considered costly;
allow all dependent insns in the same group.
&& is_load_insn (next)
&& is_store_insn (insn)
&& (!link || (int) REG_NOTE_KIND (link) == 0))
- /* Prevent load after store in the same group if it is a true dependence. */
+ /* Prevent load after store in the same group if it is a true
+ dependence. */
return true;
/* The flag is set to X; dependences with latency >= X are considered costly,
while (next_insn
&& next_insn != tail
- && (GET_CODE(next_insn) == NOTE
+ && (GET_CODE (next_insn) == NOTE
|| GET_CODE (PATTERN (next_insn)) == USE
|| GET_CODE (PATTERN (next_insn)) == CLOBBER))
{
if (which_group == current_group)
{
if (is_branch_slot_insn (insn))
- return true;
+ return true;
return false;
}
else if (which_group == previous_group)
{
if (is_dispatch_slot_restricted (insn))
- return true;
+ return true;
return false;
}
{
rtx insn = group_insns[i];
if (!insn)
- continue;
+ continue;
for (link = INSN_DEPEND (insn); link != 0; link = XEXP (link, 1))
- {
- rtx next = XEXP (link, 0);
- if (next == next_insn)
- {
- cost = insn_cost (insn, link, next_insn);
- if (rs6000_is_costly_dependence (insn, next_insn, link, cost, 0))
- return true;
- }
- }
+ {
+ rtx next = XEXP (link, 0);
+ if (next == next_insn)
+ {
+ cost = insn_cost (insn, link, next_insn);
+ if (rs6000_is_costly_dependence (insn, next_insn, link, cost, 0))
+ return true;
+ }
+ }
}
return false;
last group, and how many dispatch groups were encountered so far). */
static int
-force_new_group (int sched_verbose, FILE *dump, rtx *group_insns, rtx next_insn,
- bool *group_end, int can_issue_more, int *group_count)
+force_new_group (int sched_verbose, FILE *dump, rtx *group_insns,
+ rtx next_insn, bool *group_end, int can_issue_more,
+ int *group_count)
{
rtx nop;
bool force;
if (sched_verbose > 6)
fprintf (dump,"force: group count = %d, can_issue_more = %d\n",
- *group_count ,can_issue_more);
+ *group_count ,can_issue_more);
if (rs6000_sched_insert_nops == sched_finish_regroup_exact)
{
if (*group_end)
- can_issue_more = 0;
+ can_issue_more = 0;
/* Since only a branch can be issued in the last issue_slot, it is
sufficient to insert 'can_issue_more - 1' nops if next_insn is not
a branch. If next_insn is a branch, we insert 'can_issue_more' nops;
- in this case the last nop will start a new group and the branch will be
- forced to the new group. */
+ in this case the last nop will start a new group and the branch
+ will be forced to the new group. */
if (can_issue_more && !is_branch_slot_insn (next_insn))
- can_issue_more--;
+ can_issue_more--;
while (can_issue_more > 0)
- {
- nop = gen_nop();
- emit_insn_before (nop, next_insn);
- can_issue_more--;
- }
+ {
+ nop = gen_nop ();
+ emit_insn_before (nop, next_insn);
+ can_issue_more--;
+ }
*group_end = true;
return 0;
int n_nops = rs6000_sched_insert_nops;
/* Nops can't be issued from the branch slot, so the effective
- issue_rate for nops is 'issue_rate - 1'. */
+ issue_rate for nops is 'issue_rate - 1'. */
if (can_issue_more == 0)
- can_issue_more = issue_rate;
+ can_issue_more = issue_rate;
can_issue_more--;
if (can_issue_more == 0)
- {
- can_issue_more = issue_rate - 1;
- (*group_count)++;
- end = true;
- for (i = 0; i < issue_rate; i++)
- {
- group_insns[i] = 0;
- }
- }
+ {
+ can_issue_more = issue_rate - 1;
+ (*group_count)++;
+ end = true;
+ for (i = 0; i < issue_rate; i++)
+ {
+ group_insns[i] = 0;
+ }
+ }
while (n_nops > 0)
- {
- nop = gen_nop ();
- emit_insn_before (nop, next_insn);
- if (can_issue_more == issue_rate - 1) /* new group begins */
- end = false;
- can_issue_more--;
- if (can_issue_more == 0)
- {
- can_issue_more = issue_rate - 1;
- (*group_count)++;
- end = true;
- for (i = 0; i < issue_rate; i++)
- {
- group_insns[i] = 0;
- }
- }
- n_nops--;
- }
+ {
+ nop = gen_nop ();
+ emit_insn_before (nop, next_insn);
+ if (can_issue_more == issue_rate - 1) /* new group begins */
+ end = false;
+ can_issue_more--;
+ if (can_issue_more == 0)
+ {
+ can_issue_more = issue_rate - 1;
+ (*group_count)++;
+ end = true;
+ for (i = 0; i < issue_rate; i++)
+ {
+ group_insns[i] = 0;
+ }
+ }
+ n_nops--;
+ }
/* Scale back relative to 'issue_rate' (instead of 'issue_rate - 1'). */
can_issue_more++;
- *group_end = /* Is next_insn going to start a new group? */
- (end
+ /* Is next_insn going to start a new group? */
+ *group_end
+ = (end
|| (can_issue_more == 1 && !is_branch_slot_insn (next_insn))
|| (can_issue_more <= 2 && is_cracked_insn (next_insn))
|| (can_issue_more < issue_rate &&
- insn_terminates_group_p (next_insn, previous_group)));
+ insn_terminates_group_p (next_insn, previous_group)));
if (*group_end && end)
- (*group_count)--;
+ (*group_count)--;
if (sched_verbose > 6)
- fprintf (dump, "done force: group count = %d, can_issue_more = %d\n",
- *group_count, can_issue_more);
+ fprintf (dump, "done force: group count = %d, can_issue_more = %d\n",
+ *group_count, can_issue_more);
return can_issue_more;
}
slot = (issue_rate - can_issue_more);
group_insns[slot] = insn;
can_issue_more =
- rs6000_variable_issue (dump, sched_verbose, insn, can_issue_more);
+ rs6000_variable_issue (dump, sched_verbose, insn, can_issue_more);
if (insn_terminates_group_p (insn, current_group))
- can_issue_more = 0;
+ can_issue_more = 0;
next_insn = get_next_active_insn (insn, tail);
if (next_insn == NULL_RTX)
- return group_count + 1;
+ return group_count + 1;
- group_end = /* Is next_insn going to start a new group? */
- (can_issue_more == 0
- || (can_issue_more == 1 && !is_branch_slot_insn (next_insn))
- || (can_issue_more <= 2 && is_cracked_insn (next_insn))
- || (can_issue_more < issue_rate &&
- insn_terminates_group_p (next_insn, previous_group)));
+ /* Is next_insn going to start a new group? */
+ group_end
+ = (can_issue_more == 0
+ || (can_issue_more == 1 && !is_branch_slot_insn (next_insn))
+ || (can_issue_more <= 2 && is_cracked_insn (next_insn))
+ || (can_issue_more < issue_rate &&
+ insn_terminates_group_p (next_insn, previous_group)));
can_issue_more = force_new_group (sched_verbose, dump, group_insns,
- next_insn, &group_end, can_issue_more, &group_count);
+ next_insn, &group_end, can_issue_more,
+ &group_count);
if (group_end)
- {
- group_count++;
- can_issue_more = 0;
- for (i = 0; i < issue_rate; i++)
- {
- group_insns[i] = 0;
- }
- }
+ {
+ group_count++;
+ can_issue_more = 0;
+ for (i = 0; i < issue_rate; i++)
+ {
+ group_insns[i] = 0;
+ }
+ }
if (GET_MODE (next_insn) == TImode && can_issue_more)
- PUT_MODE(next_insn, VOIDmode);
+ PUT_MODE (next_insn, VOIDmode);
else if (!can_issue_more && GET_MODE (next_insn) != TImode)
- PUT_MODE (next_insn, TImode);
+ PUT_MODE (next_insn, TImode);
insn = next_insn;
if (can_issue_more == 0)
- can_issue_more = issue_rate;
- } /* while */
+ can_issue_more = issue_rate;
+ } /* while */
return group_count;
}
group_end = (next_insn == NULL_RTX || GET_MODE (next_insn) == TImode);
if (next_insn == NULL_RTX)
- break;
+ break;
if (group_end)
- {
- /* If the scheduler had marked group termination at this location
- (between insn and next_indn), and neither insn nor next_insn will
- force group termination, pad the group with nops to force group
- termination. */
- if (can_issue_more
- && (rs6000_sched_insert_nops == sched_finish_pad_groups)
- && !insn_terminates_group_p (insn, current_group)
- && !insn_terminates_group_p (next_insn, previous_group))
- {
- if (!is_branch_slot_insn(next_insn))
- can_issue_more--;
-
- while (can_issue_more)
- {
- nop = gen_nop ();
- emit_insn_before (nop, next_insn);
- can_issue_more--;
- }
- }
-
- can_issue_more = issue_rate;
- group_count++;
- }
+ {
+ /* If the scheduler had marked group termination at this location
+ (between insn and next_indn), and neither insn nor next_insn will
+ force group termination, pad the group with nops to force group
+ termination. */
+ if (can_issue_more
+ && (rs6000_sched_insert_nops == sched_finish_pad_groups)
+ && !insn_terminates_group_p (insn, current_group)
+ && !insn_terminates_group_p (next_insn, previous_group))
+ {
+ if (!is_branch_slot_insn (next_insn))
+ can_issue_more--;
+
+ while (can_issue_more)
+ {
+ nop = gen_nop ();
+ emit_insn_before (nop, next_insn);
+ can_issue_more--;
+ }
+ }
+
+ can_issue_more = issue_rate;
+ group_count++;
+ }
insn = next_insn;
next_insn = get_next_active_insn (insn, tail);
if (reload_completed && rs6000_sched_groups)
{
if (rs6000_sched_insert_nops == sched_finish_none)
- return;
+ return;
if (rs6000_sched_insert_nops == sched_finish_pad_groups)
- n_groups = pad_groups (dump, sched_verbose,
- current_sched_info->prev_head,
- current_sched_info->next_tail);
+ n_groups = pad_groups (dump, sched_verbose,
+ current_sched_info->prev_head,
+ current_sched_info->next_tail);
else
- n_groups = redefine_groups (dump, sched_verbose,
- current_sched_info->prev_head,
- current_sched_info->next_tail);
+ n_groups = redefine_groups (dump, sched_verbose,
+ current_sched_info->prev_head,
+ current_sched_info->next_tail);
if (sched_verbose >= 6)
{
{ "altivec", 1, 1, false, true, false, rs6000_handle_altivec_attribute },
{ "longcall", 0, 0, false, true, true, rs6000_handle_longcall_attribute },
{ "shortcall", 0, 0, false, true, true, rs6000_handle_longcall_attribute },
+#ifdef SUBTARGET_ATTRIBUTE_TABLE
+ SUBTARGET_ATTRIBUTE_TABLE,
+#endif
{ NULL, 0, 0, false, false, false, NULL }
};
given declaration. */
static tree
-rs6000_handle_altivec_attribute (tree *node, tree name, tree args,
+rs6000_handle_altivec_attribute (tree *node,
+ tree name ATTRIBUTE_UNUSED,
+ tree args,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
mode = TYPE_MODE (type);
- if (rs6000_warn_altivec_long
- && (type == long_unsigned_type_node || type == long_integer_type_node))
- warning ("use of 'long' in AltiVec types is deprecated; use 'int'");
+ /* Check for invalid AltiVec type qualifiers. */
+ if (type == long_unsigned_type_node || type == long_integer_type_node)
+ {
+ if (TARGET_64BIT)
+ error ("use of %<long%> in AltiVec types is invalid for 64-bit code");
+ else if (rs6000_warn_altivec_long)
+ warning ("use of %<long%> in AltiVec types is deprecated; use %<int%>");
+ }
+ else if (type == long_long_unsigned_type_node
+ || type == long_long_integer_type_node)
+ error ("use of %<long long%> in AltiVec types is invalid");
+ else if (type == double_type_node)
+ error ("use of %<double%> in AltiVec types is invalid");
+ else if (type == long_double_type_node)
+ error ("use of %<long double%> in AltiVec types is invalid");
+ else if (type == boolean_type_node)
+ error ("use of boolean types in AltiVec types is invalid");
+ else if (TREE_CODE (type) == COMPLEX_TYPE)
+ error ("use of %<complex%> in AltiVec types is invalid");
switch (altivec_type)
{
unsigned_p = TYPE_UNSIGNED (type);
switch (mode)
{
- case SImode:
- result = (unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node);
- break;
- case HImode:
- result = (unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node);
- break;
- case QImode:
- result = (unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node);
- break;
- case SFmode: result = V4SF_type_node; break;
- /* If the user says 'vector int bool', we may be handed the 'bool'
- attribute _before_ the 'vector' attribute, and so select the proper
- type in the 'b' case below. */
- case V4SImode: case V8HImode: case V16QImode: result = type;
- default: break;
+ case SImode:
+ result = (unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node);
+ break;
+ case HImode:
+ result = (unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node);
+ break;
+ case QImode:
+ result = (unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node);
+ break;
+ case SFmode: result = V4SF_type_node; break;
+ /* If the user says 'vector int bool', we may be handed the 'bool'
+ attribute _before_ the 'vector' attribute, and so select the
+ proper type in the 'b' case below. */
+ case V4SImode: case V8HImode: case V16QImode: case V4SFmode:
+ result = type;
+ default: break;
}
break;
case 'b':
switch (mode)
{
- case SImode: case V4SImode: result = bool_V4SI_type_node; break;
- case HImode: case V8HImode: result = bool_V8HI_type_node; break;
- case QImode: case V16QImode: result = bool_V16QI_type_node;
- default: break;
+ case SImode: case V4SImode: result = bool_V4SI_type_node; break;
+ case HImode: case V8HImode: result = bool_V8HI_type_node; break;
+ case QImode: case V16QImode: result = bool_V16QI_type_node;
+ default: break;
}
break;
case 'p':
switch (mode)
{
- case V8HImode: result = pixel_V8HI_type_node;
- default: break;
+ case V8HImode: result = pixel_V8HI_type_node;
+ default: break;
}
default: break;
}
*no_add_attrs = true; /* No need to hang on to the attribute. */
- if (!result)
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
- else
+ if (result)
*node = reconstruct_complex_type (*node, result);
return NULL_TREE;
&& TREE_CODE (*node) != FIELD_DECL
&& TREE_CODE (*node) != TYPE_DECL)
{
- warning ("`%s' attribute only applies to functions",
+ warning ("%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
/* Return a reference suitable for calling a function with the
longcall attribute. */
-struct rtx_def *
+rtx
rs6000_longcall_ref (rtx call_ref)
{
const char *call_name;
if (rs6000_sdata == SDATA_NONE)
return false;
+ /* We want to merge strings, so we never consider them small data. */
+ if (TREE_CODE (decl) == STRING_CST)
+ return false;
+
+ /* Functions are never in the small data area. */
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ return false;
+
if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl))
{
const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
register by this routine since our caller will try to
increment the returned register via an "la" instruction. */
-struct rtx_def *
+rtx
find_addr_reg (rtx addr)
{
while (GET_CODE (addr) == PLUS)
#if TARGET_MACHO
-#if 0
-/* Returns 1 if OP is either a symbol reference or a sum of a symbol
- reference and a constant. */
-
-int
-symbolic_operand (rtx op)
-{
- switch (GET_CODE (op))
- {
- case SYMBOL_REF:
- case LABEL_REF:
- return 1;
- case CONST:
- op = XEXP (op, 0);
- return (GET_CODE (op) == SYMBOL_REF ||
- (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
- || GET_CODE (XEXP (op, 0)) == LABEL_REF)
- && GET_CODE (XEXP (op, 1)) == CONST_INT);
- default:
- return 0;
- }
-}
-#endif
-
-#if TARGET_MACHO
-
static tree branch_island_list = 0;
/* Remember to generate a branch island for far calls to the given
function. */
static void
-add_compiler_branch_island (tree label_name, tree function_name, int line_number)
+add_compiler_branch_island (tree label_name, tree function_name,
+ int line_number)
{
tree branch_island = build_tree_list (function_name, label_name);
TREE_TYPE (branch_island) = build_int_cst (NULL_TREE, line_number);
strcat (tmp_buf, label);
#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
- fprintf (asm_out_file, "\t.stabd 68,0," HOST_WIDE_INT_PRINT_UNSIGNED "\n",
- BRANCH_ISLAND_LINE_NUMBER(branch_island));
+ dbxout_stabd (N_SLINE, BRANCH_ISLAND_LINE_NUMBER (branch_island));
#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
if (flag_pic)
{
output_asm_insn (tmp_buf, 0);
#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
- fprintf(asm_out_file, "\t.stabd 68,0," HOST_WIDE_INT_PRINT_UNSIGNED "\n",
- BRANCH_ISLAND_LINE_NUMBER (branch_island));
+ dbxout_stabd (N_SLINE, BRANCH_ISLAND_LINE_NUMBER (branch_island));
#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
}
CALL_DEST is the routine we are calling. */
char *
-output_call (rtx insn, rtx *operands, int dest_operand_number, int cookie_operand_number)
+output_call (rtx insn, rtx *operands, int dest_operand_number,
+ int cookie_operand_number)
{
static char buf[256];
if (GET_CODE (operands[dest_operand_number]) == SYMBOL_REF
return buf;
}
-#endif /* TARGET_MACHO */
-
/* Generate PIC and indirect symbol stubs. */
void
fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
label++;
- local_label_0 = alloca (sizeof("\"L0000000000$spb\""));
+ local_label_0 = alloca (sizeof ("\"L0000000000$spb\""));
sprintf (local_label_0, "\"L%011d$spb\"", label);
fprintf (file, "\tmflr r0\n");
fprintf (file, "\taddis r11,r11,ha16(%s-%s)\n",
lazy_ptr_name, local_label_0);
fprintf (file, "\tmtlr r0\n");
- fprintf (file, "\tlwzu r12,lo16(%s-%s)(r11)\n",
+ fprintf (file, "\t%s r12,lo16(%s-%s)(r11)\n",
+ (TARGET_64BIT ? "ldu" : "lwzu"),
lazy_ptr_name, local_label_0);
fprintf (file, "\tmtctr r12\n");
fprintf (file, "\tbctr\n");
fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
fprintf (file, "\tlis r11,ha16(%s)\n", lazy_ptr_name);
- fprintf (file, "\tlwzu r12,lo16(%s)(r11)\n", lazy_ptr_name);
+ fprintf (file, "\t%s r12,lo16(%s)(r11)\n",
+ (TARGET_64BIT ? "ldu" : "lwzu"),
+ lazy_ptr_name);
fprintf (file, "\tmtctr r12\n");
fprintf (file, "\tbctr\n");
}
position-independent addresses go into a reg. This is REG if non
zero, otherwise we allocate register(s) as necessary. */
-#define SMALL_INT(X) ((unsigned) (INTVAL(X) + 0x8000) < 0x10000)
+#define SMALL_INT(X) ((unsigned) (INTVAL (X) + 0x8000) < 0x10000)
rtx
rs6000_machopic_legitimize_pic_address (rtx orig, enum machine_mode mode,
{
}
+/* Output a .machine directive for the Darwin assembler, and call
+ the generic start_file routine. */
+
+static void
+rs6000_darwin_file_start (void)
+{
+ static const struct
+ {
+ const char *arg;
+ const char *name;
+ int if_set;
+ } mapping[] = {
+ { "ppc64", "ppc64", MASK_64BIT },
+ { "970", "ppc970", MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64 },
+ { "power4", "ppc970", 0 },
+ { "G5", "ppc970", 0 },
+ { "7450", "ppc7450", 0 },
+ { "7400", "ppc7400", MASK_ALTIVEC },
+ { "G4", "ppc7400", 0 },
+ { "750", "ppc750", 0 },
+ { "740", "ppc750", 0 },
+ { "G3", "ppc750", 0 },
+ { "604e", "ppc604e", 0 },
+ { "604", "ppc604", 0 },
+ { "603e", "ppc603", 0 },
+ { "603", "ppc603", 0 },
+ { "601", "ppc601", 0 },
+ { NULL, "ppc", 0 } };
+ const char *cpu_id = "";
+ size_t i;
+
+ rs6000_file_start ();
+
+ /* Determine the argument to -mcpu=. Default to G3 if not specified. */
+ for (i = 0; i < ARRAY_SIZE (rs6000_select); i++)
+ if (rs6000_select[i].set_arch_p && rs6000_select[i].string
+ && rs6000_select[i].string[0] != '\0')
+ cpu_id = rs6000_select[i].string;
+
+ /* Look through the mapping array. Pick the first name that either
+ matches the argument, has a bit set in IF_SET that is also set
+ in the target flags, or has a NULL name. */
+
+ i = 0;
+ while (mapping[i].arg != NULL
+ && strcmp (mapping[i].arg, cpu_id) != 0
+ && (mapping[i].if_set & target_flags) == 0)
+ i++;
+
+ fprintf (asm_out_file, "\t.machine %s\n", mapping[i].name);
+}
+
#endif /* TARGET_MACHO */
#if TARGET_ELF
if (priority != DEFAULT_INIT_PRIORITY)
{
sprintf (buf, ".ctors.%.5u",
- /* Invert the numbering so the linker puts us in the proper
- order; constructors are run from right to left, and the
- linker sorts in increasing order. */
- MAX_INIT_PRIORITY - priority);
+ /* Invert the numbering so the linker puts us in the proper
+ order; constructors are run from right to left, and the
+ linker sorts in increasing order. */
+ MAX_INIT_PRIORITY - priority);
section = buf;
}
if (priority != DEFAULT_INIT_PRIORITY)
{
sprintf (buf, ".dtors.%.5u",
- /* Invert the numbering so the linker puts us in the proper
- order; constructors are run from right to left, and the
- linker sorts in increasing order. */
- MAX_INIT_PRIORITY - priority);
+ /* Invert the numbering so the linker puts us in the proper
+ order; constructors are run from right to left, and the
+ linker sorts in increasing order. */
+ MAX_INIT_PRIORITY - priority);
section = buf;
}
}
static void
-rs6000_xcoff_asm_named_section (const char *name, unsigned int flags)
+rs6000_xcoff_asm_named_section (const char *name, unsigned int flags,
+ tree decl ATTRIBUTE_UNUSED)
{
int smclass;
static const char * const suffix[3] = { "PR", "RO", "RW" };
static void
rs6000_xcoff_select_section (tree decl, int reloc,
- unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
+ unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
if (decl_readonly_section_1 (decl, reloc, 1))
{
if (TREE_PUBLIC (decl))
- read_only_data_section ();
+ read_only_data_section ();
else
- read_only_private_data_section ();
+ read_only_private_data_section ();
}
else
{
if (TREE_PUBLIC (decl))
- data_section ();
+ data_section ();
else
- private_data_section ();
+ private_data_section ();
}
}
static void
rs6000_xcoff_select_rtx_section (enum machine_mode mode, rtx x,
- unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
+ unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (x, mode))
toc_section ();
fputs ("\t.file\t", asm_out_file);
output_quoted_string (asm_out_file, main_input_filename);
fputc ('\n', asm_out_file);
- toc_section ();
if (write_symbols != NO_DEBUG)
private_data_section ();
text_section ();
scanned. In either case, *TOTAL contains the cost result. */
static bool
-rs6000_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED,
- int *total)
+rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
{
enum machine_mode mode = GET_MODE (x);
|| outer_code == MINUS)
&& (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
|| CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')))
- || ((outer_code == IOR || outer_code == XOR)
- && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
- || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')))
- || ((outer_code == DIV || outer_code == UDIV
- || outer_code == MOD || outer_code == UMOD)
- && exact_log2 (INTVAL (x)) >= 0)
|| (outer_code == AND
&& (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
- || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')
+ || (CONST_OK_FOR_LETTER_P (INTVAL (x),
+ mode == SImode ? 'L' : 'J'))
|| mask_operand (x, VOIDmode)))
+ || ((outer_code == IOR || outer_code == XOR)
+ && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
+ || (CONST_OK_FOR_LETTER_P (INTVAL (x),
+ mode == SImode ? 'L' : 'J'))))
|| outer_code == ASHIFT
|| outer_code == ASHIFTRT
|| outer_code == LSHIFTRT
|| outer_code == ZERO_EXTRACT
|| (outer_code == MULT
&& CONST_OK_FOR_LETTER_P (INTVAL (x), 'I'))
+ || ((outer_code == DIV || outer_code == UDIV
+ || outer_code == MOD || outer_code == UMOD)
+ && exact_log2 (INTVAL (x)) >= 0)
|| (outer_code == COMPARE
&& (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
- || CONST_OK_FOR_LETTER_P (INTVAL (x), 'K'))))
+ || CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')))
+ || (outer_code == EQ
+ && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
+ || CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
+ || (CONST_OK_FOR_LETTER_P (INTVAL (x),
+ mode == SImode ? 'L' : 'J'))))
+ || (outer_code == GTU
+ && CONST_OK_FOR_LETTER_P (INTVAL (x), 'I'))
+ || (outer_code == LTU
+ && CONST_OK_FOR_LETTER_P (INTVAL (x), 'P')))
{
*total = 0;
return true;
case MEM:
/* When optimizing for size, MEM should be slightly more expensive
than generating address, e.g., (plus (reg) (const)).
- L1 cache latecy is about two instructions. */
+ L1 cache latency is about two instructions. */
*total = optimize_size ? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (2);
return true;
*total += COSTS_N_INSNS (1);
}
else
- *total = COSTS_N_INSNS (1);
+ *total = COSTS_N_INSNS (1);
return false;
case MULT:
- if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (x, 1)), 'I'))
{
if (INTVAL (XEXP (x, 1)) >= -256
&& INTVAL (XEXP (x, 1)) <= 255)
/* Shift */
*total = COSTS_N_INSNS (1);
}
- else
+ else
{
if (GET_MODE (XEXP (x, 1)) == DImode)
*total = rs6000_cost->divdi;
*total = rs6000_cost->fp;
return false;
}
+ break;
+
+ case EQ:
+ case GTU:
+ case LTU:
+ /* Carry bit requires mode == Pmode.
+ NEG or PLUS already counted so only add one. */
+ if (mode == Pmode
+ && (outer_code == NEG || outer_code == PLUS))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ if (outer_code == SET)
+ {
+ if (XEXP (x, 1) == const0_rtx)
+ {
+ *total = COSTS_N_INSNS (2);
+ return true;
+ }
+ else if (mode == Pmode)
+ {
+ *total = COSTS_N_INSNS (3);
+ return false;
+ }
+ }
+ /* FALLTHRU */
+ case GT:
+ case LT:
+ case UNORDERED:
+ if (outer_code == SET && (XEXP (x, 1) == const0_rtx))
+ {
+ *total = COSTS_N_INSNS (2);
+ return true;
+ }
+ /* CC COMPARE. */
+ if (outer_code == COMPARE)
+ {
+ *total = 0;
+ return true;
+ }
break;
default:
return (rs6000_memory_move_cost (mode, from, 0)
+ rs6000_memory_move_cost (mode, GENERAL_REGS, 0));
-/* It's more expensive to move CR_REGS than CR0_REGS because of the shift.... */
+ /* It's more expensive to move CR_REGS than CR0_REGS because of the
+ shift. */
else if (from == CR_REGS)
return 4;
else
-/* A move will cost one instruction per GPR moved. */
+ /* A move will cost one instruction per GPR moved. */
return 2 * HARD_REGNO_NREGS (0, mode);
}
-/* Moving between two similar registers is just one instruction. */
+ /* Moving between two similar registers is just one instruction. */
else if (reg_classes_intersect_p (to, from))
return mode == TFmode ? 4 : 2;
-/* Everything else has to go through GENERAL_REGS. */
+ /* Everything else has to go through GENERAL_REGS. */
else
return (rs6000_register_move_cost (mode, GENERAL_REGS, to)
+ rs6000_register_move_cost (mode, from, GENERAL_REGS));
enum machine_mode mode;
unsigned int regno;
+ /* Special handling for structs in darwin64. */
+ if (rs6000_darwin64_abi
+ && TYPE_MODE (valtype) == BLKmode
+ && TREE_CODE (valtype) == RECORD_TYPE
+ && int_size_in_bytes (valtype) > 0)
+ {
+ CUMULATIVE_ARGS valcum;
+ rtx valret;
+
+ valcum.words = 0;
+ valcum.fregno = FP_ARG_MIN_REG;
+ valcum.vregno = ALTIVEC_ARG_MIN_REG;
+ /* Do a trial code generation as if this were going to be passed as
+ an argument; if any part goes in memory, we return NULL. */
+ valret = rs6000_darwin64_record_arg (&valcum, valtype, 1, true);
+ if (valret)
+ return valret;
+ /* Otherwise fall through to standard ABI rules. */
+ }
+
if (TARGET_32BIT && TARGET_POWERPC64 && TYPE_MODE (valtype) == DImode)
{
/* Long long return value need be split in -mpowerpc64, 32bit ABI. */
return rs6000_complex_function_value (mode);
else if (TREE_CODE (valtype) == VECTOR_TYPE
&& TARGET_ALTIVEC && TARGET_ALTIVEC_ABI
- && ALTIVEC_VECTOR_MODE(mode))
+ && ALTIVEC_VECTOR_MODE (mode))
regno = ALTIVEC_ARG_RETURN;
+ else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
+ && (mode == DFmode || mode == DCmode))
+ return spe_build_register_parallel (mode, GP_ARG_RETURN);
else
regno = GP_ARG_RETURN;
{
unsigned int regno;
+ if (TARGET_32BIT && TARGET_POWERPC64 && mode == DImode)
+ {
+ /* Long long return value need be split in -mpowerpc64, 32bit ABI. */
+ return gen_rtx_PARALLEL (DImode,
+ gen_rtvec (2,
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (SImode, GP_ARG_RETURN),
+ const0_rtx),
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (SImode,
+ GP_ARG_RETURN + 1),
+ GEN_INT (4))));
+ }
+
if (GET_MODE_CLASS (mode) == MODE_FLOAT
&& TARGET_HARD_FLOAT && TARGET_FPRS)
regno = FP_ARG_RETURN;
regno = ALTIVEC_ARG_RETURN;
else if (COMPLEX_MODE_P (mode) && targetm.calls.split_complex_arg)
return rs6000_complex_function_value (mode);
+ else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
+ && (mode == DFmode || mode == DCmode))
+ return spe_build_register_parallel (mode, GP_ARG_RETURN);
else
regno = GP_ARG_RETURN;
{
unsigned regno;
- if (!TARGET_SPE || !SPE_VECTOR_MODE (GET_MODE (reg)))
+ if (TARGET_SPE
+ && (SPE_VECTOR_MODE (GET_MODE (reg))
+ || (TARGET_E500_DOUBLE && GET_MODE (reg) == DFmode)))
+ ;
+ else
return NULL_RTX;
regno = REGNO (reg);
return false;
}
+/* Target hook for invalid_arg_for_unprototyped_fn. */
+static const char *
+invalid_arg_for_unprototyped_fn (tree typelist, tree funcdecl, tree val)
+{
+ return (!rs6000_darwin64_abi
+ && typelist == 0
+ && TREE_CODE (TREE_TYPE (val)) == VECTOR_TYPE
+ && (funcdecl == NULL_TREE
+ || (TREE_CODE (funcdecl) == FUNCTION_DECL
+ && DECL_BUILT_IN_CLASS (funcdecl) != BUILT_IN_MD)))
+ ? N_("AltiVec argument passed to unprototyped function")
+ : NULL;
+}
+
#include "gt-rs6000.h"