| /* Subroutines used for code generation on IBM RS/6000. |
| Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, |
| 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 |
| 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, 51 Franklin Street, Fifth Floor, Boston, |
| MA 02110-1301, 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" |
| /* LLVM LOCAL begin - Don't link in scheduler. */ |
| #ifndef ENABLE_LLVM |
| #include "sched-int.h" |
| #endif |
| /* LLVM LOCAL end */ |
| #include "tree-gimple.h" |
| /* APPLE LOCAL 3893112 */ |
| #include "params.h" |
| #include "intl.h" |
| #include "params.h" |
| #include "tm-constrs.h" |
| #if TARGET_XCOFF |
| #include "xcoffout.h" /* get declarations of xcoff_*_section_name */ |
| #endif |
| #if TARGET_MACHO |
| #include "gstab.h" /* for N_SLINE */ |
| #endif |
| |
| /* LLVM LOCAL begin get VECTOR_SAVE_INLINE defined */ |
| /* We think 1 is right for non-Darwin targets, but no one is sure - PR 3190 */ |
| #ifndef VECTOR_SAVE_INLINE |
| #define VECTOR_SAVE_INLINE(x) (1) |
| #endif |
| /* LLVM LOCAL end */ |
| |
| /* APPLE LOCAL begin pascal strings */ |
| #include "../../libcpp/internal.h" |
| extern struct cpp_reader* parse_in; |
| /* APPLE LOCAL end pascal strings */ |
| |
| /* APPLE LOCAL begin Macintosh alignment */ |
| #ifndef OPTION_ALIGN_MAC68K |
| #define OPTION_ALIGN_MAC68K 0 |
| #endif |
| /* APPLE LOCAL end Macintosh alignment */ |
| |
| #ifndef TARGET_NO_PROTOTYPE |
| #define TARGET_NO_PROTOTYPE 0 |
| #endif |
| |
| #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 push_p; /* true if we need to allocate stack space */ |
| int calls_p; /* true if the function makes any calls */ |
| int world_save_p; /* true if we're saving *everything*: |
| r13-r31, cr, f14-f31, vrsave, v20-v31 */ |
| enum rs6000_abi abi; /* which ABI to use */ |
| int gp_save_offset; /* offset to save GP regs from initial SP */ |
| 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 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) */ |
| 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 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; |
| HOST_WIDE_INT total_size; /* total bytes allocated for stack */ |
| int spe_64bit_regs_used; |
| } rs6000_stack_t; |
| |
| /* A C structure for machine-specific, per-function data. |
| This is added to the cfun structure. */ |
| typedef struct machine_function GTY(()) |
| { |
| /* Flags if __builtin_return_address (n) with n >= 1 was used. */ |
| int ra_needs_full_frame; |
| /* Some local-dynamic symbol. */ |
| const char *some_ld_name; |
| /* Whether the instruction chain has been scanned already. */ |
| int insn_chain_scanned_p; |
| /* Flags if __builtin_return_address (0) was used. */ |
| int ra_need_lr; |
| /* APPLE LOCAL begin volatile pic base reg in leaves */ |
| /* Substitute PIC register in leaf functions */ |
| unsigned int substitute_pic_base_reg; |
| /* APPLE LOCAL end volatile pic base reg in leaves */ |
| /* Offset from virtual_stack_vars_rtx to the start of the ABI_V4 |
| varargs save area. */ |
| HOST_WIDE_INT varargs_save_offset; |
| } machine_function; |
| |
| /* Target cpu type */ |
| |
| /* LLVM LOCAL begin */ |
| #ifdef ENABLE_LLVM |
| const char *rs6000_cpu_target = "ppc"; |
| #endif |
| /* LLVM LOCAL end */ |
| |
| 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 }, |
| }; |
| |
| /* APPLE LOCAL begin 5774356 */ |
| static int debug_sp_offset = 0; |
| static int debug_vrsave_offset = 0; |
| /* APPLE LOCAL end 5774356 */ |
| /* Always emit branch hint bits. */ |
| static GTY(()) bool rs6000_always_hint; |
| |
| /* Schedule instructions for group formation. */ |
| static GTY(()) bool rs6000_sched_groups; |
| |
| /* 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; |
| |
| /* Support targetm.vectorize.builtin_mask_for_load. */ |
| static GTY(()) tree altivec_builtin_mask_for_load; |
| |
| /* Size of long double. */ |
| int rs6000_long_double_type_size; |
| |
| /* IEEE quad extended precision long double. */ |
| int rs6000_ieeequad; |
| |
| /* Whether -mabi=altivec has appeared. */ |
| int rs6000_altivec_abi; |
| |
| /* Nonzero if we want SPE ABI extensions. */ |
| int rs6000_spe_abi; |
| |
| /* Nonzero if floating point operations are done in the GPRs. */ |
| int rs6000_float_gprs = 0; |
| |
| /* Nonzero if we want Darwin's struct-by-value-in-regs ABI. */ |
| int rs6000_darwin64_abi; |
| |
| /* Set to nonzero once AIX common-mode calls have been defined. */ |
| static GTY(()) int common_mode_defined; |
| |
| /* 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; |
| |
| /* Whether to use variant of AIX ABI for PowerPC64 Linux. */ |
| int dot_symbols; |
| |
| /* Debug flags */ |
| const char *rs6000_debug_name; |
| int rs6000_debug_stack; /* debug stack applications */ |
| int rs6000_debug_arg; /* debug argument handling */ |
| |
| /* Value is TRUE if register/mode pair is acceptable. */ |
| bool rs6000_hard_regno_mode_ok_p[NUM_MACHINE_MODES][FIRST_PSEUDO_REGISTER]; |
| |
| /* Built in types. */ |
| |
| tree rs6000_builtin_types[RS6000_BTI_MAX]; |
| /* APPLE LOCAL begin AltiVec */ |
| /* NB: We do not store the PIM operations/predicates this array. */ |
| tree rs6000_builtin_decls[RS6000_BUILTIN_COUNT]; |
| /* APPLE LOCAL end AltiVec */ |
| |
| const char *rs6000_traceback_name; |
| static enum { |
| traceback_default = 0, |
| traceback_none, |
| traceback_part, |
| traceback_full |
| } rs6000_traceback; |
| |
| /* Flag to say the TOC is initialized */ |
| int toc_initialized; |
| char toc_label_name[10]; |
| |
| static GTY(()) section *read_only_data_section; |
| static GTY(()) section *private_data_section; |
| static GTY(()) section *read_only_private_data_section; |
| static GTY(()) section *sdata2_section; |
| static GTY(()) section *toc_section; |
| |
| /* Control alignment for fields within structures. */ |
| /* String from -malign-XXXXX. */ |
| int rs6000_alignment_flags; |
| |
| /* True for any options that were explicitly set. */ |
| struct { |
| bool aix_struct_ret; /* True if -maix-struct-ret was used. */ |
| bool alignment; /* True if -malign- was used. */ |
| bool abi; /* True if -mabi=spe/nospe was used. */ |
| bool spe; /* True if -mspe= was used. */ |
| bool float_gprs; /* True if -mfloat-gprs= was used. */ |
| bool isel; /* True if -misel was used. */ |
| bool long_double; /* True if -mlong-double- was used. */ |
| bool ieee; /* True if -mabi=ieee/ibmlongdouble used. */ |
| } rs6000_explicit_options; |
| |
| 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; |
| }; |
| |
| /* Target cpu costs. */ |
| |
| struct processor_costs { |
| const int mulsi; /* cost of SImode multiplication. */ |
| const int mulsi_const; /* cost of SImode multiplication by constant. */ |
| const int mulsi_const9; /* cost of SImode mult by short constant. */ |
| const int muldi; /* cost of DImode multiplication. */ |
| const int divsi; /* cost of SImode division. */ |
| const int divdi; /* cost of DImode division. */ |
| const int fp; /* cost of simple SFmode and DFmode insns. */ |
| const int dmul; /* cost of DFmode multiplication (and fmadd). */ |
| const int sdiv; /* cost of SFmode division (fdivs). */ |
| const int ddiv; /* cost of DFmode division (fdiv). */ |
| }; |
| |
| const struct processor_costs *rs6000_cost; |
| |
| /* Processor costs (relative to an add) */ |
| |
| /* Instruction size costs on 32bit processors. */ |
| static const |
| struct processor_costs size32_cost = { |
| COSTS_N_INSNS (1), /* mulsi */ |
| COSTS_N_INSNS (1), /* mulsi_const */ |
| COSTS_N_INSNS (1), /* mulsi_const9 */ |
| COSTS_N_INSNS (1), /* muldi */ |
| COSTS_N_INSNS (1), /* divsi */ |
| COSTS_N_INSNS (1), /* divdi */ |
| COSTS_N_INSNS (1), /* fp */ |
| COSTS_N_INSNS (1), /* dmul */ |
| COSTS_N_INSNS (1), /* sdiv */ |
| COSTS_N_INSNS (1), /* ddiv */ |
| }; |
| |
| /* Instruction size costs on 64bit processors. */ |
| static const |
| struct processor_costs size64_cost = { |
| COSTS_N_INSNS (1), /* mulsi */ |
| COSTS_N_INSNS (1), /* mulsi_const */ |
| COSTS_N_INSNS (1), /* mulsi_const9 */ |
| COSTS_N_INSNS (1), /* muldi */ |
| COSTS_N_INSNS (1), /* divsi */ |
| COSTS_N_INSNS (1), /* divdi */ |
| COSTS_N_INSNS (1), /* fp */ |
| COSTS_N_INSNS (1), /* dmul */ |
| COSTS_N_INSNS (1), /* sdiv */ |
| COSTS_N_INSNS (1), /* ddiv */ |
| }; |
| |
| /* Instruction costs on RIOS1 processors. */ |
| static const |
| struct processor_costs rios1_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (4), /* mulsi_const */ |
| COSTS_N_INSNS (3), /* mulsi_const9 */ |
| COSTS_N_INSNS (5), /* muldi */ |
| COSTS_N_INSNS (19), /* divsi */ |
| COSTS_N_INSNS (19), /* divdi */ |
| COSTS_N_INSNS (2), /* fp */ |
| COSTS_N_INSNS (2), /* dmul */ |
| COSTS_N_INSNS (19), /* sdiv */ |
| COSTS_N_INSNS (19), /* ddiv */ |
| }; |
| |
| /* Instruction costs on RIOS2 processors. */ |
| static const |
| struct processor_costs rios2_cost = { |
| COSTS_N_INSNS (2), /* mulsi */ |
| COSTS_N_INSNS (2), /* mulsi_const */ |
| COSTS_N_INSNS (2), /* mulsi_const9 */ |
| COSTS_N_INSNS (2), /* muldi */ |
| COSTS_N_INSNS (13), /* divsi */ |
| COSTS_N_INSNS (13), /* divdi */ |
| COSTS_N_INSNS (2), /* fp */ |
| COSTS_N_INSNS (2), /* dmul */ |
| COSTS_N_INSNS (17), /* sdiv */ |
| COSTS_N_INSNS (17), /* ddiv */ |
| }; |
| |
| /* Instruction costs on RS64A processors. */ |
| static const |
| struct processor_costs rs64a_cost = { |
| COSTS_N_INSNS (20), /* mulsi */ |
| COSTS_N_INSNS (12), /* mulsi_const */ |
| COSTS_N_INSNS (8), /* mulsi_const9 */ |
| COSTS_N_INSNS (34), /* muldi */ |
| COSTS_N_INSNS (65), /* divsi */ |
| COSTS_N_INSNS (67), /* divdi */ |
| COSTS_N_INSNS (4), /* fp */ |
| COSTS_N_INSNS (4), /* dmul */ |
| COSTS_N_INSNS (31), /* sdiv */ |
| COSTS_N_INSNS (31), /* ddiv */ |
| }; |
| |
| /* Instruction costs on MPCCORE processors. */ |
| static const |
| struct processor_costs mpccore_cost = { |
| COSTS_N_INSNS (2), /* mulsi */ |
| COSTS_N_INSNS (2), /* mulsi_const */ |
| COSTS_N_INSNS (2), /* mulsi_const9 */ |
| COSTS_N_INSNS (2), /* muldi */ |
| COSTS_N_INSNS (6), /* divsi */ |
| COSTS_N_INSNS (6), /* divdi */ |
| COSTS_N_INSNS (4), /* fp */ |
| COSTS_N_INSNS (5), /* dmul */ |
| COSTS_N_INSNS (10), /* sdiv */ |
| COSTS_N_INSNS (17), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC403 processors. */ |
| static const |
| struct processor_costs ppc403_cost = { |
| COSTS_N_INSNS (4), /* mulsi */ |
| COSTS_N_INSNS (4), /* mulsi_const */ |
| COSTS_N_INSNS (4), /* mulsi_const9 */ |
| COSTS_N_INSNS (4), /* muldi */ |
| COSTS_N_INSNS (33), /* divsi */ |
| COSTS_N_INSNS (33), /* divdi */ |
| COSTS_N_INSNS (11), /* fp */ |
| COSTS_N_INSNS (11), /* dmul */ |
| COSTS_N_INSNS (11), /* sdiv */ |
| COSTS_N_INSNS (11), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC405 processors. */ |
| static const |
| struct processor_costs ppc405_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (4), /* mulsi_const */ |
| COSTS_N_INSNS (3), /* mulsi_const9 */ |
| COSTS_N_INSNS (5), /* muldi */ |
| COSTS_N_INSNS (35), /* divsi */ |
| COSTS_N_INSNS (35), /* divdi */ |
| COSTS_N_INSNS (11), /* fp */ |
| COSTS_N_INSNS (11), /* dmul */ |
| COSTS_N_INSNS (11), /* sdiv */ |
| COSTS_N_INSNS (11), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC440 processors. */ |
| static const |
| struct processor_costs ppc440_cost = { |
| COSTS_N_INSNS (3), /* mulsi */ |
| COSTS_N_INSNS (2), /* mulsi_const */ |
| COSTS_N_INSNS (2), /* mulsi_const9 */ |
| COSTS_N_INSNS (3), /* muldi */ |
| COSTS_N_INSNS (34), /* divsi */ |
| COSTS_N_INSNS (34), /* divdi */ |
| COSTS_N_INSNS (5), /* fp */ |
| COSTS_N_INSNS (5), /* dmul */ |
| COSTS_N_INSNS (19), /* sdiv */ |
| COSTS_N_INSNS (33), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC601 processors. */ |
| static const |
| struct processor_costs ppc601_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (5), /* mulsi_const */ |
| COSTS_N_INSNS (5), /* mulsi_const9 */ |
| COSTS_N_INSNS (5), /* muldi */ |
| COSTS_N_INSNS (36), /* divsi */ |
| COSTS_N_INSNS (36), /* divdi */ |
| COSTS_N_INSNS (4), /* fp */ |
| COSTS_N_INSNS (5), /* dmul */ |
| COSTS_N_INSNS (17), /* sdiv */ |
| COSTS_N_INSNS (31), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC603 processors. */ |
| static const |
| struct processor_costs ppc603_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (3), /* mulsi_const */ |
| COSTS_N_INSNS (2), /* mulsi_const9 */ |
| COSTS_N_INSNS (5), /* muldi */ |
| COSTS_N_INSNS (37), /* divsi */ |
| COSTS_N_INSNS (37), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (4), /* dmul */ |
| COSTS_N_INSNS (18), /* sdiv */ |
| COSTS_N_INSNS (33), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC604 processors. */ |
| static const |
| struct processor_costs ppc604_cost = { |
| COSTS_N_INSNS (4), /* mulsi */ |
| COSTS_N_INSNS (4), /* mulsi_const */ |
| COSTS_N_INSNS (4), /* mulsi_const9 */ |
| COSTS_N_INSNS (4), /* muldi */ |
| COSTS_N_INSNS (20), /* divsi */ |
| COSTS_N_INSNS (20), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (18), /* sdiv */ |
| COSTS_N_INSNS (32), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC604e processors. */ |
| static const |
| struct processor_costs ppc604e_cost = { |
| COSTS_N_INSNS (2), /* mulsi */ |
| COSTS_N_INSNS (2), /* mulsi_const */ |
| COSTS_N_INSNS (2), /* mulsi_const9 */ |
| COSTS_N_INSNS (2), /* muldi */ |
| COSTS_N_INSNS (20), /* divsi */ |
| COSTS_N_INSNS (20), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (18), /* sdiv */ |
| COSTS_N_INSNS (32), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC620 processors. */ |
| static const |
| struct processor_costs ppc620_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (4), /* mulsi_const */ |
| COSTS_N_INSNS (3), /* mulsi_const9 */ |
| COSTS_N_INSNS (7), /* muldi */ |
| COSTS_N_INSNS (21), /* divsi */ |
| COSTS_N_INSNS (37), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (18), /* sdiv */ |
| COSTS_N_INSNS (32), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC630 processors. */ |
| static const |
| struct processor_costs ppc630_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (4), /* mulsi_const */ |
| COSTS_N_INSNS (3), /* mulsi_const9 */ |
| COSTS_N_INSNS (7), /* muldi */ |
| COSTS_N_INSNS (21), /* divsi */ |
| COSTS_N_INSNS (37), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (17), /* sdiv */ |
| COSTS_N_INSNS (21), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC750 and PPC7400 processors. */ |
| static const |
| struct processor_costs ppc750_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (3), /* mulsi_const */ |
| COSTS_N_INSNS (2), /* mulsi_const9 */ |
| COSTS_N_INSNS (5), /* muldi */ |
| COSTS_N_INSNS (17), /* divsi */ |
| COSTS_N_INSNS (17), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (17), /* sdiv */ |
| COSTS_N_INSNS (31), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC7450 processors. */ |
| static const |
| struct processor_costs ppc7450_cost = { |
| COSTS_N_INSNS (4), /* mulsi */ |
| COSTS_N_INSNS (3), /* mulsi_const */ |
| COSTS_N_INSNS (3), /* mulsi_const9 */ |
| COSTS_N_INSNS (4), /* muldi */ |
| COSTS_N_INSNS (23), /* divsi */ |
| COSTS_N_INSNS (23), /* divdi */ |
| COSTS_N_INSNS (5), /* fp */ |
| COSTS_N_INSNS (5), /* dmul */ |
| COSTS_N_INSNS (21), /* sdiv */ |
| COSTS_N_INSNS (35), /* ddiv */ |
| }; |
| |
| /* Instruction costs on PPC8540 processors. */ |
| static const |
| struct processor_costs ppc8540_cost = { |
| COSTS_N_INSNS (4), /* mulsi */ |
| COSTS_N_INSNS (4), /* mulsi_const */ |
| COSTS_N_INSNS (4), /* mulsi_const9 */ |
| COSTS_N_INSNS (4), /* muldi */ |
| COSTS_N_INSNS (19), /* divsi */ |
| COSTS_N_INSNS (19), /* divdi */ |
| COSTS_N_INSNS (4), /* fp */ |
| COSTS_N_INSNS (4), /* dmul */ |
| COSTS_N_INSNS (29), /* sdiv */ |
| COSTS_N_INSNS (29), /* ddiv */ |
| }; |
| |
| /* Instruction costs on POWER4 and POWER5 processors. */ |
| static const |
| struct processor_costs power4_cost = { |
| COSTS_N_INSNS (3), /* mulsi */ |
| COSTS_N_INSNS (2), /* mulsi_const */ |
| COSTS_N_INSNS (2), /* mulsi_const9 */ |
| COSTS_N_INSNS (4), /* muldi */ |
| COSTS_N_INSNS (18), /* divsi */ |
| COSTS_N_INSNS (34), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (17), /* sdiv */ |
| COSTS_N_INSNS (17), /* ddiv */ |
| }; |
| |
| |
| static bool rs6000_function_ok_for_sibcall (tree, tree); |
| static const char *rs6000_invalid_within_doloop (rtx); |
| 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 legitimate_small_data_p (enum machine_mode, rtx); |
| static bool legitimate_indexed_address_p (rtx, int); |
| 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); |
| static bool no_global_regs_above (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 *); |
| static bool rs6000_ms_bitfield_layout_p (tree); |
| static tree rs6000_handle_struct_attribute (tree *, tree, tree, int, bool *); |
| static void rs6000_eliminate_indexed_memrefs (rtx operands[2]); |
| /* APPLE LOCAL mangle_type 7105099 */ |
| static const char *rs6000_mangle_type (tree); |
| 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 int rs6000_elf_reloc_rw_mask (void); |
| static void rs6000_elf_asm_out_constructor (rtx, int); |
| static void rs6000_elf_asm_out_destructor (rtx, int); |
| static void rs6000_elf_end_indicate_exec_stack (void) ATTRIBUTE_UNUSED; |
| static void rs6000_elf_asm_init_sections (void); |
| static section *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; |
| #endif |
| static bool rs6000_use_blocks_for_constant_p (enum machine_mode, rtx); |
| #if TARGET_XCOFF |
| static void rs6000_xcoff_asm_output_anchor (rtx); |
| static void rs6000_xcoff_asm_globalize_label (FILE *, const char *); |
| static void rs6000_xcoff_asm_init_sections (void); |
| static int rs6000_xcoff_reloc_rw_mask (void); |
| static void rs6000_xcoff_asm_named_section (const char *, unsigned int, tree); |
| static section *rs6000_xcoff_select_section (tree, int, |
| unsigned HOST_WIDE_INT); |
| static void rs6000_xcoff_unique_section (tree, int); |
| static section *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 |
| /* APPLE LOCAL begin pragma reverse_bitfield */ |
| #if TARGET_MACHO |
| static bool rs6000_reverse_bitfields_p (tree); |
| #endif |
| /* APPLE LOCAL end pragma reverse_bitfield */ |
| /* LLVM LOCAL - Disable scheduler. */ |
| #ifndef ENABLE_LLVM |
| static int rs6000_variable_issue (FILE *, int, rtx, int); |
| /* LLVM LOCAL - Disable scheduler. */ |
| #endif |
| static bool rs6000_rtx_costs (rtx, int, int, int *); |
| /* LLVM LOCAL begin - Not defined */ |
| #ifndef ENABLE_LLVM |
| static int rs6000_adjust_cost (rtx, rtx, rtx, int); |
| static bool is_microcoded_insn (rtx); |
| static int is_dispatch_slot_restricted (rtx); |
| static bool is_cracked_insn (rtx); |
| static bool is_branch_slot_insn (rtx); |
| #endif |
| /* LLVM LOCAL end - Not defined */ |
| /* LLVM LOCAL - Disable scheduler. */ |
| #ifndef ENABLE_LLVM |
| 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); |
| /* LLVM LOCAL - Disable scheduler. */ |
| #endif |
| static tree rs6000_builtin_mask_for_load (void); |
| |
| static void def_builtin (int, const char *, tree, int); |
| /* APPLE LOCAL mainline 4.2 5569774 */ |
| static bool rs6000_vector_alignment_reachable (tree, bool); |
| static void rs6000_init_builtins (void); |
| 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 void rs6000_init_libfuncs (void); |
| |
| static void enable_mask_for_builtins (struct builtin_description *, int, |
| enum rs6000_builtins, |
| enum rs6000_builtins); |
| static tree build_opaque_vector_type (tree, int); |
| 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); |
| /* APPLE LOCAL begin AltiVec */ |
| static tree altivec_cov_rt_12 (tree, tree); |
| static tree altivec_cov_rt_2p (tree); |
| static tree altivec_cov_rt_1d (tree); |
| static tree altivec_cov_rt_1h (tree); |
| static struct altivec_pim_info *altivec_ovl_resolve (struct altivec_pim_info *, |
| tree, tree); |
| static tree altivec_convert_args (tree, tree); |
| /* APPLE LOCAL end AltiVec */ |
| static rtx altivec_expand_vec_init_builtin (tree, tree, rtx); |
| static rtx altivec_expand_vec_set_builtin (tree); |
| static rtx altivec_expand_vec_ext_builtin (tree, rtx); |
| static int get_element_number (tree, tree); |
| static bool rs6000_handle_option (size_t, const char *, int); |
| 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 compute_save_world_info (rs6000_stack_t *info_ptr); |
| static void is_altivec_return_reg (rtx, void *); |
| static rtx generate_set_vrsave (rtx, rs6000_stack_t *, int); |
| int easy_vector_constant (rtx, enum machine_mode); |
| static bool rs6000_is_opaque_type (tree); |
| static rtx rs6000_dwarf_register_span (rtx); |
| static rtx rs6000_legitimize_tls_address (rtx, enum tls_model); |
| static void rs6000_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED; |
| static rtx rs6000_tls_get_addr (void); |
| static rtx rs6000_got_sym (void); |
| static int rs6000_tls_symbol_ref_1 (rtx *, void *); |
| static const char *rs6000_get_some_local_dynamic_name (void); |
| static int rs6000_get_some_local_dynamic_name_1 (rtx *, void *); |
| static rtx rs6000_complex_function_value (enum machine_mode); |
| static rtx rs6000_spe_function_arg (CUMULATIVE_ARGS *, |
| enum machine_mode, tree); |
| static void rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *, |
| /* APPLE LOCAL fix 64-bit varargs 4028089 */ |
| HOST_WIDE_INT, int); |
| static void rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *, |
| tree, HOST_WIDE_INT); |
| static void rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *, |
| HOST_WIDE_INT, |
| rtx[], int *); |
| static void rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *, |
| tree, HOST_WIDE_INT, |
| rtx[], int *); |
| static rtx rs6000_darwin64_record_arg (CUMULATIVE_ARGS *, tree, int, bool); |
| static rtx rs6000_mixed_function_arg (enum machine_mode, tree, int); |
| static void rs6000_move_block_from_reg (int regno, rtx x, int nregs); |
| static void setup_incoming_varargs (CUMULATIVE_ARGS *, |
| enum machine_mode, tree, |
| int *, int); |
| /* APPLE LOCAL begin Altivec */ |
| static bool skip_vec_args (tree, int, int*); |
| /* APPLE LOCAL end Altivec */ |
| static bool rs6000_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, |
| tree, bool); |
| static int rs6000_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, |
| tree, bool); |
| static const char *invalid_arg_for_unprototyped_fn (tree, tree, tree); |
| #if TARGET_MACHO |
| static void macho_branch_islands (void); |
| /* APPLE LOCAL 4380289 */ |
| static tree add_compiler_branch_island (tree, int); |
| static int no_previous_def (tree function_name); |
| static tree get_prev_label (tree function_name); |
| static void rs6000_darwin_file_start (void); |
| #endif |
| |
| static tree rs6000_build_builtin_va_list (void); |
| static tree rs6000_gimplify_va_arg (tree, tree, tree *, tree *); |
| static bool rs6000_must_pass_in_stack (enum machine_mode, tree); |
| static bool rs6000_scalar_mode_supported_p (enum machine_mode); |
| static bool rs6000_vector_mode_supported_p (enum machine_mode); |
| static int get_vec_cmp_insn (enum rtx_code, enum machine_mode, |
| enum machine_mode); |
| static rtx rs6000_emit_vector_compare (enum rtx_code, rtx, rtx, |
| enum machine_mode); |
| static int get_vsel_insn (enum machine_mode); |
| static void rs6000_emit_vector_select (rtx, rtx, rtx, rtx); |
| static tree rs6000_stack_protect_fail (void); |
| |
| const int INSN_NOT_AVAILABLE = -1; |
| static enum machine_mode rs6000_eh_return_filter_mode (void); |
| |
| /* Hash table stuff for keeping track of TOC entries. */ |
| |
| 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", |
| /* Soft frame pointer. */ |
| "sfp" |
| /* APPLE LOCAL 3399553 */ |
| , "fpscr" |
| }; |
| |
| #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", |
| /* Soft frame pointer. */ |
| "sfp" |
| /* APPLE LOCAL 3399553 */ |
| , "fpscr" |
| }; |
| #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)) |
| |
| /* 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" |
| #undef TARGET_ASM_UNALIGNED_DI_OP |
| #define TARGET_ASM_UNALIGNED_DI_OP "\t.quad\t" |
| #undef TARGET_ASM_ALIGNED_DI_OP |
| #define TARGET_ASM_ALIGNED_DI_OP "\t.quad\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 |
| |
| /* LLVM LOCAL - Disable scheduler. */ |
| #ifndef ENABLE_LLVM |
| #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_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD |
| #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD rs6000_use_sched_lookahead |
| /* LLVM LOCAL - Disable scheduler. */ |
| #endif |
| |
| #undef TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD |
| #define TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD rs6000_builtin_mask_for_load |
| |
| /* APPLE LOCAL begin mainline 4.2 5569774 */ |
| #undef TARGET_VECTOR_ALIGNMENT_REACHABLE |
| #define TARGET_VECTOR_ALIGNMENT_REACHABLE rs6000_vector_alignment_reachable |
| /* APPLE LOCAL end mainline 4.2 5569774 */ |
| |
| #undef TARGET_INIT_BUILTINS |
| #define TARGET_INIT_BUILTINS rs6000_init_builtins |
| |
| |
| /* APPLE LOCAL begin AltiVec */ |
| /* If we are running in Apple AltiVec (as opposed to FSF AltiVec) mode, |
| we will need to handle the Motorola PIM instructions ourselves instead |
| of relying on <altivec.h>. The rs6000_fold_builtin() routine will |
| rewrite the PIM instructions into the __builtin... (AldyVec) |
| instructions. */ |
| #undef TARGET_FOLD_BUILTIN |
| #define TARGET_FOLD_BUILTIN rs6000_fold_builtin |
| /* APPLE LOCAL end AltiVec */ |
| |
| #undef TARGET_EXPAND_BUILTIN |
| #define TARGET_EXPAND_BUILTIN rs6000_expand_builtin |
| |
| /* APPLE LOCAL begin mangle_type 7105099 */ |
| #undef TARGET_MANGLE_TYPE |
| #define TARGET_MANGLE_TYPE rs6000_mangle_type |
| /* APPLE LOCAL end mangle_type 7105099 */ |
| |
| #undef TARGET_INIT_LIBFUNCS |
| #define TARGET_INIT_LIBFUNCS rs6000_init_libfuncs |
| |
| #if TARGET_MACHO |
| #undef TARGET_BINDS_LOCAL_P |
| #define TARGET_BINDS_LOCAL_P darwin_binds_local_p |
| /* APPLE LOCAL begin pragma reverse_bitfields */ |
| #undef TARGET_REVERSE_BITFIELDS_P |
| #define TARGET_REVERSE_BITFIELDS_P rs6000_reverse_bitfields_p |
| /* APPLE LOCAL end pragma reverse_bitfields */ |
| #endif |
| |
| #undef TARGET_MS_BITFIELD_LAYOUT_P |
| #define TARGET_MS_BITFIELD_LAYOUT_P rs6000_ms_bitfield_layout_p |
| |
| #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_INVALID_WITHIN_DOLOOP |
| #define TARGET_INVALID_WITHIN_DOLOOP rs6000_invalid_within_doloop |
| |
| #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 rs6000_is_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 |
| |
| #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 |
| |
| /* APPLE LOCAL begin Altivec */ |
| #undef TARGET_SKIP_VEC_ARGS |
| #define TARGET_SKIP_VEC_ARGS skip_vec_args |
| /* APPLE LOCAL end Altivec */ |
| |
| /* 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_MUST_PASS_IN_STACK |
| #define TARGET_MUST_PASS_IN_STACK rs6000_must_pass_in_stack |
| #undef TARGET_PASS_BY_REFERENCE |
| #define TARGET_PASS_BY_REFERENCE rs6000_pass_by_reference |
| #undef TARGET_ARG_PARTIAL_BYTES |
| #define TARGET_ARG_PARTIAL_BYTES rs6000_arg_partial_bytes |
| |
| #undef TARGET_BUILD_BUILTIN_VA_LIST |
| #define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list |
| |
| #undef TARGET_GIMPLIFY_VA_ARG_EXPR |
| #define TARGET_GIMPLIFY_VA_ARG_EXPR rs6000_gimplify_va_arg |
| |
| #undef TARGET_EH_RETURN_FILTER_MODE |
| #define TARGET_EH_RETURN_FILTER_MODE rs6000_eh_return_filter_mode |
| |
| #undef TARGET_SCALAR_MODE_SUPPORTED_P |
| #define TARGET_SCALAR_MODE_SUPPORTED_P rs6000_scalar_mode_supported_p |
| |
| #undef TARGET_VECTOR_MODE_SUPPORTED_P |
| #define TARGET_VECTOR_MODE_SUPPORTED_P rs6000_vector_mode_supported_p |
| |
| #undef TARGET_INVALID_ARG_FOR_UNPROTOTYPED_FN |
| #define TARGET_INVALID_ARG_FOR_UNPROTOTYPED_FN invalid_arg_for_unprototyped_fn |
| |
| #undef TARGET_HANDLE_OPTION |
| #define TARGET_HANDLE_OPTION rs6000_handle_option |
| |
| #undef TARGET_DEFAULT_TARGET_FLAGS |
| #define TARGET_DEFAULT_TARGET_FLAGS \ |
| (TARGET_DEFAULT) |
| |
| #undef TARGET_STACK_PROTECT_FAIL |
| #define TARGET_STACK_PROTECT_FAIL rs6000_stack_protect_fail |
| |
| /* MPC604EUM 3.5.2 Weak Consistency between Multiple Processors |
| The PowerPC architecture requires only weak consistency among |
| processors--that is, memory accesses between processors need not be |
| sequentially consistent and memory accesses among processors can occur |
| in any order. The ability to order memory accesses weakly provides |
| opportunities for more efficient use of the system bus. Unless a |
| dependency exists, the 604e allows read operations to precede store |
| operations. */ |
| #undef TARGET_RELAXED_ORDERING |
| #define TARGET_RELAXED_ORDERING true |
| |
| #ifdef HAVE_AS_TLS |
| #undef TARGET_ASM_OUTPUT_DWARF_DTPREL |
| #define TARGET_ASM_OUTPUT_DWARF_DTPREL rs6000_output_dwarf_dtprel |
| #endif |
| |
| /* APPLE LOCAL begin radar 5155743, mainline candidate */ |
| #undef TARGET_HAVE_DYNAMIC_STACK_SPACE |
| #define TARGET_HAVE_DYNAMIC_STACK_SPACE true |
| /* APPLE LOCAL end radar 5155743, mainline candidate */ |
| /* Use a 32-bit anchor range. This leads to sequences like: |
| |
| addis tmp,anchor,high |
| add dest,tmp,low |
| |
| where tmp itself acts as an anchor, and can be shared between |
| accesses to the same 64k page. */ |
| #undef TARGET_MIN_ANCHOR_OFFSET |
| #define TARGET_MIN_ANCHOR_OFFSET -0x7fffffff - 1 |
| #undef TARGET_MAX_ANCHOR_OFFSET |
| #define TARGET_MAX_ANCHOR_OFFSET 0x7fffffff |
| #undef TARGET_USE_BLOCKS_FOR_CONSTANT_P |
| #define TARGET_USE_BLOCKS_FOR_CONSTANT_P rs6000_use_blocks_for_constant_p |
| |
| struct gcc_target targetm = TARGET_INITIALIZER; |
| |
| |
| /* Value is 1 if hard register REGNO can hold a value of machine-mode |
| MODE. */ |
| static int |
| rs6000_hard_regno_mode_ok (int regno, enum machine_mode mode) |
| { |
| /* The GPRs can hold any mode, but values bigger than one register |
| cannot go past R31. */ |
| if (INT_REGNO_P (regno)) |
| return INT_REGNO_P (regno + HARD_REGNO_NREGS (regno, mode) - 1); |
| |
| /* The float registers can only hold floating modes and DImode. |
| This also excludes decimal float modes. */ |
| if (FP_REGNO_P (regno)) |
| return |
| (SCALAR_FLOAT_MODE_P (mode) |
| && !DECIMAL_FLOAT_MODE_P (mode) |
| && FP_REGNO_P (regno + HARD_REGNO_NREGS (regno, mode) - 1)) |
| || (GET_MODE_CLASS (mode) == MODE_INT |
| && GET_MODE_SIZE (mode) == UNITS_PER_FP_WORD); |
| |
| /* The CR register can only hold CC modes. */ |
| if (CR_REGNO_P (regno)) |
| return GET_MODE_CLASS (mode) == MODE_CC; |
| |
| if (XER_REGNO_P (regno)) |
| return mode == PSImode; |
| |
| /* AltiVec only in AldyVec registers. */ |
| if (ALTIVEC_REGNO_P (regno)) |
| return ALTIVEC_VECTOR_MODE (mode); |
| |
| /* ...but GPRs can hold SIMD data on the SPE in one register. */ |
| if (SPE_SIMD_REGNO_P (regno) && TARGET_SPE && SPE_VECTOR_MODE (mode)) |
| return 1; |
| |
| /* We cannot put TImode anywhere except general register and it must be |
| able to fit within the register set. */ |
| |
| return GET_MODE_SIZE (mode) <= UNITS_PER_WORD; |
| } |
| |
| /* Initialize rs6000_hard_regno_mode_ok_p table. */ |
| static void |
| rs6000_init_hard_regno_mode_ok (void) |
| { |
| int r, m; |
| |
| for (r = 0; r < FIRST_PSEUDO_REGISTER; ++r) |
| for (m = 0; m < NUM_MACHINE_MODES; ++m) |
| if (rs6000_hard_regno_mode_ok (r, m)) |
| rs6000_hard_regno_mode_ok_p[m][r] = true; |
| } |
| |
| /* APPLE LOCAL begin axe stubs 5571540 */ |
| #ifndef DARWIN_LINKER_GENERATES_ISLANDS |
| #define DARWIN_LINKER_GENERATES_ISLANDS 0 |
| #endif |
| |
| /* KEXTs still need branch islands. */ |
| #define DARWIN_GENERATE_ISLANDS (!DARWIN_LINKER_GENERATES_ISLANDS \ |
| || flag_mkernel || flag_apple_kext \ |
| || (!flag_pic && !MACHO_DYNAMIC_NO_PIC_P)) |
| /* APPLE LOCAL end axe stubs 5571540 */ |
| |
| /* APPLE LOCAL begin mainline 2007-02-20 5005743 */ \ |
| #if TARGET_MACHO |
| /* The Darwin version of SUBTARGET_OVERRIDE_OPTIONS. */ |
| |
| static void |
| darwin_rs6000_override_options (void) |
| { |
| /* The Darwin ABI always includes AltiVec, can't be (validly) turned |
| off. */ |
| rs6000_altivec_abi = 1; |
| TARGET_ALTIVEC_VRSAVE = 1; |
| |
| /* APPLE LOCAL begin ARM 5683689 */ |
| if (!darwin_macosx_version_min && !darwin_iphoneos_version_min) |
| darwin_macosx_version_min = "10.1"; |
| /* APPLE LOCAL end ARM 5683689 */ |
| |
| /* APPLE LOCAL begin ARM 5683689 */ |
| /* APPLE LOCAL begin constant cfstrings */ |
| if (darwin_constant_cfstrings < 0) |
| darwin_constant_cfstrings = |
| darwin_iphoneos_version_min |
| || (strverscmp (darwin_macosx_version_min, "10.2") >= 0); |
| /* APPLE LOCAL end constant cfstrings */ |
| /* APPLE LOCAL end ARM 5683689 */ |
| |
| if (DEFAULT_ABI == ABI_DARWIN) |
| { |
| if (MACHO_DYNAMIC_NO_PIC_P) |
| { |
| if (flag_pic) |
| warning (0, "-mdynamic-no-pic overrides -fpic or -fPIC"); |
| flag_pic = 0; |
| } |
| else if (flag_pic == 1) |
| { |
| flag_pic = 2; |
| } |
| /* APPLE LOCAL begin longcall */ |
| if (TARGET_64BIT && TARGET_MACHO) |
| rs6000_default_long_calls = 0; |
| /* APPLE LOCAL end longcall */ |
| } |
| if (TARGET_64BIT && ! TARGET_POWERPC64) |
| { |
| target_flags |= MASK_POWERPC64; |
| warning (0, "-m64 requires PowerPC64 architecture, enabling"); |
| } |
| if (flag_mkernel) |
| /* APPLE LOCAL begin 5731065 */ |
| /* Moved setting of SOFT_FLOAT into rs6000_handle_option. */ |
| rs6000_default_long_calls = 1; |
| /* APPLE LOCAL end 5731065 */ |
| |
| /* Make -m64 imply -maltivec. Darwin's 64-bit ABI includes |
| Altivec. */ |
| if (!flag_mkernel && !flag_apple_kext |
| && TARGET_64BIT |
| && ! (target_flags_explicit & MASK_ALTIVEC)) |
| target_flags |= MASK_ALTIVEC; |
| |
| /* Unless the user (not the configurer) has explicitly overridden |
| it with -mcpu=G3 or -mno-altivec, then 10.5+ targets default to |
| G4 unless targetting the kernel. */ |
| if (!flag_mkernel |
| && !flag_apple_kext |
| /* APPLE LOCAL ARM 5683689 */ |
| && darwin_macosx_version_min |
| && strverscmp (darwin_macosx_version_min, "10.5") >= 0 |
| && ! (target_flags_explicit & MASK_ALTIVEC) |
| && ! rs6000_select[1].string) |
| { |
| target_flags |= MASK_ALTIVEC; |
| } |
| |
| /* APPLE LOCAL begin axe stubs 5571540 */ |
| /* I'm not sure if the branch island code needs stubs or not, so |
| assume they do. */ |
| if (DARWIN_GENERATE_ISLANDS) |
| darwin_stubs = true; |
| /* APPLE LOCAL end axe stubs 5571540 */ |
| /* APPLE LOCAL begin stack-protector default 5095227 */ |
| /* Don't enable -fstack-protector by default for kexts on darwin ppc |
| targeting 10.6 because there's a bug that it exposes in some kext, |
| <rdar://problem/6034665>. */ |
| if (flag_stack_protect == -1 |
| && (flag_mkernel || flag_apple_kext) |
| && strcmp (darwin_macosx_version_min, "10.6") == 0) |
| flag_stack_protect = 0; |
| /* APPLE LOCAL end stack-protector default 5095227 */ |
| } |
| #endif |
| |
| /* APPLE LOCAL end mainline 2007-02-20 5005743 */ \ |
| /* If not otherwise specified by a target, make 'long double' equivalent to |
| 'double'. */ |
| |
| #ifndef RS6000_DEFAULT_LONG_DOUBLE_SIZE |
| #define RS6000_DEFAULT_LONG_DOUBLE_SIZE 64 |
| #endif |
| |
| /* 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; |
| /* APPLE LOCAL -fast or -fastf or -fastcp */ |
| enum processor_type mcpu_cpu = PROCESSOR_POWER4; |
| |
| /* Simplifications for entries below. */ |
| |
| enum { |
| POWERPC_BASE_MASK = MASK_POWERPC | MASK_NEW_MNEMONICS, |
| POWERPC_7400_MASK = POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_ALTIVEC |
| }; |
| |
| /* This table occasionally claims that a processor does not support |
| a particular feature even though it does, but the feature is slower |
| than the alternative. Thus, it shouldn't be relied on as a |
| complete description of the processor's support. |
| |
| Please keep this list in order, and don't forget to update the |
| documentation in invoke.texi when adding a new processor or |
| flag. */ |
| static struct ptt |
| { |
| const char *const name; /* Canonical processor name. */ |
| const enum processor_type processor; /* Processor type enum value. */ |
| const int target_enable; /* Target flags to enable. */ |
| } const processor_target_table[] |
| = {{"401", PROCESSOR_PPC403, POWERPC_BASE_MASK | MASK_SOFT_FLOAT}, |
| {"403", PROCESSOR_PPC403, |
| POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_STRICT_ALIGN}, |
| {"405", PROCESSOR_PPC405, |
| POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB}, |
| {"405fp", PROCESSOR_PPC405, |
| POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB}, |
| {"440", PROCESSOR_PPC440, |
| POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB}, |
| {"440fp", PROCESSOR_PPC440, |
| POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB}, |
| {"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 | MASK_STRICT_ALIGN}, |
| /* 8548 has a dummy entry for now. */ |
| {"8548", PROCESSOR_PPC8540, |
| POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_STRICT_ALIGN}, |
| {"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_POWERPC64 | MASK_PPC_GFXOPT |
| | MASK_MFCRF | MASK_POPCNTB}, |
| {"power5+", PROCESSOR_POWER5, |
| POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GFXOPT |
| | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND}, |
| {"power6", PROCESSOR_POWER5, |
| POWERPC_7400_MASK | MASK_POWERPC64 | MASK_MFCRF | MASK_POPCNTB |
| | MASK_FPRND}, |
| {"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK}, |
| {"powerpc64", PROCESSOR_POWERPC64, |
| POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64}, |
| {"rios", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, |
| {"rios1", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, |
| {"rios2", PROCESSOR_RIOS2, |
| MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING}, |
| {"rsc", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, |
| {"rsc1", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING}, |
| {"rs64", PROCESSOR_RS64A, |
| POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64} |
| }; |
| |
| const size_t ptt_size = ARRAY_SIZE (processor_target_table); |
| |
| /* APPLE LOCAL begin -mmultiple/-mstring fixme */ |
| /* Save current -mmultiple/-mno-multiple status. */ |
| int multiple = (target_flags & MASK_MULTIPLE); |
| /* Save current -mstring/-mno-string status. */ |
| int string = (target_flags & MASK_STRING); |
| /* APPLE LOCAL end -mmultiple/-mstring fixme */ |
| |
| /* Some OSs don't support saving the high part of 64-bit registers on |
| context switch. Other OSs don't support saving Altivec registers. |
| On those OSs, we don't touch the MASK_POWERPC64 or MASK_ALTIVEC |
| settings; if the user wants either, the user must explicitly specify |
| them and we won't interfere with the user's specification. */ |
| |
| enum { |
| POWER_MASKS = MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING, |
| POWERPC_MASKS = (POWERPC_BASE_MASK | MASK_PPC_GPOPT | MASK_STRICT_ALIGN |
| | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_ALTIVEC |
| | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_MULHW |
| | MASK_DLMZB) |
| }; |
| |
| rs6000_init_hard_regno_mode_ok (); |
| |
| 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 by the processor default if given explicitly. */ |
| set_masks &= ~target_flags_explicit; |
| |
| /* Identify the processor type. */ |
| rs6000_select[0].string = default_cpu; |
| rs6000_cpu = TARGET_POWERPC64 ? PROCESSOR_DEFAULT64 : PROCESSOR_DEFAULT; |
| |
| /* LLVM LOCAL begin */ |
| #ifdef ENABLE_LLVM |
| rs6000_cpu_target = TARGET_POWERPC64 ? "ppc64" : "ppc"; |
| #endif |
| /* LLVM LOCAL end */ |
| |
| /* APPLE LOCAL begin -fast or -fastf or -fastcp */ |
| if (flag_fast || flag_fastf || flag_fastcp) |
| { |
| if (rs6000_select[1].string == (char *)0 && rs6000_select[2].string == (char *)0) |
| { |
| /* -mcpu and -mtune unspecified. Assume both are G5 */ |
| rs6000_select[1].string = "G5"; |
| rs6000_select[2].string = "G5"; |
| } |
| } |
| /* APPLE LOCAL end -fast or -fastf or -fastcp */ |
| |
| 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); |
| /* APPLE LOCAL begin -fast or -fastf or -fastcp */ |
| mcpu_cpu = processor_target_table[j].processor; |
| /* APPLE LOCAL end -fast or -fastf or -fastcp */ |
| /* LLVM LOCAL begin */ |
| #ifdef ENABLE_LLVM |
| rs6000_cpu_target = processor_target_table[j].name; |
| #endif |
| /* LLVM LOCAL end */ |
| } |
| break; |
| } |
| |
| if (j == ptt_size) |
| error ("bad value (%s) for %s switch", ptr->string, ptr->name); |
| } |
| } |
| |
| /* APPLE LOCAL begin AltiVec */ |
| /* If '-maltivec' has been specified or if anything else turns on |
| AltiVec, enable AltiVec optimizations, even if previously turned |
| off via '-faltivec'. */ |
| if (TARGET_ALTIVEC) |
| flag_disable_opts_for_faltivec = 0; |
| |
| /* Handle -m(no-)pim-altivec. */ |
| if (rs6000_altivec_pim) |
| { |
| /* If '-faltivec' or '-mpim-altivec' has been specified and we |
| have not already selected AltiVec codegen, disable certain |
| unsafe AltiVec optimizations so that the resulting binary can |
| run on a G3. These may be re-enabled by subsequently |
| specifying '-maltivec' or '-mcpu=xxx', where xxx supports |
| AltiVec instructions. */ |
| if (! TARGET_ALTIVEC) |
| { |
| flag_disable_opts_for_faltivec = 1; |
| /* APPLE LOCAL radar 4161346 */ |
| target_flags |= MASK_ALTIVEC; |
| } |
| /* APPLE LOCAL radar 5822514 */ |
| target_flags |= MASK_PIM_ALTIVEC; |
| } |
| /* APPLE LOCAL end AltiVec */ |
| |
| /* APPLE LOCAL begin -fast or -fastf or -fastcp */ |
| if (flag_fast || flag_fastf || flag_fastcp) |
| { |
| flag_gcse_sm = 1; |
| rs6000_sched_insert_nops = sched_finish_regroup_exact; |
| flag_unroll_loops = 1; |
| flag_tree_loop_linear = 1; |
| flag_strict_aliasing = 1; |
| flag_schedule_interblock = 1; |
| flag_gcse_las = 1; |
| align_jumps_max_skip = 15; |
| align_loops_max_skip = 15; |
| align_functions = 16; |
| align_loops = 16; |
| align_jumps = 16; |
| set_fast_math_flags (1); |
| flag_reorder_blocks = 1; |
| if (flag_branch_probabilities && !flag_exceptions) |
| flag_reorder_blocks_and_partition = 1; |
| /* LLVM LOCAL begin compile on non-darwin targets */ |
| #if TARGET_MACHO |
| if (!flag_pic) |
| target_flags |= MASK_MACHO_DYNAMIC_NO_PIC; |
| #endif |
| /* LLVM LOCAL end */ |
| |
| if (mcpu_cpu == PROCESSOR_POWER4) |
| { |
| target_flags |= MASK_POWERPC; |
| target_flags_explicit |= MASK_POWERPC; |
| } |
| if (flag_fast || flag_fastcp) |
| /* This doesn't work with NAG Fortran output. The gcc 3.5 C++ libraries |
| have been adjusted so that it now works with them. */ |
| rs6000_alignment_flags = MASK_ALIGN_NATURAL; |
| if (flag_fastf) |
| /* This applies Fortran argument semantics; for NAG Fortran output only. */ |
| flag_argument_noalias = 2; |
| /* IMI flags */ |
| disable_typechecking_for_spec_flag = 1; |
| flag_unit_at_a_time = 1; |
| } |
| |
| /* rs6000_init_hard_regno_mode_ok must come AFTER setting of -fast flags. */ |
| rs6000_init_hard_regno_mode_ok (); |
| |
| /* APPLE LOCAL end -fast or -fastf or -fastcp */ |
| |
| if (TARGET_E500) |
| rs6000_isel = 1; |
| |
| /* APPLE LOCAL begin Disable string insns with -Os on Darwin (radar 3509006) */ |
| /* If we are optimizing big endian systems for space, use the load/store |
| multiple instructions. */ |
| if (BYTES_BIG_ENDIAN && optimize_size) |
| target_flags |= ~target_flags_explicit & MASK_MULTIPLE; |
| |
| /* If we are optimizing big endian systems for space, use the |
| string instructions. But do not do this for Darwin, as the |
| kernel can't properly support some hardware that doesn't have |
| these instructions. It's not clear that the compiler is the |
| right place to fix this, but that's how it is for now. See |
| *extensive* discussion in Radar 3509006. */ |
| if (BYTES_BIG_ENDIAN && optimize_size && DEFAULT_ABI != ABI_DARWIN) |
| target_flags |= MASK_STRING; |
| /* APPLE LOCAL end Disable string insns with -Os on Darwin (radar 3509006) */ |
| |
| /* APPLE LOCAL begin -mmultiple/-mstring fixme */ |
| /* If -mmultiple or -mno-multiple was explicitly used, don't |
| override with the processor default */ |
| if ((target_flags_explicit & MASK_MULTIPLE) != 0) |
| target_flags = (target_flags & ~MASK_MULTIPLE) | multiple; |
| |
| /* If -mstring or -mno-string was explicitly used, don't override |
| with the processor default. */ |
| if ((target_flags_explicit & MASK_STRING) != 0) |
| target_flags = (target_flags & ~MASK_STRING) | string; |
| /* APPLE LOCAL end -mmultiple/-mstring fixme */ |
| |
| /* 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 (0, "-mmultiple is not supported on little endian systems"); |
| } |
| |
| if (TARGET_STRING) |
| { |
| target_flags &= ~MASK_STRING; |
| if ((target_flags_explicit & MASK_STRING) != 0) |
| warning (0, "-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 %qs; expecting %<full%>, %<partial%> or %<none%>", |
| rs6000_traceback_name); |
| } |
| |
| if (!rs6000_explicit_options.long_double) |
| rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE; |
| |
| #ifndef POWERPC_LINUX |
| if (!rs6000_explicit_options.ieee) |
| rs6000_ieeequad = 1; |
| #endif |
| |
| /* Set Altivec ABI as default for powerpc64 linux. */ |
| if (TARGET_ELF && TARGET_64BIT) |
| { |
| rs6000_altivec_abi = 1; |
| TARGET_ALTIVEC_VRSAVE = 1; |
| } |
| |
| /* Set the Darwin64 ABI as default for 64-bit Darwin. */ |
| if (DEFAULT_ABI == ABI_DARWIN && TARGET_64BIT) |
| { |
| rs6000_darwin64_abi = 1; |
| #if TARGET_MACHO |
| darwin_one_byte_bool = 1; |
| /* APPLE LOCAL pragma reverse_bitfields */ |
| darwin_reverse_bitfields = 0; |
| #endif |
| /* Default to natural alignment, for better performance. */ |
| rs6000_alignment_flags = MASK_ALIGN_NATURAL; |
| } |
| |
| /* Place FP constants in the constant pool instead of TOC |
| if section anchors enabled. */ |
| if (flag_section_anchors) |
| TARGET_NO_FP_IN_TOC = 1; |
| |
| /* Handle -mtls-size option. */ |
| rs6000_parse_tls_size_option (); |
| |
| #ifdef SUBTARGET_OVERRIDE_OPTIONS |
| SUBTARGET_OVERRIDE_OPTIONS; |
| #endif |
| #ifdef SUBSUBTARGET_OVERRIDE_OPTIONS |
| SUBSUBTARGET_OVERRIDE_OPTIONS; |
| #endif |
| #ifdef SUB3TARGET_OVERRIDE_OPTIONS |
| SUB3TARGET_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; |
| } |
| 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_explicit_options.abi) |
| rs6000_spe_abi = 0; |
| if (!rs6000_explicit_options.spe) |
| rs6000_spe = 0; |
| if (!rs6000_explicit_options.float_gprs) |
| rs6000_float_gprs = 0; |
| if (!rs6000_explicit_options.isel) |
| rs6000_isel = 0; |
| if (!rs6000_explicit_options.long_double) |
| rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE; |
| } |
| |
| rs6000_always_hint = (rs6000_cpu != PROCESSOR_POWER4 |
| && rs6000_cpu != PROCESSOR_POWER5); |
| rs6000_sched_groups = (rs6000_cpu == PROCESSOR_POWER4 |
| || rs6000_cpu == PROCESSOR_POWER5); |
| |
| rs6000_sched_restricted_insns_priority |
| = (rs6000_sched_groups ? 1 : 0); |
| |
| /* APPLE LOCAL begin only consider true dependency for grouping */ |
| /* Handle -msched-costly-dep option. */ |
| rs6000_sched_costly_dep |
| = (rs6000_sched_groups ? true_store_to_load_dep_costly : no_dep_costly); |
| /* APPLE LOCAL end only consider true dependency for grouping */ |
| |
| 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 aix_struct_return last, after the ABI is determined. |
| If -maix-struct-return or -msvr4-struct-return was explicitly |
| used, don't override with the ABI default. */ |
| if (!rs6000_explicit_options.aix_struct_ret) |
| aix_struct_return = (DEFAULT_ABI != ABI_V4 || DRAFT_V4_STRUCT_RET); |
| |
| if (TARGET_LONG_DOUBLE_128 && !TARGET_IEEEQUAD) |
| REAL_MODE_FORMAT (TFmode) = &ibm_extended_format; |
| |
| if (TARGET_TOC) |
| ASM_GENERATE_INTERNAL_LABEL (toc_label_name, "LCTOC", 1); |
| |
| /* We can only guarantee the availability of DI pseudo-ops when |
| assembling for 64-bit targets. */ |
| if (!TARGET_64BIT) |
| { |
| targetm.asm_out.aligned_op.di = NULL; |
| targetm.asm_out.unaligned_op.di = NULL; |
| } |
| |
| /* Set branch target alignment, if not optimizing for size. */ |
| if (!optimize_size) |
| { |
| if (rs6000_sched_groups) |
| { |
| if (align_functions <= 0) |
| align_functions = 16; |
| if (align_jumps <= 0) |
| align_jumps = 16; |
| if (align_loops <= 0) |
| align_loops = 16; |
| } |
| if (align_jumps_max_skip <= 0) |
| align_jumps_max_skip = 15; |
| if (align_loops_max_skip <= 0) |
| align_loops_max_skip = 15; |
| } |
| |
| /* Arrange to save and restore machine status around nested functions. */ |
| init_machine_status = rs6000_init_machine_status; |
| |
| /* 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; |
| |
| /* APPLE LOCAL begin AltiVec */ |
| /* Enable '(vector signed int)(a, b, c, d)' vector literal notation. */ |
| if (rs6000_altivec_pim) |
| targetm.cast_expr_as_vector_init = true; |
| /* APPLE LOCAL end AltiVec */ |
| |
| /* Initialize rs6000_cost with the appropriate target costs. */ |
| if (optimize_size) |
| rs6000_cost = TARGET_POWERPC64 ? &size64_cost : &size32_cost; |
| else |
| switch (rs6000_cpu) |
| { |
| case PROCESSOR_RIOS1: |
| rs6000_cost = &rios1_cost; |
| break; |
| |
| case PROCESSOR_RIOS2: |
| rs6000_cost = &rios2_cost; |
| break; |
| |
| case PROCESSOR_RS64A: |
| rs6000_cost = &rs64a_cost; |
| break; |
| |
| case PROCESSOR_MPCCORE: |
| rs6000_cost = &mpccore_cost; |
| break; |
| |
| case PROCESSOR_PPC403: |
| rs6000_cost = &ppc403_cost; |
| break; |
| |
| case PROCESSOR_PPC405: |
| rs6000_cost = &ppc405_cost; |
| break; |
| |
| case PROCESSOR_PPC440: |
| rs6000_cost = &ppc440_cost; |
| break; |
| |
| case PROCESSOR_PPC601: |
| rs6000_cost = &ppc601_cost; |
| break; |
| |
| case PROCESSOR_PPC603: |
| rs6000_cost = &ppc603_cost; |
| break; |
| |
| case PROCESSOR_PPC604: |
| rs6000_cost = &ppc604_cost; |
| break; |
| |
| case PROCESSOR_PPC604e: |
| rs6000_cost = &ppc604e_cost; |
| break; |
| |
| case PROCESSOR_PPC620: |
| rs6000_cost = &ppc620_cost; |
| break; |
| |
| case PROCESSOR_PPC630: |
| rs6000_cost = &ppc630_cost; |
| break; |
| |
| case PROCESSOR_PPC750: |
| case PROCESSOR_PPC7400: |
| rs6000_cost = &ppc750_cost; |
| break; |
| |
| case PROCESSOR_PPC7450: |
| rs6000_cost = &ppc7450_cost; |
| break; |
| |
| case PROCESSOR_PPC8540: |
| rs6000_cost = &ppc8540_cost; |
| break; |
| |
| case PROCESSOR_POWER4: |
| case PROCESSOR_POWER5: |
| rs6000_cost = &power4_cost; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Implement targetm.vectorize.builtin_mask_for_load. */ |
| static tree |
| rs6000_builtin_mask_for_load (void) |
| { |
| if (TARGET_ALTIVEC) |
| return altivec_builtin_mask_for_load; |
| else |
| return 0; |
| } |
| |
| /* APPLE LOCAL begin mainline 4.2 5569774 */ |
| /* Return true iff, data reference of TYPE can reach vector alignment (16) |
| after applying N number of iterations. This routine does not determine |
| how may iterations are required to reach desired alignment. */ |
| |
| static bool |
| rs6000_vector_alignment_reachable (tree type ATTRIBUTE_UNUSED, bool is_packed) |
| { |
| if (is_packed) |
| return false; |
| |
| if (TARGET_32BIT) |
| { |
| if (rs6000_alignment_flags == MASK_ALIGN_NATURAL) |
| return true; |
| |
| if (rs6000_alignment_flags == MASK_ALIGN_POWER) |
| return true; |
| |
| return false; |
| } |
| else |
| { |
| if (TARGET_MACHO) |
| /* APPLE LOCAL 5643197 */ |
| return (rs6000_alignment_flags == MASK_ALIGN_NATURAL); |
| |
| /* Assuming that all other types are naturally aligned. CHECKME! */ |
| return true; |
| } |
| } |
| /* APPLE LOCAL end mainline 4.2 5569774 */ |
| |
| /* 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); |
| } |
| |
| /* Validate and record the size specified with the -mtls-size option. */ |
| |
| static void |
| rs6000_parse_tls_size_option (void) |
| { |
| if (rs6000_tls_size_string == 0) |
| return; |
| else if (strcmp (rs6000_tls_size_string, "16") == 0) |
| rs6000_tls_size = 16; |
| else if (strcmp (rs6000_tls_size_string, "32") == 0) |
| rs6000_tls_size = 32; |
| else if (strcmp (rs6000_tls_size_string, "64") == 0) |
| rs6000_tls_size = 64; |
| else |
| error ("bad value %qs for -mtls-size switch", rs6000_tls_size_string); |
| } |
| |
| /* APPLE LOCAL begin outwit script - cvs diff is inconsistent about |
| which of the }'s in the next 2 functions represents a local change */ |
| void |
| optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED) |
| { |
| /* APPLE LOCAL begin tweak default optimizations */ |
| if (DEFAULT_ABI == ABI_DARWIN) |
| { |
| /* Turn these on only if specifically requested, not with -O* */ |
| /* Strict aliasing breaks too much existing code */ |
| flag_strict_aliasing = 0; |
| /* Block reordering causes code bloat, and very little speedup */ |
| flag_reorder_blocks = 0; |
| /* Multi-basic-block scheduling loses badly when the compiler |
| misguesses which blocks are going to be executed, more than |
| it gains when it guesses correctly. Its guesses for cases |
| where interblock scheduling occurs (if-then-else's) are |
| little better than random, so disable this unless requested. */ |
| flag_schedule_interblock = 0; |
| /* Trapping math is not needed by many users, and is expensive. |
| C99 permits us to default it off and we do that. It is |
| turned on when <fenv.h> is included (see darwin_pragma_fenv |
| in darwin-c.c). */ |
| flag_trapping_math = 0; |
| /* APPLE LOCAL begin 3893112 */ |
| /* This value may be temporary; dje will have an opinion at some point. |
| 36 is what x86 uses and ppc should be at least as big. */ |
| set_param_value ("sra-max-structure-size", 36); |
| /* Another parameter has been added in mainline, which by default |
| should be parallel to the one above. */ |
| set_param_value ("sra-max-structure-count", |
| SRA_MAX_STRUCTURE_SIZE / UNITS_PER_WORD); |
| /* APPLE LOCAL end 3893112 */ |
| } |
| /* APPLE LOCAL end tweak default optimizations */ |
| if (DEFAULT_ABI == ABI_DARWIN) |
| /* The Darwin libraries never set errno, so we might as well |
| avoid calling them when that's the only reason we would. */ |
| flag_errno_math = 0; |
| |
| /* Double growth factor to counter reduced min jump length. */ |
| set_param_value ("max-grow-copy-bb-insns", 16); |
| |
| /* Enable section anchors by default. |
| Skip section anchors for Objective C and Objective C++ |
| until front-ends fixed. */ |
| if (!TARGET_MACHO && lang_hooks.name[4] != 'O') |
| flag_section_anchors = 1; |
| } |
| |
| /* APPLE LOCAL begin optimization pragmas 3124235/3420242 */ |
| /* Version of the above for use from #pragma optimization_level. |
| Do not reset things unless they're per-function. */ |
| #if TARGET_MACHO |
| void |
| /* APPLE LOCAL begin 4760857 optimization pragmas */ |
| reset_optimization_options (int level ATTRIBUTE_UNUSED, int size) |
| /* APPLE LOCAL end 4760857 optimization pragmas */ |
| { |
| if (DEFAULT_ABI == ABI_DARWIN) |
| { |
| /* Block reordering causes code bloat, and very little speedup */ |
| flag_reorder_blocks = 0; |
| /* Multi-basic-block scheduling loses badly when the compiler |
| misguesses which blocks are going to be executed, more than |
| it gains when it guesses correctly. Its guesses for cases |
| where interblock scheduling occurs (if-then-else's) are |
| little better than random, so disable this unless requested. */ |
| flag_schedule_interblock = 0; |
| /* The Darwin libraries never set errno, so we might as well |
| avoid calling them when that's the only reason we would. */ |
| flag_errno_math = 0; |
| /* Trapping math is not needed by many users, and is expensive. |
| C99 permits us to default it off and we do that. It is |
| turned on when <fenv.h> is included (see darwin_pragma_fenv |
| in darwin-c.c). */ |
| flag_trapping_math = 0; |
| } |
| /* APPLE LOCAL begin 4760857 optimization pragmas */ |
| /* Set branch target alignment, if not optimizing for size. */ |
| if (!size) |
| { |
| if (rs6000_sched_groups) |
| { |
| if (align_jumps <= 0) |
| align_jumps = 16; |
| if (align_loops <= 0) |
| align_loops = 16; |
| } |
| if (align_jumps_max_skip <= 0) |
| align_jumps_max_skip = 15; |
| if (align_loops_max_skip <= 0) |
| align_loops_max_skip = 15; |
| } |
| /* APPLE LOCAL end 4760857 optimization pragmas */ |
| } |
| #endif |
| /* APPLE LOCAL end optimization pragmas 3124235/3420242 */ |
| /* APPLE LOCAL end outwit script */ |
| |
| /* Implement TARGET_HANDLE_OPTION. */ |
| |
| static bool |
| rs6000_handle_option (size_t code, const char *arg, int value) |
| { |
| switch (code) |
| { |
| case OPT_mno_power: |
| target_flags &= ~(MASK_POWER | MASK_POWER2 |
| | MASK_MULTIPLE | MASK_STRING); |
| target_flags_explicit |= (MASK_POWER | MASK_POWER2 |
| | MASK_MULTIPLE | MASK_STRING); |
| break; |
| case OPT_mno_powerpc: |
| target_flags &= ~(MASK_POWERPC | MASK_PPC_GPOPT |
| | MASK_PPC_GFXOPT | MASK_POWERPC64); |
| target_flags_explicit |= (MASK_POWERPC | MASK_PPC_GPOPT |
| | MASK_PPC_GFXOPT | MASK_POWERPC64); |
| break; |
| case OPT_mfull_toc: |
| target_flags &= ~MASK_MINIMAL_TOC; |
| TARGET_NO_FP_IN_TOC = 0; |
| TARGET_NO_SUM_IN_TOC = 0; |
| target_flags_explicit |= MASK_MINIMAL_TOC; |
| #ifdef TARGET_USES_SYSV4_OPT |
| /* Note, V.4 no longer uses a normal TOC, so make -mfull-toc, be |
| just the same as -mminimal-toc. */ |
| target_flags |= MASK_MINIMAL_TOC; |
| target_flags_explicit |= MASK_MINIMAL_TOC; |
| #endif |
| break; |
| |
| #ifdef TARGET_USES_SYSV4_OPT |
| case OPT_mtoc: |
| /* Make -mtoc behave like -mminimal-toc. */ |
| target_flags |= MASK_MINIMAL_TOC; |
| target_flags_explicit |= MASK_MINIMAL_TOC; |
| break; |
| #endif |
| |
| #ifdef TARGET_USES_AIX64_OPT |
| case OPT_maix64: |
| #else |
| case OPT_m64: |
| #endif |
| target_flags |= MASK_POWERPC64 | MASK_POWERPC; |
| target_flags |= ~target_flags_explicit & MASK_PPC_GFXOPT; |
| target_flags_explicit |= MASK_POWERPC64 | MASK_POWERPC; |
| break; |
| |
| #ifdef TARGET_USES_AIX64_OPT |
| case OPT_maix32: |
| #else |
| case OPT_m32: |
| #endif |
| target_flags &= ~MASK_POWERPC64; |
| target_flags_explicit |= MASK_POWERPC64; |
| break; |
| |
| case OPT_minsert_sched_nops_: |
| rs6000_sched_insert_nops_str = arg; |
| break; |
| |
| case OPT_mminimal_toc: |
| if (value == 1) |
| { |
| TARGET_NO_FP_IN_TOC = 0; |
| TARGET_NO_SUM_IN_TOC = 0; |
| } |
| break; |
| |
| case OPT_mpower: |
| if (value == 1) |
| { |
| target_flags |= (MASK_MULTIPLE | MASK_STRING); |
| target_flags_explicit |= (MASK_MULTIPLE | MASK_STRING); |
| } |
| break; |
| |
| case OPT_mpower2: |
| if (value == 1) |
| { |
| target_flags |= (MASK_POWER | MASK_MULTIPLE | MASK_STRING); |
| target_flags_explicit |= (MASK_POWER | MASK_MULTIPLE | MASK_STRING); |
| } |
| break; |
| |
| case OPT_mpowerpc_gpopt: |
| case OPT_mpowerpc_gfxopt: |
| if (value == 1) |
| { |
| target_flags |= MASK_POWERPC; |
| target_flags_explicit |= MASK_POWERPC; |
| } |
| break; |
| |
| case OPT_maix_struct_return: |
| case OPT_msvr4_struct_return: |
| rs6000_explicit_options.aix_struct_ret = true; |
| break; |
| |
| case OPT_mvrsave_: |
| rs6000_parse_yes_no_option ("vrsave", arg, &(TARGET_ALTIVEC_VRSAVE)); |
| break; |
| |
| case OPT_misel_: |
| rs6000_explicit_options.isel = true; |
| rs6000_parse_yes_no_option ("isel", arg, &(rs6000_isel)); |
| break; |
| |
| case OPT_mspe_: |
| rs6000_explicit_options.spe = true; |
| rs6000_parse_yes_no_option ("spe", arg, &(rs6000_spe)); |
| /* No SPE means 64-bit long doubles, even if an E500. */ |
| if (!rs6000_spe) |
| rs6000_long_double_type_size = 64; |
| break; |
| |
| case OPT_mdebug_: |
| rs6000_debug_name = arg; |
| break; |
| |
| #ifdef TARGET_USES_SYSV4_OPT |
| case OPT_mcall_: |
| rs6000_abi_name = arg; |
| break; |
| |
| case OPT_msdata_: |
| rs6000_sdata_name = arg; |
| break; |
| |
| case OPT_mtls_size_: |
| rs6000_tls_size_string = arg; |
| break; |
| |
| case OPT_mrelocatable: |
| if (value == 1) |
| { |
| target_flags |= MASK_MINIMAL_TOC; |
| target_flags_explicit |= MASK_MINIMAL_TOC; |
| TARGET_NO_FP_IN_TOC = 1; |
| } |
| break; |
| |
| case OPT_mrelocatable_lib: |
| if (value == 1) |
| { |
| target_flags |= MASK_RELOCATABLE | MASK_MINIMAL_TOC; |
| target_flags_explicit |= MASK_RELOCATABLE | MASK_MINIMAL_TOC; |
| TARGET_NO_FP_IN_TOC = 1; |
| } |
| else |
| { |
| target_flags &= ~MASK_RELOCATABLE; |
| target_flags_explicit |= MASK_RELOCATABLE; |
| } |
| break; |
| #endif |
| |
| case OPT_mabi_: |
| if (!strcmp (arg, "altivec")) |
| { |
| rs6000_explicit_options.abi = true; |
| rs6000_altivec_abi = 1; |
| rs6000_spe_abi = 0; |
| } |
| else if (! strcmp (arg, "no-altivec")) |
| { |
| /* ??? Don't set rs6000_explicit_options.abi here, to allow |
| the default for rs6000_spe_abi to be chosen later. */ |
| rs6000_altivec_abi = 0; |
| } |
| else if (! strcmp (arg, "spe")) |
| { |
| rs6000_explicit_options.abi = true; |
| rs6000_spe_abi = 1; |
| rs6000_altivec_abi = 0; |
| if (!TARGET_SPE_ABI) |
| error ("not configured for ABI: '%s'", arg); |
| } |
| else if (! strcmp (arg, "no-spe")) |
| { |
| rs6000_explicit_options.abi = true; |
| rs6000_spe_abi = 0; |
| } |
| |
| /* These are here for testing during development only, do not |
| document in the manual please. */ |
| else if (! strcmp (arg, "d64")) |
| { |
| rs6000_darwin64_abi = 1; |
| warning (0, "Using darwin64 ABI"); |
| } |
| else if (! strcmp (arg, "d32")) |
| { |
| rs6000_darwin64_abi = 0; |
| warning (0, "Using old darwin ABI"); |
| } |
| |
| else if (! strcmp (arg, "ibmlongdouble")) |
| { |
| rs6000_explicit_options.ieee = true; |
| rs6000_ieeequad = 0; |
| warning (0, "Using IBM extended precision long double"); |
| } |
| else if (! strcmp (arg, "ieeelongdouble")) |
| { |
| rs6000_explicit_options.ieee = true; |
| rs6000_ieeequad = 1; |
| warning (0, "Using IEEE extended precision long double"); |
| } |
| |
| else |
| { |
| error ("unknown ABI specified: '%s'", arg); |
| return false; |
| } |
| break; |
| |
| case OPT_mcpu_: |
| rs6000_select[1].string = arg; |
| break; |
| |
| case OPT_mtune_: |
| rs6000_select[2].string = arg; |
| break; |
| |
| case OPT_mtraceback_: |
| rs6000_traceback_name = arg; |
| break; |
| |
| case OPT_mfloat_gprs_: |
| rs6000_explicit_options.float_gprs = true; |
| if (! strcmp (arg, "yes") || ! strcmp (arg, "single")) |
| rs6000_float_gprs = 1; |
| else if (! strcmp (arg, "double")) |
| rs6000_float_gprs = 2; |
| else if (! strcmp (arg, "no")) |
| rs6000_float_gprs = 0; |
| else |
| { |
| error ("invalid option for -mfloat-gprs: '%s'", arg); |
| return false; |
| } |
| break; |
| |
| case OPT_mlong_double_: |
| rs6000_explicit_options.long_double = true; |
| rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE; |
| if (value != 64 && value != 128) |
| { |
| error ("Unknown switch -mlong-double-%s", arg); |
| rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE; |
| return false; |
| } |
| else |
| rs6000_long_double_type_size = value; |
| break; |
| |
| case OPT_msched_costly_dep_: |
| rs6000_sched_costly_dep_str = arg; |
| break; |
| |
| case OPT_malign_: |
| rs6000_explicit_options.alignment = true; |
| if (! strcmp (arg, "power")) |
| { |
| /* On 64-bit Darwin, power alignment is ABI-incompatible with |
| some C library functions, so warn about it. The flag may be |
| useful for performance studies from time to time though, so |
| don't disable it entirely. */ |
| if (DEFAULT_ABI == ABI_DARWIN && TARGET_64BIT) |
| warning (0, "-malign-power is not supported for 64-bit Darwin;" |
| " it is incompatible with the installed C and C++ libraries"); |
| rs6000_alignment_flags = MASK_ALIGN_POWER; |
| } |
| else if (! strcmp (arg, "natural")) |
| rs6000_alignment_flags = MASK_ALIGN_NATURAL; |
| /* APPLE LOCAL begin Macintosh alignment 2002-2-26 --ff */ |
| else if (! strcmp (arg, "mac68k")) |
| { |
| /* The old mac68k alignment has zero value for 64-bit work, |
| forbid its use. */ |
| if (DEFAULT_ABI == ABI_DARWIN && TARGET_64BIT) |
| error ("-malign-mac68k is not allowed for 64-bit Darwin"); |
| /* APPLE LOCAL begin radar 5134231 */ |
| rs6000_alignment_flags = OPTION_MASK_ALIGN_MAC68K; |
| /* APPLE LOCAL end radar 5134231 */ |
| } |
| /* APPLE LOCAL end Macintosh alignment 2002-2-26 --ff */ |
| else |
| { |
| error ("unknown -malign-XXXXX option specified: '%s'", arg); |
| return false; |
| } |
| break; |
| /* APPLE LOCAL begin 5731065 */ |
| case OPT_mkernel: |
| /* Set this early so that a kext that wants to use the hard |
| floating point registers can use -mkernel -mhard-float. */ |
| target_flags |= MASK_SOFT_FLOAT; |
| break; |
| /* APPLE LOCAL end 5731065 */ |
| } |
| return true; |
| } |
| |
| /* 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 = ""; |
| } |
| } |
| |
| if (PPC405_ERRATUM77) |
| { |
| fprintf (file, "%s PPC405CR_ERRATUM77", start); |
| 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_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); |
| } |
| |
| if (DEFAULT_ABI == ABI_AIX || (TARGET_ELF && flag_pic == 2)) |
| { |
| switch_to_section (toc_section); |
| switch_to_section (text_section); |
| } |
| |
| /* APPLE LOCAL begin hot/cold */ |
| /* MERGE FIXME push to FSF or remove as unneeded? */ |
| #if TARGET_MACHO |
| if (DEFAULT_ABI == ABI_DARWIN) |
| { |
| /* Emit declarations for all code sections at the beginning of the file; this |
| keeps them from being separated by data sections, which can lead to |
| out-of-range branches. Also align the unlikely text section properly; the |
| first real occurrence of this may be a label within a function, which does |
| not otherwise get aligned. */ |
| if (flag_pic || MACHO_DYNAMIC_NO_PIC_P || flag_reorder_blocks_and_partition) |
| { |
| fprintf (asm_out_file, "\t.section __TEXT,__text,regular,pure_instructions\n"); |
| if (flag_reorder_blocks_and_partition) |
| { |
| fprintf (asm_out_file, "\t.section __TEXT,__unlikely,regular,pure_instructions\n"); |
| fprintf (asm_out_file, "\t.align 2\n"); |
| } |
| if (MACHO_DYNAMIC_NO_PIC_P ) |
| { |
| fprintf (asm_out_file, "\t.section __TEXT,__symbol_stub1,"); |
| fprintf (asm_out_file, "symbol_stubs,pure_instructions,16\n"); |
| } |
| else |
| { |
| fprintf (asm_out_file, "\t.section __TEXT,__picsymbolstub1,"); |
| fprintf (asm_out_file, "symbol_stubs,pure_instructions,32\n"); |
| } |
| } |
| } |
| #endif |
| /* APPLE LOCAL end hot/cold */ |
| } |
| |
| |
| /* 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; |
| } |
| |
| /* Return the number of instructions it takes to form a constant in an |
| integer register. */ |
| |
| int |
| num_insns_constant_wide (HOST_WIDE_INT value) |
| { |
| /* signed constant loadable with {cal|addi} */ |
| if ((unsigned HOST_WIDE_INT) (value + 0x8000) < 0x10000) |
| return 1; |
| |
| /* constant loadable with {cau|addis} */ |
| else if ((value & 0xffff) == 0 |
| && (value >> 31 == -1 || value >> 31 == 0)) |
| 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) |
| { |
| HOST_WIDE_INT low, high; |
| |
| switch (GET_CODE (op)) |
| { |
| case 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)); |
| |
| case CONST_DOUBLE: |
| 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 ((HOST_WIDE_INT) l); |
| } |
| |
| if (mode == VOIDmode || mode == DImode) |
| { |
| high = CONST_DOUBLE_HIGH (op); |
| low = CONST_DOUBLE_LOW (op); |
| } |
| else |
| { |
| long l[2]; |
| REAL_VALUE_TYPE rv; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, op); |
| REAL_VALUE_TO_TARGET_DOUBLE (rv, l); |
| high = l[WORDS_BIG_ENDIAN == 0]; |
| low = l[WORDS_BIG_ENDIAN != 0]; |
| } |
| |
| if (TARGET_32BIT) |
| return (num_insns_constant_wide (low) |
| + num_insns_constant_wide (high)); |
| else |
| { |
| if ((high == 0 && low >= 0) |
| || (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); |
| } |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Interpret element ELT of the CONST_VECTOR OP as an integer value. |
| If the mode of OP is MODE_VECTOR_INT, this simply returns the |
| corresponding element of the vector, but for V4SFmode and V2SFmode, |
| the corresponding "float" is interpreted as an SImode integer. */ |
| |
| static HOST_WIDE_INT |
| const_vector_elt_as_int (rtx op, unsigned int elt) |
| { |
| rtx tmp = CONST_VECTOR_ELT (op, elt); |
| if (GET_MODE (op) == V4SFmode |
| || GET_MODE (op) == V2SFmode) |
| tmp = gen_lowpart (SImode, tmp); |
| return INTVAL (tmp); |
| } |
| |
| /* Return true if OP can be synthesized with a particular vspltisb, vspltish |
| or vspltisw instruction. OP is a CONST_VECTOR. Which instruction is used |
| depends on STEP and COPIES, one of which will be 1. If COPIES > 1, |
| all items are set to the same value and contain COPIES replicas of the |
| vsplt's operand; if STEP > 1, one in STEP elements is set to the vsplt's |
| operand and the others are set to the value of the operand's msb. */ |
| |
| static bool |
| vspltis_constant (rtx op, unsigned step, unsigned copies) |
| { |
| enum machine_mode mode = GET_MODE (op); |
| enum machine_mode inner = GET_MODE_INNER (mode); |
| |
| unsigned i; |
| unsigned nunits = GET_MODE_NUNITS (mode); |
| unsigned bitsize = GET_MODE_BITSIZE (inner); |
| unsigned mask = GET_MODE_MASK (inner); |
| |
| HOST_WIDE_INT val = const_vector_elt_as_int (op, nunits - 1); |
| HOST_WIDE_INT splat_val = val; |
| HOST_WIDE_INT msb_val = val > 0 ? 0 : -1; |
| |
| /* Construct the value to be splatted, if possible. If not, return 0. */ |
| for (i = 2; i <= copies; i *= 2) |
| { |
| HOST_WIDE_INT small_val; |
| bitsize /= 2; |
| small_val = splat_val >> bitsize; |
| mask >>= bitsize; |
| if (splat_val != ((small_val << bitsize) | (small_val & mask))) |
| return false; |
| splat_val = small_val; |
| } |
| |
| /* Check if SPLAT_VAL can really be the operand of a vspltis[bhw]. */ |
| if (EASY_VECTOR_15 (splat_val)) |
| ; |
| |
| /* Also check if we can splat, and then add the result to itself. Do so if |
| the value is positive, of if the splat instruction is using OP's mode; |
| for splat_val < 0, the splat and the add should use the same mode. */ |
| else if (EASY_VECTOR_15_ADD_SELF (splat_val) |
| && (splat_val >= 0 || (step == 1 && copies == 1))) |
| ; |
| |
| else |
| return false; |
| |
| /* Check if VAL is present in every STEP-th element, and the |
| other elements are filled with its most significant bit. */ |
| for (i = 0; i < nunits - 1; ++i) |
| { |
| HOST_WIDE_INT desired_val; |
| if (((i + 1) & (step - 1)) == 0) |
| desired_val = val; |
| else |
| desired_val = msb_val; |
| |
| if (desired_val != const_vector_elt_as_int (op, i)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| /* Return true if OP is of the given MODE and can be synthesized |
| with a vspltisb, vspltish or vspltisw. */ |
| |
| bool |
| easy_altivec_constant (rtx op, enum machine_mode mode) |
| { |
| unsigned step, copies; |
| |
| if (mode == VOIDmode) |
| mode = GET_MODE (op); |
| else if (mode != GET_MODE (op)) |
| return false; |
| |
| /* Start with a vspltisw. */ |
| step = GET_MODE_NUNITS (mode) / 4; |
| copies = 1; |
| |
| if (vspltis_constant (op, step, copies)) |
| return true; |
| |
| /* Then try with a vspltish. */ |
| if (step == 1) |
| copies <<= 1; |
| else |
| step >>= 1; |
| |
| if (vspltis_constant (op, step, copies)) |
| return true; |
| |
| /* And finally a vspltisb. */ |
| if (step == 1) |
| copies <<= 1; |
| else |
| step >>= 1; |
| |
| if (vspltis_constant (op, step, copies)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Generate a VEC_DUPLICATE representing a vspltis[bhw] instruction whose |
| result is OP. Abort if it is not possible. */ |
| |
| rtx |
| gen_easy_altivec_constant (rtx op) |
| { |
| enum machine_mode mode = GET_MODE (op); |
| int nunits = GET_MODE_NUNITS (mode); |
| rtx last = CONST_VECTOR_ELT (op, nunits - 1); |
| unsigned step = nunits / 4; |
| unsigned copies = 1; |
| |
| /* Start with a vspltisw. */ |
| if (vspltis_constant (op, step, copies)) |
| return gen_rtx_VEC_DUPLICATE (V4SImode, gen_lowpart (SImode, last)); |
| |
| /* Then try with a vspltish. */ |
| if (step == 1) |
| copies <<= 1; |
| else |
| step >>= 1; |
| |
| if (vspltis_constant (op, step, copies)) |
| return gen_rtx_VEC_DUPLICATE (V8HImode, gen_lowpart (HImode, last)); |
| |
| /* And finally a vspltisb. */ |
| if (step == 1) |
| copies <<= 1; |
| else |
| step >>= 1; |
| |
| if (vspltis_constant (op, step, copies)) |
| return gen_rtx_VEC_DUPLICATE (V16QImode, gen_lowpart (QImode, last)); |
| |
| gcc_unreachable (); |
| } |
| |
| const char * |
| output_vec_const_move (rtx *operands) |
| { |
| int cst, cst2; |
| enum machine_mode mode; |
| rtx dest, vec; |
| |
| dest = operands[0]; |
| vec = operands[1]; |
| mode = GET_MODE (dest); |
| |
| if (TARGET_ALTIVEC) |
| { |
| rtx splat_vec; |
| if (zero_constant (vec, mode)) |
| return "vxor %0,%0,%0"; |
| |
| splat_vec = gen_easy_altivec_constant (vec); |
| gcc_assert (GET_CODE (splat_vec) == VEC_DUPLICATE); |
| operands[1] = XEXP (splat_vec, 0); |
| if (!EASY_VECTOR_15 (INTVAL (operands[1]))) |
| return "#"; |
| |
| switch (GET_MODE (splat_vec)) |
| { |
| case V4SImode: |
| return "vspltisw %0,%1"; |
| |
| case V8HImode: |
| return "vspltish %0,%1"; |
| |
| case V16QImode: |
| return "vspltisb %0,%1"; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| gcc_assert (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 ;-). */ |
| cst = INTVAL (CONST_VECTOR_ELT (vec, 0)); |
| cst2 = INTVAL (CONST_VECTOR_ELT (vec, 1)); |
| operands[1] = CONST_VECTOR_ELT (vec, 0); |
| operands[2] = CONST_VECTOR_ELT (vec, 1); |
| 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"; |
| } |
| |
| /* Initialize vector TARGET to VALS. */ |
| |
| void |
| rs6000_expand_vector_init (rtx target, rtx vals) |
| { |
| enum machine_mode mode = GET_MODE (target); |
| enum machine_mode inner_mode = GET_MODE_INNER (mode); |
| int n_elts = GET_MODE_NUNITS (mode); |
| int n_var = 0, one_var = -1; |
| bool all_same = true, all_const_zero = true; |
| rtx x, mem; |
| int i; |
| |
| for (i = 0; i < n_elts; ++i) |
| { |
| x = XVECEXP (vals, 0, i); |
| if (!CONSTANT_P (x)) |
| ++n_var, one_var = i; |
| else if (x != CONST0_RTX (inner_mode)) |
| all_const_zero = false; |
| |
| if (i > 0 && !rtx_equal_p (x, XVECEXP (vals, 0, 0))) |
| all_same = false; |
| } |
| |
| if (n_var == 0) |
| { |
| if (mode != V4SFmode && all_const_zero) |
| { |
| /* Zero register. */ |
| emit_insn (gen_rtx_SET (VOIDmode, target, |
| gen_rtx_XOR (mode, target, target))); |
| return; |
| } |
| else if (mode != V4SFmode && easy_vector_constant (vals, mode)) |
| { |
| /* Splat immediate. */ |
| emit_insn (gen_rtx_SET (VOIDmode, target, vals)); |
| return; |
| } |
| else if (all_same) |
| ; /* Splat vector element. */ |
| else |
| { |
| /* Load from constant pool. */ |
| emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0))); |
| return; |
| } |
| } |
| |
| /* Store value to stack temp. Load vector element. Splat. */ |
| if (all_same) |
| { |
| /* APPLE LOCAL begin 4708231 */ |
| rtx pmem; |
| |
| mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode), 0); |
| mem = adjust_address_nv (mem, inner_mode, 0); |
| |
| pmem = copy_addr_to_reg (XEXP (mem, 0)); |
| pmem = gen_rtx_MEM (mode, pmem); |
| |
| emit_move_insn (mem, XVECEXP (vals, 0, 0)); |
| x = gen_rtx_UNSPEC (VOIDmode, |
| gen_rtvec (1, const0_rtx), UNSPEC_LVE); |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, |
| gen_rtvec (2, |
| gen_rtx_SET (VOIDmode, |
| target, pmem), |
| x))); |
| /* APPLE LOCAL end 4708231 */ |
| |
| x = gen_rtx_VEC_SELECT (inner_mode, target, |
| gen_rtx_PARALLEL (VOIDmode, |
| gen_rtvec (1, const0_rtx))); |
| emit_insn (gen_rtx_SET (VOIDmode, target, |
| gen_rtx_VEC_DUPLICATE (mode, x))); |
| return; |
| } |
| |
| /* One field is non-constant. Load constant then overwrite |
| varying field. */ |
| if (n_var == 1) |
| { |
| rtx copy = copy_rtx (vals); |
| |
| /* Load constant part of vector, substitute neighboring value for |
| varying element. */ |
| XVECEXP (copy, 0, one_var) = XVECEXP (vals, 0, (one_var + 1) % n_elts); |
| rs6000_expand_vector_init (target, copy); |
| |
| /* Insert variable. */ |
| rs6000_expand_vector_set (target, XVECEXP (vals, 0, one_var), one_var); |
| return; |
| } |
| |
| /* Construct the vector in memory one field at a time |
| and load the whole vector. */ |
| mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0); |
| for (i = 0; i < n_elts; i++) |
| emit_move_insn (adjust_address_nv (mem, inner_mode, |
| i * GET_MODE_SIZE (inner_mode)), |
| XVECEXP (vals, 0, i)); |
| emit_move_insn (target, mem); |
| } |
| |
| /* Set field ELT of TARGET to VAL. */ |
| |
| void |
| rs6000_expand_vector_set (rtx target, rtx val, int elt) |
| { |
| enum machine_mode mode = GET_MODE (target); |
| enum machine_mode inner_mode = GET_MODE_INNER (mode); |
| rtx reg = gen_reg_rtx (mode); |
| /* APPLE LOCAL 4708231 */ |
| rtx mask, mem, x, pmem; |
| int width = GET_MODE_SIZE (inner_mode); |
| int i; |
| |
| /* Load single variable value. */ |
| mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode), 0); |
| emit_move_insn (adjust_address_nv (mem, inner_mode, 0), val); |
| |
| /* APPLE LOCAL begin 4708231 */ |
| /* Copy address into a register. */ |
| pmem = copy_addr_to_reg (XEXP (mem, 0)); |
| pmem = gen_rtx_MEM (mode, pmem); |
| |
| x = gen_rtx_UNSPEC (VOIDmode, |
| gen_rtvec (1, const0_rtx), UNSPEC_LVE); |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, |
| gen_rtvec (2, |
| gen_rtx_SET (VOIDmode, |
| reg, pmem), |
| x))); |
| /* APPLE LOCAL end 4708231 */ |
| |
| /* Linear sequence. */ |
| mask = gen_rtx_PARALLEL (V16QImode, rtvec_alloc (16)); |
| for (i = 0; i < 16; ++i) |
| XVECEXP (mask, 0, i) = GEN_INT (i); |
| |
| /* Set permute mask to insert element into target. */ |
| for (i = 0; i < width; ++i) |
| XVECEXP (mask, 0, elt*width + i) |
| = GEN_INT (i + 0x10); |
| x = gen_rtx_CONST_VECTOR (V16QImode, XVEC (mask, 0)); |
| x = gen_rtx_UNSPEC (mode, |
| gen_rtvec (3, target, reg, |
| force_reg (V16QImode, x)), |
| UNSPEC_VPERM); |
| emit_insn (gen_rtx_SET (VOIDmode, target, x)); |
| } |
| |
| /* Extract field ELT from VEC into TARGET. */ |
| |
| void |
| rs6000_expand_vector_extract (rtx target, rtx vec, int elt) |
| { |
| enum machine_mode mode = GET_MODE (vec); |
| enum machine_mode inner_mode = GET_MODE_INNER (mode); |
| /* APPLE LOCAL 4708231 */ |
| rtx mem, x, pmem; |
| |
| /* Allocate mode-sized buffer. */ |
| mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0); |
| |
| /* Add offset to field within buffer matching vector element. */ |
| mem = adjust_address_nv (mem, mode, elt * GET_MODE_SIZE (inner_mode)); |
| |
| /* APPLE LOCAL begin 4708231 */ |
| /* Copy address into a register. */ |
| pmem = copy_addr_to_reg (XEXP (mem, 0)); |
| pmem = gen_rtx_MEM (mode, pmem); |
| |
| /* Store single field into mode-sized buffer. */ |
| x = gen_rtx_UNSPEC (VOIDmode, |
| gen_rtvec (1, const0_rtx), UNSPEC_STVE); |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, |
| gen_rtvec (2, |
| gen_rtx_SET (VOIDmode, |
| pmem, vec), |
| x))); |
| /* APPLE LOCAL end 4708231 */ |
| emit_move_insn (target, adjust_address_nv (mem, inner_mode, 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; |
| |
| gcc_assert (GET_CODE (in) == CONST_INT); |
| |
| 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; |
| gcc_unreachable (); |
| #endif |
| } |
| |
| /* Return TRUE if OP is an invalid SUBREG operation on the e500. */ |
| |
| bool |
| invalid_e500_subreg (rtx op, enum machine_mode mode) |
| { |
| if (TARGET_E500_DOUBLE) |
| { |
| /* Reject (subreg:SI (reg:DF)). */ |
| if (GET_CODE (op) == SUBREG |
| && mode == SImode |
| && REG_P (SUBREG_REG (op)) |
| && GET_MODE (SUBREG_REG (op)) == DFmode) |
| return true; |
| |
| /* Reject (subreg:DF (reg:DI)). */ |
| if (GET_CODE (op) == SUBREG |
| && mode == DFmode |
| && REG_P (SUBREG_REG (op)) |
| && GET_MODE (SUBREG_REG (op)) == DImode) |
| return true; |
| } |
| |
| if (TARGET_SPE |
| && GET_CODE (op) == SUBREG |
| && mode == SImode |
| && REG_P (SUBREG_REG (op)) |
| && SPE_VECTOR_MODE (GET_MODE (SUBREG_REG (op)))) |
| return true; |
| |
| return false; |
| } |
| |
| /* 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, unsigned int computed, |
| unsigned int specified) |
| { |
| unsigned int align = MAX (computed, specified); |
| tree field = TYPE_FIELDS (type); |
| |
| /* Skip all non field decls */ |
| while (field != NULL && TREE_CODE (field) != FIELD_DECL) |
| field = TREE_CHAIN (field); |
| |
| if (field != NULL && field != type) |
| { |
| type = TREE_TYPE (field); |
| while (TREE_CODE (type) == ARRAY_TYPE) |
| type = TREE_TYPE (type); |
| |
| if (type != error_mark_node && TYPE_MODE (type) == DFmode) |
| align = MAX (align, 64); |
| } |
| |
| return align; |
| } |
| |
| /* APPLE LOCAL begin mainline 2006-10-31 PR 23067, radar 4869885 */ |
| /* Darwin increases record alignment to the natural alignment of |
| the first field. */ |
| |
| unsigned int |
| darwin_rs6000_special_round_type_align (tree type, unsigned int computed, |
| unsigned int specified) |
| { |
| unsigned int align = MAX (computed, specified); |
| |
| if (TYPE_PACKED (type)) |
| return align; |
| |
| /* Find the first field, looking down into aggregates. */ |
| /* APPLE LOCAL begin radar 4869885 */ |
| { |
| tree field = TYPE_FIELDS (type); |
| /* Skip all non field decls */ |
| while (field != NULL && TREE_CODE (field) != FIELD_DECL) |
| field = TREE_CHAIN (field); |
| if (field) |
| { |
| if (TREE_CODE (type) == UNION_TYPE) |
| { |
| tree union_field = field; |
| tree union_type = TREE_TYPE (union_field); |
| tree next_union_type; |
| do |
| { |
| union_field = TREE_CHAIN (union_field); |
| if (!union_field) |
| break; |
| /* Skip all non field decls */ |
| if (TREE_CODE (TREE_TYPE (union_field)) == ARRAY_TYPE) |
| next_union_type = get_inner_array_type (union_field); |
| else |
| next_union_type = TREE_TYPE (union_field); |
| if (TYPE_ALIGN (next_union_type) > TYPE_ALIGN (union_type)) |
| union_type = next_union_type; |
| } while (1); |
| type = union_type; |
| } |
| else |
| type = TREE_TYPE (field); |
| while (TREE_CODE (type) == ARRAY_TYPE) |
| type = TREE_TYPE (type); |
| } |
| } |
| /* APPLE LOCAL end radar 4869885 */ |
| |
| /* APPLE LOCAL begin Macintosh alignment 2002-1-22 --ff */ |
| if (OPTION_ALIGN_MAC68K) |
| align = MAX (align, 16); |
| /* APPLE LOCAL end Macintosh alignment 2002-1-22 --ff */ |
| |
| /* APPLE LOCAL begin radar 4869885 */ |
| else if (type != error_mark_node && ! TYPE_PACKED (type) && |
| maximum_field_alignment == 0 && (TARGET_ALIGN_NATURAL == 0)) |
| /* APPLE LOCAL end radar 4869885 */ |
| align = MAX (align, TYPE_ALIGN (type)); |
| |
| return align; |
| } |
| /* APPLE LOCAL end mainline 2006-10-31 PR 23067, radar 4869885 */ |
| /* Return 1 for an operand in small memory on V.4/eabi. */ |
| |
| int |
| small_data_operand (rtx op ATTRIBUTE_UNUSED, |
| enum machine_mode mode ATTRIBUTE_UNUSED) |
| { |
| #if TARGET_ELF |
| rtx sym_ref; |
| |
| if (rs6000_sdata == SDATA_NONE || rs6000_sdata == SDATA_DATA) |
| return 0; |
| |
| if (DEFAULT_ABI != ABI_V4) |
| return 0; |
| |
| if (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 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; |
| } |
| |
| 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; |
| } |
| |
| 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 |
| && (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST) |
| && small_data_operand (x, mode)); |
| } |
| |
| /* SPE offset addressing is limited to 5-bits worth of double words. */ |
| #define SPE_CONST_OFFSET_OK(x) (((x) & ~0xf8) == 0) |
| |
| bool |
| rs6000_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 (legitimate_constant_pool_address_p (x)) |
| return true; |
| 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: |
| /* APPLE LOCAL begin radar 4994150 */ |
| /* AltiVec vector modes. Only reg+reg addressing is valid and |
| constant offset zero should not occur due to canonicalization. |
| Allow any offset when not strict before reload. |
| */ |
| return false; |
| /* APPLE LOCAL end radar 4994150 */ |
| |
| case V4HImode: |
| case V2SImode: |
| case V1DImode: |
| case V2SFmode: |
| /* SPE vector modes. */ |
| return SPE_CONST_OFFSET_OK (offset); |
| |
| case DFmode: |
| if (TARGET_E500_DOUBLE) |
| return SPE_CONST_OFFSET_OK (offset); |
| |
| case DImode: |
| /* On e500v2, we may have: |
| |
| (subreg:DF (mem:DI (plus (reg) (const_int))) 0). |
| |
| Which gets addressed with evldd instructions. */ |
| if (TARGET_E500_DOUBLE) |
| return SPE_CONST_OFFSET_OK (offset); |
| |
| if (mode == DFmode || !TARGET_POWERPC64) |
| extra = 4; |
| else if (offset & 3) |
| return false; |
| break; |
| |
| case TFmode: |
| case TImode: |
| if (mode == TFmode || !TARGET_POWERPC64) |
| extra = 12; |
| else if (offset & 3) |
| return false; |
| 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); |
| |
| /* Recognize the rtl generated by reload which we know will later be |
| replaced with proper base and index regs. */ |
| if (!strict |
| && reload_in_progress |
| && (REG_P (op0) || GET_CODE (op0) == PLUS) |
| && REG_P (op1)) |
| return true; |
| |
| return (REG_P (op0) && REG_P (op1) |
| && ((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)))); |
| } |
| |
| inline bool |
| legitimate_indirect_address_p (rtx x, int strict) |
| { |
| return GET_CODE (x) == REG && INT_REG_OK_FOR_BASE_P (x, strict); |
| } |
| |
| 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; |
| /* Restrict addressing for DI because of our SUBREG hackery. */ |
| if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode)) |
| return false; |
| x = XEXP (x, 1); |
| |
| if (TARGET_ELF || TARGET_MACHO) |
| { |
| 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) > 64 |
| || (GET_MODE_BITSIZE (mode) > 32 && !TARGET_POWERPC64 |
| && !(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 != DImode && mode != DFmode) || TARGET_E500_DOUBLE) |
| && 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) |
| || (TARGET_E500_DOUBLE && (mode == DFmode |
| || mode == DImode))) |
| { |
| if (mode == DImode) |
| return NULL_RTX; |
| /* We accept [reg + reg] and [reg + OFFSET]. */ |
| |
| if (GET_CODE (x) == PLUS) |
| { |
| rtx op1 = XEXP (x, 0); |
| rtx op2 = XEXP (x, 1); |
| |
| 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 TARGET_ASM_OUTPUT_DWARF_DTPREL. |
| We need to emit DTP-relative relocations. */ |
| |
| static 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: |
| gcc_unreachable (); |
| } |
| 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; |
| |
| /* We currently use relocations like @got@tlsgd for tls, which |
| means the linker will handle allocation of tls entries, placing |
| them in the .got section. So use a pointer to the .got section, |
| not one to secondary TOC sections used by 64-bit -mminimal-toc, |
| or to secondary GOT sections used by 32-bit -fPIC. */ |
| if (TARGET_64BIT) |
| got = gen_rtx_REG (Pmode, 2); |
| 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 |
| { |
| rtx tempLR, tmp3, mem; |
| rtx first, last; |
| |
| tempLR = gen_reg_rtx (Pmode); |
| tmp1 = gen_reg_rtx (Pmode); |
| tmp2 = gen_reg_rtx (Pmode); |
| tmp3 = gen_reg_rtx (Pmode); |
| mem = gen_const_mem (Pmode, tmp1); |
| |
| first = emit_insn (gen_load_toc_v4_PIC_1b (tempLR, 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 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 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 |
| |
| /* Force ld/std non-word aligned offset into base register by wrapping |
| in offset 0. */ |
| if (GET_CODE (x) == PLUS |
| && GET_CODE (XEXP (x, 0)) == REG |
| && REGNO (XEXP (x, 0)) < 32 |
| && REG_MODE_OK_FOR_BASE_P (XEXP (x, 0), mode) |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && (INTVAL (XEXP (x, 1)) & 3) != 0 |
| && !ALTIVEC_VECTOR_MODE (mode) |
| && GET_MODE_SIZE (mode) >= UNITS_PER_WORD |
| && TARGET_POWERPC64) |
| { |
| x = gen_rtx_PLUS (GET_MODE (x), x, GEN_INT (0)); |
| push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, |
| BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0, |
| opnum, (enum reload_type) type); |
| *win = 1; |
| return x; |
| } |
| |
| if (GET_CODE (x) == PLUS |
| && GET_CODE (XEXP (x, 0)) == REG |
| && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER |
| && REG_MODE_OK_FOR_BASE_P (XEXP (x, 0), mode) |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && !SPE_VECTOR_MODE (mode) |
| && !(TARGET_E500_DOUBLE && (mode == DFmode |
| || mode == DImode)) |
| && !ALTIVEC_VECTOR_MODE (mode)) |
| { |
| HOST_WIDE_INT val = INTVAL (XEXP (x, 1)); |
| HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000; |
| HOST_WIDE_INT high |
| = (((val - low) & 0xffffffff) ^ 0x80000000) - 0x80000000; |
| |
| /* 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 (GET_CODE (x) == SYMBOL_REF |
| && !ALTIVEC_VECTOR_MODE (mode) |
| && !SPE_VECTOR_MODE (mode) |
| #if TARGET_MACHO |
| && DEFAULT_ABI == ABI_DARWIN |
| && (flag_pic || MACHO_DYNAMIC_NO_PIC_P) |
| #else |
| && DEFAULT_ABI == ABI_V4 |
| && !flag_pic |
| #endif |
| /* Don't do this for TFmode, since the result isn't offsettable. |
| The same goes for DImode without 64-bit gprs and DFmode |
| without fprs. */ |
| && mode != TFmode |
| && (mode != DImode || TARGET_POWERPC64) |
| && (mode != DFmode || TARGET_POWERPC64 |
| || (TARGET_FPRS && TARGET_HARD_FLOAT))) |
| { |
| #if TARGET_MACHO |
| if (flag_pic) |
| { |
| rtx offset = gen_rtx_CONST (Pmode, |
| gen_rtx_MINUS (Pmode, x, |
| machopic_function_base_sym ())); |
| x = gen_rtx_LO_SUM (GET_MODE (x), |
| gen_rtx_PLUS (Pmode, pic_offset_table_rtx, |
| gen_rtx_HIGH (Pmode, offset)), offset); |
| } |
| else |
| #endif |
| x = gen_rtx_LO_SUM (GET_MODE (x), |
| gen_rtx_HIGH (Pmode, x), x); |
| |
| push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, |
| BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, |
| opnum, (enum reload_type)type); |
| *win = 1; |
| return x; |
| } |
| |
| /* Reload an offset address wrapped by an AND that represents the |
| masking of the lower bits. Strip the outer AND and let reload |
| convert the offset address into an indirect address. */ |
| if (TARGET_ALTIVEC |
| && ALTIVEC_VECTOR_MODE (mode) |
| && GET_CODE (x) == AND |
| && GET_CODE (XEXP (x, 0)) == PLUS |
| && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG |
| && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && INTVAL (XEXP (x, 1)) == -16) |
| { |
| x = XEXP (x, 0); |
| *win = 1; |
| return x; |
| } |
| |
| if (TARGET_TOC |
| && 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, TFmode), 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 this is an unaligned stvx/ldvx type address, discard the outer AND. */ |
| if (TARGET_ALTIVEC |
| && ALTIVEC_VECTOR_MODE (mode) |
| && GET_CODE (x) == AND |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && INTVAL (XEXP (x, 1)) == -16) |
| x = XEXP (x, 0); |
| |
| if (RS6000_SYMBOL_REF_TLS_P (x)) |
| return 0; |
| if (legitimate_indirect_address_p (x, reg_ok_strict)) |
| return 1; |
| if ((GET_CODE (x) == PRE_INC || GET_CODE (x) == PRE_DEC) |
| && !ALTIVEC_VECTOR_MODE (mode) |
| && !SPE_VECTOR_MODE (mode) |
| && mode != TFmode |
| /* Restrict addressing for DI because of our SUBREG hackery. */ |
| && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode)) |
| && TARGET_UPDATE |
| && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict)) |
| return 1; |
| 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 (rs6000_legitimate_offset_address_p (mode, x, reg_ok_strict)) |
| return 1; |
| if (mode != TImode |
| && mode != TFmode |
| && ((TARGET_HARD_FLOAT && TARGET_FPRS) |
| || TARGET_POWERPC64 |
| || ((mode != DFmode || TARGET_E500_DOUBLE) && 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; |
| } |
| |
| /* More elaborate version of recog's offsettable_memref_p predicate |
| that works around the ??? note of rs6000_mode_dependent_address. |
| In particular it accepts |
| |
| (mem:DI (plus:SI (reg/f:SI 31 31) (const_int 32760 [0x7ff8]))) |
| |
| in 32-bit mode, that the recog predicate rejects. */ |
| |
| bool |
| rs6000_offsettable_memref_p (rtx op) |
| { |
| if (!MEM_P (op)) |
| return false; |
| |
| /* First mimic offsettable_memref_p. */ |
| if (offsettable_address_p (1, GET_MODE (op), XEXP (op, 0))) |
| return true; |
| |
| /* offsettable_address_p invokes rs6000_mode_dependent_address, but |
| the latter predicate knows nothing about the mode of the memory |
| reference and, therefore, assumes that it is the largest supported |
| mode (TFmode). As a consequence, legitimate offsettable memory |
| references are rejected. rs6000_legitimate_offset_address_p contains |
| the correct logic for the PLUS case of rs6000_mode_dependent_address. */ |
| return rs6000_legitimate_offset_address_p (GET_MODE (op), XEXP (op, 0), 1); |
| } |
| |
| /* Return number of consecutive hard regs needed starting at reg REGNO |
| to hold something of mode MODE. |
| This is ordinarily the length in words of a value of mode MODE |
| but can be less for certain modes in special long registers. |
| |
| For the SPE, GPRs are 64 bits but only 32 bits are visible in |
| scalar instructions. The upper 32 bits are only available to the |
| SIMD instructions. |
| |
| POWER and PowerPC GPRs hold 32 bits worth; |
| PowerPC64 GPRs and FPRs point register holds 64 bits worth. */ |
| |
| int |
| rs6000_hard_regno_nregs (int regno, enum machine_mode mode) |
| { |
| if (FP_REGNO_P (regno)) |
| return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD; |
| |
| if (SPE_SIMD_REGNO_P (regno) && TARGET_SPE && SPE_VECTOR_MODE (mode)) |
| return (GET_MODE_SIZE (mode) + UNITS_PER_SPE_WORD - 1) / UNITS_PER_SPE_WORD; |
| |
| if (ALTIVEC_REGNO_P (regno)) |
| return |
| (GET_MODE_SIZE (mode) + UNITS_PER_ALTIVEC_WORD - 1) / UNITS_PER_ALTIVEC_WORD; |
| |
| /* The value returned for SCmode in the E500 double case is 2 for |
| ABI compatibility; storing an SCmode value in a single register |
| would require function_arg and rs6000_spe_function_arg to handle |
| SCmode so as to pass the value correctly in a pair of |
| registers. */ |
| if (TARGET_E500_DOUBLE && FLOAT_MODE_P (mode) && mode != SCmode) |
| return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD; |
| |
| return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; |
| } |
| |
| /* Change register usage conditional on target flags. */ |
| void |
| rs6000_conditional_register_usage (void) |
| { |
| int i; |
| |
| /* Set MQ register fixed (already call_used) if not POWER |
| architecture (RIOS1, RIOS2, RSC, and PPC601) so that it will not |
| be allocated. */ |
| if (! TARGET_POWER) |
| fixed_regs[64] = 1; |
| |
| /* 64-bit AIX and Linux reserve GPR13 for thread-private data. */ |
| if (TARGET_64BIT) |
| fixed_regs[13] = call_used_regs[13] |
| = call_really_used_regs[13] = 1; |
| |
| /* Conditionally disable FPRs. */ |
| if (TARGET_SOFT_FLOAT || !TARGET_FPRS) |
| for (i = 32; i < 64; i++) |
| fixed_regs[i] = call_used_regs[i] |
| = call_really_used_regs[i] = 1; |
| |
| /* The TOC register is not killed across calls in a way that is |
| visible to the compiler. */ |
| if (DEFAULT_ABI == ABI_AIX) |
| call_really_used_regs[2] = 0; |
| |
| if (DEFAULT_ABI == ABI_V4 |
| && PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM |
| && flag_pic == 2) |
| fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1; |
| |
| if (DEFAULT_ABI == ABI_V4 |
| && PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM |
| && flag_pic == 1) |
| fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] |
| = call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] |
| = call_really_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1; |
| |
| if (DEFAULT_ABI == ABI_DARWIN |
| && PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM) |
| fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] |
| = call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] |
| = call_really_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1; |
| |
| if (TARGET_TOC && TARGET_MINIMAL_TOC) |
| fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] |
| = call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1; |
| |
| if (TARGET_ALTIVEC) |
| global_regs[VSCR_REGNO] = 1; |
| |
| if (TARGET_SPE) |
| { |
| global_regs[SPEFSCR_REGNO] = 1; |
| fixed_regs[FIXED_SCRATCH] |
| = call_used_regs[FIXED_SCRATCH] |
| = call_really_used_regs[FIXED_SCRATCH] = 1; |
| } |
| |
| if (! TARGET_ALTIVEC) |
| { |
| for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i) |
| fixed_regs[i] = call_used_regs[i] = call_really_used_regs[i] = 1; |
| call_really_used_regs[VRSAVE_REGNO] = 1; |
| } |
| |
| if (TARGET_ALTIVEC_ABI) |
| for (i = FIRST_ALTIVEC_REGNO; i < FIRST_ALTIVEC_REGNO + 20; ++i) |
| call_used_regs[i] = call_really_used_regs[i] = 1; |
| } |
| |
| /* 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; |
| |
| switch (mode) |
| { |
| case QImode: |
| case HImode: |
| if (dest == NULL) |
| dest = gen_reg_rtx (mode); |
| emit_insn (gen_rtx_SET (VOIDmode, dest, source)); |
| return dest; |
| |
| case 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; |
| break; |
| |
| case DImode: |
| switch (GET_CODE (source)) |
| { |
| case CONST_INT: |
| c0 = INTVAL (source); |
| c1 = -(c0 < 0); |
| break; |
| |
| case 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 |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| result = rs6000_emit_set_long_const (dest, c0, c1); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| 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 == 0xffff && (ud3 & 0x8000)) |
| || (ud4 == 0 && ! (ud3 & 0x8000))) |
| { |
| if (ud3 & 0x8000) |
| emit_move_insn (dest, GEN_INT (((ud3 << 16) ^ 0x80000000) |
| - 0x80000000)); |
| else |
| emit_move_insn (dest, GEN_INT (ud3 << 16)); |
| |
| if (ud2 != 0) |
| emit_move_insn (dest, gen_rtx_IOR (DImode, dest, GEN_INT (ud2))); |
| emit_move_insn (dest, gen_rtx_ASHIFT (DImode, dest, GEN_INT (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; |
| } |
| |
| /* Helper for the following. Get rid of [r+r] memory refs |
| in cases where it won't work (TImode, TFmode). */ |
| |
| static void |
| rs6000_eliminate_indexed_memrefs (rtx operands[2]) |
| { |
| if (GET_CODE (operands[0]) == MEM |
| && GET_CODE (XEXP (operands[0], 0)) != REG |
| && ! legitimate_constant_pool_address_p (XEXP (operands[0], 0)) |
| && ! reload_in_progress) |
| operands[0] |
| = replace_equiv_address (operands[0], |
| copy_addr_to_reg (XEXP (operands[0], 0))); |
| |
| if (GET_CODE (operands[1]) == MEM |
| && GET_CODE (XEXP (operands[1], 0)) != REG |
| && ! legitimate_constant_pool_address_p (XEXP (operands[1], 0)) |
| && ! reload_in_progress) |
| operands[1] |
| = replace_equiv_address (operands[1], |
| copy_addr_to_reg (XEXP (operands[1], 0))); |
| } |
| |
| /* 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); |
| } |
| gcc_assert (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))); |
| |
| /* 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 && GET_CODE (operands[0]) == MEM |
| && !gpc_reg_operand (operands[1], mode)) |
| operands[1] = force_reg (mode, operands[1]); |
| |
| if (mode == SFmode && ! TARGET_POWERPC |
| && TARGET_HARD_FLOAT && TARGET_FPRS |
| && 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 (rs6000_tls_referenced_p (operands[1])) |
| { |
| enum tls_model model; |
| rtx tmp = operands[1]; |
| rtx addend = NULL; |
| |
| if (GET_CODE (tmp) == CONST && GET_CODE (XEXP (tmp, 0)) == PLUS) |
| { |
| addend = XEXP (XEXP (tmp, 0), 1); |
| tmp = XEXP (XEXP (tmp, 0), 0); |
| } |
| |
| gcc_assert (GET_CODE (tmp) == SYMBOL_REF); |
| model = SYMBOL_REF_TLS_MODEL (tmp); |
| gcc_assert (model != 0); |
| |
| tmp = rs6000_legitimize_tls_address (tmp, model); |
| if (addend) |
| { |
| tmp = gen_rtx_PLUS (mode, tmp, addend); |
| tmp = force_operand (tmp, operands[0]); |
| } |
| operands[1] = tmp; |
| } |
| |
| /* 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; |
| |
| /* 128-bit constant floating-point values on Darwin should really be |
| loaded as two parts. */ |
| if (!TARGET_IEEEQUAD && 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: |
| rs6000_eliminate_indexed_memrefs (operands); |
| /* fall through */ |
| |
| 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_DATA (new_ref) = SYMBOL_REF_DATA (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) |
| { |
| /* APPLE LOCAL begin radar 4232296 */ |
| /* If a symbol node has been generated but its flags not set; such as in the course of |
| cost computation of generated code, do not attempt to update the static tables which |
| rely on flags of the referenced symbol to have been set. Otherwise, bogus PIC stub |
| will be generated. */ |
| if (!(GET_CODE (operands[1]) == SYMBOL_REF && SYMBOL_REF_FLAGS (operands[1]) == 0)) |
| operands[1] = |
| rs6000_machopic_legitimize_pic_address (operands[1], mode, |
| operands[0]); |
| /* APPLE LOCAL end radar 4232296 */ |
| 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_const_mem (mode, |
| create_TOC_reference (XEXP (operands[1], 0))); |
| set_mem_alias_set (operands[1], get_TOC_alias_set ()); |
| } |
| } |
| break; |
| |
| case TImode: |
| rs6000_eliminate_indexed_memrefs (operands); |
| |
| 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: |
| gcc_unreachable (); |
| } |
| |
| /* 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) \ |
| (SCALAR_FLOAT_MODE_P (MODE) \ |
| && !DECIMAL_FLOAT_MODE_P (MODE) \ |
| && (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, aix_struct_return |
| 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) |
| { |
| /* LLVM LOCAL begin struct return check */ |
| #ifdef ENABLE_LLVM |
| /* FIXME darwin ppc64 often returns structs partly in memory and partly |
| in regs. The binary interface of return_in_memory (which does the |
| work for aggregate_value_p) is not a good match for this; in fact |
| this target returns false if any part of it goes in registers. Which |
| means aggregate_value_p is not useful on this target for this purpose. |
| This is a big nasty longterm problem. For now put things back the |
| way they used to be (wrong, but fewer crashes). */ |
| if (TARGET_64BIT && TYPE_SIZE_UNIT(type) && |
| TREE_CODE(TYPE_SIZE_UNIT(type)) == INTEGER_CST && |
| TREE_INT_CST_LOW(TYPE_SIZE_UNIT(type)) > 8) |
| return true; |
| #endif |
| /* LLVM LOCAL end struct return check */ |
| |
| /* In the darwin64 abi, try to use registers for larger structs |
| if possible. */ |
| if (rs6000_darwin64_abi |
| && TREE_CODE (type) == RECORD_TYPE |
| && int_size_in_bytes (type) > 0) |
| { |
| CUMULATIVE_ARGS valcum; |
| rtx valret; |
| |
| valcum.words = 0; |
| valcum.fregno = FP_ARG_MIN_REG; |
| valcum.vregno = ALTIVEC_ARG_MIN_REG; |
| /* Do a trial code generation as if this were going to be passed |
| as an argument; if any part goes in memory, we return NULL. */ |
| valret = rs6000_darwin64_record_arg (&valcum, type, 1, true); |
| if (valret) |
| return false; |
| /* Otherwise fall through to more conventional ABI rules. */ |
| } |
| |
| if (AGGREGATE_TYPE_P (type) |
| && (aix_struct_return |
| || (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8)) |
| return true; |
| |
| /* Allow -maltivec -mabi=no-altivec without warning. Altivec vector |
| modes only exist for GCC vector types if -maltivec. */ |
| if (TARGET_32BIT && !TARGET_ALTIVEC_ABI |
| && ALTIVEC_VECTOR_MODE (TYPE_MODE (type))) |
| return false; |
| |
| /* Return synthetic vectors in memory. */ |
| if (TREE_CODE (type) == VECTOR_TYPE |
| && int_size_in_bytes (type) > (TARGET_ALTIVEC_ABI ? 16 : 8)) |
| { |
| static bool warned_for_return_big_vectors = false; |
| if (!warned_for_return_big_vectors) |
| { |
| warning (0, "GCC vector returned by reference: " |
| "non-standard ABI extension with no compatibility guarantee"); |
| warned_for_return_big_vectors = true; |
| } |
| return true; |
| } |
| |
| if (DEFAULT_ABI == ABI_V4 && TARGET_IEEEQUAD && 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 && rs6000_default_long_calls) |
| || (fntype |
| && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype)) |
| && !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype)))) |
| cum->call_cookie |= CALL_LONG; |
| |
| if (TARGET_DEBUG_ARG) |
| { |
| 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"); |
| } |
| } |
| |
| /* Return true if TYPE must be passed on the stack and not in registers. */ |
| |
| static bool |
| rs6000_must_pass_in_stack (enum machine_mode mode, tree type) |
| { |
| if (DEFAULT_ABI == ABI_AIX || TARGET_64BIT) |
| return must_pass_in_stack_var_size (mode, type); |
| else |
| return must_pass_in_stack_var_size_or_pad (mode, type); |
| } |
| |
| /* 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. |
| i.e. Structures of size 1 or 2 (or 4 when TARGET_64BIT) were |
| passed padded downward, except that -mstrict-align further |
| muddied the water in that multi-component structures of 2 and 4 |
| bytes in size were passed padded upward. |
| |
| 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 and doubles to be double word aligned. Just |
| testing the mode size is a boneheaded way to do this as it means |
| that other types such as complex int are also double word aligned. |
| However, we're stuck with this because changing the ABI might break |
| existing library interfaces. |
| |
| Doubleword align SPE vectors. |
| Quadword align Altivec vectors. |
| Quadword align large synthetic vector types. */ |
| |
| int |
| function_arg_boundary (enum machine_mode mode, tree type) |
| { |
| if (DEFAULT_ABI == ABI_V4 |
| && (GET_MODE_SIZE (mode) == 8 |
| || (TARGET_HARD_FLOAT |
| && TARGET_FPRS |
| && mode == TFmode))) |
| return 64; |
| else if (SPE_VECTOR_MODE (mode) |
| || (type && TREE_CODE (type) == VECTOR_TYPE |
| && int_size_in_bytes (type) >= 8 |
| && int_size_in_bytes (type) < 16)) |
| return 64; |
| else if (ALTIVEC_VECTOR_MODE (mode) |
| || (type && TREE_CODE (type) == VECTOR_TYPE |
| && int_size_in_bytes (type) >= 16)) |
| return 128; |
| else if (rs6000_darwin64_abi && mode == BLKmode |
| && type && TYPE_ALIGN (type) > 64) |
| return 128; |
| else |
| return PARM_BOUNDARY; |
| } |
| |
| /* For a function parm of MODE and TYPE, return the starting word in |
| the parameter area. NWORDS of the parameter area are already used. */ |
| |
| static unsigned int |
| rs6000_parm_start (enum machine_mode mode, tree type, unsigned int nwords) |
| { |
| unsigned int align; |
| unsigned int parm_offset; |
| |
| align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1; |
| parm_offset = DEFAULT_ABI == ABI_V4 ? 2 : 6; |
| return nwords + (-(parm_offset + nwords) & align); |
| } |
| |
| /* Compute the size (in words) of a function argument. */ |
| |
| static unsigned long |
| 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; |
| } |
| |
| /* Use this to flush pending int fields. */ |
| |
| static void |
| rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *cum, |
| /* APPLE LOCAL fix 64-bit varargs 4028089 */ |
| HOST_WIDE_INT bitpos, int final) |
| { |
| unsigned int startbit, endbit; |
| int intregs, intoffset; |
| enum machine_mode mode; |
| |
| /* APPLE LOCAL begin fix 64-bit varargs 4028089 */ |
| /* Handle the situations where a float is taking up the first half |
| of the GPR, and the other half is empty (typically due to |
| alignment restrictions). We can detect this by a 8-byte-aligned |
| int field, or by seeing that this is the final flush for this |
| argument. Count the word and continue on. */ |
| if (cum->floats_in_gpr == 1 |
| && (cum->intoffset % 64 == 0 |
| || (cum->intoffset == -1 && final))) |
| { |
| cum->words++; |
| cum->floats_in_gpr = 0; |
| } |
| /* APPLE LOCAL end fix 64-bit varargs 4028089 */ |
| |
| if (cum->intoffset == -1) |
| return; |
| |
| intoffset = cum->intoffset; |
| cum->intoffset = -1; |
| /* APPLE LOCAL fix 64-bit varargs 4028089 */ |
| cum->floats_in_gpr = 0; |
| |
| if (intoffset % BITS_PER_WORD != 0) |
| { |
| mode = mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD, |
| MODE_INT, 0); |
| if (mode == BLKmode) |
| { |
| /* We couldn't find an appropriate mode, which happens, |
| e.g., in packed structs when there are 3 bytes to load. |
| Back intoffset back to the beginning of the word in this |
| case. */ |
| intoffset = intoffset & -BITS_PER_WORD; |
| } |
| } |
| |
| startbit = intoffset & -BITS_PER_WORD; |
| endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD; |
| intregs = (endbit - startbit) / BITS_PER_WORD; |
| cum->words += intregs; |
| /* APPLE LOCAL begin ppc64 abi */ |
| /* words should be unsigned. */ |
| if ((unsigned)cum->words < (endbit/BITS_PER_WORD)) |
| { |
| int pad = (endbit/BITS_PER_WORD) - cum->words; |
| cum->words += pad; |
| } |
| /* APPLE LOCAL end ppc64 abi */ |
| } |
| |
| /* The darwin64 ABI calls for us to recurse down through structs, |
| looking for elements passed in registers. Unfortunately, we have |
| to track int register count here also because of misalignments |
| in powerpc alignment mode. */ |
| |
| static void |
| rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *cum, |
| tree type, |
| HOST_WIDE_INT startbitpos) |
| { |
| tree f; |
| |
| for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f)) |
| if (TREE_CODE (f) == FIELD_DECL) |
| { |
| HOST_WIDE_INT bitpos = startbitpos; |
| tree ftype = TREE_TYPE (f); |
| enum machine_mode mode; |
| if (ftype == error_mark_node) |
| continue; |
| mode = TYPE_MODE (ftype); |
| |
| if (DECL_SIZE (f) != 0 |
| && host_integerp (bit_position (f), 1)) |
| bitpos += int_bit_position (f); |
| |
| /* ??? FIXME: else assume zero offset. */ |
| |
| if (TREE_CODE (ftype) == RECORD_TYPE) |
| rs6000_darwin64_record_arg_advance_recurse (cum, ftype, bitpos); |
| else if (USE_FP_FOR_ARG_P (cum, mode, ftype)) |
| { |
| /* APPLE LOCAL fix 64-bit varargs 4028089 */ |
| rs6000_darwin64_record_arg_advance_flush (cum, bitpos, 0); |
| cum->fregno += (GET_MODE_SIZE (mode) + 7) >> 3; |
| /* APPLE LOCAL begin fix 64-bit varargs 4028089 */ |
| /* Single-precision floats present a special problem for |
| us, because they are smaller than an 8-byte GPR, and so |
| the structure-packing rules combined with the standard |
| varargs behavior mean that we want to pack float/float |
| and float/int combinations into a single register's |
| space. This is complicated by the arg advance flushing, |
| which works on arbitrarily large groups of int-type |
| fields. */ |
| if (mode == SFmode) |
| { |
| if (cum->floats_in_gpr == 1) |
| { |
| /* Two floats in a word; count the word and reset |
| the float count. */ |
| cum->words++; |
| cum->floats_in_gpr = 0; |
| } |
| else if (bitpos % 64 == 0) |
| { |
| /* A float at the beginning of an 8-byte word; |
| count it and put off adjusting cum->words until |
| we see if a arg advance flush is going to do it |
| for us. */ |
| cum->floats_in_gpr++; |
| } |
| else |
| { |
| /* The float is at the end of a word, preceded |
| by integer fields, so the arg advance flush |
| just above has already set cum->words and |
| everything is taken care of. */ |
| } |
| } |
| else |
| /* APPLE LOCAL end fix 64-bit varargs 4028089 */ |
| cum->words += (GET_MODE_SIZE (mode) + 7) >> 3; |
| } |
| else if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, 1)) |
| { |
| /* APPLE LOCAL fix 64-bit varargs 4028089 */ |
| rs6000_darwin64_record_arg_advance_flush (cum, bitpos, 0); |
| cum->vregno++; |
| cum->words += 2; |
| } |
| else if (cum->intoffset == -1) |
| cum->intoffset = bitpos; |
| } |
| } |
| |
| /* Update the data in CUM to advance over an argument |
| of mode MODE and data type TYPE. |
| (TYPE is null for libcalls where that information may not be available.) |
| |
| 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, int depth) |
| { |
| int size; |
| |
| /* Only tick off an argument if we're not recursing. */ |
| if (depth == 0) |
| cum->nargs_prototype--; |
| |
| if (TARGET_ALTIVEC_ABI |
| && (ALTIVEC_VECTOR_MODE (mode) |
| || (type && TREE_CODE (type) == VECTOR_TYPE |
| && int_size_in_bytes (type) == 16))) |
| { |
| bool stack = false; |
| |
| if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named)) |
| { |
| cum->vregno++; |
| if (!TARGET_ALTIVEC) |
| error ("cannot pass argument in vector register because" |
| " 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 (rs6000_darwin64_abi |
| && mode == BLKmode |
| && TREE_CODE (type) == RECORD_TYPE |
| && (size = int_size_in_bytes (type)) > 0) |
| { |
| /* Variable sized types have size == -1 and are |
| treated as if consisting entirely of ints. |
| Pad to 16 byte boundary if needed. */ |
| if (TYPE_ALIGN (type) >= 2 * BITS_PER_WORD |
| && (cum->words % 2) != 0) |
| cum->words++; |
| /* For varargs, we can just go up by the size of the struct. */ |
| if (!named) |
| cum->words += (size + 7) / 8; |
| else |
| { |
| /* It is tempting to say int register count just goes up by |
| sizeof(type)/8, but this is wrong in a case such as |
| { int; double; int; } [powerpc alignment]. We have to |
| grovel through the fields for these too. */ |
| cum->intoffset = 0; |
| /* APPLE LOCAL fix 64-bit varargs 4028089 */ |
| cum->floats_in_gpr = 0; |
| rs6000_darwin64_record_arg_advance_recurse (cum, type, 0); |
| rs6000_darwin64_record_arg_advance_flush (cum, |
| /* APPLE LOCAL fix 64-bit varargs 4028089 */ |
| size * BITS_PER_UNIT, 1); |
| } |
| } |
| else if (DEFAULT_ABI == ABI_V4) |
| { |
| if (TARGET_HARD_FLOAT && TARGET_FPRS |
| && (mode == SFmode || mode == DFmode |
| || (mode == TFmode && !TARGET_IEEEQUAD))) |
| { |
| if (cum->fregno + (mode == TFmode ? 1 : 0) <= FP_ARG_V4_MAX_REG) |
| cum->fregno += (GET_MODE_SIZE (mode) + 7) >> 3; |
| else |
| { |
| cum->fregno = FP_ARG_V4_MAX_REG + 1; |
| if (mode == DFmode || mode == TFmode) |
| 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 (n_words == 2) |
| gregno += (1 - gregno) & 1; |
| |
| /* Multi-reg args are not split between registers and stack. */ |
| if (gregno + n_words - 1 > GP_ARG_MAX_REG) |
| { |
| /* Long long and SPE vectors are aligned on the stack. |
| So are other 2 word items such as complex int due to |
| a historical mistake. */ |
| if (n_words == 2) |
| cum->words += cum->words & 1; |
| cum->words += n_words; |
| } |
| |
| /* Note: continuing to accumulate gregno past when we've started |
| spilling to the stack indicates the fact that we've started |
| spilling to the stack to expand_builtin_saveregs. */ |
| cum->sysv_gregno = gregno + n_words; |
| } |
| |
| if (TARGET_DEBUG_ARG) |
| { |
| fprintf (stderr, "function_adv: words = %2d, fregno = %2d, ", |
| cum->words, cum->fregno); |
| fprintf (stderr, "gregno = %2d, nargs = %4d, proto = %d, ", |
| cum->sysv_gregno, cum->nargs_prototype, cum->prototype); |
| fprintf (stderr, "mode = %4s, named = %d\n", |
| GET_MODE_NAME (mode), named); |
| } |
| } |
| else |
| { |
| int n_words = rs6000_arg_size (mode, type); |
| int start_words = cum->words; |
| int align_words = rs6000_parm_start (mode, type, start_words); |
| |
| cum->words = align_words + n_words; |
| |
| if (SCALAR_FLOAT_MODE_P (mode) |
| && !DECIMAL_FLOAT_MODE_P (mode) |
| && TARGET_HARD_FLOAT && TARGET_FPRS) |
| cum->fregno += (GET_MODE_SIZE (mode) + 7) >> 3; |
| |
| if (TARGET_DEBUG_ARG) |
| { |
| fprintf (stderr, "function_adv: words = %2d, fregno = %2d, ", |
| cum->words, cum->fregno); |
| fprintf (stderr, "nargs = %4d, proto = %d, mode = %4s, ", |
| cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode)); |
| fprintf (stderr, "named = %d, align = %d, depth = %d\n", |
| named, align_words - start_words, depth); |
| } |
| } |
| } |
| |
| static rtx |
| spe_build_register_parallel (enum machine_mode mode, int gregno) |
| { |
| rtx r1, r3; |
| |
| switch (mode) |
| { |
| case DFmode: |
| r1 = gen_rtx_REG (DImode, gregno); |
| r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx); |
| return gen_rtx_PARALLEL (mode, gen_rtvec (1, r1)); |
| |
| case DCmode: |
| r1 = gen_rtx_REG (DImode, gregno); |
| r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx); |
| r3 = gen_rtx_REG (DImode, gregno + 2); |
| r3 = gen_rtx_EXPR_LIST (VOIDmode, r3, GEN_INT (8)); |
| return gen_rtx_PARALLEL (mode, gen_rtvec (2, r1, r3)); |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Determine where to put a SIMD argument on the SPE. */ |
| static rtx |
| rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, |
| tree type) |
| { |
| int gregno = cum->sysv_gregno; |
| |
| /* On E500 v2, double arithmetic is done on the full 64-bit GPR, but |
| are passed and returned in a pair of GPRs for ABI compatibility. */ |
| if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DCmode)) |
| { |
| int n_words = rs6000_arg_size (mode, type); |
| |
| /* Doubles go in an odd/even register pair (r5/r6, etc). */ |
| if (mode == DFmode) |
| gregno += (1 - gregno) & 1; |
| |
| /* Multi-reg args are not split between registers and stack. */ |
| if (gregno + n_words - 1 > GP_ARG_MAX_REG) |
| return NULL_RTX; |
| |
| return spe_build_register_parallel (mode, gregno); |
| } |
| if (cum->stdarg) |
| { |
| int n_words = rs6000_arg_size (mode, type); |
| |
| /* SPE vectors are put in odd registers. */ |
| if (n_words == 2 && (gregno & 1) == 0) |
| gregno += 1; |
| |
| if (gregno + n_words - 1 <= GP_ARG_MAX_REG) |
| { |
| rtx r1, r2; |
| enum machine_mode m = SImode; |
| |
| r1 = gen_rtx_REG (m, gregno); |
| r1 = gen_rtx_EXPR_LIST (m, r1, const0_rtx); |
| r2 = gen_rtx_REG (m, gregno + 1); |
| r2 = gen_rtx_EXPR_LIST (m, r2, GEN_INT (4)); |
| return gen_rtx_PARALLEL (mode, gen_rtvec (2, r1, r2)); |
| } |
| else |
| return NULL_RTX; |
| } |
| else |
| { |
| if (gregno <= GP_ARG_MAX_REG) |
| return gen_rtx_REG (mode, gregno); |
| else |
| return NULL_RTX; |
| } |
| } |
| |
| /* A subroutine of rs6000_darwin64_record_arg. Assign the bits of the |
| structure between cum->intoffset and bitpos to integer registers. */ |
| |
| static void |
| rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *cum, |
| HOST_WIDE_INT bitpos, rtx rvec[], int *k) |
| { |
| enum machine_mode mode; |
| unsigned int regno; |
| unsigned int startbit, endbit; |
| int this_regno, intregs, intoffset; |
| rtx reg; |
| |
| if (cum->intoffset == -1) |
| return; |
| |
| intoffset = cum->intoffset; |
| cum->intoffset = -1; |
| |
| /* If this is the trailing part of a word, try to only load that |
| much into the register. Otherwise load the whole register. Note |
| that in the latter case we may pick up unwanted bits. It's not a |
| problem at the moment but may wish to revisit. */ |
| |
| if (intoffset % BITS_PER_WORD != 0) |
| { |
| mode = mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD, |
| MODE_INT, 0); |
| if (mode == BLKmode) |
| { |
| /* We couldn't find an appropriate mode, which happens, |
| e.g., in packed structs when there are 3 bytes to load. |
| Back intoffset back to the beginning of the word in this |
| case. */ |
| intoffset = intoffset & -BITS_PER_WORD; |
| mode = word_mode; |
| } |
| } |
| else |
| mode = word_mode; |
| |
| startbit = intoffset & -BITS_PER_WORD; |
| endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD; |
| intregs = (endbit - startbit) / BITS_PER_WORD; |
| this_regno = cum->words + intoffset / BITS_PER_WORD; |
| |
| if (intregs > 0 && intregs > GP_ARG_NUM_REG - this_regno) |
| cum->use_stack = 1; |
| |
| intregs = MIN (intregs, GP_ARG_NUM_REG - this_regno); |
| if (intregs <= 0) |
| return; |
| |
| intoffset /= BITS_PER_UNIT; |
| do |
| { |
| regno = GP_ARG_MIN_REG + this_regno; |
| reg = gen_rtx_REG (mode, regno); |
| rvec[(*k)++] = |
| gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset)); |
| |
| this_regno += 1; |
| intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1; |
| mode = word_mode; |
| intregs -= 1; |
| } |
| while (intregs > 0); |
| } |
| |
| /* Recursive workhorse for the following. */ |
| |
| static void |
| rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type, |
| HOST_WIDE_INT startbitpos, rtx rvec[], |
| int *k) |
| { |
| tree f; |
| |
| for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f)) |
| if (TREE_CODE (f) == FIELD_DECL) |
| { |
| HOST_WIDE_INT bitpos = startbitpos; |
| tree ftype = TREE_TYPE (f); |
| enum machine_mode mode; |
| if (ftype == error_mark_node) |
| continue; |
| mode = TYPE_MODE (ftype); |
| |
| if (DECL_SIZE (f) != 0 |
| && host_integerp (bit_position (f), 1)) |
| bitpos += int_bit_position (f); |
| |
| /* ??? FIXME: else assume zero offset. */ |
| |
| if (TREE_CODE (ftype) == RECORD_TYPE) |
| rs6000_darwin64_record_arg_recurse (cum, ftype, bitpos, rvec, k); |
| else if (cum->named && USE_FP_FOR_ARG_P (cum, mode, ftype)) |
| { |
| #if 0 |
| switch (mode) |
| { |
| case SCmode: mode = SFmode; break; |
| case DCmode: mode = DFmode; break; |
| case TCmode: mode = TFmode; break; |
| default: break; |
| } |
| #endif |
| rs6000_darwin64_record_arg_flush (cum, bitpos, rvec, k); |
| rvec[(*k)++] |
| = gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (mode, cum->fregno++), |
| GEN_INT (bitpos / BITS_PER_UNIT)); |
| if (mode == TFmode) |
| cum->fregno++; |
| } |
| else if (cum->named && USE_ALTIVEC_FOR_ARG_P (cum, mode, ftype, 1)) |
| { |
| rs6000_darwin64_record_arg_flush (cum, bitpos, rvec, k); |
| rvec[(*k)++] |
| = gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (mode, cum->vregno++), |
| GEN_INT (bitpos / BITS_PER_UNIT)); |
| } |
| else if (cum->intoffset == -1) |
| cum->intoffset = bitpos; |
| } |
| } |
| |
| /* For the darwin64 ABI, we want to construct a PARALLEL consisting of |
| the register(s) to be used for each field and subfield of a struct |
| being passed by value, along with the offset of where the |
| register's value may be found in the block. FP fields go in FP |
| register, vector fields go in vector registers, and everything |
| else goes in int registers, packed as in memory. |
| |
| This code is also used for function return values. RETVAL indicates |
| whether this is the case. |
| |
| Much of this is taken from the SPARC V9 port, which has a similar |
| calling convention. */ |
| |
| static rtx |
| rs6000_darwin64_record_arg (CUMULATIVE_ARGS *orig_cum, tree type, |
| int named, bool retval) |
| { |
| rtx rvec[FIRST_PSEUDO_REGISTER]; |
| int k = 1, kbase = 1; |
| HOST_WIDE_INT typesize = int_size_in_bytes (type); |
| /* This is a copy; modifications are not visible to our caller. */ |
| CUMULATIVE_ARGS copy_cum = *orig_cum; |
| CUMULATIVE_ARGS *cum = ©_cum; |
| |
| /* Pad to 16 byte boundary if needed. */ |
| if (!retval && TYPE_ALIGN (type) >= 2 * BITS_PER_WORD |
| && (cum->words % 2) != 0) |
| cum->words++; |
| |
| cum->intoffset = 0; |
| cum->use_stack = 0; |
| /* APPLE LOCAL fix 64-bit varargs 4028089 */ |
| cum->floats_in_gpr = 0; |
| cum->named = named; |
| |
| /* Put entries into rvec[] for individual FP and vector fields, and |
| for the chunks of memory that go in int regs. Note we start at |
| element 1; 0 is reserved for an indication of using memory, and |
| may or may not be filled in below. */ |
| rs6000_darwin64_record_arg_recurse (cum, type, 0, rvec, &k); |
| rs6000_darwin64_record_arg_flush (cum, typesize * BITS_PER_UNIT, rvec, &k); |
| |
| /* If any part of the struct went on the stack put all of it there. |
| This hack is because the generic code for |
| FUNCTION_ARG_PARTIAL_NREGS cannot handle cases where the register |
| parts of the struct are not at the beginning. */ |
| if (cum->use_stack) |
| { |
| if (retval) |
| return NULL_RTX; /* doesn't go in registers at all */ |
| kbase = 0; |
| rvec[0] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx); |
| } |
| if (k > 1 || cum->use_stack) |
| return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (k - kbase, &rvec[kbase])); |
| else |
| return NULL_RTX; |
| } |
| |
| /* Determine where to place an argument in 64-bit mode with 32-bit ABI. */ |
| |
| static rtx |
| rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words) |
| { |
| int n_units; |
| int i, k; |
| rtx rvec[GP_ARG_NUM_REG + 1]; |
| |
| if (align_words >= GP_ARG_NUM_REG) |
| return NULL_RTX; |
| |
| n_units = rs6000_arg_size (mode, type); |
| |
| /* Optimize the simple case where the arg fits in one gpr, except in |
| the case of BLKmode due to assign_parms assuming that registers are |
| BITS_PER_WORD wide. */ |
| if (n_units == 0 |
| || (n_units == 1 && mode != BLKmode)) |
| return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words); |
| |
| k = 0; |
| if (align_words + n_units > GP_ARG_NUM_REG) |
| /* Not all of the arg fits in gprs. Say that it goes in memory too, |
| using a magic NULL_RTX component. |
| This is not strictly correct. Only some of the arg belongs in |
| memory, not all of it. However, the normal scheme using |
| function_arg_partial_nregs can result in unusual subregs, eg. |
| (subreg:SI (reg:DF) 4), which are not handled well. The code to |
| store the whole arg to memory is often more efficient than code |
| to store pieces, and we know that space is available in the right |
| place for the whole arg. */ |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx); |
| |
| i = 0; |
| do |
| { |
| rtx r = gen_rtx_REG (SImode, GP_ARG_MIN_REG + align_words); |
| rtx off = GEN_INT (i++ * 4); |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off); |
| } |
| while (++align_words < GP_ARG_NUM_REG && --n_units != 0); |
| |
| return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec)); |
| } |
| |
| /* Determine where to put an argument to a function. |
| Value is zero to push the argument on the stack, |
| or a hard register in which to store the argument. |
| |
| MODE is the argument's machine mode. |
| TYPE is the data type of the argument (as a tree). |
| This is null for libcalls where that information may |
| not be available. |
| CUM is a variable of type CUMULATIVE_ARGS which gives info about |
| the preceding args and about the function being called. It is |
| not modified in this routine. |
| NAMED is nonzero if this argument is a named parameter |
| (otherwise it is an extra parameter matching an ellipsis). |
| |
| On RS/6000 the first eight words of non-FP are normally in registers |
| and the rest are pushed. Under AIX, the first 13 FP args are in registers. |
| Under V.4, the first 8 FP args are in registers. |
| |
| If this is floating-point and no prototype is specified, we use |
| both an FP and integer register (or possibly FP reg and stack). Library |
| functions (when CALL_LIBCALL is set) always have the proper types for args, |
| so we can pass the FP value just in one register. emit_library_function |
| doesn't support PARALLEL anyway. |
| |
| 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. */ |
| |
| rtx |
| function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, |
| tree type, int named) |
| { |
| enum rs6000_abi abi = DEFAULT_ABI; |
| |
| /* Return a marker to indicate whether CR1 needs to set or clear the |
| bit that V.4 uses to say fp args were passed in registers. |
| Assume that we don't need the marker for software floating point, |
| or compiler generated library calls. */ |
| if (mode == VOIDmode) |
| { |
| if (abi == ABI_V4 |
| && (cum->call_cookie & CALL_LIBCALL) == 0 |
| && (cum->stdarg |
| || (cum->nargs_prototype < 0 |
| && (cum->prototype || TARGET_NO_PROTOTYPE)))) |
| { |
| /* For the SPE, we need to crxor CR6 always. */ |
| if (TARGET_SPE_ABI) |
| return GEN_INT (cum->call_cookie | CALL_V4_SET_FP_ARGS); |
| else if (TARGET_HARD_FLOAT && TARGET_FPRS) |
| return GEN_INT (cum->call_cookie |
| | ((cum->fregno == FP_ARG_MIN_REG) |
| ? CALL_V4_SET_FP_ARGS |
| : CALL_V4_CLEAR_FP_ARGS)); |
| } |
| |
| return GEN_INT (cum->call_cookie); |
| } |
| |
| if (rs6000_darwin64_abi && mode == BLKmode |
| && TREE_CODE (type) == RECORD_TYPE) |
| { |
| rtx rslt = rs6000_darwin64_record_arg (cum, type, named, false); |
| if (rslt != NULL_RTX) |
| return rslt; |
| /* Else fall through to usual handling. */ |
| } |
| |
| if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named)) |
| if (TARGET_64BIT && ! cum->prototype) |
| { |
| /* Vector parameters get passed in vector register |
| and also in GPRs or memory, in absence of prototype. */ |
| int align_words; |
| rtx slot; |
| align_words = (cum->words + 1) & ~1; |
| |
| if (align_words >= GP_ARG_NUM_REG) |
| { |
| slot = NULL_RTX; |
| } |
| else |
| { |
| slot = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words); |
| } |
| return gen_rtx_PARALLEL (mode, |
| gen_rtvec (2, |
| gen_rtx_EXPR_LIST (VOIDmode, |
| slot, const0_rtx), |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (mode, cum->vregno), |
| const0_rtx))); |
| } |
| else |
| return gen_rtx_REG (mode, cum->vregno); |
| else if (TARGET_ALTIVEC_ABI |
| && (ALTIVEC_VECTOR_MODE (mode) |
| || (type && TREE_CODE (type) == VECTOR_TYPE |
| && int_size_in_bytes (type) == 16))) |
| { |
| if (named || abi == ABI_V4) |
| return NULL_RTX; |
| else |
| { |
| /* Vector parameters to varargs functions under AIX or Darwin |
| get passed in memory and possibly also in GPRs. */ |
| int align, align_words, n_words; |
| enum machine_mode part_mode; |
| |
| /* 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. */ |
| if (TARGET_32BIT) |
| align = (2 - cum->words) & 3; |
| else |
| align = cum->words & 1; |
| align_words = cum->words + align; |
| |
| /* Out of registers? Memory, then. */ |
| if (align_words >= GP_ARG_NUM_REG) |
| return NULL_RTX; |
| |
| if (TARGET_32BIT && TARGET_POWERPC64) |
| return rs6000_mixed_function_arg (mode, type, align_words); |
| |
| /* The vector value goes in GPRs. Only the part of the |
| value in GPRs is reported here. */ |
| part_mode = mode; |
| n_words = rs6000_arg_size (mode, type); |
| if (align_words + n_words > GP_ARG_NUM_REG) |
| /* Fortunately, there are only two possibilities, the value |
| is either wholly in GPRs or half in GPRs and half not. */ |
| part_mode = DImode; |
| |
| return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words); |
| } |
| } |
| else if (TARGET_SPE_ABI && TARGET_SPE |
| && (SPE_VECTOR_MODE (mode) |
| || (TARGET_E500_DOUBLE && (mode == DFmode |
| || mode == DCmode)))) |
| return rs6000_spe_function_arg (cum, mode, type); |
| |
| else if (abi == ABI_V4) |
| { |
| if (TARGET_HARD_FLOAT && TARGET_FPRS |
| && (mode == SFmode || mode == DFmode |
| || (mode == TFmode && !TARGET_IEEEQUAD))) |
| { |
| if (cum->fregno + (mode == TFmode ? 1 : 0) <= FP_ARG_V4_MAX_REG) |
| return gen_rtx_REG (mode, cum->fregno); |
| else |
| return NULL_RTX; |
| } |
| 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 (n_words == 2) |
| gregno += (1 - gregno) & 1; |
| |
| /* Multi-reg args are not split between registers and stack. */ |
| if (gregno + n_words - 1 > GP_ARG_MAX_REG) |
| return NULL_RTX; |
| |
| if (TARGET_32BIT && TARGET_POWERPC64) |
| return rs6000_mixed_function_arg (mode, type, |
| gregno - GP_ARG_MIN_REG); |
| return gen_rtx_REG (mode, gregno); |
| } |
| } |
| else |
| { |
| int align_words = rs6000_parm_start (mode, type, cum->words); |
| |
| if (USE_FP_FOR_ARG_P (cum, mode, type)) |
| { |
| rtx rvec[GP_ARG_NUM_REG + 1]; |
| rtx r; |
| int k; |
| bool needs_psave; |
| enum machine_mode fmode = mode; |
| unsigned long n_fpreg = (GET_MODE_SIZE (mode) + 7) >> 3; |
| |
| if (cum->fregno + n_fpreg > FP_ARG_MAX_REG + 1) |
| { |
| /* Currently, we only ever need one reg here because complex |
| doubles are split. */ |
| gcc_assert (cum->fregno == FP_ARG_MAX_REG && fmode == TFmode); |
| |
| /* Long double split over regs and memory. */ |
| fmode = DFmode; |
| } |
| |
| /* Do we also need to pass this arg in the parameter save |
| area? */ |
| needs_psave = (type |
| && (cum->nargs_prototype <= 0 |
| || (DEFAULT_ABI == ABI_AIX |
| && TARGET_XL_COMPAT |
| && align_words >= GP_ARG_NUM_REG))); |
| |
| if (!needs_psave && mode == fmode) |
| return gen_rtx_REG (fmode, cum->fregno); |
| |
| k = 0; |
| if (needs_psave) |
| { |
| /* Describe the part that goes in gprs or the stack. |
| This piece must come first, before the fprs. */ |
| if (align_words < GP_ARG_NUM_REG) |
| { |
| unsigned long n_words = rs6000_arg_size (mode, type); |
| |
| if (align_words + n_words > GP_ARG_NUM_REG |
| || (TARGET_32BIT && TARGET_POWERPC64)) |
| { |
| /* If this is partially on the stack, then we only |
| include the portion actually in registers here. */ |
| enum machine_mode rmode = TARGET_32BIT ? SImode : DImode; |
| rtx off; |
| int i = 0; |
| if (align_words + n_words > GP_ARG_NUM_REG) |
| /* Not all of the arg fits in gprs. Say that it |
| goes in memory too, using a magic NULL_RTX |
| component. Also see comment in |
| rs6000_mixed_function_arg for why the normal |
| function_arg_partial_nregs scheme doesn't work |
| in this case. */ |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, |
| const0_rtx); |
| do |
| { |
| r = gen_rtx_REG (rmode, |
| GP_ARG_MIN_REG + align_words); |
| off = GEN_INT (i++ * GET_MODE_SIZE (rmode)); |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off); |
| } |
| while (++align_words < GP_ARG_NUM_REG && --n_words != 0); |
| } |
| else |
| { |
| /* The whole arg fits in gprs. */ |
| r = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words); |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx); |
| } |
| } |
| else |
| /* It's entirely in memory. */ |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx); |
| } |
| |
| /* Describe where this piece goes in the fprs. */ |
| r = gen_rtx_REG (fmode, cum->fregno); |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx); |
| |
| return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec)); |
| } |
| else if (align_words < GP_ARG_NUM_REG) |
| { |
| if (TARGET_32BIT && TARGET_POWERPC64) |
| return rs6000_mixed_function_arg (mode, type, align_words); |
| |
| if (mode == BLKmode) |
| mode = Pmode; |
| |
| return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words); |
| } |
| else |
| return NULL_RTX; |
| } |
| } |
| |
| /* For an arg passed partly in registers and partly in memory, this is |
| the number of bytes passed in registers. For args passed entirely in |
| registers or entirely in memory, zero. When an arg is described by a |
| PARALLEL, perhaps using more than one register type, this function |
| returns the number of bytes used by the first element of the PARALLEL. */ |
| |
| static int |
| rs6000_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, |
| tree type, bool named) |
| { |
| int ret = 0; |
| int align_words; |
| |
| if (DEFAULT_ABI == ABI_V4) |
| return 0; |
| |
| if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named) |
| && cum->nargs_prototype >= 0) |
| return 0; |
| |
| /* In this complicated case we just disable the partial_nregs code. */ |
| if (rs6000_darwin64_abi && mode == BLKmode |
| && TREE_CODE (type) == RECORD_TYPE |
| && int_size_in_bytes (type) > 0) |
| return 0; |
| |
| align_words = rs6000_parm_start (mode, type, cum->words); |
| |
| if (USE_FP_FOR_ARG_P (cum, mode, type)) |
| { |
| /* If we are passing this arg in the fixed parameter save area |
| (gprs or memory) as well as fprs, then this function should |
| return the number of partial bytes passed in the parameter |
| save area rather than partial bytes passed in fprs. */ |
| if (type |
| && (cum->nargs_prototype <= 0 |
| || (DEFAULT_ABI == ABI_AIX |
| && TARGET_XL_COMPAT |
| && align_words >= GP_ARG_NUM_REG))) |
| return 0; |
| else if (cum->fregno + ((GET_MODE_SIZE (mode) + 7) >> 3) |
| > FP_ARG_MAX_REG + 1) |
| ret = (FP_ARG_MAX_REG + 1 - cum->fregno) * 8; |
| else if (cum->nargs_prototype >= 0) |
| return 0; |
| } |
| |
| if (align_words < GP_ARG_NUM_REG |
| && GP_ARG_NUM_REG < align_words + rs6000_arg_size (mode, type)) |
| ret = (GP_ARG_NUM_REG - align_words) * (TARGET_32BIT ? 4 : 8); |
| |
| if (ret != 0 && TARGET_DEBUG_ARG) |
| fprintf (stderr, "rs6000_arg_partial_bytes: %d\n", ret); |
| |
| return ret; |
| } |
| |
| /* A C expression that indicates when an argument must be passed by |
| reference. If nonzero for an argument, a copy of that argument is |
| made in memory and a pointer to the argument is passed instead of |
| the argument itself. The pointer is passed in whatever way is |
| appropriate for passing a pointer to that type. |
| |
| Under V.4, aggregates and long double are passed by reference. |
| |
| As an extension to all 32-bit ABIs, AltiVec vectors are passed by |
| reference unless the AltiVec vector extension ABI is in force. |
| |
| As an extension to all ABIs, variable sized types are passed by |
| reference. */ |
| |
| static bool |
| rs6000_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, |
| enum machine_mode mode, tree type, |
| bool named ATTRIBUTE_UNUSED) |
| { |
| if (DEFAULT_ABI == ABI_V4 && TARGET_IEEEQUAD && mode == TFmode) |
| { |
| if (TARGET_DEBUG_ARG) |
| fprintf (stderr, "function_arg_pass_by_reference: V4 long double\n"); |
| return 1; |
| } |
| |
| if (!type) |
| return 0; |
| |
| if (DEFAULT_ABI == ABI_V4 && AGGREGATE_TYPE_P (type)) |
| { |
| if (TARGET_DEBUG_ARG) |
| fprintf (stderr, "function_arg_pass_by_reference: V4 aggregate\n"); |
| return 1; |
| } |
| |
| if (int_size_in_bytes (type) < 0) |
| { |
| if (TARGET_DEBUG_ARG) |
| fprintf (stderr, "function_arg_pass_by_reference: variable size\n"); |
| return 1; |
| } |
| |
| /* Allow -maltivec -mabi=no-altivec without warning. Altivec vector |
| modes only exist for GCC vector types if -maltivec. */ |
| if (TARGET_32BIT && !TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode)) |
| { |
| if (TARGET_DEBUG_ARG) |
| fprintf (stderr, "function_arg_pass_by_reference: AltiVec\n"); |
| return 1; |
| } |
| |
| /* Pass synthetic vectors in memory. */ |
| if (TREE_CODE (type) == VECTOR_TYPE |
| && int_size_in_bytes (type) > (TARGET_ALTIVEC_ABI ? 16 : 8)) |
| { |
| static bool warned_for_pass_big_vectors = false; |
| if (TARGET_DEBUG_ARG) |
| fprintf (stderr, "function_arg_pass_by_reference: synthetic vector\n"); |
| if (!warned_for_pass_big_vectors) |
| { |
| warning (0, "GCC vector passed by reference: " |
| "non-standard ABI extension with no compatibility guarantee"); |
| warned_for_pass_big_vectors = true; |
| } |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| rs6000_move_block_from_reg (int regno, rtx x, int nregs) |
| { |
| int i; |
| enum machine_mode reg_mode = TARGET_32BIT ? SImode : DImode; |
| |
| if (nregs == 0) |
| return; |
| |
| for (i = 0; i < nregs; i++) |
| { |
| rtx tem = adjust_address_nv (x, reg_mode, i * GET_MODE_SIZE (reg_mode)); |
| if (reload_completed) |
| { |
| if (! strict_memory_address_p (reg_mode, XEXP (tem, 0))) |
| tem = NULL_RTX; |
| else |
| tem = simplify_gen_subreg (reg_mode, x, BLKmode, |
| i * GET_MODE_SIZE (reg_mode)); |
| } |
| else |
| tem = replace_equiv_address (tem, XEXP (tem, 0)); |
| |
| gcc_assert (tem); |
| |
| emit_move_insn (tem, gen_rtx_REG (reg_mode, regno + i)); |
| } |
| } |
| |
| /* Perform any needed actions needed for a function that is receiving a |
| variable number of arguments. |
| |
| CUM is as above. |
| |
| MODE and TYPE are the mode and type of the current parameter. |
| |
| PRETEND_SIZE is a variable that should be set to the amount of stack |
| that must be pushed by the prolog to pretend that our caller pushed |
| it. |
| |
| Normally, this macro will push all remaining incoming registers on the |
| stack and set PRETEND_SIZE to the length of the registers pushed. */ |
| |
| static void |
| setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode, |
| tree type, int *pretend_size ATTRIBUTE_UNUSED, |
| int no_rtl) |
| { |
| CUMULATIVE_ARGS next_cum; |
| int reg_size = TARGET_32BIT ? 4 : 8; |
| rtx save_area = NULL_RTX, mem; |
| int first_reg_offset, set; |
| |
| /* Skip the last named argument. */ |
| next_cum = *cum; |
| function_arg_advance (&next_cum, mode, type, 1, 0); |
| |
| if (DEFAULT_ABI == ABI_V4) |
| { |
| first_reg_offset = next_cum.sysv_gregno - GP_ARG_MIN_REG; |
| |
| if (! no_rtl) |
| { |
| int gpr_reg_num = 0, gpr_size = 0, fpr_size = 0; |
| HOST_WIDE_INT offset = 0; |
| |
| /* Try to optimize the size of the varargs save area. |
| The ABI requires that ap.reg_save_area is doubleword |
| aligned, but we don't need to allocate space for all |
| the bytes, only those to which we actually will save |
| anything. */ |
| if (cfun->va_list_gpr_size && first_reg_offset < GP_ARG_NUM_REG) |
| gpr_reg_num = GP_ARG_NUM_REG - first_reg_offset; |
| if (TARGET_HARD_FLOAT && TARGET_FPRS |
| && next_cum.fregno <= FP_ARG_V4_MAX_REG |
| && cfun->va_list_fpr_size) |
| { |
| if (gpr_reg_num) |
| fpr_size = (next_cum.fregno - FP_ARG_MIN_REG) |
| * UNITS_PER_FP_WORD; |
| if (cfun->va_list_fpr_size |
| < FP_ARG_V4_MAX_REG + 1 - next_cum.fregno) |
| fpr_size += cfun->va_list_fpr_size * UNITS_PER_FP_WORD; |
| else |
| fpr_size += (FP_ARG_V4_MAX_REG + 1 - next_cum.fregno) |
| * UNITS_PER_FP_WORD; |
| } |
| if (gpr_reg_num) |
| { |
| offset = -((first_reg_offset * reg_size) & ~7); |
| if (!fpr_size && gpr_reg_num > cfun->va_list_gpr_size) |
| { |
| gpr_reg_num = cfun->va_list_gpr_size; |
| if (reg_size == 4 && (first_reg_offset & 1)) |
| gpr_reg_num++; |
| } |
| gpr_size = (gpr_reg_num * reg_size + 7) & ~7; |
| } |
| else if (fpr_size) |
| offset = - (int) (next_cum.fregno - FP_ARG_MIN_REG) |
| * UNITS_PER_FP_WORD |
| - (int) (GP_ARG_NUM_REG * reg_size); |
| |
| if (gpr_size + fpr_size) |
| { |
| rtx reg_save_area |
| = assign_stack_local (BLKmode, gpr_size + fpr_size, 64); |
| gcc_assert (GET_CODE (reg_save_area) == MEM); |
| reg_save_area = XEXP (reg_save_area, 0); |
| if (GET_CODE (reg_save_area) == PLUS) |
| { |
| gcc_assert (XEXP (reg_save_area, 0) |
| == virtual_stack_vars_rtx); |
| gcc_assert (GET_CODE (XEXP (reg_save_area, 1)) == CONST_INT); |
| offset += INTVAL (XEXP (reg_save_area, 1)); |
| } |
| else |
| gcc_assert (reg_save_area == virtual_stack_vars_rtx); |
| } |
| |
| cfun->machine->varargs_save_offset = offset; |
| save_area = plus_constant (virtual_stack_vars_rtx, offset); |
| } |
| } |
| else |
| { |
| first_reg_offset = next_cum.words; |
| save_area = virtual_incoming_args_rtx; |
| |
| if (targetm.calls.must_pass_in_stack (mode, type)) |
| first_reg_offset += rs6000_arg_size (TYPE_MODE (type), type); |
| } |
| |
| set = get_varargs_alias_set (); |
| if (! no_rtl && first_reg_offset < GP_ARG_NUM_REG |
| && cfun->va_list_gpr_size) |
| { |
| int nregs = GP_ARG_NUM_REG - first_reg_offset; |
| |
| if (va_list_gpr_counter_field) |
| { |
| /* V4 va_list_gpr_size counts number of registers needed. */ |
| if (nregs > cfun->va_list_gpr_size) |
| nregs = cfun->va_list_gpr_size; |
| } |
| else |
| { |
| /* char * va_list instead counts number of bytes needed. */ |
| if (nregs > cfun->va_list_gpr_size / reg_size) |
| nregs = cfun->va_list_gpr_size / reg_size; |
| } |
| |
| mem = gen_rtx_MEM (BLKmode, |
| plus_constant (save_area, |
| first_reg_offset * reg_size)); |
| MEM_NOTRAP_P (mem) = 1; |
| set_mem_alias_set (mem, set); |
| set_mem_align (mem, BITS_PER_WORD); |
| |
| rs6000_move_block_from_reg (GP_ARG_MIN_REG + first_reg_offset, mem, |
| nregs); |
| } |
| |
| /* Save FP registers if needed. */ |
| if (DEFAULT_ABI == ABI_V4 |
| && TARGET_HARD_FLOAT && TARGET_FPRS |
| && ! no_rtl |
| && next_cum.fregno <= FP_ARG_V4_MAX_REG |
| && cfun->va_list_fpr_size) |
| { |
| int fregno = next_cum.fregno, nregs; |
| rtx cr1 = gen_rtx_REG (CCmode, CR1_REGNO); |
| rtx lab = gen_label_rtx (); |
| int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) |
| * UNITS_PER_FP_WORD); |
| |
| emit_jump_insn |
| (gen_rtx_SET (VOIDmode, |
| pc_rtx, |
| gen_rtx_IF_THEN_ELSE (VOIDmode, |
| gen_rtx_NE (VOIDmode, cr1, |
| const0_rtx), |
| gen_rtx_LABEL_REF (VOIDmode, lab), |
| pc_rtx))); |
| |
| for (nregs = 0; |
| fregno <= FP_ARG_V4_MAX_REG && nregs < cfun->va_list_fpr_size; |
| fregno++, off += UNITS_PER_FP_WORD, nregs++) |
| { |
| mem = gen_rtx_MEM (DFmode, plus_constant (save_area, off)); |
| MEM_NOTRAP_P (mem) = 1; |
| set_mem_alias_set (mem, set); |
| set_mem_align (mem, GET_MODE_ALIGNMENT (DFmode)); |
| emit_move_insn (mem, gen_rtx_REG (DFmode, fregno)); |
| } |
| |
| emit_label (lab); |
| } |
| } |
| |
| /* APPLE LOCAL begin Altivec */ |
| /* This routine determins if an extra pass over argument list is needed |
| for vector aruments. It returns true, if current argument need be |
| skipped. This depends on if we are in the first iteration (to skip |
| vectors), or 2nd iteration (to skip non-vectors). |
| */ |
| |
| static |
| bool skip_vec_args(tree arg_type, int pass, int *last_pass) |
| { |
| if (DEFAULT_ABI != ABI_DARWIN) |
| return false; |
| |
| if (TREE_CODE (arg_type) == VECTOR_TYPE) |
| { |
| *last_pass = 2; |
| if (pass == 1) |
| return true; |
| } |
| else if (pass == 2) |
| return true; |
| return false; |
| } |
| /* APPLE LOCAL end Altivec */ |
| |
| |
| /* Create the va_list data type. */ |
| |
| static tree |
| rs6000_build_builtin_va_list (void) |
| { |
| tree f_gpr, f_fpr, f_res, f_ovf, f_sav, record, type_decl; |
| |
| /* For AIX, prefer 'char *' because that's what the system |
| header files like. */ |
| if (DEFAULT_ABI != ABI_V4) |
| return build_pointer_type (char_type_node); |
| |
| record = (*lang_hooks.types.make_type) (RECORD_TYPE); |
| type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); |
| |
| f_gpr = build_decl (FIELD_DECL, get_identifier ("gpr"), |
| unsigned_char_type_node); |
| f_fpr = build_decl (FIELD_DECL, get_identifier ("fpr"), |
| unsigned_char_type_node); |
| /* Give the two bytes of padding a name, so that -Wpadded won't warn on |
| every user file. */ |
| f_res = build_decl (FIELD_DECL, get_identifier ("reserved"), |
| short_unsigned_type_node); |
| f_ovf = build_decl (FIELD_DECL, get_identifier ("overflow_arg_area"), |
| ptr_type_node); |
| f_sav = build_decl (FIELD_DECL, get_identifier ("reg_save_area"), |
| ptr_type_node); |
| |
| va_list_gpr_counter_field = f_gpr; |
| va_list_fpr_counter_field = f_fpr; |
| |
| DECL_FIELD_CONTEXT (f_gpr) = record; |
| DECL_FIELD_CONTEXT (f_fpr) = record; |
| DECL_FIELD_CONTEXT (f_res) = record; |
| DECL_FIELD_CONTEXT (f_ovf) = record; |
| DECL_FIELD_CONTEXT (f_sav) = record; |
| |
| TREE_CHAIN (record) = type_decl; |
| TYPE_NAME (record) = type_decl; |
| TYPE_FIELDS (record) = f_gpr; |
| TREE_CHAIN (f_gpr) = f_fpr; |
| TREE_CHAIN (f_fpr) = f_res; |
| TREE_CHAIN (f_res) = f_ovf; |
| TREE_CHAIN (f_ovf) = f_sav; |
| |
| layout_type (record); |
| |
| /* The correct type is an array type of one element. */ |
| return build_array_type (record, build_index_type (size_zero_node)); |
| } |
| |
| /* Implement va_start. */ |
| |
| void |
| rs6000_va_start (tree valist, rtx nextarg) |
| { |
| HOST_WIDE_INT words, n_gpr, n_fpr; |
| tree f_gpr, f_fpr, f_res, f_ovf, f_sav; |
| tree gpr, fpr, ovf, sav, t; |
| |
| /* Only SVR4 needs something special. */ |
| if (DEFAULT_ABI != ABI_V4) |
| { |
| std_expand_builtin_va_start (valist, nextarg); |
| return; |
| } |
| |
| f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); |
| f_fpr = TREE_CHAIN (f_gpr); |
| f_res = TREE_CHAIN (f_fpr); |
| f_ovf = TREE_CHAIN (f_res); |
| f_sav = TREE_CHAIN (f_ovf); |
| |
| valist = build_va_arg_indirect_ref (valist); |
| gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); |
| fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); |
| ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); |
| sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); |
| |
| /* Count number of gp and fp argument registers used. */ |
| words = current_function_args_info.words; |
| n_gpr = MIN (current_function_args_info.sysv_gregno - GP_ARG_MIN_REG, |
| GP_ARG_NUM_REG); |
| n_fpr = MIN (current_function_args_info.fregno - FP_ARG_MIN_REG, |
| FP_ARG_NUM_REG); |
| |
| if (TARGET_DEBUG_ARG) |
| fprintf (stderr, "va_start: words = "HOST_WIDE_INT_PRINT_DEC", n_gpr = " |
| HOST_WIDE_INT_PRINT_DEC", n_fpr = "HOST_WIDE_INT_PRINT_DEC"\n", |
| words, n_gpr, n_fpr); |
| |
| if (cfun->va_list_gpr_size) |
| { |
| t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr, |
| build_int_cst (NULL_TREE, n_gpr)); |
| TREE_SIDE_EFFECTS (t) = 1; |
| expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); |
| } |
| |
| if (cfun->va_list_fpr_size) |
| { |
| t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr, |
| build_int_cst (NULL_TREE, n_fpr)); |
| TREE_SIDE_EFFECTS (t) = 1; |
| expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); |
| } |
| |
| /* Find the overflow area. */ |
| t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); |
| if (words != 0) |
| t = build2 (PLUS_EXPR, TREE_TYPE (ovf), t, |
| build_int_cst (NULL_TREE, words * UNITS_PER_WORD)); |
| t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); |
| TREE_SIDE_EFFECTS (t) = 1; |
| expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); |
| |
| /* If there were no va_arg invocations, don't set up the register |
| save area. */ |
| if (!cfun->va_list_gpr_size |
| && !cfun->va_list_fpr_size |
| && n_gpr < GP_ARG_NUM_REG |
| && n_fpr < FP_ARG_V4_MAX_REG) |
| return; |
| |
| /* Find the register save area. */ |
| t = make_tree (TREE_TYPE (sav), virtual_stack_vars_rtx); |
| if (cfun->machine->varargs_save_offset) |
| t = build2 (PLUS_EXPR, TREE_TYPE (sav), t, |
| build_int_cst (NULL_TREE, cfun->machine->varargs_save_offset)); |
| t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t); |
| TREE_SIDE_EFFECTS (t) = 1; |
| expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); |
| } |
| |
| /* Implement va_arg. */ |
| |
| tree |
| rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p) |
| { |
| tree f_gpr, f_fpr, f_res, f_ovf, f_sav; |
| tree gpr, fpr, ovf, sav, reg, t, u; |
| int size, rsize, n_reg, sav_ofs, sav_scale; |
| tree lab_false, lab_over, addr; |
| int align; |
| tree ptrtype = build_pointer_type (type); |
| |
| if (pass_by_reference (NULL, TYPE_MODE (type), type, false)) |
| { |
| t = rs6000_gimplify_va_arg (valist, ptrtype, pre_p, post_p); |
| return build_va_arg_indirect_ref (t); |
| } |
| |
| if (DEFAULT_ABI != ABI_V4) |
| { |
| if (targetm.calls.split_complex_arg && TREE_CODE (type) == COMPLEX_TYPE) |
| { |
| tree elem_type = TREE_TYPE (type); |
| enum machine_mode elem_mode = TYPE_MODE (elem_type); |
| int elem_size = GET_MODE_SIZE (elem_mode); |
| |
| if (elem_size < UNITS_PER_WORD) |
| { |
| tree real_part, imag_part; |
| tree post = NULL_TREE; |
| |
| real_part = rs6000_gimplify_va_arg (valist, elem_type, pre_p, |
| &post); |
| /* Copy the value into a temporary, lest the formal temporary |
| be reused out from under us. */ |
| real_part = get_initialized_tmp_var (real_part, pre_p, &post); |
| append_to_statement_list (post, pre_p); |
| |
| imag_part = rs6000_gimplify_va_arg (valist, elem_type, pre_p, |
| post_p); |
| |
| return build2 (COMPLEX_EXPR, type, real_part, imag_part); |
| } |
| } |
| |
| return std_gimplify_va_arg_expr (valist, type, pre_p, post_p); |
| } |
| |
| f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); |
| f_fpr = TREE_CHAIN (f_gpr); |
| f_res = TREE_CHAIN (f_fpr); |
| f_ovf = TREE_CHAIN (f_res); |
| f_sav = TREE_CHAIN (f_ovf); |
| |
| valist = build_va_arg_indirect_ref (valist); |
| gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); |
| fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); |
| ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); |
| sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); |
| |
| size = int_size_in_bytes (type); |
| rsize = (size + 3) / 4; |
| align = 1; |
| |
| if (TARGET_HARD_FLOAT && TARGET_FPRS |
| && (TYPE_MODE (type) == SFmode |
| || TYPE_MODE (type) == DFmode |
| || TYPE_MODE (type) == TFmode)) |
| { |
| /* FP args go in FP registers, if present. */ |
| reg = fpr; |
| n_reg = (size + 7) / 8; |
| sav_ofs = 8*4; |
| sav_scale = 8; |
| if (TYPE_MODE (type) != SFmode) |
| align = 8; |
| } |
| else |
| { |
| /* Otherwise into GP registers. */ |
| reg = gpr; |
| n_reg = rsize; |
| sav_ofs = 0; |
| sav_scale = 4; |
| if (n_reg == 2) |
| align = 8; |
| } |
| |
| /* Pull the value out of the saved registers.... */ |
| |
| lab_over = NULL; |
| addr = create_tmp_var (ptr_type_node, "addr"); |
| DECL_POINTER_ALIAS_SET (addr) = get_varargs_alias_set (); |
| |
| /* AltiVec vectors never go in registers when -mabi=altivec. */ |
| if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (TYPE_MODE (type))) |
| align = 16; |
| else |
| { |
| lab_false = create_artificial_label (); |
| lab_over = create_artificial_label (); |
| |
| /* Long long and SPE vectors are aligned in the registers. |
| As are any other 2 gpr item such as complex int due to a |
| historical mistake. */ |
| u = reg; |
| if (n_reg == 2 && reg == gpr) |
| { |
| u = build2 (BIT_AND_EXPR, TREE_TYPE (reg), reg, |
| size_int (n_reg - 1)); |
| u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg, u); |
| } |
| |
| t = fold_convert (TREE_TYPE (reg), size_int (8 - n_reg + 1)); |
| t = build2 (GE_EXPR, boolean_type_node, u, t); |
| u = build1 (GOTO_EXPR, void_type_node, lab_false); |
| t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE); |
| gimplify_and_add (t, pre_p); |
| |
| t = sav; |
| if (sav_ofs) |
| t = build2 (PLUS_EXPR, ptr_type_node, sav, size_int (sav_ofs)); |
| |
| u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg, size_int (n_reg)); |
| u = build1 (CONVERT_EXPR, integer_type_node, u); |
| u = build2 (MULT_EXPR, integer_type_node, u, size_int (sav_scale)); |
| t = build2 (PLUS_EXPR, ptr_type_node, t, u); |
| |
| t = build2 (MODIFY_EXPR, void_type_node, addr, t); |
| gimplify_and_add (t, pre_p); |
| |
| t = build1 (GOTO_EXPR, void_type_node, lab_over); |
| gimplify_and_add (t, pre_p); |
| |
| t = build1 (LABEL_EXPR, void_type_node, lab_false); |
| append_to_statement_list (t, pre_p); |
| |
| if ((n_reg == 2 && reg != gpr) || n_reg > 2) |
| { |
| /* Ensure that we don't find any more args in regs. |
| Alignment has taken care of the n_reg == 2 gpr case. */ |
| t = build2 (MODIFY_EXPR, TREE_TYPE (reg), reg, size_int (8)); |
| gimplify_and_add (t, pre_p); |
| } |
| } |
| |
| /* ... otherwise out of the overflow area. */ |
| |
| /* Care for on-stack alignment if needed. */ |
| t = ovf; |
| if (align != 1) |
| { |
| t = build2 (PLUS_EXPR, TREE_TYPE (t), t, size_int (align - 1)); |
| t = build2 (BIT_AND_EXPR, TREE_TYPE (t), t, |
| build_int_cst (NULL_TREE, -align)); |
| } |
| gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue); |
| |
| u = build2 (MODIFY_EXPR, void_type_node, addr, t); |
| gimplify_and_add (u, pre_p); |
| |
| t = build2 (PLUS_EXPR, TREE_TYPE (t), t, size_int (size)); |
| t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); |
| gimplify_and_add (t, pre_p); |
| |
| if (lab_over) |
| { |
| t = build1 (LABEL_EXPR, void_type_node, lab_over); |
| append_to_statement_list (t, pre_p); |
| } |
| |
| if (STRICT_ALIGNMENT |
| && (TYPE_ALIGN (type) |
| > (unsigned) BITS_PER_UNIT * (align < 4 ? 4 : align))) |
| { |
| /* The value (of type complex double, for example) may not be |
| aligned in memory in the saved registers, so copy via a |
| temporary. (This is the same code as used for SPARC.) */ |
| tree tmp = create_tmp_var (type, "va_arg_tmp"); |
| tree dest_addr = build_fold_addr_expr (tmp); |
| |
| tree copy = build_function_call_expr |
| (implicit_built_in_decls[BUILT_IN_MEMCPY], |
| tree_cons (NULL_TREE, dest_addr, |
| tree_cons (NULL_TREE, addr, |
| tree_cons (NULL_TREE, size_int (rsize * 4), |
| NULL_TREE)))); |
| |
| gimplify_and_add (copy, pre_p); |
| addr = dest_addr; |
| } |
| |
| addr = fold_convert (ptrtype, addr); |
| return build_va_arg_indirect_ref (addr); |
| } |
| |
| /* Builtins. */ |
| |
| static void |
| def_builtin (int mask, const char *name, tree type, int code) |
| { |
| if (mask & target_flags) |
| { |
| if (rs6000_builtin_decls[code]) |
| abort (); |
| |
| rs6000_builtin_decls[code] = |
| lang_hooks.builtin_function (name, type, code, BUILT_IN_MD, |
| NULL, NULL_TREE); |
| } |
| } |
| |
| /* APPLE LOCAL begin AltiVec */ |
| /* The AltiVec PIM operations and predicates (used in Apple AltiVec mode) |
| are stored in ALTIVEC_PIM_TABLE below, each annotated with flags indicating |
| how its arguments should be matched and/or how its return type is to be |
| determined. */ |
| |
| enum pim_flags |
| { |
| /* CR6 predicate modifiers. Not used for operations. For predicates, |
| one of the following four values shall be prepended to the argument |
| list as an INTEGER_CST. */ |
| |
| pim_cr6_eq = 0, /* __CR6_EQ */ |
| pim_cr6_ne = 1, /* __CR6_EQ_REV */ |
| pim_cr6_lt = 2, /* __CR6_LT */ |
| pim_cr6_ge = 3, /* __CR6_LT_REV */ |
| pim_cr6_MASK = pim_cr6_eq | pim_cr6_ne | pim_cr6_lt | pim_cr6_ge, |
| |
| /* Function overload argument matching. Operations and predicates with |
| multiple overload candidates will have multiple entries, listed |
| contiguously, in the ALTIVEC_PIM_TABLE below. When the |
| rs6000_fold_builtin() routine is called, it will first point at |
| the first entry. If any of the pim_ovl_... flags is set for this |
| entry, the argument(s) to rs6000_fold_builtin() will be type-checked |
| accordingly. If the check succeeds, the current entry will be |
| used to rewrite the PIM instruction into a __builtin instruction; |
| if the check fails, the next entry in ALTIVEC_PIM_TABLE is selected |
| and the pim_ovl_... type comparison is made again. */ |
| |
| pim_ovl_16 = 4, /* First argument must be a 16-element vector */ |
| pim_ovl_16u = 8, |
| pim_ovl_8 = 12, /* First argument must be an 8-element vector */ |
| pim_ovl_8u = 16, |
| pim_ovl_8p = 20, /* First argument must be a vector pixel */ |
| pim_ovl_4 = 24, /* First argument must be a 4-element vector */ |
| pim_ovl_4u = 28, |
| pim_ovl_4f = 32, /* First argument must be a vector float */ |
| pim_ovl_16u_16u = 36, /* First two args must be unsigned 16-el vectors */ |
| pim_ovl_8u_8u = 40, |
| pim_ovl_4u_4u = 44, |
| pim_ovl_pqi_2 = 48, /* Second argument must be a pointer to QI. */ |
| pim_ovl_phi_2 = 52, /* Second argument must be a pointer to HI. */ |
| pim_ovl_psi_2 = 56, /* Second argument must be a pointer to SI. */ |
| pim_ovl_MASK = pim_ovl_16 | pim_ovl_16u | pim_ovl_8 | pim_ovl_8u |
| | pim_ovl_8p | pim_ovl_4 | pim_ovl_4u | pim_ovl_4f |
| | pim_ovl_16u_16u | pim_ovl_8u_8u | pim_ovl_4u_4u |
| | pim_ovl_pqi_2 | pim_ovl_phi_2 | pim_ovl_psi_2, |
| |
| /* Return type computation. For some operations/predicates, the return |
| type is not always the same (in which case it will be stored |
| in the ALTIVEC_PIM_table), but rather is a function of the arguments |
| supplied. */ |
| |
| pim_rt_12 = 512, /* Covariant with first two arguments. */ |
| pim_rt_2p = 1024, /* Covariant with pointee of second argument. */ |
| pim_rt_1 = 1536, /* Covariant with first argument only. */ |
| pim_rt_1d = 2048, /* Double the vector element size of first arg. */ |
| pim_rt_1h = 2560, /* Halve the vector element size of first arg. */ |
| pim_rt_MASK = pim_rt_12 | pim_rt_2p | pim_rt_1 | pim_rt_1d | pim_rt_1h, |
| |
| /* Argument manipulation. Before the __builtin instructions are called, |
| the arguments may need to be rearranged. In addition, for all |
| predicates, one of the CR6 values will be prepended to the argument |
| list (see pim_cr6_... above). */ |
| |
| pim_manip_swap = 8192, /* Swap the first two arguments. */ |
| pim_manip_dup = 16384, /* Duplicate first argument. */ |
| pim_manip_MASK = pim_manip_swap | pim_manip_dup, |
| |
| /* Mark the beginning of instruction groups. For our purposes, an |
| instruction group is the collection of overload candidates for |
| a particular instruction or predicate. For example, the entries |
| "vec_abss", "vec_abss.2" and "vec_abss.3" defined in |
| altivec_init_builtins() below constitute a group, as does the |
| singleton "vec_addc" entry. */ |
| |
| pim_group = 32768 |
| }; |
| |
| struct altivec_pim_info GTY(()) |
| { |
| tree rettype; /* Return type (unless pim_rt_... flags are used). */ |
| int insn; /* DECL_FUNCTION_CODE of the underlying '__builtin_...'. */ |
| enum pim_flags flags; /* See 'enum pim_flags' above. */ |
| }; |
| |
| static GTY(()) struct altivec_pim_info |
| altivec_pim_table[ALTIVEC_PIM__LAST - ALTIVEC_PIM__FIRST + 1]; |
| |
| #define def_pim_builtin(NAME, TYPE, INSN, FLAGS) \ |
| do { \ |
| lang_hooks.builtin_function (NAME, int_ftype_ellipsis, pim_code, \ |
| BUILT_IN_MD, NULL, NULL_TREE); \ |
| \ |
| altivec_pim_table[pim_code - ALTIVEC_PIM__FIRST].rettype = TYPE; \ |
| altivec_pim_table[pim_code - ALTIVEC_PIM__FIRST].insn \ |
| = ALTIVEC_BUILTIN_##INSN; \ |
| altivec_pim_table[pim_code - ALTIVEC_PIM__FIRST].flags = FLAGS; \ |
| \ |
| ++pim_code; \ |
| } while (0) |
| /* APPLE LOCAL end AltiVec */ |
| |
| /* Simple ternary operations: VECd = foo (VECa, VECb, VECc). */ |
| |
| static const struct builtin_description bdesc_3arg[] = |
| { |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmaddfp, "__builtin_altivec_vmaddfp", ALTIVEC_BUILTIN_VMADDFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmhaddshs, "__builtin_altivec_vmhaddshs", ALTIVEC_BUILTIN_VMHADDSHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmhraddshs, "__builtin_altivec_vmhraddshs", ALTIVEC_BUILTIN_VMHRADDSHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmladduhm, "__builtin_altivec_vmladduhm", ALTIVEC_BUILTIN_VMLADDUHM}, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmsumubm, "__builtin_altivec_vmsumubm", ALTIVEC_BUILTIN_VMSUMUBM }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmsummbm, "__builtin_altivec_vmsummbm", ALTIVEC_BUILTIN_VMSUMMBM }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmsumuhm, "__builtin_altivec_vmsumuhm", ALTIVEC_BUILTIN_VMSUMUHM }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmsumshm, "__builtin_altivec_vmsumshm", ALTIVEC_BUILTIN_VMSUMSHM }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmsumuhs, "__builtin_altivec_vmsumuhs", ALTIVEC_BUILTIN_VMSUMUHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmsumshs, "__builtin_altivec_vmsumshs", ALTIVEC_BUILTIN_VMSUMSHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vnmsubfp, "__builtin_altivec_vnmsubfp", ALTIVEC_BUILTIN_VNMSUBFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vperm_v4sf, "__builtin_altivec_vperm_4sf", ALTIVEC_BUILTIN_VPERM_4SF }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vperm_v4si, "__builtin_altivec_vperm_4si", ALTIVEC_BUILTIN_VPERM_4SI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vperm_v8hi, "__builtin_altivec_vperm_8hi", ALTIVEC_BUILTIN_VPERM_8HI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vperm_v16qi, "__builtin_altivec_vperm_16qi", ALTIVEC_BUILTIN_VPERM_16QI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsel_v4sf, "__builtin_altivec_vsel_4sf", ALTIVEC_BUILTIN_VSEL_4SF }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsel_v4si, "__builtin_altivec_vsel_4si", ALTIVEC_BUILTIN_VSEL_4SI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsel_v8hi, "__builtin_altivec_vsel_8hi", ALTIVEC_BUILTIN_VSEL_8HI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsel_v16qi, "__builtin_altivec_vsel_16qi", ALTIVEC_BUILTIN_VSEL_16QI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_v16qi, "__builtin_altivec_vsldoi_16qi", ALTIVEC_BUILTIN_VSLDOI_16QI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_v8hi, "__builtin_altivec_vsldoi_8hi", ALTIVEC_BUILTIN_VSLDOI_8HI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_v4si, "__builtin_altivec_vsldoi_4si", ALTIVEC_BUILTIN_VSLDOI_4SI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsldoi_v4sf, "__builtin_altivec_vsldoi_4sf", ALTIVEC_BUILTIN_VSLDOI_4SF }, |
| |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_madd", ALTIVEC_BUILTIN_VEC_MADD }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_madds", ALTIVEC_BUILTIN_VEC_MADDS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_mladd", ALTIVEC_BUILTIN_VEC_MLADD }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_mradds", ALTIVEC_BUILTIN_VEC_MRADDS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_msum", ALTIVEC_BUILTIN_VEC_MSUM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmsumshm", ALTIVEC_BUILTIN_VEC_VMSUMSHM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmsumuhm", ALTIVEC_BUILTIN_VEC_VMSUMUHM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmsummbm", ALTIVEC_BUILTIN_VEC_VMSUMMBM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmsumubm", ALTIVEC_BUILTIN_VEC_VMSUMUBM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_msums", ALTIVEC_BUILTIN_VEC_MSUMS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmsumshs", ALTIVEC_BUILTIN_VEC_VMSUMSHS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmsumuhs", ALTIVEC_BUILTIN_VEC_VMSUMUHS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_nmsub", ALTIVEC_BUILTIN_VEC_NMSUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_perm", ALTIVEC_BUILTIN_VEC_PERM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sel", ALTIVEC_BUILTIN_VEC_SEL }, |
| }; |
| |
| /* DST operations: void foo (void *, const int, const char). */ |
| |
| static const struct builtin_description bdesc_dst[] = |
| { |
| { MASK_ALTIVEC, CODE_FOR_altivec_dst, "__builtin_altivec_dst", ALTIVEC_BUILTIN_DST }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_dstt, "__builtin_altivec_dstt", ALTIVEC_BUILTIN_DSTT }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_dstst, "__builtin_altivec_dstst", ALTIVEC_BUILTIN_DSTST }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_dststt, "__builtin_altivec_dststt", ALTIVEC_BUILTIN_DSTSTT }, |
| |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_dst", ALTIVEC_BUILTIN_VEC_DST }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_dstt", ALTIVEC_BUILTIN_VEC_DSTT }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_dstst", ALTIVEC_BUILTIN_VEC_DSTST }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_dststt", ALTIVEC_BUILTIN_VEC_DSTSTT } |
| }; |
| |
| /* Simple binary operations: VECc = foo (VECa, VECb). */ |
| |
| static struct builtin_description bdesc_2arg[] = |
| { |
| { MASK_ALTIVEC, CODE_FOR_addv16qi3, "__builtin_altivec_vaddubm", ALTIVEC_BUILTIN_VADDUBM }, |
| { MASK_ALTIVEC, CODE_FOR_addv8hi3, "__builtin_altivec_vadduhm", ALTIVEC_BUILTIN_VADDUHM }, |
| { MASK_ALTIVEC, CODE_FOR_addv4si3, "__builtin_altivec_vadduwm", ALTIVEC_BUILTIN_VADDUWM }, |
| { MASK_ALTIVEC, CODE_FOR_addv4sf3, "__builtin_altivec_vaddfp", ALTIVEC_BUILTIN_VADDFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vaddcuw, "__builtin_altivec_vaddcuw", ALTIVEC_BUILTIN_VADDCUW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vaddubs, "__builtin_altivec_vaddubs", ALTIVEC_BUILTIN_VADDUBS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vaddsbs, "__builtin_altivec_vaddsbs", ALTIVEC_BUILTIN_VADDSBS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vadduhs, "__builtin_altivec_vadduhs", ALTIVEC_BUILTIN_VADDUHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vaddshs, "__builtin_altivec_vaddshs", ALTIVEC_BUILTIN_VADDSHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vadduws, "__builtin_altivec_vadduws", ALTIVEC_BUILTIN_VADDUWS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vaddsws, "__builtin_altivec_vaddsws", ALTIVEC_BUILTIN_VADDSWS }, |
| { MASK_ALTIVEC, CODE_FOR_andv4si3, "__builtin_altivec_vand", ALTIVEC_BUILTIN_VAND }, |
| { MASK_ALTIVEC, CODE_FOR_andcv4si3, "__builtin_altivec_vandc", ALTIVEC_BUILTIN_VANDC }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vavgub, "__builtin_altivec_vavgub", ALTIVEC_BUILTIN_VAVGUB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vavgsb, "__builtin_altivec_vavgsb", ALTIVEC_BUILTIN_VAVGSB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vavguh, "__builtin_altivec_vavguh", ALTIVEC_BUILTIN_VAVGUH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vavgsh, "__builtin_altivec_vavgsh", ALTIVEC_BUILTIN_VAVGSH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vavguw, "__builtin_altivec_vavguw", ALTIVEC_BUILTIN_VAVGUW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vavgsw, "__builtin_altivec_vavgsw", ALTIVEC_BUILTIN_VAVGSW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcfux, "__builtin_altivec_vcfux", ALTIVEC_BUILTIN_VCFUX }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcfsx, "__builtin_altivec_vcfsx", ALTIVEC_BUILTIN_VCFSX }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpbfp, "__builtin_altivec_vcmpbfp", ALTIVEC_BUILTIN_VCMPBFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpequb, "__builtin_altivec_vcmpequb", ALTIVEC_BUILTIN_VCMPEQUB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpequh, "__builtin_altivec_vcmpequh", ALTIVEC_BUILTIN_VCMPEQUH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpequw, "__builtin_altivec_vcmpequw", ALTIVEC_BUILTIN_VCMPEQUW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpeqfp, "__builtin_altivec_vcmpeqfp", ALTIVEC_BUILTIN_VCMPEQFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpgefp, "__builtin_altivec_vcmpgefp", ALTIVEC_BUILTIN_VCMPGEFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpgtub, "__builtin_altivec_vcmpgtub", ALTIVEC_BUILTIN_VCMPGTUB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpgtsb, "__builtin_altivec_vcmpgtsb", ALTIVEC_BUILTIN_VCMPGTSB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpgtuh, "__builtin_altivec_vcmpgtuh", ALTIVEC_BUILTIN_VCMPGTUH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpgtsh, "__builtin_altivec_vcmpgtsh", ALTIVEC_BUILTIN_VCMPGTSH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpgtuw, "__builtin_altivec_vcmpgtuw", ALTIVEC_BUILTIN_VCMPGTUW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpgtsw, "__builtin_altivec_vcmpgtsw", ALTIVEC_BUILTIN_VCMPGTSW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vcmpgtfp, "__builtin_altivec_vcmpgtfp", ALTIVEC_BUILTIN_VCMPGTFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vctsxs, "__builtin_altivec_vctsxs", ALTIVEC_BUILTIN_VCTSXS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vctuxs, "__builtin_altivec_vctuxs", ALTIVEC_BUILTIN_VCTUXS }, |
| { MASK_ALTIVEC, CODE_FOR_umaxv16qi3, "__builtin_altivec_vmaxub", ALTIVEC_BUILTIN_VMAXUB }, |
| { MASK_ALTIVEC, CODE_FOR_smaxv16qi3, "__builtin_altivec_vmaxsb", ALTIVEC_BUILTIN_VMAXSB }, |
| { MASK_ALTIVEC, CODE_FOR_umaxv8hi3, "__builtin_altivec_vmaxuh", ALTIVEC_BUILTIN_VMAXUH }, |
| { MASK_ALTIVEC, CODE_FOR_smaxv8hi3, "__builtin_altivec_vmaxsh", ALTIVEC_BUILTIN_VMAXSH }, |
| { MASK_ALTIVEC, CODE_FOR_umaxv4si3, "__builtin_altivec_vmaxuw", ALTIVEC_BUILTIN_VMAXUW }, |
| { MASK_ALTIVEC, CODE_FOR_smaxv4si3, "__builtin_altivec_vmaxsw", ALTIVEC_BUILTIN_VMAXSW }, |
| { MASK_ALTIVEC, CODE_FOR_smaxv4sf3, "__builtin_altivec_vmaxfp", ALTIVEC_BUILTIN_VMAXFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmrghb, "__builtin_altivec_vmrghb", ALTIVEC_BUILTIN_VMRGHB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmrghh, "__builtin_altivec_vmrghh", ALTIVEC_BUILTIN_VMRGHH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmrghw, "__builtin_altivec_vmrghw", ALTIVEC_BUILTIN_VMRGHW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmrglb, "__builtin_altivec_vmrglb", ALTIVEC_BUILTIN_VMRGLB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmrglh, "__builtin_altivec_vmrglh", ALTIVEC_BUILTIN_VMRGLH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmrglw, "__builtin_altivec_vmrglw", ALTIVEC_BUILTIN_VMRGLW }, |
| { MASK_ALTIVEC, CODE_FOR_uminv16qi3, "__builtin_altivec_vminub", ALTIVEC_BUILTIN_VMINUB }, |
| { MASK_ALTIVEC, CODE_FOR_sminv16qi3, "__builtin_altivec_vminsb", ALTIVEC_BUILTIN_VMINSB }, |
| { MASK_ALTIVEC, CODE_FOR_uminv8hi3, "__builtin_altivec_vminuh", ALTIVEC_BUILTIN_VMINUH }, |
| { MASK_ALTIVEC, CODE_FOR_sminv8hi3, "__builtin_altivec_vminsh", ALTIVEC_BUILTIN_VMINSH }, |
| { MASK_ALTIVEC, CODE_FOR_uminv4si3, "__builtin_altivec_vminuw", ALTIVEC_BUILTIN_VMINUW }, |
| { MASK_ALTIVEC, CODE_FOR_sminv4si3, "__builtin_altivec_vminsw", ALTIVEC_BUILTIN_VMINSW }, |
| { MASK_ALTIVEC, CODE_FOR_sminv4sf3, "__builtin_altivec_vminfp", ALTIVEC_BUILTIN_VMINFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmuleub, "__builtin_altivec_vmuleub", ALTIVEC_BUILTIN_VMULEUB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmulesb, "__builtin_altivec_vmulesb", ALTIVEC_BUILTIN_VMULESB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmuleuh, "__builtin_altivec_vmuleuh", ALTIVEC_BUILTIN_VMULEUH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmulesh, "__builtin_altivec_vmulesh", ALTIVEC_BUILTIN_VMULESH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmuloub, "__builtin_altivec_vmuloub", ALTIVEC_BUILTIN_VMULOUB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmulosb, "__builtin_altivec_vmulosb", ALTIVEC_BUILTIN_VMULOSB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmulouh, "__builtin_altivec_vmulouh", ALTIVEC_BUILTIN_VMULOUH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vmulosh, "__builtin_altivec_vmulosh", ALTIVEC_BUILTIN_VMULOSH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_norv4si3, "__builtin_altivec_vnor", ALTIVEC_BUILTIN_VNOR }, |
| { MASK_ALTIVEC, CODE_FOR_iorv4si3, "__builtin_altivec_vor", ALTIVEC_BUILTIN_VOR }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkuhum, "__builtin_altivec_vpkuhum", ALTIVEC_BUILTIN_VPKUHUM }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkuwum, "__builtin_altivec_vpkuwum", ALTIVEC_BUILTIN_VPKUWUM }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkpx, "__builtin_altivec_vpkpx", ALTIVEC_BUILTIN_VPKPX }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkshss, "__builtin_altivec_vpkshss", ALTIVEC_BUILTIN_VPKSHSS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkswss, "__builtin_altivec_vpkswss", ALTIVEC_BUILTIN_VPKSWSS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkuhus, "__builtin_altivec_vpkuhus", ALTIVEC_BUILTIN_VPKUHUS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkshus, "__builtin_altivec_vpkshus", ALTIVEC_BUILTIN_VPKSHUS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkuwus, "__builtin_altivec_vpkuwus", ALTIVEC_BUILTIN_VPKUWUS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vpkswus, "__builtin_altivec_vpkswus", ALTIVEC_BUILTIN_VPKSWUS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vrlb, "__builtin_altivec_vrlb", ALTIVEC_BUILTIN_VRLB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vrlh, "__builtin_altivec_vrlh", ALTIVEC_BUILTIN_VRLH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vrlw, "__builtin_altivec_vrlw", ALTIVEC_BUILTIN_VRLW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vslb, "__builtin_altivec_vslb", ALTIVEC_BUILTIN_VSLB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vslh, "__builtin_altivec_vslh", ALTIVEC_BUILTIN_VSLH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vslw, "__builtin_altivec_vslw", ALTIVEC_BUILTIN_VSLW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsl, "__builtin_altivec_vsl", ALTIVEC_BUILTIN_VSL }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vslo, "__builtin_altivec_vslo", ALTIVEC_BUILTIN_VSLO }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vspltb, "__builtin_altivec_vspltb", ALTIVEC_BUILTIN_VSPLTB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsplth, "__builtin_altivec_vsplth", ALTIVEC_BUILTIN_VSPLTH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vspltw, "__builtin_altivec_vspltw", ALTIVEC_BUILTIN_VSPLTW }, |
| { MASK_ALTIVEC, CODE_FOR_lshrv16qi3, "__builtin_altivec_vsrb", ALTIVEC_BUILTIN_VSRB }, |
| { MASK_ALTIVEC, CODE_FOR_lshrv8hi3, "__builtin_altivec_vsrh", ALTIVEC_BUILTIN_VSRH }, |
| { MASK_ALTIVEC, CODE_FOR_lshrv4si3, "__builtin_altivec_vsrw", ALTIVEC_BUILTIN_VSRW }, |
| { MASK_ALTIVEC, CODE_FOR_ashrv16qi3, "__builtin_altivec_vsrab", ALTIVEC_BUILTIN_VSRAB }, |
| { MASK_ALTIVEC, CODE_FOR_ashrv8hi3, "__builtin_altivec_vsrah", ALTIVEC_BUILTIN_VSRAH }, |
| { MASK_ALTIVEC, CODE_FOR_ashrv4si3, "__builtin_altivec_vsraw", ALTIVEC_BUILTIN_VSRAW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsr, "__builtin_altivec_vsr", ALTIVEC_BUILTIN_VSR }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsro, "__builtin_altivec_vsro", ALTIVEC_BUILTIN_VSRO }, |
| { MASK_ALTIVEC, CODE_FOR_subv16qi3, "__builtin_altivec_vsububm", ALTIVEC_BUILTIN_VSUBUBM }, |
| { MASK_ALTIVEC, CODE_FOR_subv8hi3, "__builtin_altivec_vsubuhm", ALTIVEC_BUILTIN_VSUBUHM }, |
| { MASK_ALTIVEC, CODE_FOR_subv4si3, "__builtin_altivec_vsubuwm", ALTIVEC_BUILTIN_VSUBUWM }, |
| { MASK_ALTIVEC, CODE_FOR_subv4sf3, "__builtin_altivec_vsubfp", ALTIVEC_BUILTIN_VSUBFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsubcuw, "__builtin_altivec_vsubcuw", ALTIVEC_BUILTIN_VSUBCUW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsububs, "__builtin_altivec_vsububs", ALTIVEC_BUILTIN_VSUBUBS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsubsbs, "__builtin_altivec_vsubsbs", ALTIVEC_BUILTIN_VSUBSBS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsubuhs, "__builtin_altivec_vsubuhs", ALTIVEC_BUILTIN_VSUBUHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsubshs, "__builtin_altivec_vsubshs", ALTIVEC_BUILTIN_VSUBSHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsubuws, "__builtin_altivec_vsubuws", ALTIVEC_BUILTIN_VSUBUWS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsubsws, "__builtin_altivec_vsubsws", ALTIVEC_BUILTIN_VSUBSWS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsum4ubs, "__builtin_altivec_vsum4ubs", ALTIVEC_BUILTIN_VSUM4UBS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsum4sbs, "__builtin_altivec_vsum4sbs", ALTIVEC_BUILTIN_VSUM4SBS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsum4shs, "__builtin_altivec_vsum4shs", ALTIVEC_BUILTIN_VSUM4SHS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsum2sws, "__builtin_altivec_vsum2sws", ALTIVEC_BUILTIN_VSUM2SWS }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vsumsws, "__builtin_altivec_vsumsws", ALTIVEC_BUILTIN_VSUMSWS }, |
| { MASK_ALTIVEC, CODE_FOR_xorv4si3, "__builtin_altivec_vxor", ALTIVEC_BUILTIN_VXOR }, |
| |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_add", ALTIVEC_BUILTIN_VEC_ADD }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vaddfp", ALTIVEC_BUILTIN_VEC_VADDFP }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vadduwm", ALTIVEC_BUILTIN_VEC_VADDUWM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vadduhm", ALTIVEC_BUILTIN_VEC_VADDUHM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vaddubm", ALTIVEC_BUILTIN_VEC_VADDUBM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_addc", ALTIVEC_BUILTIN_VEC_ADDC }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_adds", ALTIVEC_BUILTIN_VEC_ADDS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vaddsws", ALTIVEC_BUILTIN_VEC_VADDSWS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vadduws", ALTIVEC_BUILTIN_VEC_VADDUWS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vaddshs", ALTIVEC_BUILTIN_VEC_VADDSHS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vadduhs", ALTIVEC_BUILTIN_VEC_VADDUHS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vaddsbs", ALTIVEC_BUILTIN_VEC_VADDSBS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vaddubs", ALTIVEC_BUILTIN_VEC_VADDUBS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_and", ALTIVEC_BUILTIN_VEC_AND }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_andc", ALTIVEC_BUILTIN_VEC_ANDC }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_avg", ALTIVEC_BUILTIN_VEC_AVG }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vavgsw", ALTIVEC_BUILTIN_VEC_VAVGSW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vavguw", ALTIVEC_BUILTIN_VEC_VAVGUW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vavgsh", ALTIVEC_BUILTIN_VEC_VAVGSH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vavguh", ALTIVEC_BUILTIN_VEC_VAVGUH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vavgsb", ALTIVEC_BUILTIN_VEC_VAVGSB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vavgub", ALTIVEC_BUILTIN_VEC_VAVGUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_cmpb", ALTIVEC_BUILTIN_VEC_CMPB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_cmpeq", ALTIVEC_BUILTIN_VEC_CMPEQ }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpeqfp", ALTIVEC_BUILTIN_VEC_VCMPEQFP }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpequw", ALTIVEC_BUILTIN_VEC_VCMPEQUW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpequh", ALTIVEC_BUILTIN_VEC_VCMPEQUH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpequb", ALTIVEC_BUILTIN_VEC_VCMPEQUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_cmpge", ALTIVEC_BUILTIN_VEC_CMPGE }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_cmpgt", ALTIVEC_BUILTIN_VEC_CMPGT }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpgtfp", ALTIVEC_BUILTIN_VEC_VCMPGTFP }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpgtsw", ALTIVEC_BUILTIN_VEC_VCMPGTSW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpgtuw", ALTIVEC_BUILTIN_VEC_VCMPGTUW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpgtsh", ALTIVEC_BUILTIN_VEC_VCMPGTSH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpgtuh", ALTIVEC_BUILTIN_VEC_VCMPGTUH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpgtsb", ALTIVEC_BUILTIN_VEC_VCMPGTSB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vcmpgtub", ALTIVEC_BUILTIN_VEC_VCMPGTUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_cmple", ALTIVEC_BUILTIN_VEC_CMPLE }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_cmplt", ALTIVEC_BUILTIN_VEC_CMPLT }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_max", ALTIVEC_BUILTIN_VEC_MAX }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmaxfp", ALTIVEC_BUILTIN_VEC_VMAXFP }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmaxsw", ALTIVEC_BUILTIN_VEC_VMAXSW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmaxuw", ALTIVEC_BUILTIN_VEC_VMAXUW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmaxsh", ALTIVEC_BUILTIN_VEC_VMAXSH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmaxuh", ALTIVEC_BUILTIN_VEC_VMAXUH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmaxsb", ALTIVEC_BUILTIN_VEC_VMAXSB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmaxub", ALTIVEC_BUILTIN_VEC_VMAXUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_mergeh", ALTIVEC_BUILTIN_VEC_MERGEH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmrghw", ALTIVEC_BUILTIN_VEC_VMRGHW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmrghh", ALTIVEC_BUILTIN_VEC_VMRGHH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmrghb", ALTIVEC_BUILTIN_VEC_VMRGHB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_mergel", ALTIVEC_BUILTIN_VEC_MERGEL }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmrglw", ALTIVEC_BUILTIN_VEC_VMRGLW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmrglh", ALTIVEC_BUILTIN_VEC_VMRGLH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmrglb", ALTIVEC_BUILTIN_VEC_VMRGLB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_min", ALTIVEC_BUILTIN_VEC_MIN }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vminfp", ALTIVEC_BUILTIN_VEC_VMINFP }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vminsw", ALTIVEC_BUILTIN_VEC_VMINSW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vminuw", ALTIVEC_BUILTIN_VEC_VMINUW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vminsh", ALTIVEC_BUILTIN_VEC_VMINSH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vminuh", ALTIVEC_BUILTIN_VEC_VMINUH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vminsb", ALTIVEC_BUILTIN_VEC_VMINSB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vminub", ALTIVEC_BUILTIN_VEC_VMINUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_mule", ALTIVEC_BUILTIN_VEC_MULE }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmuleub", ALTIVEC_BUILTIN_VEC_VMULEUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmulesb", ALTIVEC_BUILTIN_VEC_VMULESB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmuleuh", ALTIVEC_BUILTIN_VEC_VMULEUH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmulesh", ALTIVEC_BUILTIN_VEC_VMULESH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_mulo", ALTIVEC_BUILTIN_VEC_MULO }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmulosh", ALTIVEC_BUILTIN_VEC_VMULOSH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmulouh", ALTIVEC_BUILTIN_VEC_VMULOUH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmulosb", ALTIVEC_BUILTIN_VEC_VMULOSB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vmuloub", ALTIVEC_BUILTIN_VEC_VMULOUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_nor", ALTIVEC_BUILTIN_VEC_NOR }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_or", ALTIVEC_BUILTIN_VEC_OR }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_pack", ALTIVEC_BUILTIN_VEC_PACK }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vpkuwum", ALTIVEC_BUILTIN_VEC_VPKUWUM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vpkuhum", ALTIVEC_BUILTIN_VEC_VPKUHUM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_packpx", ALTIVEC_BUILTIN_VEC_PACKPX }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_packs", ALTIVEC_BUILTIN_VEC_PACKS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vpkswss", ALTIVEC_BUILTIN_VEC_VPKSWSS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vpkuwus", ALTIVEC_BUILTIN_VEC_VPKUWUS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vpkshss", ALTIVEC_BUILTIN_VEC_VPKSHSS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vpkuhus", ALTIVEC_BUILTIN_VEC_VPKUHUS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_packsu", ALTIVEC_BUILTIN_VEC_PACKSU }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vpkswus", ALTIVEC_BUILTIN_VEC_VPKSWUS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vpkshus", ALTIVEC_BUILTIN_VEC_VPKSHUS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_rl", ALTIVEC_BUILTIN_VEC_RL }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vrlw", ALTIVEC_BUILTIN_VEC_VRLW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vrlh", ALTIVEC_BUILTIN_VEC_VRLH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vrlb", ALTIVEC_BUILTIN_VEC_VRLB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sl", ALTIVEC_BUILTIN_VEC_SL }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vslw", ALTIVEC_BUILTIN_VEC_VSLW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vslh", ALTIVEC_BUILTIN_VEC_VSLH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vslb", ALTIVEC_BUILTIN_VEC_VSLB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sll", ALTIVEC_BUILTIN_VEC_SLL }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_slo", ALTIVEC_BUILTIN_VEC_SLO }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sr", ALTIVEC_BUILTIN_VEC_SR }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsrw", ALTIVEC_BUILTIN_VEC_VSRW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsrh", ALTIVEC_BUILTIN_VEC_VSRH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsrb", ALTIVEC_BUILTIN_VEC_VSRB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sra", ALTIVEC_BUILTIN_VEC_SRA }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsraw", ALTIVEC_BUILTIN_VEC_VSRAW }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsrah", ALTIVEC_BUILTIN_VEC_VSRAH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsrab", ALTIVEC_BUILTIN_VEC_VSRAB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_srl", ALTIVEC_BUILTIN_VEC_SRL }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sro", ALTIVEC_BUILTIN_VEC_SRO }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sub", ALTIVEC_BUILTIN_VEC_SUB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsubfp", ALTIVEC_BUILTIN_VEC_VSUBFP }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsubuwm", ALTIVEC_BUILTIN_VEC_VSUBUWM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsubuhm", ALTIVEC_BUILTIN_VEC_VSUBUHM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsububm", ALTIVEC_BUILTIN_VEC_VSUBUBM }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_subc", ALTIVEC_BUILTIN_VEC_SUBC }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_subs", ALTIVEC_BUILTIN_VEC_SUBS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsubsws", ALTIVEC_BUILTIN_VEC_VSUBSWS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsubuws", ALTIVEC_BUILTIN_VEC_VSUBUWS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsubshs", ALTIVEC_BUILTIN_VEC_VSUBSHS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsubuhs", ALTIVEC_BUILTIN_VEC_VSUBUHS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsubsbs", ALTIVEC_BUILTIN_VEC_VSUBSBS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsububs", ALTIVEC_BUILTIN_VEC_VSUBUBS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sum4s", ALTIVEC_BUILTIN_VEC_SUM4S }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsum4shs", ALTIVEC_BUILTIN_VEC_VSUM4SHS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsum4sbs", ALTIVEC_BUILTIN_VEC_VSUM4SBS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vsum4ubs", ALTIVEC_BUILTIN_VEC_VSUM4UBS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sum2s", ALTIVEC_BUILTIN_VEC_SUM2S }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_sums", ALTIVEC_BUILTIN_VEC_SUMS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_xor", ALTIVEC_BUILTIN_VEC_XOR }, |
| |
| /* Place holder, leave as first spe builtin. */ |
| { 0, CODE_FOR_spe_evaddw, "__builtin_spe_evaddw", SPE_BUILTIN_EVADDW }, |
| { 0, CODE_FOR_spe_evand, "__builtin_spe_evand", SPE_BUILTIN_EVAND }, |
| { 0, CODE_FOR_spe_evandc, "__builtin_spe_evandc", SPE_BUILTIN_EVANDC }, |
| { 0, CODE_FOR_spe_evdivws, "__builtin_spe_evdivws", SPE_BUILTIN_EVDIVWS }, |
| { 0, CODE_FOR_spe_evdivwu, "__builtin_spe_evdivwu", SPE_BUILTIN_EVDIVWU }, |
| { 0, CODE_FOR_spe_eveqv, "__builtin_spe_eveqv", SPE_BUILTIN_EVEQV }, |
| { 0, CODE_FOR_spe_evfsadd, "__builtin_spe_evfsadd", SPE_BUILTIN_EVFSADD }, |
| { 0, CODE_FOR_spe_evfsdiv, "__builtin_spe_evfsdiv", SPE_BUILTIN_EVFSDIV }, |
| { 0, CODE_FOR_spe_evfsmul, "__builtin_spe_evfsmul", SPE_BUILTIN_EVFSMUL }, |
| { 0, CODE_FOR_spe_evfssub, "__builtin_spe_evfssub", SPE_BUILTIN_EVFSSUB }, |
| { 0, CODE_FOR_spe_evmergehi, "__builtin_spe_evmergehi", SPE_BUILTIN_EVMERGEHI }, |
| { 0, CODE_FOR_spe_evmergehilo, "__builtin_spe_evmergehilo", SPE_BUILTIN_EVMERGEHILO }, |
| { 0, CODE_FOR_spe_evmergelo, "__builtin_spe_evmergelo", SPE_BUILTIN_EVMERGELO }, |
| { 0, CODE_FOR_spe_evmergelohi, "__builtin_spe_evmergelohi", SPE_BUILTIN_EVMERGELOHI }, |
| { 0, CODE_FOR_spe_evmhegsmfaa, "__builtin_spe_evmhegsmfaa", SPE_BUILTIN_EVMHEGSMFAA }, |
| { 0, CODE_FOR_spe_evmhegsmfan, "__builtin_spe_evmhegsmfan", SPE_BUILTIN_EVMHEGSMFAN }, |
| { 0, CODE_FOR_spe_evmhegsmiaa, "__builtin_spe_evmhegsmiaa", SPE_BUILTIN_EVMHEGSMIAA }, |
| { 0, CODE_FOR_spe_evmhegsmian, "__builtin_spe_evmhegsmian", SPE_BUILTIN_EVMHEGSMIAN }, |
| { 0, CODE_FOR_spe_evmhegumiaa, "__builtin_spe_evmhegumiaa", SPE_BUILTIN_EVMHEGUMIAA }, |
| { 0, CODE_FOR_spe_evmhegumian, "__builtin_spe_evmhegumian", SPE_BUILTIN_EVMHEGUMIAN }, |
| { 0, CODE_FOR_spe_evmhesmf, "__builtin_spe_evmhesmf", SPE_BUILTIN_EVMHESMF }, |
| { 0, CODE_FOR_spe_evmhesmfa, "__builtin_spe_evmhesmfa", SPE_BUILTIN_EVMHESMFA }, |
| { 0, CODE_FOR_spe_evmhesmfaaw, "__builtin_spe_evmhesmfaaw", SPE_BUILTIN_EVMHESMFAAW }, |
| { 0, CODE_FOR_spe_evmhesmfanw, "__builtin_spe_evmhesmfanw", SPE_BUILTIN_EVMHESMFANW }, |
| { 0, CODE_FOR_spe_evmhesmi, "__builtin_spe_evmhesmi", SPE_BUILTIN_EVMHESMI }, |
| { 0, CODE_FOR_spe_evmhesmia, "__builtin_spe_evmhesmia", SPE_BUILTIN_EVMHESMIA }, |
| { 0, CODE_FOR_spe_evmhesmiaaw, "__builtin_spe_evmhesmiaaw", SPE_BUILTIN_EVMHESMIAAW }, |
| { 0, CODE_FOR_spe_evmhesmianw, "__builtin_spe_evmhesmianw", SPE_BUILTIN_EVMHESMIANW }, |
| { 0, CODE_FOR_spe_evmhessf, "__builtin_spe_evmhessf", SPE_BUILTIN_EVMHESSF }, |
| { 0, CODE_FOR_spe_evmhessfa, "__builtin_spe_evmhessfa", SPE_BUILTIN_EVMHESSFA }, |
| { 0, CODE_FOR_spe_evmhessfaaw, "__builtin_spe_evmhessfaaw", SPE_BUILTIN_EVMHESSFAAW }, |
| { 0, CODE_FOR_spe_evmhessfanw, "__builtin_spe_evmhessfanw", SPE_BUILTIN_EVMHESSFANW }, |
| { 0, CODE_FOR_spe_evmhessiaaw, "__builtin_spe_evmhessiaaw", SPE_BUILTIN_EVMHESSIAAW }, |
| { 0, CODE_FOR_spe_evmhessianw, "__builtin_spe_evmhessianw", SPE_BUILTIN_EVMHESSIANW }, |
| { 0, CODE_FOR_spe_evmheumi, "__builtin_spe_evmheumi", SPE_BUILTIN_EVMHEUMI }, |
| { 0, CODE_FOR_spe_evmheumia, "__builtin_spe_evmheumia", SPE_BUILTIN_EVMHEUMIA }, |
| { 0, CODE_FOR_spe_evmheumiaaw, "__builtin_spe_evmheumiaaw", SPE_BUILTIN_EVMHEUMIAAW }, |
| { 0, CODE_FOR_spe_evmheumianw, "__builtin_spe_evmheumianw", SPE_BUILTIN_EVMHEUMIANW }, |
| { 0, CODE_FOR_spe_evmheusiaaw, "__builtin_spe_evmheusiaaw", SPE_BUILTIN_EVMHEUSIAAW }, |
| { 0, CODE_FOR_spe_evmheusianw, "__builtin_spe_evmheusianw", SPE_BUILTIN_EVMHEUSIANW }, |
| { 0, CODE_FOR_spe_evmhogsmfaa, "__builtin_spe_evmhogsmfaa", SPE_BUILTIN_EVMHOGSMFAA }, |
| { 0, CODE_FOR_spe_evmhogsmfan, "__builtin_spe_evmhogsmfan", SPE_BUILTIN_EVMHOGSMFAN }, |
| { 0, CODE_FOR_spe_evmhogsmiaa, "__builtin_spe_evmhogsmiaa", SPE_BUILTIN_EVMHOGSMIAA }, |
| { 0, CODE_FOR_spe_evmhogsmian, "__builtin_spe_evmhogsmian", SPE_BUILTIN_EVMHOGSMIAN }, |
| { 0, CODE_FOR_spe_evmhogumiaa, "__builtin_spe_evmhogumiaa", SPE_BUILTIN_EVMHOGUMIAA }, |
| { 0, CODE_FOR_spe_evmhogumian, "__builtin_spe_evmhogumian", SPE_BUILTIN_EVMHOGUMIAN }, |
| { 0, CODE_FOR_spe_evmhosmf, "__builtin_spe_evmhosmf", SPE_BUILTIN_EVMHOSMF }, |
| { 0, CODE_FOR_spe_evmhosmfa, "__builtin_spe_evmhosmfa", SPE_BUILTIN_EVMHOSMFA }, |
| { 0, CODE_FOR_spe_evmhosmfaaw, "__builtin_spe_evmhosmfaaw", SPE_BUILTIN_EVMHOSMFAAW }, |
| { 0, CODE_FOR_spe_evmhosmfanw, "__builtin_spe_evmhosmfanw", SPE_BUILTIN_EVMHOSMFANW }, |
| { 0, CODE_FOR_spe_evmhosmi, "__builtin_spe_evmhosmi", SPE_BUILTIN_EVMHOSMI }, |
| { 0, CODE_FOR_spe_evmhosmia, "__builtin_spe_evmhosmia", SPE_BUILTIN_EVMHOSMIA }, |
| { 0, CODE_FOR_spe_evmhosmiaaw, "__builtin_spe_evmhosmiaaw", SPE_BUILTIN_EVMHOSMIAAW }, |
| { 0, CODE_FOR_spe_evmhosmianw, "__builtin_spe_evmhosmianw", SPE_BUILTIN_EVMHOSMIANW }, |
| { 0, CODE_FOR_spe_evmhossf, "__builtin_spe_evmhossf", SPE_BUILTIN_EVMHOSSF }, |
| { 0, CODE_FOR_spe_evmhossfa, "__builtin_spe_evmhossfa", SPE_BUILTIN_EVMHOSSFA }, |
| { 0, CODE_FOR_spe_evmhossfaaw, "__builtin_spe_evmhossfaaw", SPE_BUILTIN_EVMHOSSFAAW }, |
| { 0, CODE_FOR_spe_evmhossfanw, "__builtin_spe_evmhossfanw", SPE_BUILTIN_EVMHOSSFANW }, |
| { 0, CODE_FOR_spe_evmhossiaaw, "__builtin_spe_evmhossiaaw", SPE_BUILTIN_EVMHOSSIAAW }, |
| { 0, CODE_FOR_spe_evmhossianw, "__builtin_spe_evmhossianw", SPE_BUILTIN_EVMHOSSIANW }, |
| { 0, CODE_FOR_spe_evmhoumi, "__builtin_spe_evmhoumi", SPE_BUILTIN_EVMHOUMI }, |
| { 0, CODE_FOR_spe_evmhoumia, "__builtin_spe_evmhoumia", SPE_BUILTIN_EVMHOUMIA }, |
| { 0, CODE_FOR_spe_evmhoumiaaw, "__builtin_spe_evmhoumiaaw", SPE_BUILTIN_EVMHOUMIAAW }, |
| { 0, CODE_FOR_spe_evmhoumianw, "__builtin_spe_evmhoumianw", SPE_BUILTIN_EVMHOUMIANW }, |
| { 0, CODE_FOR_spe_evmhousiaaw, "__builtin_spe_evmhousiaaw", SPE_BUILTIN_EVMHOUSIAAW }, |
| { 0, CODE_FOR_spe_evmhousianw, "__builtin_spe_evmhousianw", SPE_BUILTIN_EVMHOUSIANW }, |
| { 0, CODE_FOR_spe_evmwhsmf, "__builtin_spe_evmwhsmf", SPE_BUILTIN_EVMWHSMF }, |
| { 0, CODE_FOR_spe_evmwhsmfa, "__builtin_spe_evmwhsmfa", SPE_BUILTIN_EVMWHSMFA }, |
| { 0, CODE_FOR_spe_evmwhsmi, "__builtin_spe_evmwhsmi", SPE_BUILTIN_EVMWHSMI }, |
| { 0, CODE_FOR_spe_evmwhsmia, "__builtin_spe_evmwhsmia", SPE_BUILTIN_EVMWHSMIA }, |
| { 0, CODE_FOR_spe_evmwhssf, "__builtin_spe_evmwhssf", SPE_BUILTIN_EVMWHSSF }, |
| { 0, CODE_FOR_spe_evmwhssfa, "__builtin_spe_evmwhssfa", SPE_BUILTIN_EVMWHSSFA }, |
| { 0, CODE_FOR_spe_evmwhumi, "__builtin_spe_evmwhumi", SPE_BUILTIN_EVMWHUMI }, |
| { 0, CODE_FOR_spe_evmwhumia, "__builtin_spe_evmwhumia", SPE_BUILTIN_EVMWHUMIA }, |
| { 0, CODE_FOR_spe_evmwlsmiaaw, "__builtin_spe_evmwlsmiaaw", SPE_BUILTIN_EVMWLSMIAAW }, |
| { 0, CODE_FOR_spe_evmwlsmianw, "__builtin_spe_evmwlsmianw", SPE_BUILTIN_EVMWLSMIANW }, |
| { 0, CODE_FOR_spe_evmwlssiaaw, "__builtin_spe_evmwlssiaaw", SPE_BUILTIN_EVMWLSSIAAW }, |
| { 0, CODE_FOR_spe_evmwlssianw, "__builtin_spe_evmwlssianw", SPE_BUILTIN_EVMWLSSIANW }, |
| { 0, CODE_FOR_spe_evmwlumi, "__builtin_spe_evmwlumi", SPE_BUILTIN_EVMWLUMI }, |
| { 0, CODE_FOR_spe_evmwlumia, "__builtin_spe_evmwlumia", SPE_BUILTIN_EVMWLUMIA }, |
| { 0, CODE_FOR_spe_evmwlumiaaw, "__builtin_spe_evmwlumiaaw", SPE_BUILTIN_EVMWLUMIAAW }, |
| { 0, CODE_FOR_spe_evmwlumianw, "__builtin_spe_evmwlumianw", SPE_BUILTIN_EVMWLUMIANW }, |
| { 0, CODE_FOR_spe_evmwlusiaaw, "__builtin_spe_evmwlusiaaw", SPE_BUILTIN_EVMWLUSIAAW }, |
| { 0, CODE_FOR_spe_evmwlusianw, "__builtin_spe_evmwlusianw", SPE_BUILTIN_EVMWLUSIANW }, |
| { 0, CODE_FOR_spe_evmwsmf, "__builtin_spe_evmwsmf", SPE_BUILTIN_EVMWSMF }, |
| { 0, CODE_FOR_spe_evmwsmfa, "__builtin_spe_evmwsmfa", SPE_BUILTIN_EVMWSMFA }, |
| { 0, CODE_FOR_spe_evmwsmfaa, "__builtin_spe_evmwsmfaa", SPE_BUILTIN_EVMWSMFAA }, |
| { 0, CODE_FOR_spe_evmwsmfan, "__builtin_spe_evmwsmfan", SPE_BUILTIN_EVMWSMFAN }, |
| { 0, CODE_FOR_spe_evmwsmi, "__builtin_spe_evmwsmi", SPE_BUILTIN_EVMWSMI }, |
| { 0, CODE_FOR_spe_evmwsmia, "__builtin_spe_evmwsmia", SPE_BUILTIN_EVMWSMIA }, |
| { 0, CODE_FOR_spe_evmwsmiaa, "__builtin_spe_evmwsmiaa", SPE_BUILTIN_EVMWSMIAA }, |
| { 0, CODE_FOR_spe_evmwsmian, "__builtin_spe_evmwsmian", SPE_BUILTIN_EVMWSMIAN }, |
| { 0, CODE_FOR_spe_evmwssf, "__builtin_spe_evmwssf", SPE_BUILTIN_EVMWSSF }, |
| { 0, CODE_FOR_spe_evmwssfa, "__builtin_spe_evmwssfa", SPE_BUILTIN_EVMWSSFA }, |
| { 0, CODE_FOR_spe_evmwssfaa, "__builtin_spe_evmwssfaa", SPE_BUILTIN_EVMWSSFAA }, |
| { 0, CODE_FOR_spe_evmwssfan, "__builtin_spe_evmwssfan", SPE_BUILTIN_EVMWSSFAN }, |
| { 0, CODE_FOR_spe_evmwumi, "__builtin_spe_evmwumi", SPE_BUILTIN_EVMWUMI }, |
| { 0, CODE_FOR_spe_evmwumia, "__builtin_spe_evmwumia", SPE_BUILTIN_EVMWUMIA }, |
| { 0, CODE_FOR_spe_evmwumiaa, "__builtin_spe_evmwumiaa", SPE_BUILTIN_EVMWUMIAA }, |
| { 0, CODE_FOR_spe_evmwumian, "__builtin_spe_evmwumian", SPE_BUILTIN_EVMWUMIAN }, |
| { 0, CODE_FOR_spe_evnand, "__builtin_spe_evnand", SPE_BUILTIN_EVNAND }, |
| { 0, CODE_FOR_spe_evnor, "__builtin_spe_evnor", SPE_BUILTIN_EVNOR }, |
| { 0, CODE_FOR_spe_evor, "__builtin_spe_evor", SPE_BUILTIN_EVOR }, |
| { 0, CODE_FOR_spe_evorc, "__builtin_spe_evorc", SPE_BUILTIN_EVORC }, |
| { 0, CODE_FOR_spe_evrlw, "__builtin_spe_evrlw", SPE_BUILTIN_EVRLW }, |
| { 0, CODE_FOR_spe_evslw, "__builtin_spe_evslw", SPE_BUILTIN_EVSLW }, |
| { 0, CODE_FOR_spe_evsrws, "__builtin_spe_evsrws", SPE_BUILTIN_EVSRWS }, |
| { 0, CODE_FOR_spe_evsrwu, "__builtin_spe_evsrwu", SPE_BUILTIN_EVSRWU }, |
| { 0, CODE_FOR_spe_evsubfw, "__builtin_spe_evsubfw", SPE_BUILTIN_EVSUBFW }, |
| |
| /* SPE binary operations expecting a 5-bit unsigned literal. */ |
| { 0, CODE_FOR_spe_evaddiw, "__builtin_spe_evaddiw", SPE_BUILTIN_EVADDIW }, |
| |
| { 0, CODE_FOR_spe_evrlwi, "__builtin_spe_evrlwi", SPE_BUILTIN_EVRLWI }, |
| { 0, CODE_FOR_spe_evslwi, "__builtin_spe_evslwi", SPE_BUILTIN_EVSLWI }, |
| { 0, CODE_FOR_spe_evsrwis, "__builtin_spe_evsrwis", SPE_BUILTIN_EVSRWIS }, |
| { 0, CODE_FOR_spe_evsrwiu, "__builtin_spe_evsrwiu", SPE_BUILTIN_EVSRWIU }, |
| { 0, CODE_FOR_spe_evsubifw, "__builtin_spe_evsubifw", SPE_BUILTIN_EVSUBIFW }, |
| { 0, CODE_FOR_spe_evmwhssfaa, "__builtin_spe_evmwhssfaa", SPE_BUILTIN_EVMWHSSFAA }, |
| { 0, CODE_FOR_spe_evmwhssmaa, "__builtin_spe_evmwhssmaa", SPE_BUILTIN_EVMWHSSMAA }, |
| { 0, CODE_FOR_spe_evmwhsmfaa, "__builtin_spe_evmwhsmfaa", SPE_BUILTIN_EVMWHSMFAA }, |
| { 0, CODE_FOR_spe_evmwhsmiaa, "__builtin_spe_evmwhsmiaa", SPE_BUILTIN_EVMWHSMIAA }, |
| { 0, CODE_FOR_spe_evmwhusiaa, "__builtin_spe_evmwhusiaa", SPE_BUILTIN_EVMWHUSIAA }, |
| { 0, CODE_FOR_spe_evmwhumiaa, "__builtin_spe_evmwhumiaa", SPE_BUILTIN_EVMWHUMIAA }, |
| { 0, CODE_FOR_spe_evmwhssfan, "__builtin_spe_evmwhssfan", SPE_BUILTIN_EVMWHSSFAN }, |
| { 0, CODE_FOR_spe_evmwhssian, "__builtin_spe_evmwhssian", SPE_BUILTIN_EVMWHSSIAN }, |
| { 0, CODE_FOR_spe_evmwhsmfan, "__builtin_spe_evmwhsmfan", SPE_BUILTIN_EVMWHSMFAN }, |
| { 0, CODE_FOR_spe_evmwhsmian, "__builtin_spe_evmwhsmian", SPE_BUILTIN_EVMWHSMIAN }, |
| { 0, CODE_FOR_spe_evmwhusian, "__builtin_spe_evmwhusian", SPE_BUILTIN_EVMWHUSIAN }, |
| { 0, CODE_FOR_spe_evmwhumian, "__builtin_spe_evmwhumian", SPE_BUILTIN_EVMWHUMIAN }, |
| { 0, CODE_FOR_spe_evmwhgssfaa, "__builtin_spe_evmwhgssfaa", SPE_BUILTIN_EVMWHGSSFAA }, |
| { 0, CODE_FOR_spe_evmwhgsmfaa, "__builtin_spe_evmwhgsmfaa", SPE_BUILTIN_EVMWHGSMFAA }, |
| { 0, CODE_FOR_spe_evmwhgsmiaa, "__builtin_spe_evmwhgsmiaa", SPE_BUILTIN_EVMWHGSMIAA }, |
| { 0, CODE_FOR_spe_evmwhgumiaa, "__builtin_spe_evmwhgumiaa", SPE_BUILTIN_EVMWHGUMIAA }, |
| { 0, CODE_FOR_spe_evmwhgssfan, "__builtin_spe_evmwhgssfan", SPE_BUILTIN_EVMWHGSSFAN }, |
| { 0, CODE_FOR_spe_evmwhgsmfan, "__builtin_spe_evmwhgsmfan", SPE_BUILTIN_EVMWHGSMFAN }, |
| { 0, CODE_FOR_spe_evmwhgsmian, "__builtin_spe_evmwhgsmian", SPE_BUILTIN_EVMWHGSMIAN }, |
| { 0, CODE_FOR_spe_evmwhgumian, "__builtin_spe_evmwhgumian", SPE_BUILTIN_EVMWHGUMIAN }, |
| { 0, CODE_FOR_spe_brinc, "__builtin_spe_brinc", SPE_BUILTIN_BRINC }, |
| |
| /* Place-holder. Leave as last binary SPE builtin. */ |
| { 0, CODE_FOR_xorv2si3, "__builtin_spe_evxor", SPE_BUILTIN_EVXOR } |
| }; |
| |
| /* AltiVec predicates. */ |
| |
| struct builtin_description_predicates |
| { |
| const unsigned int mask; |
| const enum insn_code icode; |
| const char *opcode; |
| const char *const name; |
| const enum rs6000_builtins code; |
| }; |
| |
| static const struct builtin_description_predicates bdesc_altivec_preds[] = |
| { |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_v4sf, "*vcmpbfp.", "__builtin_altivec_vcmpbfp_p", ALTIVEC_BUILTIN_VCMPBFP_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_v4sf, "*vcmpeqfp.", "__builtin_altivec_vcmpeqfp_p", ALTIVEC_BUILTIN_VCMPEQFP_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_v4sf, "*vcmpgefp.", "__builtin_altivec_vcmpgefp_p", ALTIVEC_BUILTIN_VCMPGEFP_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_v4sf, "*vcmpgtfp.", "__builtin_altivec_vcmpgtfp_p", ALTIVEC_BUILTIN_VCMPGTFP_P }, |
| /* APPLE LOCAL begin radar 4571747 */ |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpequw, "*vcmpequw.", "__builtin_altivec_vcmpequw_p", ALTIVEC_BUILTIN_VCMPEQUW_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpgtsw, "*vcmpgtsw.", "__builtin_altivec_vcmpgtsw_p", ALTIVEC_BUILTIN_VCMPGTSW_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpgtuw, "*vcmpgtuw.", "__builtin_altivec_vcmpgtuw_p", ALTIVEC_BUILTIN_VCMPGTUW_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpgtuh, "*vcmpgtuh.", "__builtin_altivec_vcmpgtuh_p", ALTIVEC_BUILTIN_VCMPGTUH_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpgtsh, "*vcmpgtsh.", "__builtin_altivec_vcmpgtsh_p", ALTIVEC_BUILTIN_VCMPGTSH_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpequh, "*vcmpequh.", "__builtin_altivec_vcmpequh_p", ALTIVEC_BUILTIN_VCMPEQUH_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpequb, "*vcmpequb.", "__builtin_altivec_vcmpequb_p", ALTIVEC_BUILTIN_VCMPEQUB_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpgtsb, "*vcmpgtsb.", "__builtin_altivec_vcmpgtsb_p", ALTIVEC_BUILTIN_VCMPGTSB_P }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_predicate_vcmpgtub, "*vcmpgtub.", "__builtin_altivec_vcmpgtub_p", ALTIVEC_BUILTIN_VCMPGTUB_P }, |
| /* APPLE LOCAL end radar 4571747 */ |
| |
| { MASK_ALTIVEC, 0, NULL, "__builtin_vec_vcmpeq_p", ALTIVEC_BUILTIN_VCMPEQ_P }, |
| { MASK_ALTIVEC, 0, NULL, "__builtin_vec_vcmpgt_p", ALTIVEC_BUILTIN_VCMPGT_P }, |
| { MASK_ALTIVEC, 0, NULL, "__builtin_vec_vcmpge_p", ALTIVEC_BUILTIN_VCMPGE_P } |
| }; |
| |
| /* SPE predicates. */ |
| static struct builtin_description bdesc_spe_predicates[] = |
| { |
| /* Place-holder. Leave as first. */ |
| { 0, CODE_FOR_spe_evcmpeq, "__builtin_spe_evcmpeq", SPE_BUILTIN_EVCMPEQ }, |
| { 0, CODE_FOR_spe_evcmpgts, "__builtin_spe_evcmpgts", SPE_BUILTIN_EVCMPGTS }, |
| { 0, CODE_FOR_spe_evcmpgtu, "__builtin_spe_evcmpgtu", SPE_BUILTIN_EVCMPGTU }, |
| { 0, CODE_FOR_spe_evcmplts, "__builtin_spe_evcmplts", SPE_BUILTIN_EVCMPLTS }, |
| { 0, CODE_FOR_spe_evcmpltu, "__builtin_spe_evcmpltu", SPE_BUILTIN_EVCMPLTU }, |
| { 0, CODE_FOR_spe_evfscmpeq, "__builtin_spe_evfscmpeq", SPE_BUILTIN_EVFSCMPEQ }, |
| { 0, CODE_FOR_spe_evfscmpgt, "__builtin_spe_evfscmpgt", SPE_BUILTIN_EVFSCMPGT }, |
| { 0, CODE_FOR_spe_evfscmplt, "__builtin_spe_evfscmplt", SPE_BUILTIN_EVFSCMPLT }, |
| { 0, CODE_FOR_spe_evfststeq, "__builtin_spe_evfststeq", SPE_BUILTIN_EVFSTSTEQ }, |
| { 0, CODE_FOR_spe_evfststgt, "__builtin_spe_evfststgt", SPE_BUILTIN_EVFSTSTGT }, |
| /* Place-holder. Leave as last. */ |
| { 0, CODE_FOR_spe_evfststlt, "__builtin_spe_evfststlt", SPE_BUILTIN_EVFSTSTLT }, |
| }; |
| |
| /* SPE evsel predicates. */ |
| static struct builtin_description bdesc_spe_evsel[] = |
| { |
| /* Place-holder. Leave as first. */ |
| { 0, CODE_FOR_spe_evcmpgts, "__builtin_spe_evsel_gts", SPE_BUILTIN_EVSEL_CMPGTS }, |
| { 0, CODE_FOR_spe_evcmpgtu, "__builtin_spe_evsel_gtu", SPE_BUILTIN_EVSEL_CMPGTU }, |
| { 0, CODE_FOR_spe_evcmplts, "__builtin_spe_evsel_lts", SPE_BUILTIN_EVSEL_CMPLTS }, |
| { 0, CODE_FOR_spe_evcmpltu, "__builtin_spe_evsel_ltu", SPE_BUILTIN_EVSEL_CMPLTU }, |
| { 0, CODE_FOR_spe_evcmpeq, "__builtin_spe_evsel_eq", SPE_BUILTIN_EVSEL_CMPEQ }, |
| { 0, CODE_FOR_spe_evfscmpgt, "__builtin_spe_evsel_fsgt", SPE_BUILTIN_EVSEL_FSCMPGT }, |
| { 0, CODE_FOR_spe_evfscmplt, "__builtin_spe_evsel_fslt", SPE_BUILTIN_EVSEL_FSCMPLT }, |
| { 0, CODE_FOR_spe_evfscmpeq, "__builtin_spe_evsel_fseq", SPE_BUILTIN_EVSEL_FSCMPEQ }, |
| { 0, CODE_FOR_spe_evfststgt, "__builtin_spe_evsel_fststgt", SPE_BUILTIN_EVSEL_FSTSTGT }, |
| { 0, CODE_FOR_spe_evfststlt, "__builtin_spe_evsel_fststlt", SPE_BUILTIN_EVSEL_FSTSTLT }, |
| /* Place-holder. Leave as last. */ |
| { 0, CODE_FOR_spe_evfststeq, "__builtin_spe_evsel_fststeq", SPE_BUILTIN_EVSEL_FSTSTEQ }, |
| }; |
| |
| /* ABS* operations. */ |
| |
| static const struct builtin_description bdesc_abs[] = |
| { |
| { MASK_ALTIVEC, CODE_FOR_absv4si2, "__builtin_altivec_abs_v4si", ALTIVEC_BUILTIN_ABS_V4SI }, |
| { MASK_ALTIVEC, CODE_FOR_absv8hi2, "__builtin_altivec_abs_v8hi", ALTIVEC_BUILTIN_ABS_V8HI }, |
| { MASK_ALTIVEC, CODE_FOR_absv4sf2, "__builtin_altivec_abs_v4sf", ALTIVEC_BUILTIN_ABS_V4SF }, |
| { MASK_ALTIVEC, CODE_FOR_absv16qi2, "__builtin_altivec_abs_v16qi", ALTIVEC_BUILTIN_ABS_V16QI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_abss_v4si, "__builtin_altivec_abss_v4si", ALTIVEC_BUILTIN_ABSS_V4SI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_abss_v8hi, "__builtin_altivec_abss_v8hi", ALTIVEC_BUILTIN_ABSS_V8HI }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_abss_v16qi, "__builtin_altivec_abss_v16qi", ALTIVEC_BUILTIN_ABSS_V16QI } |
| }; |
| |
| /* Simple unary operations: VECb = foo (unsigned literal) or VECb = |
| foo (VECa). */ |
| |
| static struct builtin_description bdesc_1arg[] = |
| { |
| { MASK_ALTIVEC, CODE_FOR_altivec_vexptefp, "__builtin_altivec_vexptefp", ALTIVEC_BUILTIN_VEXPTEFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vlogefp, "__builtin_altivec_vlogefp", ALTIVEC_BUILTIN_VLOGEFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vrefp, "__builtin_altivec_vrefp", ALTIVEC_BUILTIN_VREFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vrfim, "__builtin_altivec_vrfim", ALTIVEC_BUILTIN_VRFIM }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vrfin, "__builtin_altivec_vrfin", ALTIVEC_BUILTIN_VRFIN }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vrfip, "__builtin_altivec_vrfip", ALTIVEC_BUILTIN_VRFIP }, |
| { MASK_ALTIVEC, CODE_FOR_ftruncv4sf2, "__builtin_altivec_vrfiz", ALTIVEC_BUILTIN_VRFIZ }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vrsqrtefp, "__builtin_altivec_vrsqrtefp", ALTIVEC_BUILTIN_VRSQRTEFP }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vspltisb, "__builtin_altivec_vspltisb", ALTIVEC_BUILTIN_VSPLTISB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vspltish, "__builtin_altivec_vspltish", ALTIVEC_BUILTIN_VSPLTISH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vspltisw, "__builtin_altivec_vspltisw", ALTIVEC_BUILTIN_VSPLTISW }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vupkhsb, "__builtin_altivec_vupkhsb", ALTIVEC_BUILTIN_VUPKHSB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vupkhpx, "__builtin_altivec_vupkhpx", ALTIVEC_BUILTIN_VUPKHPX }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vupkhsh, "__builtin_altivec_vupkhsh", ALTIVEC_BUILTIN_VUPKHSH }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vupklsb, "__builtin_altivec_vupklsb", ALTIVEC_BUILTIN_VUPKLSB }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vupklpx, "__builtin_altivec_vupklpx", ALTIVEC_BUILTIN_VUPKLPX }, |
| { MASK_ALTIVEC, CODE_FOR_altivec_vupklsh, "__builtin_altivec_vupklsh", ALTIVEC_BUILTIN_VUPKLSH }, |
| |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_abs", ALTIVEC_BUILTIN_VEC_ABS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_abss", ALTIVEC_BUILTIN_VEC_ABSS }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_ceil", ALTIVEC_BUILTIN_VEC_CEIL }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_expte", ALTIVEC_BUILTIN_VEC_EXPTE }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_floor", ALTIVEC_BUILTIN_VEC_FLOOR }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_loge", ALTIVEC_BUILTIN_VEC_LOGE }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_mtvscr", ALTIVEC_BUILTIN_VEC_MTVSCR }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_re", ALTIVEC_BUILTIN_VEC_RE }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_round", ALTIVEC_BUILTIN_VEC_ROUND }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_rsqrte", ALTIVEC_BUILTIN_VEC_RSQRTE }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_trunc", ALTIVEC_BUILTIN_VEC_TRUNC }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_unpackh", ALTIVEC_BUILTIN_VEC_UNPACKH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vupkhsh", ALTIVEC_BUILTIN_VEC_VUPKHSH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vupkhpx", ALTIVEC_BUILTIN_VEC_VUPKHPX }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vupkhsb", ALTIVEC_BUILTIN_VEC_VUPKHSB }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_unpackl", ALTIVEC_BUILTIN_VEC_UNPACKL }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vupklpx", ALTIVEC_BUILTIN_VEC_VUPKLPX }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vupklsh", ALTIVEC_BUILTIN_VEC_VUPKLSH }, |
| { MASK_ALTIVEC, CODE_FOR_nothing, "__builtin_vec_vupklsb", ALTIVEC_BUILTIN_VEC_VUPKLSB }, |
| |
| /* The SPE unary builtins must start with SPE_BUILTIN_EVABS and |
| end with SPE_BUILTIN_EVSUBFUSIAAW. */ |
| { 0, CODE_FOR_spe_evabs, "__builtin_spe_evabs", SPE_BUILTIN_EVABS }, |
| { 0, CODE_FOR_spe_evaddsmiaaw, "__builtin_spe_evaddsmiaaw", SPE_BUILTIN_EVADDSMIAAW }, |
| { 0, CODE_FOR_spe_evaddssiaaw, "__builtin_spe_evaddssiaaw", SPE_BUILTIN_EVADDSSIAAW }, |
| { 0, CODE_FOR_spe_evaddumiaaw, "__builtin_spe_evaddumiaaw", SPE_BUILTIN_EVADDUMIAAW }, |
| { 0, CODE_FOR_spe_evaddusiaaw, "__builtin_spe_evaddusiaaw", SPE_BUILTIN_EVADDUSIAAW }, |
| { 0, CODE_FOR_spe_evcntlsw, "__builtin_spe_evcntlsw", SPE_BUILTIN_EVCNTLSW }, |
| { 0, CODE_FOR_spe_evcntlzw, "__builtin_spe_evcntlzw", SPE_BUILTIN_EVCNTLZW }, |
| { 0, CODE_FOR_spe_evextsb, "__builtin_spe_evextsb", SPE_BUILTIN_EVEXTSB }, |
| { 0, CODE_FOR_spe_evextsh, "__builtin_spe_evextsh", SPE_BUILTIN_EVEXTSH }, |
| { 0, CODE_FOR_spe_evfsabs, "__builtin_spe_evfsabs", SPE_BUILTIN_EVFSABS }, |
| { 0, CODE_FOR_spe_evfscfsf, "__builtin_spe_evfscfsf", SPE_BUILTIN_EVFSCFSF }, |
| { 0, CODE_FOR_spe_evfscfsi, "__builtin_spe_evfscfsi", SPE_BUILTIN_EVFSCFSI }, |
| { 0, CODE_FOR_spe_evfscfuf, "__builtin_spe_evfscfuf", SPE_BUILTIN_EVFSCFUF }, |
| { 0, CODE_FOR_spe_evfscfui, "__builtin_spe_evfscfui", SPE_BUILTIN_EVFSCFUI }, |
| { 0, CODE_FOR_spe_evfsctsf, "__builtin_spe_evfsctsf", SPE_BUILTIN_EVFSCTSF }, |
| { 0, CODE_FOR_spe_evfsctsi, "__builtin_spe_evfsctsi", SPE_BUILTIN_EVFSCTSI }, |
| { 0, CODE_FOR_spe_evfsctsiz, "__builtin_spe_evfsctsiz", SPE_BUILTIN_EVFSCTSIZ }, |
| { 0, CODE_FOR_spe_evfsctuf, "__builtin_spe_evfsctuf", SPE_BUILTIN_EVFSCTUF }, |
| { 0, CODE_FOR_spe_evfsctui, "__builtin_spe_evfsctui", SPE_BUILTIN_EVFSCTUI }, |
| { 0, CODE_FOR_spe_evfsctuiz, "__builtin_spe_evfsctuiz", SPE_BUILTIN_EVFSCTUIZ }, |
| { 0, CODE_FOR_spe_evfsnabs, "__builtin_spe_evfsnabs", SPE_BUILTIN_EVFSNABS }, |
| { 0, CODE_FOR_spe_evfsneg, "__builtin_spe_evfsneg", SPE_BUILTIN_EVFSNEG }, |
| { 0, CODE_FOR_spe_evmra, "__builtin_spe_evmra", SPE_BUILTIN_EVMRA }, |
| { 0, CODE_FOR_negv2si2, "__builtin_spe_evneg", SPE_BUILTIN_EVNEG }, |
| { 0, CODE_FOR_spe_evrndw, "__builtin_spe_evrndw", SPE_BUILTIN_EVRNDW }, |
| { 0, CODE_FOR_spe_evsubfsmiaaw, "__builtin_spe_evsubfsmiaaw", SPE_BUILTIN_EVSUBFSMIAAW }, |
| { 0, CODE_FOR_spe_evsubfssiaaw, "__builtin_spe_evsubfssiaaw", SPE_BUILTIN_EVSUBFSSIAAW }, |
| { 0, CODE_FOR_spe_evsubfumiaaw, "__builtin_spe_evsubfumiaaw", SPE_BUILTIN_EVSUBFUMIAAW }, |
| |
| /* Place-holder. Leave as last unary SPE builtin. */ |
| { 0, CODE_FOR_spe_evsubfusiaaw, "__builtin_spe_evsubfusiaaw", SPE_BUILTIN_EVSUBFUSIAAW } |
| }; |
| |
| /* APPLE LOCAL begin AltiVec */ |
| /* Determine the return type from types T1 and T2 of the first two arguments. |
| This is required for some of the AltiVec PIM operations/predicates. */ |
| |
| static tree |
| altivec_cov_rt_12 (tree t1, tree t2) |
| { |
| /* NB: The ordering of the following statements is important. |
| Matching of more specific types (e.g., 'vector pixel') should |
| precede matching of more general types, esp. if they subsume the |
| former (e.g., 'vector of 8 elements'). */ |
| |
| #define RETURN_IF_EITHER_IS(TYPE) if (t1 == TYPE || t2 == TYPE) return TYPE |
| |
| RETURN_IF_EITHER_IS (unsigned_V16QI_type_node); |
| RETURN_IF_EITHER_IS (V16QI_type_node); |
| RETURN_IF_EITHER_IS (bool_V16QI_type_node); |
| RETURN_IF_EITHER_IS (unsigned_V8HI_type_node); |
| RETURN_IF_EITHER_IS (pixel_V8HI_type_node); |
| RETURN_IF_EITHER_IS (V8HI_type_node); |
| RETURN_IF_EITHER_IS (bool_V8HI_type_node); |
| RETURN_IF_EITHER_IS (unsigned_V4SI_type_node); |
| RETURN_IF_EITHER_IS (V4SF_type_node); |
| RETURN_IF_EITHER_IS (V4SI_type_node); |
| RETURN_IF_EITHER_IS (bool_V4SI_type_node); |
| |
| #undef RETURN_IF_EITHER_IS |
| |
| return NULL_TREE; |
| } |
| |
| /* Determine the return type from the pointee type of argument type T. |
| This is required for some of the AltiVec PIM operations/predicates. */ |
| |
| static tree |
| altivec_cov_rt_2p (tree t) |
| { |
| /* Must be a pointer. */ |
| |
| if (!t) |
| return NULL_TREE; |
| |
| if (TREE_CODE (t) != POINTER_TYPE) |
| return NULL_TREE; |
| |
| t = TYPE_MAIN_VARIANT (TREE_TYPE (t)); |
| |
| /* For pointers to vectors, the return type is the vector itself. */ |
| |
| if (TREE_CODE (t) == VECTOR_TYPE) |
| return t; |
| |
| switch (TYPE_MODE (t)) |
| { |
| case QImode: |
| return TYPE_UNSIGNED (t) ? unsigned_V16QI_type_node : V16QI_type_node; |
| |
| case HImode: |
| return TYPE_UNSIGNED (t) ? unsigned_V8HI_type_node : V8HI_type_node; |
| |
| case SImode: |
| return TYPE_UNSIGNED (t) ? unsigned_V4SI_type_node : V4SI_type_node; |
| |
| case SFmode: |
| return V4SF_type_node; |
| |
| default: |
| return NULL_TREE; |
| } |
| } |
| |
| /* Determine the return type from type T by doubling the size of its |
| constituent vector elements. This is required for some of the AltiVec |
| PIM operations/predicates. */ |
| |
| static tree |
| altivec_cov_rt_1d (tree t) |
| { |
| if (t == V16QI_type_node) |
| return V8HI_type_node; |
| else if (t == unsigned_V16QI_type_node) |
| return unsigned_V8HI_type_node; |
| else if (t == bool_V16QI_type_node) |
| return bool_V8HI_type_node; |
| else if (t == V8HI_type_node) |
| return V4SI_type_node; |
| else if (t == unsigned_V8HI_type_node || t == pixel_V8HI_type_node) |
| return unsigned_V4SI_type_node; |
| else if (t == bool_V8HI_type_node) |
| return bool_V4SI_type_node; |
| else |
| return NULL_TREE; /* Invalid argument. */ |
| } |
| |
| /* Determine the return type from type T by halving the size of its |
| constituent vector elements. This is required for some of the AltiVec |
| PIM operations/predicates. */ |
| |
| static tree |
| altivec_cov_rt_1h (tree t) |
| { |
| if (t == V8HI_type_node) |
| return V16QI_type_node; |
| else if (t == unsigned_V8HI_type_node || t == pixel_V8HI_type_node) |
| return unsigned_V16QI_type_node; |
| else if (t == bool_V8HI_type_node) |
| return bool_V16QI_type_node; |
| else if (t == V4SI_type_node) |
| return V8HI_type_node; |
| else if (t == unsigned_V4SI_type_node) |
| return unsigned_V8HI_type_node; |
| else if (t == bool_V4SI_type_node) |
| return bool_V8HI_type_node; |
| else |
| return NULL_TREE; /* Invalid argument. */ |
| } |
| |
| /* Given the types T1 and T2 of the first two arguments, and INFO pointing |
| to the first of available overload candidates (in the ALTIVEC_PIM_TABLE) |
| for an AltiVec PIM operation or predicate, select a desired overload |
| candidate by incrementing and returning INFO as appropriate. If no |
| overload candidate is suitable, return NULL. */ |
| |
| static struct altivec_pim_info * |
| altivec_ovl_resolve (struct altivec_pim_info *info, tree t1, tree t2) |
| { |
| /* Make sure we have all the types that we need. */ |
| if (!t1) |
| return 0; |
| |
| if (!t2 && (info->flags & pim_ovl_MASK) >= pim_ovl_16u_16u) |
| return 0; |
| |
| /* Examine overload candidates in order, and return the first one |
| that matches. For this scheme to work, overload candidates must |
| be ordered from most to least type-specific. */ |
| do |
| { |
| switch (info->flags & pim_ovl_MASK) |
| { |
| |
| #define OVL_MATCH(EXPR) if (EXPR) return info; break |
| |
| case pim_ovl_16: |
| OVL_MATCH (TYPE_MODE (t1) == V16QImode); |
| |
| case pim_ovl_16u: |
| OVL_MATCH (TYPE_MODE (t1) == V16QImode && TYPE_UNSIGNED (t1)); |
| |
| case pim_ovl_8: |
| OVL_MATCH (TYPE_MODE (t1) == V8HImode); |
| |
| case pim_ovl_8u: |
| OVL_MATCH (TYPE_MODE (t1) == V8HImode && TYPE_UNSIGNED (t1)); |
| |
| case pim_ovl_8p: |
| OVL_MATCH (t1 == pixel_V8HI_type_node); |
| |
| case pim_ovl_4: |
| OVL_MATCH (TYPE_MODE (t1) == V4SImode || TYPE_MODE (t1) == V4SFmode); |
| |
| case pim_ovl_4u: |
| OVL_MATCH (TYPE_MODE (t1) == V4SImode && TYPE_UNSIGNED (t1)); |
| |
| case pim_ovl_4f: |
| OVL_MATCH (TYPE_MODE (t1) == V4SFmode); |
| |
| case pim_ovl_16u_16u: |
| OVL_MATCH (t1 == unsigned_V16QI_type_node |
| || t2 == unsigned_V16QI_type_node); |
| |
| case pim_ovl_8u_8u: |
| OVL_MATCH (t1 == unsigned_V8HI_type_node |
| || t1 == pixel_V8HI_type_node |
| || t2 == unsigned_V8HI_type_node |
| || t2 == pixel_V8HI_type_node); |
| |
| case pim_ovl_4u_4u: |
| OVL_MATCH (t1 == unsigned_V4SI_type_node |
| || t2 == unsigned_V4SI_type_node); |
| |
| case pim_ovl_pqi_2: |
| OVL_MATCH (TREE_CODE (t2) == POINTER_TYPE |
| && (TYPE_MODE (TREE_TYPE (t2)) == QImode |
| || TYPE_MODE (TREE_TYPE (t2)) == V16QImode)); |
| |
| case pim_ovl_phi_2: |
| OVL_MATCH (TREE_CODE (t2) == POINTER_TYPE |
| && (TYPE_MODE (TREE_TYPE (t2)) == HImode |
| || TYPE_MODE (TREE_TYPE (t2)) == V8HImode)); |
| |
| case pim_ovl_psi_2: |
| OVL_MATCH (TREE_CODE (t2) == POINTER_TYPE |
| && (TYPE_MODE (TREE_TYPE (t2)) == SImode |
| || TYPE_MODE (TREE_TYPE (t2)) == V4SImode |
| || TYPE_MODE (TREE_TYPE (t2)) == SFmode |
| || TYPE_MODE (TREE_TYPE (t2)) == V4SFmode)); |
| |
| default: /* Catch-all. */ |
| return info; |
| |
| #undef OVL_MATCH |
| } |
| } |
| while (!((++info)->flags & pim_group)); /* Advance to next candidate. */ |
| |
| return NULL; /* No suitable overload candidate found. */ |
| } |
| |
| /* Convert each function argument in the ARGS list into a corresponding |
| type found in the TYPES list. This must be done before calling the |
| __builtin_... AltiVec instructions, whose declared argument types may differ |
| from what was passed to rs6000_fold_builtin(). */ |
| |
| static tree |
| altivec_convert_args (tree types, tree args) |
| { |
| tree t, a; |
| |
| for (t = types, a = args; t && a; t = TREE_CHAIN (t), a = TREE_CHAIN (a)) |
| { |
| TREE_VALUE (a) = convert (TREE_VALUE (t), TREE_VALUE (a)); |
| |
| /* Suppress overflows, so that GIMPLE does not create temporary |
| variables on us. */ |
| if (TREE_CODE (TREE_VALUE (a)) == INTEGER_CST) |
| { |
| TREE_OVERFLOW (TREE_VALUE (a)) = 0; |
| TREE_CONSTANT_OVERFLOW (TREE_VALUE (a)) = 0; |
| } |
| } |
| |
| /* At this point we've converted all of the arguments we're supposed |
| to have. Anything extra is an error and we should mark it as such. */ |
| for (a = args; a; a = TREE_CHAIN (a)) |
| { |
| if (VOID_TYPE_P (TREE_TYPE (TREE_VALUE (a)))) |
| { |
| error ("Too many arguments to altivec builtin."); |
| TREE_VALUE (a) = error_mark_node; |
| } |
| } |
| |
| return args; |
| } |
| |
| /* The following function rewrites FNDECL and ARGLIST by substituting AltiVec |
| PIM operations or predicates with built-in instructions defined above. |
| Type casts are provided if needed. */ |
| |
| tree |
| rs6000_fold_builtin (tree fndecl, tree arglist, bool ARG_UNUSED (ignore)) |
| { |
| tree rettype; |
| tree typ1 = NULL_TREE, typ2 = NULL_TREE; |
| int ovl_error = 0; |
| enum built_in_function fcode; |
| struct altivec_pim_info *info; |
| |
| /* Bail out if not in Apple AltiVec mode. */ |
| if (!rs6000_altivec_pim) |
| return NULL_TREE; |
| |
| fcode = DECL_FUNCTION_CODE (fndecl); |
| |
| /* Bail out unless we are looking at one of the AltiVec PIM |
| operations/predicates. */ |
| |
| if (fcode < ALTIVEC_PIM__FIRST || fcode > ALTIVEC_PIM__LAST) |
| return NULL_TREE; |
| |
| /* Point at the first (and possibly only) entry in ALTIVEC_PIM_TABLE |
| describing this PIM operation/predicate, and how to convert it to |
| a __builtin_... call. */ |
| |
| info = altivec_pim_table + (fcode - ALTIVEC_PIM__FIRST); |
| |
| if (arglist) |
| /* APPLE LOCAL begin radar 5021057 */ |
| { |
| if (TREE_VALUE (arglist) == error_mark_node) |
| return NULL_TREE; |
| typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (arglist))); |
| } |
| /* APPLE LOCAL end radar 5021057 */ |
| |
| if (arglist && TREE_CHAIN (arglist)) |
| { |
| if (TREE_VALUE (TREE_CHAIN (arglist)) == error_mark_node) |
| return NULL_TREE; |
| typ2 = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))); |
| } |
| |
| /* Select from a list of overloaded functions, if needed. */ |
| |
| if (info->flags & pim_ovl_MASK) |
| { |
| info = altivec_ovl_resolve (info, typ1, typ2); |
| |
| if (!info) |
| { |
| /* No suitable overload candidate was found! */ |
| ovl_error = 1; /* We use this to indicate error. */ |
| /* Point at the first overload candidate again. */ |
| info = altivec_pim_table + (fcode - ALTIVEC_PIM__FIRST); |
| } |
| } |
| |
| /* Determine the return type. */ |
| |
| switch (info->flags & pim_rt_MASK) |
| { |
| case pim_rt_12: |
| /* Return type is covariant with the first two arguments. */ |
| rettype = altivec_cov_rt_12 (typ1, typ2); |
| break; |
| |
| /* Return type is covariant with pointee of second argument. */ |
| case pim_rt_2p: |
| rettype = altivec_cov_rt_2p (typ2); |
| break; |
| |
| /* Return type is covariant with the first argument only. */ |
| case pim_rt_1: |
| rettype = typ1; |
| break; |
| |
| /* Return type is covariant with first argument, but with doubled |
| vector element sizes. */ |
| case pim_rt_1d: |
| rettype = altivec_cov_rt_1d (typ1); |
| break; |
| |
| /* Return type is covariant with first argument, but with halved |
| vector element sizes. */ |
| case pim_rt_1h: |
| rettype = altivec_cov_rt_1h (typ1); |
| break; |
| |
| default: |
| /* Retrieve return type to use from ALTIVEC_PIM_TABLE. */ |
| rettype = info->rettype; |
| } |
| |
| /* Rearrange arguments, as needed. */ |
| |
| switch (info->flags & pim_manip_MASK) |
| { |
| case pim_manip_swap: |
| if (!typ1 || !typ2) |
| rettype = NULL_TREE; |
| else |
| { |
| tree swap = TREE_VALUE (arglist); |
| |
| TREE_VALUE (arglist) = TREE_VALUE (TREE_CHAIN (arglist)); |
| TREE_VALUE (TREE_CHAIN (arglist)) = swap; |
| } |
| |
| break; |
| |
| case pim_manip_dup: |
| if (!typ1 || typ2) |
| rettype = NULL_TREE; |
| else |
| TREE_CHAIN (arglist) = tree_cons (NULL_TREE, TREE_VALUE (arglist), |
| NULL_TREE); |
| |
| break; |
| } |
| |
| /* For predicates, prepend the proper CR6 value to the argument list. */ |
| |
| if (fcode >= ALTIVEC_PIM_VEC_ALL_EQ) |
| arglist = tree_cons (NULL_TREE, |
| build_int_cst (NULL_TREE, info->flags & pim_cr6_MASK), |
| arglist); |
| |
| /* If we could not properly determine an overload candidate or a return type, |
| issue an error. */ |
| |
| if (ovl_error || !rettype) |
| { |
| error ("invalid argument(s) for AltiVec operation or predicate"); |
| /* Choose the return type for the first overload candidate, if |
| a type has been provided. Otherwise, use 'vector signed int'. */ |
| rettype = info->rettype ? info->rettype : V4SI_type_node; |
| } |
| |
| /* Retrieve the underlying AltiVec __builtin_... to call, and call it. */ |
| |
| fndecl = rs6000_builtin_decls [info->insn]; |
| arglist = altivec_convert_args (TYPE_ARG_TYPES (TREE_TYPE (fndecl)), |
| arglist); |
| |
| return convert (rettype, build_function_call_expr (fndecl, arglist)); |
| } |
| /* APPLE LOCAL end AltiVec */ |
| |
| static rtx |
| rs6000_expand_unop_builtin (enum insn_code icode, tree arglist, rtx target) |
| { |
| rtx pat; |
| /* APPLE LOCAL begin Altivec */ |
| tree arg0; |
| rtx op0; |
| /* APPLE LOCAL end Altivec */ |
| enum machine_mode tmode = insn_data[icode].operand[0].mode; |
| enum machine_mode mode0 = insn_data[icode].operand[1].mode; |
| |
| /* APPLE LOCAL begin Altivec */ |
| if (!arglist || !TREE_VALUE (arglist)) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| arg0 = TREE_VALUE (arglist); |
| op0 = expand_normal (arg0); |
| /* APPLE LOCAL end Altivec */ |
| if (icode == CODE_FOR_nothing) |
| /* Builtin not supported on this processor. */ |
| return 0; |
| |
| /* If we got invalid arguments bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node) |
| return const0_rtx; |
| |
| if (icode == CODE_FOR_altivec_vspltisb |
| || icode == CODE_FOR_altivec_vspltish |
| /* APPLE LOCAL begin 4119059 */ |
| || icode == CODE_FOR_altivec_vspltisw) |
| { |
| /* Only allow 5-bit *signed* literals. */ |
| if (GET_CODE (op0) != CONST_INT |
| || INTVAL (op0) > 15 |
| || INTVAL (op0) < -16) |
| { |
| error ("argument 1 must be a 5-bit signed literal"); |
| return const0_rtx; |
| } |
| } |
| if (icode == CODE_FOR_spe_evsplatfi |
| /* APPLE LOCAL end 4119059 */ |
| || icode == CODE_FOR_spe_evsplati) |
| { |
| /* Only allow 5-bit *signed* literals. */ |
| if (GET_CODE (op0) != CONST_INT |
| || INTVAL (op0) > 15 |
| || INTVAL (op0) < -16) |
| { |
| error ("argument 1 must be a 5-bit signed literal"); |
| return const0_rtx; |
| } |
| } |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| |
| pat = GEN_FCN (icode) (target, op0); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| |
| return target; |
| } |
| |
| static rtx |
| altivec_expand_abs_builtin (enum insn_code icode, tree arglist, rtx target) |
| { |
| rtx pat, scratch1, scratch2; |
| /* APPLE LOCAL begin Alitvec radar 5447227 */ |
| tree arg0; |
| rtx op0; |
| enum machine_mode tmode; |
| enum machine_mode mode0; |
| if (!arglist |
| || !TREE_VALUE (arglist)) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| arg0 = TREE_VALUE (arglist); |
| op0 = expand_normal (arg0); |
| tmode = insn_data[icode].operand[0].mode; |
| mode0 = insn_data[icode].operand[1].mode; |
| /* APPLE LOCAL end Alitvec radar 5447227 */ |
| |
| /* If we have invalid arguments, bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node) |
| return const0_rtx; |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| |
| scratch1 = gen_reg_rtx (mode0); |
| scratch2 = gen_reg_rtx (mode0); |
| |
| pat = GEN_FCN (icode) (target, op0, scratch1, scratch2); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| |
| return target; |
| } |
| |
| static rtx |
| rs6000_expand_binop_builtin (enum insn_code icode, tree arglist, rtx target) |
| { |
| rtx pat; |
| /* APPLE LOCAL begin Altivec */ |
| tree arg0; |
| tree arg1; |
| rtx op0; |
| rtx op1; |
| /* APPLE LOCAL end Altivec */ |
| enum machine_mode tmode = insn_data[icode].operand[0].mode; |
| enum machine_mode mode0 = insn_data[icode].operand[1].mode; |
| enum machine_mode mode1 = insn_data[icode].operand[2].mode; |
| |
| /* APPLE LOCAL begin Altivec */ |
| if (!arglist |
| || !TREE_VALUE (arglist) |
| || !TREE_CHAIN (arglist) |
| || !TREE_VALUE (TREE_CHAIN (arglist))) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| |
| arg0 = TREE_VALUE (arglist); |
| arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| /* APPLE LOCAL end Altivec */ |
| |
| if (icode == CODE_FOR_nothing) |
| /* Builtin not supported on this processor. */ |
| return 0; |
| |
| /* If we got invalid arguments bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node || arg1 == error_mark_node) |
| return const0_rtx; |
| |
| if (icode == CODE_FOR_altivec_vcfux |
| || icode == CODE_FOR_altivec_vcfsx |
| || icode == CODE_FOR_altivec_vctsxs |
| || icode == CODE_FOR_altivec_vctuxs |
| || icode == CODE_FOR_altivec_vspltb |
| || icode == CODE_FOR_altivec_vsplth |
| || icode == CODE_FOR_altivec_vspltw |
| || icode == CODE_FOR_spe_evaddiw |
| || icode == CODE_FOR_spe_evldd |
| || icode == CODE_FOR_spe_evldh |
| || icode == CODE_FOR_spe_evldw |
| || icode == CODE_FOR_spe_evlhhesplat |
| || icode == CODE_FOR_spe_evlhhossplat |
| || icode == CODE_FOR_spe_evlhhousplat |
| || icode == CODE_FOR_spe_evlwhe |
| || icode == CODE_FOR_spe_evlwhos |
| || icode == CODE_FOR_spe_evlwhou |
| || icode == CODE_FOR_spe_evlwhsplat |
| || icode == CODE_FOR_spe_evlwwsplat |
| || icode == CODE_FOR_spe_evrlwi |
| || icode == CODE_FOR_spe_evslwi |
| || icode == CODE_FOR_spe_evsrwis |
| || icode == CODE_FOR_spe_evsubifw |
| || icode == CODE_FOR_spe_evsrwiu) |
| { |
| /* Only allow 5-bit unsigned literals. */ |
| STRIP_NOPS (arg1); |
| if (TREE_CODE (arg1) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg1) & ~0x1f) |
| { |
| error ("argument 2 must be a 5-bit unsigned literal"); |
| return const0_rtx; |
| } |
| } |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| |
| pat = GEN_FCN (icode) (target, op0, op1); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| |
| return target; |
| } |
| |
| static rtx |
| altivec_expand_predicate_builtin (enum insn_code icode, const char *opcode, |
| tree arglist, rtx target) |
| { |
| rtx pat, scratch; |
| /* APPLE LOCAL begin Alitvec radar 5447227 */ |
| tree cr6_form; |
| tree arg0; |
| tree arg1; |
| rtx op0; |
| rtx op1; |
| enum machine_mode tmode; |
| enum machine_mode mode0; |
| enum machine_mode mode1; |
| int cr6_form_int; |
| if (!arglist |
| || !TREE_VALUE (arglist) |
| || !TREE_CHAIN (arglist) |
| || !TREE_VALUE (TREE_CHAIN (arglist)) |
| || !TREE_CHAIN (TREE_CHAIN (arglist)) |
| || !TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)))) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| cr6_form = TREE_VALUE (arglist); |
| arg0 = TREE_VALUE (TREE_CHAIN (arglist)); |
| arg1 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| tmode = SImode; |
| mode0 = insn_data[icode].operand[1].mode; |
| mode1 = insn_data[icode].operand[2].mode; |
| /* APPLE LOCAL end Alitvec radar 5447227 */ |
| |
| if (TREE_CODE (cr6_form) != INTEGER_CST) |
| { |
| error ("argument 1 of __builtin_altivec_predicate must be a constant"); |
| return const0_rtx; |
| } |
| else |
| cr6_form_int = TREE_INT_CST_LOW (cr6_form); |
| |
| gcc_assert (mode0 == mode1); |
| |
| /* If we have invalid arguments, bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node || arg1 == error_mark_node) |
| return const0_rtx; |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| |
| scratch = gen_reg_rtx (mode0); |
| |
| pat = GEN_FCN (icode) (scratch, op0, op1, |
| gen_rtx_SYMBOL_REF (Pmode, opcode)); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| |
| /* The vec_any* and vec_all* predicates use the same opcodes for two |
| different operations, but the bits in CR6 will be different |
| depending on what information we want. So we have to play tricks |
| with CR6 to get the right bits out. |
| |
| If you think this is disgusting, look at the specs for the |
| AltiVec predicates. */ |
| |
| switch (cr6_form_int) |
| { |
| case 0: |
| emit_insn (gen_cr6_test_for_zero (target)); |
| break; |
| case 1: |
| emit_insn (gen_cr6_test_for_zero_reverse (target)); |
| break; |
| case 2: |
| emit_insn (gen_cr6_test_for_lt (target)); |
| break; |
| case 3: |
| emit_insn (gen_cr6_test_for_lt_reverse (target)); |
| break; |
| default: |
| error ("argument 1 of __builtin_altivec_predicate is out of range"); |
| break; |
| } |
| |
| return target; |
| } |
| |
| static rtx |
| altivec_expand_lv_builtin (enum insn_code icode, tree arglist, rtx target) |
| { |
| rtx pat, addr; |
| /* APPLE LOCAL begin Alitvec radar 5447227 */ |
| tree arg0; |
| tree arg1; |
| enum machine_mode tmode; |
| enum machine_mode mode0; |
| enum machine_mode mode1; |
| rtx op0; |
| rtx op1; |
| if (!arglist |
| || !TREE_VALUE (arglist) |
| || !TREE_CHAIN (arglist) |
| || !TREE_VALUE (TREE_CHAIN (arglist))) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| arg0 = TREE_VALUE (arglist); |
| arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| tmode = insn_data[icode].operand[0].mode; |
| mode0 = Pmode; |
| mode1 = Pmode; |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| /* APPLE LOCAL end Alitvec radar 5447227 */ |
| |
| if (icode == CODE_FOR_nothing) |
| /* Builtin not supported on this processor. */ |
| return 0; |
| |
| /* If we got invalid arguments bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node || arg1 == error_mark_node) |
| return const0_rtx; |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| op1 = copy_to_mode_reg (mode1, op1); |
| |
| if (op0 == const0_rtx) |
| { |
| addr = gen_rtx_MEM (tmode, op1); |
| } |
| else |
| { |
| op0 = copy_to_mode_reg (mode0, op0); |
| addr = gen_rtx_MEM (tmode, gen_rtx_PLUS (Pmode, op0, op1)); |
| } |
| |
| pat = GEN_FCN (icode) (target, addr); |
| |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| |
| return target; |
| } |
| |
| static rtx |
| spe_expand_stv_builtin (enum insn_code icode, tree arglist) |
| { |
| tree arg0 = TREE_VALUE (arglist); |
| tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| tree arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| rtx op2 = expand_normal (arg2); |
| rtx pat; |
| enum machine_mode mode0 = insn_data[icode].operand[0].mode; |
| enum machine_mode mode1 = insn_data[icode].operand[1].mode; |
| enum machine_mode mode2 = insn_data[icode].operand[2].mode; |
| |
| /* Invalid arguments. Bail before doing anything stoopid! */ |
| if (arg0 == error_mark_node |
| || arg1 == error_mark_node |
| || arg2 == error_mark_node) |
| return const0_rtx; |
| |
| if (! (*insn_data[icode].operand[2].predicate) (op0, mode2)) |
| op0 = copy_to_mode_reg (mode2, op0); |
| if (! (*insn_data[icode].operand[0].predicate) (op1, mode0)) |
| op1 = copy_to_mode_reg (mode0, op1); |
| if (! (*insn_data[icode].operand[1].predicate) (op2, mode1)) |
| op2 = copy_to_mode_reg (mode1, op2); |
| |
| pat = GEN_FCN (icode) (op1, op2, op0); |
| if (pat) |
| emit_insn (pat); |
| return NULL_RTX; |
| } |
| |
| static rtx |
| altivec_expand_stv_builtin (enum insn_code icode, tree arglist) |
| { |
| /* APPLE LOCAL begin Alitvec radar 5447227 */ |
| tree arg0; |
| tree arg1; |
| tree arg2; |
| rtx op0; |
| rtx op1; |
| rtx op2; |
| rtx pat, addr; |
| enum machine_mode tmode; |
| enum machine_mode mode1; |
| enum machine_mode mode2; |
| if (!arglist |
| || !TREE_VALUE (arglist) |
| || !TREE_CHAIN (arglist) |
| || !TREE_VALUE (TREE_CHAIN (arglist)) |
| || !TREE_CHAIN (TREE_CHAIN (arglist)) |
| || !TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)))) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| arg0 = TREE_VALUE (arglist); |
| arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| op2 = expand_normal (arg2); |
| tmode = insn_data[icode].operand[0].mode; |
| mode1 = Pmode; |
| mode2 = Pmode; |
| /* APPLE LOCAL end Alitvec radar 5447227 */ |
| |
| /* Invalid arguments. Bail before doing anything stoopid! */ |
| if (arg0 == error_mark_node |
| || arg1 == error_mark_node |
| || arg2 == error_mark_node) |
| return const0_rtx; |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, tmode)) |
| op0 = copy_to_mode_reg (tmode, op0); |
| |
| op2 = copy_to_mode_reg (mode2, op2); |
| |
| if (op1 == const0_rtx) |
| { |
| addr = gen_rtx_MEM (tmode, op2); |
| } |
| else |
| { |
| op1 = copy_to_mode_reg (mode1, op1); |
| addr = gen_rtx_MEM (tmode, gen_rtx_PLUS (Pmode, op1, op2)); |
| } |
| |
| pat = GEN_FCN (icode) (addr, op0); |
| if (pat) |
| emit_insn (pat); |
| return NULL_RTX; |
| } |
| |
| static rtx |
| rs6000_expand_ternop_builtin (enum insn_code icode, tree arglist, rtx target) |
| { |
| rtx pat; |
| /* APPLE LOCAL begin Altivec. */ |
| tree arg0, arg1, arg2; |
| rtx op0, op1, op2; |
| /* APPLE LOCAL end Altivec. */ |
| enum machine_mode tmode = insn_data[icode].operand[0].mode; |
| enum machine_mode mode0 = insn_data[icode].operand[1].mode; |
| enum machine_mode mode1 = insn_data[icode].operand[2].mode; |
| enum machine_mode mode2 = insn_data[icode].operand[3].mode; |
| |
| /* APPLE LOCAL begin Altivec. */ |
| if (!arglist |
| || !TREE_VALUE (arglist) |
| || !TREE_CHAIN (arglist) |
| || !TREE_VALUE (TREE_CHAIN (arglist)) |
| || !TREE_CHAIN (TREE_CHAIN (arglist)) |
| || !TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)))) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| |
| arg0 = TREE_VALUE (arglist); |
| arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| op2 = expand_normal (arg2); |
| /* APPLE LOCAL end Altivec. */ |
| if (icode == CODE_FOR_nothing) |
| /* Builtin not supported on this processor. */ |
| return 0; |
| |
| /* If we got invalid arguments bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node |
| || arg1 == error_mark_node |
| || arg2 == error_mark_node) |
| return const0_rtx; |
| |
| if (icode == CODE_FOR_altivec_vsldoi_v4sf |
| || icode == CODE_FOR_altivec_vsldoi_v4si |
| || icode == CODE_FOR_altivec_vsldoi_v8hi |
| || icode == CODE_FOR_altivec_vsldoi_v16qi) |
| { |
| /* Only allow 4-bit unsigned literals. */ |
| STRIP_NOPS (arg2); |
| if (TREE_CODE (arg2) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg2) & ~0xf) |
| { |
| error ("argument 3 must be a 4-bit unsigned literal"); |
| return const0_rtx; |
| } |
| } |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) |
| op2 = copy_to_mode_reg (mode2, op2); |
| |
| pat = GEN_FCN (icode) (target, op0, op1, op2); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| |
| return target; |
| } |
| |
| /* Expand the lvx builtins. */ |
| static rtx |
| altivec_expand_ld_builtin (tree exp, rtx target, bool *expandedp) |
| { |
| tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); |
| tree arglist = TREE_OPERAND (exp, 1); |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| tree arg0; |
| enum machine_mode tmode, mode0; |
| rtx pat, op0; |
| enum insn_code icode; |
| |
| switch (fcode) |
| { |
| case ALTIVEC_BUILTIN_LD_INTERNAL_16qi: |
| icode = CODE_FOR_altivec_lvx_v16qi; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_8hi: |
| icode = CODE_FOR_altivec_lvx_v8hi; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_4si: |
| icode = CODE_FOR_altivec_lvx_v4si; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_4sf: |
| icode = CODE_FOR_altivec_lvx_v4sf; |
| break; |
| default: |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| *expandedp = true; |
| |
| /* APPLE LOCAL begin Alitvec radar 5447227 */ |
| if (!arglist |
| || !TREE_VALUE (arglist)) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| /* APPLE LOCAL end Alitvec radar 5447227 */ |
| arg0 = TREE_VALUE (arglist); |
| op0 = expand_normal (arg0); |
| tmode = insn_data[icode].operand[0].mode; |
| mode0 = insn_data[icode].operand[1].mode; |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0)); |
| |
| pat = GEN_FCN (icode) (target, op0); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| } |
| |
| /* Expand the stvx builtins. */ |
| static rtx |
| altivec_expand_st_builtin (tree exp, rtx target ATTRIBUTE_UNUSED, |
| bool *expandedp) |
| { |
| tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); |
| tree arglist = TREE_OPERAND (exp, 1); |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| tree arg0, arg1; |
| enum machine_mode mode0, mode1; |
| rtx pat, op0, op1; |
| enum insn_code icode; |
| |
| switch (fcode) |
| { |
| case ALTIVEC_BUILTIN_ST_INTERNAL_16qi: |
| icode = CODE_FOR_altivec_stvx_v16qi; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_8hi: |
| icode = CODE_FOR_altivec_stvx_v8hi; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_4si: |
| icode = CODE_FOR_altivec_stvx_v4si; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_4sf: |
| icode = CODE_FOR_altivec_stvx_v4sf; |
| break; |
| default: |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| /* APPLE LOCAL begin Alitvec radar 5447227 */ |
| if (!arglist |
| || !TREE_VALUE (arglist) |
| || !TREE_CHAIN (arglist) |
| || !TREE_VALUE (TREE_CHAIN (arglist))) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| /* APPLE LOCAL end Alitvec radar 5447227 */ |
| arg0 = TREE_VALUE (arglist); |
| arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| mode0 = insn_data[icode].operand[0].mode; |
| mode1 = insn_data[icode].operand[1].mode; |
| |
| if (! (*insn_data[icode].operand[0].predicate) (op0, mode0)) |
| op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0)); |
| if (! (*insn_data[icode].operand[1].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| |
| pat = GEN_FCN (icode) (op0, op1); |
| if (pat) |
| emit_insn (pat); |
| |
| *expandedp = true; |
| return NULL_RTX; |
| } |
| |
| /* Expand the dst builtins. */ |
| static rtx |
| altivec_expand_dst_builtin (tree exp, rtx target ATTRIBUTE_UNUSED, |
| bool *expandedp) |
| { |
| tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); |
| tree arglist = TREE_OPERAND (exp, 1); |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| tree arg0, arg1, arg2; |
| enum machine_mode mode0, mode1, mode2; |
| rtx pat, op0, op1, op2; |
| struct builtin_description *d; |
| size_t i; |
| |
| *expandedp = false; |
| |
| /* Handle DST variants. */ |
| d = (struct builtin_description *) bdesc_dst; |
| for (i = 0; i < ARRAY_SIZE (bdesc_dst); i++, d++) |
| if (d->code == fcode) |
| { |
| /* APPLE LOCAL begin Alitvec radar 5447227 */ |
| if (!arglist |
| || !TREE_VALUE (arglist) |
| || !TREE_CHAIN (arglist) |
| || !TREE_VALUE (TREE_CHAIN (arglist)) |
| || !TREE_CHAIN (TREE_CHAIN (arglist)) |
| || !TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)))) |
| { |
| error ("too few arguments to function"); |
| return const0_rtx; |
| } |
| /* APPLE LOCAL end Alitvec radar 5447227 */ |
| arg0 = TREE_VALUE (arglist); |
| arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| op0 = expand_normal (arg0); |
| op1 = expand_normal (arg1); |
| op2 = expand_normal (arg2); |
| mode0 = insn_data[d->icode].operand[0].mode; |
| mode1 = insn_data[d->icode].operand[1].mode; |
| mode2 = insn_data[d->icode].operand[2].mode; |
| |
| /* Invalid arguments, bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node |
| || arg1 == error_mark_node |
| || arg2 == error_mark_node) |
| return const0_rtx; |
| |
| *expandedp = true; |
| STRIP_NOPS (arg2); |
| if (TREE_CODE (arg2) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg2) & ~0x3) |
| { |
| error ("argument to %qs must be a 2-bit unsigned literal", d->name); |
| return const0_rtx; |
| } |
| |
| if (! (*insn_data[d->icode].operand[0].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (Pmode, op0); |
| if (! (*insn_data[d->icode].operand[1].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| |
| pat = GEN_FCN (d->icode) (op0, op1, op2); |
| if (pat != 0) |
| emit_insn (pat); |
| |
| return NULL_RTX; |
| } |
| |
| return NULL_RTX; |
| } |
| |
| /* Expand vec_init builtin. */ |
| static rtx |
| altivec_expand_vec_init_builtin (tree type, tree arglist, rtx target) |
| { |
| enum machine_mode tmode = TYPE_MODE (type); |
| enum machine_mode inner_mode = GET_MODE_INNER (tmode); |
| int i, n_elt = GET_MODE_NUNITS (tmode); |
| rtvec v = rtvec_alloc (n_elt); |
| |
| gcc_assert (VECTOR_MODE_P (tmode)); |
| |
| for (i = 0; i < n_elt; ++i, arglist = TREE_CHAIN (arglist)) |
| { |
| rtx x = expand_normal (TREE_VALUE (arglist)); |
| RTVEC_ELT (v, i) = gen_lowpart (inner_mode, x); |
| } |
| |
| gcc_assert (arglist == NULL); |
| |
| if (!target || !register_operand (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| rs6000_expand_vector_init (target, gen_rtx_PARALLEL (tmode, v)); |
| return target; |
| } |
| |
| /* Return the integer constant in ARG. Constrain it to be in the range |
| of the subparts of VEC_TYPE; issue an error if not. */ |
| |
| static int |
| get_element_number (tree vec_type, tree arg) |
| { |
| unsigned HOST_WIDE_INT elt, max = TYPE_VECTOR_SUBPARTS (vec_type) - 1; |
| |
| if (!host_integerp (arg, 1) |
| || (elt = tree_low_cst (arg, 1), elt > max)) |
| { |
| error ("selector must be an integer constant in the range 0..%wi", max); |
| return 0; |
| } |
| |
| return elt; |
| } |
| |
| /* Expand vec_set builtin. */ |
| static rtx |
| altivec_expand_vec_set_builtin (tree arglist) |
| { |
| enum machine_mode tmode, mode1; |
| tree arg0, arg1, arg2; |
| int elt; |
| rtx op0, op1; |
| |
| arg0 = TREE_VALUE (arglist); |
| arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| |
| tmode = TYPE_MODE (TREE_TYPE (arg0)); |
| mode1 = TYPE_MODE (TREE_TYPE (TREE_TYPE (arg0))); |
| gcc_assert (VECTOR_MODE_P (tmode)); |
| |
| op0 = expand_expr (arg0, NULL_RTX, tmode, 0); |
| op1 = expand_expr (arg1, NULL_RTX, mode1, 0); |
| elt = get_element_number (TREE_TYPE (arg0), arg2); |
| |
| if (GET_MODE (op1) != mode1 && GET_MODE (op1) != VOIDmode) |
| op1 = convert_modes (mode1, GET_MODE (op1), op1, true); |
| |
| op0 = force_reg (tmode, op0); |
| op1 = force_reg (mode1, op1); |
| |
| rs6000_expand_vector_set (op0, op1, elt); |
| |
| return op0; |
| } |
| |
| /* Expand vec_ext builtin. */ |
| static rtx |
| altivec_expand_vec_ext_builtin (tree arglist, rtx target) |
| { |
| enum machine_mode tmode, mode0; |
| tree arg0, arg1; |
| int elt; |
| rtx op0; |
| |
| arg0 = TREE_VALUE (arglist); |
| arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| |
| op0 = expand_normal (arg0); |
| elt = get_element_number (TREE_TYPE (arg0), arg1); |
| |
| tmode = TYPE_MODE (TREE_TYPE (TREE_TYPE (arg0))); |
| mode0 = TYPE_MODE (TREE_TYPE (arg0)); |
| gcc_assert (VECTOR_MODE_P (mode0)); |
| |
| op0 = force_reg (mode0, op0); |
| |
| if (optimize || !target || !register_operand (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| rs6000_expand_vector_extract (target, op0, elt); |
| |
| return target; |
| } |
| |
| /* Expand the builtin in EXP and store the result in TARGET. Store |
| true in *EXPANDEDP if we found a builtin to expand. */ |
| static rtx |
| altivec_expand_builtin (tree exp, rtx target, bool *expandedp) |
| { |
| struct builtin_description *d; |
| struct builtin_description_predicates *dp; |
| size_t i; |
| enum insn_code icode; |
| tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); |
| tree arglist = TREE_OPERAND (exp, 1); |
| tree arg0; |
| rtx op0, pat; |
| enum machine_mode tmode, mode0; |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| |
| if (fcode >= ALTIVEC_BUILTIN_OVERLOADED_FIRST |
| && fcode <= ALTIVEC_BUILTIN_OVERLOADED_LAST) |
| { |
| *expandedp = true; |
| error ("unresolved overload for Altivec builtin %qF", fndecl); |
| return const0_rtx; |
| } |
| |
| target = altivec_expand_ld_builtin (exp, target, expandedp); |
| if (*expandedp) |
| return target; |
| |
| target = altivec_expand_st_builtin (exp, target, expandedp); |
| if (*expandedp) |
| return target; |
| |
| target = altivec_expand_dst_builtin (exp, target, expandedp); |
| if (*expandedp) |
| return target; |
| |
| *expandedp = true; |
| |
| switch (fcode) |
| { |
| case ALTIVEC_BUILTIN_STVX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvx, arglist); |
| case ALTIVEC_BUILTIN_STVEBX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvebx, arglist); |
| case ALTIVEC_BUILTIN_STVEHX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvehx, arglist); |
| case ALTIVEC_BUILTIN_STVEWX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvewx, arglist); |
| case ALTIVEC_BUILTIN_STVXL: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvxl, arglist); |
| |
| case ALTIVEC_BUILTIN_MFVSCR: |
| icode = CODE_FOR_altivec_mfvscr; |
| tmode = insn_data[icode].operand[0].mode; |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| pat = GEN_FCN (icode) (target); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| |
| case ALTIVEC_BUILTIN_MTVSCR: |
| icode = CODE_FOR_altivec_mtvscr; |
| arg0 = TREE_VALUE (arglist); |
| op0 = expand_normal (arg0); |
| mode0 = insn_data[icode].operand[0].mode; |
| |
| /* If we got invalid arguments bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node) |
| return const0_rtx; |
| |
| if (! (*insn_data[icode].operand[0].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| |
| pat = GEN_FCN (icode) (op0); |
| if (pat) |
| emit_insn (pat); |
| return NULL_RTX; |
| |
| case ALTIVEC_BUILTIN_DSSALL: |
| emit_insn (gen_altivec_dssall ()); |
| return NULL_RTX; |
| |
| case ALTIVEC_BUILTIN_DSS: |
| icode = CODE_FOR_altivec_dss; |
| arg0 = TREE_VALUE (arglist); |
| STRIP_NOPS (arg0); |
| op0 = expand_normal (arg0); |
| mode0 = insn_data[icode].operand[0].mode; |
| |
| /* If we got invalid arguments bail out before generating bad rtl. */ |
| if (arg0 == error_mark_node) |
| return const0_rtx; |
| |
| if (TREE_CODE (arg0) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg0) & ~0x3) |
| { |
| error ("argument to dss must be a 2-bit unsigned literal"); |
| return const0_rtx; |
| } |
| |
| if (! (*insn_data[icode].operand[0].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| |
| emit_insn (gen_altivec_dss (op0)); |
| return NULL_RTX; |
| |
| case ALTIVEC_BUILTIN_VEC_INIT_V4SI: |
| case ALTIVEC_BUILTIN_VEC_INIT_V8HI: |
| case ALTIVEC_BUILTIN_VEC_INIT_V16QI: |
| case ALTIVEC_BUILTIN_VEC_INIT_V4SF: |
| return altivec_expand_vec_init_builtin (TREE_TYPE (exp), arglist, target); |
| |
| case ALTIVEC_BUILTIN_VEC_SET_V4SI: |
| case ALTIVEC_BUILTIN_VEC_SET_V8HI: |
| case ALTIVEC_BUILTIN_VEC_SET_V16QI: |
| case ALTIVEC_BUILTIN_VEC_SET_V4SF: |
| return altivec_expand_vec_set_builtin (arglist); |
| |
| case ALTIVEC_BUILTIN_VEC_EXT_V4SI: |
| case ALTIVEC_BUILTIN_VEC_EXT_V8HI: |
| case ALTIVEC_BUILTIN_VEC_EXT_V16QI: |
| case ALTIVEC_BUILTIN_VEC_EXT_V4SF: |
| return altivec_expand_vec_ext_builtin (arglist, target); |
| |
| default: |
| break; |
| /* Fall through. */ |
| } |
| |
| /* Expand abs* operations. */ |
| d = (struct builtin_description *) bdesc_abs; |
| for (i = 0; i < ARRAY_SIZE (bdesc_abs); i++, d++) |
| if (d->code == fcode) |
| return altivec_expand_abs_builtin (d->icode, arglist, target); |
| |
| /* Expand the AltiVec predicates. */ |
| dp = (struct builtin_description_predicates *) bdesc_altivec_preds; |
| for (i = 0; i < ARRAY_SIZE (bdesc_altivec_preds); i++, dp++) |
| if (dp->code == fcode) |
| return altivec_expand_predicate_builtin (dp->icode, dp->opcode, |
| arglist, target); |
| |
| /* LV* are funky. We initialized them differently. */ |
| switch (fcode) |
| { |
| case ALTIVEC_BUILTIN_LVSL: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsl, |
| arglist, target); |
| case ALTIVEC_BUILTIN_LVSR: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsr, |
| arglist, target); |
| case ALTIVEC_BUILTIN_LVEBX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvebx, |
| arglist, target); |
| case ALTIVEC_BUILTIN_LVEHX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvehx, |
| arglist, target); |
| case ALTIVEC_BUILTIN_LVEWX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvewx, |
| arglist, target); |
| case ALTIVEC_BUILTIN_LVXL: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl, |
| arglist, target); |
| case ALTIVEC_BUILTIN_LVX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx, |
| arglist, target); |
| default: |
| break; |
| /* Fall through. */ |
| } |
| |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| /* Binops that need to be initialized manually, but can be expanded |
| automagically by rs6000_expand_binop_builtin. */ |
| static struct builtin_description bdesc_2arg_spe[] = |
| { |
| { 0, CODE_FOR_spe_evlddx, "__builtin_spe_evlddx", SPE_BUILTIN_EVLDDX }, |
| { 0, CODE_FOR_spe_evldwx, "__builtin_spe_evldwx", SPE_BUILTIN_EVLDWX }, |
| { 0, CODE_FOR_spe_evldhx, "__builtin_spe_evldhx", SPE_BUILTIN_EVLDHX }, |
| { 0, CODE_FOR_spe_evlwhex, "__builtin_spe_evlwhex", SPE_BUILTIN_EVLWHEX }, |
| { 0, CODE_FOR_spe_evlwhoux, "__builtin_spe_evlwhoux", SPE_BUILTIN_EVLWHOUX }, |
| { 0, CODE_FOR_spe_evlwhosx, "__builtin_spe_evlwhosx", SPE_BUILTIN_EVLWHOSX }, |
| { 0, CODE_FOR_spe_evlwwsplatx, "__builtin_spe_evlwwsplatx", SPE_BUILTIN_EVLWWSPLATX }, |
| { 0, CODE_FOR_spe_evlwhsplatx, "__builtin_spe_evlwhsplatx", SPE_BUILTIN_EVLWHSPLATX }, |
| { 0, CODE_FOR_spe_evlhhesplatx, "__builtin_spe_evlhhesplatx", SPE_BUILTIN_EVLHHESPLATX }, |
| { 0, CODE_FOR_spe_evlhhousplatx, "__builtin_spe_evlhhousplatx", SPE_BUILTIN_EVLHHOUSPLATX }, |
| { 0, CODE_FOR_spe_evlhhossplatx, "__builtin_spe_evlhhossplatx", SPE_BUILTIN_EVLHHOSSPLATX }, |
| { 0, CODE_FOR_spe_evldd, "__builtin_spe_evldd", SPE_BUILTIN_EVLDD }, |
| { 0, CODE_FOR_spe_evldw, "__builtin_spe_evldw", SPE_BUILTIN_EVLDW }, |
| { 0, CODE_FOR_spe_evldh, "__builtin_spe_evldh", SPE_BUILTIN_EVLDH }, |
| { 0, CODE_FOR_spe_evlwhe, "__builtin_spe_evlwhe", SPE_BUILTIN_EVLWHE }, |
| { 0, CODE_FOR_spe_evlwhou, "__builtin_spe_evlwhou", SPE_BUILTIN_EVLWHOU }, |
| { 0, CODE_FOR_spe_evlwhos, "__builtin_spe_evlwhos", SPE_BUILTIN_EVLWHOS }, |
| { 0, CODE_FOR_spe_evlwwsplat, "__builtin_spe_evlwwsplat", SPE_BUILTIN_EVLWWSPLAT }, |
| { 0, CODE_FOR_spe_evlwhsplat, "__builtin_spe_evlwhsplat", SPE_BUILTIN_EVLWHSPLAT }, |
| { 0, CODE_FOR_spe_evlhhesplat, "__builtin_spe_evlhhesplat", SPE_BUILTIN_EVLHHESPLAT }, |
| { 0, CODE_FOR_spe_evlhhousplat, "__builtin_spe_evlhhousplat", SPE_BUILTIN_EVLHHOUSPLAT }, |
| { 0, CODE_FOR_spe_evlhhossplat, "__builtin_spe_evlhhossplat", SPE_BUILTIN_EVLHHOSSPLAT } |
| }; |
| |
| /* Expand the builtin in EXP and store the result in TARGET. Store |
| true in *EXPANDEDP if we found a builtin to expand. |
| |
| This expands the SPE builtins that are not simple unary and binary |
| operations. */ |
| static rtx |
| spe_expand_builtin (tree exp, rtx target, bool *expandedp) |
| { |
| tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); |
| tree arglist = TREE_OPERAND (exp, 1); |
| tree arg1, arg0; |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| enum insn_code icode; |
| enum machine_mode tmode, mode0; |
| rtx pat, op0; |
| struct builtin_description *d; |
| size_t i; |
| |
| *expandedp = true; |
| |
| /* Syntax check for a 5-bit unsigned immediate. */ |
| switch (fcode) |
| { |
| case SPE_BUILTIN_EVSTDD: |
| case SPE_BUILTIN_EVSTDH: |
| case SPE_BUILTIN_EVSTDW: |
| case SPE_BUILTIN_EVSTWHE: |
| case SPE_BUILTIN_EVSTWHO: |
| case SPE_BUILTIN_EVSTWWE: |
| case SPE_BUILTIN_EVSTWWO: |
| arg1 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| if (TREE_CODE (arg1) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg1) & ~0x1f) |
| { |
| error ("argument 2 must be a 5-bit unsigned literal"); |
| return const0_rtx; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| /* The evsplat*i instructions are not quite generic. */ |
| switch (fcode) |
| { |
| case SPE_BUILTIN_EVSPLATFI: |
| return rs6000_expand_unop_builtin (CODE_FOR_spe_evsplatfi, |
| arglist, target); |
| case SPE_BUILTIN_EVSPLATI: |
| return rs6000_expand_unop_builtin (CODE_FOR_spe_evsplati, |
| arglist, target); |
| default: |
| break; |
| } |
| |
| d = (struct builtin_description *) bdesc_2arg_spe; |
| for (i = 0; i < ARRAY_SIZE (bdesc_2arg_spe); ++i, ++d) |
| if (d->code == fcode) |
| return rs6000_expand_binop_builtin (d->icode, arglist, target); |
| |
| d = (struct builtin_description *) bdesc_spe_predicates; |
| for (i = 0; i < ARRAY_SIZE (bdesc_spe_predicates); ++i, ++d) |
| if (d->code == fcode) |
| return spe_expand_predicate_builtin (d->icode, arglist, target); |
| |
| d = (struct builtin_description *) bdesc_spe_evsel; |
| for (i = 0; i < ARRAY_SIZE (bdesc_spe_evsel); ++i, ++d) |
| if (d->code == fcode) |
| return spe_expand_evsel_builtin (d->icode, arglist, target); |
| |
| switch (fcode) |
| { |
| case SPE_BUILTIN_EVSTDDX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstddx, arglist); |
| case SPE_BUILTIN_EVSTDHX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdhx, arglist); |
| case SPE_BUILTIN_EVSTDWX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdwx, arglist); |
| case SPE_BUILTIN_EVSTWHEX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwhex, arglist); |
| case SPE_BUILTIN_EVSTWHOX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwhox, arglist); |
| case SPE_BUILTIN_EVSTWWEX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwwex, arglist); |
| case SPE_BUILTIN_EVSTWWOX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwwox, arglist); |
| case SPE_BUILTIN_EVSTDD: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdd, arglist); |
| case SPE_BUILTIN_EVSTDH: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdh, arglist); |
| case SPE_BUILTIN_EVSTDW: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdw, arglist); |
| case SPE_BUILTIN_EVSTWHE: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwhe, arglist); |
| case SPE_BUILTIN_EVSTWHO: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwho, arglist); |
| case SPE_BUILTIN_EVSTWWE: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwwe, arglist); |
| case SPE_BUILTIN_EVSTWWO: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwwo, arglist); |
| case SPE_BUILTIN_MFSPEFSCR: |
| icode = CODE_FOR_spe_mfspefscr; |
| tmode = insn_data[icode].operand[0].mode; |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| pat = GEN_FCN (icode) (target); |
| if (! pat) |
| return 0; |
| emit_insn (pat); |
| return target; |
| case SPE_BUILTIN_MTSPEFSCR: |
| icode = CODE_FOR_spe_mtspefscr; |
| arg0 = TREE_VALUE (arglist); |
| op0 = expand_normal (arg0); |
| mode0 = insn_data[icode].operand[0].mode; |
| |
| if (arg0 == error_mark_node) |
| return const0_rtx; |
| |
| if (! (*insn_data[icode].operand[0].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| |
| pat = GEN_FCN (icode) (op0); |
| if (pat) |
| emit_insn (pat); |
| return NULL_RTX; |
| default: |
| break; |
| } |
| |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| static rtx |
| spe_expand_predicate_builtin (enum insn_code icode, tree arglist, rtx target) |
| { |
| rtx pat, scratch, tmp; |
| tree form = TREE_VALUE (arglist); |
| tree arg0 = TREE_VALUE (TREE_CHAIN (arglist)); |
| tree arg1 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| enum machine_mode mode0 = insn_data[icode].operand[1].mode; |
| enum machine_mode mode1 = insn_data[icode].operand[2].mode; |
| int form_int; |
| enum rtx_code code; |
| |
| if (TREE_CODE (form) != INTEGER_CST) |
| { |
| error ("argument 1 of __builtin_spe_predicate must be a constant"); |
| return const0_rtx; |
| } |
| else |
| form_int = TREE_INT_CST_LOW (form); |
| |
| gcc_assert (mode0 == mode1); |
| |
| if (arg0 == error_mark_node || arg1 == error_mark_node) |
| return const0_rtx; |
| |
| if (target == 0 |
| || GET_MODE (target) != SImode |
| || ! (*insn_data[icode].operand[0].predicate) (target, SImode)) |
| target = gen_reg_rtx (SImode); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode1, op1); |
| |
| scratch = gen_reg_rtx (CCmode); |
| |
| pat = GEN_FCN (icode) (scratch, op0, op1); |
| if (! pat) |
| return const0_rtx; |
| emit_insn (pat); |
| |
| /* There are 4 variants for each predicate: _any_, _all_, _upper_, |
| _lower_. We use one compare, but look in different bits of the |
| CR for each variant. |
| |
| There are 2 elements in each SPE simd type (upper/lower). The CR |
| bits are set as follows: |
| |
| BIT0 | BIT 1 | BIT 2 | BIT 3 |
| U | L | (U | L) | (U & L) |
| |
| So, for an "all" relationship, BIT 3 would be set. |
| For an "any" relationship, BIT 2 would be set. Etc. |
| |
| Following traditional nomenclature, these bits map to: |
| |
| BIT0 | BIT 1 | BIT 2 | BIT 3 |
| LT | GT | EQ | OV |
| |
| Later, we will generate rtl to look in the LT/EQ/EQ/OV bits. |
| */ |
| |
| switch (form_int) |
| { |
| /* All variant. OV bit. */ |
| case 0: |
| /* We need to get to the OV bit, which is the ORDERED bit. We |
| could generate (ordered:SI (reg:CC xx) (const_int 0)), but |
| that's ugly and will make validate_condition_mode die. |
| So let's just use another pattern. */ |
| emit_insn (gen_move_from_CR_ov_bit (target, scratch)); |
| return target; |
| /* Any variant. EQ bit. */ |
| case 1: |
| code = EQ; |
| break; |
| /* Upper variant. LT bit. */ |
| case 2: |
| code = LT; |
| break; |
| /* Lower variant. GT bit. */ |
| case 3: |
| code = GT; |
| break; |
| default: |
| error ("argument 1 of __builtin_spe_predicate is out of range"); |
| return const0_rtx; |
| } |
| |
| tmp = gen_rtx_fmt_ee (code, SImode, scratch, const0_rtx); |
| emit_move_insn (target, tmp); |
| |
| return target; |
| } |
| |
| /* The evsel builtins look like this: |
| |
| e = __builtin_spe_evsel_OP (a, b, c, d); |
| |
| and work like this: |
| |
| e[upper] = a[upper] *OP* b[upper] ? c[upper] : d[upper]; |
| e[lower] = a[lower] *OP* b[lower] ? c[lower] : d[lower]; |
| */ |
| |
| static rtx |
| spe_expand_evsel_builtin (enum insn_code icode, tree arglist, rtx target) |
| { |
| rtx pat, scratch; |
| tree arg0 = TREE_VALUE (arglist); |
| tree arg1 = TREE_VALUE (TREE_CHAIN (arglist)); |
| tree arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))); |
| tree arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist)))); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| rtx op2 = expand_normal (arg2); |
| rtx op3 = expand_normal (arg3); |
| enum machine_mode mode0 = insn_data[icode].operand[1].mode; |
| enum machine_mode mode1 = insn_data[icode].operand[2].mode; |
| |
| gcc_assert (mode0 == mode1); |
| |
| if (arg0 == error_mark_node || arg1 == error_mark_node |
| || arg2 == error_mark_node || arg3 == error_mark_node) |
| return const0_rtx; |
| |
| if (target == 0 |
| || GET_MODE (target) != mode0 |
| || ! (*insn_data[icode].operand[0].predicate) (target, mode0)) |
| target = gen_reg_rtx (mode0); |
| |
| if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, op0); |
| if (! (*insn_data[icode].operand[1].predicate) (op1, mode1)) |
| op1 = copy_to_mode_reg (mode0, op1); |
| if (! (*insn_data[icode].operand[1].predicate) (op2, mode1)) |
| op2 = copy_to_mode_reg (mode0, op2); |
| if (! (*insn_data[icode].operand[1].predicate) (op3, mode1)) |
| op3 = copy_to_mode_reg (mode0, op3); |
| |
| /* Generate the compare. */ |
| scratch = gen_reg_rtx (CCmode); |
| pat = GEN_FCN (icode) (scratch, op0, op1); |
| if (! pat) |
| return const0_rtx; |
| emit_insn (pat); |
| |
| if (mode0 == V2SImode) |
| emit_insn (gen_spe_evsel (target, op2, op3, scratch)); |
| else |
| emit_insn (gen_spe_evsel_fs (target, op2, op3, scratch)); |
| |
| return target; |
| } |
| |
| /* Expand an expression EXP that calls a built-in function, |
| with result going to TARGET if that's convenient |
| (and in mode MODE if that's convenient). |
| SUBTARGET may be used as the target for computing one of EXP's operands. |
| IGNORE is nonzero if the value is to be ignored. */ |
| |
| static rtx |
| rs6000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, |
| enum machine_mode mode ATTRIBUTE_UNUSED, |
| int ignore ATTRIBUTE_UNUSED) |
| { |
| tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); |
| tree arglist = TREE_OPERAND (exp, 1); |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| struct builtin_description *d; |
| size_t i; |
| rtx ret; |
| bool success; |
| |
| if (fcode == ALTIVEC_BUILTIN_MASK_FOR_LOAD |
| || fcode == ALTIVEC_BUILTIN_MASK_FOR_STORE) |
| { |
| int icode = (int) CODE_FOR_altivec_lvsr; |
| enum machine_mode tmode = insn_data[icode].operand[0].mode; |
| enum machine_mode mode = insn_data[icode].operand[1].mode; |
| tree arg; |
| rtx op, addr, pat; |
| |
| gcc_assert (TARGET_ALTIVEC); |
| |
| arg = TREE_VALUE (arglist); |
| gcc_assert (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE); |
| op = expand_expr (arg, NULL_RTX, Pmode, EXPAND_NORMAL); |
| addr = memory_address (mode, op); |
| if (fcode == ALTIVEC_BUILTIN_MASK_FOR_STORE) |
| op = addr; |
| else |
| { |
| /* For the load case need to negate the address. */ |
| op = gen_reg_rtx (GET_MODE (addr)); |
| emit_insn (gen_rtx_SET (VOIDmode, op, |
| gen_rtx_NEG (GET_MODE (addr), addr))); |
| } |
| op = gen_rtx_MEM (mode, op); |
| |
| if (target == 0 |
| || GET_MODE (target) != tmode |
| || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| /*pat = gen_altivec_lvsr (target, op);*/ |
| pat = GEN_FCN (icode) (target, op); |
| if (!pat) |
| return 0; |
| emit_insn (pat); |
| |
| return target; |
| } |
| |
| if (TARGET_ALTIVEC) |
| { |
| ret = altivec_expand_builtin (exp, target, &success); |
| |
| if (success) |
| return ret; |
| } |
| if (TARGET_SPE) |
| { |
| ret = spe_expand_builtin (exp, target, &success); |
| |
| if (success) |
| return ret; |
| } |
| |
| gcc_assert (TARGET_ALTIVEC || TARGET_SPE); |
| |
| /* Handle simple unary operations. */ |
| d = (struct builtin_description *) bdesc_1arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_1arg); i++, d++) |
| if (d->code == fcode) |
| return rs6000_expand_unop_builtin (d->icode, arglist, target); |
| |
| /* Handle simple binary operations. */ |
| d = (struct builtin_description *) bdesc_2arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_2arg); i++, d++) |
| if (d->code == fcode) |
| return rs6000_expand_binop_builtin (d->icode, arglist, target); |
| |
| /* Handle simple ternary operations. */ |
| d = (struct builtin_description *) bdesc_3arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_3arg); i++, d++) |
| if (d->code == fcode) |
| return rs6000_expand_ternop_builtin (d->icode, arglist, target); |
| |
| /* APPLE LOCAL begin 5774356 */ |
| /* It looks like a builtin call, but there is something wrong; |
| maybe the wrong number of arguments. Return failure. */ |
| return NULL_RTX; |
| /* APPLE LOCAL end 5774356 */ |
| } |
| |
| static tree |
| build_opaque_vector_type (tree node, int nunits) |
| { |
| node = copy_node (node); |
| TYPE_MAIN_VARIANT (node) = node; |
| return build_vector_type (node, nunits); |
| } |
| |
| static void |
| rs6000_init_builtins (void) |
| { |
| V2SI_type_node = build_vector_type (intSI_type_node, 2); |
| V2SF_type_node = build_vector_type (float_type_node, 2); |
| V4HI_type_node = build_vector_type (intHI_type_node, 4); |
| V4SI_type_node = build_vector_type (intSI_type_node, 4); |
| V4SF_type_node = build_vector_type (float_type_node, 4); |
| V8HI_type_node = build_vector_type (intHI_type_node, 8); |
| V16QI_type_node = build_vector_type (intQI_type_node, 16); |
| |
| unsigned_V16QI_type_node = build_vector_type (unsigned_intQI_type_node, 16); |
| unsigned_V8HI_type_node = build_vector_type (unsigned_intHI_type_node, 8); |
| unsigned_V4SI_type_node = build_vector_type (unsigned_intSI_type_node, 4); |
| |
| opaque_V2SF_type_node = build_opaque_vector_type (float_type_node, 2); |
| opaque_V2SI_type_node = build_opaque_vector_type (intSI_type_node, 2); |
| opaque_p_V2SI_type_node = build_pointer_type (opaque_V2SI_type_node); |
| opaque_V4SI_type_node = copy_node (V4SI_type_node); |
| |
| /* The 'vector bool ...' types must be kept distinct from 'vector unsigned ...' |
| types, especially in C++ land. Similarly, 'vector pixel' is distinct from |
| 'vector unsigned short'. */ |
| |
| bool_char_type_node = build_distinct_type_copy (unsigned_intQI_type_node); |
| bool_short_type_node = build_distinct_type_copy (unsigned_intHI_type_node); |
| bool_int_type_node = build_distinct_type_copy (unsigned_intSI_type_node); |
| pixel_type_node = build_distinct_type_copy (unsigned_intHI_type_node); |
| |
| long_integer_type_internal_node = long_integer_type_node; |
| long_unsigned_type_internal_node = long_unsigned_type_node; |
| intQI_type_internal_node = intQI_type_node; |
| uintQI_type_internal_node = unsigned_intQI_type_node; |
| intHI_type_internal_node = intHI_type_node; |
| uintHI_type_internal_node = unsigned_intHI_type_node; |
| intSI_type_internal_node = intSI_type_node; |
| uintSI_type_internal_node = unsigned_intSI_type_node; |
| float_type_internal_node = float_type_node; |
| void_type_internal_node = void_type_node; |
| |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__bool char"), |
| bool_char_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__bool short"), |
| bool_short_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__bool int"), |
| bool_int_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__pixel"), |
| pixel_type_node)); |
| |
| bool_V16QI_type_node = build_vector_type (bool_char_type_node, 16); |
| bool_V8HI_type_node = build_vector_type (bool_short_type_node, 8); |
| bool_V4SI_type_node = build_vector_type (bool_int_type_node, 4); |
| pixel_V8HI_type_node = build_vector_type (pixel_type_node, 8); |
| |
| /* APPLE LOCAL begin LLVM */ |
| #ifdef ENABLE_LLVM |
| /* LLVM doesn't initialize the RTL backend, so build_vector_type will assign |
| all of these types BLKmode. This interferes with rs6000.c-specific |
| handling of -faltivec overloads (an apple local patch). As such, give them |
| the correct modes here manually. */ |
| TYPE_MODE (V4SI_type_node) = V4SImode; |
| TYPE_MODE (V4SF_type_node) = V4SFmode; |
| TYPE_MODE (V8HI_type_node) = V8HImode; |
| TYPE_MODE (V16QI_type_node) = V16QImode; |
| TYPE_MODE (unsigned_V4SI_type_node) = V4SImode; |
| TYPE_MODE (unsigned_V8HI_type_node) = V8HImode; |
| TYPE_MODE (unsigned_V16QI_type_node) = V16QImode; |
| TYPE_MODE (bool_V16QI_type_node) = V16QImode; |
| TYPE_MODE (bool_V8HI_type_node) = V8HImode; |
| TYPE_MODE (bool_V4SI_type_node) = V4SImode; |
| TYPE_MODE (pixel_V8HI_type_node) = V8HImode; |
| #endif |
| /* APPLE LOCAL end LLVM */ |
| |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector unsigned char"), |
| unsigned_V16QI_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector signed char"), |
| V16QI_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector __bool char"), |
| bool_V16QI_type_node)); |
| |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector unsigned short"), |
| unsigned_V8HI_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector signed short"), |
| V8HI_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector __bool short"), |
| bool_V8HI_type_node)); |
| |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector unsigned int"), |
| unsigned_V4SI_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector signed int"), |
| V4SI_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector __bool int"), |
| bool_V4SI_type_node)); |
| |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector float"), |
| V4SF_type_node)); |
| (*lang_hooks.decls.pushdecl) (build_decl (TYPE_DECL, |
| get_identifier ("__vector __pixel"), |
| pixel_V8HI_type_node)); |
| |
| if (TARGET_SPE) |
| spe_init_builtins (); |
| if (TARGET_ALTIVEC) |
| altivec_init_builtins (); |
| if (TARGET_ALTIVEC || TARGET_SPE) |
| rs6000_common_init_builtins (); |
| |
| /* APPLE LOCAL begin constant cfstrings */ |
| #ifdef SUBTARGET_INIT_BUILTINS |
| SUBTARGET_INIT_BUILTINS; |
| #endif |
| /* APPLE LOCAL end constant cfstrings */ |
| |
| #if TARGET_XCOFF |
| /* AIX libm provides clog as __clog. */ |
| if (built_in_decls [BUILT_IN_CLOG]) |
| set_user_assembler_name (built_in_decls [BUILT_IN_CLOG], "__clog"); |
| #endif |
| } |
| |
| /* Search through a set of builtins and enable the mask bits. |
| DESC is an array of builtins. |
| SIZE is the total number of builtins. |
| START is the builtin enum at which to start. |
| END is the builtin enum at which to end. */ |
| static void |
| enable_mask_for_builtins (struct builtin_description *desc, int size, |
| enum rs6000_builtins start, |
| enum rs6000_builtins end) |
| { |
| int i; |
| |
| for (i = 0; i < size; ++i) |
| if (desc[i].code == start) |
| break; |
| |
| if (i == size) |
| return; |
| |
| for (; i < size; ++i) |
| { |
| /* Flip all the bits on. */ |
| desc[i].mask = target_flags; |
| if (desc[i].code == end) |
| break; |
| } |
| } |
| |
| static void |
| spe_init_builtins (void) |
| { |
| tree endlink = void_list_node; |
| tree puint_type_node = build_pointer_type (unsigned_type_node); |
| tree pushort_type_node = build_pointer_type (short_unsigned_type_node); |
| struct builtin_description *d; |
| size_t i; |
| |
| tree v2si_ftype_4_v2si |
| = build_function_type |
| (opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| endlink))))); |
| |
| tree v2sf_ftype_4_v2sf |
| = build_function_type |
| (opaque_V2SF_type_node, |
| tree_cons (NULL_TREE, opaque_V2SF_type_node, |
| tree_cons (NULL_TREE, opaque_V2SF_type_node, |
| tree_cons (NULL_TREE, opaque_V2SF_type_node, |
| tree_cons (NULL_TREE, opaque_V2SF_type_node, |
| endlink))))); |
| |
| tree int_ftype_int_v2si_v2si |
| = build_function_type |
| (integer_type_node, |
| tree_cons (NULL_TREE, integer_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| endlink)))); |
| |
| tree int_ftype_int_v2sf_v2sf |
| = build_function_type |
| (integer_type_node, |
| tree_cons (NULL_TREE, integer_type_node, |
| tree_cons (NULL_TREE, opaque_V2SF_type_node, |
| tree_cons (NULL_TREE, opaque_V2SF_type_node, |
| endlink)))); |
| |
| tree void_ftype_v2si_puint_int |
| = build_function_type (void_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, puint_type_node, |
| tree_cons (NULL_TREE, |
| integer_type_node, |
| endlink)))); |
| |
| tree void_ftype_v2si_puint_char |
| = build_function_type (void_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, puint_type_node, |
| tree_cons (NULL_TREE, |
| char_type_node, |
| endlink)))); |
| |
| tree void_ftype_v2si_pv2si_int |
| = build_function_type (void_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, opaque_p_V2SI_type_node, |
| tree_cons (NULL_TREE, |
| integer_type_node, |
| endlink)))); |
| |
| tree void_ftype_v2si_pv2si_char |
| = build_function_type (void_type_node, |
| tree_cons (NULL_TREE, opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, opaque_p_V2SI_type_node, |
| tree_cons (NULL_TREE, |
| char_type_node, |
| endlink)))); |
| |
| tree void_ftype_int |
| = build_function_type (void_type_node, |
| tree_cons (NULL_TREE, integer_type_node, endlink)); |
| |
| tree int_ftype_void |
| = build_function_type (integer_type_node, endlink); |
| |
| tree v2si_ftype_pv2si_int |
| = build_function_type (opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, opaque_p_V2SI_type_node, |
| tree_cons (NULL_TREE, integer_type_node, |
| endlink))); |
| |
| tree v2si_ftype_puint_int |
| = build_function_type (opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, puint_type_node, |
| tree_cons (NULL_TREE, integer_type_node, |
| endlink))); |
| |
| tree v2si_ftype_pushort_int |
| = build_function_type (opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, pushort_type_node, |
| tree_cons (NULL_TREE, integer_type_node, |
| endlink))); |
| |
| tree v2si_ftype_signed_char |
| = build_function_type (opaque_V2SI_type_node, |
| tree_cons (NULL_TREE, signed_char_type_node, |
| endlink)); |
| |
| /* The initialization of the simple binary and unary builtins is |
| done in rs6000_common_init_builtins, but we have to enable the |
| mask bits here manually because we have run out of `target_flags' |
| bits. We really need to redesign this mask business. */ |
| |
| enable_mask_for_builtins ((struct builtin_description *) bdesc_2arg, |
| ARRAY_SIZE (bdesc_2arg), |
| SPE_BUILTIN_EVADDW, |
| SPE_BUILTIN_EVXOR); |
| enable_mask_for_builtins ((struct builtin_description *) bdesc_1arg, |
| ARRAY_SIZE (bdesc_1arg), |
| SPE_BUILTIN_EVABS, |
| SPE_BUILTIN_EVSUBFUSIAAW); |
| enable_mask_for_builtins ((struct builtin_description *) bdesc_spe_predicates, |
| ARRAY_SIZE (bdesc_spe_predicates), |
| SPE_BUILTIN_EVCMPEQ, |
| SPE_BUILTIN_EVFSTSTLT); |
| enable_mask_for_builtins ((struct builtin_description *) bdesc_spe_evsel, |
| ARRAY_SIZE (bdesc_spe_evsel), |
| SPE_BUILTIN_EVSEL_CMPGTS, |
| SPE_BUILTIN_EVSEL_FSTSTEQ); |
| |
| (*lang_hooks.decls.pushdecl) |
| (build_decl (TYPE_DECL, get_identifier ("__ev64_opaque__"), |
| opaque_V2SI_type_node)); |
| |
| /* Initialize irregular SPE builtins. */ |
| |
| def_builtin (target_flags, "__builtin_spe_mtspefscr", void_ftype_int, SPE_BUILTIN_MTSPEFSCR); |
| def_builtin (target_flags, "__builtin_spe_mfspefscr", int_ftype_void, SPE_BUILTIN_MFSPEFSCR); |
| def_builtin (target_flags, "__builtin_spe_evstddx", void_ftype_v2si_pv2si_int, SPE_BUILTIN_EVSTDDX); |
| def_builtin (target_flags, "__builtin_spe_evstdhx", void_ftype_v2si_pv2si_int, SPE_BUILTIN_EVSTDHX); |
| def_builtin (target_flags, "__builtin_spe_evstdwx", void_ftype_v2si_pv2si_int, SPE_BUILTIN_EVSTDWX); |
| def_builtin (target_flags, "__builtin_spe_evstwhex", void_ftype_v2si_puint_int, SPE_BUILTIN_EVSTWHEX); |
| def_builtin (target_flags, "__builtin_spe_evstwhox", void_ftype_v2si_puint_int, SPE_BUILTIN_EVSTWHOX); |
| def_builtin (target_flags, "__builtin_spe_evstwwex", void_ftype_v2si_puint_int, SPE_BUILTIN_EVSTWWEX); |
| def_builtin (target_flags, "__builtin_spe_evstwwox", void_ftype_v2si_puint_int, SPE_BUILTIN_EVSTWWOX); |
| def_builtin (target_flags, "__builtin_spe_evstdd", void_ftype_v2si_pv2si_char, SPE_BUILTIN_EVSTDD); |
| def_builtin (target_flags, "__builtin_spe_evstdh", void_ftype_v2si_pv2si_char, SPE_BUILTIN_EVSTDH); |
| def_builtin (target_flags, "__builtin_spe_evstdw", void_ftype_v2si_pv2si_char, SPE_BUILTIN_EVSTDW); |
| def_builtin (target_flags, "__builtin_spe_evstwhe", void_ftype_v2si_puint_char, SPE_BUILTIN_EVSTWHE); |
| def_builtin (target_flags, "__builtin_spe_evstwho", void_ftype_v2si_puint_char, SPE_BUILTIN_EVSTWHO); |
| def_builtin (target_flags, "__builtin_spe_evstwwe", void_ftype_v2si_puint_char, SPE_BUILTIN_EVSTWWE); |
| def_builtin (target_flags, "__builtin_spe_evstwwo", void_ftype_v2si_puint_char, SPE_BUILTIN_EVSTWWO); |
| def_builtin (target_flags, "__builtin_spe_evsplatfi", v2si_ftype_signed_char, SPE_BUILTIN_EVSPLATFI); |
| def_builtin (target_flags, "__builtin_spe_evsplati", v2si_ftype_signed_char, SPE_BUILTIN_EVSPLATI); |
| |
| /* Loads. */ |
| def_builtin (target_flags, "__builtin_spe_evlddx", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDDX); |
| def_builtin (target_flags, "__builtin_spe_evldwx", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDWX); |
| def_builtin (target_flags, "__builtin_spe_evldhx", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDHX); |
| def_builtin (target_flags, "__builtin_spe_evlwhex", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHEX); |
| def_builtin (target_flags, "__builtin_spe_evlwhoux", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHOUX); |
| def_builtin (target_flags, "__builtin_spe_evlwhosx", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHOSX); |
| def_builtin (target_flags, "__builtin_spe_evlwwsplatx", v2si_ftype_puint_int, SPE_BUILTIN_EVLWWSPLATX); |
| def_builtin (target_flags, "__builtin_spe_evlwhsplatx", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHSPLATX); |
| def_builtin (target_flags, "__builtin_spe_evlhhesplatx", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHESPLATX); |
| def_builtin (target_flags, "__builtin_spe_evlhhousplatx", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHOUSPLATX); |
| def_builtin (target_flags, "__builtin_spe_evlhhossplatx", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHOSSPLATX); |
| def_builtin (target_flags, "__builtin_spe_evldd", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDD); |
| def_builtin (target_flags, "__builtin_spe_evldw", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDW); |
| def_builtin (target_flags, "__builtin_spe_evldh", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDH); |
| def_builtin (target_flags, "__builtin_spe_evlhhesplat", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHESPLAT); |
| def_builtin (target_flags, "__builtin_spe_evlhhossplat", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHOSSPLAT); |
| def_builtin (target_flags, "__builtin_spe_evlhhousplat", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHOUSPLAT); |
| def_builtin (target_flags, "__builtin_spe_evlwhe", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHE); |
| def_builtin (target_flags, "__builtin_spe_evlwhos", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHOS); |
| def_builtin (target_flags, "__builtin_spe_evlwhou", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHOU); |
| def_builtin (target_flags, "__builtin_spe_evlwhsplat", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHSPLAT); |
| def_builtin (target_flags, "__builtin_spe_evlwwsplat", v2si_ftype_puint_int, SPE_BUILTIN_EVLWWSPLAT); |
| |
| /* Predicates. */ |
| d = (struct builtin_description *) bdesc_spe_predicates; |
| for (i = 0; i < ARRAY_SIZE (bdesc_spe_predicates); ++i, d++) |
| { |
| tree type; |
| |
| switch (insn_data[d->icode].operand[1].mode) |
| { |
| case V2SImode: |
| type = int_ftype_int_v2si_v2si; |
| break; |
| case V2SFmode: |
| type = int_ftype_int_v2sf_v2sf; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| def_builtin (d->mask, d->name, type, d->code); |
| } |
| |
| /* Evsel predicates. */ |
| d = (struct builtin_description *) bdesc_spe_evsel; |
| for (i = 0; i < ARRAY_SIZE (bdesc_spe_evsel); ++i, d++) |
| { |
| tree type; |
| |
| switch (insn_data[d->icode].operand[1].mode) |
| { |
| case V2SImode: |
| type = v2si_ftype_4_v2si; |
| break; |
| case V2SFmode: |
| type = v2sf_ftype_4_v2sf; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| def_builtin (d->mask, d->name, type, d->code); |
| } |
| } |
| |
| static void |
| altivec_init_builtins (void) |
| { |
| struct builtin_description *d; |
| struct builtin_description_predicates *dp; |
| size_t i; |
| tree ftype; |
| |
| tree pfloat_type_node = build_pointer_type (float_type_node); |
| tree pint_type_node = build_pointer_type (integer_type_node); |
| tree pshort_type_node = build_pointer_type (short_integer_type_node); |
| tree pchar_type_node = build_pointer_type (char_type_node); |
| |
| tree pvoid_type_node = build_pointer_type (void_type_node); |
| |
| tree pcfloat_type_node = build_pointer_type (build_qualified_type (float_type_node, TYPE_QUAL_CONST)); |
| tree pcint_type_node = build_pointer_type (build_qualified_type (integer_type_node, TYPE_QUAL_CONST)); |
| tree pcshort_type_node = build_pointer_type (build_qualified_type (short_integer_type_node, TYPE_QUAL_CONST)); |
| tree pcchar_type_node = build_pointer_type (build_qualified_type (char_type_node, TYPE_QUAL_CONST)); |
| |
| tree pcvoid_type_node = build_pointer_type (build_qualified_type (void_type_node, TYPE_QUAL_CONST)); |
| |
| tree int_ftype_opaque |
| = build_function_type_list (integer_type_node, |
| opaque_V4SI_type_node, NULL_TREE); |
| |
| tree opaque_ftype_opaque_int |
| = build_function_type_list (opaque_V4SI_type_node, |
| opaque_V4SI_type_node, integer_type_node, NULL_TREE); |
| tree opaque_ftype_opaque_opaque_int |
| = build_function_type_list (opaque_V4SI_type_node, |
| opaque_V4SI_type_node, opaque_V4SI_type_node, |
| integer_type_node, NULL_TREE); |
| tree int_ftype_int_opaque_opaque |
| = build_function_type_list (integer_type_node, |
| integer_type_node, opaque_V4SI_type_node, |
| opaque_V4SI_type_node, NULL_TREE); |
| tree int_ftype_int_v4si_v4si |
| = build_function_type_list (integer_type_node, |
| integer_type_node, V4SI_type_node, |
| V4SI_type_node, NULL_TREE); |
| tree v4sf_ftype_pcfloat |
| = build_function_type_list (V4SF_type_node, pcfloat_type_node, NULL_TREE); |
| tree void_ftype_pfloat_v4sf |
| = build_function_type_list (void_type_node, |
| pfloat_type_node, V4SF_type_node, NULL_TREE); |
| tree v4si_ftype_pcint |
| = build_function_type_list (V4SI_type_node, pcint_type_node, NULL_TREE); |
| tree void_ftype_pint_v4si |
| = build_function_type_list (void_type_node, |
| pint_type_node, V4SI_type_node, NULL_TREE); |
| tree v8hi_ftype_pcshort |
| = build_function_type_list (V8HI_type_node, pcshort_type_node, NULL_TREE); |
| tree void_ftype_pshort_v8hi |
| = build_function_type_list (void_type_node, |
| pshort_type_node, V8HI_type_node, NULL_TREE); |
| tree v16qi_ftype_pcchar |
| = build_function_type_list (V16QI_type_node, pcchar_type_node, NULL_TREE); |
| tree void_ftype_pchar_v16qi |
| = build_function_type_list (void_type_node, |
| pchar_type_node, V16QI_type_node, NULL_TREE); |
| tree void_ftype_v4si |
| = build_function_type_list (void_type_node, V4SI_type_node, NULL_TREE); |
| tree v8hi_ftype_void |
| = build_function_type (V8HI_type_node, void_list_node); |
| tree void_ftype_void |
| = build_function_type (void_type_node, void_list_node); |
| tree void_ftype_int |
| = build_function_type_list (void_type_node, integer_type_node, NULL_TREE); |
| |
| tree opaque_ftype_long_pcvoid |
| = build_function_type_list (opaque_V4SI_type_node, |
| long_integer_type_node, pcvoid_type_node, NULL_TREE); |
| tree v16qi_ftype_long_pcvoid |
| = build_function_type_list (V16QI_type_node, |
| long_integer_type_node, pcvoid_type_node, NULL_TREE); |
| tree v8hi_ftype_long_pcvoid |
| = build_function_type_list (V8HI_type_node, |
| long_integer_type_node, pcvoid_type_node, NULL_TREE); |
| tree v4si_ftype_long_pcvoid |
| = build_function_type_list (V4SI_type_node, |
| long_integer_type_node, pcvoid_type_node, NULL_TREE); |
| |
| tree void_ftype_opaque_long_pvoid |
| = build_function_type_list (void_type_node, |
| opaque_V4SI_type_node, long_integer_type_node, |
| pvoid_type_node, NULL_TREE); |
| tree void_ftype_v4si_long_pvoid |
| = build_function_type_list (void_type_node, |
| V4SI_type_node, long_integer_type_node, |
| pvoid_type_node, NULL_TREE); |
| tree void_ftype_v16qi_long_pvoid |
| = build_function_type_list (void_type_node, |
| V16QI_type_node, long_integer_type_node, |
| pvoid_type_node, NULL_TREE); |
| tree void_ftype_v8hi_long_pvoid |
| = build_function_type_list (void_type_node, |
| V8HI_type_node, long_integer_type_node, |
| pvoid_type_node, NULL_TREE); |
| tree int_ftype_int_v8hi_v8hi |
| = build_function_type_list (integer_type_node, |
| integer_type_node, V8HI_type_node, |
| V8HI_type_node, NULL_TREE); |
| tree int_ftype_int_v16qi_v16qi |
| = build_function_type_list (integer_type_node, |
| integer_type_node, V16QI_type_node, |
| V16QI_type_node, NULL_TREE); |
| tree int_ftype_int_v4sf_v4sf |
| = build_function_type_list (integer_type_node, |
| integer_type_node, V4SF_type_node, |
| V4SF_type_node, NULL_TREE); |
| tree v4si_ftype_v4si |
| = build_function_type_list (V4SI_type_node, V4SI_type_node, NULL_TREE); |
| tree v8hi_ftype_v8hi |
| = build_function_type_list (V8HI_type_node, V8HI_type_node, NULL_TREE); |
| tree v16qi_ftype_v16qi |
| = build_function_type_list (V16QI_type_node, V16QI_type_node, NULL_TREE); |
| tree v4sf_ftype_v4sf |
| = build_function_type_list (V4SF_type_node, V4SF_type_node, NULL_TREE); |
| tree void_ftype_pcvoid_int_int |
| = build_function_type_list (void_type_node, |
| pcvoid_type_node, integer_type_node, |
| integer_type_node, NULL_TREE); |
| |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_ld_internal_4sf", v4sf_ftype_pcfloat, |
| ALTIVEC_BUILTIN_LD_INTERNAL_4sf); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_st_internal_4sf", void_ftype_pfloat_v4sf, |
| ALTIVEC_BUILTIN_ST_INTERNAL_4sf); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_ld_internal_4si", v4si_ftype_pcint, |
| ALTIVEC_BUILTIN_LD_INTERNAL_4si); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_st_internal_4si", void_ftype_pint_v4si, |
| ALTIVEC_BUILTIN_ST_INTERNAL_4si); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_ld_internal_8hi", v8hi_ftype_pcshort, |
| ALTIVEC_BUILTIN_LD_INTERNAL_8hi); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_st_internal_8hi", void_ftype_pshort_v8hi, |
| ALTIVEC_BUILTIN_ST_INTERNAL_8hi); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_ld_internal_16qi", v16qi_ftype_pcchar, |
| ALTIVEC_BUILTIN_LD_INTERNAL_16qi); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_st_internal_16qi", void_ftype_pchar_v16qi, |
| ALTIVEC_BUILTIN_ST_INTERNAL_16qi); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_mtvscr", void_ftype_v4si, ALTIVEC_BUILTIN_MTVSCR); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_mfvscr", v8hi_ftype_void, ALTIVEC_BUILTIN_MFVSCR); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_dssall", void_ftype_void, ALTIVEC_BUILTIN_DSSALL); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_dss", void_ftype_int, ALTIVEC_BUILTIN_DSS); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvsl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVSL); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvsr", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVSR); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvebx", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEBX); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvehx", v8hi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEHX); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvewx", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEWX); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvxl", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVXL); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvx", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVX); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvx", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVX); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvewx", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVEWX); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvxl", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVXL); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvebx", void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_STVEBX); |
| def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvehx", void_ftype_v8hi_long_pvoid, ALTIVEC_BUILTIN_STVEHX); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ld", opaque_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LD); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_lde", opaque_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LDE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ldl", opaque_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LDL); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_lvsl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVSL); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_lvsr", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVSR); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_lvebx", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVEBX); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_lvehx", v8hi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVEHX); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_lvewx", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVEWX); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_st", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_ST); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ste", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_stl", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STL); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_stvewx", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STVEWX); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_stvebx", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STVEBX); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_stvehx", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STVEHX); |
| |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_step", int_ftype_opaque, ALTIVEC_BUILTIN_VEC_STEP); |
| |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_sld", opaque_ftype_opaque_opaque_int, ALTIVEC_BUILTIN_VEC_SLD); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_splat", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_SPLAT); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_vspltw", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_VSPLTW); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_vsplth", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_VSPLTH); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_vspltb", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_VSPLTB); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ctf", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_CTF); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_vcfsx", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_VCFSX); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_vcfux", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_VCFUX); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_cts", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_CTS); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ctu", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_CTU); |
| |
| /* Add the DST variants. */ |
| d = (struct builtin_description *) bdesc_dst; |
| for (i = 0; i < ARRAY_SIZE (bdesc_dst); i++, d++) |
| def_builtin (d->mask, d->name, void_ftype_pcvoid_int_int, d->code); |
| |
| /* Initialize the predicates. */ |
| dp = (struct builtin_description_predicates *) bdesc_altivec_preds; |
| for (i = 0; i < ARRAY_SIZE (bdesc_altivec_preds); i++, dp++) |
| { |
| enum machine_mode mode1; |
| tree type; |
| bool is_overloaded = dp->code >= ALTIVEC_BUILTIN_OVERLOADED_FIRST |
| && dp->code <= ALTIVEC_BUILTIN_OVERLOADED_LAST; |
| |
| if (is_overloaded) |
| mode1 = VOIDmode; |
| else |
| mode1 = insn_data[dp->icode].operand[1].mode; |
| |
| switch (mode1) |
| { |
| case VOIDmode: |
| type = int_ftype_int_opaque_opaque; |
| break; |
| case V4SImode: |
| type = int_ftype_int_v4si_v4si; |
| break; |
| case V8HImode: |
| type = int_ftype_int_v8hi_v8hi; |
| break; |
| case V16QImode: |
| type = int_ftype_int_v16qi_v16qi; |
| break; |
| case V4SFmode: |
| type = int_ftype_int_v4sf_v4sf; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| def_builtin (dp->mask, dp->name, type, dp->code); |
| } |
| |
| /* Initialize the abs* operators. */ |
| d = (struct builtin_description *) bdesc_abs; |
| for (i = 0; i < ARRAY_SIZE (bdesc_abs); i++, d++) |
| { |
| enum machine_mode mode0; |
| tree type; |
| |
| mode0 = insn_data[d->icode].operand[0].mode; |
| |
| switch (mode0) |
| { |
| case V4SImode: |
| type = v4si_ftype_v4si; |
| break; |
| case V8HImode: |
| type = v8hi_ftype_v8hi; |
| break; |
| case V16QImode: |
| type = v16qi_ftype_v16qi; |
| break; |
| case V4SFmode: |
| type = v4sf_ftype_v4sf; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| def_builtin (d->mask, d->name, type, d->code); |
| } |
| |
| if (TARGET_ALTIVEC) |
| { |
| tree decl; |
| |
| /* Initialize target builtin that implements |
| targetm.vectorize.builtin_mask_for_load. */ |
| |
| decl = lang_hooks.builtin_function ("__builtin_altivec_mask_for_load", |
| v16qi_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_MASK_FOR_LOAD, |
| BUILT_IN_MD, NULL, |
| tree_cons (get_identifier ("const"), |
| NULL_TREE, NULL_TREE)); |
| /* Record the decl. Will be used by rs6000_builtin_mask_for_load. */ |
| altivec_builtin_mask_for_load = decl; |
| } |
| |
| /* APPLE LOCAL begin AltiVec */ |
| /* If Apple AltiVec is enabled, we need to define additional builtins |
| in lieu of what <altivec.h> provides for FSF AltiVec. */ |
| if (rs6000_altivec_pim) |
| { |
| tree int_ftype_ellipsis = build_function_type (integer_type_node, |
| NULL_TREE); |
| int pim_code = ALTIVEC_PIM__FIRST; |
| |
| /* NB: For overloaded operations/predicates, the pim_... flags specify |
| how to match up the argument types and how to determine the |
| return type, if necessary; the rs6000_fold_builtin() routine |
| does all this. */ |
| |
| /* PIM Operations. */ |
| |
| gcc_assert (pim_code == ALTIVEC_PIM_VEC_ABS); |
| |
| def_pim_builtin ("vec_abs", V16QI_type_node, ABS_V16QI, pim_ovl_16 | pim_group); |
| def_pim_builtin ("vec_abs.2", V8HI_type_node, ABS_V8HI, pim_ovl_8); |
| def_pim_builtin ("vec_abs.3", V4SF_type_node, ABS_V4SF, pim_ovl_4f); |
| def_pim_builtin ("vec_abs.4", V4SI_type_node, ABS_V4SI, pim_ovl_4); |
| |
| def_pim_builtin ("vec_abss", V16QI_type_node, ABSS_V16QI, pim_ovl_16 | pim_group); |
| def_pim_builtin ("vec_abss.2", V8HI_type_node, ABSS_V8HI, pim_ovl_8); |
| def_pim_builtin ("vec_abss.3", V4SI_type_node, ABSS_V4SI, pim_ovl_4); |
| |
| def_pim_builtin ("vec_add", NULL_TREE, VADDUBM, pim_ovl_16 | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_add.2", NULL_TREE, VADDUHM, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_add.3", V4SF_type_node, VADDFP, pim_ovl_4f); |
| def_pim_builtin ("vec_add.4", NULL_TREE, VADDUWM, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_addc", unsigned_V4SI_type_node, VADDCUW, pim_group); |
| |
| def_pim_builtin ("vec_adds", NULL_TREE, VADDUBS, pim_ovl_16u_16u | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_adds.2", NULL_TREE, VADDSBS, pim_ovl_16 | pim_rt_12); |
| def_pim_builtin ("vec_adds.3", NULL_TREE, VADDUHS, pim_ovl_8u_8u | pim_rt_12); |
| def_pim_builtin ("vec_adds.4", NULL_TREE, VADDSHS, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_adds.5", NULL_TREE, VADDUWS, pim_ovl_4u_4u | pim_rt_12); |
| def_pim_builtin ("vec_adds.6", NULL_TREE, VADDSWS, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_and", NULL_TREE, VAND, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_andc", NULL_TREE, VANDC, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_avg", NULL_TREE, VAVGUB, pim_ovl_16u | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_avg.2", NULL_TREE, VAVGSB, pim_ovl_16 | pim_rt_12); |
| def_pim_builtin ("vec_avg.3", NULL_TREE, VAVGUH, pim_ovl_8u | pim_rt_12); |
| def_pim_builtin ("vec_avg.4", NULL_TREE, VAVGSH, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_avg.5", NULL_TREE, VAVGUW, pim_ovl_4u | pim_rt_12); |
| def_pim_builtin ("vec_avg.6", NULL_TREE, VAVGSW, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_ceil", V4SF_type_node, VRFIP, pim_group); |
| |
| def_pim_builtin ("vec_cmpb", V4SI_type_node, VCMPBFP, pim_group); |
| |
| def_pim_builtin ("vec_cmpeq", bool_V16QI_type_node, VCMPEQUB, pim_ovl_16 | pim_group); |
| def_pim_builtin ("vec_cmpeq.2", bool_V8HI_type_node, VCMPEQUH, pim_ovl_8); |
| def_pim_builtin ("vec_cmpeq.3", bool_V4SI_type_node, VCMPEQFP, pim_ovl_4f); |
| def_pim_builtin ("vec_cmpeq.4", bool_V4SI_type_node, VCMPEQUW, pim_ovl_4); |
| |
| def_pim_builtin ("vec_cmpge", bool_V4SI_type_node, VCMPGEFP, pim_group); |
| |
| def_pim_builtin ("vec_cmpgt", bool_V16QI_type_node, VCMPGTUB, pim_ovl_16u | pim_group); |
| def_pim_builtin ("vec_cmpgt.2", bool_V16QI_type_node, VCMPGTSB, pim_ovl_16); |
| def_pim_builtin ("vec_cmpgt.3", bool_V8HI_type_node, VCMPGTUH, pim_ovl_8u); |
| def_pim_builtin ("vec_cmpgt.4", bool_V8HI_type_node, VCMPGTSH, pim_ovl_8); |
| def_pim_builtin ("vec_cmpgt.5", bool_V4SI_type_node, VCMPGTFP, pim_ovl_4f); |
| def_pim_builtin ("vec_cmpgt.6", bool_V4SI_type_node, VCMPGTUW, pim_ovl_4u); |
| def_pim_builtin ("vec_cmpgt.7", bool_V4SI_type_node, VCMPGTSW, pim_ovl_4); |
| |
| def_pim_builtin ("vec_cmple", bool_V4SI_type_node, VCMPGEFP, pim_manip_swap | pim_group); |
| |
| def_pim_builtin ("vec_cmplt", bool_V16QI_type_node, VCMPGTUB, pim_ovl_16u | pim_manip_swap | pim_group); |
| def_pim_builtin ("vec_cmplt.2", bool_V16QI_type_node, VCMPGTSB, pim_ovl_16 | pim_manip_swap); |
| def_pim_builtin ("vec_cmplt.3", bool_V8HI_type_node, VCMPGTUH, pim_ovl_8u | pim_manip_swap); |
| def_pim_builtin ("vec_cmplt.4", bool_V8HI_type_node, VCMPGTSH, pim_ovl_8 | pim_manip_swap); |
| def_pim_builtin ("vec_cmplt.5", bool_V4SI_type_node, VCMPGTFP, pim_ovl_4f | pim_manip_swap); |
| def_pim_builtin ("vec_cmplt.6", bool_V4SI_type_node, VCMPGTUW, pim_ovl_4u | pim_manip_swap); |
| def_pim_builtin ("vec_cmplt.7", bool_V4SI_type_node, VCMPGTSW, pim_ovl_4 | pim_manip_swap); |
| |
| def_pim_builtin ("vec_ctf", V4SF_type_node, VCFUX, pim_ovl_4u | pim_group); |
| def_pim_builtin ("vec_ctf.2", V4SF_type_node, VCFSX, pim_ovl_4); |
| |
| def_pim_builtin ("vec_cts", V4SI_type_node, VCTSXS, pim_ovl_4f | pim_group); |
| |
| def_pim_builtin ("vec_ctu", unsigned_V4SI_type_node, VCTUXS, pim_ovl_4f | pim_group); |
| |
| def_pim_builtin ("vec_dss", void_type_node, DSS, pim_group); |
| |
| def_pim_builtin ("vec_dssall", void_type_node, DSSALL, pim_group); |
| |
| def_pim_builtin ("vec_dst", void_type_node, DST, pim_group); |
| |
| def_pim_builtin ("vec_dstst", void_type_node, DSTST, pim_group); |
| |
| def_pim_builtin ("vec_dststt", void_type_node, DSTSTT, pim_group); |
| |
| def_pim_builtin ("vec_dstt", void_type_node, DSTT, pim_group); |
| |
| def_pim_builtin ("vec_expte", V4SF_type_node, VEXPTEFP, pim_group); |
| |
| def_pim_builtin ("vec_floor", V4SF_type_node, VRFIM, pim_group); |
| |
| def_pim_builtin ("vec_ld", NULL_TREE, LVX, pim_rt_2p | pim_group); |
| |
| def_pim_builtin ("vec_lde", NULL_TREE, LVEBX, pim_ovl_pqi_2 | pim_rt_2p | pim_group); |
| def_pim_builtin ("vec_lde.2", NULL_TREE, LVEHX, pim_ovl_phi_2 | pim_rt_2p); |
| def_pim_builtin ("vec_lde.3", NULL_TREE, LVEWX, pim_ovl_psi_2 | pim_rt_2p); |
| |
| def_pim_builtin ("vec_ldl", NULL_TREE, LVXL, pim_rt_2p | pim_group); |
| |
| def_pim_builtin ("vec_loge", V4SF_type_node, VLOGEFP, pim_group); |
| |
| def_pim_builtin ("vec_lvebx", NULL_TREE, LVEBX, pim_rt_2p | pim_group); |
| def_pim_builtin ("vec_lvehx", NULL_TREE, LVEHX, pim_rt_2p | pim_group); |
| def_pim_builtin ("vec_lvewx", NULL_TREE, LVEWX, pim_rt_2p | pim_group); |
| |
| def_pim_builtin ("vec_lvsl", unsigned_V16QI_type_node, LVSL, pim_group); |
| |
| def_pim_builtin ("vec_lvsr", unsigned_V16QI_type_node, LVSR, pim_group); |
| |
| def_pim_builtin ("vec_lvx", NULL_TREE, LVX, pim_rt_2p | pim_group); |
| |
| def_pim_builtin ("vec_lvxl", NULL_TREE, LVXL, pim_rt_2p | pim_group); |
| |
| def_pim_builtin ("vec_madd", V4SF_type_node, VMADDFP, pim_group); |
| |
| def_pim_builtin ("vec_madds", V8HI_type_node, VMHADDSHS, pim_group); |
| |
| def_pim_builtin ("vec_max", NULL_TREE, VMAXUB, pim_ovl_16u_16u | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_max.2", NULL_TREE, VMAXSB, pim_ovl_16 | pim_rt_12); |
| def_pim_builtin ("vec_max.3", NULL_TREE, VMAXUH, pim_ovl_8u_8u | pim_rt_12); |
| def_pim_builtin ("vec_max.4", NULL_TREE, VMAXSH, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_max.5", NULL_TREE, VMAXFP, pim_ovl_4f | pim_rt_12); |
| def_pim_builtin ("vec_max.6", NULL_TREE, VMAXUW, pim_ovl_4u_4u | pim_rt_12); |
| def_pim_builtin ("vec_max.7", NULL_TREE, VMAXSW, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_mergeh", NULL_TREE, VMRGHB, pim_ovl_16 | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_mergeh.2", NULL_TREE, VMRGHH, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_mergeh.3", NULL_TREE, VMRGHW, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_mergel", NULL_TREE, VMRGLB, pim_ovl_16 | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_mergel.2", NULL_TREE, VMRGLH, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_mergel.3", NULL_TREE, VMRGLW, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_mfvscr", unsigned_V8HI_type_node, MFVSCR, pim_group); |
| |
| def_pim_builtin ("vec_min", NULL_TREE, VMINUB, pim_ovl_16u_16u | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_min.2", NULL_TREE, VMINSB, pim_ovl_16 | pim_rt_12); |
| def_pim_builtin ("vec_min.3", NULL_TREE, VMINUH, pim_ovl_8u_8u | pim_rt_12); |
| def_pim_builtin ("vec_min.4", NULL_TREE, VMINSH, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_min.5", NULL_TREE, VMINFP, pim_ovl_4f | pim_rt_12); |
| def_pim_builtin ("vec_min.6", NULL_TREE, VMINUW, pim_ovl_4u_4u | pim_rt_12); |
| def_pim_builtin ("vec_min.7", NULL_TREE, VMINSW, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_mladd", unsigned_V8HI_type_node, VMLADDUHM, pim_ovl_8u_8u | pim_group); |
| def_pim_builtin ("vec_mladd.2", V8HI_type_node, VMLADDUHM, pim_ovl_8); |
| |
| def_pim_builtin ("vec_mradds", V8HI_type_node, VMHRADDSHS, pim_group); |
| |
| def_pim_builtin ("vec_msum", unsigned_V4SI_type_node, VMSUMUBM, pim_ovl_16u | pim_group); |
| def_pim_builtin ("vec_msum.2", V4SI_type_node, VMSUMMBM, pim_ovl_16); |
| def_pim_builtin ("vec_msum.3", unsigned_V4SI_type_node, VMSUMUHM, pim_ovl_8u); |
| def_pim_builtin ("vec_msum.4", V4SI_type_node, VMSUMSHM, pim_ovl_8); |
| |
| def_pim_builtin ("vec_msums", unsigned_V4SI_type_node, VMSUMUHS, pim_ovl_8u | pim_group); |
| def_pim_builtin ("vec_msums.2", V4SI_type_node, VMSUMSHS, pim_ovl_8); |
| |
| def_pim_builtin ("vec_mtvscr", void_type_node, MTVSCR, pim_group); |
| |
| def_pim_builtin ("vec_mule", unsigned_V8HI_type_node, VMULEUB, pim_ovl_16u | pim_group); |
| def_pim_builtin ("vec_mule.2", V8HI_type_node, VMULESB, pim_ovl_16); |
| def_pim_builtin ("vec_mule.3", unsigned_V4SI_type_node, VMULEUH, pim_ovl_8u); |
| def_pim_builtin ("vec_mule.4", V4SI_type_node, VMULESH, pim_ovl_8); |
| |
| def_pim_builtin ("vec_mulo", unsigned_V8HI_type_node, VMULOUB, pim_ovl_16u | pim_group); |
| def_pim_builtin ("vec_mulo.2", V8HI_type_node, VMULOSB, pim_ovl_16); |
| def_pim_builtin ("vec_mulo.3", unsigned_V4SI_type_node, VMULOUH, pim_ovl_8u); |
| def_pim_builtin ("vec_mulo.4", V4SI_type_node, VMULOSH, pim_ovl_8); |
| |
| def_pim_builtin ("vec_nmsub", V4SF_type_node, VNMSUBFP, pim_group); |
| |
| def_pim_builtin ("vec_nor", NULL_TREE, VNOR, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_or", NULL_TREE, VOR, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_pack", NULL_TREE, VPKUHUM, pim_ovl_8 | pim_rt_1h | pim_group); |
| def_pim_builtin ("vec_pack.2", NULL_TREE, VPKUWUM, pim_ovl_4 | pim_rt_1h); |
| |
| def_pim_builtin ("vec_packpx", pixel_V8HI_type_node, VPKPX, pim_group); |
| |
| def_pim_builtin ("vec_packs", unsigned_V16QI_type_node, VPKUHUS, pim_ovl_8u | pim_group); |
| def_pim_builtin ("vec_packs.2", V16QI_type_node, VPKSHSS, pim_ovl_8); |
| def_pim_builtin ("vec_packs.3", unsigned_V8HI_type_node, VPKUWUS, pim_ovl_4u); |
| def_pim_builtin ("vec_packs.4", V8HI_type_node, VPKSWSS, pim_ovl_4); |
| |
| def_pim_builtin ("vec_packsu", unsigned_V16QI_type_node, VPKUHUS, pim_ovl_8u | pim_group); |
| def_pim_builtin ("vec_packsu.2", unsigned_V16QI_type_node, VPKSHUS, pim_ovl_8); |
| def_pim_builtin ("vec_packsu.3", unsigned_V8HI_type_node, VPKUWUS, pim_ovl_4u); |
| def_pim_builtin ("vec_packsu.4", unsigned_V8HI_type_node, VPKSWUS, pim_ovl_4); |
| |
| def_pim_builtin ("vec_perm", V16QI_type_node, VPERM_4SI, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_re", V4SF_type_node, VREFP, pim_group); |
| |
| def_pim_builtin ("vec_rl", NULL_TREE, VRLB, pim_ovl_16 | pim_rt_1 | pim_group); |
| def_pim_builtin ("vec_rl.2", NULL_TREE, VRLH, pim_ovl_8 | pim_rt_1); |
| def_pim_builtin ("vec_rl.3", NULL_TREE, VRLW, pim_ovl_4 | pim_rt_1); |
| |
| def_pim_builtin ("vec_round", V4SF_type_node, VRFIN, pim_group); |
| |
| def_pim_builtin ("vec_rsqrte", V4SF_type_node, VRSQRTEFP, pim_group); |
| |
| def_pim_builtin ("vec_sel", NULL_TREE, VSEL_4SI, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_sl", NULL_TREE, VSLB, pim_ovl_16 | pim_rt_1 | pim_group); |
| def_pim_builtin ("vec_sl.2", NULL_TREE, VSLH, pim_ovl_8 | pim_rt_1); |
| def_pim_builtin ("vec_sl.3", NULL_TREE, VSLW, pim_ovl_4 | pim_rt_1); |
| |
| def_pim_builtin ("vec_sld", NULL_TREE, VSLDOI_4SI, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_sll", NULL_TREE, VSL, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_slo", NULL_TREE, VSLO, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_splat", NULL_TREE, VSPLTB, pim_ovl_16 | pim_rt_1 | pim_group); |
| def_pim_builtin ("vec_splat.2", NULL_TREE, VSPLTH, pim_ovl_8 | pim_rt_1); |
| def_pim_builtin ("vec_splat.3", NULL_TREE, VSPLTW, pim_ovl_4 | pim_rt_1); |
| |
| def_pim_builtin ("vec_splat_s8", V16QI_type_node, VSPLTISB, pim_group); |
| |
| def_pim_builtin ("vec_splat_s16", V8HI_type_node, VSPLTISH, pim_group); |
| |
| def_pim_builtin ("vec_splat_s32", V4SI_type_node, VSPLTISW, pim_group); |
| |
| def_pim_builtin ("vec_splat_u8", unsigned_V16QI_type_node, VSPLTISB, pim_group); |
| |
| def_pim_builtin ("vec_splat_u16", unsigned_V8HI_type_node, VSPLTISH, pim_group); |
| |
| def_pim_builtin ("vec_splat_u32", unsigned_V4SI_type_node, VSPLTISW, pim_group); |
| |
| def_pim_builtin ("vec_sr", NULL_TREE, VSRB, pim_ovl_16 | pim_rt_1 | pim_group); |
| def_pim_builtin ("vec_sr.2", NULL_TREE, VSRH, pim_ovl_8 | pim_rt_1); |
| def_pim_builtin ("vec_sr.3", NULL_TREE, VSRW, pim_ovl_4 | pim_rt_1); |
| |
| def_pim_builtin ("vec_sra", NULL_TREE, VSRAB, pim_ovl_16 | pim_rt_1 | pim_group); |
| def_pim_builtin ("vec_sra.2", NULL_TREE, VSRAH, pim_ovl_8 | pim_rt_1); |
| def_pim_builtin ("vec_sra.3", NULL_TREE, VSRAW, pim_ovl_4 | pim_rt_1); |
| |
| def_pim_builtin ("vec_srl", NULL_TREE, VSR, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_sro", NULL_TREE, VSRO, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_st", void_type_node, STVX, pim_group); |
| |
| def_pim_builtin ("vec_ste", void_type_node, STVEBX, pim_ovl_16 | pim_group); |
| def_pim_builtin ("vec_ste.2", void_type_node, STVEHX, pim_ovl_8); |
| def_pim_builtin ("vec_ste.3", void_type_node, STVEWX, pim_ovl_4); |
| |
| def_pim_builtin ("vec_stl", void_type_node, STVXL, pim_group); |
| |
| def_pim_builtin ("vec_stvebx", void_type_node, STVEBX, pim_group); |
| def_pim_builtin ("vec_stvehx", void_type_node, STVEHX, pim_group); |
| def_pim_builtin ("vec_stvewx", void_type_node, STVEWX, pim_group); |
| |
| def_pim_builtin ("vec_stvx", void_type_node, STVX, pim_group); |
| |
| def_pim_builtin ("vec_stvxl", void_type_node, STVXL, pim_group); |
| |
| def_pim_builtin ("vec_sub", NULL_TREE, VSUBUBM, pim_ovl_16 | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_sub.2", NULL_TREE, VSUBUHM, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_sub.3", NULL_TREE, VSUBFP, pim_ovl_4f | pim_rt_12); |
| def_pim_builtin ("vec_sub.4", NULL_TREE, VSUBUWM, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_subc", unsigned_V4SI_type_node, VSUBCUW, pim_group); |
| |
| def_pim_builtin ("vec_subs", NULL_TREE, VSUBUBS, pim_ovl_16u_16u | pim_rt_12 | pim_group); |
| def_pim_builtin ("vec_subs.2", NULL_TREE, VSUBSBS, pim_ovl_16 | pim_rt_12); |
| def_pim_builtin ("vec_subs.3", NULL_TREE, VSUBUHS, pim_ovl_8u_8u | pim_rt_12); |
| def_pim_builtin ("vec_subs.4", NULL_TREE, VSUBSHS, pim_ovl_8 | pim_rt_12); |
| def_pim_builtin ("vec_subs.5", NULL_TREE, VSUBUWS, pim_ovl_4u_4u | pim_rt_12); |
| def_pim_builtin ("vec_subs.6", NULL_TREE, VSUBSWS, pim_ovl_4 | pim_rt_12); |
| |
| def_pim_builtin ("vec_sum4s", unsigned_V4SI_type_node, VSUM4UBS, pim_ovl_16u | pim_group); |
| def_pim_builtin ("vec_sum4s.2", V4SI_type_node, VSUM4SBS, pim_ovl_16); |
| def_pim_builtin ("vec_sum4s.3", V4SI_type_node, VSUM4SHS, pim_ovl_8); |
| |
| def_pim_builtin ("vec_sum2s", V4SI_type_node, VSUM2SWS, pim_group); |
| |
| def_pim_builtin ("vec_sums", V4SI_type_node, VSUMSWS, pim_group); |
| |
| def_pim_builtin ("vec_trunc", V4SF_type_node, VRFIZ, pim_group); |
| |
| def_pim_builtin ("vec_unpackh", NULL_TREE, VUPKHSB, pim_ovl_16 | pim_rt_1d | pim_group); |
| def_pim_builtin ("vec_unpackh.2", NULL_TREE, VUPKHPX, pim_ovl_8p | pim_rt_1d); |
| def_pim_builtin ("vec_unpackh.3", NULL_TREE, VUPKHSH, pim_ovl_8 | pim_rt_1d); |
| |
| def_pim_builtin ("vec_unpackl", NULL_TREE, VUPKLSB, pim_ovl_16 | pim_rt_1d | pim_group); |
| def_pim_builtin ("vec_unpackl.2", NULL_TREE, VUPKLPX, pim_ovl_8p | pim_rt_1d); |
| def_pim_builtin ("vec_unpackl.3", NULL_TREE, VUPKLSH, pim_ovl_8 | pim_rt_1d); |
| |
| gcc_assert (pim_code == ALTIVEC_PIM_VEC_VADDCUW); |
| |
| def_pim_builtin ("vec_vaddcuw", unsigned_V4SI_type_node, VADDCUW, pim_group); |
| |
| def_pim_builtin ("vec_vaddfp", V4SF_type_node, VADDFP, pim_group); |
| |
| def_pim_builtin ("vec_vaddsbs", NULL_TREE, VADDSBS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vaddshs", NULL_TREE, VADDSHS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vaddsws", NULL_TREE, VADDSWS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vaddubm", NULL_TREE, VADDUBM, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vaddubs", NULL_TREE, VADDUBS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vadduhm", NULL_TREE, VADDUHM, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vadduhs", NULL_TREE, VADDUHS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vadduwm", NULL_TREE, VADDUWM, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vadduws", NULL_TREE, VADDUWS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vand", NULL_TREE, VAND, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vandc", NULL_TREE, VANDC, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vavgsb", NULL_TREE, VAVGSB, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vavgsh", NULL_TREE, VAVGSH, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vavgsw", NULL_TREE, VAVGSW, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vavgub", NULL_TREE, VAVGUB, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vavguh", NULL_TREE, VAVGUH, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vavguw", NULL_TREE, VAVGUW, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vcfsx", V4SF_type_node, VCFSX, pim_group); |
| |
| def_pim_builtin ("vec_vcfux", V4SF_type_node, VCFUX, pim_group); |
| |
| def_pim_builtin ("vec_vcmpbfp", V4SI_type_node, VCMPBFP, pim_group); |
| |
| def_pim_builtin ("vec_vcmpeqfp", bool_V4SI_type_node, VCMPEQFP, pim_group); |
| |
| def_pim_builtin ("vec_vcmpequb", bool_V16QI_type_node, VCMPEQUB, pim_group); |
| |
| def_pim_builtin ("vec_vcmpequh", bool_V8HI_type_node, VCMPEQUH, pim_group); |
| |
| def_pim_builtin ("vec_vcmpequw", bool_V4SI_type_node, VCMPEQUW, pim_group); |
| |
| def_pim_builtin ("vec_vcmpgefp", bool_V4SI_type_node, VCMPGEFP, pim_group); |
| |
| def_pim_builtin ("vec_vcmpgtfp", bool_V4SI_type_node, VCMPGTFP, pim_group); |
| |
| def_pim_builtin ("vec_vcmpgtsb", bool_V16QI_type_node, VCMPGTSB, pim_group); |
| |
| def_pim_builtin ("vec_vcmpgtsh", bool_V8HI_type_node, VCMPGTSH, pim_group); |
| |
| def_pim_builtin ("vec_vcmpgtsw", bool_V4SI_type_node, VCMPGTSW, pim_group); |
| |
| def_pim_builtin ("vec_vcmpgtub", bool_V16QI_type_node, VCMPGTUB, pim_group); |
| |
| def_pim_builtin ("vec_vcmpgtuh", bool_V8HI_type_node, VCMPGTUH, pim_group); |
| |
| def_pim_builtin ("vec_vcmpgtuw", bool_V4SI_type_node, VCMPGTUW, pim_group); |
| |
| def_pim_builtin ("vec_vctsxs", V4SI_type_node, VCTSXS, pim_group); |
| |
| def_pim_builtin ("vec_vctuxs", unsigned_V4SI_type_node, VCTUXS, pim_group); |
| |
| def_pim_builtin ("vec_vexptefp", V4SF_type_node, VEXPTEFP, pim_group); |
| |
| def_pim_builtin ("vec_vlogefp", V4SF_type_node, VLOGEFP, pim_group); |
| |
| def_pim_builtin ("vec_vmaddfp", V4SF_type_node, VMADDFP, pim_group); |
| |
| def_pim_builtin ("vec_vmaxfp", NULL_TREE, VMAXFP, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmaxsb", NULL_TREE, VMAXSB, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmaxsh", NULL_TREE, VMAXSH, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmaxsw", NULL_TREE, VMAXSW, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmaxub", NULL_TREE, VMAXUB, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmaxuh", NULL_TREE, VMAXUH, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmaxuw", NULL_TREE, VMAXUW, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmhaddshs", V8HI_type_node, VMHADDSHS, pim_group); |
| |
| def_pim_builtin ("vec_vmhraddshs", V8HI_type_node, VMHRADDSHS, pim_group); |
| |
| def_pim_builtin ("vec_vminfp", NULL_TREE, VMINFP, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vminsb", NULL_TREE, VMINSB, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vminsh", NULL_TREE, VMINSH, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vminsw", NULL_TREE, VMINSW, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vminub", NULL_TREE, VMINUB, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vminuh", NULL_TREE, VMINUH, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vminuw", NULL_TREE, VMINUW, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmladduhm", NULL_TREE, VMLADDUHM, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmrghb", NULL_TREE, VMRGHB, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmrghh", NULL_TREE, VMRGHH, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmrghw", NULL_TREE, VMRGHW, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmrglb", NULL_TREE, VMRGLB, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmrglh", NULL_TREE, VMRGLH, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmrglw", NULL_TREE, VMRGLW, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vmsummbm", V4SI_type_node, VMSUMMBM, pim_group); |
| |
| def_pim_builtin ("vec_vmsumshm", V4SI_type_node, VMSUMSHM, pim_group); |
| |
| def_pim_builtin ("vec_vmsumshs", V4SI_type_node, VMSUMSHS, pim_group); |
| |
| def_pim_builtin ("vec_vmsumubm", unsigned_V4SI_type_node, VMSUMUBM, pim_group); |
| |
| def_pim_builtin ("vec_vmsumuhm", unsigned_V4SI_type_node, VMSUMUHM, pim_group); |
| |
| def_pim_builtin ("vec_vmsumuhs", unsigned_V4SI_type_node, VMSUMUHS, pim_group); |
| |
| def_pim_builtin ("vec_vmulesb", V8HI_type_node, VMULESB, pim_group); |
| |
| def_pim_builtin ("vec_vmulesh", V4SI_type_node, VMULESH, pim_group); |
| |
| def_pim_builtin ("vec_vmuleub", unsigned_V8HI_type_node, VMULEUB, pim_group); |
| |
| def_pim_builtin ("vec_vmuleuh", unsigned_V4SI_type_node, VMULEUH, pim_group); |
| |
| def_pim_builtin ("vec_vmulosb", V8HI_type_node, VMULOSB, pim_group); |
| |
| def_pim_builtin ("vec_vmulosh", V4SI_type_node, VMULOSH, pim_group); |
| |
| def_pim_builtin ("vec_vmuloub", unsigned_V8HI_type_node, VMULOUB, pim_group); |
| |
| def_pim_builtin ("vec_vmulouh", unsigned_V4SI_type_node, VMULOUH, pim_group); |
| |
| def_pim_builtin ("vec_vnmsubfp", V4SF_type_node, VNMSUBFP, pim_group); |
| |
| def_pim_builtin ("vec_vnor", NULL_TREE, VNOR, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vor", NULL_TREE, VOR, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vperm", V16QI_type_node, VPERM_4SI, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vpkpx", pixel_V8HI_type_node, VPKPX, pim_group); |
| |
| def_pim_builtin ("vec_vpkshss", V16QI_type_node, VPKSHSS, pim_group); |
| |
| def_pim_builtin ("vec_vpkshus", unsigned_V16QI_type_node, VPKSHUS, pim_group); |
| |
| def_pim_builtin ("vec_vpkswss", V8HI_type_node, VPKSWSS, pim_group); |
| |
| def_pim_builtin ("vec_vpkswus", unsigned_V8HI_type_node, VPKSWUS, pim_group); |
| |
| def_pim_builtin ("vec_vpkuhum", NULL_TREE, VPKUHUM, pim_rt_1h | pim_group); |
| |
| def_pim_builtin ("vec_vpkuhus", unsigned_V16QI_type_node, VPKUHUS, pim_group); |
| |
| def_pim_builtin ("vec_vpkuwum", NULL_TREE, VPKUWUM, pim_rt_1h | pim_group); |
| |
| def_pim_builtin ("vec_vpkuwus", unsigned_V8HI_type_node, VPKUWUS, pim_group); |
| |
| def_pim_builtin ("vec_vrefp", V4SF_type_node, VREFP, pim_group); |
| |
| def_pim_builtin ("vec_vrfim", V4SF_type_node, VRFIM, pim_group); |
| |
| def_pim_builtin ("vec_vrfin", V4SF_type_node, VRFIN, pim_group); |
| |
| def_pim_builtin ("vec_vrfip", V4SF_type_node, VRFIP, pim_group); |
| |
| def_pim_builtin ("vec_vrfiz", V4SF_type_node, VRFIZ, pim_group); |
| |
| def_pim_builtin ("vec_vrlb", NULL_TREE, VRLB, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vrlh", NULL_TREE, VRLH, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vrlw", NULL_TREE, VRLW, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vrsqrtefp", V4SF_type_node, VRSQRTEFP, pim_group); |
| |
| def_pim_builtin ("vec_vsel", NULL_TREE, VSEL_4SI, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsl", NULL_TREE, VSL, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vslb", NULL_TREE, VSLB, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsldoi", NULL_TREE, VSLDOI_4SI, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vslh", NULL_TREE, VSLH, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vslo", NULL_TREE, VSLO, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vslw", NULL_TREE, VSLW, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vspltb", NULL_TREE, VSPLTB, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsplth", NULL_TREE, VSPLTH, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vspltisb", V16QI_type_node, VSPLTISB, pim_group); |
| |
| def_pim_builtin ("vec_vspltish", V8HI_type_node, VSPLTISH, pim_group); |
| |
| def_pim_builtin ("vec_vspltisw", V4SI_type_node, VSPLTISW, pim_group); |
| |
| def_pim_builtin ("vec_vspltw", NULL_TREE, VSPLTW, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsr", NULL_TREE, VSR, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsrab", NULL_TREE, VSRAB, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsrah", NULL_TREE, VSRAH, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsraw", NULL_TREE, VSRAW, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsrb", NULL_TREE, VSRB, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsrh", NULL_TREE, VSRH, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsro", NULL_TREE, VSRO, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsrw", NULL_TREE, VSRW, pim_rt_1 | pim_group); |
| |
| def_pim_builtin ("vec_vsubcuw", unsigned_V4SI_type_node, VSUBCUW, pim_group); |
| |
| def_pim_builtin ("vec_vsubfp", NULL_TREE, VSUBFP, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsubsbs", NULL_TREE, VSUBSBS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsubshs", NULL_TREE, VSUBSHS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsubsws", NULL_TREE, VSUBSWS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsububm", NULL_TREE, VSUBUBM, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsububs", NULL_TREE, VSUBUBS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsubuhm", NULL_TREE, VSUBUHM, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsubuhs", NULL_TREE, VSUBUHS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsubuwm", NULL_TREE, VSUBUWM, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsubuws", NULL_TREE, VSUBUWS, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_vsum4sbs", V4SI_type_node, VSUM4SBS, pim_group); |
| |
| def_pim_builtin ("vec_vsum4shs", V4SI_type_node, VSUM4SHS, pim_group); |
| |
| def_pim_builtin ("vec_vsum4ubs", unsigned_V4SI_type_node, VSUM4UBS, pim_group); |
| |
| def_pim_builtin ("vec_vsum2sws", V4SI_type_node, VSUM2SWS, pim_group); |
| |
| def_pim_builtin ("vec_vsumsws", V4SI_type_node, VSUMSWS, pim_group); |
| |
| def_pim_builtin ("vec_vupkhpx", NULL_TREE, VUPKHPX, pim_rt_1d | pim_group); |
| |
| def_pim_builtin ("vec_vupkhsb", NULL_TREE, VUPKHSB, pim_rt_1d | pim_group); |
| |
| def_pim_builtin ("vec_vupkhsh", NULL_TREE, VUPKHSH, pim_rt_1d | pim_group); |
| |
| def_pim_builtin ("vec_vupklpx", NULL_TREE, VUPKLPX, pim_rt_1d | pim_group); |
| |
| def_pim_builtin ("vec_vupklsb", NULL_TREE, VUPKLSB, pim_rt_1d | pim_group); |
| |
| def_pim_builtin ("vec_vupklsh", NULL_TREE, VUPKLSH, pim_rt_1d | pim_group); |
| |
| def_pim_builtin ("vec_vxor", NULL_TREE, VXOR, pim_rt_12 | pim_group); |
| |
| def_pim_builtin ("vec_xor", NULL_TREE, VXOR, pim_rt_12 | pim_group); |
| |
| /* PIM Predicates. */ |
| |
| gcc_assert (pim_code == ALTIVEC_PIM_VEC_ALL_EQ); |
| |
| def_pim_builtin ("vec_all_eq", integer_type_node, VCMPEQUB_P, pim_ovl_16 | pim_cr6_lt | pim_group); |
| def_pim_builtin ("vec_all_eq.2", integer_type_node, VCMPEQUH_P, pim_ovl_8 | pim_cr6_lt); |
| def_pim_builtin ("vec_all_eq.3", integer_type_node, VCMPEQFP_P, pim_ovl_4f | pim_cr6_lt); |
| def_pim_builtin ("vec_all_eq.4", integer_type_node, VCMPEQUW_P, pim_ovl_4 | pim_cr6_lt); |
| |
| def_pim_builtin ("vec_all_ge", integer_type_node, VCMPGTUB_P, pim_ovl_16u_16u | pim_manip_swap | pim_cr6_eq | pim_group); |
| def_pim_builtin ("vec_all_ge.2", integer_type_node, VCMPGTSB_P, pim_ovl_16 | pim_manip_swap | pim_cr6_eq); |
| def_pim_builtin ("vec_all_ge.3", integer_type_node, VCMPGTUH_P, pim_ovl_8u_8u | pim_manip_swap | pim_cr6_eq); |
| def_pim_builtin ("vec_all_ge.4", integer_type_node, VCMPGTSH_P, pim_ovl_8 | pim_manip_swap | pim_cr6_eq); |
| def_pim_builtin ("vec_all_ge.5", integer_type_node, VCMPGEFP_P, pim_ovl_4f | pim_cr6_lt); |
| def_pim_builtin ("vec_all_ge.6", integer_type_node, VCMPGTUW_P, pim_ovl_4u_4u | pim_manip_swap | pim_cr6_eq); |
| def_pim_builtin ("vec_all_ge.7", integer_type_node, VCMPGTSW_P, pim_ovl_4 | pim_manip_swap | pim_cr6_eq); |
| |
| def_pim_builtin ("vec_all_gt", integer_type_node, VCMPGTUB_P, pim_ovl_16u_16u | pim_cr6_lt | pim_group); |
| def_pim_builtin ("vec_all_gt.2", integer_type_node, VCMPGTSB_P, pim_ovl_16 | pim_cr6_lt); |
| def_pim_builtin ("vec_all_gt.3", integer_type_node, VCMPGTUH_P, pim_ovl_8u_8u | pim_cr6_lt); |
| def_pim_builtin ("vec_all_gt.4", integer_type_node, VCMPGTSH_P, pim_ovl_8 | pim_cr6_lt); |
| def_pim_builtin ("vec_all_gt.5", integer_type_node, VCMPGTFP_P, pim_ovl_4f | pim_cr6_lt); |
| def_pim_builtin ("vec_all_gt.6", integer_type_node, VCMPGTUW_P, pim_ovl_4u_4u | pim_cr6_lt); |
| def_pim_builtin ("vec_all_gt.7", integer_type_node, VCMPGTSW_P, pim_ovl_4 | pim_cr6_lt); |
| |
| def_pim_builtin ("vec_all_in", integer_type_node, VCMPBFP_P, pim_cr6_eq | pim_group); |
| |
| def_pim_builtin ("vec_all_le", integer_type_node, VCMPGTUB_P, pim_ovl_16u_16u | pim_cr6_eq | pim_group); |
| def_pim_builtin ("vec_all_le.2", integer_type_node, VCMPGTSB_P, pim_ovl_16 | pim_cr6_eq); |
| def_pim_builtin ("vec_all_le.3", integer_type_node, VCMPGTUH_P, pim_ovl_8u_8u | pim_cr6_eq); |
| def_pim_builtin ("vec_all_le.4", integer_type_node, VCMPGTSH_P, pim_ovl_8 | pim_cr6_eq); |
| def_pim_builtin ("vec_all_le.5", integer_type_node, VCMPGEFP_P, pim_ovl_4f | pim_manip_swap | pim_cr6_lt); |
| def_pim_builtin ("vec_all_le.6", integer_type_node, VCMPGTUW_P, pim_ovl_4u_4u | pim_cr6_eq); |
| def_pim_builtin ("vec_all_le.7", integer_type_node, VCMPGTSW_P, pim_ovl_4 | pim_cr6_eq); |
| |
| def_pim_builtin ("vec_all_lt", integer_type_node, VCMPGTUB_P, pim_ovl_16u_16u | pim_manip_swap | pim_cr6_lt | pim_group); |
| def_pim_builtin ("vec_all_lt.2", integer_type_node, VCMPGTSB_P, pim_ovl_16 | pim_manip_swap | pim_cr6_lt); |
| def_pim_builtin ("vec_all_lt.3", integer_type_node, VCMPGTUH_P, pim_ovl_8u_8u | pim_manip_swap | pim_cr6_lt); |
| def_pim_builtin ("vec_all_lt.4", integer_type_node, VCMPGTSH_P, pim_ovl_8 | pim_manip_swap | pim_cr6_lt); |
| def_pim_builtin ("vec_all_lt.5", integer_type_node, VCMPGTFP_P, pim_ovl_4f | pim_manip_swap | pim_cr6_lt); |
| def_pim_builtin ("vec_all_lt.6", integer_type_node, VCMPGTUW_P, pim_ovl_4u_4u | pim_manip_swap | pim_cr6_lt); |
| def_pim_builtin ("vec_all_lt.7", integer_type_node, VCMPGTSW_P, pim_ovl_4 | pim_manip_swap | pim_cr6_lt); |
| |
| def_pim_builtin ("vec_all_nan", integer_type_node, VCMPEQFP_P, pim_manip_dup | pim_cr6_eq | pim_group); |
| |
| def_pim_builtin ("vec_all_ne", integer_type_node, VCMPEQUB_P, pim_ovl_16 | pim_cr6_eq | pim_group); |
| def_pim_builtin ("vec_all_ne.2", integer_type_node, VCMPEQUH_P, pim_ovl_8 | pim_cr6_eq); |
| def_pim_builtin ("vec_all_ne.3", integer_type_node, VCMPEQFP_P, pim_ovl_4f | pim_cr6_eq); |
| def_pim_builtin ("vec_all_ne.4", integer_type_node, VCMPEQUW_P, pim_ovl_4 | pim_cr6_eq); |
| |
| def_pim_builtin ("vec_all_nge", integer_type_node, VCMPGEFP_P, pim_cr6_eq | pim_group); |
| |
| def_pim_builtin ("vec_all_ngt", integer_type_node, VCMPGTFP_P, pim_cr6_eq | pim_group); |
| |
| def_pim_builtin ("vec_all_nle", integer_type_node, VCMPGEFP_P, pim_manip_swap | pim_cr6_eq | pim_group); |
| |
| def_pim_builtin ("vec_all_nlt", integer_type_node, VCMPGEFP_P, pim_manip_swap | pim_cr6_eq | pim_group); |
| |
| def_pim_builtin ("vec_all_numeric", integer_type_node, VCMPEQFP_P, pim_manip_dup | pim_cr6_lt | pim_group); |
| |
| def_pim_builtin ("vec_any_eq", integer_type_node, VCMPEQUB_P, pim_ovl_16 | pim_cr6_ne | pim_group); |
| def_pim_builtin ("vec_any_eq.2", integer_type_node, VCMPEQUH_P, pim_ovl_8 | pim_cr6_ne); |
| def_pim_builtin ("vec_any_eq.3", integer_type_node, VCMPEQFP_P, pim_ovl_4f | pim_cr6_ne); |
| def_pim_builtin ("vec_any_eq.4", integer_type_node, VCMPEQUW_P, pim_ovl_4 | pim_cr6_ne); |
| |
| def_pim_builtin ("vec_any_ge", integer_type_node, VCMPGTUB_P, pim_ovl_16u_16u | pim_manip_swap | pim_cr6_ge | pim_group); |
| def_pim_builtin ("vec_any_ge.2", integer_type_node, VCMPGTSB_P, pim_ovl_16 | pim_manip_swap | pim_cr6_ge); |
| def_pim_builtin ("vec_any_ge.3", integer_type_node, VCMPGTUH_P, pim_ovl_8u_8u | pim_manip_swap | pim_cr6_ge); |
| def_pim_builtin ("vec_any_ge.4", integer_type_node, VCMPGTSH_P, pim_ovl_8 | pim_manip_swap | pim_cr6_ge); |
| def_pim_builtin ("vec_any_ge.5", integer_type_node, VCMPGEFP_P, pim_ovl_4f | pim_cr6_ne); |
| def_pim_builtin ("vec_any_ge.6", integer_type_node, VCMPGTUW_P, pim_ovl_4u_4u | pim_manip_swap | pim_cr6_ge); |
| def_pim_builtin ("vec_any_ge.7", integer_type_node, VCMPGTSW_P, pim_ovl_4 | pim_manip_swap | pim_cr6_ge); |
| |
| def_pim_builtin ("vec_any_gt", integer_type_node, VCMPGTUB_P, pim_ovl_16u_16u | pim_cr6_ne | pim_group); |
| def_pim_builtin ("vec_any_gt.2", integer_type_node, VCMPGTSB_P, pim_ovl_16 | pim_cr6_ne); |
| def_pim_builtin ("vec_any_gt.3", integer_type_node, VCMPGTUH_P, pim_ovl_8u_8u | pim_cr6_ne); |
| def_pim_builtin ("vec_any_gt.4", integer_type_node, VCMPGTSH_P, pim_ovl_8 | pim_cr6_ne); |
| def_pim_builtin ("vec_any_gt.5", integer_type_node, VCMPGTFP_P, pim_ovl_4f | pim_cr6_ne); |
| def_pim_builtin ("vec_any_gt.6", integer_type_node, VCMPGTUW_P, pim_ovl_4u_4u | pim_cr6_ne); |
| def_pim_builtin ("vec_any_gt.7", integer_type_node, VCMPGTSW_P, pim_ovl_4 | pim_cr6_ne); |
| |
| def_pim_builtin ("vec_any_le", integer_type_node, VCMPGTUB_P, pim_ovl_16u_16u | pim_cr6_ge | pim_group); |
| def_pim_builtin ("vec_any_le.2", integer_type_node, VCMPGTSB_P, pim_ovl_16 | pim_cr6_ge); |
| def_pim_builtin ("vec_any_le.3", integer_type_node, VCMPGTUH_P, pim_ovl_8u_8u | pim_cr6_ge); |
| def_pim_builtin ("vec_any_le.4", integer_type_node, VCMPGTSH_P, pim_ovl_8 | pim_cr6_ge); |
| def_pim_builtin ("vec_any_le.5", integer_type_node, VCMPGEFP_P, pim_ovl_4f | pim_manip_swap | pim_cr6_ne); |
| def_pim_builtin ("vec_any_le.6", integer_type_node, VCMPGTUW_P, pim_ovl_4u_4u | pim_cr6_ge); |
| def_pim_builtin ("vec_any_le.7", integer_type_node, VCMPGTSW_P, pim_ovl_4 | pim_cr6_ge); |
| |
| def_pim_builtin ("vec_any_lt", integer_type_node, VCMPGTUB_P, pim_ovl_16u_16u | pim_manip_swap | pim_cr6_ne | pim_group); |
| def_pim_builtin ("vec_any_lt.2", integer_type_node, VCMPGTSB_P, pim_ovl_16 | pim_manip_swap | pim_cr6_ne); |
| def_pim_builtin ("vec_any_lt.3", integer_type_node, VCMPGTUH_P, pim_ovl_8u_8u | pim_manip_swap | pim_cr6_ne); |
| def_pim_builtin ("vec_any_lt.4", integer_type_node, VCMPGTSH_P, pim_ovl_8 | pim_manip_swap | pim_cr6_ne); |
| def_pim_builtin ("vec_any_lt.5", integer_type_node, VCMPGTFP_P, pim_ovl_4f | pim_manip_swap | pim_cr6_ne); |
| def_pim_builtin ("vec_any_lt.6", integer_type_node, VCMPGTUW_P, pim_ovl_4u_4u | pim_manip_swap | pim_cr6_ne); |
| def_pim_builtin ("vec_any_lt.7", integer_type_node, VCMPGTSW_P, pim_ovl_4 | pim_manip_swap | pim_cr6_ne); |
| |
| def_pim_builtin ("vec_any_nan", integer_type_node, VCMPEQFP_P, pim_manip_dup | pim_cr6_ge | pim_group); |
| |
| def_pim_builtin ("vec_any_ne", integer_type_node, VCMPEQUB_P, pim_ovl_16 | pim_cr6_ge | pim_group); |
| def_pim_builtin ("vec_any_ne.2", integer_type_node, VCMPEQUH_P, pim_ovl_8 | pim_cr6_ge); |
| def_pim_builtin ("vec_any_ne.3", integer_type_node, VCMPEQFP_P, pim_ovl_4f | pim_cr6_ge); |
| def_pim_builtin ("vec_any_ne.4", integer_type_node, VCMPEQUW_P, pim_ovl_4 | pim_cr6_ge); |
| |
| def_pim_builtin ("vec_any_nge", integer_type_node, VCMPGEFP_P, pim_cr6_ge | pim_group); |
| |
| def_pim_builtin ("vec_any_ngt", integer_type_node, VCMPGTFP_P, pim_cr6_ge | pim_group); |
| |
| def_pim_builtin ("vec_any_nle", integer_type_node, VCMPGEFP_P, pim_manip_swap | pim_cr6_ge | pim_group); |
| |
| def_pim_builtin ("vec_any_nlt", integer_type_node, VCMPGEFP_P, pim_manip_swap | pim_cr6_ge | pim_group); |
| |
| def_pim_builtin ("vec_any_numeric", integer_type_node, VCMPEQFP_P, pim_manip_dup | pim_cr6_ne | pim_group); |
| |
| def_pim_builtin ("vec_any_out", integer_type_node, VCMPBFP_P, pim_cr6_ne | pim_group); |
| |
| gcc_assert (pim_code == ALTIVEC_PIM__LAST + 1); |
| } |
| /* APPLE LOCAL end AltiVec */ |
| |
| /* Access to the vec_init patterns. */ |
| ftype = build_function_type_list (V4SI_type_node, integer_type_node, |
| integer_type_node, integer_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_init_v4si", ftype, |
| ALTIVEC_BUILTIN_VEC_INIT_V4SI); |
| |
| ftype = build_function_type_list (V8HI_type_node, short_integer_type_node, |
| short_integer_type_node, |
| short_integer_type_node, |
| short_integer_type_node, |
| short_integer_type_node, |
| short_integer_type_node, |
| short_integer_type_node, |
| short_integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_init_v8hi", ftype, |
| ALTIVEC_BUILTIN_VEC_INIT_V8HI); |
| |
| ftype = build_function_type_list (V16QI_type_node, char_type_node, |
| char_type_node, char_type_node, |
| char_type_node, char_type_node, |
| char_type_node, char_type_node, |
| char_type_node, char_type_node, |
| char_type_node, char_type_node, |
| char_type_node, char_type_node, |
| char_type_node, char_type_node, |
| char_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_init_v16qi", ftype, |
| ALTIVEC_BUILTIN_VEC_INIT_V16QI); |
| |
| ftype = build_function_type_list (V4SF_type_node, float_type_node, |
| float_type_node, float_type_node, |
| float_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_init_v4sf", ftype, |
| ALTIVEC_BUILTIN_VEC_INIT_V4SF); |
| |
| /* Access to the vec_set patterns. */ |
| ftype = build_function_type_list (V4SI_type_node, V4SI_type_node, |
| intSI_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_set_v4si", ftype, |
| ALTIVEC_BUILTIN_VEC_SET_V4SI); |
| |
| ftype = build_function_type_list (V8HI_type_node, V8HI_type_node, |
| intHI_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_set_v8hi", ftype, |
| ALTIVEC_BUILTIN_VEC_SET_V8HI); |
| |
| ftype = build_function_type_list (V8HI_type_node, V16QI_type_node, |
| intQI_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_set_v16qi", ftype, |
| ALTIVEC_BUILTIN_VEC_SET_V16QI); |
| |
| ftype = build_function_type_list (V4SF_type_node, V4SF_type_node, |
| float_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_set_v4sf", ftype, |
| ALTIVEC_BUILTIN_VEC_SET_V4SF); |
| |
| /* Access to the vec_extract patterns. */ |
| ftype = build_function_type_list (intSI_type_node, V4SI_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ext_v4si", ftype, |
| ALTIVEC_BUILTIN_VEC_EXT_V4SI); |
| |
| ftype = build_function_type_list (intHI_type_node, V8HI_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ext_v8hi", ftype, |
| ALTIVEC_BUILTIN_VEC_EXT_V8HI); |
| |
| ftype = build_function_type_list (intQI_type_node, V16QI_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ext_v16qi", ftype, |
| ALTIVEC_BUILTIN_VEC_EXT_V16QI); |
| |
| ftype = build_function_type_list (float_type_node, V4SF_type_node, |
| integer_type_node, NULL_TREE); |
| def_builtin (MASK_ALTIVEC, "__builtin_vec_ext_v4sf", ftype, |
| ALTIVEC_BUILTIN_VEC_EXT_V4SF); |
| } |
| |
| static void |
| rs6000_common_init_builtins (void) |
| { |
| struct builtin_description *d; |
| size_t i; |
| |
| tree v4sf_ftype_v4sf_v4sf_v16qi |
| = build_function_type_list (V4SF_type_node, |
| V4SF_type_node, V4SF_type_node, |
| V16QI_type_node, NULL_TREE); |
| tree v4si_ftype_v4si_v4si_v16qi |
| = build_function_type_list (V4SI_type_node, |
| V4SI_type_node, V4SI_type_node, |
| V16QI_type_node, NULL_TREE); |
| tree v8hi_ftype_v8hi_v8hi_v16qi |
| = build_function_type_list (V8HI_type_node, |
| V8HI_type_node, V8HI_type_node, |
| V16QI_type_node, NULL_TREE); |
| tree v16qi_ftype_v16qi_v16qi_v16qi |
| = build_function_type_list (V16QI_type_node, |
| V16QI_type_node, V16QI_type_node, |
| V16QI_type_node, NULL_TREE); |
| tree v4si_ftype_int |
| = build_function_type_list (V4SI_type_node, integer_type_node, NULL_TREE); |
| tree v8hi_ftype_int |
| = build_function_type_list (V8HI_type_node, integer_type_node, NULL_TREE); |
| tree v16qi_ftype_int |
| = build_function_type_list (V16QI_type_node, integer_type_node, NULL_TREE); |
| tree v8hi_ftype_v16qi |
| = build_function_type_list (V8HI_type_node, V16QI_type_node, NULL_TREE); |
| tree v4sf_ftype_v4sf |
| = build_function_type_list (V4SF_type_node, V4SF_type_node, NULL_TREE); |
| |
| tree v2si_ftype_v2si_v2si |
| = build_function_type_list (opaque_V2SI_type_node, |
| opaque_V2SI_type_node, |
| opaque_V2SI_type_node, NULL_TREE); |
| |
| tree v2sf_ftype_v2sf_v2sf |
| = build_function_type_list (opaque_V2SF_type_node, |
| opaque_V2SF_type_node, |
| opaque_V2SF_type_node, NULL_TREE); |
| |
| tree v2si_ftype_int_int |
| = build_function_type_list (opaque_V2SI_type_node, |
| integer_type_node, integer_type_node, |
| NULL_TREE); |
| |
| tree opaque_ftype_opaque |
| = build_function_type_list (opaque_V4SI_type_node, |
| opaque_V4SI_type_node, NULL_TREE); |
| |
| tree v2si_ftype_v2si |
| = build_function_type_list (opaque_V2SI_type_node, |
| opaque_V2SI_type_node, NULL_TREE); |
| |
| tree v2sf_ftype_v2sf |
| = build_function_type_list (opaque_V2SF_type_node, |
| opaque_V2SF_type_node, NULL_TREE); |
| |
| tree v2sf_ftype_v2si |
| = build_function_type_list (opaque_V2SF_type_node, |
| opaque_V2SI_type_node, NULL_TREE); |
| |
| tree v2si_ftype_v2sf |
| = build_function_type_list (opaque_V2SI_type_node, |
| opaque_V2SF_type_node, NULL_TREE); |
| |
| tree v2si_ftype_v2si_char |
| = build_function_type_list (opaque_V2SI_type_node, |
| opaque_V2SI_type_node, |
| char_type_node, NULL_TREE); |
| |
| tree v2si_ftype_int_char |
| = build_function_type_list (opaque_V2SI_type_node, |
| integer_type_node, char_type_node, NULL_TREE); |
| |
| tree v2si_ftype_char |
| = build_function_type_list (opaque_V2SI_type_node, |
| char_type_node, NULL_TREE); |
| |
| tree int_ftype_int_int |
| = build_function_type_list (integer_type_node, |
| integer_type_node, integer_type_node, |
| NULL_TREE); |
| |
| tree opaque_ftype_opaque_opaque |
| = build_function_type_list (opaque_V4SI_type_node, |
| opaque_V4SI_type_node, opaque_V4SI_type_node, NULL_TREE); |
| tree v4si_ftype_v4si_v4si |
| = build_function_type_list (V4SI_type_node, |
| V4SI_type_node, V4SI_type_node, NULL_TREE); |
| tree v4sf_ftype_v4si_int |
| = build_function_type_list (V4SF_type_node, |
| V4SI_type_node, integer_type_node, NULL_TREE); |
| tree v4si_ftype_v4sf_int |
| = build_function_type_list (V4SI_type_node, |
| V4SF_type_node, integer_type_node, NULL_TREE); |
| tree v4si_ftype_v4si_int |
| = build_function_type_list (V4SI_type_node, |
| V4SI_type_node, integer_type_node, NULL_TREE); |
| tree v8hi_ftype_v8hi_int |
| = build_function_type_list (V8HI_type_node, |
| V8HI_type_node, integer_type_node, NULL_TREE); |
| tree v16qi_ftype_v16qi_int |
| = build_function_type_list (V16QI_type_node, |
| V16QI_type_node, integer_type_node, NULL_TREE); |
| tree v16qi_ftype_v16qi_v16qi_int |
| = build_function_type_list (V16QI_type_node, |
| V16QI_type_node, V16QI_type_node, |
| integer_type_node, NULL_TREE); |
| tree v8hi_ftype_v8hi_v8hi_int |
| = build_function_type_list (V8HI_type_node, |
| V8HI_type_node, V8HI_type_node, |
| integer_type_node, NULL_TREE); |
| tree v4si_ftype_v4si_v4si_int |
| = build_function_type_list (V4SI_type_node, |
| V4SI_type_node, V4SI_type_node, |
| integer_type_node, NULL_TREE); |
| tree v4sf_ftype_v4sf_v4sf_int |
| = build_function_type_list (V4SF_type_node, |
| V4SF_type_node, V4SF_type_node, |
| integer_type_node, NULL_TREE); |
| tree v4sf_ftype_v4sf_v4sf |
| = build_function_type_list (V4SF_type_node, |
| V4SF_type_node, V4SF_type_node, NULL_TREE); |
| tree opaque_ftype_opaque_opaque_opaque |
| = build_function_type_list (opaque_V4SI_type_node, |
| opaque_V4SI_type_node, opaque_V4SI_type_node, |
| opaque_V4SI_type_node, NULL_TREE); |
| tree v4sf_ftype_v4sf_v4sf_v4si |
| = build_function_type_list (V4SF_type_node, |
| V4SF_type_node, V4SF_type_node, |
| V4SI_type_node, NULL_TREE); |
| tree v4sf_ftype_v4sf_v4sf_v4sf |
| = build_function_type_list (V4SF_type_node, |
| V4SF_type_node, V4SF_type_node, |
| V4SF_type_node, NULL_TREE); |
| tree v4si_ftype_v4si_v4si_v4si |
| = build_function_type_list (V4SI_type_node, |
| V4SI_type_node, V4SI_type_node, |
| V4SI_type_node, NULL_TREE); |
| tree v8hi_ftype_v8hi_v8hi |
| = build_function_type_list (V8HI_type_node, |
| V8HI_type_node, V8HI_type_node, NULL_TREE); |
| tree v8hi_ftype_v8hi_v8hi_v8hi |
| = build_function_type_list (V8HI_type_node, |
| V8HI_type_node, V8HI_type_node, |
| V8HI_type_node, NULL_TREE); |
| tree v4si_ftype_v8hi_v8hi_v4si |
| = build_function_type_list (V4SI_type_node, |
| V8HI_type_node, V8HI_type_node, |
| V4SI_type_node, NULL_TREE); |
| tree v4si_ftype_v16qi_v16qi_v4si |
| = build_function_type_list (V4SI_type_node, |
| V16QI_type_node, V16QI_type_node, |
| V4SI_type_node, NULL_TREE); |
| tree v16qi_ftype_v16qi_v16qi |
| = build_function_type_list (V16QI_type_node, |
| V16QI_type_node, V16QI_type_node, NULL_TREE); |
| tree v4si_ftype_v4sf_v4sf |
| = build_function_type_list (V4SI_type_node, |
| V4SF_type_node, V4SF_type_node, NULL_TREE); |
| tree v8hi_ftype_v16qi_v16qi |
| = build_function_type_list (V8HI_type_node, |
| V16QI_type_node, V16QI_type_node, NULL_TREE); |
| tree v4si_ftype_v8hi_v8hi |
| = build_function_type_list (V4SI_type_node, |
| V8HI_type_node, V8HI_type_node, NULL_TREE); |
| tree v8hi_ftype_v4si_v4si |
| = build_function_type_list (V8HI_type_node, |
| V4SI_type_node, V4SI_type_node, NULL_TREE); |
| tree v16qi_ftype_v8hi_v8hi |
| = build_function_type_list (V16QI_type_node, |
| V8HI_type_node, V8HI_type_node, NULL_TREE); |
| tree v4si_ftype_v16qi_v4si |
| = build_function_type_list (V4SI_type_node, |
| V16QI_type_node, V4SI_type_node, NULL_TREE); |
| tree v4si_ftype_v16qi_v16qi |
| = build_function_type_list (V4SI_type_node, |
| V16QI_type_node, V16QI_type_node, NULL_TREE); |
| tree v4si_ftype_v8hi_v4si |
| = build_function_type_list (V4SI_type_node, |
| V8HI_type_node, V4SI_type_node, NULL_TREE); |
| tree v4si_ftype_v8hi |
| = build_function_type_list (V4SI_type_node, V8HI_type_node, NULL_TREE); |
| tree int_ftype_v4si_v4si |
| = build_function_type_list (integer_type_node, |
| V4SI_type_node, V4SI_type_node, NULL_TREE); |
| tree int_ftype_v4sf_v4sf |
| = build_function_type_list (integer_type_node, |
| V4SF_type_node, V4SF_type_node, NULL_TREE); |
| tree int_ftype_v16qi_v16qi |
| = build_function_type_list (integer_type_node, |
| V16QI_type_node, V16QI_type_node, NULL_TREE); |
| tree int_ftype_v8hi_v8hi |
| = build_function_type_list (integer_type_node, |
| V8HI_type_node, V8HI_type_node, NULL_TREE); |
| |
| /* Add the simple ternary operators. */ |
| d = (struct builtin_description *) bdesc_3arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_3arg); i++, d++) |
| { |
| enum machine_mode mode0, mode1, mode2, mode3; |
| tree type; |
| bool is_overloaded = d->code >= ALTIVEC_BUILTIN_OVERLOADED_FIRST |
| && d->code <= ALTIVEC_BUILTIN_OVERLOADED_LAST; |
| |
| if (is_overloaded) |
| { |
| mode0 = VOIDmode; |
| mode1 = VOIDmode; |
| mode2 = VOIDmode; |
| mode3 = VOIDmode; |
| } |
| else |
| { |
| if (d->name == 0 || d->icode == CODE_FOR_nothing) |
| continue; |
| |
| mode0 = insn_data[d->icode].operand[0].mode; |
| mode1 = insn_data[d->icode].operand[1].mode; |
| mode2 = insn_data[d->icode].operand[2].mode; |
| mode3 = insn_data[d->icode].operand[3].mode; |
| } |
| |
| /* When all four are of the same mode. */ |
| if (mode0 == mode1 && mode1 == mode2 && mode2 == mode3) |
| { |
| switch (mode0) |
| { |
| case VOIDmode: |
| type = opaque_ftype_opaque_opaque_opaque; |
| break; |
| case V4SImode: |
| type = v4si_ftype_v4si_v4si_v4si; |
| break; |
| case V4SFmode: |
| type = v4sf_ftype_v4sf_v4sf_v4sf; |
| break; |
| case V8HImode: |
| type = v8hi_ftype_v8hi_v8hi_v8hi; |
| break; |
| case V16QImode: |
| type = v16qi_ftype_v16qi_v16qi_v16qi; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else if (mode0 == mode1 && mode1 == mode2 && mode3 == V16QImode) |
| { |
| switch (mode0) |
| { |
| case V4SImode: |
| type = v4si_ftype_v4si_v4si_v16qi; |
| break; |
| case V4SFmode: |
| type = v4sf_ftype_v4sf_v4sf_v16qi; |
| break; |
| case V8HImode: |
| type = v8hi_ftype_v8hi_v8hi_v16qi; |
| break; |
| case V16QImode: |
| type = v16qi_ftype_v16qi_v16qi_v16qi; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else if (mode0 == V4SImode && mode1 == V16QImode && mode2 == V16QImode |
| && mode3 == V4SImode) |
| type = v4si_ftype_v16qi_v16qi_v4si; |
| else if (mode0 == V4SImode && mode1 == V8HImode && mode2 == V8HImode |
| && mode3 == V4SImode) |
| type = v4si_ftype_v8hi_v8hi_v4si; |
| else if (mode0 == V4SFmode && mode1 == V4SFmode && mode2 == V4SFmode |
| && mode3 == V4SImode) |
| type = v4sf_ftype_v4sf_v4sf_v4si; |
| |
| /* vchar, vchar, vchar, 4 bit literal. */ |
| else if (mode0 == V16QImode && mode1 == mode0 && mode2 == mode0 |
| && mode3 == QImode) |
| type = v16qi_ftype_v16qi_v16qi_int; |
| |
| /* vshort, vshort, vshort, 4 bit literal. */ |
| else if (mode0 == V8HImode && mode1 == mode0 && mode2 == mode0 |
| && mode3 == QImode) |
| type = v8hi_ftype_v8hi_v8hi_int; |
| |
| /* vint, vint, vint, 4 bit literal. */ |
| else if (mode0 == V4SImode && mode1 == mode0 && mode2 == mode0 |
| && mode3 == QImode) |
| type = v4si_ftype_v4si_v4si_int; |
| |
| /* vfloat, vfloat, vfloat, 4 bit literal. */ |
| else if (mode0 == V4SFmode && mode1 == mode0 && mode2 == mode0 |
| && mode3 == QImode) |
| type = v4sf_ftype_v4sf_v4sf_int; |
| |
| else |
| gcc_unreachable (); |
| |
| def_builtin (d->mask, d->name, type, d->code); |
| } |
| |
| /* Add the simple binary operators. */ |
| d = (struct builtin_description *) bdesc_2arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_2arg); i++, d++) |
| { |
| enum machine_mode mode0, mode1, mode2; |
| tree type; |
| bool is_overloaded = d->code >= ALTIVEC_BUILTIN_OVERLOADED_FIRST |
| && d->code <= ALTIVEC_BUILTIN_OVERLOADED_LAST; |
| |
| if (is_overloaded) |
| { |
| mode0 = VOIDmode; |
| mode1 = VOIDmode; |
| mode2 = VOIDmode; |
| } |
| else |
| { |
| if (d->name == 0 || d->icode == CODE_FOR_nothing) |
| continue; |
| |
| mode0 = insn_data[d->icode].operand[0].mode; |
| mode1 = insn_data[d->icode].operand[1].mode; |
| mode2 = insn_data[d->icode].operand[2].mode; |
| } |
| |
| /* When all three operands are of the same mode. */ |
| if (mode0 == mode1 && mode1 == mode2) |
| { |
| switch (mode0) |
| { |
| case VOIDmode: |
| type = opaque_ftype_opaque_opaque; |
| break; |
| case V4SFmode: |
| type = v4sf_ftype_v4sf_v4sf; |
| break; |
| case V4SImode: |
| type = v4si_ftype_v4si_v4si; |
| break; |
| case V16QImode: |
| type = v16qi_ftype_v16qi_v16qi; |
| break; |
| case V8HImode: |
| type = v8hi_ftype_v8hi_v8hi; |
| break; |
| case V2SImode: |
| type = v2si_ftype_v2si_v2si; |
| break; |
| case V2SFmode: |
| type = v2sf_ftype_v2sf_v2sf; |
| break; |
| case SImode: |
| type = int_ftype_int_int; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* A few other combos we really don't want to do manually. */ |
| |
| /* vint, vfloat, vfloat. */ |
| else if (mode0 == V4SImode && mode1 == V4SFmode && mode2 == V4SFmode) |
| type = v4si_ftype_v4sf_v4sf; |
| |
| /* vshort, vchar, vchar. */ |
| else if (mode0 == V8HImode && mode1 == V16QImode && mode2 == V16QImode) |
| type = v8hi_ftype_v16qi_v16qi; |
| |
| /* vint, vshort, vshort. */ |
| else if (mode0 == V4SImode && mode1 == V8HImode && mode2 == V8HImode) |
| type = v4si_ftype_v8hi_v8hi; |
| |
| /* vshort, vint, vint. */ |
| else if (mode0 == V8HImode && mode1 == V4SImode && mode2 == V4SImode) |
| type = v8hi_ftype_v4si_v4si; |
| |
| /* vchar, vshort, vshort. */ |
| else if (mode0 == V16QImode && mode1 == V8HImode && mode2 == V8HImode) |
| type = v16qi_ftype_v8hi_v8hi; |
| |
| /* vint, vchar, vint. */ |
| else if (mode0 == V4SImode && mode1 == V16QImode && mode2 == V4SImode) |
| type = v4si_ftype_v16qi_v4si; |
| |
| /* vint, vchar, vchar. */ |
| else if (mode0 == V4SImode && mode1 == V16QImode && mode2 == V16QImode) |
| type = v4si_ftype_v16qi_v16qi; |
| |
| /* vint, vshort, vint. */ |
| else if (mode0 == V4SImode && mode1 == V8HImode && mode2 == V4SImode) |
| type = v4si_ftype_v8hi_v4si; |
| |
| /* vint, vint, 5 bit literal. */ |
| else if (mode0 == V4SImode && mode1 == V4SImode && mode2 == QImode) |
| type = v4si_ftype_v4si_int; |
| |
| /* vshort, vshort, 5 bit literal. */ |
| else if (mode0 == V8HImode && mode1 == V8HImode && mode2 == QImode) |
| type = v8hi_ftype_v8hi_int; |
| |
| /* vchar, vchar, 5 bit literal. */ |
| else if (mode0 == V16QImode && mode1 == V16QImode && mode2 == QImode) |
| type = v16qi_ftype_v16qi_int; |
| |
| /* vfloat, vint, 5 bit literal. */ |
| else if (mode0 == V4SFmode && mode1 == V4SImode && mode2 == QImode) |
| type = v4sf_ftype_v4si_int; |
| |
| /* vint, vfloat, 5 bit literal. */ |
| else if (mode0 == V4SImode && mode1 == V4SFmode && mode2 == QImode) |
| type = v4si_ftype_v4sf_int; |
| |
| else if (mode0 == V2SImode && mode1 == SImode && mode2 == SImode) |
| type = v2si_ftype_int_int; |
| |
| else if (mode0 == V2SImode && mode1 == V2SImode && mode2 == QImode) |
| type = v2si_ftype_v2si_char; |
| |
| else if (mode0 == V2SImode && mode1 == SImode && mode2 == QImode) |
| type = v2si_ftype_int_char; |
| |
| else |
| { |
| /* int, x, x. */ |
| gcc_assert (mode0 == SImode); |
| switch (mode1) |
| { |
| case V4SImode: |
| type = int_ftype_v4si_v4si; |
| break; |
| case V4SFmode: |
| type = int_ftype_v4sf_v4sf; |
| break; |
| case V16QImode: |
| type = int_ftype_v16qi_v16qi; |
| break; |
| case V8HImode: |
| type = int_ftype_v8hi_v8hi; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| def_builtin (d->mask, d->name, type, d->code); |
| } |
| |
| /* Add the simple unary operators. */ |
| d = (struct builtin_description *) bdesc_1arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_1arg); i++, d++) |
| { |
| enum machine_mode mode0, mode1; |
| tree type; |
| bool is_overloaded = d->code >= ALTIVEC_BUILTIN_OVERLOADED_FIRST |
| && d->code <= ALTIVEC_BUILTIN_OVERLOADED_LAST; |
| |
| if (is_overloaded) |
| { |
| mode0 = VOIDmode; |
| mode1 = VOIDmode; |
| } |
| else |
| { |
| if (d->name == 0 || d->icode == CODE_FOR_nothing) |
| continue; |
| |
| mode0 = insn_data[d->icode].operand[0].mode; |
| mode1 = insn_data[d->icode].operand[1].mode; |
| } |
| |
| if (mode0 == V4SImode && mode1 == QImode) |
| type = v4si_ftype_int; |
| else if (mode0 == V8HImode && mode1 == QImode) |
| type = v8hi_ftype_int; |
| else if (mode0 == V16QImode && mode1 == QImode) |
| type = v16qi_ftype_int; |
| else if (mode0 == VOIDmode && mode1 == VOIDmode) |
| type = opaque_ftype_opaque; |
| else if (mode0 == V4SFmode && mode1 == V4SFmode) |
| type = v4sf_ftype_v4sf; |
| else if (mode0 == V8HImode && mode1 == V16QImode) |
| type = v8hi_ftype_v16qi; |
| else if (mode0 == V4SImode && mode1 == V8HImode) |
| type = v4si_ftype_v8hi; |
| else if (mode0 == V2SImode && mode1 == V2SImode) |
| type = v2si_ftype_v2si; |
| else if (mode0 == V2SFmode && mode1 == V2SFmode) |
| type = v2sf_ftype_v2sf; |
| else if (mode0 == V2SFmode && mode1 == V2SImode) |
| type = v2sf_ftype_v2si; |
| else if (mode0 == V2SImode && mode1 == V2SFmode) |
| type = v2si_ftype_v2sf; |
| else if (mode0 == V2SImode && mode1 == QImode) |
| type = v2si_ftype_char; |
| else |
| gcc_unreachable (); |
| |
| def_builtin (d->mask, d->name, type, d->code); |
| } |
| } |
| |
| static void |
| rs6000_init_libfuncs (void) |
| { |
| if (DEFAULT_ABI != ABI_V4 && TARGET_XCOFF |
| && !TARGET_POWER2 && !TARGET_POWERPC) |
| { |
| /* AIX library routines for float->int conversion. */ |
| set_conv_libfunc (sfix_optab, SImode, DFmode, "__itrunc"); |
| set_conv_libfunc (ufix_optab, SImode, DFmode, "__uitrunc"); |
| set_conv_libfunc (sfix_optab, SImode, TFmode, "_qitrunc"); |
| set_conv_libfunc (ufix_optab, SImode, TFmode, "_quitrunc"); |
| } |
| |
| if (!TARGET_IEEEQUAD) |
| /* AIX/Darwin/64-bit Linux quad floating point routines. */ |
| if (!TARGET_XL_COMPAT) |
| { |
| set_optab_libfunc (add_optab, TFmode, "__gcc_qadd"); |
| set_optab_libfunc (sub_optab, TFmode, "__gcc_qsub"); |
| set_optab_libfunc (smul_optab, TFmode, "__gcc_qmul"); |
| set_optab_libfunc (sdiv_optab, TFmode, "__gcc_qdiv"); |
| |
| if (TARGET_SOFT_FLOAT) |
| { |
| set_optab_libfunc (neg_optab, TFmode, "__gcc_qneg"); |
| set_optab_libfunc (eq_optab, TFmode, "__gcc_qeq"); |
| set_optab_libfunc (ne_optab, TFmode, "__gcc_qne"); |
| set_optab_libfunc (gt_optab, TFmode, "__gcc_qgt"); |
| set_optab_libfunc (ge_optab, TFmode, "__gcc_qge"); |
| set_optab_libfunc (lt_optab, TFmode, "__gcc_qlt"); |
| set_optab_libfunc (le_optab, TFmode, "__gcc_qle"); |
| set_optab_libfunc (unord_optab, TFmode, "__gcc_qunord"); |
| |
| set_conv_libfunc (sext_optab, TFmode, SFmode, "__gcc_stoq"); |
| set_conv_libfunc (sext_optab, TFmode, DFmode, "__gcc_dtoq"); |
| set_conv_libfunc (trunc_optab, SFmode, TFmode, "__gcc_qtos"); |
| set_conv_libfunc (trunc_optab, DFmode, TFmode, "__gcc_qtod"); |
| set_conv_libfunc (sfix_optab, SImode, TFmode, "__gcc_qtoi"); |
| set_conv_libfunc (ufix_optab, SImode, TFmode, "__gcc_qtou"); |
| set_conv_libfunc (sfloat_optab, TFmode, SImode, "__gcc_itoq"); |
| set_conv_libfunc (ufloat_optab, TFmode, SImode, "__gcc_utoq"); |
| } |
| } |
| else |
| { |
| set_optab_libfunc (add_optab, TFmode, "_xlqadd"); |
| set_optab_libfunc (sub_optab, TFmode, "_xlqsub"); |
| set_optab_libfunc (smul_optab, TFmode, "_xlqmul"); |
| set_optab_libfunc (sdiv_optab, TFmode, "_xlqdiv"); |
| } |
| else |
| { |
| /* 32-bit SVR4 quad floating point routines. */ |
| |
| set_optab_libfunc (add_optab, TFmode, "_q_add"); |
| set_optab_libfunc (sub_optab, TFmode, "_q_sub"); |
| set_optab_libfunc (neg_optab, TFmode, "_q_neg"); |
| set_optab_libfunc (smul_optab, TFmode, "_q_mul"); |
| set_optab_libfunc (sdiv_optab, TFmode, "_q_div"); |
| if (TARGET_PPC_GPOPT || TARGET_POWER2) |
| set_optab_libfunc (sqrt_optab, TFmode, "_q_sqrt"); |
| |
| set_optab_libfunc (eq_optab, TFmode, "_q_feq"); |
| set_optab_libfunc (ne_optab, TFmode, "_q_fne"); |
| set_optab_libfunc (gt_optab, TFmode, "_q_fgt"); |
| set_optab_libfunc (ge_optab, TFmode, "_q_fge"); |
| set_optab_libfunc (lt_optab, TFmode, "_q_flt"); |
| set_optab_libfunc (le_optab, TFmode, "_q_fle"); |
| |
| set_conv_libfunc (sext_optab, TFmode, SFmode, "_q_stoq"); |
| set_conv_libfunc (sext_optab, TFmode, DFmode, "_q_dtoq"); |
| set_conv_libfunc (trunc_optab, SFmode, TFmode, "_q_qtos"); |
| set_conv_libfunc (trunc_optab, DFmode, TFmode, "_q_qtod"); |
| set_conv_libfunc (sfix_optab, SImode, TFmode, "_q_qtoi"); |
| set_conv_libfunc (ufix_optab, SImode, TFmode, "_q_qtou"); |
| set_conv_libfunc (sfloat_optab, TFmode, SImode, "_q_itoq"); |
| set_conv_libfunc (ufloat_optab, TFmode, SImode, "_q_utoq"); |
| } |
| } |
| |
| |
| /* Expand a block clear operation, and return 1 if successful. Return 0 |
| if we should let the compiler generate normal code. |
| |
| operands[0] is the destination |
| operands[1] is the length |
| operands[3] is the alignment */ |
| |
| int |
| expand_block_clear (rtx operands[]) |
| { |
| rtx orig_dest = operands[0]; |
| rtx bytes_rtx = operands[1]; |
| rtx align_rtx = operands[3]; |
| bool constp = (GET_CODE (bytes_rtx) == CONST_INT); |
| HOST_WIDE_INT align; |
| HOST_WIDE_INT bytes; |
| int offset; |
| int clear_bytes; |
| int clear_step; |
| /* APPLE LOCAL Altivec 3840704 */ |
| bool cpu_altivec = TARGET_ALTIVEC && ! flag_disable_opts_for_faltivec; |
| |
| /* If this is not a fixed size move, just call memcpy */ |
| if (! constp) |
| return 0; |
| |
| /* This must be a fixed size alignment */ |
| gcc_assert (GET_CODE (align_rtx) == CONST_INT); |
| align = INTVAL (align_rtx) * BITS_PER_UNIT; |
| |
| /* Anything to clear? */ |
| bytes = INTVAL (bytes_rtx); |
| if (bytes <= 0) |
| return 1; |
| |
| /* Use the builtin memset after a point, to avoid huge code bloat. |
| When optimize_size, avoid any significant code bloat; calling |
| memset is about 4 instructions, so allow for one instruction to |
| load zero and three to do clearing. */ |
| /* APPLE LOCAL Altivec 3840704 */ |
| if (cpu_altivec && align >= 128) |
| clear_step = 16; |
| else if (TARGET_POWERPC64 && align >= 32) |
| clear_step = 8; |
| else |
| clear_step = 4; |
| |
| if (optimize_size && bytes > 3 * clear_step) |
| return 0; |
| if (! optimize_size && bytes > 8 * clear_step) |
| return 0; |
| |
| for (offset = 0; bytes > 0; offset += clear_bytes, bytes -= clear_bytes) |
| { |
| enum machine_mode mode = BLKmode; |
| rtx dest; |
| |
| /* APPLE LOCAL Altivec 3840704 */ |
| if (bytes >= 16 && cpu_altivec && align >= 128) |
| { |
| clear_bytes = 16; |
| mode = V4SImode; |
| } |
| else if (bytes >= 8 && TARGET_POWERPC64 |
| /* 64-bit loads and stores require word-aligned |
| displacements. */ |
| && (align >= 64 || (!STRICT_ALIGNMENT && align >= 32))) |
| { |
| clear_bytes = 8; |
| mode = DImode; |
| } |
| else if (bytes >= 4 && (align >= 32 || !STRICT_ALIGNMENT)) |
| { /* move 4 bytes */ |
| clear_bytes = 4; |
| mode = SImode; |
| } |
| else if (bytes >= 2 && (align >= 16 || !STRICT_ALIGNMENT)) |
| { /* move 2 bytes */ |
| clear_bytes = 2; |
| mode = HImode; |
| } |
| else /* move 1 byte at a time */ |
| { |
| clear_bytes = 1; |
| mode = QImode; |
| } |
| |
| dest = adjust_address (orig_dest, mode, offset); |
| |
| emit_move_insn (dest, CONST0_RTX (mode)); |
| } |
| |
| return 1; |
| } |
| |
| |
| /* Expand a block move operation, and return 1 if successful. Return 0 |
| if we should let the compiler generate normal code. |
| |
| operands[0] is the destination |
| operands[1] is the source |
| operands[2] is the length |
| operands[3] is the alignment */ |
| |
| #define MAX_MOVE_REG 4 |
| |
| int |
| expand_block_move (rtx operands[]) |
| { |
| rtx orig_dest = operands[0]; |
| rtx orig_src = operands[1]; |
| rtx bytes_rtx = operands[2]; |
| rtx align_rtx = operands[3]; |
| int constp = (GET_CODE (bytes_rtx) == CONST_INT); |
| int align; |
| int bytes; |
| int offset; |
| int move_bytes; |
| rtx stores[MAX_MOVE_REG]; |
| int num_reg = 0; |
| |
| /* If this is not a fixed size move, just call memcpy */ |
| if (! constp) |
| return 0; |
| |
| /* This must be a fixed size alignment */ |
| gcc_assert (GET_CODE (align_rtx) == CONST_INT); |
| align = INTVAL (align_rtx) * BITS_PER_UNIT; |
| |
| /* Anything to move? */ |
| bytes = INTVAL (bytes_rtx); |
| if (bytes <= 0) |
| return 1; |
| |
| /* store_one_arg depends on expand_block_move to handle at least the size of |
| reg_parm_stack_space. */ |
| if (bytes > (TARGET_POWERPC64 ? 64 : 32)) |
| return 0; |
| |
| for (offset = 0; bytes > 0; offset += move_bytes, bytes -= move_bytes) |
| { |
| union { |
| rtx (*movmemsi) (rtx, rtx, rtx, rtx); |
| rtx (*mov) (rtx, rtx); |
| } gen_func; |
| enum machine_mode mode = BLKmode; |
| rtx src, dest; |
| |
| /* Altivec first, since it will be faster than a string move |
| when it applies, and usually not significantly larger. */ |
| /* APPLE LOCAL begin Altivec 3840704 */ |
| if (TARGET_ALTIVEC && ! flag_disable_opts_for_faltivec |
| && bytes >= 16 && align >= 128) |
| /* APPLE LOCAL end Altivec 3840704 */ |
| { |
| move_bytes = 16; |
| mode = V4SImode; |
| gen_func.mov = gen_movv4si; |
| } |
| else if (TARGET_STRING |
| && bytes > 24 /* move up to 32 bytes at a time */ |
| && ! fixed_regs[5] |
| && ! fixed_regs[6] |
| && ! fixed_regs[7] |
| && ! fixed_regs[8] |
| && ! fixed_regs[9] |
| && ! fixed_regs[10] |
| && ! fixed_regs[11] |
| && ! fixed_regs[12]) |
| { |
| move_bytes = (bytes > 32) ? 32 : bytes; |
| gen_func.movmemsi = gen_movmemsi_8reg; |
| } |
| else if (TARGET_STRING |
| && bytes > 16 /* move up to 24 bytes at a time */ |
| && ! fixed_regs[5] |
| && ! fixed_regs[6] |
| && ! fixed_regs[7] |
| && ! fixed_regs[8] |
| && ! fixed_regs[9] |
| && ! fixed_regs[10]) |
| { |
| move_bytes = (bytes > 24) ? 24 : bytes; |
| gen_func.movmemsi = gen_movmemsi_6reg; |
| } |
| else if (TARGET_STRING |
| && bytes > 8 /* move up to 16 bytes at a time */ |
| && ! fixed_regs[5] |
| && ! fixed_regs[6] |
| && ! fixed_regs[7] |
| && ! fixed_regs[8]) |
| { |
| move_bytes = (bytes > 16) ? 16 : bytes; |
| gen_func.movmemsi = gen_movmemsi_4reg; |
| } |
| else if (bytes >= 8 && TARGET_POWERPC64 |
| /* 64-bit loads and stores require word-aligned |
| displacements. */ |
| && (align >= 64 || (!STRICT_ALIGNMENT && align >= 32))) |
| { |
| move_bytes = 8; |
| mode = DImode; |
| gen_func.mov = gen_movdi; |
| } |
| else if (TARGET_STRING && bytes > 4 && !TARGET_POWERPC64) |
| { /* move up to 8 bytes at a time */ |
| move_bytes = (bytes > 8) ? 8 : bytes; |
| gen_func.movmemsi = gen_movmemsi_2reg; |
| } |
| else if (bytes >= 4 && (align >= 32 || !STRICT_ALIGNMENT)) |
| { /* move 4 bytes */ |
| move_bytes = 4; |
| mode = SImode; |
| gen_func.mov = gen_movsi; |
| } |
| else if (bytes >= 2 && (align >= 16 || !STRICT_ALIGNMENT)) |
| { /* move 2 bytes */ |
| move_bytes = 2; |
| mode = HImode; |
| gen_func.mov = gen_movhi; |
| } |
| else if (TARGET_STRING && bytes > 1) |
| { /* move up to 4 bytes at a time */ |
| move_bytes = (bytes > 4) ? 4 : bytes; |
| gen_func.movmemsi = gen_movmemsi_1reg; |
| } |
| else /* move 1 byte at a time */ |
| { |
| move_bytes = 1; |
| mode = QImode; |
| gen_func.mov = gen_movqi; |
| } |
| |
| src = adjust_address (orig_src, mode, offset); |
| dest = adjust_address (orig_dest, mode, offset); |
| |
| if (mode != BLKmode) |
| { |
| rtx tmp_reg = gen_reg_rtx (mode); |
| |
| emit_insn ((*gen_func.mov) (tmp_reg, src)); |
| stores[num_reg++] = (*gen_func.mov) (dest, tmp_reg); |
| } |
| |
| if (mode == BLKmode || num_reg >= MAX_MOVE_REG || bytes == move_bytes) |
| { |
| int i; |
| for (i = 0; i < num_reg; i++) |
| emit_insn (stores[i]); |
| num_reg = 0; |
| } |
| |
| if (mode == BLKmode) |
| { |
| /* Move the address into scratch registers. The movmemsi |
| patterns require zero offset. */ |
| if (!REG_P (XEXP (src, 0))) |
| { |
| rtx src_reg = copy_addr_to_reg (XEXP (src, 0)); |
| src = replace_equiv_address (src, src_reg); |
| } |
| set_mem_size (src, GEN_INT (move_bytes)); |
| |
| if (!REG_P (XEXP (dest, 0))) |
| { |
| rtx dest_reg = copy_addr_to_reg (XEXP (dest, 0)); |
| dest = replace_equiv_address (dest, dest_reg); |
| } |
| set_mem_size (dest, GEN_INT (move_bytes)); |
| |
| emit_insn ((*gen_func.movmemsi) (dest, src, |
| GEN_INT (move_bytes & 31), |
| align_rtx)); |
| } |
| } |
| |
| return 1; |
| } |
| |
| |
| /* Return a string to perform a load_multiple operation. |
| operands[0] is the vector. |
| operands[1] is the source address. |
| operands[2] is the first destination register. */ |
| |
| const char * |
| rs6000_output_load_multiple (rtx operands[3]) |
| { |
| /* We have to handle the case where the pseudo used to contain the address |
| is assigned to one of the output registers. */ |
| int i, j; |
| int words = XVECLEN (operands[0], 0); |
| rtx xop[10]; |
| |
| if (XVECLEN (operands[0], 0) == 1) |
| return "{l|lwz} %2,0(%1)"; |
| |
| for (i = 0; i < words; i++) |
| if (refers_to_regno_p (REGNO (operands[2]) + i, |
| REGNO (operands[2]) + i + 1, operands[1], 0)) |
| { |
| if (i == words-1) |
| { |
| xop[0] = GEN_INT (4 * (words-1)); |
| xop[1] = operands[1]; |
| xop[2] = operands[2]; |
| output_asm_insn ("{lsi|lswi} %2,%1,%0\n\t{l|lwz} %1,%0(%1)", xop); |
| return ""; |
| } |
| else if (i == 0) |
| { |
| xop[0] = GEN_INT (4 * (words-1)); |
| xop[1] = operands[1]; |
| xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + 1); |
| output_asm_insn ("{cal %1,4(%1)|addi %1,%1,4}\n\t{lsi|lswi} %2,%1,%0\n\t{l|lwz} %1,-4(%1)", xop); |
| return ""; |
| } |
| else |
| { |
| for (j = 0; j < words; j++) |
| if (j != i) |
| { |
| xop[0] = GEN_INT (j * 4); |
| xop[1] = operands[1]; |
| xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + j); |
| output_asm_insn ("{l|lwz} %2,%0(%1)", xop); |
| } |
| xop[0] = GEN_INT (i * 4); |
| xop[1] = operands[1]; |
| output_asm_insn ("{l|lwz} %1,%0(%1)", xop); |
| return ""; |
| } |
| } |
| |
| return "{lsi|lswi} %2,%1,%N0"; |
| } |
| |
| |
| /* A validation routine: say whether CODE, a condition code, and MODE |
| match. The other alternatives either don't make sense or should |
| never be generated. */ |
| |
| void |
| validate_condition_mode (enum rtx_code code, enum machine_mode mode) |
| { |
| gcc_assert ((GET_RTX_CLASS (code) == RTX_COMPARE |
| || GET_RTX_CLASS (code) == RTX_COMM_COMPARE) |
| && GET_MODE_CLASS (mode) == MODE_CC); |
| |
| /* These don't make sense. */ |
| gcc_assert ((code != GT && code != LT && code != GE && code != LE) |
| || mode != CCUNSmode); |
| |
| gcc_assert ((code != GTU && code != LTU && code != GEU && code != LEU) |
| || mode == CCUNSmode); |
| |
| gcc_assert (mode == CCFPmode |
| || (code != ORDERED && code != UNORDERED |
| && code != UNEQ && code != LTGT |
| && code != UNGT && code != UNLT |
| && code != UNGE && code != UNLE)); |
| |
| /* These should never be generated except for |
| flag_finite_math_only. */ |
| gcc_assert (mode != CCFPmode |
| || flag_finite_math_only |
| || (code != LE && code != GE |
| && code != UNEQ && code != LTGT |
| && code != UNGT && code != UNLT)); |
| |
| /* These are invalid; the information is not there. */ |
| gcc_assert (mode != CCEQmode || code == EQ || code == NE); |
| } |
| |
| |
| /* Return 1 if ANDOP is a mask that has no bits on that are not in the |
| mask required to convert the result of a rotate insn into a shift |
| left insn of SHIFTOP bits. Both are known to be SImode CONST_INT. */ |
| |
| int |
| includes_lshift_p (rtx shiftop, rtx andop) |
| { |
| unsigned HOST_WIDE_INT shift_mask = ~(unsigned HOST_WIDE_INT) 0; |
| |
| shift_mask <<= INTVAL (shiftop); |
| |
| return (INTVAL (andop) & 0xffffffff & ~shift_mask) == 0; |
| } |
| |
| /* Similar, but for right shift. */ |
| |
| int |
| includes_rshift_p (rtx shiftop, rtx andop) |
| { |
| unsigned HOST_WIDE_INT shift_mask = ~(unsigned HOST_WIDE_INT) 0; |
| |
| shift_mask >>= INTVAL (shiftop); |
| |
| return (INTVAL (andop) & 0xffffffff & ~shift_mask) == 0; |
| } |
| |
| /* Return 1 if ANDOP is a mask suitable for use with an rldic insn |
| to perform a left shift. It must have exactly SHIFTOP least |
| significant 0's, then one or more 1's, then zero or more 0's. */ |
| |
| int |
| includes_rldic_lshift_p (rtx shiftop, rtx andop) |
| { |
| if (GET_CODE (andop) == CONST_INT) |
| { |
| HOST_WIDE_INT c, lsb, shift_mask; |
| |
| c = INTVAL (andop); |
| if (c == 0 || c == ~0) |
| return 0; |
| |
| shift_mask = ~0; |
| shift_mask <<= INTVAL (shiftop); |
| |
| /* Find the least significant one bit. */ |
| lsb = c & -c; |
| |
| /* It must coincide with the LSB of the shift mask. */ |
| if (-lsb != shift_mask) |
| return 0; |
| |
| /* Invert to look for the next transition (if any). */ |
| c = ~c; |
| |
| /* Remove the low group of ones (originally low group of zeros). */ |
| c &= -lsb; |
| |
| /* Again find the lsb, and check we have all 1's above. */ |
| lsb = c & -c; |
| return c == -lsb; |
| } |
| else if (GET_CODE (andop) == CONST_DOUBLE |
| && (GET_MODE (andop) == VOIDmode || GET_MODE (andop) == DImode)) |
| { |
| HOST_WIDE_INT low, high, lsb; |
| HOST_WIDE_INT shift_mask_low, shift_mask_high; |
| |
| low = CONST_DOUBLE_LOW (andop); |
| if (HOST_BITS_PER_WIDE_INT < 64) |
| high = CONST_DOUBLE_HIGH (andop); |
| |
| if ((low == 0 && (HOST_BITS_PER_WIDE_INT >= 64 || high == 0)) |
| || (low == ~0 && (HOST_BITS_PER_WIDE_INT >= 64 || high == ~0))) |
| return 0; |
| |
| if (HOST_BITS_PER_WIDE_INT < 64 && low == 0) |
| { |
| shift_mask_high = ~0; |
| if (INTVAL (shiftop) > 32) |
| shift_mask_high <<= INTVAL (shiftop) - 32; |
| |
| lsb = high & -high; |
| |
| if (-lsb != shift_mask_high || INTVAL (shiftop) < 32) |
| return 0; |
| |
| high = ~high; |
| high &= -lsb; |
| |
| lsb = high & -high; |
| return high == -lsb; |
| } |
| |
| shift_mask_low = ~0; |
| shift_mask_low <<= INTVAL (shiftop); |
| |
| lsb = low & -low; |
| |
| if (-lsb != shift_mask_low) |
| return 0; |
| |
| if (HOST_BITS_PER_WIDE_INT < 64) |
| high = ~high; |
| low = ~low; |
| low &= -lsb; |
| |
| if (HOST_BITS_PER_WIDE_INT < 64 && low == 0) |
| { |
| lsb = high & -high; |
| return high == -lsb; |
| } |
| |
| lsb = low & -low; |
| return low == -lsb && (HOST_BITS_PER_WIDE_INT >= 64 || high == ~0); |
| } |
| else |
| return 0; |
| } |
| |
| /* Return 1 if ANDOP is a mask suitable for use with an rldicr insn |
| to perform a left shift. It must have SHIFTOP or more least |
| significant 0's, with the remainder of the word 1's. */ |
| |
| int |
| includes_rldicr_lshift_p (rtx shiftop, rtx andop) |
| { |
| if (GET_CODE (andop) == CONST_INT) |
| { |
| HOST_WIDE_INT c, lsb, shift_mask; |
| |
| shift_mask = ~0; |
| shift_mask <<= INTVAL (shiftop); |
| c = INTVAL (andop); |
| |
| /* Find the least significant one bit. */ |
| lsb = c & -c; |
| |
| /* It must be covered by the shift mask. |
| This test also rejects c == 0. */ |
| if ((lsb & shift_mask) == 0) |
| return 0; |
| |
| /* Check we have all 1's above the transition, and reject all 1's. */ |
| return c == -lsb && lsb != 1; |
| } |
| else if (GET_CODE (andop) == CONST_DOUBLE |
| && (GET_MODE (andop) == VOIDmode || GET_MODE (andop) == DImode)) |
| { |
| HOST_WIDE_INT low, lsb, shift_mask_low; |
| |
| low = CONST_DOUBLE_LOW (andop); |
| |
| if (HOST_BITS_PER_WIDE_INT < 64) |
| { |
| HOST_WIDE_INT high, shift_mask_high; |
| |
| high = CONST_DOUBLE_HIGH (andop); |
| |
| if (low == 0) |
| { |
| shift_mask_high = ~0; |
| if (INTVAL (shiftop) > 32) |
| shift_mask_high <<= INTVAL (shiftop) - 32; |
| |
| lsb = high & -high; |
| |
| if ((lsb & shift_mask_high) == 0) |
| return 0; |
| |
| return high == -lsb; |
| } |
| if (high != ~0) |
| return 0; |
| } |
| |
| shift_mask_low = ~0; |
| shift_mask_low <<= INTVAL (shiftop); |
| |
| lsb = low & -low; |
| |
| if ((lsb & shift_mask_low) == 0) |
| return 0; |
| |
| return low == -lsb && lsb != 1; |
| } |
| else |
| return 0; |
| } |
| |
| /* Return 1 if operands will generate a valid arguments to rlwimi |
| instruction for insert with right shift in 64-bit mode. The mask may |
| not start on the first bit or stop on the last bit because wrap-around |
| effects of instruction do not correspond to semantics of RTL insn. */ |
| |
| int |
| insvdi_rshift_rlwimi_p (rtx sizeop, rtx startop, rtx shiftop) |
| { |
| if (INTVAL (startop) > 32 |
| && INTVAL (startop) < 64 |
| && INTVAL (sizeop) > 1 |
| && INTVAL (sizeop) + INTVAL (startop) < 64 |
| && INTVAL (shiftop) > 0 |
| && INTVAL (sizeop) + INTVAL (shiftop) < 32 |
| && (64 - (INTVAL (shiftop) & 63)) >= INTVAL (sizeop)) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Return 1 if REGNO (reg1) == REGNO (reg2) - 1 making them candidates |
| for lfq and stfq insns iff the registers are hard registers. */ |
| |
| int |
| registers_ok_for_quad_peep (rtx reg1, rtx reg2) |
| { |
| /* We might have been passed a SUBREG. */ |
| if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG) |
| return 0; |
| |
| /* We might have been passed non floating point registers. */ |
| if (!FP_REGNO_P (REGNO (reg1)) |
| || !FP_REGNO_P (REGNO (reg2))) |
| return 0; |
| |
| return (REGNO (reg1) == REGNO (reg2) - 1); |
| } |
| |
| /* Return 1 if addr1 and addr2 are suitable for lfq or stfq insn. |
| addr1 and addr2 must be in consecutive memory locations |
| (addr2 == addr1 + 8). */ |
| |
| int |
| mems_ok_for_quad_peep (rtx mem1, rtx mem2) |
| { |
| rtx addr1, addr2; |
| unsigned int reg1, reg2; |
| int offset1, offset2; |
| |
| /* The mems cannot be volatile. */ |
| if (MEM_VOLATILE_P (mem1) || MEM_VOLATILE_P (mem2)) |
| return 0; |
| |
| addr1 = XEXP (mem1, 0); |
| addr2 = XEXP (mem2, 0); |
| |
| /* Extract an offset (if used) from the first addr. */ |
| if (GET_CODE (addr1) == PLUS) |
| { |
| /* If not a REG, return zero. */ |
| if (GET_CODE (XEXP (addr1, 0)) != REG) |
| return 0; |
| else |
| { |
| reg1 = REGNO (XEXP (addr1, 0)); |
| /* The offset must be constant! */ |
| if (GET_CODE (XEXP (addr1, 1)) != CONST_INT) |
| return 0; |
| offset1 = INTVAL (XEXP (addr1, 1)); |
| } |
| } |
| else if (GET_CODE (addr1) != REG) |
| return 0; |
| else |
| { |
| reg1 = REGNO (addr1); |
| /* This was a simple (mem (reg)) expression. Offset is 0. */ |
| offset1 = 0; |
| } |
| |
| /* And now for the second addr. */ |
| if (GET_CODE (addr2) == PLUS) |
| { |
| /* If not a REG, return zero. */ |
| if (GET_CODE (XEXP (addr2, 0)) != REG) |
| return 0; |
| else |
| { |
| reg2 = REGNO (XEXP (addr2, 0)); |
| /* The offset must be constant. */ |
| if (GET_CODE (XEXP (addr2, 1)) != CONST_INT) |
| return 0; |
| offset2 = INTVAL (XEXP (addr2, 1)); |
| } |
| } |
| else if (GET_CODE (addr2) != REG) |
| return 0; |
| else |
| { |
| reg2 = REGNO (addr2); |
| /* This was a simple (mem (reg)) expression. Offset is 0. */ |
| offset2 = 0; |
| } |
| |
| /* Both of these must have the same base register. */ |
| if (reg1 != reg2) |
| return 0; |
| |
| /* The offset for the second addr must be 8 more than the first addr. */ |
| if (offset2 != offset1 + 8) |
| return 0; |
| |
| /* All the tests passed. addr1 and addr2 are valid for lfq or stfq |
| instructions. */ |
| return 1; |
| } |
| |
| /* Return the register class of a scratch register needed to copy IN into |
| or out of a register in CLASS in MODE. If it can be done directly, |
| NO_REGS is returned. */ |
| |
| enum reg_class |
| rs6000_secondary_reload_class (enum reg_class class, |
| enum machine_mode mode ATTRIBUTE_UNUSED, |
| rtx in) |
| { |
| int regno; |
| |
| if (TARGET_ELF || (DEFAULT_ABI == ABI_DARWIN |
| #if TARGET_MACHO |
| && MACHOPIC_INDIRECT |
| #endif |
| )) |
| { |
| /* We cannot copy a symbolic operand directly into anything |
| other than BASE_REGS for TARGET_ELF. So indicate that a |
| register from BASE_REGS is needed as an intermediate |
| register. |
| |
| On Darwin, pic addresses require a load from memory, which |
| needs a base register. */ |
| if (class != BASE_REGS |
| && (GET_CODE (in) == SYMBOL_REF |
| || GET_CODE (in) == HIGH |
| || GET_CODE (in) == LABEL_REF |
| || GET_CODE (in) == CONST)) |
| return BASE_REGS; |
| } |
| |
| if (GET_CODE (in) == REG) |
| { |
| regno = REGNO (in); |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| { |
| regno = true_regnum (in); |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| regno = -1; |
| } |
| } |
| else if (GET_CODE (in) == SUBREG) |
| { |
| regno = true_regnum (in); |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| regno = -1; |
| } |
| else |
| regno = -1; |
| |
| /* We can place anything into GENERAL_REGS and can put GENERAL_REGS |
| into anything. */ |
| if (class == GENERAL_REGS || class == BASE_REGS |
| || (regno >= 0 && INT_REGNO_P (regno))) |
| return NO_REGS; |
| |
| /* Constants, memory, and FP registers can go into FP registers. */ |
| if ((regno == -1 || FP_REGNO_P (regno)) |
| && (class == FLOAT_REGS || class == NON_SPECIAL_REGS)) |
| return NO_REGS; |
| |
| /* Memory, and AltiVec registers can go into AltiVec registers. */ |
| if ((regno == -1 || ALTIVEC_REGNO_P (regno)) |
| && class == ALTIVEC_REGS) |
| return NO_REGS; |
| |
| /* We can copy among the CR registers. */ |
| if ((class == CR_REGS || class == CR0_REGS) |
| && regno >= 0 && CR_REGNO_P (regno)) |
| return NO_REGS; |
| |
| /* Otherwise, we need GENERAL_REGS. */ |
| return GENERAL_REGS; |
| } |
| |
| /* Given a comparison operation, return the bit number in CCR to test. We |
| know this is a valid comparison. |
| |
| SCC_P is 1 if this is for an scc. That means that %D will have been |
| used instead of %C, so the bits will be in different places. |
| |
| Return -1 if OP isn't a valid comparison for some reason. */ |
| |
| int |
| ccr_bit (rtx op, int scc_p) |
| { |
| enum rtx_code code = GET_CODE (op); |
| enum machine_mode cc_mode; |
| int cc_regnum; |
| int base_bit; |
| rtx reg; |
| |
| if (!COMPARISON_P (op)) |
| return -1; |
| |
| reg = XEXP (op, 0); |
| |
| gcc_assert (GET_CODE (reg) == REG && CR_REGNO_P (REGNO (reg))); |
| |
| cc_mode = GET_MODE (reg); |
| cc_regnum = REGNO (reg); |
| base_bit = 4 * (cc_regnum - CR0_REGNO); |
| |
| validate_condition_mode (code, cc_mode); |
| |
| /* When generating a sCOND operation, only positive conditions are |
| allowed. */ |
| gcc_assert (!scc_p |
| || code == EQ || code == GT || code == LT || code == UNORDERED |
| || code == GTU || code == LTU); |
| |
| switch (code) |
| { |
| case NE: |
| return scc_p ? base_bit + 3 : base_bit + 2; |
| case EQ: |
| return base_bit + 2; |
| case GT: case GTU: case UNLE: |
| return base_bit + 1; |
| case LT: case LTU: case UNGE: |
| return base_bit; |
| case ORDERED: case UNORDERED: |
| return base_bit + 3; |
| |
| case GE: case GEU: |
| /* If scc, we will have done a cror to put the bit in the |
| unordered position. So test that bit. For integer, this is ! LT |
| unless this is an scc insn. */ |
| return scc_p ? base_bit + 3 : base_bit; |
| |
| case LE: case LEU: |
| return scc_p ? base_bit + 3 : base_bit + 1; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Return the GOT register. */ |
| |
| rtx |
| rs6000_got_register (rtx value ATTRIBUTE_UNUSED) |
| { |
| /* The second flow pass currently (June 1999) can't update |
| regs_ever_live without disturbing other parts of the compiler, so |
| update it here to make the prolog/epilogue code happy. */ |
| if (no_new_pseudos && ! regs_ever_live[RS6000_PIC_OFFSET_TABLE_REGNUM]) |
| regs_ever_live[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1; |
| |
| current_function_uses_pic_offset_table = 1; |
| |
| return pic_offset_table_rtx; |
| } |
| |
| /* Function to init struct machine_function. |
| This will be called, via a pointer variable, |
| from push_function_context. */ |
| |
| static struct machine_function * |
| rs6000_init_machine_status (void) |
| { |
| /* APPLE LOCAL begin volatile pic base reg in leaves */ |
| machine_function *mf = (machine_function *) ggc_alloc_cleared (sizeof (machine_function)); |
| mf->substitute_pic_base_reg = INVALID_REGNUM; |
| return mf; |
| /* APPLE LOCAL end volatile pic base reg in leaves */ |
| } |
| |
| /* These macros test for integers and extract the low-order bits. */ |
| #define INT_P(X) \ |
| ((GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE) \ |
| && GET_MODE (X) == VOIDmode) |
| |
| #define INT_LOWPART(X) \ |
| (GET_CODE (X) == CONST_INT ? INTVAL (X) : CONST_DOUBLE_LOW (X)) |
| |
| int |
| extract_MB (rtx op) |
| { |
| int i; |
| unsigned long val = INT_LOWPART (op); |
| |
| /* If the high bit is zero, the value is the first 1 bit we find |
| from the left. */ |
| if ((val & 0x80000000) == 0) |
| { |
| gcc_assert (val & 0xffffffff); |
| |
| i = 1; |
| while (((val <<= 1) & 0x80000000) == 0) |
| ++i; |
| return i; |
| } |
| |
| /* If the high bit is set and the low bit is not, or the mask is all |
| 1's, the value is zero. */ |
| if ((val & 1) == 0 || (val & 0xffffffff) == 0xffffffff) |
| return 0; |
| |
| /* Otherwise we have a wrap-around mask. Look for the first 0 bit |
| from the right. */ |
| i = 31; |
| while (((val >>= 1) & 1) != 0) |
| --i; |
| |
| return i; |
| } |
| |
| int |
| extract_ME (rtx op) |
| { |
| int i; |
| unsigned long val = INT_LOWPART (op); |
| |
| /* If the low bit is zero, the value is the first 1 bit we find from |
| the right. */ |
| if ((val & 1) == 0) |
| { |
| gcc_assert (val & 0xffffffff); |
| |
| i = 30; |
| while (((val >>= 1) & 1) == 0) |
| --i; |
| |
| return i; |
| } |
| |
| /* If the low bit is set and the high bit is not, or the mask is all |
| 1's, the value is 31. */ |
| if ((val & 0x80000000) == 0 || (val & 0xffffffff) == 0xffffffff) |
| return 31; |
| |
| /* Otherwise we have a wrap-around mask. Look for the first 0 bit |
| from the left. */ |
| i = 0; |
| while (((val <<= 1) & 0x80000000) != 0) |
| ++i; |
| |
| return i; |
| } |
| |
| /* Locate some local-dynamic symbol still in use by this function |
| so that we can print its name in some tls_ld pattern. */ |
| |
| static const char * |
| rs6000_get_some_local_dynamic_name (void) |
| { |
| rtx insn; |
| |
| if (cfun->machine->some_ld_name) |
| return cfun->machine->some_ld_name; |
| |
| for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) |
| if (INSN_P (insn) |
| && for_each_rtx (&PATTERN (insn), |
| rs6000_get_some_local_dynamic_name_1, 0)) |
| return cfun->machine->some_ld_name; |
| |
| gcc_unreachable (); |
| } |
| |
| /* Helper function for rs6000_get_some_local_dynamic_name. */ |
| |
| static int |
| rs6000_get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED) |
| { |
| rtx x = *px; |
| |
| if (GET_CODE (x) == SYMBOL_REF) |
| { |
| const char *str = XSTR (x, 0); |
| if (SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC) |
| { |
| cfun->machine->some_ld_name = str; |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Write out a function code label. */ |
| |
| void |
| rs6000_output_function_entry (FILE *file, const char *fname) |
| { |
| if (fname[0] != '.') |
| { |
| switch (DEFAULT_ABI) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case ABI_AIX: |
| if (DOT_SYMBOLS) |
| putc ('.', file); |
| else |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "L."); |
| break; |
| |
| case ABI_V4: |
| case ABI_DARWIN: |
| break; |
| } |
| } |
| if (TARGET_AIX) |
| RS6000_OUTPUT_BASENAME (file, fname); |
| else |
| assemble_name (file, fname); |
| } |
| |
| /* Print an operand. Recognize special options, documented below. */ |
| |
| #if TARGET_ELF |
| #define SMALL_DATA_RELOC ((rs6000_sdata == SDATA_EABI) ? "sda21" : "sdarel") |
| #define SMALL_DATA_REG ((rs6000_sdata == SDATA_EABI) ? 0 : 13) |
| #else |
| #define SMALL_DATA_RELOC "sda21" |
| #define SMALL_DATA_REG 0 |
| #endif |
| |
| void |
| print_operand (FILE *file, rtx x, int code) |
| { |
| int i; |
| HOST_WIDE_INT val; |
| unsigned HOST_WIDE_INT uval; |
| |
| switch (code) |
| { |
| case '.': |
| /* Write out an instruction after the call which may be replaced |
| with glue code by the loader. This depends on the AIX version. */ |
| asm_fprintf (file, RS6000_CALL_GLUE); |
| return; |
| |
| /* %a is output_address. */ |
| |
| case 'A': |
| /* If X is a constant integer whose low-order 5 bits are zero, |
| write 'l'. Otherwise, write 'r'. This is a kludge to fix a bug |
| in the AIX assembler where "sri" with a zero shift count |
| writes a trash instruction. */ |
| if (GET_CODE (x) == CONST_INT && (INTVAL (x) & 31) == 0) |
| putc ('l', file); |
| else |
| putc ('r', file); |
| return; |
| |
| case 'b': |
| /* If constant, low-order 16 bits of constant, unsigned. |
| Otherwise, write normally. */ |
| if (INT_P (x)) |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC, INT_LOWPART (x) & 0xffff); |
| else |
| print_operand (file, x, 0); |
| return; |
| |
| case 'B': |
| /* If the low-order bit is zero, write 'r'; otherwise, write 'l' |
| for 64-bit mask direction. */ |
| putc (((INT_LOWPART (x) & 1) == 0 ? 'r' : 'l'), file); |
| return; |
| |
| /* %c is output_addr_const if a CONSTANT_ADDRESS_P, otherwise |
| output_operand. */ |
| |
| case 'c': |
| /* X is a CR register. Print the number of the GT bit of the CR. */ |
| if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) |
| output_operand_lossage ("invalid %%E value"); |
| else |
| fprintf (file, "%d", 4 * (REGNO (x) - CR0_REGNO) + 1); |
| return; |
| |
| case 'D': |
| /* Like 'J' but get to the GT bit only. */ |
| gcc_assert (GET_CODE (x) == REG); |
| |
| /* Bit 1 is GT bit. */ |
| i = 4 * (REGNO (x) - CR0_REGNO) + 1; |
| |
| /* Add one for shift count in rlinm for scc. */ |
| fprintf (file, "%d", i + 1); |
| return; |
| |
| case 'E': |
| /* X is a CR register. Print the number of the EQ bit of the CR */ |
| if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) |
| output_operand_lossage ("invalid %%E value"); |
| else |
| fprintf (file, "%d", 4 * (REGNO (x) - CR0_REGNO) + 2); |
| return; |
| |
| case 'f': |
| /* X is a CR register. Print the shift count needed to move it |
| to the high-order four bits. */ |
| if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) |
| output_operand_lossage ("invalid %%f value"); |
| else |
| fprintf (file, "%d", 4 * (REGNO (x) - CR0_REGNO)); |
| return; |
| |
| case 'F': |
| /* Similar, but print the count for the rotate in the opposite |
| direction. */ |
| if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) |
| output_operand_lossage ("invalid %%F value"); |
| else |
| fprintf (file, "%d", 32 - 4 * (REGNO (x) - CR0_REGNO)); |
| return; |
| |
| case 'G': |
| /* X is a constant integer. If it is negative, print "m", |
| otherwise print "z". This is to make an aze or ame insn. */ |
| if (GET_CODE (x) != CONST_INT) |
| output_operand_lossage ("invalid %%G value"); |
| else if (INTVAL (x) >= 0) |
| putc ('z', file); |
| else |
| putc ('m', file); |
| return; |
| |
| case 'h': |
| /* If constant, output low-order five bits. Otherwise, write |
| normally. */ |
| if (INT_P (x)) |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC, INT_LOWPART (x) & 31); |
| else |
| print_operand (file, x, 0); |
| return; |
| |
| case 'H': |
| /* If constant, output low-order six bits. Otherwise, write |
| normally. */ |
| if (INT_P (x)) |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC, INT_LOWPART (x) & 63); |
| else |
| print_operand (file, x, 0); |
| return; |
| |
| case 'I': |
| /* Print `i' if this is a constant, else nothing. */ |
| if (INT_P (x)) |
| putc ('i', file); |
| return; |
| |
| case 'j': |
| /* Write the bit number in CCR for jump. */ |
| i = ccr_bit (x, 0); |
| if (i == -1) |
| output_operand_lossage ("invalid %%j code"); |
| else |
| fprintf (file, "%d", i); |
| return; |
| |
| case 'J': |
| /* Similar, but add one for shift count in rlinm for scc and pass |
| scc flag to `ccr_bit'. */ |
| i = ccr_bit (x, 1); |
| if (i == -1) |
| output_operand_lossage ("invalid %%J code"); |
| else |
| /* If we want bit 31, write a shift count of zero, not 32. */ |
| fprintf (file, "%d", i == 31 ? 0 : i + 1); |
| return; |
| |
| case 'k': |
| /* X must be a constant. Write the 1's complement of the |
| constant. */ |
| if (! INT_P (x)) |
| output_operand_lossage ("invalid %%k value"); |
| else |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~ INT_LOWPART (x)); |
| return; |
| |
| case 'K': |
| /* X must be a symbolic constant on ELF. Write an |
| expression suitable for an 'addi' that adds in the low 16 |
| bits of the MEM. */ |
| if (GET_CODE (x) != CONST) |
| { |
| print_operand_address (file, x); |
| fputs ("@l", file); |
| } |
| else |
| { |
| if (GET_CODE (XEXP (x, 0)) != PLUS |
| || (GET_CODE (XEXP (XEXP (x, 0), 0)) != SYMBOL_REF |
| && GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF) |
| || GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT) |
| output_operand_lossage ("invalid %%K value"); |
| print_operand_address (file, XEXP (XEXP (x, 0), 0)); |
| fputs ("@l", file); |
| /* For GNU as, there must be a non-alphanumeric character |
| between 'l' and the number. The '-' is added by |
| print_operand() already. */ |
| if (INTVAL (XEXP (XEXP (x, 0), 1)) >= 0) |
| fputs ("+", file); |
| print_operand (file, XEXP (XEXP (x, 0), 1), 0); |
| } |
| return; |
| |
| /* %l is output_asm_label. */ |
| |
| case 'L': |
| /* Write second word of DImode or DFmode reference. Works on register |
| or non-indexed memory only. */ |
| if (GET_CODE (x) == REG) |
| fputs (reg_names[REGNO (x) + 1], file); |
| else if (GET_CODE (x) == MEM) |
| { |
| /* Handle possible auto-increment. Since it is pre-increment and |
| we have already done it, we can just use an offset of word. */ |
| if (GET_CODE (XEXP (x, 0)) == PRE_INC |
| || GET_CODE (XEXP (x, 0)) == PRE_DEC) |
| output_address (plus_constant (XEXP (XEXP (x, 0), 0), |
| UNITS_PER_WORD)); |
| else |
| output_address (XEXP (adjust_address_nv (x, SImode, |
| UNITS_PER_WORD), |
| 0)); |
| |
| if (small_data_operand (x, GET_MODE (x))) |
| fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, |
| reg_names[SMALL_DATA_REG]); |
| } |
| return; |
| |
| case 'm': |
| /* MB value for a mask operand. */ |
| if (! mask_operand (x, SImode)) |
| output_operand_lossage ("invalid %%m value"); |
| |
| fprintf (file, "%d", extract_MB (x)); |
| return; |
| |
| case 'M': |
| /* ME value for a mask operand. */ |
| if (! mask_operand (x, SImode)) |
| output_operand_lossage ("invalid %%M value"); |
| |
| fprintf (file, "%d", extract_ME (x)); |
| return; |
| |
| /* %n outputs the negative of its operand. */ |
| |
| case 'N': |
| /* Write the number of elements in the vector times 4. */ |
| if (GET_CODE (x) != PARALLEL) |
| output_operand_lossage ("invalid %%N value"); |
| else |
| fprintf (file, "%d", XVECLEN (x, 0) * 4); |
| return; |
| |
| case 'O': |
| /* Similar, but subtract 1 first. */ |
| if (GET_CODE (x) != PARALLEL) |
| output_operand_lossage ("invalid %%O value"); |
| else |
| fprintf (file, "%d", (XVECLEN (x, 0) - 1) * 4); |
| return; |
| |
| case 'p': |
| /* X is a CONST_INT that is a power of two. Output the logarithm. */ |
| if (! INT_P (x) |
| || INT_LOWPART (x) < 0 |
| || (i = exact_log2 (INT_LOWPART (x))) < 0) |
| output_operand_lossage ("invalid %%p value"); |
| else |
| fprintf (file, "%d", i); |
| return; |
| |
| case 'P': |
| /* The operand must be an indirect memory reference. The result |
| is the register name. */ |
| if (GET_CODE (x) != MEM || GET_CODE (XEXP (x, 0)) != REG |
| || REGNO (XEXP (x, 0)) >= 32) |
| output_operand_lossage ("invalid %%P value"); |
| else |
| fputs (reg_names[REGNO (XEXP (x, 0))], file); |
| return; |
| |
| case 'q': |
| /* This outputs the logical code corresponding to a boolean |
| expression. The expression may have one or both operands |
| negated (if one, only the first one). For condition register |
| logical operations, it will also treat the negated |
| CR codes as NOTs, but not handle NOTs of them. */ |
| { |
| const char *const *t = 0; |
| const char *s; |
| enum rtx_code code = GET_CODE (x); |
| static const char * const tbl[3][3] = { |
| { "and", "andc", "nor" }, |
| { "or", "orc", "nand" }, |
| { "xor", "eqv", "xor" } }; |
| |
| if (code == AND) |
| t = tbl[0]; |
| else if (code == IOR) |
| t = tbl[1]; |
| else if (code == XOR) |
| t = tbl[2]; |
| else |
| output_operand_lossage ("invalid %%q value"); |
| |
| if (GET_CODE (XEXP (x, 0)) != NOT) |
| s = t[0]; |
| else |
| { |
| if (GET_CODE (XEXP (x, 1)) == NOT) |
| s = t[2]; |
| else |
| s = t[1]; |
| } |
| |
| fputs (s, file); |
| } |
| return; |
| |
| case 'Q': |
| if (TARGET_MFCRF) |
| fputc (',', file); |
| /* FALLTHRU */ |
| else |
| return; |
| |
| case 'R': |
| /* X is a CR register. Print the mask for `mtcrf'. */ |
| if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) |
| output_operand_lossage ("invalid %%R value"); |
| else |
| fprintf (file, "%d", 128 >> (REGNO (x) - CR0_REGNO)); |
| return; |
| |
| case 's': |
| /* Low 5 bits of 32 - value */ |
| if (! INT_P (x)) |
| output_operand_lossage ("invalid %%s value"); |
| else |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC, (32 - INT_LOWPART (x)) & 31); |
| return; |
| |
| case 'S': |
| /* PowerPC64 mask position. All 0's is excluded. |
| CONST_INT 32-bit mask is considered sign-extended so any |
| transition must occur within the CONST_INT, not on the boundary. */ |
| if (! mask64_operand (x, DImode)) |
| output_operand_lossage ("invalid %%S value"); |
| |
| uval = INT_LOWPART (x); |
| |
| if (uval & 1) /* Clear Left */ |
| { |
| #if HOST_BITS_PER_WIDE_INT > 64 |
| uval &= ((unsigned HOST_WIDE_INT) 1 << 64) - 1; |
| #endif |
| i = 64; |
| } |
| else /* Clear Right */ |
| { |
| uval = ~uval; |
| #if HOST_BITS_PER_WIDE_INT > 64 |
| uval &= ((unsigned HOST_WIDE_INT) 1 << 64) - 1; |
| #endif |
| i = 63; |
| } |
| while (uval != 0) |
| --i, uval >>= 1; |
| gcc_assert (i >= 0); |
| fprintf (file, "%d", i); |
| return; |
| |
| case 't': |
| /* Like 'J' but get to the OVERFLOW/UNORDERED bit. */ |
| gcc_assert (GET_CODE (x) == REG && GET_MODE (x) == CCmode); |
| |
| /* Bit 3 is OV bit. */ |
| i = 4 * (REGNO (x) - CR0_REGNO) + 3; |
| |
| /* If we want bit 31, write a shift count of zero, not 32. */ |
| fprintf (file, "%d", i == 31 ? 0 : i + 1); |
| return; |
| |
| case 'T': |
| /* Print the symbolic name of a branch target register. */ |
| if (GET_CODE (x) != REG || (REGNO (x) != LINK_REGISTER_REGNUM |
| && REGNO (x) != COUNT_REGISTER_REGNUM)) |
| output_operand_lossage ("invalid %%T value"); |
| else if (REGNO (x) == LINK_REGISTER_REGNUM) |
| fputs (TARGET_NEW_MNEMONICS ? "lr" : "r", file); |
| else |
| fputs ("ctr", file); |
| return; |
| |
| case 'u': |
| /* High-order 16 bits of constant for use in unsigned operand. */ |
| if (! INT_P (x)) |
| output_operand_lossage ("invalid %%u value"); |
| else |
| fprintf (file, HOST_WIDE_INT_PRINT_HEX, |
| (INT_LOWPART (x) >> 16) & 0xffff); |
| return; |
| |
| case 'v': |
| /* High-order 16 bits of constant for use in signed operand. */ |
| if (! INT_P (x)) |
| output_operand_lossage ("invalid %%v value"); |
| else |
| fprintf (file, HOST_WIDE_INT_PRINT_HEX, |
| (INT_LOWPART (x) >> 16) & 0xffff); |
| return; |
| |
| case 'U': |
| /* Print `u' if this has an auto-increment or auto-decrement. */ |
| if (GET_CODE (x) == MEM |
| && (GET_CODE (XEXP (x, 0)) == PRE_INC |
| || GET_CODE (XEXP (x, 0)) == PRE_DEC)) |
| putc ('u', file); |
| return; |
| |
| case 'V': |
| /* Print the trap code for this operand. */ |
| switch (GET_CODE (x)) |
| { |
| case EQ: |
| fputs ("eq", file); /* 4 */ |
| break; |
| case NE: |
| fputs ("ne", file); /* 24 */ |
| break; |
| case LT: |
| fputs ("lt", file); /* 16 */ |
| break; |
| case LE: |
| fputs ("le", file); /* 20 */ |
| break; |
| case GT: |
| fputs ("gt", file); /* 8 */ |
| break; |
| case GE: |
| fputs ("ge", file); /* 12 */ |
| break; |
| case LTU: |
| fputs ("llt", file); /* 2 */ |
| break; |
| case LEU: |
| fputs ("lle", file); /* 6 */ |
| break; |
| case GTU: |
| fputs ("lgt", file); /* 1 */ |
| break; |
| case GEU: |
| fputs ("lge", file); /* 5 */ |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| |
| case 'w': |
| /* If constant, low-order 16 bits of constant, signed. Otherwise, write |
| normally. */ |
| if (INT_P (x)) |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC, |
| ((INT_LOWPART (x) & 0xffff) ^ 0x8000) - 0x8000); |
| else |
| print_operand (file, x, 0); |
| return; |
| |
| case 'W': |
| /* MB value for a PowerPC64 rldic operand. */ |
| val = (GET_CODE (x) == CONST_INT |
| ? INTVAL (x) : CONST_DOUBLE_HIGH (x)); |
| |
| if (val < 0) |
| i = -1; |
| else |
| for (i = 0; i < HOST_BITS_PER_WIDE_INT; i++) |
| if ((val <<= 1) < 0) |
| break; |
| |
| #if HOST_BITS_PER_WIDE_INT == 32 |
| if (GET_CODE (x) == CONST_INT && i >= 0) |
| i += 32; /* zero-extend high-part was all 0's */ |
| else if (GET_CODE (x) == CONST_DOUBLE && i == 32) |
| { |
| val = CONST_DOUBLE_LOW (x); |
| |
| gcc_assert (val); |
| if (val < 0) |
| --i; |
| else |
| for ( ; i < 64; i++) |
| if ((val <<= 1) < 0) |
| break; |
| } |
| #endif |
| |
| fprintf (file, "%d", i + 1); |
| return; |
| |
| case 'X': |
| if (GET_CODE (x) == MEM |
| && legitimate_indexed_address_p (XEXP (x, 0), 0)) |
| putc ('x', file); |
| return; |
| |
| case 'Y': |
| /* Like 'L', for third word of TImode */ |
| if (GET_CODE (x) == REG) |
| fputs (reg_names[REGNO (x) + 2], file); |
| else if (GET_CODE (x) == MEM) |
| { |
| if (GET_CODE (XEXP (x, 0)) == PRE_INC |
| || GET_CODE (XEXP (x, 0)) == PRE_DEC) |
| output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8)); |
| else |
| output_address (XEXP (adjust_address_nv (x, SImode, 8), 0)); |
| if (small_data_operand (x, GET_MODE (x))) |
| fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, |
| reg_names[SMALL_DATA_REG]); |
| } |
| return; |
| |
| case 'z': |
| /* X is a SYMBOL_REF. Write out the name preceded by a |
| period and without any trailing data in brackets. Used for function |
| names. If we are configured for System V (or the embedded ABI) on |
| the PowerPC, do not emit the period, since those systems do not use |
| TOCs and the like. */ |
| gcc_assert (GET_CODE (x) == SYMBOL_REF); |
| |
| /* Mark the decl as referenced so that cgraph will output the |
| function. */ |
| if (SYMBOL_REF_DECL (x)) |
| mark_decl_referenced (SYMBOL_REF_DECL (x)); |
| |
| /* For macho, check to see if we need a stub. */ |
| if (TARGET_MACHO) |
| { |
| const char *name = XSTR (x, 0); |
| #if TARGET_MACHO |
| /* APPLE LOCAL begin axe stubs 5571540 */ |
| if (darwin_stubs |
| && MACHOPIC_INDIRECT |
| /* APPLE LOCAL end axe stubs 5571540 */ |
| && machopic_classify_symbol (x) == MACHOPIC_UNDEFINED_FUNCTION) |
| name = machopic_indirection_name (x, /*stub_p=*/true); |
| #endif |
| assemble_name (file, name); |
| } |
| else if (!DOT_SYMBOLS) |
| assemble_name (file, XSTR (x, 0)); |
| else |
| rs6000_output_function_entry (file, XSTR (x, 0)); |
| return; |
| |
| case 'Z': |
| /* Like 'L', for last word of TImode. */ |
| if (GET_CODE (x) == REG) |
| fputs (reg_names[REGNO (x) + 3], file); |
| else if (GET_CODE (x) == MEM) |
| { |
| if (GET_CODE (XEXP (x, 0)) == PRE_INC |
| || GET_CODE (XEXP (x, 0)) == PRE_DEC) |
| output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12)); |
| else |
| output_address (XEXP (adjust_address_nv (x, SImode, 12), 0)); |
| if (small_data_operand (x, GET_MODE (x))) |
| fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, |
| reg_names[SMALL_DATA_REG]); |
| } |
| return; |
| |
| /* Print AltiVec or SPE memory operand. */ |
| case 'y': |
| { |
| rtx tmp; |
| |
| gcc_assert (GET_CODE (x) == MEM); |
| |
| tmp = XEXP (x, 0); |
| |
| /* Ugly hack because %y is overloaded. */ |
| if (TARGET_E500 && GET_MODE_SIZE (GET_MODE (x)) == 8) |
| { |
| /* Handle [reg]. */ |
| if (GET_CODE (tmp) == REG) |
| { |
| fprintf (file, "0(%s)", reg_names[REGNO (tmp)]); |
| break; |
| } |
| /* Handle [reg+UIMM]. */ |
| else if (GET_CODE (tmp) == PLUS && |
| GET_CODE (XEXP (tmp, 1)) == CONST_INT) |
| { |
| int x; |
| |
| gcc_assert (GET_CODE (XEXP (tmp, 0)) == REG); |
| |
| x = INTVAL (XEXP (tmp, 1)); |
| fprintf (file, "%d(%s)", x, reg_names[REGNO (XEXP (tmp, 0))]); |
| break; |
| } |
| |
| /* Fall through. Must be [reg+reg]. */ |
| } |
| if (TARGET_ALTIVEC |
| && GET_CODE (tmp) == AND |
| && GET_CODE (XEXP (tmp, 1)) == CONST_INT |
| && INTVAL (XEXP (tmp, 1)) == -16) |
| tmp = XEXP (tmp, 0); |
| if (GET_CODE (tmp) == REG) |
| fprintf (file, "0,%s", reg_names[REGNO (tmp)]); |
| else |
| { |
| gcc_assert (GET_CODE (tmp) == PLUS |
| && REG_P (XEXP (tmp, 0)) |
| && REG_P (XEXP (tmp, 1))); |
| |
| if (REGNO (XEXP (tmp, 0)) == 0) |
| fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (tmp, 1)) ], |
| reg_names[ REGNO (XEXP (tmp, 0)) ]); |
| else |
| fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (tmp, 0)) ], |
| reg_names[ REGNO (XEXP (tmp, 1)) ]); |
| } |
| break; |
| } |
| |
| case 0: |
| if (GET_CODE (x) == REG) |
| fprintf (file, "%s", reg_names[REGNO (x)]); |
| else if (GET_CODE (x) == MEM) |
| { |
| /* We need to handle PRE_INC and PRE_DEC here, since we need to |
| know the width from the mode. */ |
| if (GET_CODE (XEXP (x, 0)) == PRE_INC) |
| fprintf (file, "%d(%s)", GET_MODE_SIZE (GET_MODE (x)), |
| reg_names[REGNO (XEXP (XEXP (x, 0), 0))]); |
| else if (GET_CODE (XEXP (x, 0)) == PRE_DEC) |
| fprintf (file, "%d(%s)", - GET_MODE_SIZE (GET_MODE (x)), |
| reg_names[REGNO (XEXP (XEXP (x, 0), 0))]); |
| else |
| output_address (XEXP (x, 0)); |
| } |
| else |
| output_addr_const (file, x); |
| return; |
| |
| case '&': |
| assemble_name (file, rs6000_get_some_local_dynamic_name ()); |
| return; |
| |
| default: |
| output_operand_lossage ("invalid %%xn code"); |
| } |
| } |
| |
| /* Print the address of an operand. */ |
| |
| void |
| print_operand_address (FILE *file, rtx x) |
| { |
| if (GET_CODE (x) == REG) |
| fprintf (file, "0(%s)", reg_names[ REGNO (x) ]); |
| else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST |
| || GET_CODE (x) == LABEL_REF) |
| { |
| output_addr_const (file, x); |
| if (small_data_operand (x, GET_MODE (x))) |
| fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, |
| reg_names[SMALL_DATA_REG]); |
| else |
| gcc_assert (!TARGET_TOC); |
| } |
| else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG) |
| { |
| gcc_assert (REG_P (XEXP (x, 0))); |
| if (REGNO (XEXP (x, 0)) == 0) |
| fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 1)) ], |
| reg_names[ REGNO (XEXP (x, 0)) ]); |
| else |
| fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 0)) ], |
| reg_names[ REGNO (XEXP (x, 1)) ]); |
| } |
| else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%s)", |
| INTVAL (XEXP (x, 1)), reg_names[ REGNO (XEXP (x, 0)) ]); |
| #if TARGET_ELF |
| else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG |
| && CONSTANT_P (XEXP (x, 1))) |
| { |
| output_addr_const (file, XEXP (x, 1)); |
| fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]); |
| } |
| #endif |
| #if TARGET_MACHO |
| else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG |
| && CONSTANT_P (XEXP (x, 1))) |
| { |
| fprintf (file, "lo16("); |
| output_addr_const (file, XEXP (x, 1)); |
| fprintf (file, ")(%s)", reg_names[ REGNO (XEXP (x, 0)) ]); |
| } |
| #endif |
| else if (legitimate_constant_pool_address_p (x)) |
| { |
| if (TARGET_AIX && (!TARGET_ELF || !TARGET_MINIMAL_TOC)) |
| { |
| rtx contains_minus = XEXP (x, 1); |
| rtx minus, symref; |
| const char *name; |
| |
| /* Find the (minus (sym) (toc)) buried in X, and temporarily |
| turn it into (sym) for output_addr_const. */ |
| while (GET_CODE (XEXP (contains_minus, 0)) != MINUS) |
| contains_minus = XEXP (contains_minus, 0); |
| |
| minus = XEXP (contains_minus, 0); |
| symref = XEXP (minus, 0); |
| XEXP (contains_minus, 0) = symref; |
| if (TARGET_ELF) |
| { |
| char *newname; |
| |
| name = XSTR (symref, 0); |
| newname = alloca (strlen (name) + sizeof ("@toc")); |
| strcpy (newname, name); |
| strcat (newname, "@toc"); |
| XSTR (symref, 0) = newname; |
| } |
| output_addr_const (file, XEXP (x, 1)); |
| if (TARGET_ELF) |
| XSTR (symref, 0) = name; |
| XEXP (contains_minus, 0) = minus; |
| } |
| else |
| output_addr_const (file, XEXP (x, 1)); |
| |
| fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]); |
| } |
| else |
| gcc_unreachable (); |
| } |
| |
| /* Target hook for assembling integer objects. The PowerPC version has |
| to handle fixup entries for relocatable code if RELOCATABLE_NEEDS_FIXUP |
| is defined. It also needs to handle DI-mode objects on 64-bit |
| targets. */ |
| |
| static bool |
| rs6000_assemble_integer (rtx x, unsigned int size, int aligned_p) |
| { |
| #ifdef RELOCATABLE_NEEDS_FIXUP |
| /* Special handling for SI values. */ |
| if (RELOCATABLE_NEEDS_FIXUP && size == 4 && aligned_p) |
| { |
| static int recurse = 0; |
| |
| /* For -mrelocatable, we mark all addresses that need to be fixed up |
| in the .fixup section. */ |
| if (TARGET_RELOCATABLE |
| && in_section != toc_section |
| && in_section != text_section |
| && !unlikely_text_section_p (in_section) |
| && !recurse |
| && GET_CODE (x) != CONST_INT |
| && GET_CODE (x) != CONST_DOUBLE |
| && CONSTANT_P (x)) |
| { |
| char buf[256]; |
| |
| recurse = 1; |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LCP", fixuplabelno); |
| fixuplabelno++; |
| ASM_OUTPUT_LABEL (asm_out_file, buf); |
| fprintf (asm_out_file, "\t.long\t("); |
| output_addr_const (asm_out_file, x); |
| fprintf (asm_out_file, ")@fixup\n"); |
| fprintf (asm_out_file, "\t.section\t\".fixup\",\"aw\"\n"); |
| ASM_OUTPUT_ALIGN (asm_out_file, 2); |
| fprintf (asm_out_file, "\t.long\t"); |
| assemble_name (asm_out_file, buf); |
| fprintf (asm_out_file, "\n\t.previous\n"); |
| recurse = 0; |
| return true; |
| } |
| /* Remove initial .'s to turn a -mcall-aixdesc function |
| address into the address of the descriptor, not the function |
| itself. */ |
| else if (GET_CODE (x) == SYMBOL_REF |
| && XSTR (x, 0)[0] == '.' |
| && DEFAULT_ABI == ABI_AIX) |
| { |
| const char *name = XSTR (x, 0); |
| while (*name == '.') |
| name++; |
| |
| fprintf (asm_out_file, "\t.long\t%s\n", name); |
| return true; |
| } |
| } |
| #endif /* RELOCATABLE_NEEDS_FIXUP */ |
| return default_assemble_integer (x, size, aligned_p); |
| } |
| |
| #ifdef HAVE_GAS_HIDDEN |
| /* Emit an assembler directive to set symbol visibility for DECL to |
| VISIBILITY_TYPE. */ |
| |
| static void |
| rs6000_assemble_visibility (tree decl, int vis) |
| { |
| /* Functions need to have their entry point symbol visibility set as |
| well as their descriptor symbol visibility. */ |
| if (DEFAULT_ABI == ABI_AIX |
| && DOT_SYMBOLS |
| && TREE_CODE (decl) == FUNCTION_DECL) |
| { |
| /* LLVM LOCAL */ |
| #ifndef ENABLE_LLVM |
| static const char * const visibility_types[] = { |
| NULL, "internal", "hidden", "protected" |
| }; |
| |
| const char *name, *type; |
| |
| name = ((* targetm.strip_name_encoding) |
| (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)))); |
| type = visibility_types[vis]; |
| |
| fprintf (asm_out_file, "\t.%s\t%s\n", type, name); |
| fprintf (asm_out_file, "\t.%s\t.%s\n", type, name); |
| /* LLVM LOCAL */ |
| #endif |
| } |
| else |
| default_assemble_visibility (decl, vis); |
| } |
| #endif |
| |
| enum rtx_code |
| rs6000_reverse_condition (enum machine_mode mode, enum rtx_code code) |
| { |
| /* Reversal of FP compares takes care -- an ordered compare |
| becomes an unordered compare and vice versa. */ |
| if (mode == CCFPmode |
| && (!flag_finite_math_only |
| || code == UNLT || code == UNLE || code == UNGT || code == UNGE |
| || code == UNEQ || code == LTGT)) |
| return reverse_condition_maybe_unordered (code); |
| else |
| return reverse_condition (code); |
| } |
| |
| /* Generate a compare for CODE. Return a brand-new rtx that |
| represents the result of the compare. */ |
| |
| static rtx |
| rs6000_generate_compare (enum rtx_code code) |
| { |
| enum machine_mode comp_mode; |
| rtx compare_result; |
| |
| if (rs6000_compare_fp_p) |
| comp_mode = CCFPmode; |
| else if (code == GTU || code == LTU |
| || code == GEU || code == LEU) |
| comp_mode = CCUNSmode; |
| else if ((code == EQ || code == NE) |
| && GET_CODE (rs6000_compare_op0) == SUBREG |
| && GET_CODE (rs6000_compare_op1) == SUBREG |
| && SUBREG_PROMOTED_UNSIGNED_P (rs6000_compare_op0) |
| && SUBREG_PROMOTED_UNSIGNED_P (rs6000_compare_op1)) |
| /* These are unsigned values, perhaps there will be a later |
| ordering compare that can be shared with this one. |
| Unfortunately we cannot detect the signedness of the operands |
| for non-subregs. */ |
| comp_mode = CCUNSmode; |
| else |
| comp_mode = CCmode; |
| |
| /* First, the compare. */ |
| compare_result = gen_reg_rtx (comp_mode); |
| |
| /* E500 FP compare instructions on the GPRs. Yuck! */ |
| if ((TARGET_E500 && !TARGET_FPRS && TARGET_HARD_FLOAT) |
| && rs6000_compare_fp_p) |
| { |
| rtx cmp, or_result, compare_result2; |
| enum machine_mode op_mode = GET_MODE (rs6000_compare_op0); |
| |
| if (op_mode == VOIDmode) |
| op_mode = GET_MODE (rs6000_compare_op1); |
| |
| /* The E500 FP compare instructions toggle the GT bit (CR bit 1) only. |
| This explains the following mess. */ |
| |
| switch (code) |
| { |
| case EQ: case UNEQ: case NE: case LTGT: |
| switch (op_mode) |
| { |
| case SFmode: |
| cmp = flag_unsafe_math_optimizations |
| ? gen_tstsfeq_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1) |
| : gen_cmpsfeq_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1); |
| break; |
| |
| case DFmode: |
| cmp = flag_unsafe_math_optimizations |
| ? gen_tstdfeq_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1) |
| : gen_cmpdfeq_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| |
| case GT: case GTU: case UNGT: case UNGE: case GE: case GEU: |
| switch (op_mode) |
| { |
| case SFmode: |
| cmp = flag_unsafe_math_optimizations |
| ? gen_tstsfgt_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1) |
| : gen_cmpsfgt_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1); |
| break; |
| |
| case DFmode: |
| cmp = flag_unsafe_math_optimizations |
| ? gen_tstdfgt_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1) |
| : gen_cmpdfgt_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| |
| case LT: case LTU: case UNLT: case UNLE: case LE: case LEU: |
| switch (op_mode) |
| { |
| case SFmode: |
| cmp = flag_unsafe_math_optimizations |
| ? gen_tstsflt_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1) |
| : gen_cmpsflt_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1); |
| break; |
| |
| case DFmode: |
| cmp = flag_unsafe_math_optimizations |
| ? gen_tstdflt_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1) |
| : gen_cmpdflt_gpr (compare_result, rs6000_compare_op0, |
| rs6000_compare_op1); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* Synthesize LE and GE from LT/GT || EQ. */ |
| if (code == LE || code == GE || code == LEU || code == GEU) |
| { |
| emit_insn (cmp); |
| |
| switch (code) |
| { |
| case LE: code = LT; break; |
| case GE: code = GT; break; |
| case LEU: code = LT; break; |
| case GEU: code = GT; break; |
| default: gcc_unreachable (); |
| } |
| |
| compare_result2 = gen_reg_rtx (CCFPmode); |
| |
| /* Do the EQ. */ |
| switch (op_mode) |
| { |
| case SFmode: |
| cmp = flag_unsafe_math_optimizations |
| ? gen_tstsfeq_gpr (compare_result2, rs6000_compare_op0, |
| rs6000_compare_op1) |
| : gen_cmpsfeq_gpr (compare_result2, rs6000_compare_op0, |
| rs6000_compare_op1); |
| break; |
| |
| case DFmode: |
| cmp = flag_unsafe_math_optimizations |
| ? gen_tstdfeq_gpr (compare_result2, rs6000_compare_op0, |
| rs6000_compare_op1) |
| : gen_cmpdfeq_gpr (compare_result2, rs6000_compare_op0, |
| rs6000_compare_op1); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| emit_insn (cmp); |
| |
| /* OR them together. */ |
| or_result = gen_reg_rtx (CCFPmode); |
| cmp = gen_e500_cr_ior_compare (or_result, compare_result, |
| compare_result2); |
| compare_result = or_result; |
| code = EQ; |
| } |
| else |
| { |
| if (code == NE || code == LTGT) |
| code = NE; |
| else |
| code = EQ; |
| } |
| |
| emit_insn (cmp); |
| } |
| else |
| { |
| /* Generate XLC-compatible TFmode compare as PARALLEL with extra |
| CLOBBERs to match cmptf_internal2 pattern. */ |
| if (comp_mode == CCFPmode && TARGET_XL_COMPAT |
| && GET_MODE (rs6000_compare_op0) == TFmode |
| && !TARGET_IEEEQUAD |
| && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128) |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, |
| gen_rtvec (9, |
| gen_rtx_SET (VOIDmode, |
| compare_result, |
| gen_rtx_COMPARE (comp_mode, |
| rs6000_compare_op0, |
| rs6000_compare_op1)), |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)), |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode))))); |
| else if (GET_CODE (rs6000_compare_op1) == UNSPEC |
| && XINT (rs6000_compare_op1, 1) == UNSPEC_SP_TEST) |
| { |
| rtx op1 = XVECEXP (rs6000_compare_op1, 0, 0); |
| comp_mode = CCEQmode; |
| compare_result = gen_reg_rtx (CCEQmode); |
| if (TARGET_64BIT) |
| emit_insn (gen_stack_protect_testdi (compare_result, |
| rs6000_compare_op0, op1)); |
| else |
| emit_insn (gen_stack_protect_testsi (compare_result, |
| rs6000_compare_op0, op1)); |
| } |
| else |
| emit_insn (gen_rtx_SET (VOIDmode, compare_result, |
| gen_rtx_COMPARE (comp_mode, |
| rs6000_compare_op0, |
| rs6000_compare_op1))); |
| } |
| |
| /* Some kinds of FP comparisons need an OR operation; |
| under flag_finite_math_only we don't bother. */ |
| if (rs6000_compare_fp_p |
| && !flag_finite_math_only |
| && !(TARGET_HARD_FLOAT && TARGET_E500 && !TARGET_FPRS) |
| && (code == LE || code == GE |
| || code == UNEQ || code == LTGT |
| || code == UNGT || code == UNLT)) |
| { |
| enum rtx_code or1, or2; |
| rtx or1_rtx, or2_rtx, compare2_rtx; |
| rtx or_result = gen_reg_rtx (CCEQmode); |
| |
| switch (code) |
| { |
| case LE: or1 = LT; or2 = EQ; break; |
| case GE: or1 = GT; or2 = EQ; break; |
| case UNEQ: or1 = UNORDERED; or2 = EQ; break; |
| case LTGT: or1 = LT; or2 = GT; break; |
| case UNGT: or1 = UNORDERED; or2 = GT; break; |
| case UNLT: or1 = UNORDERED; or2 = LT; break; |
| default: gcc_unreachable (); |
| } |
| validate_condition_mode (or1, comp_mode); |
| validate_condition_mode (or2, comp_mode); |
| or1_rtx = gen_rtx_fmt_ee (or1, SImode, compare_result, const0_rtx); |
| or2_rtx = gen_rtx_fmt_ee (or2, SImode, compare_result, const0_rtx); |
| compare2_rtx = gen_rtx_COMPARE (CCEQmode, |
| gen_rtx_IOR (SImode, or1_rtx, or2_rtx), |
| const_true_rtx); |
| emit_insn (gen_rtx_SET (VOIDmode, or_result, compare2_rtx)); |
| |
| compare_result = or_result; |
| code = EQ; |
| } |
| |
| validate_condition_mode (code, GET_MODE (compare_result)); |
| |
| return gen_rtx_fmt_ee (code, VOIDmode, compare_result, const0_rtx); |
| } |
| |
| |
| /* Emit the RTL for an sCOND pattern. */ |
| |
| void |
| rs6000_emit_sCOND (enum rtx_code code, rtx result) |
| { |
| rtx condition_rtx; |
| enum machine_mode op_mode; |
| enum rtx_code cond_code; |
| |
| condition_rtx = rs6000_generate_compare (code); |
| cond_code = GET_CODE (condition_rtx); |
| |
| if (TARGET_E500 && rs6000_compare_fp_p |
| && !TARGET_FPRS && TARGET_HARD_FLOAT) |
| { |
| rtx t; |
| |
| PUT_MODE (condition_rtx, SImode); |
| t = XEXP (condition_rtx, 0); |
| |
| gcc_assert (cond_code == NE || cond_code == EQ); |
| |
| if (cond_code == NE) |
| emit_insn (gen_e500_flip_gt_bit (t, t)); |
| |
| emit_insn (gen_move_from_CR_gt_bit (result, t)); |
| return; |
| } |
| |
| if (cond_code == NE |
| || cond_code == GE || cond_code == LE |
| || cond_code == GEU || cond_code == LEU |
| || cond_code == ORDERED || cond_code == UNGE || cond_code == UNLE) |
| { |
| rtx not_result = gen_reg_rtx (CCEQmode); |
| rtx not_op, rev_cond_rtx; |
| enum machine_mode cc_mode; |
| |
| cc_mode = GET_MODE (XEXP (condition_rtx, 0)); |
| |
| rev_cond_rtx = gen_rtx_fmt_ee (rs6000_reverse_condition (cc_mode, cond_code), |
| SImode, XEXP (condition_rtx, 0), const0_rtx); |
| not_op = gen_rtx_COMPARE (CCEQmode, rev_cond_rtx, const0_rtx); |
| emit_insn (gen_rtx_SET (VOIDmode, not_result, not_op)); |
| condition_rtx = gen_rtx_EQ (VOIDmode, not_result, const0_rtx); |
| } |
| |
| op_mode = GET_MODE (rs6000_compare_op0); |
| if (op_mode == VOIDmode) |
| op_mode = GET_MODE (rs6000_compare_op1); |
| |
| if (TARGET_POWERPC64 && (op_mode == DImode || rs6000_compare_fp_p)) |
| { |
| PUT_MODE (condition_rtx, DImode); |
| convert_move (result, condition_rtx, 0); |
| } |
| else |
| { |
| PUT_MODE (condition_rtx, SImode); |
| emit_insn (gen_rtx_SET (VOIDmode, result, condition_rtx)); |
| } |
| } |
| |
| /* Emit a branch of kind CODE to location LOC. */ |
| |
| void |
| rs6000_emit_cbranch (enum rtx_code code, rtx loc) |
| { |
| rtx condition_rtx, loc_ref; |
| |
| condition_rtx = rs6000_generate_compare (code); |
| loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc); |
| emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, |
| gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx, |
| loc_ref, pc_rtx))); |
| } |
| |
| /* Return the string to output a conditional branch to LABEL, which is |
| the operand number of the label, or -1 if the branch is really a |
| conditional return. |
| |
| OP is the conditional expression. XEXP (OP, 0) is assumed to be a |
| condition code register and its mode specifies what kind of |
| comparison we made. |
| |
| REVERSED is nonzero if we should reverse the sense of the comparison. |
| |
| INSN is the insn. */ |
| |
| char * |
| output_cbranch (rtx op, const char *label, int reversed, rtx insn) |
| { |
| static char string[64]; |
| enum rtx_code code = GET_CODE (op); |
| rtx cc_reg = XEXP (op, 0); |
| enum machine_mode mode = GET_MODE (cc_reg); |
| int cc_regno = REGNO (cc_reg) - CR0_REGNO; |
| int need_longbranch = label != NULL && get_attr_length (insn) == 8; |
| int really_reversed = reversed ^ need_longbranch; |
| char *s = string; |
| const char *ccode; |
| const char *pred; |
| rtx note; |
| |
| validate_condition_mode (code, mode); |
| |
| /* Work out which way this really branches. We could use |
| reverse_condition_maybe_unordered here always but this |
| makes the resulting assembler clearer. */ |
| if (really_reversed) |
| { |
| /* Reversal of FP compares takes care -- an ordered compare |
| becomes an unordered compare and vice versa. */ |
| if (mode == CCFPmode) |
| code = reverse_condition_maybe_unordered (code); |
| else |
| code = reverse_condition (code); |
| } |
| |
| if ((TARGET_E500 && !TARGET_FPRS && TARGET_HARD_FLOAT) && mode == CCFPmode) |
| { |
| /* The efscmp/tst* instructions twiddle bit 2, which maps nicely |
| to the GT bit. */ |
| switch (code) |
| { |
| case EQ: |
| /* Opposite of GT. */ |
| code = GT; |
| break; |
| |
| case NE: |
| code = UNLE; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| switch (code) |
| { |
| /* Not all of these are actually distinct opcodes, but |
| we distinguish them for clarity of the resulting assembler. */ |
| case NE: case LTGT: |
| ccode = "ne"; break; |
| case EQ: case UNEQ: |
| ccode = "eq"; break; |
| case GE: case GEU: |
| ccode = "ge"; break; |
| case GT: case GTU: case UNGT: |
| ccode = "gt"; break; |
| case LE: case LEU: |
| ccode = "le"; break; |
| case LT: case LTU: case UNLT: |
| ccode = "lt"; break; |
| case UNORDERED: ccode = "un"; break; |
| case ORDERED: ccode = "nu"; break; |
| case UNGE: ccode = "nl"; break; |
| case UNLE: ccode = "ng"; break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* Maybe we have a guess as to how likely the branch is. |
| The old mnemonics don't have a way to specify this information. */ |
| pred = ""; |
| note = find_reg_note (insn, REG_BR_PROB, NULL_RTX); |
| if (note != NULL_RTX) |
| { |
| /* PROB is the difference from 50%. */ |
| int prob = INTVAL (XEXP (note, 0)) - REG_BR_PROB_BASE / 2; |
| |
| /* Only hint for highly probable/improbable branches on newer |
| cpus as static prediction overrides processor dynamic |
| prediction. For older cpus we may as well always hint, but |
| assume not taken for branches that are very close to 50% as a |
| mispredicted taken branch is more expensive than a |
| mispredicted not-taken branch. */ |
| if (rs6000_always_hint |
| || (abs (prob) > REG_BR_PROB_BASE / 100 * 48 |
| && br_prob_note_reliable_p (note))) |
| { |
| if (abs (prob) > REG_BR_PROB_BASE / 20 |
| && ((prob > 0) ^ need_longbranch)) |
| pred = "+"; |
| else |
| pred = "-"; |
| } |
| } |
| |
| if (label == NULL) |
| s += sprintf (s, "{b%sr|b%slr%s} ", ccode, ccode, pred); |
| else |
| s += sprintf (s, "{b%s|b%s%s} ", ccode, ccode, pred); |
| |
| /* We need to escape any '%' characters in the reg_names string. |
| Assume they'd only be the first character.... */ |
| if (reg_names[cc_regno + CR0_REGNO][0] == '%') |
| *s++ = '%'; |
| s += sprintf (s, "%s", reg_names[cc_regno + CR0_REGNO]); |
| |
| if (label != NULL) |
| { |
| /* If the branch distance was too far, we may have to use an |
| unconditional branch to go the distance. */ |
| if (need_longbranch) |
| s += sprintf (s, ",$+8\n\tb %s", label); |
| else |
| s += sprintf (s, ",%s", label); |
| } |
| |
| return string; |
| } |
| |
| /* Return the string to flip the GT bit on a CR. */ |
| char * |
| output_e500_flip_gt_bit (rtx dst, rtx src) |
| { |
| static char string[64]; |
| int a, b; |
| |
| gcc_assert (GET_CODE (dst) == REG && CR_REGNO_P (REGNO (dst)) |
| && GET_CODE (src) == REG && CR_REGNO_P (REGNO (src))); |
| |
| /* GT bit. */ |
| a = 4 * (REGNO (dst) - CR0_REGNO) + 1; |
| b = 4 * (REGNO (src) - CR0_REGNO) + 1; |
| |
| sprintf (string, "crnot %d,%d", a, b); |
| return string; |
| } |
| |
| /* Return insn index for the vector compare instruction for given CODE, |
| and DEST_MODE, OP_MODE. Return INSN_NOT_AVAILABLE if valid insn is |
| not available. */ |
| |
| static int |
| get_vec_cmp_insn (enum rtx_code code, |
| enum machine_mode dest_mode, |
| enum machine_mode op_mode) |
| { |
| if (!TARGET_ALTIVEC) |
| return INSN_NOT_AVAILABLE; |
| |
| switch (code) |
| { |
| case EQ: |
| if (dest_mode == V16QImode && op_mode == V16QImode) |
| return UNSPEC_VCMPEQUB; |
| if (dest_mode == V8HImode && op_mode == V8HImode) |
| return UNSPEC_VCMPEQUH; |
| if (dest_mode == V4SImode && op_mode == V4SImode) |
| return UNSPEC_VCMPEQUW; |
| if (dest_mode == V4SImode && op_mode == V4SFmode) |
| return UNSPEC_VCMPEQFP; |
| break; |
| case GE: |
| if (dest_mode == V4SImode && op_mode == V4SFmode) |
| return UNSPEC_VCMPGEFP; |
| case GT: |
| if (dest_mode == V16QImode && op_mode == V16QImode) |
| return UNSPEC_VCMPGTSB; |
| if (dest_mode == V8HImode && op_mode == V8HImode) |
| return UNSPEC_VCMPGTSH; |
| if (dest_mode == V4SImode && op_mode == V4SImode) |
| return UNSPEC_VCMPGTSW; |
| if (dest_mode == V4SImode && op_mode == V4SFmode) |
| return UNSPEC_VCMPGTFP; |
| break; |
| case GTU: |
| if (dest_mode == V16QImode && op_mode == V16QImode) |
| return UNSPEC_VCMPGTUB; |
| if (dest_mode == V8HImode && op_mode == V8HImode) |
| return UNSPEC_VCMPGTUH; |
| if (dest_mode == V4SImode && op_mode == V4SImode) |
| return UNSPEC_VCMPGTUW; |
| break; |
| default: |
| break; |
| } |
| return INSN_NOT_AVAILABLE; |
| } |
| |
| /* Emit vector compare for operands OP0 and OP1 using code RCODE. |
| DMODE is expected destination mode. This is a recursive function. */ |
| |
| static rtx |
| rs6000_emit_vector_compare (enum rtx_code rcode, |
| rtx op0, rtx op1, |
| enum machine_mode dmode) |
| { |
| int vec_cmp_insn; |
| rtx mask; |
| enum machine_mode dest_mode; |
| enum machine_mode op_mode = GET_MODE (op1); |
| |
| gcc_assert (TARGET_ALTIVEC); |
| gcc_assert (GET_MODE (op0) == GET_MODE (op1)); |
| |
| /* Floating point vector compare instructions uses destination V4SImode. |
| Move destination to appropriate mode later. */ |
| if (dmode == V4SFmode) |
| dest_mode = V4SImode; |
| else |
| dest_mode = dmode; |
| |
| mask = gen_reg_rtx (dest_mode); |
| vec_cmp_insn = get_vec_cmp_insn (rcode, dest_mode, op_mode); |
| |
| if (vec_cmp_insn == INSN_NOT_AVAILABLE) |
| { |
| bool swap_operands = false; |
| bool try_again = false; |
| switch (rcode) |
| { |
| case LT: |
| rcode = GT; |
| swap_operands = true; |
| try_again = true; |
| break; |
| case LTU: |
| rcode = GTU; |
| swap_operands = true; |
| try_again = true; |
| break; |
| case NE: |
| /* APPLE LOCAL begin mainline 2007-01-18 4875159 */ |
| case UNLE: |
| case UNLT: |
| case UNGE: |
| case UNGT: |
| /* Invert condition and try again. |
| e.g., A != B becomes ~(A==B). */ |
| { |
| enum rtx_code rev_code; |
| enum insn_code nor_code; |
| rtx eq_rtx; |
| |
| rev_code = reverse_condition_maybe_unordered (rcode); |
| eq_rtx = rs6000_emit_vector_compare (rev_code, op0, op1, |
| dest_mode); |
| |
| nor_code = one_cmpl_optab->handlers[(int)dest_mode].insn_code; |
| gcc_assert (nor_code != CODE_FOR_nothing); |
| emit_insn (GEN_FCN (nor_code) (mask, eq_rtx)); |
| |
| if (dmode != dest_mode) |
| { |
| rtx temp = gen_reg_rtx (dest_mode); |
| convert_move (temp, mask, 0); |
| return temp; |
| } |
| return mask; |
| } |
| break; |
| /* APPLE LOCAL end mainline 2007-01-18 4875159 */ |
| case GE: |
| case GEU: |
| case LE: |
| case LEU: |
| /* Try GT/GTU/LT/LTU OR EQ */ |
| { |
| rtx c_rtx, eq_rtx; |
| enum insn_code ior_code; |
| enum rtx_code new_code; |
| |
| switch (rcode) |
| { |
| case GE: |
| new_code = GT; |
| break; |
| |
| case GEU: |
| new_code = GTU; |
| break; |
| |
| case LE: |
| new_code = LT; |
| break; |
| |
| case LEU: |
| new_code = LTU; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| c_rtx = rs6000_emit_vector_compare (new_code, |
| op0, op1, dest_mode); |
| eq_rtx = rs6000_emit_vector_compare (EQ, op0, op1, |
| dest_mode); |
| |
| ior_code = ior_optab->handlers[(int)dest_mode].insn_code; |
| gcc_assert (ior_code != CODE_FOR_nothing); |
| emit_insn (GEN_FCN (ior_code) (mask, c_rtx, eq_rtx)); |
| if (dmode != dest_mode) |
| { |
| rtx temp = gen_reg_rtx (dest_mode); |
| convert_move (temp, mask, 0); |
| return temp; |
| } |
| return mask; |
| } |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (try_again) |
| { |
| vec_cmp_insn = get_vec_cmp_insn (rcode, dest_mode, op_mode); |
| /* You only get two chances. */ |
| gcc_assert (vec_cmp_insn != INSN_NOT_AVAILABLE); |
| } |
| |
| if (swap_operands) |
| { |
| rtx tmp; |
| tmp = op0; |
| op0 = op1; |
| op1 = tmp; |
| } |
| } |
| |
| emit_insn (gen_rtx_SET (VOIDmode, mask, |
| gen_rtx_UNSPEC (dest_mode, |
| gen_rtvec (2, op0, op1), |
| vec_cmp_insn))); |
| if (dmode != dest_mode) |
| { |
| rtx temp = gen_reg_rtx (dest_mode); |
| convert_move (temp, mask, 0); |
| return temp; |
| } |
| return mask; |
| } |
| |
| /* Return vector select instruction for MODE. Return INSN_NOT_AVAILABLE, if |
| valid insn doesn exist for given mode. */ |
| |
| static int |
| get_vsel_insn (enum machine_mode mode) |
| { |
| switch (mode) |
| { |
| case V4SImode: |
| return UNSPEC_VSEL4SI; |
| break; |
| case V4SFmode: |
| return UNSPEC_VSEL4SF; |
| break; |
| case V8HImode: |
| return UNSPEC_VSEL8HI; |
| break; |
| case V16QImode: |
| return UNSPEC_VSEL16QI; |
| break; |
| default: |
| return INSN_NOT_AVAILABLE; |
| break; |
| } |
| return INSN_NOT_AVAILABLE; |
| } |
| |
| /* Emit vector select insn where DEST is destination using |
| operands OP1, OP2 and MASK. */ |
| |
| static void |
| rs6000_emit_vector_select (rtx dest, rtx op1, rtx op2, rtx mask) |
| { |
| rtx t, temp; |
| enum machine_mode dest_mode = GET_MODE (dest); |
| int vsel_insn_index = get_vsel_insn (GET_MODE (dest)); |
| |
| temp = gen_reg_rtx (dest_mode); |
| |
| /* For each vector element, select op1 when mask is 1 otherwise |
| select op2. */ |
| t = gen_rtx_SET (VOIDmode, temp, |
| gen_rtx_UNSPEC (dest_mode, |
| gen_rtvec (3, op2, op1, mask), |
| vsel_insn_index)); |
| emit_insn (t); |
| emit_move_insn (dest, temp); |
| return; |
| } |
| |
| /* Emit vector conditional expression. |
| DEST is destination. OP1 and OP2 are two VEC_COND_EXPR operands. |
| CC_OP0 and CC_OP1 are the two operands for the relation operation COND. */ |
| |
| int |
| rs6000_emit_vector_cond_expr (rtx dest, rtx op1, rtx op2, |
| rtx cond, rtx cc_op0, rtx cc_op1) |
| { |
| enum machine_mode dest_mode = GET_MODE (dest); |
| enum rtx_code rcode = GET_CODE (cond); |
| rtx mask; |
| |
| if (!TARGET_ALTIVEC) |
| return 0; |
| |
| /* Get the vector mask for the given relational operations. */ |
| mask = rs6000_emit_vector_compare (rcode, cc_op0, cc_op1, dest_mode); |
| |
| rs6000_emit_vector_select (dest, op1, op2, mask); |
| |
| return 1; |
| } |
| |
| /* Emit a conditional move: move TRUE_COND to DEST if OP of the |
| operands of the last comparison is nonzero/true, FALSE_COND if it |
| is zero/false. Return 0 if the hardware has no such operation. */ |
| |
| int |
| rs6000_emit_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond) |
| { |
| enum rtx_code code = GET_CODE (op); |
| rtx op0 = rs6000_compare_op0; |
| rtx op1 = rs6000_compare_op1; |
| REAL_VALUE_TYPE c1; |
| enum machine_mode compare_mode = GET_MODE (op0); |
| enum machine_mode result_mode = GET_MODE (dest); |
| rtx temp; |
| bool is_against_zero; |
| |
| /* These modes should always match. */ |
| if (GET_MODE (op1) != compare_mode |
| /* In the isel case however, we can use a compare immediate, so |
| op1 may be a small constant. */ |
| && (!TARGET_ISEL || !short_cint_operand (op1, VOIDmode))) |
| return 0; |
| if (GET_MODE (true_cond) != result_mode) |
| return 0; |
| if (GET_MODE (false_cond) != result_mode) |
| return 0; |
| |
| /* First, work out if the hardware can do this at all, or |
| if it's too slow.... */ |
| if (! rs6000_compare_fp_p) |
| { |
| if (TARGET_ISEL) |
| return rs6000_emit_int_cmove (dest, op, true_cond, false_cond); |
| return 0; |
| } |
| else if (TARGET_E500 && TARGET_HARD_FLOAT && !TARGET_FPRS |
| && SCALAR_FLOAT_MODE_P (compare_mode)) |
| return 0; |
| |
| is_against_zero = op1 == CONST0_RTX (compare_mode); |
| |
| /* A floating-point subtract might overflow, underflow, or produce |
| an inexact result, thus changing the floating-point flags, so it |
| can't be generated if we care about that. It's safe if one side |
| of the construct is zero, since then no subtract will be |
| generated. */ |
| if (SCALAR_FLOAT_MODE_P (compare_mode) |
| && flag_trapping_math && ! is_against_zero) |
| return 0; |
| |
| /* Eliminate half of the comparisons by switching operands, this |
| makes the remaining code simpler. */ |
| if (code == UNLT || code == UNGT || code == UNORDERED || code == NE |
| || code == LTGT || code == LT || code == UNLE) |
| { |
| code = reverse_condition_maybe_unordered (code); |
| temp = true_cond; |
| true_cond = false_cond; |
| false_cond = temp; |
| } |
| |
| /* UNEQ and LTGT take four instructions for a comparison with zero, |
| it'll probably be faster to use a branch here too. */ |
| if (code == UNEQ && HONOR_NANS (compare_mode)) |
| return 0; |
| |
| if (GET_CODE (op1) == CONST_DOUBLE) |
| REAL_VALUE_FROM_CONST_DOUBLE (c1, op1); |
| |
| /* We're going to try to implement comparisons by performing |
| a subtract, then comparing against zero. Unfortunately, |
| Inf - Inf is NaN which is not zero, and so if we don't |
| know that the operand is finite and the comparison |
| would treat EQ different to UNORDERED, we can't do it. */ |
| if (HONOR_INFINITIES (compare_mode) |
| && code != GT && code != UNGE |
| && (GET_CODE (op1) != CONST_DOUBLE || real_isinf (&c1)) |
| /* Constructs of the form (a OP b ? a : b) are safe. */ |
| && ((! rtx_equal_p (op0, false_cond) && ! rtx_equal_p (op1, false_cond)) |
| || (! rtx_equal_p (op0, true_cond) |
| && ! rtx_equal_p (op1, true_cond)))) |
| return 0; |
| |
| /* At this point we know we can use fsel. */ |
| |
| /* Reduce the comparison to a comparison against zero. */ |
| if (! is_against_zero) |
| { |
| temp = gen_reg_rtx (compare_mode); |
| emit_insn (gen_rtx_SET (VOIDmode, temp, |
| gen_rtx_MINUS (compare_mode, op0, op1))); |
| op0 = temp; |
| op1 = CONST0_RTX (compare_mode); |
| } |
| |
| /* If we don't care about NaNs we can reduce some of the comparisons |
| down to faster ones. */ |
| if (! HONOR_NANS (compare_mode)) |
| switch (code) |
| { |
| case GT: |
| code = LE; |
| temp = true_cond; |
| true_cond = false_cond; |
| false_cond = temp; |
| break; |
| case UNGE: |
| code = GE; |
| break; |
| case UNEQ: |
| code = EQ; |
| break; |
| default: |
| break; |
| } |
| |
| /* Now, reduce everything down to a GE. */ |
| switch (code) |
| { |
| case GE: |
| break; |
| |
| case LE: |
| temp = gen_reg_rtx (compare_mode); |
| emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_NEG (compare_mode, op0))); |
| op0 = temp; |
| break; |
| |
| case ORDERED: |
| temp = gen_reg_rtx (compare_mode); |
| emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_ABS (compare_mode, op0))); |
| op0 = temp; |
| break; |
| |
| case EQ: |
| temp = gen_reg_rtx (compare_mode); |
| emit_insn (gen_rtx_SET (VOIDmode, temp, |
| gen_rtx_NEG (compare_mode, |
| gen_rtx_ABS (compare_mode, op0)))); |
| op0 = temp; |
| break; |
| |
| case UNGE: |
| /* a UNGE 0 <-> (a GE 0 || -a UNLT 0) */ |
| temp = gen_reg_rtx (result_mode); |
| emit_insn (gen_rtx_SET (VOIDmode, temp, |
| gen_rtx_IF_THEN_ELSE (result_mode, |
| gen_rtx_GE (VOIDmode, |
| op0, op1), |
| true_cond, false_cond))); |
| false_cond = true_cond; |
| true_cond = temp; |
| |
| temp = gen_reg_rtx (compare_mode); |
| emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_NEG (compare_mode, op0))); |
| op0 = temp; |
| break; |
| |
| case GT: |
| /* a GT 0 <-> (a GE 0 && -a UNLT 0) */ |
| temp = gen_reg_rtx (result_mode); |
| emit_insn (gen_rtx_SET (VOIDmode, temp, |
| gen_rtx_IF_THEN_ELSE (result_mode, |
| gen_rtx_GE (VOIDmode, |
| op0, op1), |
| true_cond, false_cond))); |
| true_cond = false_cond; |
| false_cond = temp; |
| |
| temp = gen_reg_rtx (compare_mode); |
| emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_NEG (compare_mode, op0))); |
| op0 = temp; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| emit_insn (gen_rtx_SET (VOIDmode, dest, |
| gen_rtx_IF_THEN_ELSE (result_mode, |
| gen_rtx_GE (VOIDmode, |
| op0, op1), |
| true_cond, false_cond))); |
| return 1; |
| } |
| |
| /* Same as above, but for ints (isel). */ |
| |
| static int |
| rs6000_emit_int_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond) |
| { |
| rtx condition_rtx, cr; |
| |
| /* All isel implementations thus far are 32-bits. */ |
| if (GET_MODE (rs6000_compare_op0) != SImode) |
| return 0; |
| |
| /* We still have to do the compare, because isel doesn't do a |
| compare, it just looks at the CRx bits set by a previous compare |
| instruction. */ |
| condition_rtx = rs6000_generate_compare (GET_CODE (op)); |
| cr = XEXP (condition_rtx, 0); |
| |
| if (GET_MODE (cr) == CCmode) |
| emit_insn (gen_isel_signed (dest, condition_rtx, |
| true_cond, false_cond, cr)); |
| else |
| emit_insn (gen_isel_unsigned (dest, condition_rtx, |
| true_cond, false_cond, cr)); |
| |
| return 1; |
| } |
| |
| const char * |
| output_isel (rtx *operands) |
| { |
| enum rtx_code code; |
| |
| code = GET_CODE (operands[1]); |
| if (code == GE || code == GEU || code == LE || code == LEU || code == NE) |
| { |
| PUT_CODE (operands[1], reverse_condition (code)); |
| return "isel %0,%3,%2,%j1"; |
| } |
| else |
| return "isel %0,%2,%3,%j1"; |
| } |
| |
| void |
| rs6000_emit_minmax (rtx dest, enum rtx_code code, rtx op0, rtx op1) |
| { |
| enum machine_mode mode = GET_MODE (op0); |
| enum rtx_code c; |
| rtx target; |
| |
| if (code == SMAX || code == SMIN) |
| c = GE; |
| else |
| c = GEU; |
| |
| if (code == SMAX || code == UMAX) |
| target = emit_conditional_move (dest, c, op0, op1, mode, |
| op0, op1, mode, 0); |
| else |
| target = emit_conditional_move (dest, c, op0, op1, mode, |
| op1, op0, mode, 0); |
| /* APPLE LOCAL begin pragma fenv 3943021 */ |
| if (flag_trapping_math && target == NULL_RTX) |
| return; |
| /* APPLE LOCAL end pragma fenv 3943021 */ |
| gcc_assert (target); |
| if (target != dest) |
| emit_move_insn (dest, target); |
| } |
| |
| /* Emit instructions to perform a load-reserved/store-conditional operation. |
| The operation performed is an atomic |
| (set M (CODE:MODE M OP)) |
| If not NULL, BEFORE is atomically set to M before the operation, and |
| AFTER is set to M after the operation (that is, (CODE:MODE M OP)). |
| If SYNC_P then a memory barrier is emitted before the operation. |
| Either OP or M may be wrapped in a NOT operation. */ |
| |
| void |
| rs6000_emit_sync (enum rtx_code code, enum machine_mode mode, |
| rtx m, rtx op, rtx before_param, rtx after_param, |
| bool sync_p) |
| { |
| enum machine_mode used_mode; |
| rtx the_op, set_before, set_after, set_atomic, cc_scratch, before, after; |
| rtx used_m; |
| rtvec vec; |
| HOST_WIDE_INT imask = GET_MODE_MASK (mode); |
| rtx shift = NULL_RTX; |
| |
| if (sync_p) |
| emit_insn (gen_memory_barrier ()); |
| |
| if (GET_CODE (m) == NOT) |
| used_m = XEXP (m, 0); |
| else |
| used_m = m; |
| |
| /* If this is smaller than SImode, we'll have to use SImode with |
| adjustments. */ |
| if (mode == QImode || mode == HImode) |
| { |
| rtx newop, oldop; |
| |
| if (MEM_ALIGN (used_m) >= 32) |
| { |
| int ishift = 0; |
| if (BYTES_BIG_ENDIAN) |
| ishift = GET_MODE_BITSIZE (SImode) - GET_MODE_BITSIZE (mode); |
| |
| shift = GEN_INT (ishift); |
| } |
| else |
| { |
| rtx addrSI, aligned_addr; |
| int shift_mask = mode == QImode ? 0x18 : 0x10; |
| |
| addrSI = force_reg (SImode, gen_lowpart_common (SImode, |
| XEXP (used_m, 0))); |
| shift = gen_reg_rtx (SImode); |
| |
| emit_insn (gen_rlwinm (shift, addrSI, GEN_INT (3), |
| GEN_INT (shift_mask))); |
| emit_insn (gen_xorsi3 (shift, shift, GEN_INT (shift_mask))); |
| |
| aligned_addr = expand_binop (Pmode, and_optab, |
| XEXP (used_m, 0), |
| GEN_INT (-4), NULL_RTX, |
| 1, OPTAB_LIB_WIDEN); |
| used_m = change_address (used_m, SImode, aligned_addr); |
| set_mem_align (used_m, 32); |
| /* It's safe to keep the old alias set of USED_M, because |
| the operation is atomic and only affects the original |
| USED_M. */ |
| if (GET_CODE (m) == NOT) |
| m = gen_rtx_NOT (SImode, used_m); |
| else |
| m = used_m; |
| } |
| |
| if (GET_CODE (op) == NOT) |
| { |
| oldop = lowpart_subreg (SImode, XEXP (op, 0), mode); |
| oldop = gen_rtx_NOT (SImode, oldop); |
| } |
| else |
| oldop = lowpart_subreg (SImode, op, mode); |
| |
| switch (code) |
| { |
| case IOR: |
| case XOR: |
| newop = expand_binop (SImode, and_optab, |
| oldop, GEN_INT (imask), NULL_RTX, |
| 1, OPTAB_LIB_WIDEN); |
| emit_insn (gen_ashlsi3 (newop, newop, shift)); |
| break; |
| |
| case AND: |
| newop = expand_binop (SImode, ior_optab, |
| oldop, GEN_INT (~imask), NULL_RTX, |
| 1, OPTAB_LIB_WIDEN); |
| emit_insn (gen_rotlsi3 (newop, newop, shift)); |
| break; |
| |
| case PLUS: |
| case MINUS: |
| { |
| rtx mask; |
| |
| newop = expand_binop (SImode, and_optab, |
| oldop, GEN_INT (imask), NULL_RTX, |
| 1, OPTAB_LIB_WIDEN); |
| emit_insn (gen_ashlsi3 (newop, newop, shift)); |
| |
| mask = gen_reg_rtx (SImode); |
| emit_move_insn (mask, GEN_INT (imask)); |
| emit_insn (gen_ashlsi3 (mask, mask, shift)); |
| |
| if (code == PLUS) |
| newop = gen_rtx_PLUS (SImode, m, newop); |
| else |
| newop = gen_rtx_MINUS (SImode, m, newop); |
| newop = gen_rtx_AND (SImode, newop, mask); |
| newop = gen_rtx_IOR (SImode, newop, |
| gen_rtx_AND (SImode, |
| gen_rtx_NOT (SImode, mask), |
| m)); |
| break; |
| } |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (GET_CODE (m) == NOT) |
| { |
| rtx mask, xorm; |
| |
| mask = gen_reg_rtx (SImode); |
| emit_move_insn (mask, GEN_INT (imask)); |
| emit_insn (gen_ashlsi3 (mask, mask, shift)); |
| |
| xorm = gen_rtx_XOR (SImode, used_m, mask); |
| /* Depending on the value of 'op', the XOR or the operation might |
| be able to be simplified away. */ |
| newop = simplify_gen_binary (code, SImode, xorm, newop); |
| } |
| op = newop; |
| used_mode = SImode; |
| before = gen_reg_rtx (used_mode); |
| after = gen_reg_rtx (used_mode); |
| } |
| else |
| { |
| used_mode = mode; |
| before = before_param; |
| after = after_param; |
| |
| if (before == NULL_RTX) |
| before = gen_reg_rtx (used_mode); |
| if (after == NULL_RTX) |
| after = gen_reg_rtx (used_mode); |
| } |
| |
| if ((code == PLUS || code == MINUS || GET_CODE (m) == NOT) |
| && used_mode != mode) |
| the_op = op; /* Computed above. */ |
| else if (GET_CODE (op) == NOT && GET_CODE (m) != NOT) |
| the_op = gen_rtx_fmt_ee (code, used_mode, op, m); |
| else |
| the_op = gen_rtx_fmt_ee (code, used_mode, m, op); |
| |
| set_after = gen_rtx_SET (VOIDmode, after, the_op); |
| set_before = gen_rtx_SET (VOIDmode, before, used_m); |
| set_atomic = gen_rtx_SET (VOIDmode, used_m, |
| gen_rtx_UNSPEC (used_mode, |
| gen_rtvec (1, the_op), |
| UNSPEC_SYNC_OP)); |
| cc_scratch = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (CCmode)); |
| |
| if ((code == PLUS || code == MINUS) && used_mode != mode) |
| vec = gen_rtvec (5, set_after, set_before, set_atomic, cc_scratch, |
| gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode))); |
| else |
| vec = gen_rtvec (4, set_after, set_before, set_atomic, cc_scratch); |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, vec)); |
| |
| /* Shift and mask the return values properly. */ |
| if (used_mode != mode && before_param) |
| { |
| emit_insn (gen_lshrsi3 (before, before, shift)); |
| convert_move (before_param, before, 1); |
| } |
| |
| if (used_mode != mode && after_param) |
| { |
| emit_insn (gen_lshrsi3 (after, after, shift)); |
| convert_move (after_param, after, 1); |
| } |
| |
| /* The previous sequence will end with a branch that's dependent on |
| the conditional store, so placing an isync will ensure that no |
| other instructions (especially, no load or store instructions) |
| can start before the atomic operation completes. */ |
| if (sync_p) |
| emit_insn (gen_isync ()); |
| } |
| |
| /* A subroutine of the atomic operation splitters. Jump to LABEL if |
| COND is true. Mark the jump as unlikely to be taken. */ |
| |
| static void |
| emit_unlikely_jump (rtx cond, rtx label) |
| { |
| rtx very_unlikely = GEN_INT (REG_BR_PROB_BASE / 100 - 1); |
| rtx x; |
| |
| x = gen_rtx_IF_THEN_ELSE (VOIDmode, cond, label, pc_rtx); |
| x = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, x)); |
| REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_BR_PROB, very_unlikely, NULL_RTX); |
| } |
| |
| /* A subroutine of the atomic operation splitters. Emit a load-locked |
| instruction in MODE. */ |
| |
| static void |
| emit_load_locked (enum machine_mode mode, rtx reg, rtx mem) |
| { |
| rtx (*fn) (rtx, rtx) = NULL; |
| if (mode == SImode) |
| fn = gen_load_locked_si; |
| else if (mode == DImode) |
| fn = gen_load_locked_di; |
| emit_insn (fn (reg, mem)); |
| } |
| |
| /* A subroutine of the atomic operation splitters. Emit a store-conditional |
| instruction in MODE. */ |
| |
| static void |
| emit_store_conditional (enum machine_mode mode, rtx res, rtx mem, rtx val) |
| { |
| rtx (*fn) (rtx, rtx, rtx) = NULL; |
| if (mode == SImode) |
| fn = gen_store_conditional_si; |
| else if (mode == DImode) |
| fn = gen_store_conditional_di; |
| |
| /* Emit sync before stwcx. to address PPC405 Erratum. */ |
| if (PPC405_ERRATUM77) |
| emit_insn (gen_memory_barrier ()); |
| |
| emit_insn (fn (res, mem, val)); |
| } |
| |
| /* Expand an an atomic fetch-and-operate pattern. CODE is the binary operation |
| to perform. MEM is the memory on which to operate. VAL is the second |
| operand of the binary operator. BEFORE and AFTER are optional locations to |
| return the value of MEM either before of after the operation. SCRATCH is |
| a scratch register. */ |
| |
| void |
| rs6000_split_atomic_op (enum rtx_code code, rtx mem, rtx val, |
| rtx before, rtx after, rtx scratch) |
| { |
| enum machine_mode mode = GET_MODE (mem); |
| rtx label, x, cond = gen_rtx_REG (CCmode, CR0_REGNO); |
| |
| emit_insn (gen_memory_barrier ()); |
| |
| label = gen_label_rtx (); |
| emit_label (label); |
| label = gen_rtx_LABEL_REF (VOIDmode, label); |
| |
| if (before == NULL_RTX) |
| before = scratch; |
| emit_load_locked (mode, before, mem); |
| |
| if (code == NOT) |
| x = gen_rtx_AND (mode, gen_rtx_NOT (mode, before), val); |
| else if (code == AND) |
| x = gen_rtx_UNSPEC (mode, gen_rtvec (2, before, val), UNSPEC_AND); |
| else |
| x = gen_rtx_fmt_ee (code, mode, before, val); |
| |
| if (after != NULL_RTX) |
| emit_insn (gen_rtx_SET (VOIDmode, after, copy_rtx (x))); |
| emit_insn (gen_rtx_SET (VOIDmode, scratch, x)); |
| |
| emit_store_conditional (mode, cond, mem, scratch); |
| |
| x = gen_rtx_NE (VOIDmode, cond, const0_rtx); |
| emit_unlikely_jump (x, label); |
| |
| emit_insn (gen_isync ()); |
| } |
| |
| /* Expand an atomic compare and swap operation. MEM is the memory on which |
| to operate. OLDVAL is the old value to be compared. NEWVAL is the new |
| value to be stored. SCRATCH is a scratch GPR. */ |
| |
| void |
| rs6000_split_compare_and_swap (rtx retval, rtx mem, rtx oldval, rtx newval, |
| rtx scratch) |
| { |
| enum machine_mode mode = GET_MODE (mem); |
| rtx label1, label2, x, cond = gen_rtx_REG (CCmode, CR0_REGNO); |
| |
| emit_insn (gen_memory_barrier ()); |
| |
| label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); |
| label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); |
| emit_label (XEXP (label1, 0)); |
| |
| emit_load_locked (mode, retval, mem); |
| |
| x = gen_rtx_COMPARE (CCmode, retval, oldval); |
| emit_insn (gen_rtx_SET (VOIDmode, cond, x)); |
| |
| x = gen_rtx_NE (VOIDmode, cond, const0_rtx); |
| emit_unlikely_jump (x, label2); |
| |
| emit_move_insn (scratch, newval); |
| emit_store_conditional (mode, cond, mem, scratch); |
| |
| x = gen_rtx_NE (VOIDmode, cond, const0_rtx); |
| emit_unlikely_jump (x, label1); |
| |
| emit_insn (gen_isync ()); |
| emit_label (XEXP (label2, 0)); |
| } |
| |
| /* Expand an atomic test and set operation. MEM is the memory on which |
| to operate. VAL is the value set. SCRATCH is a scratch GPR. */ |
| |
| void |
| rs6000_split_lock_test_and_set (rtx retval, rtx mem, rtx val, rtx scratch) |
| { |
| enum machine_mode mode = GET_MODE (mem); |
| rtx label, x, cond = gen_rtx_REG (CCmode, CR0_REGNO); |
| |
| emit_insn (gen_memory_barrier ()); |
| |
| label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); |
| emit_label (XEXP (label, 0)); |
| |
| emit_load_locked (mode, retval, mem); |
| emit_move_insn (scratch, val); |
| emit_store_conditional (mode, cond, mem, scratch); |
| |
| x = gen_rtx_NE (VOIDmode, cond, const0_rtx); |
| emit_unlikely_jump (x, label); |
| |
| emit_insn (gen_isync ()); |
| } |
| |
| void |
| rs6000_expand_compare_and_swapqhi (rtx dst, rtx mem, rtx oldval, rtx newval) |
| { |
| enum machine_mode mode = GET_MODE (mem); |
| rtx addrSI, align, wdst, shift, mask; |
| HOST_WIDE_INT shift_mask = mode == QImode ? 0x18 : 0x10; |
| HOST_WIDE_INT imask = GET_MODE_MASK (mode); |
| |
| /* Shift amount for subword relative to aligned word. */ |
| addrSI = force_reg (SImode, gen_lowpart_common (SImode, XEXP (mem, 0))); |
| shift = gen_reg_rtx (SImode); |
| emit_insn (gen_rlwinm (shift, addrSI, GEN_INT (3), |
| GEN_INT (shift_mask))); |
| emit_insn (gen_xorsi3 (shift, shift, GEN_INT (shift_mask))); |
| |
| /* Shift and mask old value into position within word. */ |
| oldval = convert_modes (SImode, mode, oldval, 1); |
| oldval = expand_binop (SImode, and_optab, |
| oldval, GEN_INT (imask), NULL_RTX, |
| 1, OPTAB_LIB_WIDEN); |
| emit_insn (gen_ashlsi3 (oldval, oldval, shift)); |
| |
| /* Shift and mask new value into position within word. */ |
| newval = convert_modes (SImode, mode, newval, 1); |
| newval = expand_binop (SImode, and_optab, |
| newval, GEN_INT (imask), NULL_RTX, |
| 1, OPTAB_LIB_WIDEN); |
| emit_insn (gen_ashlsi3 (newval, newval, shift)); |
| |
| /* Mask for insertion. */ |
| mask = gen_reg_rtx (SImode); |
| emit_move_insn (mask, GEN_INT (imask)); |
| emit_insn (gen_ashlsi3 (mask, mask, shift)); |
| |
| /* Address of aligned word containing subword. */ |
| align = expand_binop (Pmode, and_optab, XEXP (mem, 0), GEN_INT (-4), |
| NULL_RTX, 1, OPTAB_LIB_WIDEN); |
| mem = change_address (mem, SImode, align); |
| set_mem_align (mem, 32); |
| MEM_VOLATILE_P (mem) = 1; |
| |
| wdst = gen_reg_rtx (SImode); |
| emit_insn (gen_sync_compare_and_swapqhi_internal (wdst, mask, |
| oldval, newval, mem)); |
| |
| emit_move_insn (dst, gen_lowpart (mode, wdst)); |
| } |
| |
| void |
| rs6000_split_compare_and_swapqhi (rtx dest, rtx mask, |
| rtx oldval, rtx newval, rtx mem, |
| rtx scratch) |
| { |
| rtx label1, label2, x, cond = gen_rtx_REG (CCmode, CR0_REGNO); |
| |
| emit_insn (gen_memory_barrier ()); |
| label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); |
| label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); |
| emit_label (XEXP (label1, 0)); |
| |
| emit_load_locked (SImode, scratch, mem); |
| |
| /* Mask subword within loaded value for comparison with oldval. |
| Use UNSPEC_AND to avoid clobber.*/ |
| emit_insn (gen_rtx_SET (SImode, dest, |
| gen_rtx_UNSPEC (SImode, |
| gen_rtvec (2, scratch, mask), |
| UNSPEC_AND))); |
| |
| x = gen_rtx_COMPARE (CCmode, dest, oldval); |
| emit_insn (gen_rtx_SET (VOIDmode, cond, x)); |
| |
| x = gen_rtx_NE (VOIDmode, cond, const0_rtx); |
| emit_unlikely_jump (x, label2); |
| |
| /* Clear subword within loaded value for insertion of new value. */ |
| emit_insn (gen_rtx_SET (SImode, scratch, |
| gen_rtx_AND (SImode, |
| gen_rtx_NOT (SImode, mask), scratch))); |
| emit_insn (gen_iorsi3 (scratch, scratch, newval)); |
| emit_store_conditional (SImode, cond, mem, scratch); |
| |
| x = gen_rtx_NE (VOIDmode, cond, const0_rtx); |
| emit_unlikely_jump (x, label1); |
| |
| emit_insn (gen_isync ()); |
| emit_label (XEXP (label2, 0)); |
| } |
| |
| |
| /* Emit instructions to move SRC to DST. Called by splitters for |
| multi-register moves. It will emit at most one instruction for |
| each register that is accessed; that is, it won't emit li/lis pairs |
| (or equivalent for 64-bit code). One of SRC or DST must be a hard |
| register. */ |
| |
| void |
| rs6000_split_multireg_move (rtx dst, rtx src) |
| { |
| /* The register number of the first register being moved. */ |
| int reg; |
| /* The mode that is to be moved. */ |
| enum machine_mode mode; |
| /* The mode that the move is being done in, and its size. */ |
| enum machine_mode reg_mode; |
| int reg_mode_size; |
| /* The number of registers that will be moved. */ |
| int nregs; |
| |
| reg = REG_P (dst) ? REGNO (dst) : REGNO (src); |
| mode = GET_MODE (dst); |
| nregs = hard_regno_nregs[reg][mode]; |
| if (FP_REGNO_P (reg)) |
| reg_mode = DFmode; |
| else if (ALTIVEC_REGNO_P (reg)) |
| reg_mode = V16QImode; |
| else if (TARGET_E500_DOUBLE && mode == TFmode) |
| reg_mode = DFmode; |
| else |
| reg_mode = word_mode; |
| reg_mode_size = GET_MODE_SIZE (reg_mode); |
| |
| gcc_assert (reg_mode_size * nregs == GET_MODE_SIZE (mode)); |
| |
| if (REG_P (src) && REG_P (dst) && (REGNO (src) < REGNO (dst))) |
| { |
| /* Move register range backwards, if we might have destructive |
| overlap. */ |
| int i; |
| for (i = nregs - 1; i >= 0; i--) |
| emit_insn (gen_rtx_SET (VOIDmode, |
| simplify_gen_subreg (reg_mode, dst, mode, |
| i * reg_mode_size), |
| simplify_gen_subreg (reg_mode, src, mode, |
| i * reg_mode_size))); |
| } |
| else |
| { |
| int i; |
| int j = -1; |
| bool used_update = false; |
| |
| if (MEM_P (src) && INT_REGNO_P (reg)) |
| { |
| rtx breg; |
| |
| if (GET_CODE (XEXP (src, 0)) == PRE_INC |
| || GET_CODE (XEXP (src, 0)) == PRE_DEC) |
| { |
| rtx delta_rtx; |
| breg = XEXP (XEXP (src, 0), 0); |
| delta_rtx = (GET_CODE (XEXP (src, 0)) == PRE_INC |
| ? GEN_INT (GET_MODE_SIZE (GET_MODE (src))) |
| : GEN_INT (-GET_MODE_SIZE (GET_MODE (src)))); |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (breg, breg, delta_rtx) |
| : gen_adddi3 (breg, breg, delta_rtx)); |
| src = replace_equiv_address (src, breg); |
| } |
| else if (! rs6000_offsettable_memref_p (src)) |
| { |
| rtx basereg; |
| basereg = gen_rtx_REG (Pmode, reg); |
| emit_insn (gen_rtx_SET (VOIDmode, basereg, XEXP (src, 0))); |
| src = replace_equiv_address (src, basereg); |
| } |
| |
| breg = XEXP (src, 0); |
| if (GET_CODE (breg) == PLUS || GET_CODE (breg) == LO_SUM) |
| breg = XEXP (breg, 0); |
| |
| /* If the base register we are using to address memory is |
| also a destination reg, then change that register last. */ |
| if (REG_P (breg) |
| && REGNO (breg) >= REGNO (dst) |
| && REGNO (breg) < REGNO (dst) + nregs) |
| j = REGNO (breg) - REGNO (dst); |
| } |
| |
| if (GET_CODE (dst) == MEM && INT_REGNO_P (reg)) |
| { |
| rtx breg; |
| |
| if (GET_CODE (XEXP (dst, 0)) == PRE_INC |
| || GET_CODE (XEXP (dst, 0)) == PRE_DEC) |
| { |
| rtx delta_rtx; |
| breg = XEXP (XEXP (dst, 0), 0); |
| delta_rtx = (GET_CODE (XEXP (dst, 0)) == PRE_INC |
| ? GEN_INT (GET_MODE_SIZE (GET_MODE (dst))) |
| : GEN_INT (-GET_MODE_SIZE (GET_MODE (dst)))); |
| |
| /* We have to update the breg before doing the store. |
| Use store with update, if available. */ |
| |
| if (TARGET_UPDATE) |
| { |
| rtx nsrc = simplify_gen_subreg (reg_mode, src, mode, 0); |
| emit_insn (TARGET_32BIT |
| ? (TARGET_POWERPC64 |
| ? gen_movdi_si_update (breg, breg, delta_rtx, nsrc) |
| : gen_movsi_update (breg, breg, delta_rtx, nsrc)) |
| : gen_movdi_di_update (breg, breg, delta_rtx, nsrc)); |
| used_update = true; |
| } |
| else |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (breg, breg, delta_rtx) |
| : gen_adddi3 (breg, breg, delta_rtx)); |
| dst = replace_equiv_address (dst, breg); |
| } |
| else |
| gcc_assert (rs6000_offsettable_memref_p (dst)); |
| } |
| |
| for (i = 0; i < nregs; i++) |
| { |
| /* Calculate index to next subword. */ |
| ++j; |
| if (j == nregs) |
| j = 0; |
| |
| /* If compiler already emitted move of first word by |
| store with update, no need to do anything. */ |
| if (j == 0 && used_update) |
| continue; |
| |
| emit_insn (gen_rtx_SET (VOIDmode, |
| simplify_gen_subreg (reg_mode, dst, mode, |
| j * reg_mode_size), |
| simplify_gen_subreg (reg_mode, src, mode, |
| j * reg_mode_size))); |
| } |
| } |
| } |
| |
| |
| /* This page contains routines that are used to determine what the |
| function prologue and epilogue code will do and write them out. */ |
| |
| /* Return the first fixed-point register that is required to be |
| saved. 32 if none. */ |
| |
| int |
| first_reg_to_save (void) |
| { |
| int first_reg; |
| |
| /* Find lowest numbered live register. */ |
| for (first_reg = 13; first_reg <= 31; first_reg++) |
| if (regs_ever_live[first_reg] |
| && (! call_used_regs[first_reg] |
| || (first_reg == RS6000_PIC_OFFSET_TABLE_REGNUM |
| && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0) |
| || (DEFAULT_ABI == ABI_DARWIN && flag_pic) |
| || (TARGET_TOC && TARGET_MINIMAL_TOC))))) |
| break; |
| |
| #if TARGET_MACHO |
| if (flag_pic |
| && current_function_uses_pic_offset_table |
| /* APPLE LOCAL volatile pic base reg in leaves */ |
| && cfun->machine->substitute_pic_base_reg == INVALID_REGNUM |
| && first_reg > RS6000_PIC_OFFSET_TABLE_REGNUM) |
| return RS6000_PIC_OFFSET_TABLE_REGNUM; |
| #endif |
| |
| return first_reg; |
| } |
| |
| /* Similar, for FP regs. */ |
| |
| int |
| first_fp_reg_to_save (void) |
| { |
| int first_reg; |
| |
| /* Find lowest numbered live register. */ |
| for (first_reg = 14 + 32; first_reg <= 63; first_reg++) |
| if (regs_ever_live[first_reg]) |
| break; |
| |
| return first_reg; |
| } |
| |
| /* Similar, for AltiVec regs. */ |
| |
| static int |
| first_altivec_reg_to_save (void) |
| { |
| int i; |
| |
| /* Stack frame remains as is unless we are in AltiVec ABI. */ |
| if (! TARGET_ALTIVEC_ABI) |
| return LAST_ALTIVEC_REGNO + 1; |
| |
| /* On Darwin, the unwind routines are compiled without |
| TARGET_ALTIVEC, and use save_world to save/restore the |
| altivec registers when necessary. */ |
| if (DEFAULT_ABI == ABI_DARWIN && current_function_calls_eh_return |
| && ! TARGET_ALTIVEC) |
| return FIRST_ALTIVEC_REGNO + 20; |
| |
| /* Find lowest numbered live register. */ |
| for (i = FIRST_ALTIVEC_REGNO + 20; i <= LAST_ALTIVEC_REGNO; ++i) |
| if (regs_ever_live[i]) |
| break; |
| |
| return i; |
| } |
| |
| /* Return a 32-bit mask of the AltiVec registers we need to set in |
| VRSAVE. Bit n of the return value is 1 if Vn is live. The MSB in |
| the 32-bit word is 0. */ |
| |
| static unsigned int |
| compute_vrsave_mask (void) |
| { |
| unsigned int i, mask = 0; |
| |
| /* On Darwin, the unwind routines are compiled without |
| TARGET_ALTIVEC, and use save_world to save/restore the |
| call-saved altivec registers when necessary. */ |
| if (DEFAULT_ABI == ABI_DARWIN && current_function_calls_eh_return |
| && ! TARGET_ALTIVEC) |
| mask |= 0xFFF; |
| |
| /* First, find out if we use _any_ altivec registers. */ |
| for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i) |
| if (regs_ever_live[i]) |
| mask |= ALTIVEC_REG_BIT (i); |
| |
| if (mask == 0) |
| return mask; |
| |
| /* Next, remove the argument registers from the set. These must |
| be in the VRSAVE mask set by the caller, so we don't need to add |
| them in again. More importantly, the mask we compute here is |
| used to generate CLOBBERs in the set_vrsave insn, and we do not |
| wish the argument registers to die. */ |
| for (i = cfun->args_info.vregno - 1; i >= ALTIVEC_ARG_MIN_REG; --i) |
| mask &= ~ALTIVEC_REG_BIT (i); |
| |
| /* Similarly, remove the return value from the set. */ |
| { |
| bool yes = false; |
| diddle_return_value (is_altivec_return_reg, &yes); |
| if (yes) |
| mask &= ~ALTIVEC_REG_BIT (ALTIVEC_ARG_RETURN); |
| } |
| |
| return mask; |
| } |
| |
| /* For a very restricted set of circumstances, we can cut down the |
| size of prologues/epilogues by calling our own save/restore-the-world |
| routines. */ |
| |
| static void |
| compute_save_world_info (rs6000_stack_t *info_ptr) |
| { |
| info_ptr->world_save_p = 1; |
| info_ptr->world_save_p |
| = (WORLD_SAVE_P (info_ptr) |
| && DEFAULT_ABI == ABI_DARWIN |
| && ! (current_function_calls_setjmp && flag_exceptions) |
| && info_ptr->first_fp_reg_save == FIRST_SAVED_FP_REGNO |
| && info_ptr->first_gp_reg_save == FIRST_SAVED_GP_REGNO |
| && info_ptr->first_altivec_reg_save == FIRST_SAVED_ALTIVEC_REGNO |
| && info_ptr->cr_save_p); |
| |
| /* This will not work in conjunction with sibcalls. Make sure there |
| are none. (This check is expensive, but seldom executed.) */ |
| if (WORLD_SAVE_P (info_ptr)) |
| { |
| rtx insn; |
| for ( insn = get_last_insn_anywhere (); insn; insn = PREV_INSN (insn)) |
| if ( GET_CODE (insn) == CALL_INSN |
| && SIBLING_CALL_P (insn)) |
| { |
| info_ptr->world_save_p = 0; |
| break; |
| } |
| } |
| |
| if (WORLD_SAVE_P (info_ptr)) |
| { |
| /* Even if we're not touching VRsave, make sure there's room on the |
| stack for it, if it looks like we're calling SAVE_WORLD, which |
| will attempt to save it. */ |
| info_ptr->vrsave_size = 4; |
| |
| /* "Save" the VRsave register too if we're saving the world. */ |
| if (info_ptr->vrsave_mask == 0) |
| info_ptr->vrsave_mask = compute_vrsave_mask (); |
| |
| /* Because the Darwin register save/restore routines only handle |
| F14 .. F31 and V20 .. V31 as per the ABI, perform a consistency |
| check. */ |
| gcc_assert (info_ptr->first_fp_reg_save >= FIRST_SAVED_FP_REGNO |
| && (info_ptr->first_altivec_reg_save |
| >= FIRST_SAVED_ALTIVEC_REGNO)); |
| } |
| return; |
| } |
| |
| |
| static void |
| is_altivec_return_reg (rtx reg, void *xyes) |
| { |
| bool *yes = (bool *) xyes; |
| if (REGNO (reg) == ALTIVEC_ARG_RETURN) |
| *yes = true; |
| } |
| |
| |
| /* Calculate the stack information for the current function. This is |
| complicated by having two separate calling sequences, the AIX calling |
| sequence and the V.4 calling sequence. |
| |
| AIX (and Darwin/Mac OS X) stack frames look like: |
| 32-bit 64-bit |
| SP----> +---------------------------------------+ |
| | back chain to caller | 0 0 |
| +---------------------------------------+ |
| | saved CR | 4 8 (8-11) |
| +---------------------------------------+ |
| | saved LR | 8 16 |
| +---------------------------------------+ |
| | reserved for compilers | 12 24 |
| +---------------------------------------+ |
| | reserved for binders | 16 32 |
| +---------------------------------------+ |
| | saved TOC pointer | 20 40 |
| +---------------------------------------+ |
| | Parameter save area (P) | 24 48 |
| +---------------------------------------+ |
| | Alloca space (A) | 24+P etc. |
| +---------------------------------------+ |
| | Local variable space (L) | 24+P+A |
| +---------------------------------------+ |
| | Float/int conversion temporary (X) | 24+P+A+L |
| +---------------------------------------+ |
| | Save area for AltiVec registers (W) | 24+P+A+L+X |
| +---------------------------------------+ |
| | AltiVec alignment padding (Y) | 24+P+A+L+X+W |
| +---------------------------------------+ |
| | Save area for VRSAVE register (Z) | 24+P+A+L+X+W+Y |
| +---------------------------------------+ |
| | Save area for GP registers (G) | 24+P+A+X+L+X+W+Y+Z |
| +---------------------------------------+ |
| | Save area for FP registers (F) | 24+P+A+X+L+X+W+Y+Z+G |
| +---------------------------------------+ |
| old SP->| back chain to caller's caller | |
| +---------------------------------------+ |
| |
| The required alignment for AIX configurations is two words (i.e., 8 |
| or 16 bytes). |
| |
| |
| V.4 stack frames look like: |
| |
| SP----> +---------------------------------------+ |
| | back chain to caller | 0 |
| +---------------------------------------+ |
| | caller's saved LR | 4 |
| +---------------------------------------+ |
| | Parameter save area (P) | 8 |
| +---------------------------------------+ |
| | Alloca space (A) | 8+P |
| +---------------------------------------+ |
| | Varargs save area (V) | 8+P+A |
| +---------------------------------------+ |
| | Local variable space (L) | 8+P+A+V |
| +---------------------------------------+ |
| | Float/int conversion temporary (X) | 8+P+A+V+L |
| +---------------------------------------+ |
| | Save area for AltiVec registers (W) | 8+P+A+V+L+X |
| +---------------------------------------+ |
| | AltiVec alignment padding (Y) | 8+P+A+V+L+X+W |
| +---------------------------------------+ |
| | Save area for VRSAVE register (Z) | 8+P+A+V+L+X+W+Y |
| +---------------------------------------+ |
| | SPE: area for 64-bit GP registers | |
| +---------------------------------------+ |
| | SPE alignment padding | |
| +---------------------------------------+ |
| | saved CR (C) | 8+P+A+V+L+X+W+Y+Z |
| +---------------------------------------+ |
| | Save area for GP registers (G) | 8+P+A+V+L+X+W+Y+Z+C |
| +---------------------------------------+ |
| | Save area for FP registers (F) | 8+P+A+V+L+X+W+Y+Z+C+G |
| +---------------------------------------+ |
| old SP->| back chain to caller's caller | |
| +---------------------------------------+ |
| |
| The required alignment for V.4 is 16 bytes, or 8 bytes if -meabi is |
| given. (But note below and in sysv4.h that we require only 8 and |
| may round up the size of our stack frame anyways. The historical |
| reason is early versions of powerpc-linux which didn't properly |
| align the stack at program startup. A happy side-effect is that |
| -mno-eabi libraries can be used with -meabi programs.) |
| |
| The EABI configuration defaults to the V.4 layout. However, |
| the stack alignment requirements may differ. If -mno-eabi is not |
| given, the required stack alignment is 8 bytes; if -mno-eabi is |
| given, the required alignment is 16 bytes. (But see V.4 comment |
| above.) */ |
| |
| #ifndef ABI_STACK_BOUNDARY |
| #define ABI_STACK_BOUNDARY STACK_BOUNDARY |
| #endif |
| |
| static rs6000_stack_t * |
| rs6000_stack_info (void) |
| { |
| static rs6000_stack_t info; |
| rs6000_stack_t *info_ptr = &info; |
| int reg_size = TARGET_32BIT ? 4 : 8; |
| int ehrd_size; |
| int save_align; |
| HOST_WIDE_INT non_fixed_size; |
| |
| memset (&info, 0, sizeof (info)); |
| |
| if (TARGET_SPE) |
| { |
| /* Cache value so we don't rescan instruction chain over and over. */ |
| if (cfun->machine->insn_chain_scanned_p == 0) |
| cfun->machine->insn_chain_scanned_p |
| = spe_func_has_64bit_regs_p () + 1; |
| info_ptr->spe_64bit_regs_used = cfun->machine->insn_chain_scanned_p - 1; |
| } |
| |
| /* Select which calling sequence. */ |
| info_ptr->abi = DEFAULT_ABI; |
| |
| /* Calculate which registers need to be saved & save area size. */ |
| info_ptr->first_gp_reg_save = first_reg_to_save (); |
| /* Assume that we will have to save RS6000_PIC_OFFSET_TABLE_REGNUM, |
| even if it currently looks like we won't. */ |
| if (((TARGET_TOC && TARGET_MINIMAL_TOC) |
| || (flag_pic == 1 && DEFAULT_ABI == ABI_V4) |
| || (flag_pic && DEFAULT_ABI == ABI_DARWIN)) |
| && info_ptr->first_gp_reg_save > RS6000_PIC_OFFSET_TABLE_REGNUM) |
| info_ptr->gp_size = reg_size * (32 - RS6000_PIC_OFFSET_TABLE_REGNUM); |
| else |
| info_ptr->gp_size = reg_size * (32 - info_ptr->first_gp_reg_save); |
| |
| /* For the SPE, we have an additional upper 32-bits on each GPR. |
| Ideally we should save the entire 64-bits only when the upper |
| half is used in SIMD instructions. Since we only record |
| registers live (not the size they are used in), this proves |
| difficult because we'd have to traverse the instruction chain at |
| the right time, taking reload into account. This is a real pain, |
| so we opt to save the GPRs in 64-bits always if but one register |
| gets used in 64-bits. Otherwise, all the registers in the frame |
| get saved in 32-bits. |
| |
| So... since when we save all GPRs (except the SP) in 64-bits, the |
| traditional GP save area will be empty. */ |
| if (TARGET_SPE_ABI && info_ptr->spe_64bit_regs_used != 0) |
| info_ptr->gp_size = 0; |
| |
| info_ptr->first_fp_reg_save = first_fp_reg_to_save (); |
| info_ptr->fp_size = 8 * (64 - info_ptr->first_fp_reg_save); |
| |
| info_ptr->first_altivec_reg_save = first_altivec_reg_to_save (); |
| info_ptr->altivec_size = 16 * (LAST_ALTIVEC_REGNO + 1 |
| - info_ptr->first_altivec_reg_save); |
| |
| /* Does this function call anything? */ |
| info_ptr->calls_p = (! current_function_is_leaf |
| || cfun->machine->ra_needs_full_frame); |
| |
| /* Determine if we need to save the link register. */ |
| if ((DEFAULT_ABI == ABI_AIX |
| && current_function_profile |
| && !TARGET_PROFILE_KERNEL) |
| #ifdef TARGET_RELOCATABLE |
| || (TARGET_RELOCATABLE && (get_pool_size () != 0)) |
| #endif |
| || (info_ptr->first_fp_reg_save != 64 |
| && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save)) |
| || info_ptr->first_altivec_reg_save <= LAST_ALTIVEC_REGNO |
| || (DEFAULT_ABI == ABI_V4 && current_function_calls_alloca) |
| || info_ptr->calls_p |
| || rs6000_ra_ever_killed ()) |
| { |
| info_ptr->lr_save_p = 1; |
| regs_ever_live[LINK_REGISTER_REGNUM] = 1; |
| } |
| |
| /* Determine if we need to save the condition code registers. */ |
| if (regs_ever_live[CR2_REGNO] |
| || regs_ever_live[CR3_REGNO] |
| || regs_ever_live[CR4_REGNO]) |
| { |
| info_ptr->cr_save_p = 1; |
| if (DEFAULT_ABI == ABI_V4) |
| info_ptr->cr_size = reg_size; |
| } |
| |
| /* If the current function calls __builtin_eh_return, then we need |
| to allocate stack space for registers that will hold data for |
| the exception handler. */ |
| if (current_function_calls_eh_return) |
| { |
| unsigned int i; |
| for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM; ++i) |
| continue; |
| |
| /* SPE saves EH registers in 64-bits. */ |
| ehrd_size = i * (TARGET_SPE_ABI |
| && info_ptr->spe_64bit_regs_used != 0 |
| ? UNITS_PER_SPE_WORD : UNITS_PER_WORD); |
| } |
| else |
| ehrd_size = 0; |
| |
| /* Determine various sizes. */ |
| info_ptr->reg_size = reg_size; |
| info_ptr->fixed_size = RS6000_SAVE_AREA; |
| info_ptr->vars_size = RS6000_ALIGN (get_frame_size (), 8); |
| info_ptr->parm_size = RS6000_ALIGN (current_function_outgoing_args_size, |
| TARGET_ALTIVEC ? 16 : 8); |
| if (FRAME_GROWS_DOWNWARD) |
| info_ptr->vars_size |
| += RS6000_ALIGN (info_ptr->fixed_size + info_ptr->vars_size |
| + info_ptr->parm_size, |
| ABI_STACK_BOUNDARY / BITS_PER_UNIT) |
| - (info_ptr->fixed_size + info_ptr->vars_size |
| + info_ptr->parm_size); |
| |
| if (TARGET_SPE_ABI && info_ptr->spe_64bit_regs_used != 0) |
| info_ptr->spe_gp_size = 8 * (32 - info_ptr->first_gp_reg_save); |
| else |
| info_ptr->spe_gp_size = 0; |
| |
| if (TARGET_ALTIVEC_ABI) |
| info_ptr->vrsave_mask = compute_vrsave_mask (); |
| else |
| info_ptr->vrsave_mask = 0; |
| |
| if (TARGET_ALTIVEC_VRSAVE && info_ptr->vrsave_mask) |
| info_ptr->vrsave_size = 4; |
| else |
| info_ptr->vrsave_size = 0; |
| |
| compute_save_world_info (info_ptr); |
| |
| /* Calculate the offsets. */ |
| switch (DEFAULT_ABI) |
| { |
| case ABI_NONE: |
| default: |
| gcc_unreachable (); |
| |
| case ABI_AIX: |
| case ABI_DARWIN: |
| info_ptr->fp_save_offset = - info_ptr->fp_size; |
| info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size; |
| |
| if (TARGET_ALTIVEC_ABI) |
| { |
| info_ptr->vrsave_save_offset |
| = info_ptr->gp_save_offset - info_ptr->vrsave_size; |
| |
| /* Align stack so vector save area is on a quadword boundary. |
| The padding goes above the vectors. */ |
| if (info_ptr->altivec_size != 0) |
| info_ptr->altivec_padding_size |
| = info_ptr->vrsave_save_offset & 0xF; |
| else |
| info_ptr->altivec_padding_size = 0; |
| |
| info_ptr->altivec_save_offset |
| = info_ptr->vrsave_save_offset |
| - info_ptr->altivec_padding_size |
| - info_ptr->altivec_size; |
| gcc_assert (info_ptr->altivec_size == 0 |
| || info_ptr->altivec_save_offset % 16 == 0); |
| |
| /* Adjust for AltiVec case. */ |
| info_ptr->ehrd_offset = info_ptr->altivec_save_offset - ehrd_size; |
| } |
| else |
| info_ptr->ehrd_offset = info_ptr->gp_save_offset - ehrd_size; |
| info_ptr->cr_save_offset = reg_size; /* first word when 64-bit. */ |
| info_ptr->lr_save_offset = 2*reg_size; |
| break; |
| |
| case ABI_V4: |
| info_ptr->fp_save_offset = - info_ptr->fp_size; |
| info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size; |
| info_ptr->cr_save_offset = info_ptr->gp_save_offset - info_ptr->cr_size; |
| |
| if (TARGET_SPE_ABI && info_ptr->spe_64bit_regs_used != 0) |
| { |
| /* Align stack so SPE GPR save area is aligned on a |
| double-word boundary. */ |
| if (info_ptr->spe_gp_size != 0) |
| info_ptr->spe_padding_size |
| = 8 - (-info_ptr->cr_save_offset % 8); |
| else |
| info_ptr->spe_padding_size = 0; |
| |
| info_ptr->spe_gp_save_offset |
| = info_ptr->cr_save_offset |
| - info_ptr->spe_padding_size |
| - info_ptr->spe_gp_size; |
| |
| /* Adjust for SPE case. */ |
| info_ptr->ehrd_offset = info_ptr->spe_gp_save_offset; |
| } |
| else if (TARGET_ALTIVEC_ABI) |
| { |
| info_ptr->vrsave_save_offset |
| = info_ptr->cr_save_offset - info_ptr->vrsave_size; |
| |
| /* Align stack so vector save area is on a quadword boundary. */ |
| if (info_ptr->altivec_size != 0) |
| info_ptr->altivec_padding_size |
| = 16 - (-info_ptr->vrsave_save_offset % 16); |
| else |
| info_ptr->altivec_padding_size = 0; |
| |
| info_ptr->altivec_save_offset |
| = info_ptr->vrsave_save_offset |
| - info_ptr->altivec_padding_size |
| - info_ptr->altivec_size; |
| |
| /* Adjust for AltiVec case. */ |
| info_ptr->ehrd_offset = info_ptr->altivec_save_offset; |
| } |
| else |
| info_ptr->ehrd_offset = info_ptr->cr_save_offset; |
| info_ptr->ehrd_offset -= ehrd_size; |
| info_ptr->lr_save_offset = reg_size; |
| break; |
| } |
| |
| save_align = (TARGET_ALTIVEC_ABI || DEFAULT_ABI == ABI_DARWIN) ? 16 : 8; |
| info_ptr->save_size = RS6000_ALIGN (info_ptr->fp_size |
| + info_ptr->gp_size |
| + info_ptr->altivec_size |
| + info_ptr->altivec_padding_size |
| + info_ptr->spe_gp_size |
| + info_ptr->spe_padding_size |
| + ehrd_size |
| + info_ptr->cr_size |
| + info_ptr->vrsave_size, |
| save_align); |
| |
| non_fixed_size = (info_ptr->vars_size |
| + info_ptr->parm_size |
| + info_ptr->save_size); |
| |
| /* APPLE LOCAL begin CW asm blocks */ |
| /* If we have an assembly function, maybe use an explicit size. To |
| be consistent with CW behavior (and because it's safer), let |
| RS6000_ALIGN round the explicit size up if necessary. */ |
| if (cfun->iasm_asm_function && cfun->iasm_frame_size != -2) |
| { |
| if (cfun->iasm_frame_size == -1) |
| non_fixed_size = 32; |
| else if (cfun->iasm_frame_size < 32) |
| error ("fralloc frame size must be at least 32"); |
| else |
| non_fixed_size = cfun->iasm_frame_size; |
| non_fixed_size += 24; |
| info_ptr->total_size = RS6000_ALIGN (non_fixed_size, |
| ABI_STACK_BOUNDARY / BITS_PER_UNIT); |
| } |
| else |
| info_ptr->total_size = RS6000_ALIGN (non_fixed_size + info_ptr->fixed_size, |
| ABI_STACK_BOUNDARY / BITS_PER_UNIT); |
| /* APPLE LOCAL end CW asm blocks */ |
| |
| /* Determine if we need to allocate any stack frame: |
| |
| For AIX we need to push the stack if a frame pointer is needed |
| (because the stack might be dynamically adjusted), if we are |
| debugging, if we make calls, or if the sum of fp_save, gp_save, |
| and local variables are more than the space needed to save all |
| non-volatile registers: 32-bit: 18*8 + 19*4 = 220 or 64-bit: 18*8 |
| + 18*8 = 288 (GPR13 reserved). |
| |
| For V.4 we don't have the stack cushion that AIX uses, but assume |
| that the debugger can handle stackless frames. */ |
| |
| /* APPLE LOCAL CW asm blocks */ |
| if (info_ptr->calls_p || (cfun->iasm_asm_function && cfun->iasm_frame_size != -2)) |
| info_ptr->push_p = 1; |
| |
| else if (DEFAULT_ABI == ABI_V4) |
| info_ptr->push_p = non_fixed_size != 0; |
| |
| else if (frame_pointer_needed) |
| info_ptr->push_p = 1; |
| |
| else if (TARGET_XCOFF && write_symbols != NO_DEBUG) |
| info_ptr->push_p = 1; |
| |
| else |
| info_ptr->push_p = non_fixed_size > (TARGET_32BIT ? 220 : 288); |
| |
| /* Zero offsets if we're not saving those registers. */ |
| if (info_ptr->fp_size == 0) |
| info_ptr->fp_save_offset = 0; |
| |
| if (info_ptr->gp_size == 0) |
| info_ptr->gp_save_offset = 0; |
| |
| if (! TARGET_ALTIVEC_ABI || info_ptr->altivec_size == 0) |
| info_ptr->altivec_save_offset = 0; |
| |
| if (! TARGET_ALTIVEC_ABI || info_ptr->vrsave_mask == 0) |
| info_ptr->vrsave_save_offset = 0; |
| |
| if (! TARGET_SPE_ABI |
| || info_ptr->spe_64bit_regs_used == 0 |
| || info_ptr->spe_gp_size == 0) |
| info_ptr->spe_gp_save_offset = 0; |
| |
| if (! info_ptr->lr_save_p) |
| info_ptr->lr_save_offset = 0; |
| |
| if (! info_ptr->cr_save_p) |
| info_ptr->cr_save_offset = 0; |
| |
| return info_ptr; |
| } |
| |
| /* Return true if the current function uses any GPRs in 64-bit SIMD |
| mode. */ |
| |
| static bool |
| spe_func_has_64bit_regs_p (void) |
| { |
| rtx insns, insn; |
| |
| /* Functions that save and restore all the call-saved registers will |
| need to save/restore the registers in 64-bits. */ |
| if (current_function_calls_eh_return |
| || current_function_calls_setjmp |
| || current_function_has_nonlocal_goto) |
| return true; |
| |
| insns = get_insns (); |
| |
| for (insn = NEXT_INSN (insns); insn != NULL_RTX; insn = NEXT_INSN (insn)) |
| { |
| if (INSN_P (insn)) |
| { |
| rtx i; |
| |
| /* FIXME: This should be implemented with attributes... |
| |
| (set_attr "spe64" "true")....then, |
| if (get_spe64(insn)) return true; |
| |
| It's the only reliable way to do the stuff below. */ |
| |
| i = PATTERN (insn); |
| if (GET_CODE (i) == SET) |
| { |
| enum machine_mode mode = GET_MODE (SET_SRC (i)); |
| |
| if (SPE_VECTOR_MODE (mode)) |
| return true; |
| if (TARGET_E500_DOUBLE && mode == DFmode) |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| static void |
| debug_stack_info (rs6000_stack_t *info) |
| { |
| const char *abi_string; |
| |
| if (! info) |
| info = rs6000_stack_info (); |
| |
| fprintf (stderr, "\nStack information for function %s:\n", |
| ((current_function_decl && DECL_NAME (current_function_decl)) |
| ? IDENTIFIER_POINTER (DECL_NAME (current_function_decl)) |
| : "<unknown>")); |
| |
| switch (info->abi) |
| { |
| default: abi_string = "Unknown"; break; |
| case ABI_NONE: abi_string = "NONE"; break; |
| case ABI_AIX: abi_string = "AIX"; break; |
| case ABI_DARWIN: abi_string = "Darwin"; break; |
| case ABI_V4: abi_string = "V.4"; break; |
| } |
| |
| fprintf (stderr, "\tABI = %5s\n", abi_string); |
| |
| if (TARGET_ALTIVEC_ABI) |
| fprintf (stderr, "\tALTIVEC ABI extensions enabled.\n"); |
| |
| if (TARGET_SPE_ABI) |
| fprintf (stderr, "\tSPE ABI extensions enabled.\n"); |
| |
| if (info->first_gp_reg_save != 32) |
| fprintf (stderr, "\tfirst_gp_reg_save = %5d\n", info->first_gp_reg_save); |
| |
| if (info->first_fp_reg_save != 64) |
| fprintf (stderr, "\tfirst_fp_reg_save = %5d\n", info->first_fp_reg_save); |
| |
| if (info->first_altivec_reg_save <= LAST_ALTIVEC_REGNO) |
| fprintf (stderr, "\tfirst_altivec_reg_save = %5d\n", |
| info->first_altivec_reg_save); |
| |
| if (info->lr_save_p) |
| fprintf (stderr, "\tlr_save_p = %5d\n", info->lr_save_p); |
| |
| if (info->cr_save_p) |
| fprintf (stderr, "\tcr_save_p = %5d\n", info->cr_save_p); |
| |
| if (info->vrsave_mask) |
| fprintf (stderr, "\tvrsave_mask = 0x%x\n", info->vrsave_mask); |
| |
| if (info->push_p) |
| fprintf (stderr, "\tpush_p = %5d\n", info->push_p); |
| |
| if (info->calls_p) |
| fprintf (stderr, "\tcalls_p = %5d\n", info->calls_p); |
| |
| if (info->gp_save_offset) |
| fprintf (stderr, "\tgp_save_offset = %5d\n", info->gp_save_offset); |
| |
| if (info->fp_save_offset) |
| fprintf (stderr, "\tfp_save_offset = %5d\n", info->fp_save_offset); |
| |
| if (info->altivec_save_offset) |
| fprintf (stderr, "\taltivec_save_offset = %5d\n", |
| info->altivec_save_offset); |
| |
| if (info->spe_gp_save_offset) |
| fprintf (stderr, "\tspe_gp_save_offset = %5d\n", |
| info->spe_gp_save_offset); |
| |
| if (info->vrsave_save_offset) |
| fprintf (stderr, "\tvrsave_save_offset = %5d\n", |
| info->vrsave_save_offset); |
| |
| if (info->lr_save_offset) |
| fprintf (stderr, "\tlr_save_offset = %5d\n", info->lr_save_offset); |
| |
| if (info->cr_save_offset) |
| fprintf (stderr, "\tcr_save_offset = %5d\n", info->cr_save_offset); |
| |
| if (info->varargs_save_offset) |
| fprintf (stderr, "\tvarargs_save_offset = %5d\n", info->varargs_save_offset); |
| |
| if (info->total_size) |
| fprintf (stderr, "\ttotal_size = "HOST_WIDE_INT_PRINT_DEC"\n", |
| info->total_size); |
| |
| if (info->vars_size) |
| fprintf (stderr, "\tvars_size = "HOST_WIDE_INT_PRINT_DEC"\n", |
| info->vars_size); |
| |
| if (info->parm_size) |
| fprintf (stderr, "\tparm_size = %5d\n", info->parm_size); |
| |
| if (info->fixed_size) |
| fprintf (stderr, "\tfixed_size = %5d\n", info->fixed_size); |
| |
| if (info->gp_size) |
| fprintf (stderr, "\tgp_size = %5d\n", info->gp_size); |
| |
| if (info->spe_gp_size) |
| fprintf (stderr, "\tspe_gp_size = %5d\n", info->spe_gp_size); |
| |
| if (info->fp_size) |
| fprintf (stderr, "\tfp_size = %5d\n", info->fp_size); |
| |
| if (info->altivec_size) |
| fprintf (stderr, "\taltivec_size = %5d\n", info->altivec_size); |
| |
| if (info->vrsave_size) |
| fprintf (stderr, "\tvrsave_size = %5d\n", info->vrsave_size); |
| |
| if (info->altivec_padding_size) |
| fprintf (stderr, "\taltivec_padding_size= %5d\n", |
| info->altivec_padding_size); |
| |
| if (info->spe_padding_size) |
| fprintf (stderr, "\tspe_padding_size = %5d\n", |
| info->spe_padding_size); |
| |
| if (info->cr_size) |
| fprintf (stderr, "\tcr_size = %5d\n", info->cr_size); |
| |
| if (info->save_size) |
| fprintf (stderr, "\tsave_size = %5d\n", info->save_size); |
| |
| if (info->reg_size != 4) |
| fprintf (stderr, "\treg_size = %5d\n", info->reg_size); |
| |
| fprintf (stderr, "\n"); |
| } |
| |
| rtx |
| rs6000_return_addr (int count, rtx frame) |
| { |
| /* Currently we don't optimize very well between prolog and body |
| code and for PIC code the code can be actually quite bad, so |
| don't try to be too clever here. */ |
| if (count != 0 || (DEFAULT_ABI != ABI_AIX && flag_pic)) |
| { |
| cfun->machine->ra_needs_full_frame = 1; |
| |
| return |
| gen_rtx_MEM |
| (Pmode, |
| memory_address |
| (Pmode, |
| plus_constant (copy_to_reg |
| (gen_rtx_MEM (Pmode, |
| memory_address (Pmode, frame))), |
| RETURN_ADDRESS_OFFSET))); |
| } |
| |
| cfun->machine->ra_need_lr = 1; |
| return get_hard_reg_initial_val (Pmode, LINK_REGISTER_REGNUM); |
| } |
| |
| /* Say whether a function is a candidate for sibcall handling or not. |
| APPLE LOCAL sibling calls |
| |
| Also, we can't do it if there are any vector parameters; there's |
| nowhere to put the VRsave code so it works; note that functions with |
| vector parameters are required to have a prototype, so the argument |
| type info must be available here. (The tail recursion case can work |
| with vector parameters, but there's no way to distinguish here.) */ |
| |
| /* APPLE LOCAL begin sibling calls |
| On Darwin only, indirect calls may be sibcalls. This is enabled |
| primarily by target-specific logic in calls.c. |
| APPLE LOCAL end sibling calls */ |
| static bool |
| rs6000_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) |
| { |
| tree type; |
| /* APPLE LOCAL begin long-branch */ |
| /* This is wrong, short functions are ok for sibcall. (mrs) */ |
| if (rs6000_default_long_calls) |
| return false; |
| /* APPLE LOCAL end long-branch */ |
| |
| /* APPLE LOCAL begin indirect sibcalls */ |
| /* This goes with a lot of local changes in expand_call. */ |
| if (DEFAULT_ABI == ABI_DARWIN && !decl) |
| return true; |
| /* APPLE LOCAL end indirect sibcalls */ |
| |
| if (decl) |
| { |
| if (TARGET_ALTIVEC_VRSAVE) |
| { |
| for (type = TYPE_ARG_TYPES (TREE_TYPE (decl)); |
| type; type = TREE_CHAIN (type)) |
| { |
| if (TREE_CODE (TREE_VALUE (type)) == VECTOR_TYPE) |
| return false; |
| } |
| } |
| if (DEFAULT_ABI == ABI_DARWIN |
| || ((*targetm.binds_local_p) (decl) |
| && (DEFAULT_ABI != ABI_AIX || !DECL_EXTERNAL (decl)))) |
| { |
| tree attr_list = TYPE_ATTRIBUTES (TREE_TYPE (decl)); |
| |
| if (!lookup_attribute ("longcall", attr_list) |
| || lookup_attribute ("shortcall", attr_list)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* NULL if INSN insn is valid within a low-overhead loop. |
| Otherwise return why doloop cannot be applied. |
| PowerPC uses the COUNT register for branch on table instructions. */ |
| |
| static const char * |
| rs6000_invalid_within_doloop (rtx insn) |
| { |
| if (CALL_P (insn)) |
| return "Function call in the loop."; |
| |
| if (JUMP_P (insn) |
| && (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC |
| || GET_CODE (PATTERN (insn)) == ADDR_VEC)) |
| return "Computed branch in the loop."; |
| |
| return NULL; |
| } |
| |
| static int |
| rs6000_ra_ever_killed (void) |
| { |
| rtx top; |
| rtx reg; |
| rtx insn; |
| |
| if (current_function_is_thunk) |
| return 0; |
| |
| /* regs_ever_live has LR marked as used if any sibcalls are present, |
| but this should not force saving and restoring in the |
| pro/epilogue. Likewise, reg_set_between_p thinks a sibcall |
| clobbers LR, so that is inappropriate. */ |
| |
| /* Also, the prologue can generate a store into LR that |
| doesn't really count, like this: |
| |
| move LR->R0 |
| bcl to set PIC register |
| move LR->R31 |
| move R0->LR |
| |
| When we're called from the epilogue, we need to avoid counting |
| this as a store. */ |
| |
| push_topmost_sequence (); |
| top = get_insns (); |
| pop_topmost_sequence (); |
| reg = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM); |
| |
| for (insn = NEXT_INSN (top); insn != NULL_RTX; insn = NEXT_INSN (insn)) |
| { |
| if (INSN_P (insn)) |
| { |
| if (CALL_P (insn)) |
| { |
| if (!SIBLING_CALL_P (insn)) |
| return 1; |
| } |
| else if (find_regno_note (insn, REG_INC, LINK_REGISTER_REGNUM)) |
| return 1; |
| else if (set_of (reg, insn) != NULL_RTX |
| && !prologue_epilogue_contains (insn)) |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| /* Add a REG_MAYBE_DEAD note to the insn. */ |
| static void |
| rs6000_maybe_dead (rtx insn) |
| { |
| REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, |
| const0_rtx, |
| REG_NOTES (insn)); |
| } |
| |
| /* Emit instructions needed to load the TOC register. |
| This is only needed when TARGET_TOC, TARGET_MINIMAL_TOC, and there is |
| a constant pool; or for SVR4 -fpic. */ |
| |
| void |
| rs6000_emit_load_toc_table (int fromprolog) |
| { |
| rtx dest, insn; |
| dest = gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM); |
| |
| if (TARGET_ELF && TARGET_SECURE_PLT && DEFAULT_ABI != ABI_AIX && flag_pic) |
| { |
| char buf[30]; |
| rtx lab, tmp1, tmp2, got, tempLR; |
| |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); |
| lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); |
| if (flag_pic == 2) |
| got = gen_rtx_SYMBOL_REF (Pmode, toc_label_name); |
| else |
| got = rs6000_got_sym (); |
| tmp1 = tmp2 = dest; |
| if (!fromprolog) |
| { |
| tmp1 = gen_reg_rtx (Pmode); |
| tmp2 = gen_reg_rtx (Pmode); |
| } |
| tempLR = (fromprolog |
| ? gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM) |
| : gen_reg_rtx (Pmode)); |
| insn = emit_insn (gen_load_toc_v4_PIC_1 (tempLR, lab)); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| insn = emit_move_insn (tmp1, tempLR); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| insn = emit_insn (gen_load_toc_v4_PIC_3b (tmp2, tmp1, got, lab)); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| insn = emit_insn (gen_load_toc_v4_PIC_3c (dest, tmp2, got, lab)); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| } |
| else if (TARGET_ELF && DEFAULT_ABI == ABI_V4 && flag_pic == 1) |
| { |
| rtx tempLR = (fromprolog |
| ? gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM) |
| : gen_reg_rtx (Pmode)); |
| |
| insn = emit_insn (gen_load_toc_v4_pic_si (tempLR)); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| insn = emit_move_insn (dest, tempLR); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| } |
| else if (TARGET_ELF && DEFAULT_ABI != ABI_AIX && flag_pic == 2) |
| { |
| char buf[30]; |
| rtx tempLR = (fromprolog |
| ? gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM) |
| : gen_reg_rtx (Pmode)); |
| rtx temp0 = (fromprolog |
| ? gen_rtx_REG (Pmode, 0) |
| : gen_reg_rtx (Pmode)); |
| |
| if (fromprolog) |
| { |
| rtx symF, symL; |
| |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); |
| symF = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); |
| |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno); |
| symL = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); |
| |
| rs6000_maybe_dead (emit_insn (gen_load_toc_v4_PIC_1 (tempLR, |
| symF))); |
| rs6000_maybe_dead (emit_move_insn (dest, tempLR)); |
| rs6000_maybe_dead (emit_insn (gen_load_toc_v4_PIC_2 (temp0, dest, |
| symL, |
| symF))); |
| } |
| else |
| { |
| rtx tocsym; |
| |
| tocsym = gen_rtx_SYMBOL_REF (Pmode, toc_label_name); |
| emit_insn (gen_load_toc_v4_PIC_1b (tempLR, tocsym)); |
| emit_move_insn (dest, tempLR); |
| emit_move_insn (temp0, gen_rtx_MEM (Pmode, dest)); |
| } |
| insn = emit_insn (gen_addsi3 (dest, temp0, dest)); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| } |
| else if (TARGET_ELF && !TARGET_AIX && flag_pic == 0 && TARGET_MINIMAL_TOC) |
| { |
| /* This is for AIX code running in non-PIC ELF32. */ |
| char buf[30]; |
| rtx realsym; |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); |
| realsym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); |
| |
| insn = emit_insn (gen_elf_high (dest, realsym)); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| insn = emit_insn (gen_elf_low (dest, dest, realsym)); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| } |
| else |
| { |
| gcc_assert (DEFAULT_ABI == ABI_AIX); |
| |
| if (TARGET_32BIT) |
| insn = emit_insn (gen_load_toc_aix_si (dest)); |
| else |
| insn = emit_insn (gen_load_toc_aix_di (dest)); |
| if (fromprolog) |
| rs6000_maybe_dead (insn); |
| } |
| } |
| |
| /* Emit instructions to restore the link register after determining where |
| its value has been stored. */ |
| |
| void |
| rs6000_emit_eh_reg_restore (rtx source, rtx scratch) |
| { |
| rs6000_stack_t *info = rs6000_stack_info (); |
| rtx operands[2]; |
| |
| operands[0] = source; |
| operands[1] = scratch; |
| |
| if (info->lr_save_p) |
| { |
| rtx frame_rtx = stack_pointer_rtx; |
| HOST_WIDE_INT sp_offset = 0; |
| rtx tmp; |
| |
| if (frame_pointer_needed |
| || current_function_calls_alloca |
| || info->total_size > 32767) |
| { |
| tmp = gen_frame_mem (Pmode, frame_rtx); |
| emit_move_insn (operands[1], tmp); |
| frame_rtx = operands[1]; |
| } |
| else if (info->push_p) |
| sp_offset = info->total_size; |
| |
| tmp = plus_constant (frame_rtx, info->lr_save_offset + sp_offset); |
| tmp = gen_frame_mem (Pmode, tmp); |
| emit_move_insn (tmp, operands[0]); |
| } |
| else |
| emit_move_insn (gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM), operands[0]); |
| } |
| |
| static GTY(()) int set = -1; |
| |
| int |
| get_TOC_alias_set (void) |
| { |
| if (set == -1) |
| set = new_alias_set (); |
| return set; |
| } |
| |
| /* This returns nonzero if the current function uses the TOC. This is |
| determined by the presence of (use (unspec ... UNSPEC_TOC)), which |
| is generated by the ABI_V4 load_toc_* patterns. */ |
| #if TARGET_ELF |
| static int |
| uses_TOC (void) |
| { |
| rtx insn; |
| |
| for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) |
| if (INSN_P (insn)) |
| { |
| rtx pat = PATTERN (insn); |
| int i; |
| |
| if (GET_CODE (pat) == PARALLEL) |
| for (i = 0; i < XVECLEN (pat, 0); i++) |
| { |
| rtx sub = XVECEXP (pat, 0, i); |
| if (GET_CODE (sub) == USE) |
| { |
| sub = XEXP (sub, 0); |
| if (GET_CODE (sub) == UNSPEC |
| && XINT (sub, 1) == UNSPEC_TOC) |
| return 1; |
| } |
| } |
| } |
| return 0; |
| } |
| #endif |
| |
| rtx |
| create_TOC_reference (rtx symbol) |
| { |
| if (no_new_pseudos) |
| regs_ever_live[TOC_REGISTER] = 1; |
| return gen_rtx_PLUS (Pmode, |
| gen_rtx_REG (Pmode, TOC_REGISTER), |
| gen_rtx_CONST (Pmode, |
| gen_rtx_MINUS (Pmode, symbol, |
| gen_rtx_SYMBOL_REF (Pmode, toc_label_name)))); |
| } |
| |
| /* If _Unwind_* has been called from within the same module, |
| toc register is not guaranteed to be saved to 40(1) on function |
| entry. Save it there in that case. */ |
| |
| void |
| rs6000_aix_emit_builtin_unwind_init (void) |
| { |
| rtx mem; |
| rtx stack_top = gen_reg_rtx (Pmode); |
| rtx opcode_addr = gen_reg_rtx (Pmode); |
| rtx opcode = gen_reg_rtx (SImode); |
| rtx tocompare = gen_reg_rtx (SImode); |
| rtx no_toc_save_needed = gen_label_rtx (); |
| |
| mem = gen_frame_mem (Pmode, hard_frame_pointer_rtx); |
| emit_move_insn (stack_top, mem); |
| |
| mem = gen_frame_mem (Pmode, |
| gen_rtx_PLUS (Pmode, stack_top, |
| GEN_INT (2 * GET_MODE_SIZE (Pmode)))); |
| emit_move_insn (opcode_addr, mem); |
| emit_move_insn (opcode, gen_rtx_MEM (SImode, opcode_addr)); |
| emit_move_insn (tocompare, gen_int_mode (TARGET_32BIT ? 0x80410014 |
| : 0xE8410028, SImode)); |
| |
| do_compare_rtx_and_jump (opcode, tocompare, EQ, 1, |
| SImode, NULL_RTX, NULL_RTX, |
| no_toc_save_needed); |
| |
| mem = gen_frame_mem (Pmode, |
| gen_rtx_PLUS (Pmode, stack_top, |
| GEN_INT (5 * GET_MODE_SIZE (Pmode)))); |
| emit_move_insn (mem, gen_rtx_REG (Pmode, 2)); |
| emit_label (no_toc_save_needed); |
| } |
| |
| /* This ties together stack memory (MEM with an alias set of frame_alias_set) |
| and the change to the stack pointer. */ |
| |
| static void |
| rs6000_emit_stack_tie (void) |
| { |
| rtx mem = gen_frame_mem (BLKmode, |
| gen_rtx_REG (Pmode, STACK_POINTER_REGNUM)); |
| |
| emit_insn (gen_stack_tie (mem)); |
| } |
| |
| /* Emit the correct code for allocating stack space, as insns. |
| If COPY_R12, make sure a copy of the old frame is left in r12. |
| The generated code may use hard register 0 as a temporary. */ |
| |
| static void |
| rs6000_emit_allocate_stack (HOST_WIDE_INT size, int copy_r12) |
| { |
| rtx insn; |
| rtx stack_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); |
| rtx tmp_reg = gen_rtx_REG (Pmode, 0); |
| rtx todec = gen_int_mode (-size, Pmode); |
| |
| if (INTVAL (todec) != -size) |
| { |
| warning (0, "stack frame too large"); |
| emit_insn (gen_trap ()); |
| return; |
| } |
| |
| if (current_function_limit_stack) |
| { |
| if (REG_P (stack_limit_rtx) |
| && REGNO (stack_limit_rtx) > 1 |
| && REGNO (stack_limit_rtx) <= 31) |
| { |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (tmp_reg, |
| stack_limit_rtx, |
| GEN_INT (size)) |
| : gen_adddi3 (tmp_reg, |
| stack_limit_rtx, |
| GEN_INT (size))); |
| |
| emit_insn (gen_cond_trap (LTU, stack_reg, tmp_reg, |
| const0_rtx)); |
| } |
| else if (GET_CODE (stack_limit_rtx) == SYMBOL_REF |
| && TARGET_32BIT |
| && DEFAULT_ABI == ABI_V4) |
| { |
| rtx toload = gen_rtx_CONST (VOIDmode, |
| gen_rtx_PLUS (Pmode, |
| stack_limit_rtx, |
| GEN_INT (size))); |
| |
| emit_insn (gen_elf_high (tmp_reg, toload)); |
| emit_insn (gen_elf_low (tmp_reg, tmp_reg, toload)); |
| emit_insn (gen_cond_trap (LTU, stack_reg, tmp_reg, |
| const0_rtx)); |
| } |
| else |
| warning (0, "stack limit expression is not supported"); |
| } |
| |
| if (copy_r12 || ! TARGET_UPDATE) |
| emit_move_insn (gen_rtx_REG (Pmode, 12), stack_reg); |
| |
| if (TARGET_UPDATE) |
| { |
| if (size > 32767) |
| { |
| /* Need a note here so that try_split doesn't get confused. */ |
| if (get_last_insn () == NULL_RTX) |
| emit_note (NOTE_INSN_DELETED); |
| insn = emit_move_insn (tmp_reg, todec); |
| try_split (PATTERN (insn), insn, 0); |
| todec = tmp_reg; |
| } |
| |
| insn = emit_insn (TARGET_32BIT |
| ? gen_movsi_update (stack_reg, stack_reg, |
| todec, stack_reg) |
| : gen_movdi_di_update (stack_reg, stack_reg, |
| todec, stack_reg)); |
| } |
| else |
| { |
| insn = emit_insn (TARGET_32BIT |
| ? gen_addsi3 (stack_reg, stack_reg, todec) |
| : gen_adddi3 (stack_reg, stack_reg, todec)); |
| emit_move_insn (gen_rtx_MEM (Pmode, stack_reg), |
| gen_rtx_REG (Pmode, 12)); |
| } |
| |
| RTX_FRAME_RELATED_P (insn) = 1; |
| REG_NOTES (insn) = |
| gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, |
| gen_rtx_SET (VOIDmode, stack_reg, |
| gen_rtx_PLUS (Pmode, stack_reg, |
| GEN_INT (-size))), |
| REG_NOTES (insn)); |
| } |
| |
| /* Add to 'insn' a note which is PATTERN (INSN) but with REG replaced |
| with (plus:P (reg 1) VAL), and with REG2 replaced with RREG if REG2 |
| is not NULL. It would be nice if dwarf2out_frame_debug_expr could |
| deduce these equivalences by itself so it wasn't necessary to hold |
| its hand so much. */ |
| |
| static void |
| rs6000_frame_related (rtx insn, rtx reg, HOST_WIDE_INT val, |
| rtx reg2, rtx rreg) |
| { |
| rtx real, temp; |
| |
| /* copy_rtx will not make unique copies of registers, so we need to |
| ensure we don't have unwanted sharing here. */ |
| if (reg == reg2) |
| reg = gen_raw_REG (GET_MODE (reg), REGNO (reg)); |
| |
| if (reg == rreg) |
| reg = gen_raw_REG (GET_MODE (reg), REGNO (reg)); |
| |
| real = copy_rtx (PATTERN (insn)); |
| |
| if (reg2 != NULL_RTX) |
| real = replace_rtx (real, reg2, rreg); |
| |
| real = replace_rtx (real, reg, |
| gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, |
| STACK_POINTER_REGNUM), |
| GEN_INT (val))); |
| |
| /* We expect that 'real' is either a SET or a PARALLEL containing |
| SETs (and possibly other stuff). In a PARALLEL, all the SETs |
| are important so they all have to be marked RTX_FRAME_RELATED_P. */ |
| |
| if (GET_CODE (real) == SET) |
| { |
| rtx set = real; |
| |
| temp = simplify_rtx (SET_SRC (set)); |
| if (temp) |
| SET_SRC (set) = temp; |
| temp = simplify_rtx (SET_DEST (set)); |
| if (temp) |
| SET_DEST (set) = temp; |
| if (GET_CODE (SET_DEST (set)) == MEM) |
| { |
| temp = simplify_rtx (XEXP (SET_DEST (set), 0)); |
| if (temp) |
| XEXP (SET_DEST (set), 0) = temp; |
| } |
| } |
| else |
| { |
| int i; |
| |
| gcc_assert (GET_CODE (real) == PARALLEL); |
| for (i = 0; i < XVECLEN (real, 0); i++) |
| if (GET_CODE (XVECEXP (real, 0, i)) == SET) |
| { |
| rtx set = XVECEXP (real, 0, i); |
| |
| temp = simplify_rtx (SET_SRC (set)); |
| if (temp) |
| SET_SRC (set) = temp; |
| temp = simplify_rtx (SET_DEST (set)); |
| if (temp) |
| SET_DEST (set) = temp; |
| if (GET_CODE (SET_DEST (set)) == MEM) |
| { |
| temp = simplify_rtx (XEXP (SET_DEST (set), 0)); |
| if (temp) |
| XEXP (SET_DEST (set), 0) = temp; |
| } |
| RTX_FRAME_RELATED_P (set) = 1; |
| } |
| } |
| |
| if (TARGET_SPE) |
| real = spe_synthesize_frame_save (real); |
| |
| RTX_FRAME_RELATED_P (insn) = 1; |
| REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, |
| real, |
| REG_NOTES (insn)); |
| } |
| |
| /* Given an SPE frame note, return a PARALLEL of SETs with the |
| original note, plus a synthetic register save. */ |
| |
| static rtx |
| spe_synthesize_frame_save (rtx real) |
| { |
| rtx synth, offset, reg, real2; |
| |
| if (GET_CODE (real) != SET |
| || GET_MODE (SET_SRC (real)) != V2SImode) |
| return real; |
| |
| /* For the SPE, registers saved in 64-bits, get a PARALLEL for their |
| frame related note. The parallel contains a set of the register |
| being saved, and another set to a synthetic register (n+1200). |
| This is so we can differentiate between 64-bit and 32-bit saves. |
| Words cannot describe this nastiness. */ |
| |
| gcc_assert (GET_CODE (SET_DEST (real)) == MEM |
| && GET_CODE (XEXP (SET_DEST (real), 0)) == PLUS |
| && GET_CODE (SET_SRC (real)) == REG); |
| |
| /* Transform: |
| (set (mem (plus (reg x) (const y))) |
| (reg z)) |
| into: |
| (set (mem (plus (reg x) (const y+4))) |
| (reg z+1200)) |
| */ |
| |
| real2 = copy_rtx (real); |
| PUT_MODE (SET_DEST (real2), SImode); |
| reg = SET_SRC (real2); |
| real2 = replace_rtx (real2, reg, gen_rtx_REG (SImode, REGNO (reg))); |
| synth = copy_rtx (real2); |
| |
| if (BYTES_BIG_ENDIAN) |
| { |
| offset = XEXP (XEXP (SET_DEST (real2), 0), 1); |
| real2 = replace_rtx (real2, offset, GEN_INT (INTVAL (offset) + 4)); |
| } |
| |
| reg = SET_SRC (synth); |
| |
| synth = replace_rtx (synth, reg, |
| gen_rtx_REG (SImode, REGNO (reg) + 1200)); |
| |
| offset = XEXP (XEXP (SET_DEST (synth), 0), 1); |
| synth = replace_rtx (synth, offset, |
| GEN_INT (INTVAL (offset) |
| + (BYTES_BIG_ENDIAN ? 0 : 4))); |
| |
| RTX_FRAME_RELATED_P (synth) = 1; |
| RTX_FRAME_RELATED_P (real2) = 1; |
| if (BYTES_BIG_ENDIAN) |
| real = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, synth, real2)); |
| else |
| real = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, real2, synth)); |
| |
| return real; |
| } |
| |
| /* Returns an insn that has a vrsave set operation with the |
| appropriate CLOBBERs. */ |
| |
| static rtx |
| generate_set_vrsave (rtx reg, rs6000_stack_t *info, int epiloguep) |
| { |
| int nclobs, i; |
| rtx insn, clobs[TOTAL_ALTIVEC_REGS + 1]; |
| rtx vrsave = gen_rtx_REG (SImode, VRSAVE_REGNO); |
| |
| clobs[0] |
| = gen_rtx_SET (VOIDmode, |
| vrsave, |
| gen_rtx_UNSPEC_VOLATILE (SImode, |
| gen_rtvec (2, reg, vrsave), |
| UNSPECV_SET_VRSAVE)); |
| |
| nclobs = 1; |
| |
| /* We need to clobber the registers in the mask so the scheduler |
| does not move sets to VRSAVE before sets of AltiVec registers. |
| |
| However, if the function receives nonlocal gotos, reload will set |
| all call saved registers live. We will end up with: |
| |
| (set (reg 999) (mem)) |
| (parallel [ (set (reg vrsave) (unspec blah)) |
| (clobber (reg 999))]) |
| |
| The clobber will cause the store into reg 999 to be dead, and |
| flow will attempt to delete an epilogue insn. In this case, we |
| need an unspec use/set of the register. */ |
| |
| for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i) |
| if (info->vrsave_mask & ALTIVEC_REG_BIT (i)) |
| { |
| if (!epiloguep || call_used_regs [i]) |
| clobs[nclobs++] = gen_rtx_CLOBBER (VOIDmode, |
| gen_rtx_REG (V4SImode, i)); |
| else |
| { |
| rtx reg = gen_rtx_REG (V4SImode, i); |
| |
| clobs[nclobs++] |
| = gen_rtx_SET (VOIDmode, |
| reg, |
| gen_rtx_UNSPEC (V4SImode, |
| gen_rtvec (1, reg), 27)); |
| } |
| } |
| |
| insn = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (nclobs)); |
| |
| for (i = 0; i < nclobs; ++i) |
| XVECEXP (insn, 0, i) = clobs[i]; |
| |
| return insn; |
| } |
| |
| /* APPLE LOCAL begin special ObjC method use of R12 */ |
| /* Determine whether a name is an ObjC method. */ |
| |
| static int name_encodes_objc_method_p (const char *piclabel_name) |
| { |
| return (piclabel_name[0] == '+' || piclabel_name[0] == '-'); |
| } |
| /* APPLE LOCAL end special ObjC method use of R12 */ |
| |
| /* APPLE LOCAL begin recompute PIC register use */ |
| /* Sometimes a function has references that require the PIC register, |
| but optimization removes them all. To catch this case |
| recompute current_function_uses_pic_offset_table here. |
| This may allow us to eliminate the prologue and epilogue. */ |
| |
| static int |
| recompute_PIC_register_use (void) |
| { |
| if (DEFAULT_ABI == ABI_DARWIN |
| && flag_pic && current_function_uses_pic_offset_table |
| && !cfun->machine->ra_needs_full_frame) |
| { |
| rtx insn; |
| current_function_uses_pic_offset_table = 0; |
| push_topmost_sequence (); |
| for (insn = get_insns (); insn != NULL; insn = NEXT_INSN (insn)) |
| if ( reg_mentioned_p (pic_offset_table_rtx, insn)) |
| { |
| current_function_uses_pic_offset_table = 1; |
| break; |
| } |
| pop_topmost_sequence (); |
| } |
| return 0; |
| } |
| /* APPLE LOCAL end recompute PIC register use */ |
| |
| /* APPLE LOCAL begin volatile pic base reg in leaves */ |
| /* If this is a leaf function and we used any pic-based references, |
| see if there is an unused volatile reg we can use instead of R31. |
| If so set substitute_pic_base_reg to this reg, set its reg_ever_used |
| bit (to avoid confusing later calls to alloc_volatile_reg), and |
| make a pass through the existing RTL, substituting the new reg for |
| the old one wherever it appears. |
| Logically this is a void function; it is int so it can be used to |
| initialize a dummy variable, thus getting executed ahead of other |
| initializations. Technicolour yawn. */ |
| |
| /* ALLOC_VOLATILE_REG allocates a volatile register AFTER all gcc |
| register allocations have been done; we use it to reserve an |
| unused reg for holding VRsave. Returns -1 in case of failure (all |
| volatile regs are in use.) */ |
| /* Note, this is called from both the prologue and epilogue code, |
| with the assumption that it will return the same result both |
| times! Since the register arrays are not changed in between |
| this is valid, if a bit fragile. */ |
| /* In future we may also use this to grab an unused volatile reg to |
| hold the PIC base reg in the event that the current function makes |
| no procedure calls; this was done in 2.95. */ |
| static int |
| alloc_volatile_reg (void) |
| { |
| if (current_function_is_leaf |
| && reload_completed |
| && !cfun->machine->ra_needs_full_frame) |
| { |
| int r; |
| for (r = 10; r >= 2; --r) |
| if (! fixed_regs[r] && ! regs_ever_live[r]) |
| return r; |
| } |
| |
| return -1; /* fail */ |
| } |
| |
| extern rtx replace_regs (rtx x, rtx *reg_map, unsigned int nregs, int replace_dest); |
| |
| static int |
| try_leaf_pic_optimization (void) |
| { |
| if ( DEFAULT_ABI==ABI_DARWIN |
| && flag_pic && current_function_uses_pic_offset_table |
| && current_function_is_leaf |
| && !cfun->machine->ra_needs_full_frame ) |
| { |
| int reg = alloc_volatile_reg (); |
| if ( reg != -1 ) |
| { |
| /* Run through the insns, changing references to the original |
| PIC_OFFSET_TABLE_REGNUM to our new one. */ |
| rtx insn; |
| const int nregs = PIC_OFFSET_TABLE_REGNUM + 1; |
| rtx *reg_map = (rtx *) xmalloc (nregs * sizeof (rtx)); |
| memset (reg_map, 0, nregs * sizeof (rtx)); |
| reg_map[PIC_OFFSET_TABLE_REGNUM] = gen_rtx_REG (SImode, reg); |
| |
| push_topmost_sequence (); |
| for (insn = get_insns (); insn != NULL; insn = NEXT_INSN (insn)) |
| { |
| if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) |
| { |
| replace_regs (PATTERN (insn), reg_map, nregs, 1); |
| replace_regs (REG_NOTES (insn), reg_map, nregs, 1); |
| } |
| else if (GET_CODE (insn) == CALL_INSN) |
| { |
| if ( !SIBLING_CALL_P (insn)) |
| abort (); |
| } |
| } |
| pop_topmost_sequence (); |
| free (reg_map); |
| |
| regs_ever_live[reg] = 1; |
| regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 0; |
| cfun->machine->substitute_pic_base_reg = reg; |
| } |
| } |
| return 0; |
| } |
| /* APPLE LOCAL end volatile pic base reg in leaves */ |
| |
| /* Save a register into the frame, and emit RTX_FRAME_RELATED_P notes. |
| Save REGNO into [FRAME_REG + OFFSET] in mode MODE. */ |
| |
| static void |
| emit_frame_save (rtx frame_reg, rtx frame_ptr, enum machine_mode mode, |
| unsigned int regno, int offset, HOST_WIDE_INT total_size) |
| { |
| rtx reg, offset_rtx, insn, mem, addr, int_rtx; |
| rtx replacea, replaceb; |
| |
| int_rtx = GEN_INT (offset); |
| |
| /* Some cases that need register indexed addressing. */ |
| if ((TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode)) |
| || (TARGET_E500_DOUBLE && mode == DFmode) |
| || (TARGET_SPE_ABI |
| && SPE_VECTOR_MODE (mode) |
| && !SPE_CONST_OFFSET_OK (offset))) |
| { |
| /* Whomever calls us must make sure r11 is available in the |
| flow path of instructions in the prologue. */ |
| offset_rtx = gen_rtx_REG (Pmode, 11); |
| emit_move_insn (offset_rtx, int_rtx); |
| |
| replacea = offset_rtx; |
| replaceb = int_rtx; |
| } |
| else |
| { |
| offset_rtx = int_rtx; |
| replacea = NULL_RTX; |
| replaceb = NULL_RTX; |
| } |
| |
| reg = gen_rtx_REG (mode, regno); |
| addr = gen_rtx_PLUS (Pmode, frame_reg, offset_rtx); |
| mem = gen_frame_mem (mode, addr); |
| |
| insn = emit_move_insn (mem, reg); |
| |
| rs6000_frame_related (insn, frame_ptr, total_size, replacea, replaceb); |
| } |
| |
| /* Emit an offset memory reference suitable for a frame store, while |
| converting to a valid addressing mode. */ |
| |
| static rtx |
| gen_frame_mem_offset (enum machine_mode mode, rtx reg, int offset) |
| { |
| rtx int_rtx, offset_rtx; |
| |
| int_rtx = GEN_INT (offset); |
| |
| if ((TARGET_SPE_ABI && SPE_VECTOR_MODE (mode)) |
| || (TARGET_E500_DOUBLE && mode == DFmode)) |
| { |
| offset_rtx = gen_rtx_REG (Pmode, FIXED_SCRATCH); |
| emit_move_insn (offset_rtx, int_rtx); |
| } |
| else |
| offset_rtx = int_rtx; |
| |
| return gen_frame_mem (mode, gen_rtx_PLUS (Pmode, reg, offset_rtx)); |
| } |
| |
| /* Look for user-defined global regs. We should not save and restore these, |
| and cannot use stmw/lmw if there are any in its range. */ |
| |
| static bool |
| no_global_regs_above (int first_greg) |
| { |
| int i; |
| for (i = 0; i < 32 - first_greg; i++) |
| if (global_regs[first_greg + i]) |
| return false; |
| return true; |
| } |
| |
| #ifndef TARGET_FIX_AND_CONTINUE |
| #define TARGET_FIX_AND_CONTINUE 0 |
| #endif |
| |
| /* Emit function prologue as insns. */ |
| |
| void |
| rs6000_emit_prologue (void) |
| { |
| /* APPLE LOCAL recompute PIC register use */ |
| int dummy ATTRIBUTE_UNUSED = recompute_PIC_register_use (); |
| /* APPLE LOCAL volatile pic base reg in leaves */ |
| int ignored ATTRIBUTE_UNUSED = try_leaf_pic_optimization (); |
| rs6000_stack_t *info = rs6000_stack_info (); |
| enum machine_mode reg_mode = Pmode; |
| int reg_size = TARGET_32BIT ? 4 : 8; |
| rtx sp_reg_rtx = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); |
| rtx frame_ptr_rtx = gen_rtx_REG (Pmode, 12); |
| rtx frame_reg_rtx = sp_reg_rtx; |
| rtx cr_save_rtx = NULL_RTX; |
| rtx insn; |
| int saving_FPRs_inline; |
| int using_store_multiple; |
| HOST_WIDE_INT sp_offset = 0; |
| /* APPLE LOCAL begin callers_lr_already_saved */ |
| int callers_lr_already_saved = 0; |
| #if TARGET_MACHO |
| int lr_already_set_up_for_pic = 0; |
| #endif |
| /* APPLE LOCAL end callers_lr_already_saved */ |
| /* APPLE LOCAL special ObjC method use of R12 */ |
| int objc_method_using_pic = 0; |
| |
| /* APPLE LOCAL begin CW asm block */ |
| if (cfun->iasm_asm_function && cfun->iasm_frame_size == -2) |
| return; |
| /* APPLE LOCAL end CW asm block */ |
| /* APPLE LOCAL begin special ObjC method use of R12 */ |
| #if TARGET_MACHO |
| if (DEFAULT_ABI == ABI_DARWIN |
| && current_function_uses_pic_offset_table && flag_pic |
| && current_function_decl |
| && DECL_ASSEMBLER_NAME_SET_P (current_function_decl)) |
| { |
| /* At -O0, this will not be set yet, so we won't do this opt. */ |
| const char *piclabel_name |
| = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)); |
| |
| if (name_encodes_objc_method_p (piclabel_name) |
| /* If we're saving vector or FP regs via a function call, |
| then don't bother with this ObjC R12 optimization. |
| This test also eliminates world_save. */ |
| && (info->first_altivec_reg_save > LAST_ALTIVEC_REGNO |
| || VECTOR_SAVE_INLINE (info->first_altivec_reg_save)) |
| && (info->first_fp_reg_save == 64 |
| || FP_SAVE_INLINE (info->first_fp_reg_save))) |
| { |
| rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM); |
| rtx src = machopic_function_base_sym (); |
| objc_method_using_pic = 1; |
| rs6000_maybe_dead (emit_insn (gen_load_macho_picbase_label (lr, |
| src))); |
| } |
| } |
| #endif /* TARGET_MACHO */ |
| /* APPLE LOCAL end special ObjC method use of R12 */ |
| |
| if (TARGET_FIX_AND_CONTINUE) |
| { |
| /* gdb on darwin arranges to forward a function from the old |
| address by modifying the first 5 instructions of the function |
| to branch to the overriding function. This is necessary to |
| permit function pointers that point to the old function to |
| actually forward to the new function. */ |
| emit_insn (gen_nop ()); |
| emit_insn (gen_nop ()); |
| emit_insn (gen_nop ()); |
| emit_insn (gen_nop ()); |
| emit_insn (gen_nop ()); |
| } |
| |
| if (TARGET_SPE_ABI && info->spe_64bit_regs_used != 0) |
| { |
| reg_mode = V2SImode; |
| reg_size = 8; |
| } |
| |
| using_store_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64 |
| && (!TARGET_SPE_ABI |
| || info->spe_64bit_regs_used == 0) |
| && info->first_gp_reg_save < 31 |
| && no_global_regs_above (info->first_gp_reg_save)); |
| saving_FPRs_inline = (info->first_fp_reg_save == 64 |
| || FP_SAVE_INLINE (info->first_fp_reg_save) |
| || current_function_calls_eh_return |
| || cfun->machine->ra_need_lr); |
| |
| /* For V.4, update stack before we do any saving and set back pointer. */ |
| if (! WORLD_SAVE_P (info) |
| && info->push_p |
| && (DEFAULT_ABI == ABI_V4 |
| || current_function_calls_eh_return)) |
| { |
| if (info->total_size < 32767) |
| sp_offset = info->total_size; |
| else |
| frame_reg_rtx = frame_ptr_rtx; |
| rs6000_emit_allocate_stack (info->total_size, |
| (frame_reg_rtx != sp_reg_rtx |
| && (info->cr_save_p |
| || info->lr_save_p |
| || info->first_fp_reg_save < 64 |
| || info->first_gp_reg_save < 32 |
| ))); |
| if (frame_reg_rtx != sp_reg_rtx) |
| rs6000_emit_stack_tie (); |
| } |
| |
| /* Handle world saves specially here. */ |
| if (WORLD_SAVE_P (info)) |
| { |
| int i, j, sz; |
| rtx treg; |
| rtvec p; |
| rtx reg0; |
| |
| /* save_world expects lr in r0. */ |
| reg0 = gen_rtx_REG (Pmode, 0); |
| if (info->lr_save_p) |
| { |
| insn = emit_move_insn (reg0, |
| gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| } |
| |
| /* The SAVE_WORLD and RESTORE_WORLD routines make a number of |
| assumptions about the offsets of various bits of the stack |
| frame. */ |
| gcc_assert (info->gp_save_offset == -220 |
| && info->fp_save_offset == -144 |
| && info->lr_save_offset == 8 |
| && info->cr_save_offset == 4 |
| && info->push_p |
| && info->lr_save_p |
| && (!current_function_calls_eh_return |
| || info->ehrd_offset == -432) |
| && info->vrsave_save_offset == -224 |
| && info->altivec_save_offset == -416); |
| |
| treg = gen_rtx_REG (SImode, 11); |
| emit_move_insn (treg, GEN_INT (-info->total_size)); |
| |
| /* SAVE_WORLD takes the caller's LR in R0 and the frame size |
| in R11. It also clobbers R12, so beware! */ |
| |
| /* Preserve CR2 for save_world prologues */ |
| sz = 5; |
| sz += 32 - info->first_gp_reg_save; |
| sz += 64 - info->first_fp_reg_save; |
| sz += LAST_ALTIVEC_REGNO - info->first_altivec_reg_save + 1; |
| p = rtvec_alloc (sz); |
| j = 0; |
| RTVEC_ELT (p, j++) = gen_rtx_CLOBBER (VOIDmode, |
| gen_rtx_REG (Pmode, |
| LINK_REGISTER_REGNUM)); |
| RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode, |
| gen_rtx_SYMBOL_REF (Pmode, |
| "*save_world")); |
| /* We do floats first so that the instruction pattern matches |
| properly. */ |
| for (i = 0; i < 64 - info->first_fp_reg_save; i++) |
| { |
| rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i); |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->fp_save_offset |
| + sp_offset + 8 * i)); |
| rtx mem = gen_frame_mem (DFmode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg); |
| } |
| for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++) |
| { |
| rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i); |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->altivec_save_offset |
| + sp_offset + 16 * i)); |
| rtx mem = gen_frame_mem (V4SImode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg); |
| } |
| for (i = 0; i < 32 - info->first_gp_reg_save; i++) |
| { |
| rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i); |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->gp_save_offset |
| + sp_offset + reg_size * i)); |
| rtx mem = gen_frame_mem (reg_mode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg); |
| } |
| |
| { |
| /* CR register traditionally saved as CR2. */ |
| rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO); |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->cr_save_offset |
| + sp_offset)); |
| rtx mem = gen_frame_mem (reg_mode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg); |
| } |
| /* Explain about use of R0. */ |
| if (info->lr_save_p) |
| { |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->lr_save_offset |
| + sp_offset)); |
| rtx mem = gen_frame_mem (reg_mode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg0); |
| } |
| /* Explain what happens to the stack pointer. */ |
| { |
| rtx newval = gen_rtx_PLUS (Pmode, sp_reg_rtx, treg); |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, sp_reg_rtx, newval); |
| } |
| |
| insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| treg, GEN_INT (-info->total_size)); |
| sp_offset = info->total_size; |
| } |
| |
| /* APPLE LOCAL mainline */ |
| /* Moved altivec save/restore. */ |
| |
| /* If we use the link register, get it into r0. */ |
| if (!WORLD_SAVE_P (info) && info->lr_save_p) |
| { |
| insn = emit_move_insn (gen_rtx_REG (Pmode, 0), |
| gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| } |
| |
| /* If we need to save CR, put it into r12. */ |
| if (!WORLD_SAVE_P (info) && info->cr_save_p && frame_reg_rtx != frame_ptr_rtx) |
| { |
| rtx set; |
| |
| /* APPLE LOCAL begin special ObjC method use of R12 */ |
| /* For Darwin, use R2, so we don't clobber the special ObjC |
| method use of R12. R11 has a special meaning for Ada, so we |
| can't use that. */ |
| cr_save_rtx = gen_rtx_REG (SImode, DEFAULT_ABI == ABI_DARWIN ? 2 : 12); |
| /* APPLE LOCAL end special ObjC method use of R12 */ |
| insn = emit_insn (gen_movesi_from_cr (cr_save_rtx)); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| /* Now, there's no way that dwarf2out_frame_debug_expr is going |
| to understand '(unspec:SI [(reg:CC 68) ...] UNSPEC_MOVESI_FROM_CR)'. |
| But that's OK. All we have to do is specify that _one_ condition |
| code register is saved in this stack slot. The thrower's epilogue |
| will then restore all the call-saved registers. |
| We use CR2_REGNO (70) to be compatible with gcc-2.95 on Linux. */ |
| set = gen_rtx_SET (VOIDmode, cr_save_rtx, |
| gen_rtx_REG (SImode, CR2_REGNO)); |
| REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, |
| set, |
| REG_NOTES (insn)); |
| } |
| |
| /* Do any required saving of fpr's. If only one or two to save, do |
| it ourselves. Otherwise, call function. */ |
| if (!WORLD_SAVE_P (info) && saving_FPRs_inline) |
| { |
| int i; |
| for (i = 0; i < 64 - info->first_fp_reg_save; i++) |
| if ((regs_ever_live[info->first_fp_reg_save+i] |
| && ! call_used_regs[info->first_fp_reg_save+i])) |
| emit_frame_save (frame_reg_rtx, frame_ptr_rtx, DFmode, |
| info->first_fp_reg_save + i, |
| info->fp_save_offset + sp_offset + 8 * i, |
| info->total_size); |
| } |
| else if (!WORLD_SAVE_P (info) && info->first_fp_reg_save != 64) |
| { |
| int i; |
| char rname[30]; |
| const char *alloc_rname; |
| rtvec p; |
| /* APPLE LOCAL begin reduce code size */ |
| |
| int gen_following_label = 0; |
| int count = 0; |
| |
| if (current_function_uses_pic_offset_table && flag_pic |
| #ifdef INSN_SCHEDULING |
| /* Prevent the compiler from crashing |
| while scheduling insns after global_alloc! */ |
| && (optimize == 0 || !flag_schedule_insns_after_reload) |
| #endif |
| /* If this is the last CALL in the prolog, then we've got our PC. |
| If we're saving AltiVec regs via a function, we're not last. */ |
| && (info->first_altivec_reg_save > LAST_ALTIVEC_REGNO |
| || VECTOR_SAVE_INLINE (info->first_altivec_reg_save))) |
| /* LLVM LOCAL begin compile on non-darwin targets */ |
| gen_following_label = |
| #if TARGET_MACHO |
| lr_already_set_up_for_pic = |
| #endif |
| 1; |
| /* LLVM LOCAL end */ |
| /* APPLE LOCAL end reduce code size */ |
| |
| /* APPLE LOCAL begin +2 (could be conditionalized) */ |
| p = rtvec_alloc (2 + 64 - info->first_fp_reg_save + 2 |
| + gen_following_label); |
| /* APPLE LOCAL end +2 (could be conditionalized) */ |
| |
| /* APPLE LOCAL begin reduce code size */ |
| /* 0 -> count++ */ |
| RTVEC_ELT (p, count++) = gen_rtx_CLOBBER (VOIDmode, |
| gen_rtx_REG (Pmode, |
| LINK_REGISTER_REGNUM)); |
| #if TARGET_MACHO |
| /* We have to calculate the offset into saveFP to where we must |
| call (!!) SAVEFP also saves the caller's LR -- placed into |
| R0 above -- into 8(R1). SAVEFP/RESTOREFP should never be |
| called to save or restore only F31. */ |
| |
| if (info->lr_save_offset != (POINTER_SIZE / 4) || info->first_fp_reg_save == 63) |
| abort (); |
| |
| sprintf (rname, "*saveFP%s%.0d ; save f%d-f31", |
| (info->first_fp_reg_save - 32 == 14 ? "" : "+"), |
| (info->first_fp_reg_save - 46) * 4, |
| info->first_fp_reg_save - 32); |
| #else |
| /* APPLE LOCAL end reduce code size */ |
| sprintf (rname, "%s%d%s", SAVE_FP_PREFIX, |
| info->first_fp_reg_save - 32, SAVE_FP_SUFFIX); |
| /* APPLE LOCAL reduce code size */ |
| #endif /* TARGET_MACHO */ |
| alloc_rname = ggc_strdup (rname); |
| /* APPLE LOCAL reduce code size */ |
| RTVEC_ELT (p, count++) = gen_rtx_USE (VOIDmode, |
| gen_rtx_SYMBOL_REF (Pmode, |
| alloc_rname)); |
| /* APPLE LOCAL begin reduce code size */ |
| if (gen_following_label) |
| RTVEC_ELT (p, count++) = gen_rtx_USE (VOIDmode, const0_rtx); |
| /* APPLE LOCAL end reduce code size */ |
| for (i = 0; i < 64 - info->first_fp_reg_save; i++) |
| { |
| rtx addr, reg, mem; |
| reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i); |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->fp_save_offset |
| + sp_offset + 8*i)); |
| mem = gen_frame_mem (DFmode, addr); |
| |
| /* APPLE LOCAL reduce code size */ |
| RTVEC_ELT (p, count++) = gen_rtx_SET (VOIDmode, mem, reg); |
| /* APPLE LOCAL begin C++ EH and setjmp (radar 2866661) */ |
| } |
| #if TARGET_MACHO |
| /* Darwin version of these functions stores R0. */ |
| RTVEC_ELT (p, count++) = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 0)); |
| |
| /* If we saved LR, *tell* people about it! */ |
| if (info->lr_save_p) |
| { |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->lr_save_offset + sp_offset)); |
| rtx mem = gen_rtx_MEM (Pmode, addr); |
| /* This should not be of rs6000_sr_alias_set, because of |
| __builtin_return_address. */ |
| RTVEC_ELT (p, count++) = gen_rtx_SET (Pmode, mem, |
| gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); |
| } |
| #endif |
| /* APPLE LOCAL end C++ EH and setjmp (radar 2866661) */ |
| insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| NULL_RTX, NULL_RTX); |
| /* APPLE LOCAL callers_lr_already_saved */ |
| callers_lr_already_saved = 1; |
| } |
| |
| /* Save GPRs. This is done as a PARALLEL if we are using |
| the store-multiple instructions. */ |
| if (!WORLD_SAVE_P (info) && using_store_multiple) |
| { |
| rtvec p; |
| int i; |
| p = rtvec_alloc (32 - info->first_gp_reg_save); |
| for (i = 0; i < 32 - info->first_gp_reg_save; i++) |
| { |
| rtx addr, reg, mem; |
| reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i); |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->gp_save_offset |
| + sp_offset |
| + reg_size * i)); |
| mem = gen_frame_mem (reg_mode, addr); |
| |
| RTVEC_ELT (p, i) = gen_rtx_SET (VOIDmode, mem, reg); |
| } |
| insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| NULL_RTX, NULL_RTX); |
| } |
| else if (!WORLD_SAVE_P (info)) |
| { |
| int i; |
| for (i = 0; i < 32 - info->first_gp_reg_save; i++) |
| if ((regs_ever_live[info->first_gp_reg_save + i] |
| && (!call_used_regs[info->first_gp_reg_save + i] |
| || (i + info->first_gp_reg_save |
| == RS6000_PIC_OFFSET_TABLE_REGNUM |
| && TARGET_TOC && TARGET_MINIMAL_TOC))) |
| || (i + info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM |
| && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0) |
| /* APPLE LOCAL begin volatile pic base reg in leaves */ |
| || (DEFAULT_ABI == ABI_DARWIN && flag_pic |
| && ((current_function_uses_pic_offset_table |
| && cfun->machine->substitute_pic_base_reg |
| == INVALID_REGNUM) |
| || cfun->machine->ra_needs_full_frame))))) |
| /* APPLE LOCAL end volatile pic base reg in leaves */ |
| { |
| rtx addr, reg, mem; |
| reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i); |
| |
| if (TARGET_SPE_ABI && info->spe_64bit_regs_used != 0) |
| { |
| int offset = info->spe_gp_save_offset + sp_offset + 8 * i; |
| rtx b; |
| |
| if (!SPE_CONST_OFFSET_OK (offset)) |
| { |
| b = gen_rtx_REG (Pmode, FIXED_SCRATCH); |
| emit_move_insn (b, GEN_INT (offset)); |
| } |
| else |
| b = GEN_INT (offset); |
| |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, b); |
| mem = gen_frame_mem (V2SImode, addr); |
| insn = emit_move_insn (mem, reg); |
| |
| if (GET_CODE (b) == CONST_INT) |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| NULL_RTX, NULL_RTX); |
| else |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| b, GEN_INT (offset)); |
| } |
| else |
| { |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->gp_save_offset |
| + sp_offset |
| + reg_size * i)); |
| mem = gen_frame_mem (reg_mode, addr); |
| |
| insn = emit_move_insn (mem, reg); |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| NULL_RTX, NULL_RTX); |
| } |
| } |
| } |
| |
| /* ??? There's no need to emit actual instructions here, but it's the |
| easiest way to get the frame unwind information emitted. */ |
| if (current_function_calls_eh_return) |
| { |
| unsigned int i, regno; |
| |
| /* In AIX ABI we need to pretend we save r2 here. */ |
| if (TARGET_AIX) |
| { |
| rtx addr, reg, mem; |
| |
| reg = gen_rtx_REG (reg_mode, 2); |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (sp_offset + 5 * reg_size)); |
| mem = gen_frame_mem (reg_mode, addr); |
| |
| insn = emit_move_insn (mem, reg); |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| NULL_RTX, NULL_RTX); |
| PATTERN (insn) = gen_blockage (); |
| } |
| |
| for (i = 0; ; ++i) |
| { |
| regno = EH_RETURN_DATA_REGNO (i); |
| if (regno == INVALID_REGNUM) |
| break; |
| |
| emit_frame_save (frame_reg_rtx, frame_ptr_rtx, reg_mode, regno, |
| info->ehrd_offset + sp_offset |
| + reg_size * (int) i, |
| info->total_size); |
| } |
| } |
| |
| /* APPLE LOCAL begin special ObjC method use of R12 */ |
| if (objc_method_using_pic) |
| rs6000_maybe_dead ( |
| emit_move_insn (gen_rtx_REG (Pmode, |
| cfun->machine->substitute_pic_base_reg |
| == INVALID_REGNUM |
| ? PIC_OFFSET_TABLE_REGNUM |
| : cfun->machine->substitute_pic_base_reg), |
| gen_rtx_REG (Pmode, 12))); |
| /* APPLE LOCAL end special ObjC method use of R12 */ |
| |
| /* Save lr if we used it. */ |
| /* APPLE LOCAL callers_lr_already_saved */ |
| if (!WORLD_SAVE_P (info) && info->lr_save_p && !callers_lr_already_saved) |
| { |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->lr_save_offset + sp_offset)); |
| rtx reg = gen_rtx_REG (Pmode, 0); |
| rtx mem = gen_rtx_MEM (Pmode, addr); |
| /* This should not be of frame_alias_set, because of |
| __builtin_return_address. */ |
| |
| insn = emit_move_insn (mem, reg); |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| NULL_RTX, NULL_RTX); |
| } |
| |
| /* Save CR if we use any that must be preserved. */ |
| if (!WORLD_SAVE_P (info) && info->cr_save_p) |
| { |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->cr_save_offset + sp_offset)); |
| rtx mem = gen_frame_mem (SImode, addr); |
| /* See the large comment above about why CR2_REGNO is used. */ |
| rtx magic_eh_cr_reg = gen_rtx_REG (SImode, CR2_REGNO); |
| |
| /* If r12 was used to hold the original sp, copy cr into r0 now |
| that it's free. */ |
| if (REGNO (frame_reg_rtx) == 12) |
| { |
| rtx set; |
| |
| cr_save_rtx = gen_rtx_REG (SImode, 0); |
| insn = emit_insn (gen_movesi_from_cr (cr_save_rtx)); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| set = gen_rtx_SET (VOIDmode, cr_save_rtx, magic_eh_cr_reg); |
| REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, |
| set, |
| REG_NOTES (insn)); |
| |
| } |
| insn = emit_move_insn (mem, cr_save_rtx); |
| |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| NULL_RTX, NULL_RTX); |
| } |
| |
| /* Update stack and set back pointer unless this is V.4, |
| for which it was done previously. */ |
| /* APPLE LOCAL begin mainline */ |
| if (!WORLD_SAVE_P (info) && info->push_p |
| && !(DEFAULT_ABI == ABI_V4 || current_function_calls_eh_return)) |
| { |
| if (info->total_size < 32767) |
| sp_offset = info->total_size; |
| else |
| frame_reg_rtx = frame_ptr_rtx; |
| |
| rs6000_emit_allocate_stack (info->total_size, |
| (frame_reg_rtx != sp_reg_rtx |
| && ((info->altivec_size != 0) |
| || (info->vrsave_mask != 0)))); |
| |
| if (frame_reg_rtx != sp_reg_rtx) |
| rs6000_emit_stack_tie (); |
| } |
| /* APPLE LOCAL end mainline */ |
| |
| /* Set frame pointer, if needed. */ |
| if (frame_pointer_needed) |
| { |
| insn = emit_move_insn (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM), |
| sp_reg_rtx); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| } |
| |
| /* APPLE LOCAL begin mainline */ |
| /* Save AltiVec registers if needed. Save here because the red zone does |
| not include AltiVec registers. */ |
| if (!WORLD_SAVE_P (info) && TARGET_ALTIVEC_ABI && info->altivec_size != 0) |
| { |
| int i; |
| |
| /* There should be a non inline version of this, for when we |
| are saving lots of vector registers. */ |
| for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i) |
| if (info->vrsave_mask & ALTIVEC_REG_BIT (i)) |
| { |
| rtx areg, savereg, mem; |
| int offset; |
| |
| offset = info->altivec_save_offset + sp_offset |
| + 16 * (i - info->first_altivec_reg_save); |
| |
| savereg = gen_rtx_REG (V4SImode, i); |
| |
| areg = gen_rtx_REG (Pmode, 0); |
| emit_move_insn (areg, GEN_INT (offset)); |
| |
| /* AltiVec addressing mode is [reg+reg]. */ |
| mem = gen_frame_mem (V4SImode, |
| gen_rtx_PLUS (Pmode, frame_reg_rtx, areg)); |
| |
| insn = emit_move_insn (mem, savereg); |
| |
| rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, |
| areg, GEN_INT (offset)); |
| } |
| } |
| |
| /* VRSAVE is a bit vector representing which AltiVec registers |
| are used. The OS uses this to determine which vector |
| registers to save on a context switch. We need to save |
| VRSAVE on the stack frame, add whatever AltiVec registers we |
| used in this function, and do the corresponding magic in the |
| epilogue. */ |
| |
| if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE |
| && info->vrsave_mask != 0) |
| { |
| rtx reg, mem, vrsave; |
| int offset; |
| |
| /* Get VRSAVE onto a GPR. Note that ABI_V4 might be using r12 |
| as frame_reg_rtx and r11 as the static chain pointer for |
| nested functions. */ |
| reg = gen_rtx_REG (SImode, 0); |
| vrsave = gen_rtx_REG (SImode, VRSAVE_REGNO); |
| if (TARGET_MACHO) |
| emit_insn (gen_get_vrsave_internal (reg)); |
| else |
| emit_insn (gen_rtx_SET (VOIDmode, reg, vrsave)); |
| |
| if (!WORLD_SAVE_P (info)) |
| { |
| /* Save VRSAVE. */ |
| offset = info->vrsave_save_offset + sp_offset; |
| /* APPLE LOCAL begin 5774356 */ |
| debug_vrsave_offset = offset; |
| debug_sp_offset = sp_offset; |
| /* APPLE LOCAL end 5774356 */ |
| mem = gen_frame_mem (SImode, |
| gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (offset))); |
| insn = emit_move_insn (mem, reg); |
| } |
| |
| /* Include the registers in the mask. */ |
| emit_insn (gen_iorsi3 (reg, reg, GEN_INT ((int) info->vrsave_mask))); |
| |
| insn = emit_insn (generate_set_vrsave (reg, info, 0)); |
| } |
| |
| /* APPLE LOCAL end mainline */ |
| /* If we are using RS6000_PIC_OFFSET_TABLE_REGNUM, we need to set it up. */ |
| if ((TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0) |
| || (DEFAULT_ABI == ABI_V4 |
| && (flag_pic == 1 || (flag_pic && TARGET_SECURE_PLT)) |
| && regs_ever_live[RS6000_PIC_OFFSET_TABLE_REGNUM])) |
| { |
| /* If emit_load_toc_table will use the link register, we need to save |
| it. We use R12 for this purpose because emit_load_toc_table |
| can use register 0. This allows us to use a plain 'blr' to return |
| from the procedure more often. */ |
| int save_LR_around_toc_setup = (TARGET_ELF |
| && DEFAULT_ABI != ABI_AIX |
| && flag_pic |
| && ! info->lr_save_p |
| && EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0); |
| if (save_LR_around_toc_setup) |
| { |
| rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM); |
| |
| insn = emit_move_insn (frame_ptr_rtx, lr); |
| rs6000_maybe_dead (insn); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| |
| rs6000_emit_load_toc_table (TRUE); |
| |
| insn = emit_move_insn (lr, frame_ptr_rtx); |
| rs6000_maybe_dead (insn); |
| RTX_FRAME_RELATED_P (insn) = 1; |
| } |
| else |
| rs6000_emit_load_toc_table (TRUE); |
| } |
| |
| #if TARGET_MACHO |
| if (DEFAULT_ABI == ABI_DARWIN |
| /* APPLE LOCAL special ObjC method use of R12 */ |
| && !objc_method_using_pic |
| && flag_pic && current_function_uses_pic_offset_table) |
| { |
| rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM); |
| rtx src = machopic_function_base_sym (); |
| |
| /* Save and restore LR locally around this call (in R0). */ |
| if (!info->lr_save_p) |
| rs6000_maybe_dead (emit_move_insn (gen_rtx_REG (Pmode, 0), lr)); |
| |
| /* APPLE LOCAL begin performance enhancement */ |
| if (!lr_already_set_up_for_pic) |
| rs6000_maybe_dead (emit_insn (gen_load_macho_picbase (lr, src))); |
| /* APPLE LOCAL end performance enhancement */ |
| |
| /* APPLE LOCAL begin volatile pic base reg in leaves */ |
| insn = emit_move_insn (gen_rtx_REG (Pmode, |
| (cfun->machine->substitute_pic_base_reg |
| == INVALID_REGNUM) |
| ? RS6000_PIC_OFFSET_TABLE_REGNUM |
| : cfun->machine->substitute_pic_base_reg), |
| lr); |
| rs6000_maybe_dead (insn); |
| /* APPLE LOCAL end volatile pic base reg in leaves */ |
| |
| if (!info->lr_save_p) |
| rs6000_maybe_dead (emit_move_insn (lr, gen_rtx_REG (Pmode, 0))); |
| } |
| #endif |
| } |
| |
| /* Write function prologue. */ |
| |
| static void |
| rs6000_output_function_prologue (FILE *file, |
| HOST_WIDE_INT size ATTRIBUTE_UNUSED) |
| { |
| rs6000_stack_t *info = rs6000_stack_info (); |
| |
| if (TARGET_DEBUG_STACK) |
| debug_stack_info (info); |
| |
| /* APPLE LOCAL do not extern fp save/restore */ |
| #if !TARGET_MACHO |
| /* Write .extern for any function we will call to save and restore |
| fp values. */ |
| if (info->first_fp_reg_save < 64 |
| && !FP_SAVE_INLINE (info->first_fp_reg_save)) |
| fprintf (file, "\t.extern %s%d%s\n\t.extern %s%d%s\n", |
| SAVE_FP_PREFIX, info->first_fp_reg_save - 32, SAVE_FP_SUFFIX, |
| RESTORE_FP_PREFIX, info->first_fp_reg_save - 32, |
| RESTORE_FP_SUFFIX); |
| /* APPLE LOCAL do not extern fp save/restore */ |
| #endif /* !TARGET_MACHO */ |
| |
| /* Write .extern for AIX common mode routines, if needed. */ |
| if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined) |
| { |
| fputs ("\t.extern __mulh\n", file); |
| fputs ("\t.extern __mull\n", file); |
| fputs ("\t.extern __divss\n", file); |
| fputs ("\t.extern __divus\n", file); |
| fputs ("\t.extern __quoss\n", file); |
| fputs ("\t.extern __quous\n", file); |
| common_mode_defined = 1; |
| } |
| |
| if (! HAVE_prologue) |
| { |
| start_sequence (); |
| |
| /* A NOTE_INSN_DELETED is supposed to be at the start and end of |
| the "toplevel" insn chain. */ |
| emit_note (NOTE_INSN_DELETED); |
| rs6000_emit_prologue (); |
| emit_note (NOTE_INSN_DELETED); |
| |
| /* Expand INSN_ADDRESSES so final() doesn't crash. */ |
| { |
| rtx insn; |
| unsigned addr = 0; |
| for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn)) |
| { |
| INSN_ADDRESSES_NEW (insn, addr); |
| addr += 4; |
| } |
| } |
| |
| if (TARGET_DEBUG_STACK) |
| debug_rtx_list (get_insns (), 100); |
| final (get_insns (), file, FALSE); |
| end_sequence (); |
| } |
| |
| rs6000_pic_labelno++; |
| } |
| |
| /* Emit function epilogue as insns. |
| |
| At present, dwarf2out_frame_debug_expr doesn't understand |
| register restores, so we don't bother setting RTX_FRAME_RELATED_P |
| anywhere in the epilogue. Most of the insns below would in any case |
| need special notes to explain where r11 is in relation to the stack. */ |
| |
| void |
| rs6000_emit_epilogue (int sibcall) |
| { |
| rs6000_stack_t *info; |
| int restoring_FPRs_inline; |
| int using_load_multiple; |
| int using_mfcr_multiple; |
| int use_backchain_to_restore_sp; |
| int sp_offset = 0; |
| rtx sp_reg_rtx = gen_rtx_REG (Pmode, 1); |
| rtx frame_reg_rtx = sp_reg_rtx; |
| enum machine_mode reg_mode = Pmode; |
| int reg_size = TARGET_32BIT ? 4 : 8; |
| int i; |
| |
| /* APPLE LOCAL begin CW asm block */ |
| if (cfun->iasm_asm_function && cfun->iasm_frame_size == -2) |
| { |
| |
| rtvec p = rtvec_alloc (2); |
| |
| RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode); |
| RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, |
| gen_rtx_REG (Pmode, |
| LINK_REGISTER_REGNUM)); |
| emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
| return; |
| } |
| /* APPLE LOCAL end CW asm block */ |
| |
| info = rs6000_stack_info (); |
| |
| if (TARGET_SPE_ABI && info->spe_64bit_regs_used != 0) |
| { |
| reg_mode = V2SImode; |
| reg_size = 8; |
| } |
| |
| using_load_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64 |
| && (!TARGET_SPE_ABI |
| || info->spe_64bit_regs_used == 0) |
| && info->first_gp_reg_save < 31 |
| && no_global_regs_above (info->first_gp_reg_save)); |
| restoring_FPRs_inline = (sibcall |
| || current_function_calls_eh_return |
| || info->first_fp_reg_save == 64 |
| || FP_SAVE_INLINE (info->first_fp_reg_save)); |
| use_backchain_to_restore_sp = (frame_pointer_needed |
| || current_function_calls_alloca |
| || info->total_size > 32767); |
| using_mfcr_multiple = (rs6000_cpu == PROCESSOR_PPC601 |
| || rs6000_cpu == PROCESSOR_PPC603 |
| || rs6000_cpu == PROCESSOR_PPC750 |
| /* APPLE LOCAL ? */ |
| || rs6000_cpu == PROCESSOR_PPC7400 |
| || optimize_size); |
| |
| if (WORLD_SAVE_P (info)) |
| { |
| int i, j; |
| char rname[30]; |
| const char *alloc_rname; |
| rtvec p; |
| |
| /* eh_rest_world_r10 will return to the location saved in the LR |
| stack slot (which is not likely to be our caller.) |
| Input: R10 -- stack adjustment. Clobbers R0, R11, R12, R7, R8. |
| rest_world is similar, except any R10 parameter is ignored. |
| The exception-handling stuff that was here in 2.95 is no |
| longer necessary. */ |
| |
| p = rtvec_alloc (9 |
| + 1 |
| + 32 - info->first_gp_reg_save |
| + LAST_ALTIVEC_REGNO + 1 - info->first_altivec_reg_save |
| + 63 + 1 - info->first_fp_reg_save); |
| |
| strcpy (rname, ((current_function_calls_eh_return) ? |
| "*eh_rest_world_r10" : "*rest_world")); |
| alloc_rname = ggc_strdup (rname); |
| |
| j = 0; |
| RTVEC_ELT (p, j++) = gen_rtx_RETURN (VOIDmode); |
| RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode, |
| gen_rtx_REG (Pmode, |
| LINK_REGISTER_REGNUM)); |
| RTVEC_ELT (p, j++) |
| = gen_rtx_USE (VOIDmode, gen_rtx_SYMBOL_REF (Pmode, alloc_rname)); |
| /* The instruction pattern requires a clobber here; |
| it is shared with the restVEC helper. */ |
| RTVEC_ELT (p, j++) |
| = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 11)); |
| |
| { |
| /* CR register traditionally saved as CR2. */ |
| rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO); |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->cr_save_offset)); |
| rtx mem = gen_frame_mem (reg_mode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem); |
| } |
| |
| for (i = 0; i < 32 - info->first_gp_reg_save; i++) |
| { |
| rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i); |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->gp_save_offset |
| + reg_size * i)); |
| rtx mem = gen_frame_mem (reg_mode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem); |
| } |
| for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++) |
| { |
| rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i); |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->altivec_save_offset |
| + 16 * i)); |
| rtx mem = gen_frame_mem (V4SImode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem); |
| } |
| for (i = 0; info->first_fp_reg_save + i <= 63; i++) |
| { |
| rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i); |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->fp_save_offset |
| + 8 * i)); |
| rtx mem = gen_frame_mem (DFmode, addr); |
| |
| RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem); |
| } |
| RTVEC_ELT (p, j++) |
| = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 0)); |
| RTVEC_ELT (p, j++) |
| = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 12)); |
| RTVEC_ELT (p, j++) |
| = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 7)); |
| RTVEC_ELT (p, j++) |
| = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 8)); |
| RTVEC_ELT (p, j++) |
| = gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, 10)); |
| emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
| |
| return; |
| } |
| |
| /* APPLE LOCAL begin mainline */ |
| /* Set sp_offset based on the stack push from the prologue. */ |
| /* APPLE LOCAL begin 5774356 */ |
| if (info->push_p |
| && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_DARWIN || current_function_calls_eh_return) |
| /* APPLE LOCAL end 5664356 */ |
| && info->total_size < 32767) |
| sp_offset = info->total_size; |
| |
| /* Restore AltiVec registers if needed. */ |
| if (TARGET_ALTIVEC_ABI && info->altivec_size != 0) |
| { |
| int i; |
| |
| for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i) |
| if (info->vrsave_mask & ALTIVEC_REG_BIT (i)) |
| { |
| rtx addr, areg, mem; |
| |
| areg = gen_rtx_REG (Pmode, 0); |
| emit_move_insn |
| (areg, GEN_INT (info->altivec_save_offset |
| + sp_offset |
| + 16 * (i - info->first_altivec_reg_save))); |
| |
| /* AltiVec addressing mode is [reg+reg]. */ |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg); |
| mem = gen_frame_mem (V4SImode, addr); |
| |
| emit_move_insn (gen_rtx_REG (V4SImode, i), mem); |
| } |
| } |
| |
| /* Restore VRSAVE if needed. */ |
| if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE |
| && info->vrsave_mask != 0) |
| { |
| rtx addr, mem, reg; |
| |
| /* APPLE LOCAL begin 5774356 */ |
| gcc_assert (debug_sp_offset == sp_offset); |
| gcc_assert (debug_vrsave_offset == (info->vrsave_save_offset + sp_offset)); |
| /* APPLE LOCAL end 5774356 */ |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->vrsave_save_offset + sp_offset)); |
| mem = gen_frame_mem (SImode, addr); |
| reg = gen_rtx_REG (SImode, 12); |
| emit_move_insn (reg, mem); |
| |
| emit_insn (generate_set_vrsave (reg, info, 1)); |
| } |
| |
| sp_offset = 0; |
| |
| /* If we have a frame pointer, a call to alloca, or a large stack |
| frame, restore the old stack pointer using the backchain. Otherwise, |
| we know what size to update it with. */ |
| if (use_backchain_to_restore_sp) |
| { |
| /* Under V.4, don't reset the stack pointer until after we're done |
| loading the saved registers. */ |
| if (DEFAULT_ABI == ABI_V4) |
| frame_reg_rtx = gen_rtx_REG (Pmode, 11); |
| |
| emit_move_insn (frame_reg_rtx, |
| gen_rtx_MEM (Pmode, sp_reg_rtx)); |
| } |
| else if (info->push_p) |
| { |
| if (DEFAULT_ABI == ABI_V4 |
| || current_function_calls_eh_return) |
| sp_offset = info->total_size; |
| else |
| { |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx, |
| GEN_INT (info->total_size)) |
| : gen_adddi3 (sp_reg_rtx, sp_reg_rtx, |
| GEN_INT (info->total_size))); |
| } |
| } |
| |
| /* APPLE LOCAL end mainline */ |
| /* Get the old lr if we saved it. */ |
| if (info->lr_save_p) |
| { |
| rtx mem = gen_frame_mem_offset (Pmode, frame_reg_rtx, |
| info->lr_save_offset + sp_offset); |
| |
| emit_move_insn (gen_rtx_REG (Pmode, 0), mem); |
| } |
| |
| /* Get the old cr if we saved it. */ |
| if (info->cr_save_p) |
| { |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->cr_save_offset + sp_offset)); |
| rtx mem = gen_frame_mem (SImode, addr); |
| |
| /* APPLE LOCAL begin use R11 because of ObjC use of R12 in sibcall to CTR */ |
| emit_move_insn (gen_rtx_REG (SImode, |
| DEFAULT_ABI == ABI_DARWIN ? 11 : 12), mem); |
| /* APPLE LOCAL end use R11 because of ObjC use of R12 in sibcall to CTR */ |
| } |
| |
| /* Set LR here to try to overlap restores below. */ |
| if (info->lr_save_p) |
| emit_move_insn (gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM), |
| gen_rtx_REG (Pmode, 0)); |
| |
| /* Load exception handler data registers, if needed. */ |
| if (current_function_calls_eh_return) |
| { |
| unsigned int i, regno; |
| |
| if (TARGET_AIX) |
| { |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (sp_offset + 5 * reg_size)); |
| rtx mem = gen_frame_mem (reg_mode, addr); |
| |
| emit_move_insn (gen_rtx_REG (reg_mode, 2), mem); |
| } |
| |
| for (i = 0; ; ++i) |
| { |
| rtx mem; |
| |
| regno = EH_RETURN_DATA_REGNO (i); |
| if (regno == INVALID_REGNUM) |
| break; |
| |
| mem = gen_frame_mem_offset (reg_mode, frame_reg_rtx, |
| info->ehrd_offset + sp_offset |
| + reg_size * (int) i); |
| |
| emit_move_insn (gen_rtx_REG (reg_mode, regno), mem); |
| } |
| } |
| |
| /* Restore GPRs. This is done as a PARALLEL if we are using |
| the load-multiple instructions. */ |
| if (using_load_multiple) |
| { |
| rtvec p; |
| p = rtvec_alloc (32 - info->first_gp_reg_save); |
| for (i = 0; i < 32 - info->first_gp_reg_save; i++) |
| { |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->gp_save_offset |
| + sp_offset |
| + reg_size * i)); |
| rtx mem = gen_frame_mem (reg_mode, addr); |
| |
| RTVEC_ELT (p, i) = |
| gen_rtx_SET (VOIDmode, |
| gen_rtx_REG (reg_mode, info->first_gp_reg_save + i), |
| mem); |
| } |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
| } |
| else |
| for (i = 0; i < 32 - info->first_gp_reg_save; i++) |
| if ((regs_ever_live[info->first_gp_reg_save + i] |
| && (!call_used_regs[info->first_gp_reg_save + i] |
| || (i + info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM |
| && TARGET_TOC && TARGET_MINIMAL_TOC))) |
| || (i + info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM |
| && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0) |
| /* APPLE LOCAL begin darwin native */ |
| || (DEFAULT_ABI == ABI_DARWIN && flag_pic |
| && ((current_function_uses_pic_offset_table |
| && cfun->machine->substitute_pic_base_reg |
| == INVALID_REGNUM) |
| || cfun->machine->ra_needs_full_frame))))) |
| |
| /* APPLE LOCAL end darwin native */ |
| { |
| rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->gp_save_offset |
| + sp_offset |
| + reg_size * i)); |
| rtx mem = gen_frame_mem (reg_mode, addr); |
| |
| /* Restore 64-bit quantities for SPE. */ |
| if (TARGET_SPE_ABI && info->spe_64bit_regs_used != 0) |
| { |
| int offset = info->spe_gp_save_offset + sp_offset + 8 * i; |
| rtx b; |
| |
| if (!SPE_CONST_OFFSET_OK (offset)) |
| { |
| b = gen_rtx_REG (Pmode, FIXED_SCRATCH); |
| emit_move_insn (b, GEN_INT (offset)); |
| } |
| else |
| b = GEN_INT (offset); |
| |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, b); |
| mem = gen_frame_mem (V2SImode, addr); |
| } |
| |
| emit_move_insn (gen_rtx_REG (reg_mode, |
| info->first_gp_reg_save + i), mem); |
| } |
| |
| /* Restore fpr's if we need to do it without calling a function. */ |
| if (restoring_FPRs_inline) |
| for (i = 0; i < 64 - info->first_fp_reg_save; i++) |
| if ((regs_ever_live[info->first_fp_reg_save+i] |
| && ! call_used_regs[info->first_fp_reg_save+i])) |
| { |
| rtx addr, mem; |
| addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, |
| GEN_INT (info->fp_save_offset |
| + sp_offset |
| + 8 * i)); |
| mem = gen_frame_mem (DFmode, addr); |
| |
| emit_move_insn (gen_rtx_REG (DFmode, |
| info->first_fp_reg_save + i), |
| mem); |
| } |
| |
| /* If we saved cr, restore it here. Just those that were used. */ |
| if (info->cr_save_p) |
| { |
| /* APPLE LOCAL use R11 because of ObjC use of R12 in sibcall to CTR */ |
| /* APPLE LOCAL silly name retained to minimize deviation from FSF */ |
| rtx r12_rtx = gen_rtx_REG (SImode, DEFAULT_ABI == ABI_DARWIN ? 11 : 12); |
| int count = 0; |
| |
| if (using_mfcr_multiple) |
| { |
| for (i = 0; i < 8; i++) |
| if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) |
| count++; |
| gcc_assert (count); |
| } |
| |
| if (using_mfcr_multiple && count > 1) |
| { |
| rtvec p; |
| int ndx; |
| |
| p = rtvec_alloc (count); |
| |
| ndx = 0; |
| for (i = 0; i < 8; i++) |
| if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) |
| { |
| rtvec r = rtvec_alloc (2); |
| RTVEC_ELT (r, 0) = r12_rtx; |
| RTVEC_ELT (r, 1) = GEN_INT (1 << (7-i)); |
| RTVEC_ELT (p, ndx) = |
| gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CR0_REGNO+i), |
| gen_rtx_UNSPEC (CCmode, r, UNSPEC_MOVESI_TO_CR)); |
| ndx++; |
| } |
| emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
| gcc_assert (ndx == count); |
| } |
| else |
| for (i = 0; i < 8; i++) |
| if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) |
| { |
| emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode, |
| CR0_REGNO+i), |
| r12_rtx)); |
| } |
| } |
| |
| /* If this is V.4, unwind the stack pointer after all of the loads |
| have been done. */ |
| if (frame_reg_rtx != sp_reg_rtx) |
| { |
| /* This blockage is needed so that sched doesn't decide to move |
| the sp change before the register restores. */ |
| rs6000_emit_stack_tie (); |
| emit_move_insn (sp_reg_rtx, frame_reg_rtx); |
| } |
| else if (sp_offset != 0) |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx, |
| GEN_INT (sp_offset)) |
| : gen_adddi3 (sp_reg_rtx, sp_reg_rtx, |
| GEN_INT (sp_offset))); |
| |
| if (current_function_calls_eh_return) |
| { |
| rtx sa = EH_RETURN_STACKADJ_RTX; |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx, sa) |
| : gen_adddi3 (sp_reg_rtx, sp_reg_rtx, sa)); |
| } |
| |
| if (!sibcall) |
| { |
| rtvec p; |
| if (! restoring_FPRs_inline) |
| p = rtvec_alloc (3 + 64 - info->first_fp_reg_save); |
| else |
| p = rtvec_alloc (2); |
| |
| RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode); |
| RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, |
| gen_rtx_REG (Pmode, |
| LINK_REGISTER_REGNUM)); |
| |
| /* If we have to restore more than two FP registers, branch to the |
| restore function. It will return to our caller. */ |
| if (! restoring_FPRs_inline) |
| { |
| int i; |
| char rname[30]; |
| const char *alloc_rname; |
| |
| /* APPLE LOCAL begin Reduce code size / improve performance */ |
| #if TARGET_MACHO |
| /* We have to calculate the offset into RESTFP to where we must |
| call (!!) RESTFP also restores the caller's LR from 8(R1). |
| RESTFP should *never* be called to restore only F31. */ |
| |
| if (info->lr_save_offset != (POINTER_SIZE / 4) || info->first_fp_reg_save == 63) |
| abort (); |
| |
| sprintf (rname, "*restFP%s%.0d ; restore f%d-f31", |
| (info->first_fp_reg_save - 32 == 14 ? "" : "+"), |
| (info->first_fp_reg_save - 46) * 4, |
| info->first_fp_reg_save - 32); |
| #else |
| /* APPLE LOCAL end Reduce code size / improve performance */ |
| sprintf (rname, "%s%d%s", RESTORE_FP_PREFIX, |
| info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX); |
| /* APPLE LOCAL Reduce code size / improve performance */ |
| #endif /* TARGET_MACHO */ |
| alloc_rname = ggc_strdup (rname); |
| RTVEC_ELT (p, 2) = gen_rtx_USE (VOIDmode, |
| gen_rtx_SYMBOL_REF (Pmode, |
| alloc_rname)); |
| |
| for (i = 0; i < 64 - info->first_fp_reg_save; i++) |
| { |
| rtx addr, mem; |
| addr = gen_rtx_PLUS (Pmode, sp_reg_rtx, |
| GEN_INT (info->fp_save_offset + 8*i)); |
| mem = gen_frame_mem (DFmode, addr); |
| |
| RTVEC_ELT (p, i+3) = |
| gen_rtx_SET (VOIDmode, |
| gen_rtx_REG (DFmode, info->first_fp_reg_save + i), |
| mem); |
| } |
| } |
| |
| emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p)); |
| } |
| } |
| |
| /* Write function epilogue. */ |
| |
| static void |
| rs6000_output_function_epilogue (FILE *file, |
| HOST_WIDE_INT size ATTRIBUTE_UNUSED) |
| { |
| if (! HAVE_epilogue) |
| { |
| rtx insn = get_last_insn (); |
| /* If the last insn was a BARRIER, we don't have to write anything except |
| the trace table. */ |
| if (GET_CODE (insn) == NOTE) |
| insn = prev_nonnote_insn (insn); |
| if (insn == 0 || GET_CODE (insn) != BARRIER) |
| { |
| /* This is slightly ugly, but at least we don't have two |
| copies of the epilogue-emitting code. */ |
| start_sequence (); |
| |
| /* A NOTE_INSN_DELETED is supposed to be at the start |
| and end of the "toplevel" insn chain. */ |
| emit_note (NOTE_INSN_DELETED); |
| rs6000_emit_epilogue (FALSE); |
| emit_note (NOTE_INSN_DELETED); |
| |
| /* Expand INSN_ADDRESSES so final() doesn't crash. */ |
| { |
| rtx insn; |
| unsigned addr = 0; |
| for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn)) |
| { |
| INSN_ADDRESSES_NEW (insn, addr); |
| addr += 4; |
| } |
| } |
| |
| if (TARGET_DEBUG_STACK) |
| debug_rtx_list (get_insns (), 100); |
| final (get_insns (), file, FALSE); |
| end_sequence (); |
| } |
| } |
| |
| #if TARGET_MACHO |
| macho_branch_islands (); |
| /* Mach-O doesn't support labels at the end of objects, so if |
| it looks like we might want one, insert a NOP. */ |
| { |
| rtx insn = get_last_insn (); |
| while (insn |
| && NOTE_P (insn) |
| && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED_LABEL) |
| insn = PREV_INSN (insn); |
| if (insn |
| && (LABEL_P (insn) |
| || (NOTE_P (insn) |
| && NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL))) |
| fputs ("\tnop\n", file); |
| } |
| #endif |
| |
| /* Output a traceback table here. See /usr/include/sys/debug.h for info |
| on its format. |
| |
| We don't output a traceback table if -finhibit-size-directive was |
| used. The documentation for -finhibit-size-directive reads |
| ``don't output a @code{.size} assembler directive, or anything |
| else that would cause trouble if the function is split in the |
| middle, and the two halves are placed at locations far apart in |
| memory.'' The traceback table has this property, since it |
| includes the offset from the start of the function to the |
| traceback table itself. |
| |
| System V.4 Powerpc's (and the embedded ABI derived from it) use a |
| different traceback table. */ |
| if (DEFAULT_ABI == ABI_AIX && ! flag_inhibit_size_directive |
| && rs6000_traceback != traceback_none && !current_function_is_thunk) |
| { |
| const char *fname = NULL; |
| const char *language_string = lang_hooks.name; |
| int fixed_parms = 0, float_parms = 0, parm_info = 0; |
| int i; |
| int optional_tbtab; |
| rs6000_stack_t *info = rs6000_stack_info (); |
| |
| if (rs6000_traceback == traceback_full) |
| optional_tbtab = 1; |
| else if (rs6000_traceback == traceback_part) |
| optional_tbtab = 0; |
| else |
| optional_tbtab = !optimize_size && !TARGET_ELF; |
| |
| if (optional_tbtab) |
| { |
| fname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); |
| while (*fname == '.') /* V.4 encodes . in the name */ |
| fname++; |
| |
| /* Need label immediately before tbtab, so we can compute |
| its offset from the function start. */ |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT"); |
| ASM_OUTPUT_LABEL (file, fname); |
| } |
| |
| /* The .tbtab pseudo-op can only be used for the first eight |
| expressions, since it can't handle the possibly variable |
| length fields that follow. However, if you omit the optional |
| fields, the assembler outputs zeros for all optional fields |
| anyways, giving each variable length field is minimum length |
| (as defined in sys/debug.h). Thus we can not use the .tbtab |
| pseudo-op at all. */ |
| |
| /* An all-zero word flags the start of the tbtab, for debuggers |
| that have to find it by searching forward from the entry |
| point or from the current pc. */ |
| fputs ("\t.long 0\n", file); |
| |
| /* Tbtab format type. Use format type 0. */ |
| fputs ("\t.byte 0,", file); |
| |
| /* Language type. Unfortunately, there does not seem to be any |
| official way to discover the language being compiled, so we |
| use language_string. |
| C is 0. Fortran is 1. Pascal is 2. Ada is 3. C++ is 9. |
| Java is 13. Objective-C is 14. Objective-C++ isn't assigned |
| a number, so for now use 9. */ |
| if (! strcmp (language_string, "GNU C")) |
| i = 0; |
| else if (! strcmp (language_string, "GNU F77") |
| || ! strcmp (language_string, "GNU F95")) |
| i = 1; |
| else if (! strcmp (language_string, "GNU Pascal")) |
| i = 2; |
| else if (! strcmp (language_string, "GNU Ada")) |
| i = 3; |
| else if (! strcmp (language_string, "GNU C++") |
| || ! strcmp (language_string, "GNU Objective-C++")) |
| i = 9; |
| else if (! strcmp (language_string, "GNU Java")) |
| i = 13; |
| else if (! strcmp (language_string, "GNU Objective-C")) |
| i = 14; |
| else |
| gcc_unreachable (); |
| fprintf (file, "%d,", i); |
| |
| /* 8 single bit fields: global linkage (not set for C extern linkage, |
| apparently a PL/I convention?), out-of-line epilogue/prologue, offset |
| from start of procedure stored in tbtab, internal function, function |
| has controlled storage, function has no toc, function uses fp, |
| function logs/aborts fp operations. */ |
| /* Assume that fp operations are used if any fp reg must be saved. */ |
| fprintf (file, "%d,", |
| (optional_tbtab << 5) | ((info->first_fp_reg_save != 64) << 1)); |
| |
| /* 6 bitfields: function is interrupt handler, name present in |
| proc table, function calls alloca, on condition directives |
| (controls stack walks, 3 bits), saves condition reg, saves |
| link reg. */ |
| /* The `function calls alloca' bit seems to be set whenever reg 31 is |
| set up as a frame pointer, even when there is no alloca call. */ |
| fprintf (file, "%d,", |
| ((optional_tbtab << 6) |
| | ((optional_tbtab & frame_pointer_needed) << 5) |
| | (info->cr_save_p << 1) |
| | (info->lr_save_p))); |
| |
| /* 3 bitfields: saves backchain, fixup code, number of fpr saved |
| (6 bits). */ |
| fprintf (file, "%d,", |
| (info->push_p << 7) | (64 - info->first_fp_reg_save)); |
| |
| /* 2 bitfields: spare bits (2 bits), number of gpr saved (6 bits). */ |
| fprintf (file, "%d,", (32 - first_reg_to_save ())); |
| |
| if (optional_tbtab) |
| { |
| /* Compute the parameter info from the function decl argument |
| list. */ |
| tree decl; |
| int next_parm_info_bit = 31; |
| |
| for (decl = DECL_ARGUMENTS (current_function_decl); |
| decl; decl = TREE_CHAIN (decl)) |
| { |
| rtx parameter = DECL_INCOMING_RTL (decl); |
| enum machine_mode mode = GET_MODE (parameter); |
| |
| if (GET_CODE (parameter) == REG) |
| { |
| if (SCALAR_FLOAT_MODE_P (mode)) |
| { |
| int bits; |
| |
| float_parms++; |
| |
| switch (mode) |
| { |
| case SFmode: |
| bits = 0x2; |
| break; |
| |
| case DFmode: |
| case TFmode: |
| bits = 0x3; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* If only one bit will fit, don't or in this entry. */ |
| if (next_parm_info_bit > 0) |
| parm_info |= (bits << (next_parm_info_bit - 1)); |
| next_parm_info_bit -= 2; |
| } |
| else |
| { |
| fixed_parms += ((GET_MODE_SIZE (mode) |
| + (UNITS_PER_WORD - 1)) |
| / UNITS_PER_WORD); |
| next_parm_info_bit -= 1; |
| } |
| } |
| } |
| } |
| |
| /* Number of fixed point parameters. */ |
| /* This is actually the number of words of fixed point parameters; thus |
| an 8 byte struct counts as 2; and thus the maximum value is 8. */ |
| fprintf (file, "%d,", fixed_parms); |
| |
| /* 2 bitfields: number of floating point parameters (7 bits), parameters |
| all on stack. */ |
| /* This is actually the number of fp registers that hold parameters; |
| and thus the maximum value is 13. */ |
| /* Set parameters on stack bit if parameters are not in their original |
| registers, regardless of whether they are on the stack? Xlc |
| seems to set the bit when not optimizing. */ |
| fprintf (file, "%d\n", ((float_parms << 1) | (! optimize))); |
| |
| if (! optional_tbtab) |
| return; |
| |
| /* Optional fields follow. Some are variable length. */ |
| |
| /* Parameter types, left adjusted bit fields: 0 fixed, 10 single float, |
| 11 double float. */ |
| /* There is an entry for each parameter in a register, in the order that |
| they occur in the parameter list. Any intervening arguments on the |
| stack are ignored. If the list overflows a long (max possible length |
| 34 bits) then completely leave off all elements that don't fit. */ |
| /* Only emit this long if there was at least one parameter. */ |
| if (fixed_parms || float_parms) |
| fprintf (file, "\t.long %d\n", parm_info); |
| |
| /* Offset from start of code to tb table. */ |
| fputs ("\t.long ", file); |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT"); |
| if (TARGET_AIX) |
| RS6000_OUTPUT_BASENAME (file, fname); |
| else |
| assemble_name (file, fname); |
| putc ('-', file); |
| rs6000_output_function_entry (file, fname); |
| putc ('\n', file); |
| |
| /* Interrupt handler mask. */ |
| /* Omit this long, since we never set the interrupt handler bit |
| above. */ |
| |
| /* Number of CTL (controlled storage) anchors. */ |
| /* Omit this long, since the has_ctl bit is never set above. */ |
| |
| /* Displacement into stack of each CTL anchor. */ |
| /* Omit this list of longs, because there are no CTL anchors. */ |
| |
| /* Length of function name. */ |
| if (*fname == '*') |
| ++fname; |
| fprintf (file, "\t.short %d\n", (int) strlen (fname)); |
| |
| /* Function name. */ |
| assemble_string (fname, strlen (fname)); |
| |
| /* Register for alloca automatic storage; this is always reg 31. |
| Only emit this if the alloca bit was set above. */ |
| if (frame_pointer_needed) |
| fputs ("\t.byte 31\n", file); |
| |
| fputs ("\t.align 2\n", file); |
| } |
| } |
| |
| /* A C compound statement that outputs the assembler code for a thunk |
| function, used to implement C++ virtual function calls with |
| multiple inheritance. The thunk acts as a wrapper around a virtual |
| function, adjusting the implicit object parameter before handing |
| control off to the real function. |
| |
| First, emit code to add the integer DELTA to the location that |
| contains the incoming first argument. Assume that this argument |
| contains a pointer, and is the one used to pass the `this' pointer |
| in C++. This is the incoming argument *before* the function |
| prologue, e.g. `%o0' on a sparc. The addition must preserve the |
| values of all other incoming arguments. |
| |
| After the addition, emit code to jump to FUNCTION, which is a |
| `FUNCTION_DECL'. This is a direct pure jump, not a call, and does |
| not touch the return address. Hence returning from FUNCTION will |
| return to whoever called the current `thunk'. |
| |
| The effect must be as if FUNCTION had been called directly with the |
| adjusted first argument. This macro is responsible for emitting |
| all of the code for a thunk function; output_function_prologue() |
| and output_function_epilogue() are not invoked. |
| |
| The THUNK_FNDECL is redundant. (DELTA and FUNCTION have already |
| been extracted from it.) It might possibly be useful on some |
| targets, but probably not. |
| |
| If you do not define this macro, the target-independent code in the |
| C++ frontend will generate a less efficient heavyweight thunk that |
| calls FUNCTION instead of jumping to it. The generic approach does |
| not support varargs. */ |
| |
| static void |
| rs6000_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, |
| HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, |
| tree function) |
| { |
| rtx this, insn, funexp; |
| /* APPLE LOCAL begin 4299630 */ |
| bool is_longcall_p; |
| rtx symbol_ref; |
| /* APPLE LOCAL end 4299630 */ |
| |
| reload_completed = 1; |
| epilogue_completed = 1; |
| no_new_pseudos = 1; |
| reset_block_changes (); |
| |
| /* Mark the end of the (empty) prologue. */ |
| emit_note (NOTE_INSN_PROLOGUE_END); |
| |
| /* Find the "this" pointer. If the function returns a structure, |
| the structure return pointer is in r3. */ |
| if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) |
| this = gen_rtx_REG (Pmode, 4); |
| else |
| this = gen_rtx_REG (Pmode, 3); |
| |
| /* Apply the constant offset, if required. */ |
| if (delta) |
| { |
| rtx delta_rtx = GEN_INT (delta); |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (this, this, delta_rtx) |
| : gen_adddi3 (this, this, delta_rtx)); |
| } |
| |
| /* Apply the offset from the vtable, if required. */ |
| if (vcall_offset) |
| { |
| rtx vcall_offset_rtx = GEN_INT (vcall_offset); |
| rtx tmp = gen_rtx_REG (Pmode, 12); |
| |
| emit_move_insn (tmp, gen_rtx_MEM (Pmode, this)); |
| if (((unsigned HOST_WIDE_INT) vcall_offset) + 0x8000 >= 0x10000) |
| { |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (tmp, tmp, vcall_offset_rtx) |
| : gen_adddi3 (tmp, tmp, vcall_offset_rtx)); |
| emit_move_insn (tmp, gen_rtx_MEM (Pmode, tmp)); |
| } |
| else |
| { |
| rtx loc = gen_rtx_PLUS (Pmode, tmp, vcall_offset_rtx); |
| |
| emit_move_insn (tmp, gen_rtx_MEM (Pmode, loc)); |
| } |
| emit_insn (TARGET_32BIT |
| ? gen_addsi3 (this, this, tmp) |
| : gen_adddi3 (this, this, tmp)); |
| } |
| |
| /* Generate a tail call to the target function. */ |
| if (!TREE_USED (function)) |
| { |
| assemble_external (function); |
| TREE_USED (function) = 1; |
| } |
| funexp = XEXP (DECL_RTL (function), 0); |
| /* APPLE LOCAL 4299630 */ |
| symbol_ref = funexp; |
| funexp = gen_rtx_MEM (FUNCTION_MODE, funexp); |
| |
| #if TARGET_MACHO |
| if (MACHOPIC_INDIRECT) |
| funexp = machopic_indirect_call_target (funexp); |
| #endif |
| |
| /* APPLE LOCAL begin 4299630 */ |
| if (DEFAULT_ABI == ABI_DARWIN |
| || (*targetm.binds_local_p) (function)) |
| { |
| tree attr_list = TYPE_ATTRIBUTES (TREE_TYPE (function)); |
| if (lookup_attribute ("shortcall", attr_list)) |
| is_longcall_p = FALSE; |
| else if (lookup_attribute ("longcall", attr_list)) |
| is_longcall_p = TRUE; |
| else |
| is_longcall_p = (rs6000_default_long_calls); |
| } |
| if (!is_longcall_p) |
| { |
| /* gen_sibcall expects reload to convert scratch pseudo to LR so we must |
| generate sibcall RTL explicitly. */ |
| insn = emit_call_insn ( |
| gen_rtx_PARALLEL (VOIDmode, |
| gen_rtvec (4, |
| gen_rtx_CALL (VOIDmode, |
| funexp, const0_rtx), |
| gen_rtx_USE (VOIDmode, const0_rtx), |
| gen_rtx_USE (VOIDmode, |
| gen_rtx_REG (SImode, |
| LINK_REGISTER_REGNUM)), |
| gen_rtx_RETURN (VOIDmode)))); |
| SIBLING_CALL_P (insn) = 1; |
| } |
| else |
| { |
| /* APPLE LOCAL begin 4380289 */ |
| tree label_decl; |
| int line_number = 0; |
| /* APPLE LOCAL end 4380289 */ |
| /* APPLE LOCAL begin 3910248, 3915171 */ |
| for (insn = get_last_insn (); |
| insn && (GET_CODE (insn) != NOTE |
| || NOTE_LINE_NUMBER (insn) < 0); |
| insn = PREV_INSN (insn)) |
| ; |
| /* APPLE LOCAL end 3910248, 3915171 */ |
| if (insn) |
| line_number = NOTE_LINE_NUMBER (insn); |
| /* LLVM LOCAL begin compile on non-darwin targets */ |
| #if TARGET_MACHO |
| /* APPLE LOCAL begin 4380289 */ |
| /* This JMP is in a coalesced section, and Mach-O forbids us to |
| directly reference anything else in a coalesced section; if |
| our target gets coalesced away, the linker (static or |
| dynamic) won't know where to send our JMP. Ergo, force a |
| stub. */ |
| label_decl = add_compiler_branch_island (function, line_number); |
| /* Emit "jmp <function>, L42", and define L42 as a branch island. */ |
| insn = emit_jump_insn (gen_longjump (label_rtx (label_decl), |
| XEXP (DECL_RTL (function), 0))); |
| /* APPLE LOCAL end 4380289 */ |
| #endif |
| /* LLVM LOCAL end */ |
| } |
| /* APPLE LOCAL end 4299630 */ |
| emit_barrier (); |
| |
| /* Run just enough of rest_of_compilation to get the insns emitted. |
| There's not really enough bulk here to make other passes such as |
| instruction scheduling worth while. Note that use_thunk calls |
| assemble_start_function and assemble_end_function. */ |
| insn = get_insns (); |
| insn_locators_initialize (); |
| shorten_branches (insn); |
| final_start_function (insn, file, 1); |
| final (insn, file, 1); |
| final_end_function (); |
| |
| reload_completed = 0; |
| epilogue_completed = 0; |
| no_new_pseudos = 0; |
| } |
| |
| /* A quick summary of the various types of 'constant-pool tables' |
| under PowerPC: |
| |
| Target Flags Name One table per |
| AIX (none) AIX TOC object file |
| AIX -mfull-toc AIX TOC object file |
| AIX -mminimal-toc AIX minimal TOC translation unit |
| SVR4/EABI (none) SVR4 SDATA object file |
| SVR4/EABI -fpic SVR4 pic object file |
| SVR4/EABI -fPIC SVR4 PIC translation unit |
| SVR4/EABI -mrelocatable EABI TOC function |
| SVR4/EABI -maix AIX TOC object file |
| SVR4/EABI -maix -mminimal-toc |
| AIX minimal TOC translation unit |
| |
| Name Reg. Set by entries contains: |
| made by addrs? fp? sum? |
| |
| AIX TOC 2 crt0 as Y option option |
| AIX minimal TOC 30 prolog gcc Y Y option |
| SVR4 SDATA 13 crt0 gcc N Y N |
| SVR4 pic 30 prolog ld Y not yet N |
| SVR4 PIC 30 prolog gcc Y option option |
| EABI TOC 30 prolog gcc Y option option |
| |
| */ |
| |
| /* Hash functions for the hash table. */ |
| |
| static unsigned |
| rs6000_hash_constant (rtx k) |
| { |
| enum rtx_code code = GET_CODE (k); |
| enum machine_mode mode = GET_MODE (k); |
| unsigned result = (code << 3) ^ mode; |
| const char *format; |
| int flen, fidx; |
| |
| format = GET_RTX_FORMAT (code); |
| flen = strlen (format); |
| fidx = 0; |
| |
| switch (code) |
| { |
| case LABEL_REF: |
| return result * 1231 + (unsigned) INSN_UID (XEXP (k, 0)); |
| |
| case CONST_DOUBLE: |
| if (mode != VOIDmode) |
| return real_hash (CONST_DOUBLE_REAL_VALUE (k)) * result; |
| flen = 2; |
| break; |
| |
| case CODE_LABEL: |
| fidx = 3; |
| break; |
| |
| default: |
| break; |
| } |
| |
| for (; fidx < flen; fidx++) |
| switch (format[fidx]) |
| { |
| case 's': |
| { |
| unsigned i, len; |
| const char *str = XSTR (k, fidx); |
| len = strlen (str); |
| result = result * 613 + len; |
| for (i = 0; i < len; i++) |
| result = result * 613 + (unsigned) str[i]; |
| break; |
| } |
| case 'u': |
| case 'e': |
| result = result * 1231 + rs6000_hash_constant (XEXP (k, fidx)); |
| break; |
| case 'i': |
| case 'n': |
| result = result * 613 + (unsigned) XINT (k, fidx); |
| break; |
| case 'w': |
| if (sizeof (unsigned) >= sizeof (HOST_WIDE_INT)) |
| result = result * 613 + (unsigned) XWINT (k, fidx); |
| else |
| { |
| size_t i; |
| for (i = 0; i < sizeof (HOST_WIDE_INT) / sizeof (unsigned); i++) |
| result = result * 613 + (unsigned) (XWINT (k, fidx) |
| >> CHAR_BIT * i); |
| } |
| break; |
| case '0': |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| return result; |
| } |
| |
| static unsigned |
| toc_hash_function (const void *hash_entry) |
| { |
| const struct toc_hash_struct *thc = |
| (const struct toc_hash_struct *) hash_entry; |
| return rs6000_hash_constant (thc->key) ^ thc->key_mode; |
| } |
| |
| /* Compare H1 and H2 for equivalence. */ |
| |
| static int |
| toc_hash_eq (const void *h1, const void *h2) |
| { |
| rtx r1 = ((const struct toc_hash_struct *) h1)->key; |
| rtx r2 = ((const struct toc_hash_struct *) h2)->key; |
| |
| if (((const struct toc_hash_struct *) h1)->key_mode |
| != ((const struct toc_hash_struct *) h2)->key_mode) |
| return 0; |
| |
| return rtx_equal_p (r1, r2); |
| } |
| |
| /* These are the names given by the C++ front-end to vtables, and |
| vtable-like objects. Ideally, this logic should not be here; |
| instead, there should be some programmatic way of inquiring as |
| to whether or not an object is a vtable. */ |
| |
| #define VTABLE_NAME_P(NAME) \ |
| (strncmp ("_vt.", name, strlen ("_vt.")) == 0 \ |
| || strncmp ("_ZTV", name, strlen ("_ZTV")) == 0 \ |
| || strncmp ("_ZTT", name, strlen ("_ZTT")) == 0 \ |
| || strncmp ("_ZTI", name, strlen ("_ZTI")) == 0 \ |
| || strncmp ("_ZTC", name, strlen ("_ZTC")) == 0) |
| |
| void |
| rs6000_output_symbol_ref (FILE *file, rtx x) |
| { |
| /* Currently C++ toc references to vtables can be emitted before it |
| is decided whether the vtable is public or private. If this is |
| the case, then the linker will eventually complain that there is |
| a reference to an unknown section. Thus, for vtables only, |
| we emit the TOC reference to reference the symbol and not the |
| section. */ |
| const char *name = XSTR (x, 0); |
| |
| if (VTABLE_NAME_P (name)) |
| { |
| RS6000_OUTPUT_BASENAME (file, name); |
| } |
| else |
| assemble_name (file, name); |
| } |
| |
| /* Output a TOC entry. We derive the entry name from what is being |
| written. */ |
| |
| void |
| output_toc (FILE *file, rtx x, int labelno, enum machine_mode mode) |
| { |
| char buf[256]; |
| const char *name = buf; |
| const char *real_name; |
| rtx base = x; |
| HOST_WIDE_INT offset = 0; |
| |
| gcc_assert (!TARGET_NO_TOC); |
| |
| /* When the linker won't eliminate them, don't output duplicate |
| TOC entries (this happens on AIX if there is any kind of TOC, |
| and on SVR4 under -fPIC or -mrelocatable). Don't do this for |
| CODE_LABELs. */ |
| if (TARGET_TOC && GET_CODE (x) != LABEL_REF) |
| { |
| struct toc_hash_struct *h; |
| void * * found; |
| |
| /* Create toc_hash_table. This can't be done at OVERRIDE_OPTIONS |
| time because GGC is not initialized at that point. */ |
| if (toc_hash_table == NULL) |
| toc_hash_table = htab_create_ggc (1021, toc_hash_function, |
| toc_hash_eq, NULL); |
| |
| h = ggc_alloc (sizeof (*h)); |
| h->key = x; |
| h->key_mode = mode; |
| h->labelno = labelno; |
| |
| found = htab_find_slot (toc_hash_table, h, 1); |
| if (*found == NULL) |
| *found = h; |
| else /* This is indeed a duplicate. |
| Set this label equal to that label. */ |
| { |
| fputs ("\t.set ", file); |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC"); |
| fprintf (file, "%d,", labelno); |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC"); |
| fprintf (file, "%d\n", ((*(const struct toc_hash_struct **) |
| found)->labelno)); |
| return; |
| } |
| } |
| |
| /* If we're going to put a double constant in the TOC, make sure it's |
| aligned properly when strict alignment is on. */ |
| if (GET_CODE (x) == CONST_DOUBLE |
| && STRICT_ALIGNMENT |
| && GET_MODE_BITSIZE (mode) >= 64 |
| && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC)) { |
| ASM_OUTPUT_ALIGN (file, 3); |
| } |
| |
| (*targetm.asm_out.internal_label) (file, "LC", labelno); |
| |
| /* Handle FP constants specially. Note that if we have a minimal |
| TOC, things we put here aren't actually in the TOC, so we can allow |
| FP constants. */ |
| if (GET_CODE (x) == CONST_DOUBLE && |
| (GET_MODE (x) == TFmode || GET_MODE (x) == TDmode)) |
| { |
| REAL_VALUE_TYPE rv; |
| long k[4]; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
| if (DECIMAL_FLOAT_MODE_P (GET_MODE (x))) |
| REAL_VALUE_TO_TARGET_DECIMAL128 (rv, k); |
| else |
| REAL_VALUE_TO_TARGET_LONG_DOUBLE (rv, k); |
| |
| if (TARGET_64BIT) |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs (DOUBLE_INT_ASM_OP, file); |
| else |
| fprintf (file, "\t.tc FT_%lx_%lx_%lx_%lx[TC],", |
| k[0] & 0xffffffff, k[1] & 0xffffffff, |
| k[2] & 0xffffffff, k[3] & 0xffffffff); |
| fprintf (file, "0x%lx%08lx,0x%lx%08lx\n", |
| k[0] & 0xffffffff, k[1] & 0xffffffff, |
| k[2] & 0xffffffff, k[3] & 0xffffffff); |
| return; |
| } |
| else |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs ("\t.long ", file); |
| else |
| fprintf (file, "\t.tc FT_%lx_%lx_%lx_%lx[TC],", |
| k[0] & 0xffffffff, k[1] & 0xffffffff, |
| k[2] & 0xffffffff, k[3] & 0xffffffff); |
| fprintf (file, "0x%lx,0x%lx,0x%lx,0x%lx\n", |
| k[0] & 0xffffffff, k[1] & 0xffffffff, |
| k[2] & 0xffffffff, k[3] & 0xffffffff); |
| return; |
| } |
| } |
| else if (GET_CODE (x) == CONST_DOUBLE && |
| (GET_MODE (x) == DFmode || GET_MODE (x) == DDmode)) |
| { |
| REAL_VALUE_TYPE rv; |
| long k[2]; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
| |
| if (DECIMAL_FLOAT_MODE_P (GET_MODE (x))) |
| REAL_VALUE_TO_TARGET_DECIMAL64 (rv, k); |
| else |
| REAL_VALUE_TO_TARGET_DOUBLE (rv, k); |
| |
| if (TARGET_64BIT) |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs (DOUBLE_INT_ASM_OP, file); |
| else |
| fprintf (file, "\t.tc FD_%lx_%lx[TC],", |
| k[0] & 0xffffffff, k[1] & 0xffffffff); |
| fprintf (file, "0x%lx%08lx\n", |
| k[0] & 0xffffffff, k[1] & 0xffffffff); |
| return; |
| } |
| else |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs ("\t.long ", file); |
| else |
| fprintf (file, "\t.tc FD_%lx_%lx[TC],", |
| k[0] & 0xffffffff, k[1] & 0xffffffff); |
| fprintf (file, "0x%lx,0x%lx\n", |
| k[0] & 0xffffffff, k[1] & 0xffffffff); |
| return; |
| } |
| } |
| else if (GET_CODE (x) == CONST_DOUBLE && |
| (GET_MODE (x) == SFmode || GET_MODE (x) == SDmode)) |
| { |
| REAL_VALUE_TYPE rv; |
| long l; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, x); |
| if (DECIMAL_FLOAT_MODE_P (GET_MODE (x))) |
| REAL_VALUE_TO_TARGET_DECIMAL32 (rv, l); |
| else |
| REAL_VALUE_TO_TARGET_SINGLE (rv, l); |
| |
| if (TARGET_64BIT) |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs (DOUBLE_INT_ASM_OP, file); |
| else |
| fprintf (file, "\t.tc FS_%lx[TC],", l & 0xffffffff); |
| fprintf (file, "0x%lx00000000\n", l & 0xffffffff); |
| return; |
| } |
| else |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs ("\t.long ", file); |
| else |
| fprintf (file, "\t.tc FS_%lx[TC],", l & 0xffffffff); |
| fprintf (file, "0x%lx\n", l & 0xffffffff); |
| return; |
| } |
| } |
| else if (GET_MODE (x) == VOIDmode |
| && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)) |
| { |
| unsigned HOST_WIDE_INT low; |
| HOST_WIDE_INT high; |
| |
| if (GET_CODE (x) == CONST_DOUBLE) |
| { |
| low = CONST_DOUBLE_LOW (x); |
| high = CONST_DOUBLE_HIGH (x); |
| } |
| else |
| #if HOST_BITS_PER_WIDE_INT == 32 |
| { |
| low = INTVAL (x); |
| high = (low & 0x80000000) ? ~0 : 0; |
| } |
| #else |
| { |
| low = INTVAL (x) & 0xffffffff; |
| high = (HOST_WIDE_INT) INTVAL (x) >> 32; |
| } |
| #endif |
| |
| /* TOC entries are always Pmode-sized, but since this |
| is a bigendian machine then if we're putting smaller |
| integer constants in the TOC we have to pad them. |
| (This is still a win over putting the constants in |
| a separate constant pool, because then we'd have |
| to have both a TOC entry _and_ the actual constant.) |
| |
| For a 32-bit target, CONST_INT values are loaded and shifted |
| entirely within `low' and can be stored in one TOC entry. */ |
| |
| /* It would be easy to make this work, but it doesn't now. */ |
| gcc_assert (!TARGET_64BIT || POINTER_SIZE >= GET_MODE_BITSIZE (mode)); |
| |
| if (POINTER_SIZE > GET_MODE_BITSIZE (mode)) |
| { |
| #if HOST_BITS_PER_WIDE_INT == 32 |
| lshift_double (low, high, POINTER_SIZE - GET_MODE_BITSIZE (mode), |
| POINTER_SIZE, &low, &high, 0); |
| #else |
| low |= high << 32; |
| low <<= POINTER_SIZE - GET_MODE_BITSIZE (mode); |
| high = (HOST_WIDE_INT) low >> 32; |
| low &= 0xffffffff; |
| #endif |
| } |
| |
| if (TARGET_64BIT) |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs (DOUBLE_INT_ASM_OP, file); |
| else |
| fprintf (file, "\t.tc ID_%lx_%lx[TC],", |
| (long) high & 0xffffffff, (long) low & 0xffffffff); |
| fprintf (file, "0x%lx%08lx\n", |
| (long) high & 0xffffffff, (long) low & 0xffffffff); |
| return; |
| } |
| else |
| { |
| if (POINTER_SIZE < GET_MODE_BITSIZE (mode)) |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs ("\t.long ", file); |
| else |
| fprintf (file, "\t.tc ID_%lx_%lx[TC],", |
| (long) high & 0xffffffff, (long) low & 0xffffffff); |
| fprintf (file, "0x%lx,0x%lx\n", |
| (long) high & 0xffffffff, (long) low & 0xffffffff); |
| } |
| else |
| { |
| if (TARGET_MINIMAL_TOC) |
| fputs ("\t.long ", file); |
| else |
| fprintf (file, "\t.tc IS_%lx[TC],", (long) low & 0xffffffff); |
| fprintf (file, "0x%lx\n", (long) low & 0xffffffff); |
| } |
| return; |
| } |
| } |
| |
| if (GET_CODE (x) == CONST) |
| { |
| gcc_assert (GET_CODE (XEXP (x, 0)) == PLUS); |
| |
| base = XEXP (XEXP (x, 0), 0); |
| offset = INTVAL (XEXP (XEXP (x, 0), 1)); |
| } |
| |
| switch (GET_CODE (base)) |
| { |
| case SYMBOL_REF: |
| name = XSTR (base, 0); |
| break; |
| |
| case LABEL_REF: |
| ASM_GENERATE_INTERNAL_LABEL (buf, "L", |
| CODE_LABEL_NUMBER (XEXP (base, 0))); |
| break; |
| |
| case CODE_LABEL: |
| ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (base)); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| real_name = (*targetm.strip_name_encoding) (name); |
| if (TARGET_MINIMAL_TOC) |
| fputs (TARGET_32BIT ? "\t.long " : DOUBLE_INT_ASM_OP, file); |
| else |
| { |
| fprintf (file, "\t.tc %s", real_name); |
| |
| if (offset < 0) |
| fprintf (file, ".N" HOST_WIDE_INT_PRINT_UNSIGNED, - offset); |
| else if (offset) |
| fprintf (file, ".P" HOST_WIDE_INT_PRINT_UNSIGNED, offset); |
| |
| fputs ("[TC],", file); |
| } |
| |
| /* Currently C++ toc references to vtables can be emitted before it |
| is decided whether the vtable is public or private. If this is |
| the case, then the linker will eventually complain that there is |
| a TOC reference to an unknown section. Thus, for vtables only, |
| we emit the TOC reference to reference the symbol and not the |
| section. */ |
| if (VTABLE_NAME_P (name)) |
| { |
| RS6000_OUTPUT_BASENAME (file, name); |
| if (offset < 0) |
| fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset); |
| else if (offset > 0) |
| fprintf (file, "+" HOST_WIDE_INT_PRINT_DEC, offset); |
| } |
| else |
| output_addr_const (file, x); |
| putc ('\n', file); |
| } |
| |
| /* Output an assembler pseudo-op to write an ASCII string of N characters |
| starting at P to FILE. |
| |
| On the RS/6000, we have to do this using the .byte operation and |
| write out special characters outside the quoted string. |
| Also, the assembler is broken; very long strings are truncated, |
| so we must artificially break them up early. */ |
| |
| void |
| output_ascii (FILE *file, const char *p, int n) |
| { |
| char c; |
| int i, count_string; |
| const char *for_string = "\t.byte \""; |
| const char *for_decimal = "\t.byte "; |
| const char *to_close = NULL; |
| |
| count_string = 0; |
| for (i = 0; i < n; i++) |
| { |
| c = *p++; |
| if (c >= ' ' && c < 0177) |
| { |
| if (for_string) |
| fputs (for_string, file); |
| putc (c, file); |
| |
| /* Write two quotes to get one. */ |
| if (c == '"') |
| { |
| putc (c, file); |
| ++count_string; |
| } |
| |
| for_string = NULL; |
| for_decimal = "\"\n\t.byte "; |
| to_close = "\"\n"; |
| ++count_string; |
| |
| if (count_string >= 512) |
| { |
| fputs (to_close, file); |
| |
| for_string = "\t.byte \""; |
| for_decimal = "\t.byte "; |
| to_close = NULL; |
| count_string = 0; |
| } |
| } |
| else |
| { |
| if (for_decimal) |
| fputs (for_decimal, file); |
| fprintf (file, "%d", c); |
| |
| for_string = "\n\t.byte \""; |
| for_decimal = ", "; |
| to_close = "\n"; |
| count_string = 0; |
| } |
| } |
| |
| /* Now close the string if we have written one. Then end the line. */ |
| if (to_close) |
| fputs (to_close, file); |
| } |
| |
| /* Generate a unique section name for FILENAME for a section type |
| represented by SECTION_DESC. Output goes into BUF. |
| |
| SECTION_DESC can be any string, as long as it is different for each |
| possible section type. |
| |
| We name the section in the same manner as xlc. The name begins with an |
| underscore followed by the filename (after stripping any leading directory |
| names) with the last period replaced by the string SECTION_DESC. If |
| FILENAME does not contain a period, SECTION_DESC is appended to the end of |
| the name. */ |
| |
| void |
| rs6000_gen_section_name (char **buf, const char *filename, |
| const char *section_desc) |
| { |
| const char *q, *after_last_slash, *last_period = 0; |
| char *p; |
| int len; |
| |
| after_last_slash = filename; |
| for (q = filename; *q; q++) |
| { |
| if (*q == '/') |
| after_last_slash = q + 1; |
| else if (*q == '.') |
| last_period = q; |
| } |
| |
| len = strlen (after_last_slash) + strlen (section_desc) + 2; |
| *buf = (char *) xmalloc (len); |
| |
| p = *buf; |
| *p++ = '_'; |
| |
| for (q = after_last_slash; *q; q++) |
| { |
| if (q == last_period) |
| { |
| strcpy (p, section_desc); |
| p += strlen (section_desc); |
| break; |
| } |
| |
| else if (ISALNUM (*q)) |
| *p++ = *q; |
| } |
| |
| if (last_period == 0) |
| strcpy (p, section_desc); |
| else |
| *p = '\0'; |
| } |
| |
| /* Emit profile function. */ |
| |
| void |
| output_profile_hook (int labelno ATTRIBUTE_UNUSED) |
| { |
| /* Non-standard profiling for kernels, which just saves LR then calls |
| _mcount without worrying about arg saves. The idea is to change |
| the function prologue as little as possible as it isn't easy to |
| account for arg save/restore code added just for _mcount. */ |
| if (TARGET_PROFILE_KERNEL) |
| return; |
| |
| if (DEFAULT_ABI == ABI_AIX) |
| { |
| #ifndef NO_PROFILE_COUNTERS |
| # define NO_PROFILE_COUNTERS 0 |
| #endif |
| if (NO_PROFILE_COUNTERS) |
| emit_library_call (init_one_libfunc (RS6000_MCOUNT), 0, VOIDmode, 0); |
| else |
| { |
| char buf[30]; |
| const char *label_name; |
| rtx fun; |
| |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno); |
| label_name = (*targetm.strip_name_encoding) (ggc_strdup (buf)); |
| fun = gen_rtx_SYMBOL_REF (Pmode, label_name); |
| |
| emit_library_call (init_one_libfunc (RS6000_MCOUNT), 0, VOIDmode, 1, |
| fun, Pmode); |
| } |
| } |
| else if (DEFAULT_ABI == ABI_DARWIN) |
| { |
| const char *mcount_name = RS6000_MCOUNT; |
| int caller_addr_regno = LINK_REGISTER_REGNUM; |
| |
| /* Be conservative and always set this, at least for now. */ |
| current_function_uses_pic_offset_table = 1; |
| |
| #if TARGET_MACHO |
| /* For PIC code, set up a stub and collect the caller's address |
| from r0, which is where the prologue puts it. */ |
| if (MACHOPIC_INDIRECT |
| && current_function_uses_pic_offset_table) |
| caller_addr_regno = 0; |
| #endif |
| emit_library_call (gen_rtx_SYMBOL_REF (Pmode, mcount_name), |
| 0, VOIDmode, 1, |
| gen_rtx_REG (Pmode, caller_addr_regno), Pmode); |
| } |
| } |
| |
| /* Write function profiler code. */ |
| |
| void |
| output_function_profiler (FILE *file, int labelno) |
| { |
| char buf[100]; |
| |
| switch (DEFAULT_ABI) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case ABI_V4: |
| if (!TARGET_32BIT) |
| { |
| warning (0, "no profiling of 64-bit code for this ABI"); |
| return; |
| } |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno); |
| fprintf (file, "\tmflr %s\n", reg_names[0]); |
| if (NO_PROFILE_COUNTERS) |
| { |
| asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", |
| reg_names[0], reg_names[1]); |
| } |
| else if (TARGET_SECURE_PLT && flag_pic) |
| { |
| asm_fprintf (file, "\tbcl 20,31,1f\n1:\n\t{st|stw} %s,4(%s)\n", |
| reg_names[0], reg_names[1]); |
| asm_fprintf (file, "\tmflr %s\n", reg_names[12]); |
| asm_fprintf (file, "\t{cau|addis} %s,%s,", |
| reg_names[12], reg_names[12]); |
| assemble_name (file, buf); |
| asm_fprintf (file, "-1b@ha\n\t{cal|la} %s,", reg_names[0]); |
| assemble_name (file, buf); |
| asm_fprintf (file, "-1b@l(%s)\n", reg_names[12]); |
| } |
| else if (flag_pic == 1) |
| { |
| fputs ("\tbl _GLOBAL_OFFSET_TABLE_@local-4\n", file); |
| asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", |
| reg_names[0], reg_names[1]); |
| asm_fprintf (file, "\tmflr %s\n", reg_names[12]); |
| asm_fprintf (file, "\t{l|lwz} %s,", reg_names[0]); |
| assemble_name (file, buf); |
| asm_fprintf (file, "@got(%s)\n", reg_names[12]); |
| } |
| else if (flag_pic > 1) |
| { |
| asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", |
| reg_names[0], reg_names[1]); |
| /* Now, we need to get the address of the label. */ |
| fputs ("\tbcl 20,31,1f\n\t.long ", file); |
| assemble_name (file, buf); |
| fputs ("-.\n1:", file); |
| asm_fprintf (file, "\tmflr %s\n", reg_names[11]); |
| asm_fprintf (file, "\t{l|lwz} %s,0(%s)\n", |
| reg_names[0], reg_names[11]); |
| asm_fprintf (file, "\t{cax|add} %s,%s,%s\n", |
| reg_names[0], reg_names[0], reg_names[11]); |
| } |
| else |
| { |
| asm_fprintf (file, "\t{liu|lis} %s,", reg_names[12]); |
| assemble_name (file, buf); |
| fputs ("@ha\n", file); |
| asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", |
| reg_names[0], reg_names[1]); |
| asm_fprintf (file, "\t{cal|la} %s,", reg_names[0]); |
| assemble_name (file, buf); |
| asm_fprintf (file, "@l(%s)\n", reg_names[12]); |
| } |
| |
| /* ABI_V4 saves the static chain reg with ASM_OUTPUT_REG_PUSH. */ |
| fprintf (file, "\tbl %s%s\n", |
| RS6000_MCOUNT, flag_pic ? "@plt" : ""); |
| break; |
| |
| case ABI_AIX: |
| case ABI_DARWIN: |
| if (!TARGET_PROFILE_KERNEL) |
| { |
| /* Don't do anything, done in output_profile_hook (). */ |
| } |
| else |
| { |
| gcc_assert (!TARGET_32BIT); |
| |
| asm_fprintf (file, "\tmflr %s\n", reg_names[0]); |
| asm_fprintf (file, "\tstd %s,16(%s)\n", reg_names[0], reg_names[1]); |
| |
| if (cfun->static_chain_decl != NULL) |
| { |
| asm_fprintf (file, "\tstd %s,24(%s)\n", |
| reg_names[STATIC_CHAIN_REGNUM], reg_names[1]); |
| fprintf (file, "\tbl %s\n", RS6000_MCOUNT); |
| asm_fprintf (file, "\tld %s,24(%s)\n", |
| reg_names[STATIC_CHAIN_REGNUM], reg_names[1]); |
| } |
| else |
| fprintf (file, "\tbl %s\n", RS6000_MCOUNT); |
| } |
| break; |
| } |
| } |
| |
| /* LLVM LOCAL - Disable scheduler. */ |
| #ifndef ENABLE_LLVM |
| |
| /* Power4 load update and store update instructions are cracked into a |
| load or store and an integer insn which are executed in the same cycle. |
| Branches have their own dispatch slot which does not count against the |
| GCC issue rate, but it changes the program flow so there are no other |
| instructions to issue in this cycle. */ |
| |
| static int |
| rs6000_variable_issue (FILE *stream ATTRIBUTE_UNUSED, |
| int verbose ATTRIBUTE_UNUSED, |
| rtx insn, int more) |
| { |
| if (GET_CODE (PATTERN (insn)) == USE |
| || GET_CODE (PATTERN (insn)) == CLOBBER) |
| return more; |
| |
| if (rs6000_sched_groups) |
| { |
| if (is_microcoded_insn (insn)) |
| return 0; |
| else if (is_cracked_insn (insn)) |
| return more > 2 ? more - 2 : 0; |
| } |
| |
| return more - 1; |
| } |
| |
| /* Adjust the cost of a scheduling dependency. Return the new cost of |
| a dependency LINK or INSN on DEP_INSN. COST is the current cost. */ |
| |
| static int |
| rs6000_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost) |
| { |
| if (! recog_memoized (insn)) |
| return 0; |
| |
| if (REG_NOTE_KIND (link) != 0) |
| return 0; |
| |
| if (REG_NOTE_KIND (link) == 0) |
| { |
| /* Data dependency; DEP_INSN writes a register that INSN reads |
| some cycles later. */ |
| |
| /* Separate a load from a narrower, dependent store. */ |
| if (rs6000_sched_groups |
| && GET_CODE (PATTERN (insn)) == SET |
| && GET_CODE (PATTERN (dep_insn)) == SET |
| && GET_CODE (XEXP (PATTERN (insn), 1)) == MEM |
| && GET_CODE (XEXP (PATTERN (dep_insn), 0)) == MEM |
| && (GET_MODE_SIZE (GET_MODE (XEXP (PATTERN (insn), 1))) |
| > GET_MODE_SIZE (GET_MODE (XEXP (PATTERN (dep_insn), 0))))) |
| return cost + 14; |
| |
| switch (get_attr_type (insn)) |
| { |
| case TYPE_JMPREG: |
| /* Tell the first scheduling pass about the latency between |
| a mtctr and bctr (and mtlr and br/blr). The first |
| scheduling pass will not know about this latency since |
| the mtctr instruction, which has the latency associated |
| to it, will be generated by reload. */ |
| return TARGET_POWER ? 5 : 4; |
| case TYPE_BRANCH: |
| /* Leave some extra cycles between a compare and its |
| dependent branch, to inhibit expensive mispredicts. */ |
| if ((rs6000_cpu_attr == CPU_PPC603 |
| || rs6000_cpu_attr == CPU_PPC604 |
| || rs6000_cpu_attr == CPU_PPC604E |
| || rs6000_cpu_attr == CPU_PPC620 |
| || rs6000_cpu_attr == CPU_PPC630 |
| || rs6000_cpu_attr == CPU_PPC750 |
| || rs6000_cpu_attr == CPU_PPC7400 |
| || rs6000_cpu_attr == CPU_PPC7450 |
| || rs6000_cpu_attr == CPU_POWER4 |
| || rs6000_cpu_attr == CPU_POWER5) |
| && recog_memoized (dep_insn) |
| && (INSN_CODE (dep_insn) >= 0) |
| && (get_attr_type (dep_insn) == TYPE_CMP |
| || get_attr_type (dep_insn) == TYPE_COMPARE |
| || get_attr_type (dep_insn) == TYPE_DELAYED_COMPARE |
| || get_attr_type (dep_insn) == TYPE_IMUL_COMPARE |
| || get_attr_type (dep_insn) == TYPE_LMUL_COMPARE |
| || get_attr_type (dep_insn) == TYPE_FPCOMPARE |
| || get_attr_type (dep_insn) == TYPE_CR_LOGICAL |
| || get_attr_type (dep_insn) == TYPE_DELAYED_CR)) |
| return cost + 2; |
| default: |
| break; |
| } |
| /* Fall out to return default cost. */ |
| } |
| |
| return cost; |
| } |
| |
| /* The function returns a true if INSN is microcoded. |
| Return false otherwise. */ |
| |
| static bool |
| is_microcoded_insn (rtx insn) |
| { |
| if (!insn || !INSN_P (insn) |
| || GET_CODE (PATTERN (insn)) == USE |
| || GET_CODE (PATTERN (insn)) == CLOBBER) |
| return false; |
| |
| if (rs6000_sched_groups) |
| { |
| enum attr_type type = get_attr_type (insn); |
| if (type == TYPE_LOAD_EXT_U |
| || type == TYPE_LOAD_EXT_UX |
| || type == TYPE_LOAD_UX |
| || type == TYPE_STORE_UX |
| || type == TYPE_MFCR) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* The function returns a nonzero value if INSN can be scheduled only |
| as the first insn in a dispatch group ("dispatch-slot restricted"). |
| In this case, the returned value indicates how many dispatch slots |
| the insn occupies (at the beginning of the group). |
| Return 0 otherwise. */ |
| |
| static int |
| is_dispatch_slot_restricted (rtx insn) |
| { |
| enum attr_type type; |
| |
| if (!rs6000_sched_groups) |
| return 0; |
| |
| if (!insn |
| || insn == NULL_RTX |
| || GET_CODE (insn) == NOTE |
| || GET_CODE (PATTERN (insn)) == USE |
| || GET_CODE (PATTERN (insn)) == CLOBBER) |
| return 0; |
| |
| type = get_attr_type (insn); |
| |
| switch (type) |
| { |
| case TYPE_MFCR: |
| case TYPE_MFCRF: |
| case TYPE_MTCR: |
| case TYPE_DELAYED_CR: |
| case TYPE_CR_LOGICAL: |
| case TYPE_MTJMPR: |
| case TYPE_MFJMPR: |
| return 1; |
| case TYPE_IDIV: |
| case TYPE_LDIV: |
| return 2; |
| case TYPE_LOAD_L: |
| case TYPE_STORE_C: |
| case TYPE_ISYNC: |
| case TYPE_SYNC: |
| return 4; |
| default: |
| if (rs6000_cpu == PROCESSOR_POWER5 |
| && is_cracked_insn (insn)) |
| return 2; |
| return 0; |
| } |
| } |
| |
| /* The function returns true if INSN is cracked into 2 instructions |
| by the processor (and therefore occupies 2 issue slots). */ |
| |
| static bool |
| is_cracked_insn (rtx insn) |
| { |
| if (!insn || !INSN_P (insn) |
| || GET_CODE (PATTERN (insn)) == USE |
| || GET_CODE (PATTERN (insn)) == CLOBBER) |
| return false; |
| |
| if (rs6000_sched_groups) |
| { |
| enum attr_type type = get_attr_type (insn); |
| if (type == TYPE_LOAD_U || type == TYPE_STORE_U |
| || type == TYPE_FPLOAD_U || type == TYPE_FPSTORE_U |
| || type == TYPE_FPLOAD_UX || type == TYPE_FPSTORE_UX |
| || type == TYPE_LOAD_EXT || type == TYPE_DELAYED_CR |
| || type == TYPE_COMPARE || type == TYPE_DELAYED_COMPARE |
| || type == TYPE_IMUL_COMPARE || type == TYPE_LMUL_COMPARE |
| || type == TYPE_IDIV || type == TYPE_LDIV |
| || type == TYPE_INSERT_WORD) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* The function returns true if INSN can be issued only from |
| the branch slot. */ |
| |
| static bool |
| is_branch_slot_insn (rtx insn) |
| { |
| if (!insn || !INSN_P (insn) |
| || GET_CODE (PATTERN (insn)) == USE |
| || GET_CODE (PATTERN (insn)) == CLOBBER) |
| return false; |
| |
| if (rs6000_sched_groups) |
| { |
| enum attr_type type = get_attr_type (insn); |
| if (type == TYPE_BRANCH || type == TYPE_JMPREG) |
| return true; |
| return false; |
| } |
| |
| return false; |
| } |
| |
| /* A C statement (sans semicolon) to update the integer scheduling |
| priority INSN_PRIORITY (INSN). Increase the priority to execute the |
| INSN earlier, reduce the priority to execute INSN later. Do not |
| define this macro if you do not need to adjust the scheduling |
| priorities of insns. */ |
| |
| static int |
| rs6000_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority) |
| { |
| /* On machines (like the 750) which have asymmetric integer units, |
| where one integer unit can do multiply and divides and the other |
| can't, reduce the priority of multiply/divide so it is scheduled |
| before other integer operations. */ |
| |
| #if 0 |
| if (! INSN_P (insn)) |
| return priority; |
| |
| if (GET_CODE (PATTERN (insn)) == USE) |
| return priority; |
| |
| switch (rs6000_cpu_attr) { |
| case CPU_PPC750: |
| switch (get_attr_type (insn)) |
| { |
| default: |
| break; |
| |
| case TYPE_IMUL: |
| case TYPE_IDIV: |
| fprintf (stderr, "priority was %#x (%d) before adjustment\n", |
| priority, priority); |
| if (priority >= 0 && priority < 0x01000000) |
| priority >>= 3; |
| break; |
| } |
| } |
| #endif |
| |
| if (is_dispatch_slot_restricted (insn) |
| && reload_completed |
| && current_sched_info->sched_max_insns_priority |
| && rs6000_sched_restricted_insns_priority) |
| { |
| |
| /* Prioritize insns that can be dispatched only in the first |
| dispatch slot. */ |
| if (rs6000_sched_restricted_insns_priority == 1) |
| /* Attach highest priority to insn. This means that in |
| haifa-sched.c:ready_sort(), dispatch-slot restriction considerations |
| precede 'priority' (critical path) considerations. */ |
| return current_sched_info->sched_max_insns_priority; |
| else if (rs6000_sched_restricted_insns_priority == 2) |
| /* Increase priority of insn by a minimal amount. This means that in |
| haifa-sched.c:ready_sort(), only 'priority' (critical path) |
| considerations precede dispatch-slot restriction considerations. */ |
| return (priority + 1); |
| } |
| |
| return priority; |
| } |
| |
| /* Return how many instructions the machine can issue per cycle. */ |
| |
| static int |
| rs6000_issue_rate (void) |
| { |
| /* Use issue rate of 1 for first scheduling pass to decrease degradation. */ |
| if (!reload_completed) |
| return 1; |
| |
| switch (rs6000_cpu_attr) { |
| case CPU_RIOS1: /* ? */ |
| case CPU_RS64A: |
| case CPU_PPC601: /* ? */ |
| case CPU_PPC7450: |
| return 3; |
| case CPU_PPC440: |
| case CPU_PPC603: |
| case CPU_PPC750: |
| case CPU_PPC7400: |
| case CPU_PPC8540: |
| return 2; |
| case CPU_RIOS2: |
| case CPU_PPC604: |
| case CPU_PPC604E: |
| case CPU_PPC620: |
| case CPU_PPC630: |
| return 4; |
| case CPU_POWER4: |
| case CPU_POWER5: |
| return 5; |
| default: |
| return 1; |
| } |
| } |
| |
| /* Return how many instructions to look ahead for better insn |
| scheduling. */ |
| |
| static int |
| rs6000_use_sched_lookahead (void) |
| { |
| if (rs6000_cpu_attr == CPU_PPC8540) |
| return 4; |
| return 0; |
| } |
| |
| /* Determine is PAT refers to memory. */ |
| |
| static bool |
| is_mem_ref (rtx pat) |
| { |
| const char * fmt; |
| int i, j; |
| bool ret = false; |
| |
| if (GET_CODE (pat) == MEM) |
| return true; |
| |
| /* Recursively process the pattern. */ |
| fmt = GET_RTX_FORMAT (GET_CODE (pat)); |
| |
| for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0 && !ret; i--) |
| { |
| if (fmt[i] == 'e') |
| ret |= is_mem_ref (XEXP (pat, i)); |
| else if (fmt[i] == 'E') |
| for (j = XVECLEN (pat, i) - 1; j >= 0; j--) |
| ret |= is_mem_ref (XVECEXP (pat, i, j)); |
| } |
| |
| return ret; |
| } |
| |
| /* Determine if PAT is a PATTERN of a load insn. */ |
| |
| static bool |
| is_load_insn1 (rtx pat) |
| { |
| if (!pat || pat == NULL_RTX) |
| return false; |
| |
| if (GET_CODE (pat) == SET) |
| return is_mem_ref (SET_SRC (pat)); |
| |
| if (GET_CODE (pat) == PARALLEL) |
| { |
| int i; |
| |
| for (i = 0; i < XVECLEN (pat, 0); i++) |
| if (is_load_insn1 (XVECEXP (pat, 0, i))) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Determine if INSN loads from memory. */ |
| |
| static bool |
| is_load_insn (rtx insn) |
| { |
| if (!insn || !INSN_P (insn)) |
| return false; |
| |
| if (GET_CODE (insn) == CALL_INSN) |
| return false; |
| |
| return is_load_insn1 (PATTERN (insn)); |
| } |
| |
| /* Determine if PAT is a PATTERN of a store insn. */ |
| |
| static bool |
| is_store_insn1 (rtx pat) |
| { |
| if (!pat || pat == NULL_RTX) |
| return false; |
| |
| if (GET_CODE (pat) == SET) |
| return is_mem_ref (SET_DEST (pat)); |
| |
| if (GET_CODE (pat) == PARALLEL) |
| { |
| int i; |
| |
| for (i = 0; i < XVECLEN (pat, 0); i++) |
| if (is_store_insn1 (XVECEXP (pat, 0, i))) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Determine if INSN stores to memory. */ |
| |
| static bool |
| is_store_insn (rtx insn) |
| { |
| if (!insn || !INSN_P (insn)) |
| return false; |
| |
| return is_store_insn1 (PATTERN (insn)); |
| } |
| |
| /* Returns whether the dependence between INSN and NEXT is considered |
| costly by the given target. */ |
| |
| static bool |
| rs6000_is_costly_dependence (rtx insn, rtx next, rtx link, int cost, |
| int distance) |
| { |
| /* If the flag is not enabled - no dependence is considered costly; |
| allow all dependent insns in the same group. |
| This is the most aggressive option. */ |
| if (rs6000_sched_costly_dep == no_dep_costly) |
| return false; |
| |
| /* If the flag is set to 1 - a dependence is always considered costly; |
| do not allow dependent instructions in the same group. |
| This is the most conservative option. */ |
| if (rs6000_sched_costly_dep == all_deps_costly) |
| return true; |
| |
| if (rs6000_sched_costly_dep == store_to_load_dep_costly |
| && is_load_insn (next) |
| && is_store_insn (insn)) |
| /* Prevent load after store in the same group. */ |
| return true; |
| |
| if (rs6000_sched_costly_dep == true_store_to_load_dep_costly |
| && is_load_insn (next) |
| && is_store_insn (insn) |
| && (!link || (int) REG_NOTE_KIND (link) == 0)) |
| /* Prevent load after store in the same group if it is a true |
| dependence. */ |
| /* APPLE LOCAL begin nop on true-dependence. */ |
| { |
| if (GET_CODE (PATTERN (next)) == SET && GET_CODE (PATTERN (insn)) == SET) |
| { |
| rtx load_mem = SET_SRC (PATTERN (next)); |
| rtx sto_mem = SET_DEST (PATTERN (insn)); |
| if (GET_CODE (load_mem) == ZERO_EXTEND |
| || GET_CODE (load_mem) == SIGN_EXTEND) |
| load_mem = XEXP (load_mem, 0); |
| if (GET_CODE (sto_mem) == ZERO_EXTEND |
| || GET_CODE (sto_mem) == SIGN_EXTEND) |
| load_mem = XEXP (sto_mem, 0); |
| if (GET_CODE (load_mem) == MEM && GET_CODE (sto_mem) == MEM) |
| /* Only consider those true-depenedence cases that memory conflict |
| can be determined. Exclude cases, where true-dependency was |
| decided because memory conflict could not be determined from |
| aliasing info. */ |
| return must_true_dependence (load_mem, sto_mem); |
| } |
| return true; |
| } |
| /* APPLE LOCAL end nop on true-dependence. */ |
| |
| /* The flag is set to X; dependences with latency >= X are considered costly, |
| and will not be scheduled in the same group. */ |
| if (rs6000_sched_costly_dep <= max_dep_latency |
| && ((cost - distance) >= (int)rs6000_sched_costly_dep)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Return the next insn after INSN that is found before TAIL is reached, |
| skipping any "non-active" insns - insns that will not actually occupy |
| an issue slot. Return NULL_RTX if such an insn is not found. */ |
| |
| static rtx |
| get_next_active_insn (rtx insn, rtx tail) |
| { |
| if (insn == NULL_RTX || insn == tail) |
| return NULL_RTX; |
| |
| while (1) |
| { |
| insn = NEXT_INSN (insn); |
| if (insn == NULL_RTX || insn == tail) |
| return NULL_RTX; |
| |
| if (CALL_P (insn) |
| || JUMP_P (insn) |
| || (NONJUMP_INSN_P (insn) |
| && GET_CODE (PATTERN (insn)) != USE |
| && GET_CODE (PATTERN (insn)) != CLOBBER |
| && INSN_CODE (insn) != CODE_FOR_stack_tie)) |
| break; |
| } |
| return insn; |
| } |
| |
| /* Return whether the presence of INSN causes a dispatch group termination |
| of group WHICH_GROUP. |
| |
| If WHICH_GROUP == current_group, this function will return true if INSN |
| causes the termination of the current group (i.e, the dispatch group to |
| which INSN belongs). This means that INSN will be the last insn in the |
| group it belongs to. |
| |
| If WHICH_GROUP == previous_group, this function will return true if INSN |
| causes the termination of the previous group (i.e, the dispatch group that |
| precedes the group to which INSN belongs). This means that INSN will be |
| the first insn in the group it belongs to). */ |
| |
| static bool |
| insn_terminates_group_p (rtx insn, enum group_termination which_group) |
| { |
| enum attr_type type; |
| |
| if (! insn) |
| return false; |
| |
| type = get_attr_type (insn); |
| |
| if (is_microcoded_insn (insn)) |
| return true; |
| |
| if (which_group == current_group) |
| { |
| if (is_branch_slot_insn (insn)) |
| return true; |
| return false; |
| } |
| else if (which_group == previous_group) |
| { |
| if (is_dispatch_slot_restricted (insn)) |
| return true; |
| return false; |
| } |
| |
| return false; |
| } |
| |
| /* Return true if it is recommended to keep NEXT_INSN "far" (in a separate |
| dispatch group) from the insns in GROUP_INSNS. Return false otherwise. */ |
| |
| static bool |
| is_costly_group (rtx *group_insns, rtx next_insn) |
| { |
| int i; |
| rtx link; |
| int cost; |
| int issue_rate = rs6000_issue_rate (); |
| |
| for (i = 0; i < issue_rate; i++) |
| { |
| rtx insn = group_insns[i]; |
| if (!insn) |
| continue; |
| for (link = INSN_DEPEND (insn); link != 0; link = XEXP (link, 1)) |
| { |
| rtx next = XEXP (link, 0); |
| if (next == next_insn) |
| { |
| cost = insn_cost (insn, link, next_insn); |
| if (rs6000_is_costly_dependence (insn, next_insn, link, cost, 0)) |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /* Utility of the function redefine_groups. |
| Check if it is too costly to schedule NEXT_INSN together with GROUP_INSNS |
| in the same dispatch group. If so, insert nops before NEXT_INSN, in order |
| to keep it "far" (in a separate group) from GROUP_INSNS, following |
| one of the following schemes, depending on the value of the flag |
| -minsert_sched_nops = X: |
| (1) X == sched_finish_regroup_exact: insert exactly as many nops as needed |
| in order to force NEXT_INSN into a separate group. |
| (2) X < sched_finish_regroup_exact: insert exactly X nops. |
| GROUP_END, CAN_ISSUE_MORE and GROUP_COUNT record the state after nop |
| insertion (has a group just ended, how many vacant issue slots remain in the |
| last group, and how many dispatch groups were encountered so far). */ |
| |
| static int |
| force_new_group (int sched_verbose, FILE *dump, rtx *group_insns, |
| rtx next_insn, bool *group_end, int can_issue_more, |
| int *group_count) |
| { |
| rtx nop; |
| bool force; |
| int issue_rate = rs6000_issue_rate (); |
| bool end = *group_end; |
| int i; |
| |
| if (next_insn == NULL_RTX) |
| return can_issue_more; |
| |
| if (rs6000_sched_insert_nops > sched_finish_regroup_exact) |
| return can_issue_more; |
| |
| force = is_costly_group (group_insns, next_insn); |
| if (!force) |
| return can_issue_more; |
| |
| if (sched_verbose > 6) |
| fprintf (dump,"force: group count = %d, can_issue_more = %d\n", |
| *group_count ,can_issue_more); |
| |
| if (rs6000_sched_insert_nops == sched_finish_regroup_exact) |
| { |
| if (*group_end) |
| can_issue_more = 0; |
| |
| /* Since only a branch can be issued in the last issue_slot, it is |
| sufficient to insert 'can_issue_more - 1' nops if next_insn is not |
| a branch. If next_insn is a branch, we insert 'can_issue_more' nops; |
| in this case the last nop will start a new group and the branch |
| will be forced to the new group. */ |
| if (can_issue_more && !is_branch_slot_insn (next_insn)) |
| can_issue_more--; |
| |
| while (can_issue_more > 0) |
| { |
| nop = gen_nop (); |
| emit_insn_before (nop, next_insn); |
| can_issue_more--; |
| } |
| |
| *group_end = true; |
| return 0; |
| } |
| |
| if (rs6000_sched_insert_nops < sched_finish_regroup_exact) |
| { |
| int n_nops = rs6000_sched_insert_nops; |
| |
| /* Nops can't be issued from the branch slot, so the effective |
| issue_rate for nops is 'issue_rate - 1'. */ |
| if (can_issue_more == 0) |
| can_issue_more = issue_rate; |
| can_issue_more--; |
| if (can_issue_more == 0) |
| { |
| can_issue_more = issue_rate - 1; |
| (*group_count)++; |
| end = true; |
| for (i = 0; i < issue_rate; i++) |
| { |
| group_insns[i] = 0; |
| } |
| } |
| |
| while (n_nops > 0) |
| { |
| nop = gen_nop (); |
| emit_insn_before (nop, next_insn); |
| if (can_issue_more == issue_rate - 1) /* new group begins */ |
| end = false; |
| can_issue_more--; |
| if (can_issue_more == 0) |
| { |
| can_issue_more = issue_rate - 1; |
| (*group_count)++; |
| end = true; |
| for (i = 0; i < issue_rate; i++) |
| { |
| group_insns[i] = 0; |
| } |
| } |
| n_nops--; |
| } |
| |
| /* Scale back relative to 'issue_rate' (instead of 'issue_rate - 1'). */ |
| can_issue_more++; |
| |
| /* Is next_insn going to start a new group? */ |
| *group_end |
| = (end |
| || (can_issue_more == 1 && !is_branch_slot_insn (next_insn)) |
| || (can_issue_more <= 2 && is_cracked_insn (next_insn)) |
| || (can_issue_more < issue_rate && |
| insn_terminates_group_p (next_insn, previous_group))); |
| if (*group_end && end) |
| (*group_count)--; |
| |
| if (sched_verbose > 6) |
| fprintf (dump, "done force: group count = %d, can_issue_more = %d\n", |
| *group_count, can_issue_more); |
| return can_issue_more; |
| } |
| |
| return can_issue_more; |
| } |
| |
| /* This function tries to synch the dispatch groups that the compiler "sees" |
| with the dispatch groups that the processor dispatcher is expected to |
| form in practice. It tries to achieve this synchronization by forcing the |
| estimated processor grouping on the compiler (as opposed to the function |
| 'pad_goups' which tries to force the scheduler's grouping on the processor). |
| |
| The function scans the insn sequence between PREV_HEAD_INSN and TAIL and |
| examines the (estimated) dispatch groups that will be formed by the processor |
| dispatcher. It marks these group boundaries to reflect the estimated |
| processor grouping, overriding the grouping that the scheduler had marked. |
| Depending on the value of the flag '-minsert-sched-nops' this function can |
| force certain insns into separate groups or force a certain distance between |
| them by inserting nops, for example, if there exists a "costly dependence" |
| between the insns. |
| |
| The function estimates the group boundaries that the processor will form as |
| follows: It keeps track of how many vacant issue slots are available after |
| each insn. A subsequent insn will start a new group if one of the following |
| 4 cases applies: |
| - no more vacant issue slots remain in the current dispatch group. |
| - only the last issue slot, which is the branch slot, is vacant, but the next |
| insn is not a branch. |
| - only the last 2 or less issue slots, including the branch slot, are vacant, |
| which means that a cracked insn (which occupies two issue slots) can't be |
| issued in this group. |
| - less than 'issue_rate' slots are vacant, and the next insn always needs to |
| start a new group. */ |
| |
| static int |
| redefine_groups (FILE *dump, int sched_verbose, rtx prev_head_insn, rtx tail) |
| { |
| rtx insn, next_insn; |
| int issue_rate; |
| int can_issue_more; |
| int slot, i; |
| bool group_end; |
| int group_count = 0; |
| rtx *group_insns; |
| |
| /* Initialize. */ |
| issue_rate = rs6000_issue_rate (); |
| group_insns = alloca (issue_rate * sizeof (rtx)); |
| for (i = 0; i < issue_rate; i++) |
| { |
| group_insns[i] = 0; |
| } |
| can_issue_more = issue_rate; |
| slot = 0; |
| insn = get_next_active_insn (prev_head_insn, tail); |
| group_end = false; |
| |
| while (insn != NULL_RTX) |
| { |
| slot = (issue_rate - can_issue_more); |
| group_insns[slot] = insn; |
| can_issue_more = |
| rs6000_variable_issue (dump, sched_verbose, insn, can_issue_more); |
| if (insn_terminates_group_p (insn, current_group)) |
| can_issue_more = 0; |
| |
| next_insn = get_next_active_insn (insn, tail); |
| if (next_insn == NULL_RTX) |
| return group_count + 1; |
| |
| /* Is next_insn going to start a new group? */ |
| group_end |
| = (can_issue_more == 0 |
| || (can_issue_more == 1 && !is_branch_slot_insn (next_insn)) |
| || (can_issue_more <= 2 && is_cracked_insn (next_insn)) |
| || (can_issue_more < issue_rate && |
| insn_terminates_group_p (next_insn, previous_group))); |
| |
| can_issue_more = force_new_group (sched_verbose, dump, group_insns, |
| next_insn, &group_end, can_issue_more, |
| &group_count); |
| |
| if (group_end) |
| { |
| group_count++; |
| can_issue_more = 0; |
| for (i = 0; i < issue_rate; i++) |
| { |
| group_insns[i] = 0; |
| } |
| } |
| |
| if (GET_MODE (next_insn) == TImode && can_issue_more) |
| PUT_MODE (next_insn, VOIDmode); |
| else if (!can_issue_more && GET_MODE (next_insn) != TImode) |
| PUT_MODE (next_insn, TImode); |
| |
| insn = next_insn; |
| if (can_issue_more == 0) |
| can_issue_more = issue_rate; |
| } /* while */ |
| |
| return group_count; |
| } |
| |
| /* Scan the insn sequence between PREV_HEAD_INSN and TAIL and examine the |
| dispatch group boundaries that the scheduler had marked. Pad with nops |
| any dispatch groups which have vacant issue slots, in order to force the |
| scheduler's grouping on the processor dispatcher. The function |
| returns the number of dispatch groups found. */ |
| |
| static int |
| pad_groups (FILE *dump, int sched_verbose, rtx prev_head_insn, rtx tail) |
| { |
| rtx insn, next_insn; |
| rtx nop; |
| int issue_rate; |
| int can_issue_more; |
| int group_end; |
| int group_count = 0; |
| |
| /* Initialize issue_rate. */ |
| issue_rate = rs6000_issue_rate (); |
| can_issue_more = issue_rate; |
| |
| insn = get_next_active_insn (prev_head_insn, tail); |
| next_insn = get_next_active_insn (insn, tail); |
| |
| while (insn != NULL_RTX) |
| { |
| can_issue_more = |
| rs6000_variable_issue (dump, sched_verbose, insn, can_issue_more); |
| |
| group_end = (next_insn == NULL_RTX || GET_MODE (next_insn) == TImode); |
| |
| if (next_insn == NULL_RTX) |
| break; |
| |
| if (group_end) |
| { |
| /* If the scheduler had marked group termination at this location |
| (between insn and next_indn), and neither insn nor next_insn will |
| force group termination, pad the group with nops to force group |
| termination. */ |
| if (can_issue_more |
| && (rs6000_sched_insert_nops == sched_finish_pad_groups) |
| && !insn_terminates_group_p (insn, current_group) |
| && !insn_terminates_group_p (next_insn, previous_group)) |
| { |
| if (!is_branch_slot_insn (next_insn)) |
| can_issue_more--; |
| |
| while (can_issue_more) |
| { |
| nop = gen_nop (); |
| emit_insn_before (nop, next_insn); |
| can_issue_more--; |
| } |
| } |
| |
| can_issue_more = issue_rate; |
| group_count++; |
| } |
| |
| insn = next_insn; |
| next_insn = get_next_active_insn (insn, tail); |
| } |
| |
| return group_count; |
| } |
| |
| /* The following function is called at the end of scheduling BB. |
| After reload, it inserts nops at insn group bundling. */ |
| |
| static void |
| rs6000_sched_finish (FILE *dump, int sched_verbose) |
| { |
| int n_groups; |
| |
| if (sched_verbose) |
| fprintf (dump, "=== Finishing schedule.\n"); |
| |
| if (reload_completed && rs6000_sched_groups) |
| { |
| if (rs6000_sched_insert_nops == sched_finish_none) |
| return; |
| |
| if (rs6000_sched_insert_nops == sched_finish_pad_groups) |
| n_groups = pad_groups (dump, sched_verbose, |
| current_sched_info->prev_head, |
| current_sched_info->next_tail); |
| else |
| n_groups = redefine_groups (dump, sched_verbose, |
| current_sched_info->prev_head, |
| current_sched_info->next_tail); |
| |
| if (sched_verbose >= 6) |
| { |
| fprintf (dump, "ngroups = %d\n", n_groups); |
| print_rtl (dump, current_sched_info->prev_head); |
| fprintf (dump, "Done finish_sched\n"); |
| } |
| } |
| } |
| |
| /* LLVM LOCAL - Disable scheduler. */ |
| #endif /* ENABLE_LLVM */ |
| |
| /* Length in units of the trampoline for entering a nested function. */ |
| |
| int |
| rs6000_trampoline_size (void) |
| { |
| int ret = 0; |
| |
| switch (DEFAULT_ABI) |
| { |
| default: |
| gcc_unreachable (); |
| |
| case ABI_AIX: |
| ret = (TARGET_32BIT) ? 12 : 24; |
| break; |
| |
| case ABI_DARWIN: |
| case ABI_V4: |
| ret = (TARGET_32BIT) ? 40 : 48; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /* Emit RTL insns to initialize the variable parts of a trampoline. |
| FNADDR is an RTX for the address of the function's pure code. |
| CXT is an RTX for the static chain value for the function. */ |
| |
| void |
| rs6000_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt) |
| { |
| int regsize = (TARGET_32BIT) ? 4 : 8; |
| rtx ctx_reg = force_reg (Pmode, cxt); |
| |
| switch (DEFAULT_ABI) |
| { |
| default: |
| gcc_unreachable (); |
| |
| /* Macros to shorten the code expansions below. */ |
| #define MEM_DEREF(addr) gen_rtx_MEM (Pmode, memory_address (Pmode, addr)) |
| #define MEM_PLUS(addr,offset) \ |
| gen_rtx_MEM (Pmode, memory_address (Pmode, plus_constant (addr, offset))) |
| |
| /* Under AIX, just build the 3 word function descriptor */ |
| case ABI_AIX: |
| { |
| rtx fn_reg = gen_reg_rtx (Pmode); |
| rtx toc_reg = gen_reg_rtx (Pmode); |
| emit_move_insn (fn_reg, MEM_DEREF (fnaddr)); |
| emit_move_insn (toc_reg, MEM_PLUS (fnaddr, regsize)); |
| emit_move_insn (MEM_DEREF (addr), fn_reg); |
| emit_move_insn (MEM_PLUS (addr, regsize), toc_reg); |
| emit_move_insn (MEM_PLUS (addr, 2*regsize), ctx_reg); |
| } |
| break; |
| |
| /* Under V.4/eabi/darwin, __trampoline_setup does the real work. */ |
| case ABI_DARWIN: |
| case ABI_V4: |
| emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__trampoline_setup"), |
| FALSE, VOIDmode, 4, |
| addr, Pmode, |
| GEN_INT (rs6000_trampoline_size ()), SImode, |
| fnaddr, Pmode, |
| ctx_reg, Pmode); |
| break; |
| } |
| |
| return; |
| } |
| |
| |
| /* Table of valid machine attributes. */ |
| |
| const struct attribute_spec rs6000_attribute_table[] = |
| { |
| /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ |
| { "altivec", 1, 1, false, true, false, rs6000_handle_altivec_attribute }, |
| { "longcall", 0, 0, false, true, true, rs6000_handle_longcall_attribute }, |
| { "shortcall", 0, 0, false, true, true, rs6000_handle_longcall_attribute }, |
| { "ms_struct", 0, 0, false, false, false, rs6000_handle_struct_attribute }, |
| { "gcc_struct", 0, 0, false, false, false, rs6000_handle_struct_attribute }, |
| #ifdef SUBTARGET_ATTRIBUTE_TABLE |
| SUBTARGET_ATTRIBUTE_TABLE, |
| #endif |
| { NULL, 0, 0, false, false, false, NULL } |
| }; |
| |
| /* Handle the "altivec" attribute. The attribute may have |
| arguments as follows: |
| |
| __attribute__((altivec(vector__))) |
| __attribute__((altivec(pixel__))) (always followed by 'unsigned short') |
| __attribute__((altivec(bool__))) (always followed by 'unsigned') |
| |
| and may appear more than once (e.g., 'vector bool char') in a |
| given declaration. */ |
| |
| static tree |
| rs6000_handle_altivec_attribute (tree *node, |
| tree name ATTRIBUTE_UNUSED, |
| tree args, |
| int flags ATTRIBUTE_UNUSED, |
| bool *no_add_attrs) |
| { |
| tree type = *node, result = NULL_TREE; |
| enum machine_mode mode; |
| int unsigned_p; |
| char altivec_type |
| = ((args && TREE_CODE (args) == TREE_LIST && TREE_VALUE (args) |
| && TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE) |
| ? *IDENTIFIER_POINTER (TREE_VALUE (args)) |
| : '?'); |
| |
| while (POINTER_TYPE_P (type) |
| || TREE_CODE (type) == FUNCTION_TYPE |
| || TREE_CODE (type) == METHOD_TYPE |
| || TREE_CODE (type) == ARRAY_TYPE) |
| type = TREE_TYPE (type); |
| |
| mode = TYPE_MODE (type); |
| |
| /* Check for invalid AltiVec type qualifiers. */ |
| if (type == long_unsigned_type_node || type == long_integer_type_node) |
| { |
| if (TARGET_64BIT) |
| error ("use of %<long%> in AltiVec types is invalid for 64-bit code"); |
| else if (rs6000_warn_altivec_long) |
| warning (0, "use of %<long%> in AltiVec types is deprecated; use %<int%>"); |
| } |
| else if (type == long_long_unsigned_type_node |
| || type == long_long_integer_type_node) |
| error ("use of %<long long%> in AltiVec types is invalid"); |
| else if (type == double_type_node) |
| error ("use of %<double%> in AltiVec types is invalid"); |
| else if (type == long_double_type_node) |
| error ("use of %<long double%> in AltiVec types is invalid"); |
| else if (type == boolean_type_node) |
| error ("use of boolean types in AltiVec types is invalid"); |
| else if (TREE_CODE (type) == COMPLEX_TYPE) |
| error ("use of %<complex%> in AltiVec types is invalid"); |
| else if (DECIMAL_FLOAT_MODE_P (mode)) |
| error ("use of decimal floating point types in AltiVec types is invalid"); |
| |
| switch (altivec_type) |
| { |
| /* APPLE LOCAL begin AltiVec */ |
| case 'e': |
| /* Return the constituent element type. */ |
| result = (ALTIVEC_VECTOR_MODE (mode) ? TREE_TYPE (type) : type); |
| break; |
| /* APPLE LOCAL end AltiVec */ |
| |
| case 'v': |
| unsigned_p = TYPE_UNSIGNED (type); |
| switch (mode) |
| { |
| case SImode: |
| result = (unsigned_p ? unsigned_V4SI_type_node : V4SI_type_node); |
| break; |
| case HImode: |
| result = (unsigned_p ? unsigned_V8HI_type_node : V8HI_type_node); |
| break; |
| case QImode: |
| result = (unsigned_p ? unsigned_V16QI_type_node : V16QI_type_node); |
| break; |
| case SFmode: result = V4SF_type_node; break; |
| /* If the user says 'vector int bool', we may be handed the 'bool' |
| attribute _before_ the 'vector' attribute, and so select the |
| proper type in the 'b' case below. */ |
| case V4SImode: case V8HImode: case V16QImode: case V4SFmode: |
| result = type; |
| default: break; |
| } |
| break; |
| case 'b': |
| switch (mode) |
| { |
| case SImode: case V4SImode: result = bool_V4SI_type_node; break; |
| case HImode: case V8HImode: result = bool_V8HI_type_node; break; |
| case QImode: case V16QImode: result = bool_V16QI_type_node; |
| default: break; |
| } |
| break; |
| case 'p': |
| switch (mode) |
| { |
| case V8HImode: result = pixel_V8HI_type_node; |
| default: break; |
| } |
| default: break; |
| } |
| |
| /* APPLE LOCAL begin AltiVec */ |
| /* Propagate qualifiers attached to the element type |
| onto the vector type. */ |
| if (result && result != type && TYPE_QUALS (type)) |
| result = build_qualified_type (result, TYPE_QUALS (type)); |
| /* APPLE LOCAL end AltiVec */ |
| |
| *no_add_attrs = true; /* No need to hang on to the attribute. */ |
| |
| if (result) |
| *node = reconstruct_complex_type (*node, result); |
| |
| return NULL_TREE; |
| } |
| |
| /* AltiVec defines four built-in scalar types that serve as vector |
| elements; we must teach the compiler how to mangle them. */ |
| |
| static const char * |
| /* APPLE LOCAL mangle_type 7105099 */ |
| rs6000_mangle_type (tree type) |
| { |
| /* APPLE LOCAL begin mangle_type 7105099 */ |
| type = TYPE_MAIN_VARIANT (type); |
| |
| if (TREE_CODE (type) != VOID_TYPE && TREE_CODE (type) != BOOLEAN_TYPE |
| && TREE_CODE (type) != INTEGER_TYPE && TREE_CODE (type) != REAL_TYPE) |
| return NULL; |
| |
| /* APPLE LOCAL end mangle_type 7105099 */ |
| if (type == bool_char_type_node) return "U6__boolc"; |
| if (type == bool_short_type_node) return "U6__bools"; |
| if (type == pixel_type_node) return "u7__pixel"; |
| if (type == bool_int_type_node) return "U6__booli"; |
| |
| /* Mangle IBM extended float long double as `g' (__float128) on |
| powerpc*-linux where long-double-64 previously was the default. */ |
| if (TYPE_MAIN_VARIANT (type) == long_double_type_node |
| && TARGET_ELF |
| && TARGET_LONG_DOUBLE_128 |
| && !TARGET_IEEEQUAD) |
| return "g"; |
| |
| /* For all other types, use normal C++ mangling. */ |
| return NULL; |
| } |
| |
| /* Handle a "longcall" or "shortcall" attribute; arguments as in |
| struct attribute_spec.handler. */ |
| |
| static tree |
| rs6000_handle_longcall_attribute (tree *node, tree name, |
| tree args ATTRIBUTE_UNUSED, |
| int flags ATTRIBUTE_UNUSED, |
| bool *no_add_attrs) |
| { |
| if (TREE_CODE (*node) != FUNCTION_TYPE |
| && TREE_CODE (*node) != FIELD_DECL |
| && TREE_CODE (*node) != TYPE_DECL) |
| { |
| warning (OPT_Wattributes, "%qs attribute only applies to functions", |
| IDENTIFIER_POINTER (name)); |
| *no_add_attrs = true; |
| } |
| /* APPLE LOCAL begin longcall */ |
| else if (TARGET_64BIT && TARGET_MACHO) |
| *no_add_attrs = true; |
| /* APPLE LOCAL end longcall */ |
| |
| return NULL_TREE; |
| } |
| |
| /* Set longcall attributes on all functions declared when |
| rs6000_default_long_calls is true. */ |
| static void |
| rs6000_set_default_type_attributes (tree type) |
| { |
| if (rs6000_default_long_calls |
| && (TREE_CODE (type) == FUNCTION_TYPE |
| || TREE_CODE (type) == METHOD_TYPE)) |
| TYPE_ATTRIBUTES (type) = tree_cons (get_identifier ("longcall"), |
| NULL_TREE, |
| TYPE_ATTRIBUTES (type)); |
| |
| #if TARGET_MACHO |
| darwin_set_default_type_attributes (type); |
| #endif |
| } |
| |
| /* Return a reference suitable for calling a function with the |
| longcall attribute. */ |
| |
| rtx |
| rs6000_longcall_ref (rtx call_ref) |
| { |
| const char *call_name; |
| tree node; |
| |
| if (GET_CODE (call_ref) != SYMBOL_REF) |
| return call_ref; |
| |
| /* System V adds '.' to the internal name, so skip them. */ |
| call_name = XSTR (call_ref, 0); |
| if (*call_name == '.') |
| { |
| while (*call_name == '.') |
| call_name++; |
| |
| node = get_identifier (call_name); |
| call_ref = gen_rtx_SYMBOL_REF (VOIDmode, IDENTIFIER_POINTER (node)); |
| } |
| |
| return force_reg (Pmode, call_ref); |
| } |
| |
| #ifndef TARGET_USE_MS_BITFIELD_LAYOUT |
| #define TARGET_USE_MS_BITFIELD_LAYOUT 0 |
| #endif |
| |
| /* Handle a "ms_struct" or "gcc_struct" attribute; arguments as in |
| struct attribute_spec.handler. */ |
| static tree |
| rs6000_handle_struct_attribute (tree *node, tree name, |
| tree args ATTRIBUTE_UNUSED, |
| int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) |
| { |
| tree *type = NULL; |
| if (DECL_P (*node)) |
| { |
| if (TREE_CODE (*node) == TYPE_DECL) |
| type = &TREE_TYPE (*node); |
| } |
| else |
| type = node; |
| |
| if (!(type && (TREE_CODE (*type) == RECORD_TYPE |
| || TREE_CODE (*type) == UNION_TYPE))) |
| { |
| warning (OPT_Wattributes, "%qs attribute ignored", IDENTIFIER_POINTER (name)); |
| *no_add_attrs = true; |
| } |
| |
| else if ((is_attribute_p ("ms_struct", name) |
| && lookup_attribute ("gcc_struct", TYPE_ATTRIBUTES (*type))) |
| || ((is_attribute_p ("gcc_struct", name) |
| && lookup_attribute ("ms_struct", TYPE_ATTRIBUTES (*type))))) |
| { |
| warning (OPT_Wattributes, "%qs incompatible attribute ignored", |
| IDENTIFIER_POINTER (name)); |
| *no_add_attrs = true; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| static bool |
| rs6000_ms_bitfield_layout_p (tree record_type) |
| { |
| return (TARGET_USE_MS_BITFIELD_LAYOUT && |
| !lookup_attribute ("gcc_struct", TYPE_ATTRIBUTES (record_type))) |
| || lookup_attribute ("ms_struct", TYPE_ATTRIBUTES (record_type)); |
| } |
| |
| #ifdef USING_ELFOS_H |
| |
| /* A get_unnamed_section callback, used for switching to toc_section. */ |
| |
| static void |
| rs6000_elf_output_toc_section_asm_op (const void *data ATTRIBUTE_UNUSED) |
| { |
| if (DEFAULT_ABI == ABI_AIX |
| && TARGET_MINIMAL_TOC |
| && !TARGET_RELOCATABLE) |
| { |
| if (!toc_initialized) |
| { |
| toc_initialized = 1; |
| fprintf (asm_out_file, "%s\n", TOC_SECTION_ASM_OP); |
| (*targetm.asm_out.internal_label) (asm_out_file, "LCTOC", 0); |
| fprintf (asm_out_file, "\t.tc "); |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC1[TC],"); |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC1"); |
| fprintf (asm_out_file, "\n"); |
| |
| fprintf (asm_out_file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC1"); |
| fprintf (asm_out_file, " = .+32768\n"); |
| } |
| else |
| fprintf (asm_out_file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); |
| } |
| else if (DEFAULT_ABI == ABI_AIX && !TARGET_RELOCATABLE) |
| fprintf (asm_out_file, "%s\n", TOC_SECTION_ASM_OP); |
| else |
| { |
| fprintf (asm_out_file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); |
| if (!toc_initialized) |
| { |
| ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC1"); |
| fprintf (asm_out_file, " = .+32768\n"); |
| toc_initialized = 1; |
| } |
| } |
| } |
| |
| /* Implement TARGET_ASM_INIT_SECTIONS. */ |
| |
| static void |
| rs6000_elf_asm_init_sections (void) |
| { |
| toc_section |
| = get_unnamed_section (0, rs6000_elf_output_toc_section_asm_op, NULL); |
| |
| sdata2_section |
| = get_unnamed_section (SECTION_WRITE, output_section_asm_op, |
| SDATA2_SECTION_ASM_OP); |
| } |
| |
| /* Implement TARGET_SELECT_RTX_SECTION. */ |
| |
| static section * |
| rs6000_elf_select_rtx_section (enum machine_mode mode, rtx x, |
| unsigned HOST_WIDE_INT align) |
| { |
| if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (x, mode)) |
| return toc_section; |
| else |
| return default_elf_select_rtx_section (mode, x, align); |
| } |
| |
| /* For a SYMBOL_REF, set generic flags and then perform some |
| target-specific processing. |
| |
| When the AIX ABI is requested on a non-AIX system, replace the |
| function name with the real name (with a leading .) rather than the |
| function descriptor name. This saves a lot of overriding code to |
| read the prefixes. */ |
| |
| static void |
| rs6000_elf_encode_section_info (tree decl, rtx rtl, int first) |
| { |
| default_encode_section_info (decl, rtl, first); |
| |
| if (first |
| && TREE_CODE (decl) == FUNCTION_DECL |
| && !TARGET_AIX |
| && DEFAULT_ABI == ABI_AIX) |
| { |
| rtx sym_ref = XEXP (rtl, 0); |
| size_t len = strlen (XSTR (sym_ref, 0)); |
| char *str = alloca (len + 2); |
| str[0] = '.'; |
| memcpy (str + 1, XSTR (sym_ref, 0), len + 1); |
| XSTR (sym_ref, 0) = ggc_alloc_string (str, len + 1); |
| } |
| } |
| |
| bool |
| rs6000_elf_in_small_data_p (tree decl) |
| { |
| if (rs6000_sdata == SDATA_NONE) |
| return false; |
| |
| /* We want to merge strings, so we never consider them small data. */ |
| if (TREE_CODE (decl) == STRING_CST) |
| return false; |
| |
| /* Functions are never in the small data area. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| return false; |
| |
| if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl)) |
| { |
| const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); |
| if (strcmp (section, ".sdata") == 0 |
| || strcmp (section, ".sdata2") == 0 |
| || strcmp (section, ".sbss") == 0 |
| || strcmp (section, ".sbss2") == 0 |
| || strcmp (section, ".PPC.EMB.sdata0") == 0 |
| || strcmp (section, ".PPC.EMB.sbss0") == 0) |
| return true; |
| } |
| else |
| { |
| HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl)); |
| |
| if (size > 0 |
| && (unsigned HOST_WIDE_INT) size <= g_switch_value |
| /* If it's not public, and we're not going to reference it there, |
| there's no need to put it in the small data section. */ |
| && (rs6000_sdata != SDATA_DATA || TREE_PUBLIC (decl))) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| #endif /* USING_ELFOS_H */ |
| |
| /* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P. */ |
| |
| static bool |
| rs6000_use_blocks_for_constant_p (enum machine_mode mode, rtx x) |
| { |
| return !ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (x, mode); |
| } |
| |
| /* Return a REG that occurs in ADDR with coefficient 1. |
| ADDR can be effectively incremented by incrementing REG. |
| |
| r0 is special and we must not select it as an address |
| register by this routine since our caller will try to |
| increment the returned register via an "la" instruction. */ |
| |
| rtx |
| find_addr_reg (rtx addr) |
| { |
| while (GET_CODE (addr) == PLUS) |
| { |
| if (GET_CODE (XEXP (addr, 0)) == REG |
| && REGNO (XEXP (addr, 0)) != 0) |
| addr = XEXP (addr, 0); |
| else if (GET_CODE (XEXP (addr, 1)) == REG |
| && REGNO (XEXP (addr, 1)) != 0) |
| addr = XEXP (addr, 1); |
| else if (CONSTANT_P (XEXP (addr, 0))) |
| addr = XEXP (addr, 1); |
| else if (CONSTANT_P (XEXP (addr, 1))) |
| addr = XEXP (addr, 0); |
| else |
| gcc_unreachable (); |
| } |
| gcc_assert (GET_CODE (addr) == REG && REGNO (addr) != 0); |
| return addr; |
| } |
| |
| void |
| rs6000_fatal_bad_address (rtx op) |
| { |
| fatal_insn ("bad address", op); |
| } |
| |
| /* LLVM LOCAL begin move branch_island_list outside #if */ |
| static GTY (()) tree branch_island_list = 0; |
| |
| #if TARGET_MACHO |
| |
| /* APPLE LOCAL mlongcall long names 4271187 */ |
| /* LLVM LOCAL end move branch_island_list outside #if */ |
| |
| /* APPLE LOCAL begin 4380289 */ |
| /* Remember to generate a branch island for far calls to the given |
| function. Force the creation of a Mach-O stub. */ |
| |
| static tree |
| add_compiler_branch_island (tree function_name, int line_number) |
| { |
| tree branch_island; |
| tree label_decl = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); |
| |
| branch_island = build_tree_list (function_name, label_decl); |
| TREE_TYPE (branch_island) = build_int_cst (NULL_TREE, line_number); |
| TREE_CHAIN (branch_island) = branch_island_list; |
| branch_island_list = branch_island; |
| return label_decl; |
| } |
| /* APPLE LOCAL end 4380289 */ |
| |
| #define BRANCH_ISLAND_LABEL_NAME(BRANCH_ISLAND) TREE_VALUE (BRANCH_ISLAND) |
| #define BRANCH_ISLAND_FUNCTION_NAME(BRANCH_ISLAND) TREE_PURPOSE (BRANCH_ISLAND) |
| #define BRANCH_ISLAND_LINE_NUMBER(BRANCH_ISLAND) \ |
| TREE_INT_CST_LOW (TREE_TYPE (BRANCH_ISLAND)) |
| |
| /* Generate far-jump branch islands for everything on the |
| branch_island_list. Invoked immediately after the last instruction |
| of the epilogue has been emitted; the branch-islands must be |
| appended to, and contiguous with, the function body. Mach-O stubs |
| are generated in machopic_output_stub(). */ |
| |
| static void |
| macho_branch_islands (void) |
| { |
| /* APPLE LOCAL begin 4380289 */ |
| tree branch_island; |
| |
| for (branch_island = branch_island_list; |
| branch_island; |
| branch_island = TREE_CHAIN (branch_island)) |
| { |
| rtx operands[2]; |
| rtx decl_rtl; |
| |
| #if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) |
| if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) |
| dbxout_stabd (N_SLINE, BRANCH_ISLAND_LINE_NUMBER (branch_island)); |
| #endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */ |
| decl_rtl = DECL_RTL (BRANCH_ISLAND_FUNCTION_NAME (branch_island)); |
| operands[0] = (in_section == text_section) |
| ? machopic_indirect_call_target (decl_rtl) |
| : machopic_force_indirect_call_target (decl_rtl); |
| operands[1] = label_rtx (BRANCH_ISLAND_LABEL_NAME (branch_island)); |
| if (flag_pic) |
| { |
| output_asm_insn ("\n%1:\n\tmflr r0\n" |
| "\tbcl 20,31,%1_pic\n" |
| "%1_pic:\n" |
| "\tmflr r12\n" |
| "\taddis r12,r12,ha16(%0 - %1_pic)\n" |
| "\tmtlr r0\n" |
| "\taddi r12,r12,lo16(%0 - %1_pic)\n" |
| "\tmtctr r12\n" |
| "\tbctr", |
| operands); |
| } |
| else |
| { |
| output_asm_insn ("\n%1:\n" |
| "\tlis r12,hi16(%0)\n" |
| "\tori r12,r12,lo16(%0)\n" |
| "\tmtctr r12\n" |
| "\tbctr", |
| operands); |
| } |
| #if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) |
| if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) |
| dbxout_stabd (N_SLINE, BRANCH_ISLAND_LINE_NUMBER (branch_island)); |
| #endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */ |
| } |
| /* APPLE LOCAL end 4380289 */ |
| |
| branch_island_list = 0; |
| } |
| |
| /* NO_PREVIOUS_DEF checks in the link list whether the function name is |
| already there or not. */ |
| |
| static int |
| no_previous_def (tree function_name) |
| { |
| tree branch_island; |
| for (branch_island = branch_island_list; |
| branch_island; |
| branch_island = TREE_CHAIN (branch_island)) |
| if (function_name == BRANCH_ISLAND_FUNCTION_NAME (branch_island)) |
| return 0; |
| return 1; |
| } |
| |
| /* GET_PREV_LABEL gets the label name from the previous definition of |
| the function. */ |
| |
| static tree |
| get_prev_label (tree function_name) |
| { |
| tree branch_island; |
| for (branch_island = branch_island_list; |
| branch_island; |
| branch_island = TREE_CHAIN (branch_island)) |
| if (function_name == BRANCH_ISLAND_FUNCTION_NAME (branch_island)) |
| return BRANCH_ISLAND_LABEL_NAME (branch_island); |
| return 0; |
| } |
| |
| /* APPLE LOCAL begin axe stubs 5571540 */ |
| /* DARWIN_LINKER_GENERATES_ISLANDS and DARWIN_GENERATE_ISLANDS moved up */ |
| /* APPLE LOCAL end axe stubs 5571540 */ |
| |
| /* INSN is either a function call or a millicode call. It may have an |
| unconditional jump in its delay slot. |
| |
| CALL_DEST is the routine we are calling. */ |
| |
| char * |
| output_call (rtx insn, rtx *operands, int dest_operand_number, |
| int cookie_operand_number) |
| { |
| static char buf[256]; |
| if (DARWIN_GENERATE_ISLANDS |
| && GET_CODE (operands[dest_operand_number]) == SYMBOL_REF |
| && (INTVAL (operands[cookie_operand_number]) & CALL_LONG)) |
| { |
| tree labelname; |
| /* APPLE LOCAL begin 4380289 */ |
| tree funname = SYMBOL_REF_DECL (operands[dest_operand_number]); |
| |
| if (!funname) |
| { |
| funname = build_decl_stat (FUNCTION_DECL, |
| get_identifier (XSTR (operands[dest_operand_number], 0)), |
| void_type_node); |
| set_decl_rtl (funname, operands[dest_operand_number]); |
| } |
| |
| { |
| int line_number = 0; |
| |
| /* APPLE LOCAL begin 3910248, 3915171 */ |
| for (; |
| insn && (GET_CODE (insn) != NOTE |
| || NOTE_LINE_NUMBER (insn) < 0); |
| insn = PREV_INSN (insn)) |
| ; |
| /* APPLE LOCAL end 3910248, 3915171 */ |
| if (insn) |
| line_number = NOTE_LINE_NUMBER (insn); |
| |
| labelname = no_previous_def (funname) |
| ? add_compiler_branch_island (funname, line_number) |
| : get_prev_label (funname); |
| } |
| |
| /* If we're generating a long call from the text section, we |
| can use the usual rules for Mach-O indirection. If we're |
| in a coalesced text section, we must always refer to a |
| Mach-O stub; if refer directly to our callee, and our |
| callee is also in a coalesced section, and is coalesced |
| away, the linkers (static and dynamic) won't know where |
| to send us. Ergo, when we're in a coalesced section, we |
| must always use a stub for all callees. */ |
| |
| operands[dest_operand_number] = (in_section == text_section) |
| ? machopic_indirect_call_target (operands[dest_operand_number]) |
| : machopic_force_indirect_call_target (operands[dest_operand_number]); |
| operands[1+dest_operand_number] = label_rtx (labelname); |
| |
| /* "jbsr foo, L42" is Mach-O for "Link as 'bl foo' if a 'bl' |
| instruction will reach 'foo', otherwise link as 'bl L42'". |
| "L42" should be a 'branch island', that will do a far jump to |
| 'foo'. Branch islands are generated in |
| macho_branch_islands(). */ |
| sprintf (buf, "jbsr %%z%d,%%l%d", |
| dest_operand_number, 1+dest_operand_number); |
| /* APPLE LOCAL end 4380289 */ |
| } |
| else |
| sprintf (buf, "bl %%z%d", dest_operand_number); |
| return buf; |
| } |
| |
| /* Generate PIC and indirect symbol stubs. */ |
| |
| void |
| machopic_output_stub (FILE *file, const char *symb, const char *stub) |
| { |
| unsigned int length; |
| char *symbol_name, *lazy_ptr_name; |
| char *local_label_0; |
| static int label = 0; |
| |
| /* Lose our funky encoding stuff so it doesn't contaminate the stub. */ |
| symb = (*targetm.strip_name_encoding) (symb); |
| |
| |
| length = strlen (symb); |
| symbol_name = alloca (length + 32); |
| GEN_SYMBOL_NAME_FOR_SYMBOL (symbol_name, symb, length); |
| |
| lazy_ptr_name = alloca (length + 32); |
| GEN_LAZY_PTR_NAME_FOR_SYMBOL (lazy_ptr_name, symb, length); |
| |
| if (flag_pic == 2) |
| switch_to_section (darwin_sections[machopic_picsymbol_stub1_section]); |
| else |
| switch_to_section (darwin_sections[machopic_symbol_stub1_section]); |
| |
| if (flag_pic == 2) |
| { |
| fprintf (file, "\t.align 5\n"); |
| |
| fprintf (file, "%s:\n", stub); |
| fprintf (file, "\t.indirect_symbol %s\n", symbol_name); |
| |
| label++; |
| local_label_0 = alloca (sizeof ("\"L00000000000$spb\"")); |
| sprintf (local_label_0, "\"L%011d$spb\"", label); |
| |
| fprintf (file, "\tmflr r0\n"); |
| fprintf (file, "\tbcl 20,31,%s\n", local_label_0); |
| fprintf (file, "%s:\n\tmflr r11\n", local_label_0); |
| fprintf (file, "\taddis r11,r11,ha16(%s-%s)\n", |
| lazy_ptr_name, local_label_0); |
| fprintf (file, "\tmtlr r0\n"); |
| fprintf (file, "\t%s r12,lo16(%s-%s)(r11)\n", |
| (TARGET_64BIT ? "ldu" : "lwzu"), |
| lazy_ptr_name, local_label_0); |
| fprintf (file, "\tmtctr r12\n"); |
| fprintf (file, "\tbctr\n"); |
| } |
| else |
| { |
| fprintf (file, "\t.align 4\n"); |
| |
| fprintf (file, "%s:\n", stub); |
| fprintf (file, "\t.indirect_symbol %s\n", symbol_name); |
| |
| fprintf (file, "\tlis r11,ha16(%s)\n", lazy_ptr_name); |
| fprintf (file, "\t%s r12,lo16(%s)(r11)\n", |
| (TARGET_64BIT ? "ldu" : "lwzu"), |
| lazy_ptr_name); |
| fprintf (file, "\tmtctr r12\n"); |
| fprintf (file, "\tbctr\n"); |
| } |
| |
| switch_to_section (darwin_sections[machopic_lazy_symbol_ptr_section]); |
| fprintf (file, "%s:\n", lazy_ptr_name); |
| fprintf (file, "\t.indirect_symbol %s\n", symbol_name); |
| fprintf (file, "%sdyld_stub_binding_helper\n", |
| (TARGET_64BIT ? DOUBLE_INT_ASM_OP : "\t.long\t")); |
| } |
| |
| /* Legitimize PIC addresses. If the address is already |
| position-independent, we return ORIG. Newly generated |
| position-independent addresses go into a reg. This is REG if non |
| zero, otherwise we allocate register(s) as necessary. */ |
| |
| #define SMALL_INT(X) ((UINTVAL (X) + 0x8000) < 0x10000) |
| |
| rtx |
| rs6000_machopic_legitimize_pic_address (rtx orig, enum machine_mode mode, |
| rtx reg) |
| { |
| rtx base, offset; |
| |
| if (reg == NULL && ! reload_in_progress && ! reload_completed) |
| reg = gen_reg_rtx (Pmode); |
| |
| if (GET_CODE (orig) == CONST) |
| { |
| rtx reg_temp; |
| |
| if (GET_CODE (XEXP (orig, 0)) == PLUS |
| && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) |
| return orig; |
| |
| gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS); |
| |
| /* Use a different reg for the intermediate value, as |
| it will be marked UNCHANGING. */ |
| reg_temp = no_new_pseudos ? reg : gen_reg_rtx (Pmode); |
| base = rs6000_machopic_legitimize_pic_address (XEXP (XEXP (orig, 0), 0), |
| Pmode, reg_temp); |
| offset = |
| rs6000_machopic_legitimize_pic_address (XEXP (XEXP (orig, 0), 1), |
| Pmode, reg); |
| |
| if (GET_CODE (offset) == CONST_INT) |
| { |
| if (SMALL_INT (offset)) |
| return plus_constant (base, INTVAL (offset)); |
| else if (! reload_in_progress && ! reload_completed) |
| offset = force_reg (Pmode, offset); |
| else |
| { |
| rtx mem = force_const_mem (Pmode, orig); |
| return machopic_legitimize_pic_address (mem, Pmode, reg); |
| } |
| } |
| return gen_rtx_PLUS (Pmode, base, offset); |
| } |
| |
| /* Fall back on generic machopic code. */ |
| return machopic_legitimize_pic_address (orig, mode, reg); |
| } |
| |
| /* Output a .machine directive for the Darwin assembler, and call |
| the generic start_file routine. */ |
| |
| static void |
| rs6000_darwin_file_start (void) |
| { |
| static const struct |
| { |
| const char *arg; |
| const char *name; |
| int if_set; |
| } mapping[] = { |
| { "ppc64", "ppc64", MASK_64BIT }, |
| { "970", "ppc970", MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64 }, |
| { "power4", "ppc970", 0 }, |
| { "G5", "ppc970", 0 }, |
| { "7450", "ppc7450", 0 }, |
| /* APPLE LOCAL radar 4161346 */ |
| { "ppc", "ppc", MASK_PIM_ALTIVEC }, |
| { "7400", "ppc7400", MASK_ALTIVEC }, |
| { "G4", "ppc7400", 0 }, |
| { "750", "ppc750", 0 }, |
| { "740", "ppc750", 0 }, |
| { "G3", "ppc750", 0 }, |
| { "604e", "ppc604e", 0 }, |
| { "604", "ppc604", 0 }, |
| { "603e", "ppc603", 0 }, |
| { "603", "ppc603", 0 }, |
| { "601", "ppc601", 0 }, |
| { NULL, "ppc", 0 } }; |
| const char *cpu_id = ""; |
| size_t i; |
| |
| rs6000_file_start (); |
| darwin_file_start (); |
| |
| /* Determine the argument to -mcpu=. Default to G3 if not specified. */ |
| for (i = 0; i < ARRAY_SIZE (rs6000_select); i++) |
| if (rs6000_select[i].set_arch_p && rs6000_select[i].string |
| && rs6000_select[i].string[0] != '\0') |
| cpu_id = rs6000_select[i].string; |
| |
| /* Look through the mapping array. Pick the first name that either |
| matches the argument, has a bit set in IF_SET that is also set |
| in the target flags, or has a NULL name. */ |
| |
| i = 0; |
| while (mapping[i].arg != NULL |
| && strcmp (mapping[i].arg, cpu_id) != 0 |
| && (mapping[i].if_set & target_flags) == 0) |
| i++; |
| |
| fprintf (asm_out_file, "\t.machine %s\n", mapping[i].name); |
| } |
| |
| #endif /* TARGET_MACHO */ |
| |
| #if TARGET_ELF |
| static int |
| rs6000_elf_reloc_rw_mask (void) |
| { |
| if (flag_pic) |
| return 3; |
| else if (DEFAULT_ABI == ABI_AIX) |
| return 2; |
| else |
| return 0; |
| } |
| |
| /* Record an element in the table of global constructors. SYMBOL is |
| a SYMBOL_REF of the function to be called; PRIORITY is a number |
| between 0 and MAX_INIT_PRIORITY. |
| |
| This differs from default_named_section_asm_out_constructor in |
| that we have special handling for -mrelocatable. */ |
| |
| static void |
| rs6000_elf_asm_out_constructor (rtx symbol, int priority) |
| { |
| const char *section = ".ctors"; |
| char buf[16]; |
| |
| if (priority != DEFAULT_INIT_PRIORITY) |
| { |
| sprintf (buf, ".ctors.%.5u", |
| /* Invert the numbering so the linker puts us in the proper |
| order; constructors are run from right to left, and the |
| linker sorts in increasing order. */ |
| MAX_INIT_PRIORITY - priority); |
| section = buf; |
| } |
| |
| switch_to_section (get_section (section, SECTION_WRITE, NULL)); |
| assemble_align (POINTER_SIZE); |
| |
| if (TARGET_RELOCATABLE) |
| { |
| fputs ("\t.long (", asm_out_file); |
| output_addr_const (asm_out_file, symbol); |
| fputs (")@fixup\n", asm_out_file); |
| } |
| else |
| assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1); |
| } |
| |
| static void |
| rs6000_elf_asm_out_destructor (rtx symbol, int priority) |
| { |
| const char *section = ".dtors"; |
| char buf[16]; |
| |
| if (priority != DEFAULT_INIT_PRIORITY) |
| { |
| sprintf (buf, ".dtors.%.5u", |
| /* Invert the numbering so the linker puts us in the proper |
| order; constructors are run from right to left, and the |
| linker sorts in increasing order. */ |
| MAX_INIT_PRIORITY - priority); |
| section = buf; |
| } |
| |
| switch_to_section (get_section (section, SECTION_WRITE, NULL)); |
| assemble_align (POINTER_SIZE); |
| |
| if (TARGET_RELOCATABLE) |
| { |
| fputs ("\t.long (", asm_out_file); |
| output_addr_const (asm_out_file, symbol); |
| fputs (")@fixup\n", asm_out_file); |
| } |
| else |
| assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1); |
| } |
| |
| void |
| rs6000_elf_declare_function_name (FILE *file, const char *name, tree decl) |
| { |
| if (TARGET_64BIT) |
| { |
| fputs ("\t.section\t\".opd\",\"aw\"\n\t.align 3\n", file); |
| ASM_OUTPUT_LABEL (file, name); |
| fputs (DOUBLE_INT_ASM_OP, file); |
| rs6000_output_function_entry (file, name); |
| fputs (",.TOC.@tocbase,0\n\t.previous\n", file); |
| if (DOT_SYMBOLS) |
| { |
| fputs ("\t.size\t", file); |
| assemble_name (file, name); |
| fputs (",24\n\t.type\t.", file); |
| assemble_name (file, name); |
| fputs (",@function\n", file); |
| if (TREE_PUBLIC (decl) && ! DECL_WEAK (decl)) |
| { |
| fputs ("\t.globl\t.", file); |
| assemble_name (file, name); |
| putc ('\n', file); |
| } |
| } |
| else |
| ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function"); |
| ASM_DECLARE_RESULT (file, DECL_RESULT (decl)); |
| rs6000_output_function_entry (file, name); |
| fputs (":\n", file); |
| return; |
| } |
| |
| if (TARGET_RELOCATABLE |
| && !TARGET_SECURE_PLT |
| && (get_pool_size () != 0 || current_function_profile) |
| && uses_TOC ()) |
| { |
| char buf[256]; |
| |
| (*targetm.asm_out.internal_label) (file, "LCL", rs6000_pic_labelno); |
| |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); |
| fprintf (file, "\t.long "); |
| assemble_name (file, buf); |
| putc ('-', file); |
| ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); |
| assemble_name (file, buf); |
| putc ('\n', file); |
| } |
| |
| ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function"); |
| ASM_DECLARE_RESULT (file, DECL_RESULT (decl)); |
| |
| if (DEFAULT_ABI == ABI_AIX) |
| { |
| const char *desc_name, *orig_name; |
| |
| orig_name = (*targetm.strip_name_encoding) (name); |
| desc_name = orig_name; |
| while (*desc_name == '.') |
| desc_name++; |
| |
| if (TREE_PUBLIC (decl)) |
| fprintf (file, "\t.globl %s\n", desc_name); |
| |
| fprintf (file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); |
| fprintf (file, "%s:\n", desc_name); |
| fprintf (file, "\t.long %s\n", orig_name); |
| fputs ("\t.long _GLOBAL_OFFSET_TABLE_\n", file); |
| if (DEFAULT_ABI == ABI_AIX) |
| fputs ("\t.long 0\n", file); |
| fprintf (file, "\t.previous\n"); |
| } |
| ASM_OUTPUT_LABEL (file, name); |
| } |
| |
| static void |
| rs6000_elf_end_indicate_exec_stack (void) |
| { |
| if (TARGET_32BIT) |
| file_end_indicate_exec_stack (); |
| } |
| #endif |
| |
| #if TARGET_XCOFF |
| static void |
| rs6000_xcoff_asm_output_anchor (rtx symbol) |
| { |
| char buffer[100]; |
| |
| sprintf (buffer, "$ + " HOST_WIDE_INT_PRINT_DEC, |
| SYMBOL_REF_BLOCK_OFFSET (symbol)); |
| ASM_OUTPUT_DEF (asm_out_file, XSTR (symbol, 0), buffer); |
| } |
| |
| static void |
| rs6000_xcoff_asm_globalize_label (FILE *stream, const char *name) |
| { |
| fputs (GLOBAL_ASM_OP, stream); |
| RS6000_OUTPUT_BASENAME (stream, name); |
| putc ('\n', stream); |
| } |
| |
| /* A get_unnamed_decl callback, used for read-only sections. PTR |
| points to the section string variable. */ |
| |
| static void |
| rs6000_xcoff_output_readonly_section_asm_op (const void *directive) |
| { |
| fprintf (asm_out_file, "\t.csect %s[RO],3\n", |
| *(const char *const *) directive); |
| } |
| |
| /* Likewise for read-write sections. */ |
| |
| static void |
| rs6000_xcoff_output_readwrite_section_asm_op (const void *directive) |
| { |
| fprintf (asm_out_file, "\t.csect %s[RW],3\n", |
| *(const char *const *) directive); |
| } |
| |
| /* A get_unnamed_section callback, used for switching to toc_section. */ |
| |
| static void |
| rs6000_xcoff_output_toc_section_asm_op (const void *data ATTRIBUTE_UNUSED) |
| { |
| if (TARGET_MINIMAL_TOC) |
| { |
| /* toc_section is always selected at least once from |
| rs6000_xcoff_file_start, so this is guaranteed to |
| always be defined once and only once in each file. */ |
| if (!toc_initialized) |
| { |
| fputs ("\t.toc\nLCTOC..1:\n", asm_out_file); |
| fputs ("\t.tc toc_table[TC],toc_table[RW]\n", asm_out_file); |
| toc_initialized = 1; |
| } |
| fprintf (asm_out_file, "\t.csect toc_table[RW]%s\n", |
| (TARGET_32BIT ? "" : ",3")); |
| } |
| else |
| fputs ("\t.toc\n", asm_out_file); |
| } |
| |
| /* Implement TARGET_ASM_INIT_SECTIONS. */ |
| |
| static void |
| rs6000_xcoff_asm_init_sections (void) |
| { |
| read_only_data_section |
| = get_unnamed_section (0, rs6000_xcoff_output_readonly_section_asm_op, |
| &xcoff_read_only_section_name); |
| |
| private_data_section |
| = get_unnamed_section (SECTION_WRITE, |
| rs6000_xcoff_output_readwrite_section_asm_op, |
| &xcoff_private_data_section_name); |
| |
| read_only_private_data_section |
| = get_unnamed_section (0, rs6000_xcoff_output_readonly_section_asm_op, |
| &xcoff_private_data_section_name); |
| |
| toc_section |
| = get_unnamed_section (0, rs6000_xcoff_output_toc_section_asm_op, NULL); |
| |
| readonly_data_section = read_only_data_section; |
| exception_section = data_section; |
| } |
| |
| static int |
| rs6000_xcoff_reloc_rw_mask (void) |
| { |
| return 3; |
| } |
| |
| static void |
| rs6000_xcoff_asm_named_section (const char *name, unsigned int flags, |
| tree decl ATTRIBUTE_UNUSED) |
| { |
| int smclass; |
| static const char * const suffix[3] = { "PR", "RO", "RW" }; |
| |
| if (flags & SECTION_CODE) |
| smclass = 0; |
| else if (flags & SECTION_WRITE) |
| smclass = 2; |
| else |
| smclass = 1; |
| |
| fprintf (asm_out_file, "\t.csect %s%s[%s],%u\n", |
| (flags & SECTION_CODE) ? "." : "", |
| name, suffix[smclass], flags & SECTION_ENTSIZE); |
| } |
| |
| static section * |
| rs6000_xcoff_select_section (tree decl, int reloc, |
| unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED) |
| { |
| if (decl_readonly_section (decl, reloc)) |
| { |
| if (TREE_PUBLIC (decl)) |
| return read_only_data_section; |
| else |
| return read_only_private_data_section; |
| } |
| else |
| { |
| if (TREE_PUBLIC (decl)) |
| return data_section; |
| else |
| return private_data_section; |
| } |
| } |
| |
| static void |
| rs6000_xcoff_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED) |
| { |
| const char *name; |
| |
| /* Use select_section for private and uninitialized data. */ |
| if (!TREE_PUBLIC (decl) |
| || DECL_COMMON (decl) |
| || DECL_INITIAL (decl) == NULL_TREE |
| || DECL_INITIAL (decl) == error_mark_node |
| || (flag_zero_initialized_in_bss |
| && initializer_zerop (DECL_INITIAL (decl)))) |
| return; |
| |
| name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| name = (*targetm.strip_name_encoding) (name); |
| DECL_SECTION_NAME (decl) = build_string (strlen (name), name); |
| } |
| |
| /* Select section for constant in constant pool. |
| |
| On RS/6000, all constants are in the private read-only data area. |
| However, if this is being placed in the TOC it must be output as a |
| toc entry. */ |
| |
| static section * |
| rs6000_xcoff_select_rtx_section (enum machine_mode mode, rtx x, |
| unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED) |
| { |
| if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (x, mode)) |
| return toc_section; |
| else |
| return read_only_private_data_section; |
| } |
| |
| /* Remove any trailing [DS] or the like from the symbol name. */ |
| |
| static const char * |
| rs6000_xcoff_strip_name_encoding (const char *name) |
| { |
| size_t len; |
| if (*name == '*') |
| name++; |
| len = strlen (name); |
| if (name[len - 1] == ']') |
| return ggc_alloc_string (name, len - 4); |
| else |
| return name; |
| } |
| |
| /* Section attributes. AIX is always PIC. */ |
| |
| static unsigned int |
| rs6000_xcoff_section_type_flags (tree decl, const char *name, int reloc) |
| { |
| unsigned int align; |
| unsigned int flags = default_section_type_flags (decl, name, reloc); |
| |
| /* Align to at least UNIT size. */ |
| if (flags & SECTION_CODE) |
| align = MIN_UNITS_PER_WORD; |
| else |
| /* Increase alignment of large objects if not already stricter. */ |
| align = MAX ((DECL_ALIGN (decl) / BITS_PER_UNIT), |
| int_size_in_bytes (TREE_TYPE (decl)) > MIN_UNITS_PER_WORD |
| ? UNITS_PER_FP_WORD : MIN_UNITS_PER_WORD); |
| |
| return flags | (exact_log2 (align) & SECTION_ENTSIZE); |
| } |
| |
| /* Output at beginning of assembler file. |
| |
| Initialize the section names for the RS/6000 at this point. |
| |
| Specify filename, including full path, to assembler. |
| |
| We want to go into the TOC section so at least one .toc will be emitted. |
| Also, in order to output proper .bs/.es pairs, we need at least one static |
| [RW] section emitted. |
| |
| Finally, declare mcount when profiling to make the assembler happy. */ |
| |
| static void |
| rs6000_xcoff_file_start (void) |
| { |
| rs6000_gen_section_name (&xcoff_bss_section_name, |
| main_input_filename, ".bss_"); |
| rs6000_gen_section_name (&xcoff_private_data_section_name, |
| main_input_filename, ".rw_"); |
| rs6000_gen_section_name (&xcoff_read_only_section_name, |
| main_input_filename, ".ro_"); |
| |
| fputs ("\t.file\t", asm_out_file); |
| output_quoted_string (asm_out_file, main_input_filename); |
| fputc ('\n', asm_out_file); |
| if (write_symbols != NO_DEBUG) |
| switch_to_section (private_data_section); |
| switch_to_section (text_section); |
| if (profile_flag) |
| fprintf (asm_out_file, "\t.extern %s\n", RS6000_MCOUNT); |
| rs6000_file_start (); |
| } |
| |
| /* Output at end of assembler file. |
| On the RS/6000, referencing data should automatically pull in text. */ |
| |
| static void |
| rs6000_xcoff_file_end (void) |
| { |
| switch_to_section (text_section); |
| fputs ("_section_.text:\n", asm_out_file); |
| switch_to_section (data_section); |
| fputs (TARGET_32BIT |
| ? "\t.long _section_.text\n" : "\t.llong _section_.text\n", |
| asm_out_file); |
| } |
| #endif /* TARGET_XCOFF */ |
| |
| /* APPLE LOCAL begin pragma reverse_bitfields */ |
| #if TARGET_MACHO |
| /* Pragma reverse_bitfields. For compatibility with CW. |
| This feature is not well defined by CW, and results in |
| code that does not work in some cases! Bug compatibility |
| is the requirement, however. */ |
| |
| static bool |
| rs6000_reverse_bitfields_p (tree record_type ATTRIBUTE_UNUSED) |
| { |
| return darwin_reverse_bitfields; |
| } |
| #endif |
| /* APPLE LOCAL end prgama reverse_bitfields */ |
| |
| /* Compute a (partial) cost for rtx X. Return true if the complete |
| cost has been computed, and false if subexpressions should be |
| scanned. In either case, *TOTAL contains the cost result. */ |
| |
| static bool |
| rs6000_rtx_costs (rtx x, int code, int outer_code, int *total) |
| { |
| enum machine_mode mode = GET_MODE (x); |
| |
| switch (code) |
| { |
| /* On the RS/6000, if it is valid in the insn, it is free. */ |
| case CONST_INT: |
| if (((outer_code == SET |
| || outer_code == PLUS |
| || outer_code == MINUS) |
| && (satisfies_constraint_I (x) |
| || satisfies_constraint_L (x))) |
| || (outer_code == AND |
| && (satisfies_constraint_K (x) |
| || (mode == SImode |
| ? satisfies_constraint_L (x) |
| : satisfies_constraint_J (x)) |
| || mask_operand (x, mode) |
| || (mode == DImode |
| && mask64_operand (x, DImode)))) |
| || ((outer_code == IOR || outer_code == XOR) |
| && (satisfies_constraint_K (x) |
| || (mode == SImode |
| ? satisfies_constraint_L (x) |
| : satisfies_constraint_J (x)))) |
| || outer_code == ASHIFT |
| || outer_code == ASHIFTRT |
| || outer_code == LSHIFTRT |
| || outer_code == ROTATE |
| || outer_code == ROTATERT |
| || outer_code == ZERO_EXTRACT |
| || (outer_code == MULT |
| && satisfies_constraint_I (x)) |
| || ((outer_code == DIV || outer_code == UDIV |
| || outer_code == MOD || outer_code == UMOD) |
| && exact_log2 (INTVAL (x)) >= 0) |
| || (outer_code == COMPARE |
| && (satisfies_constraint_I (x) |
| || satisfies_constraint_K (x))) |
| || (outer_code == EQ |
| && (satisfies_constraint_I (x) |
| || satisfies_constraint_K (x) |
| || (mode == SImode |
| ? satisfies_constraint_L (x) |
| : satisfies_constraint_J (x)))) |
| || (outer_code == GTU |
| && satisfies_constraint_I (x)) |
| || (outer_code == LTU |
| && satisfies_constraint_P (x))) |
| { |
| *total = 0; |
| return true; |
| } |
| else if ((outer_code == PLUS |
| && reg_or_add_cint_operand (x, VOIDmode)) |
| || (outer_code == MINUS |
| && reg_or_sub_cint_operand (x, VOIDmode)) |
| || ((outer_code == SET |
| || outer_code == IOR |
| || outer_code == XOR) |
| && (INTVAL (x) |
| & ~ (unsigned HOST_WIDE_INT) 0xffffffff) == 0)) |
| { |
| *total = COSTS_N_INSNS (1); |
| return true; |
| } |
| /* FALLTHRU */ |
| |
| case CONST_DOUBLE: |
| if (mode == DImode && code == CONST_DOUBLE) |
| { |
| if ((outer_code == IOR || outer_code == XOR) |
| && CONST_DOUBLE_HIGH (x) == 0 |
| && (CONST_DOUBLE_LOW (x) |
| & ~ (unsigned HOST_WIDE_INT) 0xffff) == 0) |
| { |
| *total = 0; |
| return true; |
| } |
| else if ((outer_code == AND && and64_2_operand (x, DImode)) |
| || ((outer_code == SET |
| || outer_code == IOR |
| || outer_code == XOR) |
| && CONST_DOUBLE_HIGH (x) == 0)) |
| { |
| *total = COSTS_N_INSNS (1); |
| return true; |
| } |
| } |
| /* FALLTHRU */ |
| |
| case CONST: |
| case HIGH: |
| case SYMBOL_REF: |
| case MEM: |
| /* When optimizing for size, MEM should be slightly more expensive |
| than generating address, e.g., (plus (reg) (const)). |
| L1 cache latency is about two instructions. */ |
| *total = optimize_size ? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (2); |
| return true; |
| |
| case LABEL_REF: |
| *total = 0; |
| return true; |
| |
| case PLUS: |
| if (mode == DFmode) |
| { |
| if (GET_CODE (XEXP (x, 0)) == MULT) |
| { |
| /* FNMA accounted in outer NEG. */ |
| if (outer_code == NEG) |
| *total = rs6000_cost->dmul - rs6000_cost->fp; |
| else |
| *total = rs6000_cost->dmul; |
| } |
| else |
| *total = rs6000_cost->fp; |
| } |
| else if (mode == SFmode) |
| { |
| /* FNMA accounted in outer NEG. */ |
| if (outer_code == NEG && GET_CODE (XEXP (x, 0)) == MULT) |
| *total = 0; |
| else |
| *total = rs6000_cost->fp; |
| } |
| else |
| *total = COSTS_N_INSNS (1); |
| return false; |
| |
| case MINUS: |
| if (mode == DFmode) |
| { |
| if (GET_CODE (XEXP (x, 0)) == MULT) |
| { |
| /* FNMA accounted in outer NEG. */ |
| if (outer_code == NEG) |
| *total = 0; |
| else |
| *total = rs6000_cost->dmul; |
| } |
| else |
| *total = rs6000_cost->fp; |
| } |
| else if (mode == SFmode) |
| { |
| /* FNMA accounted in outer NEG. */ |
| if (outer_code == NEG && GET_CODE (XEXP (x, 0)) == MULT) |
| *total = 0; |
| else |
| *total = rs6000_cost->fp; |
| } |
| else |
| *total = COSTS_N_INSNS (1); |
| return false; |
| |
| case MULT: |
| if (GET_CODE (XEXP (x, 1)) == CONST_INT |
| && satisfies_constraint_I (XEXP (x, 1))) |
| { |
| if (INTVAL (XEXP (x, 1)) >= -256 |
| && INTVAL (XEXP (x, 1)) <= 255) |
| *total = rs6000_cost->mulsi_const9; |
| else |
| *total = rs6000_cost->mulsi_const; |
| } |
| /* FMA accounted in outer PLUS/MINUS. */ |
| else if ((mode == DFmode || mode == SFmode) |
| && (outer_code == PLUS || outer_code == MINUS)) |
| *total = 0; |
| else if (mode == DFmode) |
| *total = rs6000_cost->dmul; |
| else if (mode == SFmode) |
| *total = rs6000_cost->fp; |
| else if (mode == DImode) |
| *total = rs6000_cost->muldi; |
| else |
| *total = rs6000_cost->mulsi; |
| return false; |
| |
| case DIV: |
| case MOD: |
| if (FLOAT_MODE_P (mode)) |
| { |
| *total = mode == DFmode ? rs6000_cost->ddiv |
| : rs6000_cost->sdiv; |
| return false; |
| } |
| /* FALLTHRU */ |
| |
| case UDIV: |
| case UMOD: |
| if (GET_CODE (XEXP (x, 1)) == CONST_INT |
| && exact_log2 (INTVAL (XEXP (x, 1))) >= 0) |
| { |
| if (code == DIV || code == MOD) |
| /* Shift, addze */ |
| *total = COSTS_N_INSNS (2); |
| else |
| /* Shift */ |
| *total = COSTS_N_INSNS (1); |
| } |
| else |
| { |
| if (GET_MODE (XEXP (x, 1)) == DImode) |
| *total = rs6000_cost->divdi; |
| else |
| *total = rs6000_cost->divsi; |
| } |
| /* Add in shift and subtract for MOD. */ |
| if (code == MOD || code == UMOD) |
| *total += COSTS_N_INSNS (2); |
| return false; |
| |
| case FFS: |
| *total = COSTS_N_INSNS (4); |
| return false; |
| |
| case NOT: |
| if (outer_code == AND || outer_code == IOR || outer_code == XOR) |
| { |
| *total = 0; |
| return false; |
| } |
| /* FALLTHRU */ |
| |
| case AND: |
| case IOR: |
| case XOR: |
| case ZERO_EXTRACT: |
| *total = COSTS_N_INSNS (1); |
| return false; |
| |
| case ASHIFT: |
| case ASHIFTRT: |
| case LSHIFTRT: |
| case ROTATE: |
| case ROTATERT: |
| /* Handle mul_highpart. */ |
| if (outer_code == TRUNCATE |
| && GET_CODE (XEXP (x, 0)) == MULT) |
| { |
| if (mode == DImode) |
| *total = rs6000_cost->muldi; |
| else |
| *total = rs6000_cost->mulsi; |
| return true; |
| } |
| else if (outer_code == AND) |
| *total = 0; |
| else |
| *total = COSTS_N_INSNS (1); |
| return false; |
| |
| case SIGN_EXTEND: |
| case ZERO_EXTEND: |
| if (GET_CODE (XEXP (x, 0)) == MEM) |
| *total = 0; |
| else |
| *total = COSTS_N_INSNS (1); |
| return false; |
| |
| case COMPARE: |
| case NEG: |
| case ABS: |
| if (!FLOAT_MODE_P (mode)) |
| { |
| *total = COSTS_N_INSNS (1); |
| return false; |
| } |
| /* FALLTHRU */ |
| |
| case FLOAT: |
| case UNSIGNED_FLOAT: |
| case FIX: |
| case UNSIGNED_FIX: |
| case FLOAT_TRUNCATE: |
| *total = rs6000_cost->fp; |
| return false; |
| |
| case FLOAT_EXTEND: |
| if (mode == DFmode) |
| *total = 0; |
| else |
| *total = rs6000_cost->fp; |
| return false; |
| |
| case UNSPEC: |
| switch (XINT (x, 1)) |
| { |
| case UNSPEC_FRSP: |
| *total = rs6000_cost->fp; |
| return true; |
| |
| default: |
| break; |
| } |
| break; |
| |
| case CALL: |
| case IF_THEN_ELSE: |
| if (optimize_size) |
| { |
| *total = COSTS_N_INSNS (1); |
| return true; |
| } |
| else if (FLOAT_MODE_P (mode) |
| && TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS) |
| { |
| *total = rs6000_cost->fp; |
| return false; |
| } |
| break; |
| |
| case EQ: |
| case GTU: |
| case LTU: |
| /* Carry bit requires mode == Pmode. |
| NEG or PLUS already counted so only add one. */ |
| if (mode == Pmode |
| && (outer_code == NEG || outer_code == PLUS)) |
| { |
| *total = COSTS_N_INSNS (1); |
| return true; |
| } |
| if (outer_code == SET) |
| { |
| if (XEXP (x, 1) == const0_rtx) |
| { |
| *total = COSTS_N_INSNS (2); |
| return true; |
| } |
| else if (mode == Pmode) |
| { |
| *total = COSTS_N_INSNS (3); |
| return false; |
| } |
| } |
| /* FALLTHRU */ |
| |
| case GT: |
| case LT: |
| case UNORDERED: |
| if (outer_code == SET && (XEXP (x, 1) == const0_rtx)) |
| { |
| *total = COSTS_N_INSNS (2); |
| return true; |
| } |
| /* CC COMPARE. */ |
| if (outer_code == COMPARE) |
| { |
| *total = 0; |
| return true; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* A C expression returning the cost of moving data from a register of class |
| CLASS1 to one of CLASS2. */ |
| |
| int |
| rs6000_register_move_cost (enum machine_mode mode, |
| enum reg_class from, enum reg_class to) |
| { |
| /* Moves from/to GENERAL_REGS. */ |
| if (reg_classes_intersect_p (to, GENERAL_REGS) |
| || reg_classes_intersect_p (from, GENERAL_REGS)) |
| { |
| if (! reg_classes_intersect_p (to, GENERAL_REGS)) |
| from = to; |
| |
| if (from == FLOAT_REGS || from == ALTIVEC_REGS) |
| return (rs6000_memory_move_cost (mode, from, 0) |
| + rs6000_memory_move_cost (mode, GENERAL_REGS, 0)); |
| |
| /* It's more expensive to move CR_REGS than CR0_REGS because of the |
| shift. */ |
| else if (from == CR_REGS) |
| return 4; |
| |
| else |
| /* A move will cost one instruction per GPR moved. */ |
| return 2 * hard_regno_nregs[0][mode]; |
| } |
| |
| /* Moving between two similar registers is just one instruction. */ |
| else if (reg_classes_intersect_p (to, from)) |
| return mode == TFmode ? 4 : 2; |
| |
| /* Everything else has to go through GENERAL_REGS. */ |
| else |
| return (rs6000_register_move_cost (mode, GENERAL_REGS, to) |
| + rs6000_register_move_cost (mode, from, GENERAL_REGS)); |
| } |
| |
| /* A C expressions returning the cost of moving data of MODE from a register to |
| or from memory. */ |
| |
| int |
| rs6000_memory_move_cost (enum machine_mode mode, enum reg_class class, |
| int in ATTRIBUTE_UNUSED) |
| { |
| if (reg_classes_intersect_p (class, GENERAL_REGS)) |
| return 4 * hard_regno_nregs[0][mode]; |
| else if (reg_classes_intersect_p (class, FLOAT_REGS)) |
| return 4 * hard_regno_nregs[32][mode]; |
| else if (reg_classes_intersect_p (class, ALTIVEC_REGS)) |
| return 4 * hard_regno_nregs[FIRST_ALTIVEC_REGNO][mode]; |
| else |
| return 4 + rs6000_register_move_cost (mode, class, GENERAL_REGS); |
| } |
| |
| /* Newton-Raphson approximation of single-precision floating point divide n/d. |
| Assumes no trapping math and finite arguments. */ |
| |
| void |
| rs6000_emit_swdivsf (rtx res, rtx n, rtx d) |
| { |
| rtx x0, e0, e1, y1, u0, v0, one; |
| |
| x0 = gen_reg_rtx (SFmode); |
| e0 = gen_reg_rtx (SFmode); |
| e1 = gen_reg_rtx (SFmode); |
| y1 = gen_reg_rtx (SFmode); |
| u0 = gen_reg_rtx (SFmode); |
| v0 = gen_reg_rtx (SFmode); |
| one = force_reg (SFmode, CONST_DOUBLE_FROM_REAL_VALUE (dconst1, SFmode)); |
| |
| /* x0 = 1./d estimate */ |
| emit_insn (gen_rtx_SET (VOIDmode, x0, |
| gen_rtx_UNSPEC (SFmode, gen_rtvec (1, d), |
| UNSPEC_FRES))); |
| /* e0 = 1. - d * x0 */ |
| emit_insn (gen_rtx_SET (VOIDmode, e0, |
| gen_rtx_MINUS (SFmode, one, |
| gen_rtx_MULT (SFmode, d, x0)))); |
| /* e1 = e0 + e0 * e0 */ |
| emit_insn (gen_rtx_SET (VOIDmode, e1, |
| gen_rtx_PLUS (SFmode, |
| gen_rtx_MULT (SFmode, e0, e0), e0))); |
| /* y1 = x0 + e1 * x0 */ |
| emit_insn (gen_rtx_SET (VOIDmode, y1, |
| gen_rtx_PLUS (SFmode, |
| gen_rtx_MULT (SFmode, e1, x0), x0))); |
| /* u0 = n * y1 */ |
| emit_insn (gen_rtx_SET (VOIDmode, u0, |
| gen_rtx_MULT (SFmode, n, y1))); |
| /* v0 = n - d * u0 */ |
| emit_insn (gen_rtx_SET (VOIDmode, v0, |
| gen_rtx_MINUS (SFmode, n, |
| gen_rtx_MULT (SFmode, d, u0)))); |
| /* res = u0 + v0 * y1 */ |
| emit_insn (gen_rtx_SET (VOIDmode, res, |
| gen_rtx_PLUS (SFmode, |
| gen_rtx_MULT (SFmode, v0, y1), u0))); |
| } |
| |
| /* Newton-Raphson approximation of double-precision floating point divide n/d. |
| Assumes no trapping math and finite arguments. */ |
| |
| void |
| rs6000_emit_swdivdf (rtx res, rtx n, rtx d) |
| { |
| rtx x0, e0, e1, e2, y1, y2, y3, u0, v0, one; |
| |
| x0 = gen_reg_rtx (DFmode); |
| e0 = gen_reg_rtx (DFmode); |
| e1 = gen_reg_rtx (DFmode); |
| e2 = gen_reg_rtx (DFmode); |
| y1 = gen_reg_rtx (DFmode); |
| y2 = gen_reg_rtx (DFmode); |
| y3 = gen_reg_rtx (DFmode); |
| u0 = gen_reg_rtx (DFmode); |
| v0 = gen_reg_rtx (DFmode); |
| one = force_reg (DFmode, CONST_DOUBLE_FROM_REAL_VALUE (dconst1, DFmode)); |
| |
| /* x0 = 1./d estimate */ |
| emit_insn (gen_rtx_SET (VOIDmode, x0, |
| gen_rtx_UNSPEC (DFmode, gen_rtvec (1, d), |
| UNSPEC_FRES))); |
| /* e0 = 1. - d * x0 */ |
| emit_insn (gen_rtx_SET (VOIDmode, e0, |
| gen_rtx_MINUS (DFmode, one, |
| gen_rtx_MULT (SFmode, d, x0)))); |
| /* y1 = x0 + e0 * x0 */ |
| emit_insn (gen_rtx_SET (VOIDmode, y1, |
| gen_rtx_PLUS (DFmode, |
| gen_rtx_MULT (DFmode, e0, x0), x0))); |
| /* e1 = e0 * e0 */ |
| emit_insn (gen_rtx_SET (VOIDmode, e1, |
| gen_rtx_MULT (DFmode, e0, e0))); |
| /* y2 = y1 + e1 * y1 */ |
| emit_insn (gen_rtx_SET (VOIDmode, y2, |
| gen_rtx_PLUS (DFmode, |
| gen_rtx_MULT (DFmode, e1, y1), y1))); |
| /* e2 = e1 * e1 */ |
| emit_insn (gen_rtx_SET (VOIDmode, e2, |
| gen_rtx_MULT (DFmode, e1, e1))); |
| /* y3 = y2 + e2 * y2 */ |
| emit_insn (gen_rtx_SET (VOIDmode, y3, |
| gen_rtx_PLUS (DFmode, |
| gen_rtx_MULT (DFmode, e2, y2), y2))); |
| /* u0 = n * y3 */ |
| emit_insn (gen_rtx_SET (VOIDmode, u0, |
| gen_rtx_MULT (DFmode, n, y3))); |
| /* v0 = n - d * u0 */ |
| emit_insn (gen_rtx_SET (VOIDmode, v0, |
| gen_rtx_MINUS (DFmode, n, |
| gen_rtx_MULT (DFmode, d, u0)))); |
| /* res = u0 + v0 * y3 */ |
| emit_insn (gen_rtx_SET (VOIDmode, res, |
| gen_rtx_PLUS (DFmode, |
| gen_rtx_MULT (DFmode, v0, y3), u0))); |
| } |
| |
| /* Return an RTX representing where to find the function value of a |
| function returning MODE. */ |
| static rtx |
| rs6000_complex_function_value (enum machine_mode mode) |
| { |
| unsigned int regno; |
| rtx r1, r2; |
| enum machine_mode inner = GET_MODE_INNER (mode); |
| unsigned int inner_bytes = GET_MODE_SIZE (inner); |
| |
| if (FLOAT_MODE_P (mode) && TARGET_HARD_FLOAT && TARGET_FPRS) |
| regno = FP_ARG_RETURN; |
| else |
| { |
| regno = GP_ARG_RETURN; |
| |
| /* 32-bit is OK since it'll go in r3/r4. */ |
| if (TARGET_32BIT && inner_bytes >= 4) |
| return gen_rtx_REG (mode, regno); |
| } |
| |
| if (inner_bytes >= 8) |
| return gen_rtx_REG (mode, regno); |
| |
| r1 = gen_rtx_EXPR_LIST (inner, gen_rtx_REG (inner, regno), |
| const0_rtx); |
| r2 = gen_rtx_EXPR_LIST (inner, gen_rtx_REG (inner, regno + 1), |
| GEN_INT (inner_bytes)); |
| return gen_rtx_PARALLEL (mode, gen_rtvec (2, r1, r2)); |
| } |
| |
| /* Define how to find the value returned by a function. |
| VALTYPE is the data type of the value (as a tree). |
| If the precise function being called is known, FUNC is its FUNCTION_DECL; |
| otherwise, FUNC is 0. |
| |
| On the SPE, both FPs and vectors are returned in r3. |
| |
| On RS/6000 an integer value is in r3 and a floating-point value is in |
| fp1, unless -msoft-float. */ |
| |
| rtx |
| rs6000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED) |
| { |
| enum machine_mode mode; |
| unsigned int regno; |
| |
| /* Special handling for structs in darwin64. */ |
| if (rs6000_darwin64_abi |
| && TYPE_MODE (valtype) == BLKmode |
| && TREE_CODE (valtype) == RECORD_TYPE |
| && int_size_in_bytes (valtype) > 0) |
| { |
| CUMULATIVE_ARGS valcum; |
| rtx valret; |
| |
| valcum.words = 0; |
| valcum.fregno = FP_ARG_MIN_REG; |
| valcum.vregno = ALTIVEC_ARG_MIN_REG; |
| /* Do a trial code generation as if this were going to be passed as |
| an argument; if any part goes in memory, we return NULL. */ |
| valret = rs6000_darwin64_record_arg (&valcum, valtype, 1, true); |
| if (valret) |
| return valret; |
| /* Otherwise fall through to standard ABI rules. */ |
| } |
| |
| if (TARGET_32BIT && TARGET_POWERPC64 && TYPE_MODE (valtype) == DImode) |
| { |
| /* Long long return value need be split in -mpowerpc64, 32bit ABI. */ |
| return gen_rtx_PARALLEL (DImode, |
| gen_rtvec (2, |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (SImode, GP_ARG_RETURN), |
| const0_rtx), |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (SImode, |
| GP_ARG_RETURN + 1), |
| GEN_INT (4)))); |
| } |
| if (TARGET_32BIT && TARGET_POWERPC64 && TYPE_MODE (valtype) == DCmode) |
| { |
| return gen_rtx_PARALLEL (DCmode, |
| gen_rtvec (4, |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (SImode, GP_ARG_RETURN), |
| const0_rtx), |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (SImode, |
| GP_ARG_RETURN + 1), |
| GEN_INT (4)), |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (SImode, |
| GP_ARG_RETURN + 2), |
| GEN_INT (8)), |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (SImode, |
| GP_ARG_RETURN + 3), |
| GEN_INT (12)))); |
| } |
| |
| mode = TYPE_MODE (valtype); |
| if ((INTEGRAL_TYPE_P (valtype) && GET_MODE_BITSIZE (mode) < BITS_PER_WORD) |
| || POINTER_TYPE_P (valtype)) |
| mode = TARGET_32BIT ? SImode : DImode; |
| |
| if (DECIMAL_FLOAT_MODE_P (mode)) |
| regno = GP_ARG_RETURN; |
| else if (SCALAR_FLOAT_TYPE_P (valtype) && TARGET_HARD_FLOAT && TARGET_FPRS) |
| regno = FP_ARG_RETURN; |
| else if (TREE_CODE (valtype) == COMPLEX_TYPE |
| && targetm.calls.split_complex_arg) |
| return rs6000_complex_function_value (mode); |
| else if (TREE_CODE (valtype) == VECTOR_TYPE |
| && TARGET_ALTIVEC && TARGET_ALTIVEC_ABI |
| && ALTIVEC_VECTOR_MODE (mode)) |
| regno = ALTIVEC_ARG_RETURN; |
| else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT |
| && (mode == DFmode || mode == DCmode)) |
| return spe_build_register_parallel (mode, GP_ARG_RETURN); |
| else |
| regno = GP_ARG_RETURN; |
| |
| return gen_rtx_REG (mode, regno); |
| } |
| |
| /* Define how to find the value returned by a library function |
| assuming the value has mode MODE. */ |
| rtx |
| rs6000_libcall_value (enum machine_mode mode) |
| { |
| unsigned int regno; |
| |
| if (TARGET_32BIT && TARGET_POWERPC64 && mode == DImode) |
| { |
| /* Long long return value need be split in -mpowerpc64, 32bit ABI. */ |
| return gen_rtx_PARALLEL (DImode, |
| gen_rtvec (2, |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (SImode, GP_ARG_RETURN), |
| const0_rtx), |
| gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (SImode, |
| GP_ARG_RETURN + 1), |
| GEN_INT (4)))); |
| } |
| |
| if (DECIMAL_FLOAT_MODE_P (mode)) |
| regno = GP_ARG_RETURN; |
| else if (SCALAR_FLOAT_MODE_P (mode) |
| && TARGET_HARD_FLOAT && TARGET_FPRS) |
| regno = FP_ARG_RETURN; |
| else if (ALTIVEC_VECTOR_MODE (mode) |
| && TARGET_ALTIVEC && TARGET_ALTIVEC_ABI) |
| regno = ALTIVEC_ARG_RETURN; |
| else if (COMPLEX_MODE_P (mode) && targetm.calls.split_complex_arg) |
| return rs6000_complex_function_value (mode); |
| else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT |
| && (mode == DFmode || mode == DCmode)) |
| return spe_build_register_parallel (mode, GP_ARG_RETURN); |
| else |
| regno = GP_ARG_RETURN; |
| |
| return gen_rtx_REG (mode, regno); |
| } |
| |
| /* Define the offset between two registers, FROM to be eliminated and its |
| replacement TO, at the start of a routine. */ |
| HOST_WIDE_INT |
| rs6000_initial_elimination_offset (int from, int to) |
| { |
| rs6000_stack_t *info = rs6000_stack_info (); |
| HOST_WIDE_INT offset; |
| |
| if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| offset = info->push_p ? 0 : -info->total_size; |
| else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| { |
| offset = info->push_p ? 0 : -info->total_size; |
| if (FRAME_GROWS_DOWNWARD) |
| offset += info->fixed_size + info->vars_size + info->parm_size; |
| } |
| else if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) |
| offset = FRAME_GROWS_DOWNWARD |
| ? info->fixed_size + info->vars_size + info->parm_size |
| : 0; |
| else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) |
| offset = info->total_size; |
| else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) |
| offset = info->push_p ? info->total_size : 0; |
| else if (from == RS6000_PIC_OFFSET_TABLE_REGNUM) |
| offset = 0; |
| else |
| gcc_unreachable (); |
| |
| return offset; |
| } |
| |
| /* Return true if TYPE is a SPE or AltiVec opaque type. */ |
| |
| static bool |
| rs6000_is_opaque_type (tree type) |
| { |
| return (type == opaque_V2SI_type_node |
| || type == opaque_V2SF_type_node |
| || type == opaque_p_V2SI_type_node |
| || type == opaque_V4SI_type_node); |
| } |
| |
| static rtx |
| rs6000_dwarf_register_span (rtx reg) |
| { |
| unsigned regno; |
| |
| if (TARGET_SPE |
| && (SPE_VECTOR_MODE (GET_MODE (reg)) |
| || (TARGET_E500_DOUBLE && GET_MODE (reg) == DFmode))) |
| ; |
| else |
| return NULL_RTX; |
| |
| regno = REGNO (reg); |
| |
| /* The duality of the SPE register size wreaks all kinds of havoc. |
| This is a way of distinguishing r0 in 32-bits from r0 in |
| 64-bits. */ |
| return |
| gen_rtx_PARALLEL (VOIDmode, |
| BYTES_BIG_ENDIAN |
| ? gen_rtvec (2, |
| gen_rtx_REG (SImode, regno + 1200), |
| gen_rtx_REG (SImode, regno)) |
| : gen_rtvec (2, |
| gen_rtx_REG (SImode, regno), |
| gen_rtx_REG (SImode, regno + 1200))); |
| } |
| |
| /* Map internal gcc register numbers to DWARF2 register numbers. */ |
| |
| unsigned int |
| rs6000_dbx_register_number (unsigned int regno) |
| { |
| if (regno <= 63 || write_symbols != DWARF2_DEBUG) |
| return regno; |
| if (regno == MQ_REGNO) |
| return 100; |
| if (regno == LINK_REGISTER_REGNUM) |
| return 108; |
| if (regno == COUNT_REGISTER_REGNUM) |
| return 109; |
| if (CR_REGNO_P (regno)) |
| return regno - CR0_REGNO + 86; |
| if (regno == XER_REGNO) |
| return 101; |
| if (ALTIVEC_REGNO_P (regno)) |
| return regno - FIRST_ALTIVEC_REGNO + 1124; |
| if (regno == VRSAVE_REGNO) |
| return 356; |
| if (regno == VSCR_REGNO) |
| return 67; |
| if (regno == SPE_ACC_REGNO) |
| return 99; |
| if (regno == SPEFSCR_REGNO) |
| return 612; |
| /* SPE high reg number. We get these values of regno from |
| rs6000_dwarf_register_span. */ |
| gcc_assert (regno >= 1200 && regno < 1232); |
| return regno; |
| } |
| |
| /* APPLE LOCAL begin CW asm blocks */ |
| /* Translate some register names seen in CW asm into GCC standard |
| forms. */ |
| |
| const char * |
| rs6000_iasm_register_name (const char *regname, char *buf) |
| { |
| /* SP is a valid reg name, but asm doesn't like it yet, so translate. */ |
| if (strcmp (regname, "sp") == 0) |
| return "r1"; |
| if (decode_reg_name (regname) >= 0) |
| return regname; |
| /* Change "gpr0" to "r0". */ |
| if (regname[0] == 'g' |
| && regname[1] == 'p' |
| && decode_reg_name (regname + 2) >= 0) |
| return regname + 2; |
| /* Change "fp0" to "f0". */ |
| if (regname[0] == 'f' && regname[1] == 'p') |
| { |
| buf[0] = 'f'; |
| strcpy (buf + 1, regname + 2); |
| if (decode_reg_name (buf) >= 0) |
| return buf; |
| } |
| if (regname[0] == 's' |
| && regname[1] == 'p' |
| && regname[2] == 'r' |
| ) |
| /* Temp hack, return it as a number. */ |
| return regname + 3; |
| if (strcmp (regname, "RTOC") == 0) |
| return "r2"; |
| return NULL; |
| } |
| |
| extern bool iasm_memory_clobber (const char *); |
| /* Return true iff the opcode wants memory to be stable. We arrange |
| for a memory clobber in these instances. */ |
| bool |
| iasm_memory_clobber (const char *ARG_UNUSED (opcode)) |
| { |
| return strncmp (opcode, "st", 2) == 0 |
| || (strncmp (opcode, "l", 1) == 0 && (strcmp (opcode, "la") != 0 |
| && strcmp (opcode, "li") != 0 |
| && strcmp (opcode, "lis") != 0)) |
| || strcmp (opcode, "sc") == 0 |
| || strncmp (opcode, "td", 2) == 0 |
| || strcmp (opcode, "trap") == 0 |
| || strncmp (opcode, "tw", 2) == 0; |
| } |
| /* APPLE LOCAL end CW asm blocks */ |
| |
| /* target hook eh_return_filter_mode */ |
| static enum machine_mode |
| rs6000_eh_return_filter_mode (void) |
| { |
| return TARGET_32BIT ? SImode : word_mode; |
| } |
| |
| /* Target hook for scalar_mode_supported_p. */ |
| static bool |
| rs6000_scalar_mode_supported_p (enum machine_mode mode) |
| { |
| if (DECIMAL_FLOAT_MODE_P (mode)) |
| return true; |
| else |
| return default_scalar_mode_supported_p (mode); |
| } |
| |
| /* Target hook for vector_mode_supported_p. */ |
| static bool |
| rs6000_vector_mode_supported_p (enum machine_mode mode) |
| { |
| |
| if (TARGET_SPE && SPE_VECTOR_MODE (mode)) |
| return true; |
| |
| else if (TARGET_ALTIVEC && ALTIVEC_VECTOR_MODE (mode)) |
| return true; |
| |
| else |
| return false; |
| } |
| |
| /* Target hook for invalid_arg_for_unprototyped_fn. */ |
| static const char * |
| invalid_arg_for_unprototyped_fn (tree typelist, tree funcdecl, tree val) |
| { |
| return (!rs6000_darwin64_abi |
| && typelist == 0 |
| && TREE_CODE (TREE_TYPE (val)) == VECTOR_TYPE |
| && (funcdecl == NULL_TREE |
| || (TREE_CODE (funcdecl) == FUNCTION_DECL |
| && DECL_BUILT_IN_CLASS (funcdecl) != BUILT_IN_MD))) |
| ? N_("AltiVec argument passed to unprototyped function") |
| : NULL; |
| } |
| |
| /* For TARGET_SECURE_PLT 32-bit PIC code we can save PIC register |
| setup by using __stack_chk_fail_local hidden function instead of |
| calling __stack_chk_fail directly. Otherwise it is better to call |
| __stack_chk_fail directly. */ |
| |
| static tree |
| rs6000_stack_protect_fail (void) |
| { |
| return (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic) |
| ? default_hidden_stack_protect_fail () |
| : default_external_stack_protect_fail (); |
| } |
| |
| /* APPLE LOCAL begin 3399553 */ |
| /* Calculate the value of FLT_ROUNDS into DEST. |
| |
| The rounding mode is in bits 30:31 of FPSCR, and has the following |
| settings: |
| 00 Round to nearest |
| 01 Round to 0 |
| 10 Round to +inf |
| 11 Round to -inf |
| |
| FLT_ROUNDS, on the other hand, expects the following: |
| -1 Undefined |
| 0 Round to 0 |
| 1 Round to nearest |
| 2 Round to +inf |
| 3 Round to -inf |
| |
| To perform the conversion, we do: |
| ((FPSCR & 0x3) ^ ((~FPSCR & 0x3) >> 1)) |
| */ |
| extern void |
| rs6000_expand_flt_rounds (rtx dest) |
| { |
| if (TARGET_HARD_FLOAT && TARGET_FPRS) |
| { |
| rtx mem = assign_stack_temp (DFmode, GET_MODE_SIZE (DFmode), 0); |
| rtx temp_fp = gen_reg_rtx (DFmode); |
| rtx temp_int = gen_reg_rtx (SImode); |
| |
| /* Step #1: Read FPSCR. Unfortunately, this can only be done into |
| bits 32:63 of a FP reg. */ |
| emit_insn (gen_mffs (temp_fp)); |
| |
| /* Step #2: Copy onto a stack temp. */ |
| emit_move_insn (mem, temp_fp); |
| |
| /* Step #3: Copy into an integer register. */ |
| emit_move_insn (dest, adjust_address (mem, SImode, |
| WORDS_BIG_ENDIAN ? 4 : 0)); |
| |
| /* Step #4: Perform conversion described above. */ |
| emit_insn (gen_one_cmplsi2 (temp_int, dest)); |
| emit_insn (gen_andsi3 (dest, dest, GEN_INT (0x3))); |
| emit_insn (gen_andsi3 (temp_int, temp_int, GEN_INT (0x3))); |
| emit_insn (gen_lshrsi3 (temp_int, temp_int, const1_rtx)); |
| emit_insn (gen_xorsi3 (dest, dest, temp_int)); |
| } |
| else |
| { |
| /* Default: return 1 (round to nearest). */ |
| emit_move_insn (dest, const1_rtx); |
| } |
| } |
| /* APPLE LOCAL end 3399553 */ |
| |
| #include "gt-rs6000.h" |