/* 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. Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "rtl.h" #include "regs.h" #include "hard-reg-set.h" #include "real.h" #include "insn-config.h" #include "conditions.h" #include "insn-attr.h" #include "flags.h" #include "recog.h" #include "obstack.h" #include "tree.h" #include "expr.h" #include "optabs.h" #include "except.h" #include "function.h" #include "output.h" #include "basic-block.h" #include "integrate.h" #include "toplev.h" #include "ggc.h" #include "hashtab.h" #include "tm_p.h" #include "target.h" #include "target-def.h" #include "langhooks.h" #include "reload.h" #include "cfglayout.h" #include "sched-int.h" #if TARGET_XCOFF #include "xcoffout.h" /* get declarations of xcoff_*_section_name */ #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)) /* Structure used to define the rs6000 stack */ typedef struct rs6000_stack { int first_gp_reg_save; /* first callee saved GP register used */ int first_fp_reg_save; /* first callee saved FP register used */ int first_altivec_reg_save; /* first callee saved AltiVec register used */ int lr_save_p; /* true if the link reg needs to be saved */ int cr_save_p; /* true if the CR reg needs to be saved */ unsigned int vrsave_mask; /* mask of vec registers to save */ 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 */ enum rs6000_abi abi; /* which ABI to use */ int gp_save_offset; /* offset to save GP regs from initial SP */ int fp_save_offset; /* offset to save FP regs from initial SP */ int altivec_save_offset; /* offset to save AltiVec regs from initial SP */ int lr_save_offset; /* offset to save LR from initial SP */ int cr_save_offset; /* offset to save CR from initial SP */ int vrsave_save_offset; /* offset to save VRSAVE from initial SP */ int spe_gp_save_offset; /* offset to save spe 64-bit gprs */ int toc_save_offset; /* offset to save the TOC pointer */ int varargs_save_offset; /* offset to save the varargs registers */ int ehrd_offset; /* offset to EH return data */ int reg_size; /* register size (4 or 8) */ int varargs_size; /* size to hold V.4 args passed in regs */ HOST_WIDE_INT vars_size; /* variable save area size */ int parm_size; /* outgoing parameter size */ int save_size; /* save area size */ int fixed_size; /* fixed size of stack frame */ int gp_size; /* size of saved GP registers */ int fp_size; /* size of saved FP registers */ int altivec_size; /* size of saved AltiVec registers */ int cr_size; /* size to hold CR if not in save_size */ int lr_size; /* size to hold LR if not in save_size */ int vrsave_size; /* size to hold VRSAVE if not in save_size */ int altivec_padding_size; /* size of altivec alignment padding if not in save_size */ int spe_gp_size; /* size of 64-bit GPR save size for SPE */ int spe_padding_size; int toc_size; /* size to hold TOC if not in save_size */ HOST_WIDE_INT total_size; /* total bytes allocated for stack */ int spe_64bit_regs_used; } rs6000_stack_t; /* Target cpu type */ enum processor_type rs6000_cpu; struct rs6000_cpu_select rs6000_select[3] = { /* switch name, tune arch */ { (const char *)0, "--with-cpu=", 1, 1 }, { (const char *)0, "-mcpu=", 1, 1 }, { (const char *)0, "-mtune=", 1, 0 }, }; /* Always emit branch hint bits. */ static GTY(()) bool rs6000_always_hint; /* Schedule instructions for group formation. */ static GTY(()) bool rs6000_sched_groups; /* Support adjust_priority scheduler hook and -mprioritize-restricted-insns= option. */ const char *rs6000_sched_restricted_insns_priority_str; int rs6000_sched_restricted_insns_priority; /* Support for -msched-costly-dep option. */ const char *rs6000_sched_costly_dep_str; enum rs6000_dependence_cost rs6000_sched_costly_dep; /* Support for -minsert-sched-nops option. */ const char *rs6000_sched_insert_nops_str; enum rs6000_nop_insertion rs6000_sched_insert_nops; /* Size of long double */ const char *rs6000_long_double_size_string; int rs6000_long_double_type_size; /* Whether -mabi=altivec has appeared. */ int rs6000_altivec_abi; /* Whether VRSAVE instructions should be generated. */ int rs6000_altivec_vrsave; /* String from -mvrsave= option. */ const char *rs6000_altivec_vrsave_string; /* Nonzero if we want SPE ABI extensions. */ int rs6000_spe_abi; /* Whether isel instructions should be generated. */ int rs6000_isel; /* Whether SPE simd instructions should be generated. */ int rs6000_spe; /* Nonzero if floating point operations are done in the GPRs. */ int rs6000_float_gprs = 0; /* String from -mfloat-gprs=. */ const char *rs6000_float_gprs_string; /* String from -misel=. */ const char *rs6000_isel_string; /* String from -mspe=. */ const char *rs6000_spe_string; /* Set to nonzero once AIX common-mode calls have been defined. */ static GTY(()) int common_mode_defined; /* Save information from a "cmpxx" operation until the branch or scc is emitted. */ rtx rs6000_compare_op0, rs6000_compare_op1; int rs6000_compare_fp_p; /* Label number of label created for -mrelocatable, to call to so we can get the address of the GOT section */ int rs6000_pic_labelno; #ifdef USING_ELFOS_H /* Which abi to adhere to */ const char *rs6000_abi_name; /* Semantics of the small data area */ enum rs6000_sdata_type rs6000_sdata = SDATA_DATA; /* Which small data model to use */ const char *rs6000_sdata_name = (char *)0; /* Counter for labels which are to be placed in .fixup. */ int fixuplabelno = 0; #endif /* Bit size of immediate TLS offsets and string from which it is decoded. */ int rs6000_tls_size = 32; const char *rs6000_tls_size_string; /* ABI enumeration available for subtarget to use. */ enum rs6000_abi rs6000_current_abi; /* ABI string from -mabi= option. */ const char *rs6000_abi_string; /* Debug flags */ const char *rs6000_debug_name; int rs6000_debug_stack; /* debug stack applications */ int rs6000_debug_arg; /* debug argument handling */ /* Opaque types. */ static GTY(()) tree opaque_V2SI_type_node; static GTY(()) tree opaque_V2SF_type_node; static GTY(()) tree opaque_p_V2SI_type_node; int rs6000_warn_altivec_long = 1; /* On by default. */ const char *rs6000_warn_altivec_long_switch; int rs6000_testabi = 0; /* Off by default. */ const char *rs6000_testabi_switch; int rs6000_dd2_sched = 0; /* Off by default. */ const char *rs6000_dd2_sched_switch; int rs6000_gen_microcode = 1; /* On by default. On for CELLPPU !-Os */ const char *rs6000_gen_microcode_switch; int rs6000_warn_microcode = 0; /* Off by default */ const char *rs6000_warn_microcode_switch; const char *rs6000_traceback_name; static enum { traceback_default = 0, traceback_none, traceback_part, traceback_full } rs6000_traceback; /* Flag to say the TOC is initialized */ int toc_initialized; char toc_label_name[10]; /* Alias set for saves and restores from the rs6000 stack. */ static GTY(()) int rs6000_sr_alias_set; /* 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. */ int rs6000_default_long_calls; const char *rs6000_longcall_switch; /* Control alignment for fields within structures. */ /* String from -malign-XXXXX. */ const char *rs6000_alignment_string; int rs6000_alignment_flags; struct builtin_description { /* mask is not const because we're going to alter it below. This nonsense will go away when we rewrite the -march infrastructure to give us more target flag bits. */ unsigned int mask; const enum insn_code icode; const char *const name; const enum rs6000_builtins code; }; 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 void rs6000_frame_related (rtx, rtx, HOST_WIDE_INT, rtx, rtx); static rtx spe_synthesize_frame_save (rtx); static bool spe_func_has_64bit_regs_p (void); static void emit_frame_save (rtx, rtx, enum machine_mode, unsigned int, int, HOST_WIDE_INT); static rtx gen_frame_mem_offset (enum machine_mode, rtx, int); static void rs6000_emit_allocate_stack (HOST_WIDE_INT, int); static unsigned rs6000_hash_constant (rtx); static unsigned toc_hash_function (const 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_offset_address_p (enum machine_mode, rtx, int); 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); #ifdef HAVE_GAS_HIDDEN static void rs6000_assemble_visibility (tree, int); #endif static int rs6000_ra_ever_killed (void); static tree rs6000_handle_longcall_attribute (tree *, tree, tree, int, bool *); static tree rs6000_handle_altivec_attribute (tree *, tree, tree, int, bool *); extern const struct attribute_spec rs6000_attribute_table[]; static void rs6000_set_default_type_attributes (tree); static void rs6000_output_function_prologue (FILE *, HOST_WIDE_INT); static void rs6000_output_function_epilogue (FILE *, HOST_WIDE_INT); static void rs6000_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); static rtx rs6000_emit_set_long_const (rtx, HOST_WIDE_INT, HOST_WIDE_INT); static bool rs6000_return_in_memory (tree, tree); static void rs6000_file_start (void); #if TARGET_ELF static unsigned int rs6000_elf_section_type_flags (tree, const char *, int); static void rs6000_elf_asm_out_constructor (rtx, int); static void rs6000_elf_asm_out_destructor (rtx, int); static void rs6000_elf_select_section (tree, int, unsigned HOST_WIDE_INT); static void rs6000_elf_unique_section (tree, int); static void rs6000_elf_select_rtx_section (enum machine_mode, rtx, unsigned HOST_WIDE_INT); static void rs6000_elf_encode_section_info (tree, rtx, int) ATTRIBUTE_UNUSED; static bool rs6000_elf_in_small_data_p (tree); #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_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, unsigned HOST_WIDE_INT); static const char * rs6000_xcoff_strip_name_encoding (const char *); static unsigned int rs6000_xcoff_section_type_flags (tree, const char *, int); static void rs6000_xcoff_file_start (void); static void rs6000_xcoff_file_end (void); #endif #if TARGET_MACHO static bool rs6000_binds_local_p (tree); #endif static int rs6000_use_dfa_pipeline_interface (void); static int rs6000_dfa_new_cycle (FILE *, int, rtx, int, int, int *, int *); static int rs6000_variable_issue (FILE *, int, rtx, int); static bool rs6000_rtx_costs (rtx, int, int, int *); static int rs6000_adjust_cost (rtx, rtx, rtx, int); static bool is_microcoded_insn (rtx); static bool is_nonpipeline_insn (rtx); static int is_dispatch_slot_restricted (rtx); static bool is_cracked_insn (rtx); static bool is_branch_slot_insn (rtx); static int rs6000_adjust_priority (rtx, int); static int rs6000_issue_rate (void); static bool rs6000_is_costly_dependence (rtx, rtx, rtx, int, int); static rtx get_next_active_insn (rtx, rtx); static bool insn_terminates_group_p (rtx , enum group_termination); static bool is_costly_group (rtx *, rtx); static int force_new_group (int, FILE *, rtx *, rtx, bool *, int, int *); static int redefine_groups (FILE *, int, rtx, rtx); static int pad_groups (FILE *, int, rtx, rtx); static void rs6000_sched_finish (FILE *, int); static int rs6000_use_sched_lookahead (void); static void rs6000_init_builtins (void); static rtx altivec_copy_to_mode_reg (enum machine_mode, rtx); static rtx rs6000_expand_unop_builtin (enum insn_code, tree, rtx); static rtx rs6000_expand_binop_builtin (enum insn_code, tree, rtx); static rtx rs6000_expand_ternop_builtin (enum insn_code, tree, rtx); static rtx rs6000_expand_builtin (tree, rtx, rtx, enum machine_mode, int); static void altivec_init_builtins (void); static void rs6000_common_init_builtins (void); static int altivec_comptypes (tree t1, tree t2); static tree rs6000_select_overloaded_builtin (tree, tree); static void rs6000_init_libfuncs (void); static void enable_mask_for_builtins (struct builtin_description *, int, enum rs6000_builtins, enum rs6000_builtins); static void spe_init_builtins (void); static rtx spe_expand_builtin (tree, rtx, bool *); static rtx spe_expand_stv_builtin (enum insn_code, tree); static rtx spe_expand_predicate_builtin (enum insn_code, tree, rtx); static rtx spe_expand_evsel_builtin (enum insn_code, tree, rtx); static int rs6000_emit_int_cmove (rtx, rtx, rtx, rtx); static rs6000_stack_t *rs6000_stack_info (void); static void debug_stack_info (rs6000_stack_t *); static rtx altivec_expand_builtin (tree, rtx, bool *); static rtx altivec_expand_ld_builtin (tree, rtx, bool *); static rtx altivec_expand_st_builtin (tree, rtx, bool *); 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); 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 int first_altivec_reg_to_save (void); static unsigned int compute_vrsave_mask (void); 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 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 rtx rs6000_mixed_function_arg (CUMULATIVE_ARGS *, 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 *, enum machine_mode, tree, int *, int); #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); #endif static tree rs6000_build_builtin_va_list (void); /* Hash table stuff for keeping track of TOC entries. */ struct toc_hash_struct GTY(()) { /* `key' will satisfy CONSTANT_P; in fact, it will satisfy ASM_OUTPUT_SPECIAL_POOL_ENTRY_P. */ rtx key; enum machine_mode key_mode; int labelno; }; static GTY ((param_is (struct toc_hash_struct))) htab_t toc_hash_table; /* Default register names. */ char rs6000_reg_names[][8] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "mq", "lr", "ctr","ap", "0", "1", "2", "3", "4", "5", "6", "7", "xer", /* AltiVec registers. */ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "vrsave", "vscr", /* SPE registers. */ "spe_acc", "spefscr" }; #ifdef TARGET_REGNAMES static const char alt_reg_names[][8] = { "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23", "%r24", "%r25", "%r26", "%r27", "%r28", "%r29", "%r30", "%r31", "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31", "mq", "lr", "ctr", "ap", "%cr0", "%cr1", "%cr2", "%cr3", "%cr4", "%cr5", "%cr6", "%cr7", "xer", /* AltiVec registers. */ "%v0", "%v1", "%v2", "%v3", "%v4", "%v5", "%v6", "%v7", "%v8", "%v9", "%v10", "%v11", "%v12", "%v13", "%v14", "%v15", "%v16", "%v17", "%v18", "%v19", "%v20", "%v21", "%v22", "%v23", "%v24", "%v25", "%v26", "%v27", "%v28", "%v29", "%v30", "%v31", "vrsave", "vscr", /* SPE registers. */ "spe_acc", "spefscr" }; #endif #ifndef MASK_STRICT_ALIGN #define MASK_STRICT_ALIGN 0 #endif #ifndef TARGET_PROFILE_KERNEL #define TARGET_PROFILE_KERNEL 0 #endif /* 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) /* Initialize the GCC target structure. */ #undef TARGET_ATTRIBUTE_TABLE #define TARGET_ATTRIBUTE_TABLE rs6000_attribute_table #undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES #define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES rs6000_set_default_type_attributes #undef TARGET_ASM_ALIGNED_DI_OP #define TARGET_ASM_ALIGNED_DI_OP DOUBLE_INT_ASM_OP /* Default unaligned ops are only provided for ELF. Find the ops needed for non-ELF systems. */ #ifndef OBJECT_FORMAT_ELF #if TARGET_XCOFF /* For XCOFF. rs6000_assemble_integer will handle unaligned DIs on 64-bit targets. */ #undef TARGET_ASM_UNALIGNED_HI_OP #define TARGET_ASM_UNALIGNED_HI_OP "\t.vbyte\t2," #undef TARGET_ASM_UNALIGNED_SI_OP #define TARGET_ASM_UNALIGNED_SI_OP "\t.vbyte\t4," #undef TARGET_ASM_UNALIGNED_DI_OP #define TARGET_ASM_UNALIGNED_DI_OP "\t.vbyte\t8," #else /* For Darwin. */ #undef TARGET_ASM_UNALIGNED_HI_OP #define TARGET_ASM_UNALIGNED_HI_OP "\t.short\t" #undef TARGET_ASM_UNALIGNED_SI_OP #define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t" #endif #endif /* This hook deals with fixups for relocatable code and DI-mode objects in 64-bit code. */ #undef TARGET_ASM_INTEGER #define TARGET_ASM_INTEGER rs6000_assemble_integer #ifdef HAVE_GAS_HIDDEN #undef TARGET_ASM_ASSEMBLE_VISIBILITY #define TARGET_ASM_ASSEMBLE_VISIBILITY rs6000_assemble_visibility #endif #undef TARGET_HAVE_TLS #define TARGET_HAVE_TLS HAVE_AS_TLS #undef TARGET_CANNOT_FORCE_CONST_MEM #define TARGET_CANNOT_FORCE_CONST_MEM rs6000_tls_referenced_p #undef TARGET_ASM_FUNCTION_PROLOGUE #define TARGET_ASM_FUNCTION_PROLOGUE rs6000_output_function_prologue #undef TARGET_ASM_FUNCTION_EPILOGUE #define TARGET_ASM_FUNCTION_EPILOGUE rs6000_output_function_epilogue #undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE #define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE rs6000_use_dfa_pipeline_interface #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD rs6000_use_sched_lookahead #undef TARGET_SCHED_DFA_NEW_CYCLE #define TARGET_SCHED_DFA_NEW_CYCLE rs6000_dfa_new_cycle #undef TARGET_SCHED_VARIABLE_ISSUE #define TARGET_SCHED_VARIABLE_ISSUE rs6000_variable_issue #undef TARGET_SCHED_ISSUE_RATE #define TARGET_SCHED_ISSUE_RATE rs6000_issue_rate #undef TARGET_SCHED_ADJUST_COST #define TARGET_SCHED_ADJUST_COST rs6000_adjust_cost #undef TARGET_SCHED_ADJUST_PRIORITY #define TARGET_SCHED_ADJUST_PRIORITY rs6000_adjust_priority #undef TARGET_SCHED_IS_COSTLY_DEPENDENCE #define TARGET_SCHED_IS_COSTLY_DEPENDENCE rs6000_is_costly_dependence #undef TARGET_SCHED_FINISH #define TARGET_SCHED_FINISH rs6000_sched_finish #undef TARGET_INIT_BUILTINS #define TARGET_INIT_BUILTINS rs6000_init_builtins #undef TARGET_EXPAND_BUILTIN #define TARGET_EXPAND_BUILTIN rs6000_expand_builtin #undef TARGET_SELECT_OVERLOADED_BUILTIN #define TARGET_SELECT_OVERLOADED_BUILTIN rs6000_select_overloaded_builtin #undef TARGET_INIT_LIBFUNCS #define TARGET_INIT_LIBFUNCS rs6000_init_libfuncs #if TARGET_MACHO #undef TARGET_BINDS_LOCAL_P #define TARGET_BINDS_LOCAL_P rs6000_binds_local_p #endif #undef TARGET_ASM_OUTPUT_MI_THUNK #define TARGET_ASM_OUTPUT_MI_THUNK rs6000_output_mi_thunk #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true #undef TARGET_FUNCTION_OK_FOR_SIBCALL #define TARGET_FUNCTION_OK_FOR_SIBCALL rs6000_function_ok_for_sibcall #undef TARGET_RTX_COSTS #define TARGET_RTX_COSTS rs6000_rtx_costs #undef TARGET_ADDRESS_COST #define TARGET_ADDRESS_COST hook_int_rtx_0 #undef TARGET_VECTOR_OPAQUE_P #define TARGET_VECTOR_OPAQUE_P is_ev64_opaque_type #undef TARGET_DWARF_REGISTER_SPAN #define TARGET_DWARF_REGISTER_SPAN rs6000_dwarf_register_span /* On rs6000, function arguments are promoted, as are function return values. */ #undef TARGET_PROMOTE_FUNCTION_ARGS #define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true #undef TARGET_PROMOTE_FUNCTION_RETURN #define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true /* Structure return values are passed as an extra parameter. */ #undef TARGET_STRUCT_VALUE_RTX #define TARGET_STRUCT_VALUE_RTX hook_rtx_tree_int_null #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY rs6000_return_in_memory #undef TARGET_SETUP_INCOMING_VARARGS #define TARGET_SETUP_INCOMING_VARARGS setup_incoming_varargs /* Always strict argument naming on rs6000. */ #undef TARGET_STRICT_ARGUMENT_NAMING #define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true #undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED #define TARGET_PRETEND_OUTGOING_VARARGS_NAMED hook_bool_CUMULATIVE_ARGS_true #undef TARGET_SPLIT_COMPLEX_ARG #define TARGET_SPLIT_COMPLEX_ARG hook_bool_tree_true #undef TARGET_BUILD_BUILTIN_VA_LIST #define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list struct gcc_target targetm = TARGET_INITIALIZER; /* Override command line options. Mostly we process the processor type and sometimes adjust other TARGET_ options. */ void rs6000_override_options (const char *default_cpu) { size_t i, j; struct rs6000_cpu_select *ptr; int set_masks; /* Simplifications for entries below. */ enum { POWERPC_BASE_MASK = MASK_POWERPC | MASK_NEW_MNEMONICS, POWERPC_7400_MASK = POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_ALTIVEC }; /* This table occasionally claims that a processor does not support a particular feature even though it does, but the feature is slower than the alternative. Thus, it shouldn't be relied on as a complete description of the processor's support. Please keep this list in order, and don't forget to update the documentation in invoke.texi when adding a new processor or flag. */ static struct ptt { const char *const name; /* Canonical processor name. */ const enum processor_type processor; /* Processor type enum value. */ const int target_enable; /* Target flags to enable. */ } const processor_target_table[] = {{"401", PROCESSOR_PPC403, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, {"403", PROCESSOR_PPC403, POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_STRICT_ALIGN}, {"405", PROCESSOR_PPC405, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, {"405fp", PROCESSOR_PPC405, POWERPC_BASE_MASK}, {"440", PROCESSOR_PPC440, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, {"440fp", PROCESSOR_PPC440, POWERPC_BASE_MASK}, {"505", PROCESSOR_MPCCORE, POWERPC_BASE_MASK}, {"601", PROCESSOR_PPC601, MASK_POWER | POWERPC_BASE_MASK | MASK_MULTIPLE | MASK_STRING}, {"602", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"603", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"603e", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"604", PROCESSOR_PPC604, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"604e", PROCESSOR_PPC604e, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"620", PROCESSOR_PPC620, POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64}, {"630", PROCESSOR_PPC630, POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64}, {"740", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"7400", PROCESSOR_PPC7400, POWERPC_7400_MASK}, {"7450", PROCESSOR_PPC7450, POWERPC_7400_MASK}, {"750", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"801", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, {"821", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, {"823", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, {"8540", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, {"970", PROCESSOR_POWER4, POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64}, {"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS}, {"ec603e", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, {"G3", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT}, {"G4", PROCESSOR_PPC7450, POWERPC_7400_MASK}, {"G5", PROCESSOR_POWER4, POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64}, {"power", PROCESSOR_POWER, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, {"power2", PROCESSOR_POWER, MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING}, {"power3", PROCESSOR_PPC630, POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64}, {"power4", PROCESSOR_POWER4, POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_MFCRF | MASK_POWERPC64}, {"power5", PROCESSOR_POWER5, POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_MFCRF | MASK_POWERPC64}, {"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK}, {"powerpc64", PROCESSOR_POWERPC64, POWERPC_BASE_MASK | MASK_POWERPC64}, {"cellppu", PROCESSOR_CELLPPU, POWERPC_BASE_MASK | MASK_POWERPC64 }, {"rios", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, {"rios1", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, {"rios2", PROCESSOR_RIOS2, MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING}, {"rsc", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, {"rsc1", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, {"rs64a", PROCESSOR_RS64A, POWERPC_BASE_MASK | MASK_POWERPC64}, }; const size_t ptt_size = ARRAY_SIZE (processor_target_table); /* Some OSs don't support saving the high part of 64-bit registers on context switch. Other OSs don't support saving Altivec registers. On those OSs, we don't touch the MASK_POWERPC64 or MASK_ALTIVEC settings; if the user wants either, the user must explicitly specify them and we won't interfere with the user's specification. */ enum { POWER_MASKS = MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING, POWERPC_MASKS = (POWERPC_BASE_MASK | MASK_PPC_GPOPT | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_ALTIVEC | MASK_MFCRF) }; set_masks = POWER_MASKS | POWERPC_MASKS | MASK_SOFT_FLOAT; #ifdef OS_MISSING_POWERPC64 if (OS_MISSING_POWERPC64) set_masks &= ~MASK_POWERPC64; #endif #ifdef OS_MISSING_ALTIVEC if (OS_MISSING_ALTIVEC) 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)); /* Identify the processor type. */ rs6000_select[0].string = default_cpu; rs6000_cpu = TARGET_POWERPC64 ? PROCESSOR_DEFAULT64 : PROCESSOR_DEFAULT; for (i = 0; i < ARRAY_SIZE (rs6000_select); i++) { ptr = &rs6000_select[i]; if (ptr->string != (char *)0 && ptr->string[0] != '\0') { for (j = 0; j < ptt_size; j++) if (! strcmp (ptr->string, processor_target_table[j].name)) { if (ptr->set_tune_p) rs6000_cpu = processor_target_table[j].processor; if (ptr->set_arch_p) { target_flags &= ~set_masks; target_flags |= (processor_target_table[j].target_enable & set_masks); } break; } if (j == ptt_size) error ("bad value (%s) for %s switch", ptr->string, ptr->name); } } if (TARGET_E500) rs6000_isel = 1; /* If we are optimizing big endian systems for space, use the load/store multiple and string instructions. */ if (BYTES_BIG_ENDIAN && optimize_size) target_flags |= ~target_flags_explicit & (MASK_MULTIPLE | MASK_STRING); /* Don't allow -mmultiple or -mstring on little endian systems unless the cpu is a 750, because the hardware doesn't support the instructions used in little endian mode, and causes an alignment trap. The 750 does not cause an alignment trap (except when the target is unaligned). */ if (!BYTES_BIG_ENDIAN && rs6000_cpu != PROCESSOR_PPC750) { if (TARGET_MULTIPLE) { target_flags &= ~MASK_MULTIPLE; if ((target_flags_explicit & MASK_MULTIPLE) != 0) warning ("-mmultiple is not supported on little endian systems"); } if (TARGET_STRING) { target_flags &= ~MASK_STRING; if ((target_flags_explicit & MASK_STRING) != 0) warning ("-mstring is not supported on little endian systems"); } } /* Set debug flags */ if (rs6000_debug_name) { if (! strcmp (rs6000_debug_name, "all")) rs6000_debug_stack = rs6000_debug_arg = 1; else if (! strcmp (rs6000_debug_name, "stack")) rs6000_debug_stack = 1; else if (! strcmp (rs6000_debug_name, "arg")) rs6000_debug_arg = 1; else error ("unknown -mdebug-%s switch", rs6000_debug_name); } if (rs6000_traceback_name) { if (! strncmp (rs6000_traceback_name, "full", 4)) rs6000_traceback = traceback_full; else if (! strncmp (rs6000_traceback_name, "part", 4)) rs6000_traceback = traceback_part; else if (! strncmp (rs6000_traceback_name, "no", 2)) rs6000_traceback = traceback_none; else error ("unknown -mtraceback arg `%s'; expecting `full', `partial' or `none'", rs6000_traceback_name); } /* Set size of long double */ rs6000_long_double_type_size = 64; if (rs6000_long_double_size_string) { char *tail; int size = strtol (rs6000_long_double_size_string, &tail, 10); if (*tail != '\0' || (size != 64 && size != 128)) error ("Unknown switch -mlong-double-%s", rs6000_long_double_size_string); else rs6000_long_double_type_size = size; } /* Set Altivec ABI as default for powerpc64 linux. */ if (TARGET_ELF && TARGET_64BIT) { rs6000_altivec_abi = 1; rs6000_altivec_vrsave = 1; } /* Handle -mabi= options. */ rs6000_parse_abi_options (); /* Handle -malign-XXXXX option. */ rs6000_parse_alignment_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 SUBTARGET_OVERRIDE_OPTIONS SUBTARGET_OVERRIDE_OPTIONS; #endif #ifdef SUBSUBTARGET_OVERRIDE_OPTIONS SUBSUBTARGET_OVERRIDE_OPTIONS; #endif if (TARGET_E500) { if (TARGET_ALTIVEC) error ("AltiVec and E500 instructions cannot coexist"); /* The e500 does not have string instructions, and we set MASK_STRING above when optimizing for size. */ if ((target_flags & MASK_STRING) != 0) target_flags = target_flags & ~MASK_STRING; /* No SPE means 64-bit long doubles, even if an E500. */ if (rs6000_spe_string != 0 && !strcmp (rs6000_spe_string, "no")) rs6000_long_double_type_size = 64; } else if (rs6000_select[1].string != NULL) { /* For the powerpc-eabispe configuration, we set all these by default, so let's unset them if we manually set another CPU that is not the E500. */ if (rs6000_abi_string == 0) rs6000_spe_abi = 0; if (rs6000_spe_string == 0) rs6000_spe = 0; if (rs6000_float_gprs_string == 0) rs6000_float_gprs = 0; if (rs6000_isel_string == 0) rs6000_isel = 0; if (rs6000_long_double_size_string == 0) rs6000_long_double_type_size = 64; } rs6000_always_hint = (rs6000_cpu != PROCESSOR_POWER4 && rs6000_cpu != PROCESSOR_POWER5); rs6000_sched_groups = (rs6000_cpu == PROCESSOR_POWER4 || rs6000_cpu == PROCESSOR_POWER5); rs6000_gen_microcode = !(rs6000_cpu == PROCESSOR_CELLPPU && !optimize_size && TARGET_64BIT); rs6000_testabi = (rs6000_cpu == PROCESSOR_CELLPPU); rs6000_dd2_sched = (rs6000_cpu == PROCESSOR_CELLPPU); /* Handle -m(no-)longcall option. This is a bit of a cheap hack, using TARGET_OPTIONS to handle a toggle switch, but we're out of bits in target_flags so TARGET_SWITCHES cannot be used. Assumption here is that rs6000_longcall_switch points into the text of the complete option, rather than being a copy, so we can scan back for the presence or absence of the no- modifier. */ if (rs6000_longcall_switch) { const char *base = rs6000_longcall_switch; while (base[-1] != 'm') base--; if (*rs6000_longcall_switch != '\0') error ("invalid option `%s'", base); rs6000_default_long_calls = (base[0] != 'n'); } /* Handle -m(no-)warn-altivec-long similarly. */ if (rs6000_warn_altivec_long_switch) { const char *base = rs6000_warn_altivec_long_switch; while (base[-1] != 'm') base--; if (*rs6000_warn_altivec_long_switch != '\0') error ("invalid option `%s'", base); rs6000_warn_altivec_long = (base[0] != 'n'); } /* Handle -m(no-)testabi similarly. */ if (rs6000_testabi_switch) { const char *base = rs6000_testabi_switch; while (base[-1] != 'm') base--; if (*rs6000_testabi_switch != '\0') error ("invalid option `%s'", base); rs6000_testabi = (base[0] != 'n'); } /* Handle -m(no-)dd2-sched similarly. */ if (rs6000_dd2_sched_switch) { const char *base = rs6000_dd2_sched_switch; while (base[-1] != 'm') base--; if (*rs6000_dd2_sched_switch != '\0') error ("invalid option `%s'", base); rs6000_dd2_sched = (base[0] != 'n'); } /* Handle -m(no-)gen-microcode similarly. */ if (rs6000_gen_microcode_switch) { const char *base = rs6000_gen_microcode_switch; while (base[-1] != 'm') base--; if (*rs6000_gen_microcode_switch != '\0') error ("invalid option `%s'", base); rs6000_gen_microcode = (base[0] != 'n'); } /* Handle -m(no-)warn-microcode similarly. */ if (rs6000_warn_microcode_switch) { const char *base = rs6000_warn_microcode_switch; while (base[-1] != 'm') base--; if (*rs6000_warn_microcode_switch != '\0') error ("invalid option `%s'", base); rs6000_warn_microcode = (base[0] != 'n'); } /* Handle -mprioritize-restricted-insns option. */ rs6000_sched_restricted_insns_priority = (rs6000_sched_groups ? 1 : 0); if (rs6000_sched_restricted_insns_priority_str) rs6000_sched_restricted_insns_priority = atoi (rs6000_sched_restricted_insns_priority_str); /* Handle -msched-costly-dep option. */ rs6000_sched_costly_dep = (rs6000_sched_groups ? store_to_load_dep_costly : no_dep_costly); if (rs6000_sched_costly_dep_str) { if (! strcmp (rs6000_sched_costly_dep_str, "no")) rs6000_sched_costly_dep = no_dep_costly; else if (! strcmp (rs6000_sched_costly_dep_str, "all")) 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; else if (! strcmp (rs6000_sched_costly_dep_str, "store_to_load")) rs6000_sched_costly_dep = store_to_load_dep_costly; else rs6000_sched_costly_dep = atoi (rs6000_sched_costly_dep_str); } /* Handle -minsert-sched-nops option. */ rs6000_sched_insert_nops = (rs6000_sched_groups ? sched_finish_regroup_exact : sched_finish_none); if (rs6000_sched_insert_nops_str) { if (! strcmp (rs6000_sched_insert_nops_str, "no")) rs6000_sched_insert_nops = sched_finish_none; else if (! strcmp (rs6000_sched_insert_nops_str, "pad")) 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; else rs6000_sched_insert_nops = atoi (rs6000_sched_insert_nops_str); } #ifdef TARGET_REGNAMES /* If the user desires alternate register names, copy in the alternate names now. */ if (TARGET_REGNAMES) memcpy (rs6000_reg_names, alt_reg_names, sizeof (rs6000_reg_names)); #endif /* Set TARGET_AIX_STRUCT_RET last, after the ABI is determined. If -maix-struct-return or -msvr4-struct-return was explicitly used, don't override with the ABI default. */ if ((target_flags_explicit & MASK_AIX_STRUCT_RET) == 0) { if (DEFAULT_ABI == ABI_V4 && !DRAFT_V4_STRUCT_RET) target_flags = (target_flags & ~MASK_AIX_STRUCT_RET); else target_flags |= MASK_AIX_STRUCT_RET; } if (TARGET_LONG_DOUBLE_128 && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_DARWIN)) REAL_MODE_FORMAT (TFmode) = &ibm_extended_format; /* Allocate an alias set for register saves & restores from stack. */ rs6000_sr_alias_set = new_alias_set (); if (TARGET_TOC) ASM_GENERATE_INTERNAL_LABEL (toc_label_name, "LCTOC", 1); /* We can only guarantee the availability of DI pseudo-ops when assembling for 64-bit targets. */ if (!TARGET_64BIT) { targetm.asm_out.aligned_op.di = NULL; targetm.asm_out.unaligned_op.di = NULL; } /* Set maximum branch target alignment at two instructions, eight bytes. */ align_jumps_max_skip = 8; align_loops_max_skip = 8; /* Arrange to save and restore machine status around nested functions. */ init_machine_status = rs6000_init_machine_status; /* We should always be splitting complex arguments, but we can't break Linux and Darwin ABIs at the moment. For now, only AIX is fixed. */ if (DEFAULT_ABI != ABI_AIX) targetm.calls.split_complex_arg = NULL; if (TARGET_ALTIVEC) { /* Enable '(vector signed int)(a, b, c, d)' vector literal notation. */ targetm.cast_expr_as_vector_init = true; /* Enable AltiVec-specific name-mangling. */ targetm.mangle_vendor_type = rs6000_mangle_vendor_type; } } /* Handle generic options of the form -mfoo=yes/no. NAME is the option name. VALUE is the option value. FLAG is the pointer to the flag where to store a 1 or 0, depending on whether the option value is 'yes' or 'no' respectively. */ static void rs6000_parse_yes_no_option (const char *name, const char *value, int *flag) { if (value == 0) return; else if (!strcmp (value, "yes")) *flag = 1; else if (!strcmp (value, "no")) *flag = 0; else error ("unknown -m%s= option specified: '%s'", name, value); } /* Handle -mabi= options. */ static void rs6000_parse_abi_options (void) { if (rs6000_abi_string == 0) return; else if (! strcmp (rs6000_abi_string, "altivec")) { rs6000_altivec_abi = 1; rs6000_spe_abi = 0; } else if (! strcmp (rs6000_abi_string, "no-altivec")) rs6000_altivec_abi = 0; else if (! strcmp (rs6000_abi_string, "spe")) { rs6000_spe_abi = 1; rs6000_altivec_abi = 0; if (!TARGET_SPE_ABI) error ("not configured for ABI: '%s'", rs6000_abi_string); } else if (! strcmp (rs6000_abi_string, "no-spe")) rs6000_spe_abi = 0; else error ("unknown ABI specified: '%s'", rs6000_abi_string); } /* 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; else if (! strcmp (rs6000_alignment_string, "natural")) rs6000_alignment_flags = MASK_ALIGN_NATURAL; else error ("unknown -malign-XXXXX option specified: '%s'", rs6000_alignment_string); } /* Validate and record the size specified with the -mtls-size option. */ static void rs6000_parse_tls_size_option (void) { if (rs6000_tls_size_string == 0) return; else if (strcmp (rs6000_tls_size_string, "16") == 0) rs6000_tls_size = 16; else if (strcmp (rs6000_tls_size_string, "32") == 0) rs6000_tls_size = 32; else if (strcmp (rs6000_tls_size_string, "64") == 0) rs6000_tls_size = 64; else error ("bad value `%s' for -mtls-size switch", rs6000_tls_size_string); } void optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED) { } /* Do anything needed at the start of the asm file. */ static void rs6000_file_start (void) { size_t i; char buffer[80]; const char *start = buffer; struct rs6000_cpu_select *ptr; const char *default_cpu = TARGET_CPU_DEFAULT; FILE *file = asm_out_file; default_file_start (); #ifdef TARGET_BI_ARCH if ((TARGET_DEFAULT ^ target_flags) & MASK_64BIT) default_cpu = 0; #endif if (flag_verbose_asm) { sprintf (buffer, "\n%s rs6000/powerpc options:", ASM_COMMENT_START); rs6000_select[0].string = default_cpu; for (i = 0; i < ARRAY_SIZE (rs6000_select); i++) { ptr = &rs6000_select[i]; if (ptr->string != (char *)0 && ptr->string[0] != '\0') { fprintf (file, "%s %s%s", start, ptr->name, ptr->string); start = ""; } } #ifdef USING_ELFOS_H switch (rs6000_sdata) { case SDATA_NONE: fprintf (file, "%s -msdata=none", start); start = ""; break; case SDATA_DATA: fprintf (file, "%s -msdata=data", start); start = ""; break; case SDATA_BE: fprintf (file, "%s -msdata=be", start); start = ""; break; case SDATA_SYSV: fprintf (file, "%s -msdata=sysv", start); start = ""; break; case SDATA_EABI: fprintf (file, "%s -msdata=eabi", start); start = ""; break; } if (rs6000_sdata && g_switch_value) { fprintf (file, "%s -G " HOST_WIDE_INT_PRINT_UNSIGNED, start, g_switch_value); start = ""; } #endif if (*start == '\0') putc ('\n', file); } } /* Return nonzero if this function is known to have a null epilogue. */ int direct_return (void) { if (reload_completed) { rs6000_stack_t *info = rs6000_stack_info (); if (info->first_gp_reg_save == 32 && info->first_fp_reg_save == 64 && info->first_altivec_reg_save == LAST_ALTIVEC_REGNO + 1 && ! info->lr_save_p && ! info->cr_save_p && info->vrsave_mask == 0 && ! info->push_p) return 1; } return 0; } /* Returns 1 always. */ int any_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 CR0 field. */ int cr0_reg_operand (rtx op, enum machine_mode mode) { return (register_operand (op, mode) && REGNO(op) == CR0_REGNO); } /* 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 num_insns_constant_wide (HOST_WIDE_INT value) { /* signed constant loadable with {cal|addi} */ if (CONST_OK_FOR_LETTER_P (value, 'I')) return 1; /* constant loadable with {cau|addis} */ else if (CONST_OK_FOR_LETTER_P (value, 'L')) return 1; #if HOST_BITS_PER_WIDE_INT == 64 else if (TARGET_POWERPC64) { HOST_WIDE_INT low = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; HOST_WIDE_INT high = value >> 31; if (high == 0 || high == -1) return 2; high >>= 1; if (low == 0) return num_insns_constant_wide (high) + 1; else return (num_insns_constant_wide (high) + num_insns_constant_wide (low) + 1); } #endif else return 2; } int num_insns_constant (rtx op, enum machine_mode mode) { if (GET_CODE (op) == CONST_INT) { #if HOST_BITS_PER_WIDE_INT == 64 if ((INTVAL (op) >> 31) != 0 && (INTVAL (op) >> 31) != -1 && mask64_operand (op, mode)) return 2; else #endif return num_insns_constant_wide (INTVAL (op)); } else if (GET_CODE (op) == CONST_DOUBLE && 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 ((HOST_WIDE_INT) l); } else if (GET_CODE (op) == CONST_DOUBLE) { HOST_WIDE_INT low; HOST_WIDE_INT high; long l[2]; REAL_VALUE_TYPE rv; int endian = (WORDS_BIG_ENDIAN == 0); if (mode == VOIDmode || mode == DImode) { high = CONST_DOUBLE_HIGH (op); low = CONST_DOUBLE_LOW (op); } else { REAL_VALUE_FROM_CONST_DOUBLE (rv, op); REAL_VALUE_TO_TARGET_DOUBLE (rv, l); high = l[endian]; low = l[1 - endian]; } if (TARGET_32BIT) return (num_insns_constant_wide (low) + num_insns_constant_wide (high)); else { if (high == 0 && low >= 0) return num_insns_constant_wide (low); else if (high == -1 && low < 0) return num_insns_constant_wide (low); else if (mask64_operand (op, mode)) return 2; else if (low == 0) return num_insns_constant_wide (high) + 1; else return (num_insns_constant_wide (high) + num_insns_constant_wide (low) + 1); } } else 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}. */ int easy_fp_constant (rtx op, 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) { long k[4]; REAL_VALUE_TYPE rv; REAL_VALUE_FROM_CONST_DOUBLE (rv, op); REAL_VALUE_TO_TARGET_LONG_DOUBLE (rv, k); 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); } 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 instrunction, 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 nonzero if all elements of a vector have the same value. */ static int easy_vector_same (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { int units, i, cst; units = CONST_VECTOR_NUNITS (op); cst = INTVAL (CONST_VECTOR_ELT (op, 0)); for (i = 1; i < units; ++i) if (INTVAL (CONST_VECTOR_ELT (op, i)) != cst) break; if (i == units && easy_vector_splat_const (cst, mode)) return 1; 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 gen_easy_vector_constant_add_self (rtx op) { int i, units; rtvec v; units = GET_MODE_NUNITS (GET_MODE (op)); v = rtvec_alloc (units); for (i = 0; i < units; i++) RTVEC_ELT (v, i) = GEN_INT (INTVAL (CONST_VECTOR_ELT (op, i)) >> 1); return gen_rtx_raw_CONST_VECTOR (GET_MODE (op), v); } const char * output_vec_const_move (rtx *operands) { int cst, cst2; enum machine_mode mode; rtx dest, vec; dest = operands[0]; vec = operands[1]; cst = INTVAL (CONST_VECTOR_ELT (vec, 0)); cst2 = INTVAL (CONST_VECTOR_ELT (vec, 1)); mode = GET_MODE (dest); if (TARGET_ALTIVEC) { if (zero_constant (vec, mode)) return "vxor %0,%0,%0"; else if (easy_vector_constant (vec, mode)) { operands[1] = GEN_INT (cst); switch (mode) { case V4SImode: if (EASY_VECTOR_15 (cst)) { operands[1] = GEN_INT (cst); return "vspltisw %0,%1"; } else if (EASY_VECTOR_15_ADD_SELF (cst)) return "#"; cst = cst >> 16; case V8HImode: if (EASY_VECTOR_15 (cst)) { operands[1] = GEN_INT (cst); return "vspltish %0,%1"; } else if (EASY_VECTOR_15_ADD_SELF (cst)) return "#"; cst = cst >> 8; case V16QImode: if (EASY_VECTOR_15 (cst)) { operands[1] = GEN_INT (cst); return "vspltisb %0,%1"; } else if (EASY_VECTOR_15_ADD_SELF (cst)) return "#"; default: abort (); } } else abort (); } if (TARGET_SPE) { /* Vector constant 0 is handled as a splitter of V2SI, and in the pattern of V1DI, V4HI, and V2SF. FIXME: We should probably return # and add post reload splitters for these, but this way is so easy ;-). */ operands[1] = GEN_INT (cst); operands[2] = GEN_INT (cst2); if (cst == cst2) return "li %0,%1\n\tevmergelo %0,%0,%0"; else return "li %0,%1\n\tevmergelo %0,%0,%0\n\tli %0,%2"; } 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) { if (GET_CODE (op) == CONST_INT) { HOST_WIDE_INT c, lsb; c = INTVAL (op); /* Disallow 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 first transition. */ lsb = c & -c; /* Invert to look for a second transition. */ c = ~c; /* Erase first transition. */ c &= -lsb; /* Find the second transition. */ lsb = c & -c; /* Invert to look for a third transition. */ c = ~c; /* Erase second transition. */ c &= -lsb; /* Find the third transition (if any). */ lsb = c & -c; /* Match if all the bits above are 1's (or c is zero). */ return c == -lsb; } return 0; } /* Generates shifts and masks for a pair of rldicl or rldicr insns to implement ANDing by the mask IN. */ void build_mask64_2_operands (rtx in, rtx *out) { #if HOST_BITS_PER_WIDE_INT >= 64 unsigned HOST_WIDE_INT c, lsb, m1, m2; int shift; if (GET_CODE (in) != CONST_INT) abort (); c = INTVAL (in); if (c & 1) { /* Assume c initially something like 0x00fff000000fffff. The idea is to rotate the word so that the middle ^^^^^^ group of zeros is at the MS end and can be cleared with an rldicl mask. We then rotate back and clear off the MS ^^ group of zeros with a second rldicl. */ c = ~c; /* c == 0xff000ffffff00000 */ lsb = c & -c; /* lsb == 0x0000000000100000 */ m1 = -lsb; /* m1 == 0xfffffffffff00000 */ c = ~c; /* c == 0x00fff000000fffff */ c &= -lsb; /* c == 0x00fff00000000000 */ lsb = c & -c; /* lsb == 0x0000100000000000 */ c = ~c; /* c == 0xff000fffffffffff */ c &= -lsb; /* c == 0xff00000000000000 */ shift = 0; while ((lsb >>= 1) != 0) shift++; /* shift == 44 on exit from loop */ m1 <<= 64 - shift; /* m1 == 0xffffff0000000000 */ m1 = ~m1; /* m1 == 0x000000ffffffffff */ m2 = ~c; /* m2 == 0x00ffffffffffffff */ } else { /* Assume c initially something like 0xff000f0000000000. The idea is to rotate the word so that the ^^^ middle group of zeros is at the LS end and can be cleared with an rldicr mask. We then rotate back and clear off the LS group of ^^^^^^^^^^ zeros with a second rldicr. */ lsb = c & -c; /* lsb == 0x0000010000000000 */ m2 = -lsb; /* m2 == 0xffffff0000000000 */ c = ~c; /* c == 0x00fff0ffffffffff */ c &= -lsb; /* c == 0x00fff00000000000 */ lsb = c & -c; /* lsb == 0x0000100000000000 */ c = ~c; /* c == 0xff000fffffffffff */ c &= -lsb; /* c == 0xff00000000000000 */ shift = 0; while ((lsb >>= 1) != 0) shift++; /* shift == 44 on exit from loop */ m1 = ~c; /* m1 == 0x00ffffffffffffff */ m1 >>= shift; /* m1 == 0x0000000000000fff */ m1 = ~m1; /* m1 == 0xfffffffffffff000 */ } /* Note that when we only have two 0->1 and 1->0 transitions, one of the masks will be all 1's. We are guaranteed more than one transition. */ out[0] = GEN_INT (64 - shift); out[1] = GEN_INT (m1); out[2] = GEN_INT (shift); out[3] = GEN_INT (m2); #else (void)in; (void)out; abort (); #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. */ int and64_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_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. */ 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); return logical_operand (op, mode) || mask64_2_operand (op, mode); } /* 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. */ int and_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) || mask_operand (op, mode)); return (logical_operand (op, mode) || mask_operand (op, mode)); } /* Return 1 if the operand is a general register or memory operand. */ 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 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. */ int lwa_operand (rtx op, enum machine_mode mode) { rtx inner = op; if (reload_completed && GET_CODE (inner) == SUBREG) inner = SUBREG_REG (inner); 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)); } int lwa_mem_operand (rtx op, enum machine_mode mode) { rtx inner = op; if (reload_completed && GET_CODE (inner) == SUBREG) inner = SUBREG_REG (inner); return (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)); } /* 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; /* Only a tiny bit of handling for CONSTANT_P_RTX is necessary. */ if (GET_CODE (op) == CONSTANT_P_RTX) 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 && rs6000_sdata != SDATA_BE) return 0; if (GET_CODE (op) == SYMBOL_REF) sym_ref = op; else if (GET_CODE (op) != CONST || GET_CODE (XEXP (op, 0)) != PLUS || GET_CODE (XEXP (XEXP (op, 0), 0)) != SYMBOL_REF || GET_CODE (XEXP (XEXP (op, 0), 1)) != CONST_INT) return 0; else { rtx sum = XEXP (op, 0); 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. */ summand = INTVAL (XEXP (sum, 1)); if (summand < 0 || (unsigned HOST_WIDE_INT) summand > g_switch_value) return 0; sym_ref = XEXP (sum, 0); } return SYMBOL_REF_SMALL_P (sym_ref); #else return 0; #endif } /* Return true, if operand is a memory operand and has a displacement divisible by 4. */ int word_offset_memref_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { rtx addr; int off = 0; if (!memory_operand (op, mode)) return 0; addr = XEXP (op, 0); if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 0)) == REG && GET_CODE (XEXP (addr, 1)) == CONST_INT) off = INTVAL (XEXP (addr, 1)); return (off % 4) == 0; } /* Return true if operand is a (MEM (PLUS (REG) (offset))) where offset is not divisible by four. */ int invalid_gpr_mem (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { rtx addr; long off; if (GET_CODE (op) != MEM) return 0; addr = XEXP (op, 0); if (GET_CODE (addr) != PLUS || GET_CODE (XEXP (addr, 0)) != REG || GET_CODE (XEXP (addr, 1)) != CONST_INT) return 0; off = INTVAL (XEXP (addr, 1)); return (off & 3) != 0; } /* Return true if operand is a hard register that can be used as a base register. */ int base_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { unsigned int regno; if (!REG_P (op)) return 0; regno = REGNO (op); return regno != 0 && regno <= 31; } /* Return true if either operand is a general purpose register. */ bool gpr_or_gpr_p (rtx op0, rtx op1) { return ((REG_P (op0) && INT_REGNO_P (REGNO (op0))) || (REG_P (op1) && INT_REGNO_P (REGNO (op1)))); } /* Subroutines of rs6000_legitimize_address and rs6000_legitimate_address. */ static int constant_pool_expr_1 (rtx op, int *have_sym, int *have_toc) { switch (GET_CODE(op)) { case SYMBOL_REF: if (RS6000_SYMBOL_REF_TLS_P (op)) return 0; else if (CONSTANT_POOL_ADDRESS_P (op)) { if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (op), Pmode)) { *have_sym = 1; return 1; } else return 0; } else if (! strcmp (XSTR (op, 0), toc_label_name)) { *have_toc = 1; return 1; } else return 0; case PLUS: case MINUS: return (constant_pool_expr_1 (XEXP (op, 0), have_sym, have_toc) && constant_pool_expr_1 (XEXP (op, 1), have_sym, have_toc)); case CONST: return constant_pool_expr_1 (XEXP (op, 0), have_sym, have_toc); case CONST_INT: return 1; default: return 0; } } static bool constant_pool_expr_p (rtx op) { int have_sym = 0; int have_toc = 0; return constant_pool_expr_1 (op, &have_sym, &have_toc) && have_sym; } static bool toc_relative_expr_p (rtx op) { int have_sym = 0; int have_toc = 0; return constant_pool_expr_1 (op, &have_sym, &have_toc) && have_toc; } /* SPE offset addressing is limited to 5-bits worth of double words. */ #define SPE_CONST_OFFSET_OK(x) (((x) & ~0xf8) == 0) bool legitimate_constant_pool_address_p (rtx x) { return (TARGET_TOC && GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG && (TARGET_MINIMAL_TOC || REGNO (XEXP (x, 0)) == TOC_REGISTER) && constant_pool_expr_p (XEXP (x, 1))); } static bool legitimate_small_data_p (enum machine_mode mode, rtx x) { return (((DEFAULT_ABI == ABI_V4 && !flag_pic && !TARGET_TOC) || rs6000_sdata == SDATA_BE) && (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST) && small_data_operand (x, mode)); } static bool legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict) { unsigned HOST_WIDE_INT offset, extra; if (GET_CODE (x) != PLUS) return false; if (GET_CODE (XEXP (x, 0)) != REG) return false; if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict)) return false; if (GET_CODE (XEXP (x, 1)) != CONST_INT) return false; offset = INTVAL (XEXP (x, 1)); extra = 0; switch (mode) { case V16QImode: case V8HImode: case V4SFmode: case V4SImode: /* AltiVec vector modes. Only reg+reg addressing is valid here, which leaves the only valid constant offset of zero, which by canonicalization rules is also invalid. */ return false; case V4HImode: case V2SImode: case V1DImode: case V2SFmode: /* SPE vector modes. */ return SPE_CONST_OFFSET_OK (offset); case DFmode: case DImode: /* Both DFmode and DImode may end up in gprs. If gprs are 32-bit, then we need to load/store at both offset and offset+4. */ if (!TARGET_POWERPC64) extra = 4; break; case TFmode: case TImode: if (!TARGET_POWERPC64) extra = 12; else extra = 8; break; default: break; } offset += 0x8000; return (offset < 0x10000) && (offset + extra < 0x10000); } static bool legitimate_indexed_address_p (rtx x, int strict) { rtx op0, op1; if (GET_CODE (x) != PLUS) return false; op0 = XEXP (x, 0); op1 = XEXP (x, 1); if (!REG_P (op0) || !REG_P (op1)) return false; return ((INT_REG_OK_FOR_BASE_P (op0, strict) && INT_REG_OK_FOR_INDEX_P (op1, strict)) || (INT_REG_OK_FOR_BASE_P (op1, strict) && INT_REG_OK_FOR_INDEX_P (op0, strict))); } static 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 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 (GET_CODE (x) != LO_SUM) return false; if (GET_CODE (XEXP (x, 0)) != REG) return false; if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), 0)) return false; x = XEXP (x, 1); return CONSTANT_P (x); } static bool legitimate_lo_sum_address_p (enum machine_mode mode, rtx x, int strict) { if (GET_CODE (x) != LO_SUM) return false; if (GET_CODE (XEXP (x, 0)) != REG) return false; if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict)) return false; x = XEXP (x, 1); if (TARGET_ELF || TARGET_MACHO) { if (DEFAULT_ABI != ABI_AIX && DEFAULT_ABI != ABI_DARWIN && flag_pic) return false; if (TARGET_TOC) return false; if (GET_MODE_NUNITS (mode) != 1) return false; if (GET_MODE_BITSIZE (mode) > 32 && !(TARGET_HARD_FLOAT && TARGET_FPRS && mode == DFmode)) return false; return CONSTANT_P (x); } return false; } /* Try machine-dependent ways of modifying an illegitimate address to be legitimate. If we find one, return the new, valid address. This is used from only one place: `memory_address' in explow.c. OLDX is the address as it was before break_out_memory_refs was called. In some cases it is useful to look at this to decide what needs to be done. MODE is passed so that this function can use GO_IF_LEGITIMATE_ADDRESS. It is always safe for this function to do nothing. It exists to recognize opportunities to optimize the output. On RS/6000, first check for the sum of a register with a constant integer that is out of range. If so, generate code to add the constant with the low-order 16 bits masked to the register and force this result into another register (this can be done with `cau'). Then generate an address of REG+(CONST&0xffff), allowing for the possibility of bit 16 being a one. Then check for the sum of a register and something not constant, try to load the other things into a register and return the sum. */ rtx rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, enum machine_mode mode) { if (GET_CODE (x) == SYMBOL_REF) { enum tls_model model = SYMBOL_REF_TLS_MODEL (x); if (model != 0) return rs6000_legitimize_tls_address (x, model); } if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG && GET_CODE (XEXP (x, 1)) == CONST_INT && (unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1)) + 0x8000) >= 0x10000) { HOST_WIDE_INT high_int, low_int; rtx sum; low_int = ((INTVAL (XEXP (x, 1)) & 0xffff) ^ 0x8000) - 0x8000; high_int = INTVAL (XEXP (x, 1)) - low_int; sum = force_operand (gen_rtx_PLUS (Pmode, XEXP (x, 0), GEN_INT (high_int)), 0); return gen_rtx_PLUS (Pmode, sum, GEN_INT (low_int)); } else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG && GET_CODE (XEXP (x, 1)) != CONST_INT && GET_MODE_NUNITS (mode) == 1 && ((TARGET_HARD_FLOAT && TARGET_FPRS) || TARGET_POWERPC64 || (mode != DFmode && mode != TFmode)) && (TARGET_POWERPC64 || mode != DImode) && mode != TImode) { return gen_rtx_PLUS (Pmode, XEXP (x, 0), force_reg (Pmode, force_operand (XEXP (x, 1), 0))); } else if (ALTIVEC_VECTOR_MODE (mode)) { rtx reg; /* Make sure both operands are registers. */ if (GET_CODE (x) == PLUS) return gen_rtx_PLUS (Pmode, force_reg (Pmode, XEXP (x, 0)), force_reg (Pmode, XEXP (x, 1))); reg = force_reg (Pmode, x); return reg; } else if (SPE_VECTOR_MODE (mode)) { /* We accept [reg + reg] and [reg + OFFSET]. */ if (GET_CODE (x) == PLUS) { rtx op1 = XEXP (x, 0); rtx op2 = XEXP (x, 1); 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); return gen_rtx_PLUS (Pmode, op1, op2); } return force_reg (Pmode, x); } else if (TARGET_ELF && TARGET_32BIT && TARGET_NO_TOC && ! flag_pic && GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_DOUBLE && CONSTANT_P (x) && GET_MODE_NUNITS (mode) == 1 && (GET_MODE_BITSIZE (mode) <= 32 || ((TARGET_HARD_FLOAT && TARGET_FPRS) && mode == DFmode))) { rtx reg = gen_reg_rtx (Pmode); emit_insn (gen_elf_high (reg, x)); return gen_rtx_LO_SUM (Pmode, reg, x); } else if (TARGET_MACHO && TARGET_32BIT && TARGET_NO_TOC && ! flag_pic #if TARGET_MACHO && ! MACHO_DYNAMIC_NO_PIC_P #endif && GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_DOUBLE && CONSTANT_P (x) && ((TARGET_HARD_FLOAT && TARGET_FPRS) || mode != DFmode) && mode != DImode && mode != TImode) { rtx reg = gen_reg_rtx (Pmode); emit_insn (gen_macho_high (reg, x)); return gen_rtx_LO_SUM (Pmode, reg, x); } else if (TARGET_TOC && constant_pool_expr_p (x) && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), Pmode)) { return create_TOC_reference (x); } else return NULL_RTX; } /* This is called from dwarf2out.c via ASM_OUTPUT_DWARF_DTPREL. We need to emit DTP-relative relocations. */ void rs6000_output_dwarf_dtprel (FILE *file, int size, rtx x) { switch (size) { case 4: fputs ("\t.long\t", file); break; case 8: fputs (DOUBLE_INT_ASM_OP, file); break; default: abort (); } output_addr_const (file, x); fputs ("@dtprel+0x8000", file); } /* Construct the SYMBOL_REF for the tls_get_addr function. */ static GTY(()) rtx rs6000_tls_symbol; static rtx rs6000_tls_get_addr (void) { if (!rs6000_tls_symbol) rs6000_tls_symbol = init_one_libfunc ("__tls_get_addr"); return rs6000_tls_symbol; } /* Construct the SYMBOL_REF for TLS GOT references. */ static GTY(()) rtx rs6000_got_symbol; static rtx rs6000_got_sym (void) { if (!rs6000_got_symbol) { rs6000_got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); SYMBOL_REF_FLAGS (rs6000_got_symbol) |= SYMBOL_FLAG_LOCAL; SYMBOL_REF_FLAGS (rs6000_got_symbol) |= SYMBOL_FLAG_EXTERNAL; } return rs6000_got_symbol; } /* ADDR contains a thread-local SYMBOL_REF. Generate code to compute this (thread-local) address. */ static rtx rs6000_legitimize_tls_address (rtx addr, enum tls_model model) { rtx dest, insn; dest = gen_reg_rtx (Pmode); if (model == TLS_MODEL_LOCAL_EXEC && rs6000_tls_size == 16) { rtx tlsreg; if (TARGET_64BIT) { tlsreg = gen_rtx_REG (Pmode, 13); insn = gen_tls_tprel_64 (dest, tlsreg, addr); } else { tlsreg = gen_rtx_REG (Pmode, 2); insn = gen_tls_tprel_32 (dest, tlsreg, addr); } emit_insn (insn); } else if (model == TLS_MODEL_LOCAL_EXEC && rs6000_tls_size == 32) { rtx tlsreg, tmp; tmp = gen_reg_rtx (Pmode); if (TARGET_64BIT) { tlsreg = gen_rtx_REG (Pmode, 13); insn = gen_tls_tprel_ha_64 (tmp, tlsreg, addr); } else { tlsreg = gen_rtx_REG (Pmode, 2); insn = gen_tls_tprel_ha_32 (tmp, tlsreg, addr); } emit_insn (insn); if (TARGET_64BIT) insn = gen_tls_tprel_lo_64 (dest, tmp, addr); else insn = gen_tls_tprel_lo_32 (dest, tmp, addr); emit_insn (insn); } else { rtx r3, got, tga, tmp1, tmp2, eqv; if (TARGET_64BIT) got = gen_rtx_REG (Pmode, TOC_REGISTER); else { if (flag_pic == 1) got = gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM); else { rtx gsym = rs6000_got_sym (); got = gen_reg_rtx (Pmode); if (flag_pic == 0) rs6000_emit_move (got, gsym, Pmode); else { char buf[30]; static int tls_got_labelno = 0; rtx tempLR, lab, tmp3, mem; rtx first, last; ASM_GENERATE_INTERNAL_LABEL (buf, "LTLS", tls_got_labelno++); lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); tempLR = gen_reg_rtx (Pmode); tmp1 = gen_reg_rtx (Pmode); tmp2 = gen_reg_rtx (Pmode); tmp3 = gen_reg_rtx (Pmode); mem = gen_rtx_MEM (Pmode, tmp1); RTX_UNCHANGING_P (mem) = 1; first = emit_insn (gen_load_toc_v4_PIC_1b (tempLR, lab, gsym)); emit_move_insn (tmp1, tempLR); emit_move_insn (tmp2, mem); emit_insn (gen_addsi3 (tmp3, tmp1, tmp2)); last = emit_move_insn (got, tmp3); REG_NOTES (last) = gen_rtx_EXPR_LIST (REG_EQUAL, gsym, REG_NOTES (last)); REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last, REG_NOTES (first)); REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first, REG_NOTES (last)); } } } if (model == TLS_MODEL_GLOBAL_DYNAMIC) { r3 = gen_rtx_REG (Pmode, 3); if (TARGET_64BIT) insn = gen_tls_gd_64 (r3, got, addr); else insn = gen_tls_gd_32 (r3, got, addr); start_sequence (); emit_insn (insn); tga = gen_rtx_MEM (Pmode, rs6000_tls_get_addr ()); insn = gen_call_value (r3, tga, const0_rtx, const0_rtx); insn = emit_call_insn (insn); CONST_OR_PURE_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r3); insn = get_insns (); end_sequence (); emit_libcall_block (insn, dest, r3, addr); } else if (model == TLS_MODEL_LOCAL_DYNAMIC) { r3 = gen_rtx_REG (Pmode, 3); if (TARGET_64BIT) insn = gen_tls_ld_64 (r3, got); else insn = gen_tls_ld_32 (r3, got); start_sequence (); emit_insn (insn); tga = gen_rtx_MEM (Pmode, rs6000_tls_get_addr ()); insn = gen_call_value (r3, tga, const0_rtx, const0_rtx); insn = emit_call_insn (insn); CONST_OR_PURE_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r3); insn = get_insns (); end_sequence (); tmp1 = gen_reg_rtx (Pmode); eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLD); emit_libcall_block (insn, tmp1, r3, eqv); if (rs6000_tls_size == 16) { if (TARGET_64BIT) insn = gen_tls_dtprel_64 (dest, tmp1, addr); else insn = gen_tls_dtprel_32 (dest, tmp1, addr); } else if (rs6000_tls_size == 32) { tmp2 = gen_reg_rtx (Pmode); if (TARGET_64BIT) insn = gen_tls_dtprel_ha_64 (tmp2, tmp1, addr); else insn = gen_tls_dtprel_ha_32 (tmp2, tmp1, addr); emit_insn (insn); if (TARGET_64BIT) insn = gen_tls_dtprel_lo_64 (dest, tmp2, addr); else insn = gen_tls_dtprel_lo_32 (dest, tmp2, addr); } else { tmp2 = gen_reg_rtx (Pmode); if (TARGET_64BIT) insn = gen_tls_got_dtprel_64 (tmp2, got, addr); else insn = gen_tls_got_dtprel_32 (tmp2, got, addr); emit_insn (insn); insn = gen_rtx_SET (Pmode, dest, gen_rtx_PLUS (Pmode, tmp2, tmp1)); } emit_insn (insn); } else { /* IE, or 64 bit offset LE. */ tmp2 = gen_reg_rtx (Pmode); if (TARGET_64BIT) insn = gen_tls_got_tprel_64 (tmp2, got, addr); else insn = gen_tls_got_tprel_32 (tmp2, got, addr); emit_insn (insn); if (TARGET_64BIT) insn = gen_tls_tls_64 (dest, tmp2, addr); else insn = gen_tls_tls_32 (dest, tmp2, addr); emit_insn (insn); } } 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 rs6000_tls_referenced_p (rtx x) { if (! TARGET_HAVE_TLS) return false; return for_each_rtx (&x, &rs6000_tls_symbol_ref_1, 0); } /* 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 rs6000_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED) { return RS6000_SYMBOL_REF_TLS_P (*x); } /* The convention appears to be to define this wherever it is used. With legitimize_reload_address now defined here, REG_MODE_OK_FOR_BASE_P is now used here. */ #ifndef REG_MODE_OK_FOR_BASE_P #define REG_MODE_OK_FOR_BASE_P(REGNO, MODE) REG_OK_FOR_BASE_P (REGNO) #endif /* Our implementation of LEGITIMIZE_RELOAD_ADDRESS. Returns a value to replace the input X, or the original X if no replacement is called for. The output parameter *WIN is 1 if the calling macro should goto WIN, 0 if it should not. For RS/6000, we wish to handle large displacements off a base register by splitting the addend across an addiu/addis and the mem insn. This cuts number of extra insns needed from 3 to 1. On Darwin, we use this to generate code for floating point constants. A movsf_low is generated so we wind up with 2 instructions rather than 3. The Darwin code is inside #if TARGET_MACHO because only then is 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) { /* We must recognize output that we have already generated ourselves. */ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == PLUS && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT && GET_CODE (XEXP (x, 1)) == CONST_INT) { 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 TARGET_MACHO if (DEFAULT_ABI == ABI_DARWIN && flag_pic && GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == PLUS && XEXP (XEXP (x, 0), 0) == pic_offset_table_rtx && GET_CODE (XEXP (XEXP (x, 0), 1)) == HIGH && GET_CODE (XEXP (XEXP (XEXP (x, 0), 1), 0)) == CONST && XEXP (XEXP (XEXP (x, 0), 1), 0) == XEXP (x, 1) && GET_CODE (XEXP (XEXP (x, 1), 0)) == MINUS && GET_CODE (XEXP (XEXP (XEXP (x, 1), 0), 0)) == SYMBOL_REF && GET_CODE (XEXP (XEXP (XEXP (x, 1), 0), 1)) == SYMBOL_REF) { /* Result of previous invocation of this function on Darwin floating point constant. */ push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, (enum reload_type)type); *win = 1; return x; } #endif if (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) && !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; /* 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. */ x = gen_rtx_PLUS (GET_MODE (x), 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); *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) { if (flag_pic) { rtx offset = gen_rtx_CONST (Pmode, gen_rtx_MINUS (Pmode, x, gen_rtx_SYMBOL_REF (Pmode, machopic_function_base_name ()))); x = gen_rtx_LO_SUM (GET_MODE (x), gen_rtx_PLUS (Pmode, pic_offset_table_rtx, gen_rtx_HIGH (Pmode, offset)), offset); } else x = gen_rtx_LO_SUM (GET_MODE (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, opnum, (enum reload_type)type); *win = 1; return x; } #endif if (TARGET_TOC && constant_pool_expr_p (x) && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), mode)) { (x) = create_TOC_reference (x); *win = 1; return x; } *win = 0; return x; } /* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a valid memory address for an instruction. The MODE argument is the machine mode for the MEM expression that wants to use this address. On the RS/6000, there are four valid address: a SYMBOL_REF that refers to a constant pool entry of an address (or the sum of it plus a constant), a short (16-bit signed) constant plus a register, the sum of two registers, or a register indirect, possibly with an auto-increment. For DFmode and DImode with a constant plus register, we must ensure that both words are addressable or PowerPC64 with offset word aligned. For modes spanning multiple registers (DFmode in 32-bit GPRs, 32-bit DImode, TImode), indexed addressing cannot be used because adjacent memory cells are accessed by adding word-sized offsets during assembly output. */ int rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict) { if (RS6000_SYMBOL_REF_TLS_P (x)) return 0; if (legitimate_indirect_address_p (x, reg_ok_strict)) return 1; if ((GET_CODE (x) == PRE_INC || GET_CODE (x) == PRE_DEC) && !ALTIVEC_VECTOR_MODE (mode) && !SPE_VECTOR_MODE (mode) && TARGET_UPDATE && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict)) return 1; if (legitimate_small_data_p (mode, x)) return 1; if (legitimate_constant_pool_address_p (x)) return 1; /* If not REG_OK_STRICT (before reload) let pass any stack offset. */ if (! reg_ok_strict && GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG && (XEXP (x, 0) == virtual_stack_vars_rtx || XEXP (x, 0) == arg_pointer_rtx) && GET_CODE (XEXP (x, 1)) == CONST_INT) return 1; if (legitimate_offset_address_p (mode, x, reg_ok_strict)) return 1; if (mode != TImode && ((TARGET_HARD_FLOAT && TARGET_FPRS) || TARGET_POWERPC64 || (mode != DFmode && mode != TFmode)) && (TARGET_POWERPC64 || mode != DImode) && legitimate_indexed_address_p (x, reg_ok_strict)) return 1; if (legitimate_lo_sum_address_p (mode, x, reg_ok_strict)) return 1; return 0; } /* Go to LABEL if ADDR (a legitimate address expression) has an effect that depends on the machine mode it is used for. On the RS/6000 this is true of all integral offsets (since AltiVec modes don't allow them) or is a pre-increment or decrement. ??? Except that due to conceptual problems in offsettable_address_p we can't really report the problems of integral offsets. So leave this assuming that the adjustable offset must be valid for the sub-words of a TFmode operand, which is what we had before. */ bool rs6000_mode_dependent_address (rtx addr) { switch (GET_CODE (addr)) { case PLUS: if (GET_CODE (XEXP (addr, 1)) == CONST_INT) { unsigned HOST_WIDE_INT val = INTVAL (XEXP (addr, 1)); return val + 12 + 0x8000 >= 0x10000; } break; case LO_SUM: return true; case PRE_INC: case PRE_DEC: return TARGET_UPDATE; default: break; } return false; } /* Try to output insns to set TARGET equal to the constant C if it can be done in less than N insns. Do all computations in MODE. Returns the place where the output has been placed if it can be done and the insns have been emitted. If it would take more than N insns, zero is returned and no insns and emitted. */ rtx rs6000_emit_set_const (rtx dest, enum machine_mode mode, rtx source, int n ATTRIBUTE_UNUSED) { rtx result, insn, set; HOST_WIDE_INT c0, c1; if (mode == QImode || mode == HImode) { if (dest == NULL) dest = gen_reg_rtx (mode); emit_insn (gen_rtx_SET (VOIDmode, dest, source)); return dest; } else if (mode == SImode) { result = no_new_pseudos ? dest : gen_reg_rtx (SImode); emit_insn (gen_rtx_SET (VOIDmode, result, GEN_INT (INTVAL (source) & (~ (HOST_WIDE_INT) 0xffff)))); emit_insn (gen_rtx_SET (VOIDmode, dest, gen_rtx_IOR (SImode, result, GEN_INT (INTVAL (source) & 0xffff)))); result = dest; } else if (mode == DImode) { if (GET_CODE (source) == CONST_INT) { c0 = INTVAL (source); c1 = -(c0 < 0); } else if (GET_CODE (source) == CONST_DOUBLE) { #if HOST_BITS_PER_WIDE_INT >= 64 c0 = CONST_DOUBLE_LOW (source); c1 = -(c0 < 0); #else c0 = CONST_DOUBLE_LOW (source); c1 = CONST_DOUBLE_HIGH (source); #endif } else abort (); result = rs6000_emit_set_long_const (dest, c0, c1); } else abort (); insn = get_last_insn (); set = single_set (insn); if (! CONSTANT_P (SET_SRC (set))) set_unique_reg_note (insn, REG_EQUAL, source); return result; } /* Having failed to find a 3 insn sequence in rs6000_emit_set_const, fall back to a straight forward decomposition. We do this to avoid exponential run times encountered when looking for longer sequences with rs6000_emit_set_const. */ static rtx rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c1, HOST_WIDE_INT c2) { if (!TARGET_POWERPC64) { rtx operand1, operand2; operand1 = operand_subword_force (dest, WORDS_BIG_ENDIAN == 0, DImode); operand2 = operand_subword_force (dest, WORDS_BIG_ENDIAN != 0, DImode); emit_move_insn (operand1, GEN_INT (c1)); emit_move_insn (operand2, GEN_INT (c2)); } else { HOST_WIDE_INT ud1, ud2, ud3, ud4; ud1 = c1 & 0xffff; ud2 = (c1 & 0xffff0000) >> 16; #if HOST_BITS_PER_WIDE_INT >= 64 c2 = c1 >> 32; #endif ud3 = c2 & 0xffff; ud4 = (c2 & 0xffff0000) >> 16; if ((ud4 == 0xffff && ud3 == 0xffff && ud2 == 0xffff && (ud1 & 0x8000)) || (ud4 == 0 && ud3 == 0 && ud2 == 0 && ! (ud1 & 0x8000))) { if (ud1 & 0x8000) emit_move_insn (dest, GEN_INT (((ud1 ^ 0x8000) - 0x8000))); else emit_move_insn (dest, GEN_INT (ud1)); } else if ((ud4 == 0xffff && ud3 == 0xffff && (ud2 & 0x8000)) || (ud4 == 0 && ud3 == 0 && ! (ud2 & 0x8000))) { if (ud2 & 0x8000) emit_move_insn (dest, GEN_INT (((ud2 << 16) ^ 0x80000000) - 0x80000000)); else emit_move_insn (dest, GEN_INT (ud2 << 16)); if (ud1 != 0) emit_move_insn (dest, gen_rtx_IOR (DImode, dest, GEN_INT (ud1))); } else if (ud4 == 0 && ud3 == 0 && (ud2 & 0x8000) && ! (ud1 & 0x8000)) { // addi tgt,r0,ud1 ; oris tgt,tgt,ud2 emit_move_insn (dest, GEN_INT (ud1)); if (ud2 != 0) emit_move_insn (dest, gen_rtx_IOR (DImode, dest, GEN_INT (ud2 << 16))); } else if ((ud4 == 0xffff && (ud3 & 0x8000)) || (ud4 == 0 && ! (ud3 & 0x8000))) { if (ud3 & 0x8000) emit_move_insn (dest, GEN_INT (((ud3) ^ 0x8000) - 0x8000)); else emit_move_insn (dest, GEN_INT (ud3)); if (ud3 != 0) emit_move_insn (dest, gen_rtx_ASHIFT (DImode, dest, GEN_INT (32))); if (ud2 != 0) emit_move_insn (dest, gen_rtx_IOR (DImode, dest, GEN_INT (ud2 << 16))); if (ud1 != 0) emit_move_insn (dest, gen_rtx_IOR (DImode, dest, GEN_INT (ud1))); } else { if (ud4 & 0x8000) emit_move_insn (dest, GEN_INT (((ud4 << 16) ^ 0x80000000) - 0x80000000)); else emit_move_insn (dest, GEN_INT (ud4 << 16)); if (ud3 != 0) emit_move_insn (dest, gen_rtx_IOR (DImode, dest, GEN_INT (ud3))); emit_move_insn (dest, gen_rtx_ASHIFT (DImode, dest, GEN_INT (32))); if (ud2 != 0) emit_move_insn (dest, gen_rtx_IOR (DImode, dest, GEN_INT (ud2 << 16))); if (ud1 != 0) emit_move_insn (dest, gen_rtx_IOR (DImode, dest, GEN_INT (ud1))); } } return dest; } /* Emit a move from SOURCE to DEST in mode MODE. */ void rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode) { rtx operands[2]; operands[0] = dest; operands[1] = source; /* Sanity checks. Check that we get CONST_DOUBLE only when we should. */ if (GET_CODE (operands[1]) == CONST_DOUBLE && ! FLOAT_MODE_P (mode) && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) { /* FIXME. This should never happen. */ /* Since it seems that it does, do the safe thing and convert to a CONST_INT. */ operands[1] = gen_int_mode (CONST_DOUBLE_LOW (operands[1]), mode); } if (GET_CODE (operands[1]) == CONST_DOUBLE && ! FLOAT_MODE_P (mode) && ((CONST_DOUBLE_HIGH (operands[1]) == 0 && CONST_DOUBLE_LOW (operands[1]) >= 0) || (CONST_DOUBLE_HIGH (operands[1]) == -1 && CONST_DOUBLE_LOW (operands[1]) < 0))) abort (); /* Check if GCC is setting up a block move that will end up using FP registers as temporaries. We must make sure this is acceptable. */ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM && mode == DImode && (SLOW_UNALIGNED_ACCESS (DImode, MEM_ALIGN (operands[0])) || SLOW_UNALIGNED_ACCESS (DImode, MEM_ALIGN (operands[1]))) && ! (SLOW_UNALIGNED_ACCESS (SImode, (MEM_ALIGN (operands[0]) > 32 ? 32 : MEM_ALIGN (operands[0]))) || SLOW_UNALIGNED_ACCESS (SImode, (MEM_ALIGN (operands[1]) > 32 ? 32 : MEM_ALIGN (operands[1])))) && ! MEM_VOLATILE_P (operands [0]) && ! MEM_VOLATILE_P (operands [1])) { emit_move_insn (adjust_address (operands[0], SImode, 0), adjust_address (operands[1], SImode, 0)); emit_move_insn (adjust_address (operands[0], SImode, 4), adjust_address (operands[1], SImode, 4)); 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 (mode == SFmode && ! TARGET_POWERPC && TARGET_HARD_FLOAT && TARGET_FPRS && GET_CODE (operands[0]) == MEM) { int regnum; if (reload_in_progress || reload_completed) regnum = true_regnum (operands[1]); else if (GET_CODE (operands[1]) == REG) regnum = REGNO (operands[1]); else regnum = -1; /* If operands[1] is a register, on POWER it may have double-precision data in it, so truncate it to single precision. */ if (FP_REGNO_P (regnum) || regnum >= FIRST_PSEUDO_REGISTER) { rtx newreg; newreg = (no_new_pseudos ? operands[1] : gen_reg_rtx (mode)); emit_insn (gen_aux_truncdfsf2 (newreg, operands[1])); operands[1] = newreg; } } /* Recognize the case where operand[1] is a reference to thread-local data and load its address to a register. */ if (GET_CODE (operands[1]) == SYMBOL_REF) { enum tls_model model = SYMBOL_REF_TLS_MODEL (operands[1]); if (model != 0) operands[1] = rs6000_legitimize_tls_address (operands[1], model); } /* Handle the case where reload calls us with an invalid address. */ if (reload_in_progress && mode == Pmode && (! general_operand (operands[1], mode) || ! nonimmediate_operand (operands[0], mode))) goto emit_set; /* Handle the case of CONSTANT_P_RTX. */ if (GET_CODE (operands[1]) == CONSTANT_P_RTX) goto emit_set; /* 128-bit constant floating-point values on Darwin should really be loaded as two parts. */ if ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_DARWIN) && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128 && mode == TFmode && GET_CODE (operands[1]) == CONST_DOUBLE) { /* DImode is used, not DFmode, because simplify_gen_subreg doesn't know how to get a DFmode SUBREG of a TFmode. */ rs6000_emit_move (simplify_gen_subreg (DImode, operands[0], mode, 0), simplify_gen_subreg (DImode, operands[1], mode, 0), DImode); rs6000_emit_move (simplify_gen_subreg (DImode, operands[0], mode, GET_MODE_SIZE (DImode)), simplify_gen_subreg (DImode, operands[1], mode, GET_MODE_SIZE (DImode)), DImode); return; } /* FIXME: In the long term, this switch statement should go away and be replaced by a sequence of tests based on things like mode == Pmode. */ switch (mode) { case HImode: case QImode: if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != CONST_INT) operands[1] = force_const_mem (mode, operands[1]); break; case TFmode: case DFmode: case SFmode: if (CONSTANT_P (operands[1]) && ! easy_fp_constant (operands[1], mode)) operands[1] = force_const_mem (mode, operands[1]); break; case V16QImode: case V8HImode: case V4SFmode: case V4SImode: case V4HImode: case V2SFmode: case V2SImode: case V1DImode: if (CONSTANT_P (operands[1]) && !easy_vector_constant (operands[1], mode)) operands[1] = force_const_mem (mode, operands[1]); break; case SImode: case DImode: /* Use default pattern for address of ELF small data */ if (TARGET_ELF && mode == Pmode && DEFAULT_ABI == ABI_V4 && (GET_CODE (operands[1]) == SYMBOL_REF || GET_CODE (operands[1]) == CONST) && small_data_operand (operands[1], mode)) { emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); return; } if (DEFAULT_ABI == ABI_V4 && mode == Pmode && mode == SImode && flag_pic == 1 && got_operand (operands[1], mode)) { emit_insn (gen_movsi_got (operands[0], operands[1])); return; } if ((TARGET_ELF || DEFAULT_ABI == ABI_DARWIN) && TARGET_NO_TOC && ! flag_pic && mode == Pmode && CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != HIGH && GET_CODE (operands[1]) != CONST_INT) { rtx target = (no_new_pseudos ? operands[0] : gen_reg_rtx (mode)); /* If this is a function address on -mcall-aixdesc, convert it to the address of the descriptor. */ if (DEFAULT_ABI == ABI_AIX && GET_CODE (operands[1]) == SYMBOL_REF && XSTR (operands[1], 0)[0] == '.') { const char *name = XSTR (operands[1], 0); rtx new_ref; while (*name == '.') name++; new_ref = gen_rtx_SYMBOL_REF (Pmode, name); CONSTANT_POOL_ADDRESS_P (new_ref) = CONSTANT_POOL_ADDRESS_P (operands[1]); SYMBOL_REF_FLAGS (new_ref) = SYMBOL_REF_FLAGS (operands[1]); SYMBOL_REF_USED (new_ref) = SYMBOL_REF_USED (operands[1]); SYMBOL_REF_DECL (new_ref) = SYMBOL_REF_DECL (operands[1]); operands[1] = new_ref; } if (DEFAULT_ABI == ABI_DARWIN) { #if TARGET_MACHO if (MACHO_DYNAMIC_NO_PIC_P) { /* Take care of any required data indirection. */ operands[1] = rs6000_machopic_legitimize_pic_address ( operands[1], mode, operands[0]); if (operands[0] != operands[1]) emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); return; } #endif emit_insn (gen_macho_high (target, operands[1])); emit_insn (gen_macho_low (operands[0], target, operands[1])); return; } emit_insn (gen_elf_high (target, operands[1])); emit_insn (gen_elf_low (operands[0], target, operands[1])); return; } /* If this is a SYMBOL_REF that refers to a constant pool entry, and we have put it in the TOC, we just need to make a TOC-relative reference to it. */ if (TARGET_TOC && GET_CODE (operands[1]) == SYMBOL_REF && constant_pool_expr_p (operands[1]) && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (operands[1]), get_pool_mode (operands[1]))) { operands[1] = create_TOC_reference (operands[1]); } else if (mode == Pmode && CONSTANT_P (operands[1]) && ((GET_CODE (operands[1]) != CONST_INT && ! easy_fp_constant (operands[1], mode)) || (GET_CODE (operands[1]) == CONST_INT && num_insns_constant (operands[1], mode) > 2) || (GET_CODE (operands[0]) == REG && FP_REGNO_P (REGNO (operands[0])))) && GET_CODE (operands[1]) != HIGH && ! legitimate_constant_pool_address_p (operands[1]) && ! toc_relative_expr_p (operands[1])) { /* Emit a USE operation so that the constant isn't deleted if expensive optimizations are turned on because nobody references it. This should only be done for operands that contain SYMBOL_REFs with CONSTANT_POOL_ADDRESS_P set. This should not be done for operands that contain LABEL_REFs. For now, we just handle the obvious case. */ if (GET_CODE (operands[1]) != LABEL_REF) emit_insn (gen_rtx_USE (VOIDmode, operands[1])); #if TARGET_MACHO /* Darwin uses a special PIC legitimizer. */ if (DEFAULT_ABI == ABI_DARWIN && MACHOPIC_INDIRECT) { operands[1] = rs6000_machopic_legitimize_pic_address (operands[1], mode, operands[0]); if (operands[0] != operands[1]) emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); return; } #endif /* If we are to limit the number of things we put in the TOC and this is a symbol plus a constant we can add in one insn, just put the symbol in the TOC and add the constant. Don't do this if reload is in progress. */ if (GET_CODE (operands[1]) == CONST && TARGET_NO_SUM_IN_TOC && ! reload_in_progress && GET_CODE (XEXP (operands[1], 0)) == PLUS && add_operand (XEXP (XEXP (operands[1], 0), 1), mode) && (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == LABEL_REF || GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == SYMBOL_REF) && ! side_effects_p (operands[0])) { rtx sym = force_const_mem (mode, XEXP (XEXP (operands[1], 0), 0)); rtx other = XEXP (XEXP (operands[1], 0), 1); sym = force_reg (mode, sym); if (mode == SImode) emit_insn (gen_addsi3 (operands[0], sym, other)); else emit_insn (gen_adddi3 (operands[0], sym, other)); return; } operands[1] = force_const_mem (mode, operands[1]); if (TARGET_TOC && constant_pool_expr_p (XEXP (operands[1], 0)) && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P ( get_pool_constant (XEXP (operands[1], 0)), get_pool_mode (XEXP (operands[1], 0)))) { operands[1] = gen_rtx_MEM (mode, create_TOC_reference (XEXP (operands[1], 0))); set_mem_alias_set (operands[1], get_TOC_alias_set ()); RTX_UNCHANGING_P (operands[1]) = 1; } } break; case TImode: if (GET_CODE (operands[0]) == MEM && GET_CODE (XEXP (operands[0], 0)) != REG && ! reload_in_progress) operands[0] = replace_equiv_address (operands[0], copy_addr_to_reg (XEXP (operands[0], 0))); if (GET_CODE (operands[1]) == MEM && GET_CODE (XEXP (operands[1], 0)) != REG && ! reload_in_progress) operands[1] = replace_equiv_address (operands[1], copy_addr_to_reg (XEXP (operands[1], 0))); if (TARGET_POWER) { emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, gen_rtx_SET (VOIDmode, operands[0], operands[1]), gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode))))); return; } break; default: abort (); } /* Above, we may have called force_const_mem which may have returned an invalid address. If we can, fix this up; otherwise, reload will have to deal with it. */ if (GET_CODE (operands[1]) == MEM && ! reload_in_progress) operands[1] = validize_mem (operands[1]); emit_set: emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); } /* Nonzero if we can use a floating-point register to pass this arg. */ #define USE_FP_FOR_ARG_P(CUM,MODE,TYPE) \ (GET_MODE_CLASS (MODE) == MODE_FLOAT \ && (CUM)->fregno <= FP_ARG_MAX_REG \ && TARGET_HARD_FLOAT && TARGET_FPRS) /* Nonzero if we can use an AltiVec register to pass this arg. */ #define USE_ALTIVEC_FOR_ARG_P(CUM,MODE,TYPE,NAMED) \ (ALTIVEC_VECTOR_MODE (MODE) \ && (CUM)->vregno <= ALTIVEC_ARG_MAX_REG \ && TARGET_ALTIVEC_ABI \ && (NAMED)) /* Return a nonzero value to say to return the function value in memory, just as large structures are always returned. TYPE will be the data type of the value, and FNTYPE will be the type of the function doing the returning, or @code{NULL} for libcalls. The AIX ABI for the RS/6000 specifies that all structures are returned in memory. The Darwin ABI does the same. The SVR4 ABI specifies that structures <= 8 bytes are returned in r3/r4, but a draft put them in memory, and GCC used to implement the draft instead of the final standard. Therefore, TARGET_AIX_STRUCT_RET controls this instead of DEFAULT_ABI; V.4 targets needing backward compatibility can change DRAFT_V4_STRUCT_RET to override the default, and -m switches get the final word. See rs6000_override_options for more details. The PPC32 SVR4 ABI uses IEEE double extended for long double, if 128-bit long double support is enabled. These values are returned in memory. int_size_in_bytes returns -1 for variable size objects, which go in memory always. The cast to unsigned makes -1 > 8. */ static bool rs6000_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED) { if (AGGREGATE_TYPE_P (type) && (TARGET_AIX_STRUCT_RET || (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8)) return true; if (DEFAULT_ABI == ABI_V4 && TYPE_MODE (type) == TFmode) return true; return false; } /* Initialize a variable CUM of type CUMULATIVE_ARGS for a call to a function whose data type is FNTYPE. For a library call, FNTYPE is 0. For incoming args we set the number of arguments in the prototype large so we never return a PARALLEL. */ void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname ATTRIBUTE_UNUSED, int incoming, int libcall, int n_named_args) { static CUMULATIVE_ARGS zero_cumulative; *cum = zero_cumulative; cum->words = 0; cum->fregno = FP_ARG_MIN_REG; cum->vregno = ALTIVEC_ARG_MIN_REG; cum->prototype = (fntype && TYPE_ARG_TYPES (fntype)); cum->call_cookie = ((DEFAULT_ABI == ABI_V4 && libcall) ? CALL_LIBCALL : CALL_NORMAL); cum->sysv_gregno = GP_ARG_MIN_REG; cum->stdarg = fntype && (TYPE_ARG_TYPES (fntype) != 0 && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)); cum->nargs_prototype = 0; if (incoming || cum->prototype) 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 (TARGET_DEBUG_ARG) { fprintf (stderr, "\ninit_cumulative_args:"); if (fntype) { tree ret_type = TREE_TYPE (fntype); fprintf (stderr, " ret code = %s,", tree_code_name[ (int)TREE_CODE (ret_type) ]); } if (cum->call_cookie & CALL_LONG) fprintf (stderr, " longcall,"); fprintf (stderr, " proto = %d, nargs = %d\n", 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 defined, a C expression which determines whether, and in which direction, to pad out an argument with extra space. The value should be of type `enum direction': either `upward' to pad above the argument, `downward' to pad below, or `none' to inhibit padding. For the AIX ABI structs are always stored left shifted in their argument slot. */ enum direction function_arg_padding (enum machine_mode mode, tree type) { #ifndef AGGREGATE_PADDING_FIXED #define AGGREGATE_PADDING_FIXED 0 #endif #ifndef AGGREGATES_PAD_UPWARD_ALWAYS #define AGGREGATES_PAD_UPWARD_ALWAYS 0 #endif if (!AGGREGATE_PADDING_FIXED) { /* 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 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. The following arranges for best compatibility with previous versions of gcc, but removes the -mstrict-align dependency. */ if (BYTES_BIG_ENDIAN) { HOST_WIDE_INT size = 0; if (mode == BLKmode) { if (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) size = int_size_in_bytes (type); } else size = GET_MODE_SIZE (mode); if (size == 1 || size == 2 || size == 4) return downward; } return upward; } if (AGGREGATES_PAD_UPWARD_ALWAYS) { if (type != 0 && AGGREGATE_TYPE_P (type)) return upward; } /* Fall back to the default. */ return DEFAULT_FUNCTION_ARG_PADDING (mode, type); } /* If defined, a C expression that gives the alignment boundary, in bits, 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. */ int function_arg_boundary (enum machine_mode mode, tree type ATTRIBUTE_UNUSED) { if (DEFAULT_ABI == ABI_V4 && GET_MODE_SIZE (mode) == 8) return 64; else if (SPE_VECTOR_MODE (mode)) return 64; else if (ALTIVEC_VECTOR_MODE (mode)) return 128; /* According to the 64-bit PowerPC ELF ABI, aggregates and unions with * alignment requirements should meet those requirements when passed * as arguments. */ else if (TARGET_64BIT && type && TYPE_ALIGN(type) > PARM_BOUNDARY) return TYPE_ALIGN(type); else return PARM_BOUNDARY; } /* Compute the size (in words) of a function argument. */ static unsigned long rs6000_arg_size (enum machine_mode mode, tree type) { unsigned long size; if (mode != BLKmode) size = GET_MODE_SIZE (mode); else size = int_size_in_bytes (type); if (TARGET_32BIT) return (size + 3) >> 2; else return (size + 7) >> 3; } /* 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.) Note that for args passed by reference, function_arg will be called with MODE and TYPE set to that of the pointer to the arg, not the arg itself. */ void function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, int named) { cum->nargs_prototype--; if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode)) { 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" " altivec instructions are disabled, use -maltivec" " to enable them."); /* PowerPC64 Linux and AIX allocate GPRs for a vector argument even if it is going to be passed in a vector register. Darwin does the same for variable-argument functions. */ if ((DEFAULT_ABI == ABI_AIX && TARGET_64BIT) || (cum->stdarg && DEFAULT_ABI != ABI_V4)) stack = true; } else stack = true; if (stack) { int align; /* Vector parameters must be 16-byte aligned. This places them at 2 mod 4 in terms of words in 32-bit mode, since the parameter save area starts at offset 24 from the stack. In 64-bit mode, they just have to start on an even word, since the parameter save area is 16-byte aligned. Space for GPRs is reserved even if the argument will be passed in memory. */ if (TARGET_32BIT) align = (2 - cum->words) & 3; else align = cum->words & 1; cum->words += align + rs6000_arg_size (mode, type); if (TARGET_DEBUG_ARG) { fprintf (stderr, "function_adv: words = %2d, align=%d, ", cum->words, align); fprintf (stderr, "nargs = %4d, proto = %d, mode = %4s\n", cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode)); } } } else if (TARGET_SPE_ABI && TARGET_SPE && SPE_VECTOR_MODE (mode) && !cum->stdarg && cum->sysv_gregno <= GP_ARG_MAX_REG) cum->sysv_gregno++; else if (DEFAULT_ABI == ABI_V4) { if (TARGET_HARD_FLOAT && TARGET_FPRS && (mode == SFmode || mode == DFmode)) { if (cum->fregno <= FP_ARG_V4_MAX_REG) cum->fregno++; else { if (mode == DFmode) cum->words += cum->words & 1; cum->words += rs6000_arg_size (mode, type); } } else { int n_words = rs6000_arg_size (mode, type); int gregno = cum->sysv_gregno; /* Long long and SPE vectors are put in (r3,r4), (r5,r6), (r7,r8) or (r9,r10). As does any other 2 word item such as complex int due to a historical mistake. */ if