| /* Subroutines used for code generation on IBM RS/6000. |
| Copyright (C) 1991-2015 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 3, 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 COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "rtl.h" |
| #include "regs.h" |
| #include "hard-reg-set.h" |
| #include "insn-config.h" |
| #include "conditions.h" |
| #include "insn-attr.h" |
| #include "flags.h" |
| #include "recog.h" |
| #include "obstack.h" |
| #include "hash-set.h" |
| #include "machmode.h" |
| #include "vec.h" |
| #include "double-int.h" |
| #include "input.h" |
| #include "alias.h" |
| #include "symtab.h" |
| #include "wide-int.h" |
| #include "inchash.h" |
| #include "tree.h" |
| #include "fold-const.h" |
| #include "stringpool.h" |
| #include "stor-layout.h" |
| #include "calls.h" |
| #include "print-tree.h" |
| #include "varasm.h" |
| #include "hashtab.h" |
| #include "function.h" |
| #include "statistics.h" |
| #include "real.h" |
| #include "fixed-value.h" |
| #include "expmed.h" |
| #include "dojump.h" |
| #include "explow.h" |
| #include "emit-rtl.h" |
| #include "stmt.h" |
| #include "expr.h" |
| #include "insn-codes.h" |
| #include "optabs.h" |
| #include "except.h" |
| #include "output.h" |
| #include "dbxout.h" |
| #include "predict.h" |
| #include "dominance.h" |
| #include "cfg.h" |
| #include "cfgrtl.h" |
| #include "cfganal.h" |
| #include "lcm.h" |
| #include "cfgbuild.h" |
| #include "cfgcleanup.h" |
| #include "basic-block.h" |
| #include "diagnostic-core.h" |
| #include "toplev.h" |
| #include "ggc.h" |
| #include "tm_p.h" |
| #include "target.h" |
| #include "target-def.h" |
| #include "common/common-target.h" |
| #include "langhooks.h" |
| #include "reload.h" |
| #include "cfgloop.h" |
| #include "sched-int.h" |
| #include "hash-table.h" |
| #include "tree-ssa-alias.h" |
| #include "internal-fn.h" |
| #include "gimple-fold.h" |
| #include "tree-eh.h" |
| #include "gimple-expr.h" |
| #include "is-a.h" |
| #include "gimple.h" |
| #include "gimplify.h" |
| #include "gimple-iterator.h" |
| #include "gimple-walk.h" |
| #include "intl.h" |
| #include "params.h" |
| #include "tm-constrs.h" |
| #include "ira.h" |
| #include "opts.h" |
| #include "tree-vectorizer.h" |
| #include "dumpfile.h" |
| #include "hash-map.h" |
| #include "plugin-api.h" |
| #include "ipa-ref.h" |
| #include "cgraph.h" |
| #include "target-globals.h" |
| #include "builtins.h" |
| #include "context.h" |
| #include "tree-pass.h" |
| #if TARGET_XCOFF |
| #include "xcoffout.h" /* get declarations of xcoff_*_section_name */ |
| #endif |
| #if TARGET_MACHO |
| #include "gstab.h" /* for N_SLINE */ |
| #endif |
| |
| #ifndef TARGET_NO_PROTOTYPE |
| #define TARGET_NO_PROTOTYPE 0 |
| #endif |
| |
| #define 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 reload_completed; /* stack info won't change from here on */ |
| int first_gp_reg_save; /* first callee saved GP register used */ |
| int first_fp_reg_save; /* first callee saved FP register used */ |
| int first_altivec_reg_save; /* first callee saved AltiVec register used */ |
| int 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 ehcr_offset; /* offset to EH CR field 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; |
| int savres_strategy; |
| } rs6000_stack_t; |
| |
| /* A C structure for machine-specific, per-function data. |
| This is added to the cfun structure. */ |
| typedef struct GTY(()) machine_function |
| { |
| /* Whether the instruction chain has been scanned already. */ |
| int insn_chain_scanned_p; |
| /* Flags if __builtin_return_address (n) with n >= 1 was used. */ |
| int ra_needs_full_frame; |
| /* Flags if __builtin_return_address (0) was used. */ |
| int ra_need_lr; |
| /* Cache lr_save_p after expansion of builtin_eh_return. */ |
| int lr_save_state; |
| /* Whether we need to save the TOC to the reserved stack location in the |
| function prologue. */ |
| bool save_toc_in_prologue; |
| /* Offset from virtual_stack_vars_rtx to the start of the ABI_V4 |
| varargs save area. */ |
| HOST_WIDE_INT varargs_save_offset; |
| /* Temporary stack slot to use for SDmode copies. This slot is |
| 64-bits wide and is allocated early enough so that the offset |
| does not overflow the 16-bit load/store offset field. */ |
| rtx sdmode_stack_slot; |
| /* Flag if r2 setup is needed with ELFv2 ABI. */ |
| bool r2_setup_needed; |
| } machine_function; |
| |
| /* Support targetm.vectorize.builtin_mask_for_load. */ |
| static GTY(()) tree altivec_builtin_mask_for_load; |
| |
| /* Set to nonzero once AIX common-mode calls have been defined. */ |
| static GTY(()) int common_mode_defined; |
| |
| /* Label number of label created for -mrelocatable, to call to so we can |
| get the address of the GOT section */ |
| static int rs6000_pic_labelno; |
| |
| #ifdef USING_ELFOS_H |
| /* Counter for labels which are to be placed in .fixup. */ |
| int fixuplabelno = 0; |
| #endif |
| |
| /* Whether to use variant of AIX ABI for PowerPC64 Linux. */ |
| int dot_symbols; |
| |
| /* Specify the machine mode that pointers have. After generation of rtl, the |
| compiler makes no further distinction between pointers and any other objects |
| of this machine mode. The type is unsigned since not all things that |
| include rs6000.h also include machmode.h. */ |
| unsigned rs6000_pmode; |
| |
| /* Width in bits of a pointer. */ |
| unsigned rs6000_pointer_size; |
| |
| #ifdef HAVE_AS_GNU_ATTRIBUTE |
| /* Flag whether floating point values have been passed/returned. */ |
| static bool rs6000_passes_float; |
| /* Flag whether vector values have been passed/returned. */ |
| static bool rs6000_passes_vector; |
| /* Flag whether small (<= 8 byte) structures have been returned. */ |
| static bool rs6000_returns_struct; |
| #endif |
| |
| /* Value is TRUE if register/mode pair is acceptable. */ |
| bool rs6000_hard_regno_mode_ok_p[NUM_MACHINE_MODES][FIRST_PSEUDO_REGISTER]; |
| |
| /* Maximum number of registers needed for a given register class and mode. */ |
| unsigned char rs6000_class_max_nregs[NUM_MACHINE_MODES][LIM_REG_CLASSES]; |
| |
| /* How many registers are needed for a given register and mode. */ |
| unsigned char rs6000_hard_regno_nregs[NUM_MACHINE_MODES][FIRST_PSEUDO_REGISTER]; |
| |
| /* Map register number to register class. */ |
| enum reg_class rs6000_regno_regclass[FIRST_PSEUDO_REGISTER]; |
| |
| static int dbg_cost_ctrl; |
| |
| /* Built in types. */ |
| tree rs6000_builtin_types[RS6000_BTI_MAX]; |
| tree rs6000_builtin_decls[RS6000_BUILTIN_COUNT]; |
| |
| /* Flag to say the TOC is initialized */ |
| int toc_initialized; |
| char toc_label_name[10]; |
| |
| /* Cached value of rs6000_variable_issue. This is cached in |
| rs6000_variable_issue hook and returned from rs6000_sched_reorder2. */ |
| static short cached_can_issue_more; |
| |
| static GTY(()) section *read_only_data_section; |
| static GTY(()) section *private_data_section; |
| static GTY(()) section *tls_data_section; |
| static GTY(()) section *tls_private_data_section; |
| static GTY(()) section *read_only_private_data_section; |
| static GTY(()) section *sdata2_section; |
| static GTY(()) section *toc_section; |
| |
| struct builtin_description |
| { |
| const HOST_WIDE_INT mask; |
| const enum insn_code icode; |
| const char *const name; |
| const enum rs6000_builtins code; |
| }; |
| |
| /* Describe the vector unit used for modes. */ |
| enum rs6000_vector rs6000_vector_unit[NUM_MACHINE_MODES]; |
| enum rs6000_vector rs6000_vector_mem[NUM_MACHINE_MODES]; |
| |
| /* Register classes for various constraints that are based on the target |
| switches. */ |
| enum reg_class rs6000_constraints[RS6000_CONSTRAINT_MAX]; |
| |
| /* Describe the alignment of a vector. */ |
| int rs6000_vector_align[NUM_MACHINE_MODES]; |
| |
| /* Map selected modes to types for builtins. */ |
| static GTY(()) tree builtin_mode_to_type[MAX_MACHINE_MODE][2]; |
| |
| /* What modes to automatically generate reciprocal divide estimate (fre) and |
| reciprocal sqrt (frsqrte) for. */ |
| unsigned char rs6000_recip_bits[MAX_MACHINE_MODE]; |
| |
| /* Masks to determine which reciprocal esitmate instructions to generate |
| automatically. */ |
| enum rs6000_recip_mask { |
| RECIP_SF_DIV = 0x001, /* Use divide estimate */ |
| RECIP_DF_DIV = 0x002, |
| RECIP_V4SF_DIV = 0x004, |
| RECIP_V2DF_DIV = 0x008, |
| |
| RECIP_SF_RSQRT = 0x010, /* Use reciprocal sqrt estimate. */ |
| RECIP_DF_RSQRT = 0x020, |
| RECIP_V4SF_RSQRT = 0x040, |
| RECIP_V2DF_RSQRT = 0x080, |
| |
| /* Various combination of flags for -mrecip=xxx. */ |
| RECIP_NONE = 0, |
| RECIP_ALL = (RECIP_SF_DIV | RECIP_DF_DIV | RECIP_V4SF_DIV |
| | RECIP_V2DF_DIV | RECIP_SF_RSQRT | RECIP_DF_RSQRT |
| | RECIP_V4SF_RSQRT | RECIP_V2DF_RSQRT), |
| |
| RECIP_HIGH_PRECISION = RECIP_ALL, |
| |
| /* On low precision machines like the power5, don't enable double precision |
| reciprocal square root estimate, since it isn't accurate enough. */ |
| RECIP_LOW_PRECISION = (RECIP_ALL & ~(RECIP_DF_RSQRT | RECIP_V2DF_RSQRT)) |
| }; |
| |
| /* -mrecip options. */ |
| static struct |
| { |
| const char *string; /* option name */ |
| unsigned int mask; /* mask bits to set */ |
| } recip_options[] = { |
| { "all", RECIP_ALL }, |
| { "none", RECIP_NONE }, |
| { "div", (RECIP_SF_DIV | RECIP_DF_DIV | RECIP_V4SF_DIV |
| | RECIP_V2DF_DIV) }, |
| { "divf", (RECIP_SF_DIV | RECIP_V4SF_DIV) }, |
| { "divd", (RECIP_DF_DIV | RECIP_V2DF_DIV) }, |
| { "rsqrt", (RECIP_SF_RSQRT | RECIP_DF_RSQRT | RECIP_V4SF_RSQRT |
| | RECIP_V2DF_RSQRT) }, |
| { "rsqrtf", (RECIP_SF_RSQRT | RECIP_V4SF_RSQRT) }, |
| { "rsqrtd", (RECIP_DF_RSQRT | RECIP_V2DF_RSQRT) }, |
| }; |
| |
| /* Pointer to function (in rs6000-c.c) that can define or undefine target |
| macros that have changed. Languages that don't support the preprocessor |
| don't link in rs6000-c.c, so we can't call it directly. */ |
| void (*rs6000_target_modify_macros_ptr) (bool, HOST_WIDE_INT, HOST_WIDE_INT); |
| |
| /* Simplfy register classes into simpler classifications. We assume |
| GPR_REG_TYPE - FPR_REG_TYPE are ordered so that we can use a simple range |
| check for standard register classes (gpr/floating/altivec/vsx) and |
| floating/vector classes (float/altivec/vsx). */ |
| |
| enum rs6000_reg_type { |
| NO_REG_TYPE, |
| PSEUDO_REG_TYPE, |
| GPR_REG_TYPE, |
| VSX_REG_TYPE, |
| ALTIVEC_REG_TYPE, |
| FPR_REG_TYPE, |
| SPR_REG_TYPE, |
| CR_REG_TYPE, |
| SPE_ACC_TYPE, |
| SPEFSCR_REG_TYPE |
| }; |
| |
| /* Map register class to register type. */ |
| static enum rs6000_reg_type reg_class_to_reg_type[N_REG_CLASSES]; |
| |
| /* First/last register type for the 'normal' register types (i.e. general |
| purpose, floating point, altivec, and VSX registers). */ |
| #define IS_STD_REG_TYPE(RTYPE) IN_RANGE(RTYPE, GPR_REG_TYPE, FPR_REG_TYPE) |
| |
| #define IS_FP_VECT_REG_TYPE(RTYPE) IN_RANGE(RTYPE, VSX_REG_TYPE, FPR_REG_TYPE) |
| |
| |
| /* Register classes we care about in secondary reload or go if legitimate |
| address. We only need to worry about GPR, FPR, and Altivec registers here, |
| along an ANY field that is the OR of the 3 register classes. */ |
| |
| enum rs6000_reload_reg_type { |
| RELOAD_REG_GPR, /* General purpose registers. */ |
| RELOAD_REG_FPR, /* Traditional floating point regs. */ |
| RELOAD_REG_VMX, /* Altivec (VMX) registers. */ |
| RELOAD_REG_ANY, /* OR of GPR, FPR, Altivec masks. */ |
| N_RELOAD_REG |
| }; |
| |
| /* For setting up register classes, loop through the 3 register classes mapping |
| into real registers, and skip the ANY class, which is just an OR of the |
| bits. */ |
| #define FIRST_RELOAD_REG_CLASS RELOAD_REG_GPR |
| #define LAST_RELOAD_REG_CLASS RELOAD_REG_VMX |
| |
| /* Map reload register type to a register in the register class. */ |
| struct reload_reg_map_type { |
| const char *name; /* Register class name. */ |
| int reg; /* Register in the register class. */ |
| }; |
| |
| static const struct reload_reg_map_type reload_reg_map[N_RELOAD_REG] = { |
| { "Gpr", FIRST_GPR_REGNO }, /* RELOAD_REG_GPR. */ |
| { "Fpr", FIRST_FPR_REGNO }, /* RELOAD_REG_FPR. */ |
| { "VMX", FIRST_ALTIVEC_REGNO }, /* RELOAD_REG_VMX. */ |
| { "Any", -1 }, /* RELOAD_REG_ANY. */ |
| }; |
| |
| /* Mask bits for each register class, indexed per mode. Historically the |
| compiler has been more restrictive which types can do PRE_MODIFY instead of |
| PRE_INC and PRE_DEC, so keep track of sepaate bits for these two. */ |
| typedef unsigned char addr_mask_type; |
| |
| #define RELOAD_REG_VALID 0x01 /* Mode valid in register.. */ |
| #define RELOAD_REG_MULTIPLE 0x02 /* Mode takes multiple registers. */ |
| #define RELOAD_REG_INDEXED 0x04 /* Reg+reg addressing. */ |
| #define RELOAD_REG_OFFSET 0x08 /* Reg+offset addressing. */ |
| #define RELOAD_REG_PRE_INCDEC 0x10 /* PRE_INC/PRE_DEC valid. */ |
| #define RELOAD_REG_PRE_MODIFY 0x20 /* PRE_MODIFY valid. */ |
| #define RELOAD_REG_AND_M16 0x40 /* AND -16 addressing. */ |
| |
| /* Register type masks based on the type, of valid addressing modes. */ |
| struct rs6000_reg_addr { |
| enum insn_code reload_load; /* INSN to reload for loading. */ |
| enum insn_code reload_store; /* INSN to reload for storing. */ |
| enum insn_code reload_fpr_gpr; /* INSN to move from FPR to GPR. */ |
| enum insn_code reload_gpr_vsx; /* INSN to move from GPR to VSX. */ |
| enum insn_code reload_vsx_gpr; /* INSN to move from VSX to GPR. */ |
| addr_mask_type addr_mask[(int)N_RELOAD_REG]; /* Valid address masks. */ |
| bool scalar_in_vmx_p; /* Scalar value can go in VMX. */ |
| }; |
| |
| static struct rs6000_reg_addr reg_addr[NUM_MACHINE_MODES]; |
| |
| /* Helper function to say whether a mode supports PRE_INC or PRE_DEC. */ |
| static inline bool |
| mode_supports_pre_incdec_p (machine_mode mode) |
| { |
| return ((reg_addr[mode].addr_mask[RELOAD_REG_ANY] & RELOAD_REG_PRE_INCDEC) |
| != 0); |
| } |
| |
| /* Helper function to say whether a mode supports PRE_MODIFY. */ |
| static inline bool |
| mode_supports_pre_modify_p (machine_mode mode) |
| { |
| return ((reg_addr[mode].addr_mask[RELOAD_REG_ANY] & RELOAD_REG_PRE_MODIFY) |
| != 0); |
| } |
| |
| |
| /* 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 int cache_line_size; /* cache line size in bytes. */ |
| const int l1_cache_size; /* size of l1 cache, in kilobytes. */ |
| const int l2_cache_size; /* size of l2 cache, in kilobytes. */ |
| const int simultaneous_prefetches; /* number of parallel prefetch |
| operations. */ |
| const int sfdf_convert; /* cost of SF->DF conversion. */ |
| }; |
| |
| 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 */ |
| 32, /* cache line size */ |
| 0, /* l1 cache */ |
| 0, /* l2 cache */ |
| 0, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 128, /* cache line size */ |
| 0, /* l1 cache */ |
| 0, /* l2 cache */ |
| 0, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 128, /* cache line size */ |
| 128, /* l1 cache */ |
| 2048, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 4, /* l1 cache */ |
| 16, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 4, /* l1 cache */ |
| 16, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 16, /* l1 cache */ |
| 128, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 32, /* l1 cache */ |
| 256, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on PPC476 processors. */ |
| static const |
| struct processor_costs ppc476_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 (11), /* divsi */ |
| COSTS_N_INSNS (11), /* divdi */ |
| COSTS_N_INSNS (6), /* fp */ |
| COSTS_N_INSNS (6), /* dmul */ |
| COSTS_N_INSNS (19), /* sdiv */ |
| COSTS_N_INSNS (33), /* ddiv */ |
| 32, /* l1 cache line size */ |
| 32, /* l1 cache */ |
| 512, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 32, /* l1 cache */ |
| 256, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 8, /* l1 cache */ |
| 64, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 16, /* l1 cache */ |
| 512, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 32, /* l1 cache */ |
| 1024, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 128, /* cache line size */ |
| 32, /* l1 cache */ |
| 1024, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 128, /* cache line size */ |
| 64, /* l1 cache */ |
| 1024, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on Cell processor. */ |
| /* COSTS_N_INSNS (1) ~ one add. */ |
| static const |
| struct processor_costs ppccell_cost = { |
| COSTS_N_INSNS (9/2)+2, /* mulsi */ |
| COSTS_N_INSNS (6/2), /* mulsi_const */ |
| COSTS_N_INSNS (6/2), /* mulsi_const9 */ |
| COSTS_N_INSNS (15/2)+2, /* muldi */ |
| COSTS_N_INSNS (38/2), /* divsi */ |
| COSTS_N_INSNS (70/2), /* divdi */ |
| COSTS_N_INSNS (10/2), /* fp */ |
| COSTS_N_INSNS (10/2), /* dmul */ |
| COSTS_N_INSNS (74/2), /* sdiv */ |
| COSTS_N_INSNS (74/2), /* ddiv */ |
| 128, /* cache line size */ |
| 32, /* l1 cache */ |
| 512, /* l2 cache */ |
| 6, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 32, /* l1 cache */ |
| 512, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 32, /* l1 cache */ |
| 1024, /* l2 cache */ |
| 1, /* streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 32, /* cache line size */ |
| 32, /* l1 cache */ |
| 256, /* l2 cache */ |
| 1, /* prefetch streams /*/ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on E300C2 and E300C3 cores. */ |
| static const |
| struct processor_costs ppce300c2c3_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 (3), /* fp */ |
| COSTS_N_INSNS (4), /* dmul */ |
| COSTS_N_INSNS (18), /* sdiv */ |
| COSTS_N_INSNS (33), /* ddiv */ |
| 32, |
| 16, /* l1 cache */ |
| 16, /* l2 cache */ |
| 1, /* prefetch streams /*/ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on PPCE500MC processors. */ |
| static const |
| struct processor_costs ppce500mc_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 (14), /* divsi */ |
| COSTS_N_INSNS (14), /* divdi */ |
| COSTS_N_INSNS (8), /* fp */ |
| COSTS_N_INSNS (10), /* dmul */ |
| COSTS_N_INSNS (36), /* sdiv */ |
| COSTS_N_INSNS (66), /* ddiv */ |
| 64, /* cache line size */ |
| 32, /* l1 cache */ |
| 128, /* l2 cache */ |
| 1, /* prefetch streams /*/ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on PPCE500MC64 processors. */ |
| static const |
| struct processor_costs ppce500mc64_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 (14), /* divsi */ |
| COSTS_N_INSNS (14), /* divdi */ |
| COSTS_N_INSNS (4), /* fp */ |
| COSTS_N_INSNS (10), /* dmul */ |
| COSTS_N_INSNS (36), /* sdiv */ |
| COSTS_N_INSNS (66), /* ddiv */ |
| 64, /* cache line size */ |
| 32, /* l1 cache */ |
| 128, /* l2 cache */ |
| 1, /* prefetch streams /*/ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on PPCE5500 processors. */ |
| static const |
| struct processor_costs ppce5500_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (5), /* mulsi_const */ |
| COSTS_N_INSNS (4), /* mulsi_const9 */ |
| COSTS_N_INSNS (5), /* muldi */ |
| COSTS_N_INSNS (14), /* divsi */ |
| COSTS_N_INSNS (14), /* divdi */ |
| COSTS_N_INSNS (7), /* fp */ |
| COSTS_N_INSNS (10), /* dmul */ |
| COSTS_N_INSNS (36), /* sdiv */ |
| COSTS_N_INSNS (66), /* ddiv */ |
| 64, /* cache line size */ |
| 32, /* l1 cache */ |
| 128, /* l2 cache */ |
| 1, /* prefetch streams /*/ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on PPCE6500 processors. */ |
| static const |
| struct processor_costs ppce6500_cost = { |
| COSTS_N_INSNS (5), /* mulsi */ |
| COSTS_N_INSNS (5), /* mulsi_const */ |
| COSTS_N_INSNS (4), /* mulsi_const9 */ |
| COSTS_N_INSNS (5), /* muldi */ |
| COSTS_N_INSNS (14), /* divsi */ |
| COSTS_N_INSNS (14), /* divdi */ |
| COSTS_N_INSNS (7), /* fp */ |
| COSTS_N_INSNS (10), /* dmul */ |
| COSTS_N_INSNS (36), /* sdiv */ |
| COSTS_N_INSNS (66), /* ddiv */ |
| 64, /* cache line size */ |
| 32, /* l1 cache */ |
| 128, /* l2 cache */ |
| 1, /* prefetch streams /*/ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on AppliedMicro Titan processors. */ |
| static const |
| struct processor_costs titan_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 (18), /* divsi */ |
| COSTS_N_INSNS (18), /* divdi */ |
| COSTS_N_INSNS (10), /* fp */ |
| COSTS_N_INSNS (10), /* dmul */ |
| COSTS_N_INSNS (46), /* sdiv */ |
| COSTS_N_INSNS (72), /* ddiv */ |
| 32, /* cache line size */ |
| 32, /* l1 cache */ |
| 512, /* l2 cache */ |
| 1, /* prefetch streams /*/ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* 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 */ |
| 128, /* cache line size */ |
| 32, /* l1 cache */ |
| 1024, /* l2 cache */ |
| 8, /* prefetch streams /*/ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on POWER6 processors. */ |
| static const |
| struct processor_costs power6_cost = { |
| COSTS_N_INSNS (8), /* mulsi */ |
| COSTS_N_INSNS (8), /* mulsi_const */ |
| COSTS_N_INSNS (8), /* mulsi_const9 */ |
| COSTS_N_INSNS (8), /* muldi */ |
| COSTS_N_INSNS (22), /* divsi */ |
| COSTS_N_INSNS (28), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (13), /* sdiv */ |
| COSTS_N_INSNS (16), /* ddiv */ |
| 128, /* cache line size */ |
| 64, /* l1 cache */ |
| 2048, /* l2 cache */ |
| 16, /* prefetch streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on POWER7 processors. */ |
| static const |
| struct processor_costs power7_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 (18), /* divsi */ |
| COSTS_N_INSNS (34), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (13), /* sdiv */ |
| COSTS_N_INSNS (16), /* ddiv */ |
| 128, /* cache line size */ |
| 32, /* l1 cache */ |
| 256, /* l2 cache */ |
| 12, /* prefetch streams */ |
| COSTS_N_INSNS (3), /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on POWER8 processors. */ |
| static const |
| struct processor_costs power8_cost = { |
| COSTS_N_INSNS (3), /* mulsi */ |
| COSTS_N_INSNS (3), /* mulsi_const */ |
| COSTS_N_INSNS (3), /* mulsi_const9 */ |
| COSTS_N_INSNS (3), /* muldi */ |
| COSTS_N_INSNS (19), /* divsi */ |
| COSTS_N_INSNS (35), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (14), /* sdiv */ |
| COSTS_N_INSNS (17), /* ddiv */ |
| 128, /* cache line size */ |
| 32, /* l1 cache */ |
| 256, /* l2 cache */ |
| 12, /* prefetch streams */ |
| COSTS_N_INSNS (3), /* SF->DF convert */ |
| }; |
| |
| /* Instruction costs on POWER A2 processors. */ |
| static const |
| struct processor_costs ppca2_cost = { |
| COSTS_N_INSNS (16), /* mulsi */ |
| COSTS_N_INSNS (16), /* mulsi_const */ |
| COSTS_N_INSNS (16), /* mulsi_const9 */ |
| COSTS_N_INSNS (16), /* muldi */ |
| COSTS_N_INSNS (22), /* divsi */ |
| COSTS_N_INSNS (28), /* divdi */ |
| COSTS_N_INSNS (3), /* fp */ |
| COSTS_N_INSNS (3), /* dmul */ |
| COSTS_N_INSNS (59), /* sdiv */ |
| COSTS_N_INSNS (72), /* ddiv */ |
| 64, |
| 16, /* l1 cache */ |
| 2048, /* l2 cache */ |
| 16, /* prefetch streams */ |
| 0, /* SF->DF convert */ |
| }; |
| |
| |
| /* Table that classifies rs6000 builtin functions (pure, const, etc.). */ |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { NAME, ICODE, MASK, ATTR }, |
| |
| struct rs6000_builtin_info_type { |
| const char *name; |
| const enum insn_code icode; |
| const HOST_WIDE_INT mask; |
| const unsigned attr; |
| }; |
| |
| static const struct rs6000_builtin_info_type rs6000_builtin_info[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| /* Support for -mveclibabi=<xxx> to control which vector library to use. */ |
| static tree (*rs6000_veclib_handler) (tree, tree, tree); |
| |
| |
| static bool rs6000_debug_legitimate_address_p (machine_mode, rtx, bool); |
| static bool spe_func_has_64bit_regs_p (void); |
| static struct machine_function * rs6000_init_machine_status (void); |
| 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 tree rs6000_handle_struct_attribute (tree *, tree, tree, int, bool *); |
| static tree rs6000_builtin_vectorized_libmass (tree, tree, tree); |
| static void rs6000_emit_set_long_const (rtx, HOST_WIDE_INT); |
| static int rs6000_memory_move_cost (machine_mode, reg_class_t, bool); |
| static bool rs6000_debug_rtx_costs (rtx, int, int, int, int *, bool); |
| static int rs6000_debug_address_cost (rtx, machine_mode, addr_space_t, |
| bool); |
| static int rs6000_debug_adjust_cost (rtx_insn *, rtx, rtx_insn *, int); |
| static bool is_microcoded_insn (rtx_insn *); |
| static bool is_nonpipeline_insn (rtx_insn *); |
| static bool is_cracked_insn (rtx_insn *); |
| static bool is_load_insn (rtx, rtx *); |
| static bool is_store_insn (rtx, rtx *); |
| static bool set_to_load_agen (rtx_insn *,rtx_insn *); |
| static bool insn_terminates_group_p (rtx_insn *, enum group_termination); |
| static bool insn_must_be_first_in_group (rtx_insn *); |
| static bool insn_must_be_last_in_group (rtx_insn *); |
| static void altivec_init_builtins (void); |
| static tree builtin_function_type (machine_mode, machine_mode, |
| machine_mode, machine_mode, |
| enum rs6000_builtins, const char *name); |
| static void rs6000_common_init_builtins (void); |
| static void paired_init_builtins (void); |
| static rtx paired_expand_predicate_builtin (enum insn_code, tree, rtx); |
| static void spe_init_builtins (void); |
| static void htm_init_builtins (void); |
| 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 is_altivec_return_reg (rtx, void *); |
| int easy_vector_constant (rtx, machine_mode); |
| static rtx rs6000_debug_legitimize_address (rtx, rtx, machine_mode); |
| static rtx rs6000_legitimize_tls_address (rtx, enum tls_model); |
| static rtx rs6000_darwin64_record_arg (CUMULATIVE_ARGS *, const_tree, |
| bool, bool); |
| #if TARGET_MACHO |
| static void macho_branch_islands (void); |
| #endif |
| static rtx rs6000_legitimize_reload_address (rtx, machine_mode, int, int, |
| int, int *); |
| static rtx rs6000_debug_legitimize_reload_address (rtx, machine_mode, int, |
| int, int, int *); |
| static bool rs6000_mode_dependent_address (const_rtx); |
| static bool rs6000_debug_mode_dependent_address (const_rtx); |
| static enum reg_class rs6000_secondary_reload_class (enum reg_class, |
| machine_mode, rtx); |
| static enum reg_class rs6000_debug_secondary_reload_class (enum reg_class, |
| machine_mode, |
| rtx); |
| static enum reg_class rs6000_preferred_reload_class (rtx, enum reg_class); |
| static enum reg_class rs6000_debug_preferred_reload_class (rtx, |
| enum reg_class); |
| static bool rs6000_secondary_memory_needed (enum reg_class, enum reg_class, |
| machine_mode); |
| static bool rs6000_debug_secondary_memory_needed (enum reg_class, |
| enum reg_class, |
| machine_mode); |
| static bool rs6000_cannot_change_mode_class (machine_mode, |
| machine_mode, |
| enum reg_class); |
| static bool rs6000_debug_cannot_change_mode_class (machine_mode, |
| machine_mode, |
| enum reg_class); |
| static bool rs6000_save_toc_in_prologue_p (void); |
| |
| rtx (*rs6000_legitimize_reload_address_ptr) (rtx, machine_mode, int, int, |
| int, int *) |
| = rs6000_legitimize_reload_address; |
| |
| static bool (*rs6000_mode_dependent_address_ptr) (const_rtx) |
| = rs6000_mode_dependent_address; |
| |
| enum reg_class (*rs6000_secondary_reload_class_ptr) (enum reg_class, |
| machine_mode, rtx) |
| = rs6000_secondary_reload_class; |
| |
| enum reg_class (*rs6000_preferred_reload_class_ptr) (rtx, enum reg_class) |
| = rs6000_preferred_reload_class; |
| |
| bool (*rs6000_secondary_memory_needed_ptr) (enum reg_class, enum reg_class, |
| machine_mode) |
| = rs6000_secondary_memory_needed; |
| |
| bool (*rs6000_cannot_change_mode_class_ptr) (machine_mode, |
| machine_mode, |
| enum reg_class) |
| = rs6000_cannot_change_mode_class; |
| |
| const int INSN_NOT_AVAILABLE = -1; |
| |
| static void rs6000_print_isa_options (FILE *, int, const char *, |
| HOST_WIDE_INT); |
| static void rs6000_print_builtin_options (FILE *, int, const char *, |
| HOST_WIDE_INT); |
| |
| static enum rs6000_reg_type register_to_reg_type (rtx, bool *); |
| static bool rs6000_secondary_reload_move (enum rs6000_reg_type, |
| enum rs6000_reg_type, |
| machine_mode, |
| secondary_reload_info *, |
| bool); |
| rtl_opt_pass *make_pass_analyze_swaps (gcc::context*); |
| |
| /* Hash table stuff for keeping track of TOC entries. */ |
| |
| struct GTY((for_user)) toc_hash_struct |
| { |
| /* `key' will satisfy CONSTANT_P; in fact, it will satisfy |
| ASM_OUTPUT_SPECIAL_POOL_ENTRY_P. */ |
| rtx key; |
| machine_mode key_mode; |
| int labelno; |
| }; |
| |
| struct toc_hasher : ggc_hasher<toc_hash_struct *> |
| { |
| static hashval_t hash (toc_hash_struct *); |
| static bool equal (toc_hash_struct *, toc_hash_struct *); |
| }; |
| |
| static GTY (()) hash_table<toc_hasher> *toc_hash_table; |
| |
| /* Hash table to keep track of the argument types for builtin functions. */ |
| |
| struct GTY((for_user)) builtin_hash_struct |
| { |
| tree type; |
| machine_mode mode[4]; /* return value + 3 arguments. */ |
| unsigned char uns_p[4]; /* and whether the types are unsigned. */ |
| }; |
| |
| struct builtin_hasher : ggc_hasher<builtin_hash_struct *> |
| { |
| static hashval_t hash (builtin_hash_struct *); |
| static bool equal (builtin_hash_struct *, builtin_hash_struct *); |
| }; |
| |
| static GTY (()) hash_table<builtin_hasher> *builtin_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", |
| "ca", |
| /* AltiVec registers. */ |
| "0", "1", "2", "3", "4", "5", "6", "7", |
| "8", "9", "10", "11", "12", "13", "14", "15", |
| "16", "17", "18", "19", "20", "21", "22", "23", |
| "24", "25", "26", "27", "28", "29", "30", "31", |
| "vrsave", "vscr", |
| /* SPE registers. */ |
| "spe_acc", "spefscr", |
| /* Soft frame pointer. */ |
| "sfp", |
| /* HTM SPR registers. */ |
| "tfhar", "tfiar", "texasr", |
| /* SPE High 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" |
| }; |
| |
| #ifdef TARGET_REGNAMES |
| static const char alt_reg_names[][8] = |
| { |
| "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", |
| "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", |
| "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23", |
| "%r24", "%r25", "%r26", "%r27", "%r28", "%r29", "%r30", "%r31", |
| "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", |
| "%f8", "%f9", "%f10", "%f11", "%f12", "%f13", "%f14", "%f15", |
| "%f16", "%f17", "%f18", "%f19", "%f20", "%f21", "%f22", "%f23", |
| "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31", |
| "mq", "lr", "ctr", "ap", |
| "%cr0", "%cr1", "%cr2", "%cr3", "%cr4", "%cr5", "%cr6", "%cr7", |
| "ca", |
| /* AltiVec registers. */ |
| "%v0", "%v1", "%v2", "%v3", "%v4", "%v5", "%v6", "%v7", |
| "%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", |
| /* HTM SPR registers. */ |
| "tfhar", "tfiar", "texasr", |
| /* SPE High registers. */ |
| "%rh0", "%rh1", "%rh2", "%rh3", "%rh4", "%rh5", "%rh6", "%rh7", |
| "%rh8", "%rh9", "%rh10", "%r11", "%rh12", "%rh13", "%rh14", "%rh15", |
| "%rh16", "%rh17", "%rh18", "%rh19", "%rh20", "%rh21", "%rh22", "%rh23", |
| "%rh24", "%rh25", "%rh26", "%rh27", "%rh28", "%rh29", "%rh30", "%rh31" |
| }; |
| #endif |
| |
| /* Table of valid machine attributes. */ |
| |
| static const struct attribute_spec rs6000_attribute_table[] = |
| { |
| /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, |
| affects_type_identity } */ |
| { "altivec", 1, 1, false, true, false, rs6000_handle_altivec_attribute, |
| false }, |
| { "longcall", 0, 0, false, true, true, rs6000_handle_longcall_attribute, |
| false }, |
| { "shortcall", 0, 0, false, true, true, rs6000_handle_longcall_attribute, |
| false }, |
| { "ms_struct", 0, 0, false, false, false, rs6000_handle_struct_attribute, |
| false }, |
| { "gcc_struct", 0, 0, false, false, false, rs6000_handle_struct_attribute, |
| false }, |
| #ifdef SUBTARGET_ATTRIBUTE_TABLE |
| SUBTARGET_ATTRIBUTE_TABLE, |
| #endif |
| { NULL, 0, 0, false, false, false, NULL, false } |
| }; |
| |
| #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_ATTRIBUTE_TAKES_IDENTIFIER_P |
| #define TARGET_ATTRIBUTE_TAKES_IDENTIFIER_P rs6000_attribute_takes_identifier_p |
| |
| #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 |
| |
| #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO |
| #undef TARGET_ASM_ASSEMBLE_VISIBILITY |
| #define TARGET_ASM_ASSEMBLE_VISIBILITY rs6000_assemble_visibility |
| #endif |
| |
| #undef TARGET_SET_UP_BY_PROLOGUE |
| #define TARGET_SET_UP_BY_PROLOGUE rs6000_set_up_by_prologue |
| |
| #undef TARGET_HAVE_TLS |
| #define TARGET_HAVE_TLS HAVE_AS_TLS |
| |
| #undef TARGET_CANNOT_FORCE_CONST_MEM |
| #define TARGET_CANNOT_FORCE_CONST_MEM rs6000_cannot_force_const_mem |
| |
| #undef TARGET_DELEGITIMIZE_ADDRESS |
| #define TARGET_DELEGITIMIZE_ADDRESS rs6000_delegitimize_address |
| |
| #undef TARGET_CONST_NOT_OK_FOR_DEBUG_P |
| #define TARGET_CONST_NOT_OK_FOR_DEBUG_P rs6000_const_not_ok_for_debug_p |
| |
| #undef TARGET_ASM_FUNCTION_PROLOGUE |
| #define TARGET_ASM_FUNCTION_PROLOGUE rs6000_output_function_prologue |
| #undef TARGET_ASM_FUNCTION_EPILOGUE |
| #define TARGET_ASM_FUNCTION_EPILOGUE rs6000_output_function_epilogue |
| |
| #undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA |
| #define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA rs6000_output_addr_const_extra |
| |
| #undef TARGET_LEGITIMIZE_ADDRESS |
| #define TARGET_LEGITIMIZE_ADDRESS rs6000_legitimize_address |
| |
| #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_INIT |
| #define TARGET_SCHED_INIT rs6000_sched_init |
| #undef TARGET_SCHED_FINISH |
| #define TARGET_SCHED_FINISH rs6000_sched_finish |
| #undef TARGET_SCHED_REORDER |
| #define TARGET_SCHED_REORDER rs6000_sched_reorder |
| #undef TARGET_SCHED_REORDER2 |
| #define TARGET_SCHED_REORDER2 rs6000_sched_reorder2 |
| |
| #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD |
| #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD rs6000_use_sched_lookahead |
| |
| #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD_GUARD |
| #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD_GUARD rs6000_use_sched_lookahead_guard |
| |
| #undef TARGET_SCHED_ALLOC_SCHED_CONTEXT |
| #define TARGET_SCHED_ALLOC_SCHED_CONTEXT rs6000_alloc_sched_context |
| #undef TARGET_SCHED_INIT_SCHED_CONTEXT |
| #define TARGET_SCHED_INIT_SCHED_CONTEXT rs6000_init_sched_context |
| #undef TARGET_SCHED_SET_SCHED_CONTEXT |
| #define TARGET_SCHED_SET_SCHED_CONTEXT rs6000_set_sched_context |
| #undef TARGET_SCHED_FREE_SCHED_CONTEXT |
| #define TARGET_SCHED_FREE_SCHED_CONTEXT rs6000_free_sched_context |
| |
| #undef TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD |
| #define TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD rs6000_builtin_mask_for_load |
| #undef TARGET_VECTORIZE_SUPPORT_VECTOR_MISALIGNMENT |
| #define TARGET_VECTORIZE_SUPPORT_VECTOR_MISALIGNMENT \ |
| rs6000_builtin_support_vector_misalignment |
| #undef TARGET_VECTORIZE_VECTOR_ALIGNMENT_REACHABLE |
| #define TARGET_VECTORIZE_VECTOR_ALIGNMENT_REACHABLE rs6000_vector_alignment_reachable |
| #undef TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST |
| #define TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST \ |
| rs6000_builtin_vectorization_cost |
| #undef TARGET_VECTORIZE_PREFERRED_SIMD_MODE |
| #define TARGET_VECTORIZE_PREFERRED_SIMD_MODE \ |
| rs6000_preferred_simd_mode |
| #undef TARGET_VECTORIZE_INIT_COST |
| #define TARGET_VECTORIZE_INIT_COST rs6000_init_cost |
| #undef TARGET_VECTORIZE_ADD_STMT_COST |
| #define TARGET_VECTORIZE_ADD_STMT_COST rs6000_add_stmt_cost |
| #undef TARGET_VECTORIZE_FINISH_COST |
| #define TARGET_VECTORIZE_FINISH_COST rs6000_finish_cost |
| #undef TARGET_VECTORIZE_DESTROY_COST_DATA |
| #define TARGET_VECTORIZE_DESTROY_COST_DATA rs6000_destroy_cost_data |
| |
| #undef TARGET_INIT_BUILTINS |
| #define TARGET_INIT_BUILTINS rs6000_init_builtins |
| #undef TARGET_BUILTIN_DECL |
| #define TARGET_BUILTIN_DECL rs6000_builtin_decl |
| |
| #undef TARGET_EXPAND_BUILTIN |
| #define TARGET_EXPAND_BUILTIN rs6000_expand_builtin |
| |
| #undef TARGET_MANGLE_TYPE |
| #define TARGET_MANGLE_TYPE rs6000_mangle_type |
| |
| #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 |
| #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_const_tree_hwi_hwi_const_tree_true |
| |
| #undef TARGET_FUNCTION_OK_FOR_SIBCALL |
| #define TARGET_FUNCTION_OK_FOR_SIBCALL rs6000_function_ok_for_sibcall |
| |
| #undef TARGET_REGISTER_MOVE_COST |
| #define TARGET_REGISTER_MOVE_COST rs6000_register_move_cost |
| #undef TARGET_MEMORY_MOVE_COST |
| #define TARGET_MEMORY_MOVE_COST rs6000_memory_move_cost |
| #undef TARGET_RTX_COSTS |
| #define TARGET_RTX_COSTS rs6000_rtx_costs |
| #undef TARGET_ADDRESS_COST |
| #define TARGET_ADDRESS_COST hook_int_rtx_mode_as_bool_0 |
| |
| #undef TARGET_DWARF_REGISTER_SPAN |
| #define TARGET_DWARF_REGISTER_SPAN rs6000_dwarf_register_span |
| |
| #undef TARGET_INIT_DWARF_REG_SIZES_EXTRA |
| #define TARGET_INIT_DWARF_REG_SIZES_EXTRA rs6000_init_dwarf_reg_sizes_extra |
| |
| #undef TARGET_MEMBER_TYPE_FORCES_BLK |
| #define TARGET_MEMBER_TYPE_FORCES_BLK rs6000_member_type_forces_blk |
| |
| #undef TARGET_PROMOTE_FUNCTION_MODE |
| #define TARGET_PROMOTE_FUNCTION_MODE rs6000_promote_function_mode |
| |
| #undef TARGET_RETURN_IN_MEMORY |
| #define TARGET_RETURN_IN_MEMORY rs6000_return_in_memory |
| |
| #undef TARGET_RETURN_IN_MSB |
| #define TARGET_RETURN_IN_MSB rs6000_return_in_msb |
| |
| #undef TARGET_SETUP_INCOMING_VARARGS |
| #define TARGET_SETUP_INCOMING_VARARGS setup_incoming_varargs |
| |
| /* Always strict argument naming on rs6000. */ |
| #undef TARGET_STRICT_ARGUMENT_NAMING |
| #define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true |
| #undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED |
| #define TARGET_PRETEND_OUTGOING_VARARGS_NAMED hook_bool_CUMULATIVE_ARGS_true |
| #undef TARGET_SPLIT_COMPLEX_ARG |
| #define TARGET_SPLIT_COMPLEX_ARG hook_bool_const_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_FUNCTION_ARG_ADVANCE |
| #define TARGET_FUNCTION_ARG_ADVANCE rs6000_function_arg_advance |
| #undef TARGET_FUNCTION_ARG |
| #define TARGET_FUNCTION_ARG rs6000_function_arg |
| #undef TARGET_FUNCTION_ARG_BOUNDARY |
| #define TARGET_FUNCTION_ARG_BOUNDARY rs6000_function_arg_boundary |
| |
| #undef TARGET_BUILD_BUILTIN_VA_LIST |
| #define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list |
| |
| #undef TARGET_EXPAND_BUILTIN_VA_START |
| #define TARGET_EXPAND_BUILTIN_VA_START rs6000_va_start |
| |
| #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_ASM_LOOP_ALIGN_MAX_SKIP |
| #define TARGET_ASM_LOOP_ALIGN_MAX_SKIP rs6000_loop_align_max_skip |
| |
| #undef TARGET_MD_ASM_CLOBBERS |
| #define TARGET_MD_ASM_CLOBBERS rs6000_md_asm_clobbers |
| |
| #undef TARGET_OPTION_OVERRIDE |
| #define TARGET_OPTION_OVERRIDE rs6000_option_override |
| |
| #undef TARGET_VECTORIZE_BUILTIN_VECTORIZED_FUNCTION |
| #define TARGET_VECTORIZE_BUILTIN_VECTORIZED_FUNCTION \ |
| rs6000_builtin_vectorized_function |
| |
| #if !TARGET_MACHO |
| #undef TARGET_STACK_PROTECT_FAIL |
| #define TARGET_STACK_PROTECT_FAIL rs6000_stack_protect_fail |
| #endif |
| |
| /* 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 |
| |
| /* 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 |
| #undef TARGET_USE_BLOCKS_FOR_DECL_P |
| #define TARGET_USE_BLOCKS_FOR_DECL_P rs6000_use_blocks_for_decl_p |
| |
| #undef TARGET_BUILTIN_RECIPROCAL |
| #define TARGET_BUILTIN_RECIPROCAL rs6000_builtin_reciprocal |
| |
| #undef TARGET_EXPAND_TO_RTL_HOOK |
| #define TARGET_EXPAND_TO_RTL_HOOK rs6000_alloc_sdmode_stack_slot |
| |
| #undef TARGET_INSTANTIATE_DECLS |
| #define TARGET_INSTANTIATE_DECLS rs6000_instantiate_decls |
| |
| #undef TARGET_SECONDARY_RELOAD |
| #define TARGET_SECONDARY_RELOAD rs6000_secondary_reload |
| |
| #undef TARGET_LEGITIMATE_ADDRESS_P |
| #define TARGET_LEGITIMATE_ADDRESS_P rs6000_legitimate_address_p |
| |
| #undef TARGET_MODE_DEPENDENT_ADDRESS_P |
| #define TARGET_MODE_DEPENDENT_ADDRESS_P rs6000_mode_dependent_address_p |
| |
| #undef TARGET_LRA_P |
| #define TARGET_LRA_P rs6000_lra_p |
| |
| #undef TARGET_CAN_ELIMINATE |
| #define TARGET_CAN_ELIMINATE rs6000_can_eliminate |
| |
| #undef TARGET_CONDITIONAL_REGISTER_USAGE |
| #define TARGET_CONDITIONAL_REGISTER_USAGE rs6000_conditional_register_usage |
| |
| #undef TARGET_TRAMPOLINE_INIT |
| #define TARGET_TRAMPOLINE_INIT rs6000_trampoline_init |
| |
| #undef TARGET_FUNCTION_VALUE |
| #define TARGET_FUNCTION_VALUE rs6000_function_value |
| |
| #undef TARGET_OPTION_VALID_ATTRIBUTE_P |
| #define TARGET_OPTION_VALID_ATTRIBUTE_P rs6000_valid_attribute_p |
| |
| #undef TARGET_OPTION_SAVE |
| #define TARGET_OPTION_SAVE rs6000_function_specific_save |
| |
| #undef TARGET_OPTION_RESTORE |
| #define TARGET_OPTION_RESTORE rs6000_function_specific_restore |
| |
| #undef TARGET_OPTION_PRINT |
| #define TARGET_OPTION_PRINT rs6000_function_specific_print |
| |
| #undef TARGET_CAN_INLINE_P |
| #define TARGET_CAN_INLINE_P rs6000_can_inline_p |
| |
| #undef TARGET_SET_CURRENT_FUNCTION |
| #define TARGET_SET_CURRENT_FUNCTION rs6000_set_current_function |
| |
| #undef TARGET_LEGITIMATE_CONSTANT_P |
| #define TARGET_LEGITIMATE_CONSTANT_P rs6000_legitimate_constant_p |
| |
| #undef TARGET_VECTORIZE_VEC_PERM_CONST_OK |
| #define TARGET_VECTORIZE_VEC_PERM_CONST_OK rs6000_vectorize_vec_perm_const_ok |
| |
| #undef TARGET_CAN_USE_DOLOOP_P |
| #define TARGET_CAN_USE_DOLOOP_P can_use_doloop_if_innermost |
| |
| #undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV |
| #define TARGET_ATOMIC_ASSIGN_EXPAND_FENV rs6000_atomic_assign_expand_fenv |
| |
| #undef TARGET_LIBGCC_CMP_RETURN_MODE |
| #define TARGET_LIBGCC_CMP_RETURN_MODE rs6000_abi_word_mode |
| #undef TARGET_LIBGCC_SHIFT_COUNT_MODE |
| #define TARGET_LIBGCC_SHIFT_COUNT_MODE rs6000_abi_word_mode |
| #undef TARGET_UNWIND_WORD_MODE |
| #define TARGET_UNWIND_WORD_MODE rs6000_abi_word_mode |
| |
| |
| /* Processor table. */ |
| struct rs6000_ptt |
| { |
| const char *const name; /* Canonical processor name. */ |
| const enum processor_type processor; /* Processor type enum value. */ |
| const HOST_WIDE_INT target_enable; /* Target flags to enable. */ |
| }; |
| |
| static struct rs6000_ptt const processor_target_table[] = |
| { |
| #define RS6000_CPU(NAME, CPU, FLAGS) { NAME, CPU, FLAGS }, |
| #include "rs6000-cpus.def" |
| #undef RS6000_CPU |
| }; |
| |
| /* Look up a processor name for -mcpu=xxx and -mtune=xxx. Return -1 if the |
| name is invalid. */ |
| |
| static int |
| rs6000_cpu_name_lookup (const char *name) |
| { |
| size_t i; |
| |
| if (name != NULL) |
| { |
| for (i = 0; i < ARRAY_SIZE (processor_target_table); i++) |
| if (! strcmp (name, processor_target_table[i].name)) |
| return (int)i; |
| } |
| |
| return -1; |
| } |
| |
| |
| /* 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. */ |
| |
| static int |
| rs6000_hard_regno_nregs_internal (int regno, machine_mode mode) |
| { |
| unsigned HOST_WIDE_INT reg_size; |
| |
| /* TF/TD modes are special in that they always take 2 registers. */ |
| if (FP_REGNO_P (regno)) |
| reg_size = ((VECTOR_MEM_VSX_P (mode) && mode != TDmode && mode != TFmode) |
| ? UNITS_PER_VSX_WORD |
| : UNITS_PER_FP_WORD); |
| |
| else if (SPE_SIMD_REGNO_P (regno) && TARGET_SPE && SPE_VECTOR_MODE (mode)) |
| reg_size = UNITS_PER_SPE_WORD; |
| |
| else if (ALTIVEC_REGNO_P (regno)) |
| reg_size = 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. */ |
| else if (TARGET_E500_DOUBLE && FLOAT_MODE_P (mode) && mode != SCmode |
| && !DECIMAL_FLOAT_MODE_P (mode) && SPE_SIMD_REGNO_P (regno)) |
| reg_size = UNITS_PER_FP_WORD; |
| |
| else |
| reg_size = UNITS_PER_WORD; |
| |
| return (GET_MODE_SIZE (mode) + reg_size - 1) / reg_size; |
| } |
| |
| /* Value is 1 if hard register REGNO can hold a value of machine-mode |
| MODE. */ |
| static int |
| rs6000_hard_regno_mode_ok (int regno, machine_mode mode) |
| { |
| int last_regno = regno + rs6000_hard_regno_nregs[mode][regno] - 1; |
| |
| /* PTImode can only go in GPRs. Quad word memory operations require even/odd |
| register combinations, and use PTImode where we need to deal with quad |
| word memory operations. Don't allow quad words in the argument or frame |
| pointer registers, just registers 0..31. */ |
| if (mode == PTImode) |
| return (IN_RANGE (regno, FIRST_GPR_REGNO, LAST_GPR_REGNO) |
| && IN_RANGE (last_regno, FIRST_GPR_REGNO, LAST_GPR_REGNO) |
| && ((regno & 1) == 0)); |
| |
| /* VSX registers that overlap the FPR registers are larger than for non-VSX |
| implementations. Don't allow an item to be split between a FP register |
| and an Altivec register. Allow TImode in all VSX registers if the user |
| asked for it. */ |
| if (TARGET_VSX && VSX_REGNO_P (regno) |
| && (VECTOR_MEM_VSX_P (mode) |
| || reg_addr[mode].scalar_in_vmx_p |
| || (TARGET_VSX_TIMODE && mode == TImode) |
| || (TARGET_VADDUQM && mode == V1TImode))) |
| { |
| if (FP_REGNO_P (regno)) |
| return FP_REGNO_P (last_regno); |
| |
| if (ALTIVEC_REGNO_P (regno)) |
| { |
| if (GET_MODE_SIZE (mode) != 16 && !reg_addr[mode].scalar_in_vmx_p) |
| return 0; |
| |
| return ALTIVEC_REGNO_P (last_regno); |
| } |
| } |
| |
| /* 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 (last_regno); |
| |
| /* The float registers (except for VSX vector modes) can only hold floating |
| modes and DImode. */ |
| if (FP_REGNO_P (regno)) |
| { |
| if (SCALAR_FLOAT_MODE_P (mode) |
| && (mode != TDmode || (regno % 2) == 0) |
| && FP_REGNO_P (last_regno)) |
| return 1; |
| |
| if (GET_MODE_CLASS (mode) == MODE_INT |
| && GET_MODE_SIZE (mode) == UNITS_PER_FP_WORD) |
| return 1; |
| |
| if (PAIRED_SIMD_REGNO_P (regno) && TARGET_PAIRED_FLOAT |
| && PAIRED_VECTOR_MODE (mode)) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* The CR register can only hold CC modes. */ |
| if (CR_REGNO_P (regno)) |
| return GET_MODE_CLASS (mode) == MODE_CC; |
| |
| if (CA_REGNO_P (regno)) |
| return mode == Pmode || mode == SImode; |
| |
| /* AltiVec only in AldyVec registers. */ |
| if (ALTIVEC_REGNO_P (regno)) |
| return (VECTOR_MEM_ALTIVEC_OR_VSX_P (mode) |
| || mode == V1TImode); |
| |
| /* ...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 non-VSX TImode or PTImode anywhere except general register |
| and it must be able to fit within the register set. */ |
| |
| return GET_MODE_SIZE (mode) <= UNITS_PER_WORD; |
| } |
| |
| /* Print interesting facts about registers. */ |
| static void |
| rs6000_debug_reg_print (int first_regno, int last_regno, const char *reg_name) |
| { |
| int r, m; |
| |
| for (r = first_regno; r <= last_regno; ++r) |
| { |
| const char *comma = ""; |
| int len; |
| |
| if (first_regno == last_regno) |
| fprintf (stderr, "%s:\t", reg_name); |
| else |
| fprintf (stderr, "%s%d:\t", reg_name, r - first_regno); |
| |
| len = 8; |
| for (m = 0; m < NUM_MACHINE_MODES; ++m) |
| if (rs6000_hard_regno_mode_ok_p[m][r] && rs6000_hard_regno_nregs[m][r]) |
| { |
| if (len > 70) |
| { |
| fprintf (stderr, ",\n\t"); |
| len = 8; |
| comma = ""; |
| } |
| |
| if (rs6000_hard_regno_nregs[m][r] > 1) |
| len += fprintf (stderr, "%s%s/%d", comma, GET_MODE_NAME (m), |
| rs6000_hard_regno_nregs[m][r]); |
| else |
| len += fprintf (stderr, "%s%s", comma, GET_MODE_NAME (m)); |
| |
| comma = ", "; |
| } |
| |
| if (call_used_regs[r]) |
| { |
| if (len > 70) |
| { |
| fprintf (stderr, ",\n\t"); |
| len = 8; |
| comma = ""; |
| } |
| |
| len += fprintf (stderr, "%s%s", comma, "call-used"); |
| comma = ", "; |
| } |
| |
| if (fixed_regs[r]) |
| { |
| if (len > 70) |
| { |
| fprintf (stderr, ",\n\t"); |
| len = 8; |
| comma = ""; |
| } |
| |
| len += fprintf (stderr, "%s%s", comma, "fixed"); |
| comma = ", "; |
| } |
| |
| if (len > 70) |
| { |
| fprintf (stderr, ",\n\t"); |
| comma = ""; |
| } |
| |
| len += fprintf (stderr, "%sreg-class = %s", comma, |
| reg_class_names[(int)rs6000_regno_regclass[r]]); |
| comma = ", "; |
| |
| if (len > 70) |
| { |
| fprintf (stderr, ",\n\t"); |
| comma = ""; |
| } |
| |
| fprintf (stderr, "%sregno = %d\n", comma, r); |
| } |
| } |
| |
| static const char * |
| rs6000_debug_vector_unit (enum rs6000_vector v) |
| { |
| const char *ret; |
| |
| switch (v) |
| { |
| case VECTOR_NONE: ret = "none"; break; |
| case VECTOR_ALTIVEC: ret = "altivec"; break; |
| case VECTOR_VSX: ret = "vsx"; break; |
| case VECTOR_P8_VECTOR: ret = "p8_vector"; break; |
| case VECTOR_PAIRED: ret = "paired"; break; |
| case VECTOR_SPE: ret = "spe"; break; |
| case VECTOR_OTHER: ret = "other"; break; |
| default: ret = "unknown"; break; |
| } |
| |
| return ret; |
| } |
| |
| /* Inner function printing just the address mask for a particular reload |
| register class. */ |
| DEBUG_FUNCTION char * |
| rs6000_debug_addr_mask (addr_mask_type mask, bool keep_spaces) |
| { |
| static char ret[8]; |
| char *p = ret; |
| |
| if ((mask & RELOAD_REG_VALID) != 0) |
| *p++ = 'v'; |
| else if (keep_spaces) |
| *p++ = ' '; |
| |
| if ((mask & RELOAD_REG_MULTIPLE) != 0) |
| *p++ = 'm'; |
| else if (keep_spaces) |
| *p++ = ' '; |
| |
| if ((mask & RELOAD_REG_INDEXED) != 0) |
| *p++ = 'i'; |
| else if (keep_spaces) |
| *p++ = ' '; |
| |
| if ((mask & RELOAD_REG_OFFSET) != 0) |
| *p++ = 'o'; |
| else if (keep_spaces) |
| *p++ = ' '; |
| |
| if ((mask & RELOAD_REG_PRE_INCDEC) != 0) |
| *p++ = '+'; |
| else if (keep_spaces) |
| *p++ = ' '; |
| |
| if ((mask & RELOAD_REG_PRE_MODIFY) != 0) |
| *p++ = '+'; |
| else if (keep_spaces) |
| *p++ = ' '; |
| |
| if ((mask & RELOAD_REG_AND_M16) != 0) |
| *p++ = '&'; |
| else if (keep_spaces) |
| *p++ = ' '; |
| |
| *p = '\0'; |
| |
| return ret; |
| } |
| |
| /* Print the address masks in a human readble fashion. */ |
| DEBUG_FUNCTION void |
| rs6000_debug_print_mode (ssize_t m) |
| { |
| ssize_t rc; |
| |
| fprintf (stderr, "Mode: %-5s", GET_MODE_NAME (m)); |
| for (rc = 0; rc < N_RELOAD_REG; rc++) |
| fprintf (stderr, " %s: %s", reload_reg_map[rc].name, |
| rs6000_debug_addr_mask (reg_addr[m].addr_mask[rc], true)); |
| |
| if (rs6000_vector_unit[m] != VECTOR_NONE |
| || rs6000_vector_mem[m] != VECTOR_NONE |
| || (reg_addr[m].reload_store != CODE_FOR_nothing) |
| || (reg_addr[m].reload_load != CODE_FOR_nothing) |
| || reg_addr[m].scalar_in_vmx_p) |
| { |
| fprintf (stderr, |
| " Vector-arith=%-10s Vector-mem=%-10s Reload=%c%c Upper=%c", |
| rs6000_debug_vector_unit (rs6000_vector_unit[m]), |
| rs6000_debug_vector_unit (rs6000_vector_mem[m]), |
| (reg_addr[m].reload_store != CODE_FOR_nothing) ? 's' : '*', |
| (reg_addr[m].reload_load != CODE_FOR_nothing) ? 'l' : '*', |
| (reg_addr[m].scalar_in_vmx_p) ? 'y' : 'n'); |
| } |
| |
| fputs ("\n", stderr); |
| } |
| |
| #define DEBUG_FMT_ID "%-32s= " |
| #define DEBUG_FMT_D DEBUG_FMT_ID "%d\n" |
| #define DEBUG_FMT_WX DEBUG_FMT_ID "%#.12" HOST_WIDE_INT_PRINT "x: " |
| #define DEBUG_FMT_S DEBUG_FMT_ID "%s\n" |
| |
| /* Print various interesting information with -mdebug=reg. */ |
| static void |
| rs6000_debug_reg_global (void) |
| { |
| static const char *const tf[2] = { "false", "true" }; |
| const char *nl = (const char *)0; |
| int m; |
| size_t m1, m2, v; |
| char costly_num[20]; |
| char nop_num[20]; |
| char flags_buffer[40]; |
| const char *costly_str; |
| const char *nop_str; |
| const char *trace_str; |
| const char *abi_str; |
| const char *cmodel_str; |
| struct cl_target_option cl_opts; |
| |
| /* Modes we want tieable information on. */ |
| static const machine_mode print_tieable_modes[] = { |
| QImode, |
| HImode, |
| SImode, |
| DImode, |
| TImode, |
| PTImode, |
| SFmode, |
| DFmode, |
| TFmode, |
| SDmode, |
| DDmode, |
| TDmode, |
| V8QImode, |
| V4HImode, |
| V2SImode, |
| V16QImode, |
| V8HImode, |
| V4SImode, |
| V2DImode, |
| V1TImode, |
| V32QImode, |
| V16HImode, |
| V8SImode, |
| V4DImode, |
| V2TImode, |
| V2SFmode, |
| V4SFmode, |
| V2DFmode, |
| V8SFmode, |
| V4DFmode, |
| CCmode, |
| CCUNSmode, |
| CCEQmode, |
| }; |
| |
| /* Virtual regs we are interested in. */ |
| const static struct { |
| int regno; /* register number. */ |
| const char *name; /* register name. */ |
| } virtual_regs[] = { |
| { STACK_POINTER_REGNUM, "stack pointer:" }, |
| { TOC_REGNUM, "toc: " }, |
| { STATIC_CHAIN_REGNUM, "static chain: " }, |
| { RS6000_PIC_OFFSET_TABLE_REGNUM, "pic offset: " }, |
| { HARD_FRAME_POINTER_REGNUM, "hard frame: " }, |
| { ARG_POINTER_REGNUM, "arg pointer: " }, |
| { FRAME_POINTER_REGNUM, "frame pointer:" }, |
| { FIRST_PSEUDO_REGISTER, "first pseudo: " }, |
| { FIRST_VIRTUAL_REGISTER, "first virtual:" }, |
| { VIRTUAL_INCOMING_ARGS_REGNUM, "incoming_args:" }, |
| { VIRTUAL_STACK_VARS_REGNUM, "stack_vars: " }, |
| { VIRTUAL_STACK_DYNAMIC_REGNUM, "stack_dynamic:" }, |
| { VIRTUAL_OUTGOING_ARGS_REGNUM, "outgoing_args:" }, |
| { VIRTUAL_CFA_REGNUM, "cfa (frame): " }, |
| { VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM, "stack boundry:" }, |
| { LAST_VIRTUAL_REGISTER, "last virtual: " }, |
| }; |
| |
| fputs ("\nHard register information:\n", stderr); |
| rs6000_debug_reg_print (FIRST_GPR_REGNO, LAST_GPR_REGNO, "gr"); |
| rs6000_debug_reg_print (FIRST_FPR_REGNO, LAST_FPR_REGNO, "fp"); |
| rs6000_debug_reg_print (FIRST_ALTIVEC_REGNO, |
| LAST_ALTIVEC_REGNO, |
| "vs"); |
| rs6000_debug_reg_print (LR_REGNO, LR_REGNO, "lr"); |
| rs6000_debug_reg_print (CTR_REGNO, CTR_REGNO, "ctr"); |
| rs6000_debug_reg_print (CR0_REGNO, CR7_REGNO, "cr"); |
| rs6000_debug_reg_print (CA_REGNO, CA_REGNO, "ca"); |
| rs6000_debug_reg_print (VRSAVE_REGNO, VRSAVE_REGNO, "vrsave"); |
| rs6000_debug_reg_print (VSCR_REGNO, VSCR_REGNO, "vscr"); |
| rs6000_debug_reg_print (SPE_ACC_REGNO, SPE_ACC_REGNO, "spe_a"); |
| rs6000_debug_reg_print (SPEFSCR_REGNO, SPEFSCR_REGNO, "spe_f"); |
| |
| fputs ("\nVirtual/stack/frame registers:\n", stderr); |
| for (v = 0; v < ARRAY_SIZE (virtual_regs); v++) |
| fprintf (stderr, "%s regno = %3d\n", virtual_regs[v].name, virtual_regs[v].regno); |
| |
| fprintf (stderr, |
| "\n" |
| "d reg_class = %s\n" |
| "f reg_class = %s\n" |
| "v reg_class = %s\n" |
| "wa reg_class = %s\n" |
| "wd reg_class = %s\n" |
| "wf reg_class = %s\n" |
| "wg reg_class = %s\n" |
| "wh reg_class = %s\n" |
| "wi reg_class = %s\n" |
| "wj reg_class = %s\n" |
| "wk reg_class = %s\n" |
| "wl reg_class = %s\n" |
| "wm reg_class = %s\n" |
| "wr reg_class = %s\n" |
| "ws reg_class = %s\n" |
| "wt reg_class = %s\n" |
| "wu reg_class = %s\n" |
| "wv reg_class = %s\n" |
| "ww reg_class = %s\n" |
| "wx reg_class = %s\n" |
| "wy reg_class = %s\n" |
| "wz reg_class = %s\n" |
| "\n", |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_d]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_f]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_v]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wa]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wd]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wf]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wg]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wh]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wi]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wj]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wk]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wl]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wm]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wr]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_ws]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wt]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wu]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wv]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_ww]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wx]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wy]], |
| reg_class_names[rs6000_constraints[RS6000_CONSTRAINT_wz]]); |
| |
| nl = "\n"; |
| for (m = 0; m < NUM_MACHINE_MODES; ++m) |
| rs6000_debug_print_mode (m); |
| |
| fputs ("\n", stderr); |
| |
| for (m1 = 0; m1 < ARRAY_SIZE (print_tieable_modes); m1++) |
| { |
| machine_mode mode1 = print_tieable_modes[m1]; |
| bool first_time = true; |
| |
| nl = (const char *)0; |
| for (m2 = 0; m2 < ARRAY_SIZE (print_tieable_modes); m2++) |
| { |
| machine_mode mode2 = print_tieable_modes[m2]; |
| if (mode1 != mode2 && MODES_TIEABLE_P (mode1, mode2)) |
| { |
| if (first_time) |
| { |
| fprintf (stderr, "Tieable modes %s:", GET_MODE_NAME (mode1)); |
| nl = "\n"; |
| first_time = false; |
| } |
| |
| fprintf (stderr, " %s", GET_MODE_NAME (mode2)); |
| } |
| } |
| |
| if (!first_time) |
| fputs ("\n", stderr); |
| } |
| |
| if (nl) |
| fputs (nl, stderr); |
| |
| if (rs6000_recip_control) |
| { |
| fprintf (stderr, "\nReciprocal mask = 0x%x\n", rs6000_recip_control); |
| |
| for (m = 0; m < NUM_MACHINE_MODES; ++m) |
| if (rs6000_recip_bits[m]) |
| { |
| fprintf (stderr, |
| "Reciprocal estimate mode: %-5s divide: %s rsqrt: %s\n", |
| GET_MODE_NAME (m), |
| (RS6000_RECIP_AUTO_RE_P (m) |
| ? "auto" |
| : (RS6000_RECIP_HAVE_RE_P (m) ? "have" : "none")), |
| (RS6000_RECIP_AUTO_RSQRTE_P (m) |
| ? "auto" |
| : (RS6000_RECIP_HAVE_RSQRTE_P (m) ? "have" : "none"))); |
| } |
| |
| fputs ("\n", stderr); |
| } |
| |
| if (rs6000_cpu_index >= 0) |
| { |
| const char *name = processor_target_table[rs6000_cpu_index].name; |
| HOST_WIDE_INT flags |
| = processor_target_table[rs6000_cpu_index].target_enable; |
| |
| sprintf (flags_buffer, "-mcpu=%s flags", name); |
| rs6000_print_isa_options (stderr, 0, flags_buffer, flags); |
| } |
| else |
| fprintf (stderr, DEBUG_FMT_S, "cpu", "<none>"); |
| |
| if (rs6000_tune_index >= 0) |
| { |
| const char *name = processor_target_table[rs6000_tune_index].name; |
| HOST_WIDE_INT flags |
| = processor_target_table[rs6000_tune_index].target_enable; |
| |
| sprintf (flags_buffer, "-mtune=%s flags", name); |
| rs6000_print_isa_options (stderr, 0, flags_buffer, flags); |
| } |
| else |
| fprintf (stderr, DEBUG_FMT_S, "tune", "<none>"); |
| |
| cl_target_option_save (&cl_opts, &global_options); |
| rs6000_print_isa_options (stderr, 0, "rs6000_isa_flags", |
| rs6000_isa_flags); |
| |
| rs6000_print_isa_options (stderr, 0, "rs6000_isa_flags_explicit", |
| rs6000_isa_flags_explicit); |
| |
| rs6000_print_builtin_options (stderr, 0, "rs6000_builtin_mask", |
| rs6000_builtin_mask); |
| |
| rs6000_print_isa_options (stderr, 0, "TARGET_DEFAULT", TARGET_DEFAULT); |
| |
| fprintf (stderr, DEBUG_FMT_S, "--with-cpu default", |
| OPTION_TARGET_CPU_DEFAULT ? OPTION_TARGET_CPU_DEFAULT : "<none>"); |
| |
| switch (rs6000_sched_costly_dep) |
| { |
| case max_dep_latency: |
| costly_str = "max_dep_latency"; |
| break; |
| |
| case no_dep_costly: |
| costly_str = "no_dep_costly"; |
| break; |
| |
| case all_deps_costly: |
| costly_str = "all_deps_costly"; |
| break; |
| |
| case true_store_to_load_dep_costly: |
| costly_str = "true_store_to_load_dep_costly"; |
| break; |
| |
| case store_to_load_dep_costly: |
| costly_str = "store_to_load_dep_costly"; |
| break; |
| |
| default: |
| costly_str = costly_num; |
| sprintf (costly_num, "%d", (int)rs6000_sched_costly_dep); |
| break; |
| } |
| |
| fprintf (stderr, DEBUG_FMT_S, "sched_costly_dep", costly_str); |
| |
| switch (rs6000_sched_insert_nops) |
| { |
| case sched_finish_regroup_exact: |
| nop_str = "sched_finish_regroup_exact"; |
| break; |
| |
| case sched_finish_pad_groups: |
| nop_str = "sched_finish_pad_groups"; |
| break; |
| |
| case sched_finish_none: |
| nop_str = "sched_finish_none"; |
| break; |
| |
| default: |
| nop_str = nop_num; |
| sprintf (nop_num, "%d", (int)rs6000_sched_insert_nops); |
| break; |
| } |
| |
| fprintf (stderr, DEBUG_FMT_S, "sched_insert_nops", nop_str); |
| |
| switch (rs6000_sdata) |
| { |
| default: |
| case SDATA_NONE: |
| break; |
| |
| case SDATA_DATA: |
| fprintf (stderr, DEBUG_FMT_S, "sdata", "data"); |
| break; |
| |
| case SDATA_SYSV: |
| fprintf (stderr, DEBUG_FMT_S, "sdata", "sysv"); |
| break; |
| |
| case SDATA_EABI: |
| fprintf (stderr, DEBUG_FMT_S, "sdata", "eabi"); |
| break; |
| |
| } |
| |
| switch (rs6000_traceback) |
| { |
| case traceback_default: trace_str = "default"; break; |
| case traceback_none: trace_str = "none"; break; |
| case traceback_part: trace_str = "part"; break; |
| case traceback_full: trace_str = "full"; break; |
| default: trace_str = "unknown"; break; |
| } |
| |
| fprintf (stderr, DEBUG_FMT_S, "traceback", trace_str); |
| |
| switch (rs6000_current_cmodel) |
| { |
| case CMODEL_SMALL: cmodel_str = "small"; break; |
| case CMODEL_MEDIUM: cmodel_str = "medium"; break; |
| case CMODEL_LARGE: cmodel_str = "large"; break; |
| default: cmodel_str = "unknown"; break; |
| } |
| |
| fprintf (stderr, DEBUG_FMT_S, "cmodel", cmodel_str); |
| |
| switch (rs6000_current_abi) |
| { |
| case ABI_NONE: abi_str = "none"; break; |
| case ABI_AIX: abi_str = "aix"; break; |
| case ABI_ELFv2: abi_str = "ELFv2"; break; |
| case ABI_V4: abi_str = "V4"; break; |
| case ABI_DARWIN: abi_str = "darwin"; break; |
| default: abi_str = "unknown"; break; |
| } |
| |
| fprintf (stderr, DEBUG_FMT_S, "abi", abi_str); |
| |
| if (rs6000_altivec_abi) |
| fprintf (stderr, DEBUG_FMT_S, "altivec_abi", "true"); |
| |
| if (rs6000_spe_abi) |
| fprintf (stderr, DEBUG_FMT_S, "spe_abi", "true"); |
| |
| if (rs6000_darwin64_abi) |
| fprintf (stderr, DEBUG_FMT_S, "darwin64_abi", "true"); |
| |
| if (rs6000_float_gprs) |
| fprintf (stderr, DEBUG_FMT_S, "float_gprs", "true"); |
| |
| fprintf (stderr, DEBUG_FMT_S, "fprs", |
| (TARGET_FPRS ? "true" : "false")); |
| |
| fprintf (stderr, DEBUG_FMT_S, "single_float", |
| (TARGET_SINGLE_FLOAT ? "true" : "false")); |
| |
| fprintf (stderr, DEBUG_FMT_S, "double_float", |
| (TARGET_DOUBLE_FLOAT ? "true" : "false")); |
| |
| fprintf (stderr, DEBUG_FMT_S, "soft_float", |
| (TARGET_SOFT_FLOAT ? "true" : "false")); |
| |
| fprintf (stderr, DEBUG_FMT_S, "e500_single", |
| (TARGET_E500_SINGLE ? "true" : "false")); |
| |
| fprintf (stderr, DEBUG_FMT_S, "e500_double", |
| (TARGET_E500_DOUBLE ? "true" : "false")); |
| |
| if (TARGET_LINK_STACK) |
| fprintf (stderr, DEBUG_FMT_S, "link_stack", "true"); |
| |
| if (targetm.lra_p ()) |
| fprintf (stderr, DEBUG_FMT_S, "lra", "true"); |
| |
| if (TARGET_P8_FUSION) |
| fprintf (stderr, DEBUG_FMT_S, "p8 fusion", |
| (TARGET_P8_FUSION_SIGN) ? "zero+sign" : "zero"); |
| |
| fprintf (stderr, DEBUG_FMT_S, "plt-format", |
| TARGET_SECURE_PLT ? "secure" : "bss"); |
| fprintf (stderr, DEBUG_FMT_S, "struct-return", |
| aix_struct_return ? "aix" : "sysv"); |
| fprintf (stderr, DEBUG_FMT_S, "always_hint", tf[!!rs6000_always_hint]); |
| fprintf (stderr, DEBUG_FMT_S, "sched_groups", tf[!!rs6000_sched_groups]); |
| fprintf (stderr, DEBUG_FMT_S, "align_branch", |
| tf[!!rs6000_align_branch_targets]); |
| fprintf (stderr, DEBUG_FMT_D, "tls_size", rs6000_tls_size); |
| fprintf (stderr, DEBUG_FMT_D, "long_double_size", |
| rs6000_long_double_type_size); |
| fprintf (stderr, DEBUG_FMT_D, "sched_restricted_insns_priority", |
| (int)rs6000_sched_restricted_insns_priority); |
| fprintf (stderr, DEBUG_FMT_D, "Number of standard builtins", |
| (int)END_BUILTINS); |
| fprintf (stderr, DEBUG_FMT_D, "Number of rs6000 builtins", |
| (int)RS6000_BUILTIN_COUNT); |
| |
| if (TARGET_VSX) |
| fprintf (stderr, DEBUG_FMT_D, "VSX easy 64-bit scalar element", |
| (int)VECTOR_ELEMENT_SCALAR_64BIT); |
| } |
| |
| |
| /* Update the addr mask bits in reg_addr to help secondary reload and go if |
| legitimate address support to figure out the appropriate addressing to |
| use. */ |
| |
| static void |
| rs6000_setup_reg_addr_masks (void) |
| { |
| ssize_t rc, reg, m, nregs; |
| addr_mask_type any_addr_mask, addr_mask; |
| |
| for (m = 0; m < NUM_MACHINE_MODES; ++m) |
| { |
| machine_mode m2 = (machine_mode)m; |
| |
| /* SDmode is special in that we want to access it only via REG+REG |
| addressing on power7 and above, since we want to use the LFIWZX and |
| STFIWZX instructions to load it. */ |
| bool indexed_only_p = (m == SDmode && TARGET_NO_SDMODE_STACK); |
| |
| any_addr_mask = 0; |
| for (rc = FIRST_RELOAD_REG_CLASS; rc <= LAST_RELOAD_REG_CLASS; rc++) |
| { |
| addr_mask = 0; |
| reg = reload_reg_map[rc].reg; |
| |
| /* Can mode values go in the GPR/FPR/Altivec registers? */ |
| if (reg >= 0 && rs6000_hard_regno_mode_ok_p[m][reg]) |
| { |
| nregs = rs6000_hard_regno_nregs[m][reg]; |
| addr_mask |= RELOAD_REG_VALID; |
| |
| /* Indicate if the mode takes more than 1 physical register. If |
| it takes a single register, indicate it can do REG+REG |
| addressing. */ |
| if (nregs > 1 || m == BLKmode) |
| addr_mask |= RELOAD_REG_MULTIPLE; |
| else |
| addr_mask |= RELOAD_REG_INDEXED; |
| |
| /* Figure out if we can do PRE_INC, PRE_DEC, or PRE_MODIFY |
| addressing. Restrict addressing on SPE for 64-bit types |
| because of the SUBREG hackery used to address 64-bit floats in |
| '32-bit' GPRs. */ |
| |
| if (TARGET_UPDATE |
| && (rc == RELOAD_REG_GPR || rc == RELOAD_REG_FPR) |
| && GET_MODE_SIZE (m2) <= 8 |
| && !VECTOR_MODE_P (m2) |
| && !COMPLEX_MODE_P (m2) |
| && !indexed_only_p |
| && !(TARGET_E500_DOUBLE && GET_MODE_SIZE (m2) == 8)) |
| { |
| addr_mask |= RELOAD_REG_PRE_INCDEC; |
| |
| /* PRE_MODIFY is more restricted than PRE_INC/PRE_DEC in that |
| we don't allow PRE_MODIFY for some multi-register |
| operations. */ |
| switch (m) |
| { |
| default: |
| addr_mask |= RELOAD_REG_PRE_MODIFY; |
| break; |
| |
| case DImode: |
| if (TARGET_POWERPC64) |
| addr_mask |= RELOAD_REG_PRE_MODIFY; |
| break; |
| |
| case DFmode: |
| case DDmode: |
| if (TARGET_DF_INSN) |
| addr_mask |= RELOAD_REG_PRE_MODIFY; |
| break; |
| } |
| } |
| } |
| |
| /* GPR and FPR registers can do REG+OFFSET addressing, except |
| possibly for SDmode. */ |
| if ((addr_mask != 0) && !indexed_only_p |
| && (rc == RELOAD_REG_GPR || rc == RELOAD_REG_FPR)) |
| addr_mask |= RELOAD_REG_OFFSET; |
| |
| /* VMX registers can do (REG & -16) and ((REG+REG) & -16) |
| addressing on 128-bit types. */ |
| if (rc == RELOAD_REG_VMX && GET_MODE_SIZE (m2) == 16 |
| && (addr_mask & RELOAD_REG_VALID) != 0) |
| addr_mask |= RELOAD_REG_AND_M16; |
| |
| reg_addr[m].addr_mask[rc] = addr_mask; |
| any_addr_mask |= addr_mask; |
| } |
| |
| reg_addr[m].addr_mask[RELOAD_REG_ANY] = any_addr_mask; |
| } |
| } |
| |
| |
| /* Initialize the various global tables that are based on register size. */ |
| static void |
| rs6000_init_hard_regno_mode_ok (bool global_init_p) |
| { |
| ssize_t r, m, c; |
| int align64; |
| int align32; |
| |
| /* Precalculate REGNO_REG_CLASS. */ |
| rs6000_regno_regclass[0] = GENERAL_REGS; |
| for (r = 1; r < 32; ++r) |
| rs6000_regno_regclass[r] = BASE_REGS; |
| |
| for (r = 32; r < 64; ++r) |
| rs6000_regno_regclass[r] = FLOAT_REGS; |
| |
| for (r = 64; r < FIRST_PSEUDO_REGISTER; ++r) |
| rs6000_regno_regclass[r] = NO_REGS; |
| |
| for (r = FIRST_ALTIVEC_REGNO; r <= LAST_ALTIVEC_REGNO; ++r) |
| rs6000_regno_regclass[r] = ALTIVEC_REGS; |
| |
| rs6000_regno_regclass[CR0_REGNO] = CR0_REGS; |
| for (r = CR1_REGNO; r <= CR7_REGNO; ++r) |
| rs6000_regno_regclass[r] = CR_REGS; |
| |
| rs6000_regno_regclass[LR_REGNO] = LINK_REGS; |
| rs6000_regno_regclass[CTR_REGNO] = CTR_REGS; |
| rs6000_regno_regclass[CA_REGNO] = NO_REGS; |
| rs6000_regno_regclass[VRSAVE_REGNO] = VRSAVE_REGS; |
| rs6000_regno_regclass[VSCR_REGNO] = VRSAVE_REGS; |
| rs6000_regno_regclass[SPE_ACC_REGNO] = SPE_ACC_REGS; |
| rs6000_regno_regclass[SPEFSCR_REGNO] = SPEFSCR_REGS; |
| rs6000_regno_regclass[TFHAR_REGNO] = SPR_REGS; |
| rs6000_regno_regclass[TFIAR_REGNO] = SPR_REGS; |
| rs6000_regno_regclass[TEXASR_REGNO] = SPR_REGS; |
| rs6000_regno_regclass[ARG_POINTER_REGNUM] = BASE_REGS; |
| rs6000_regno_regclass[FRAME_POINTER_REGNUM] = BASE_REGS; |
| |
| /* Precalculate register class to simpler reload register class. We don't |
| need all of the register classes that are combinations of different |
| classes, just the simple ones that have constraint letters. */ |
| for (c = 0; c < N_REG_CLASSES; c++) |
| reg_class_to_reg_type[c] = NO_REG_TYPE; |
| |
| reg_class_to_reg_type[(int)GENERAL_REGS] = GPR_REG_TYPE; |
| reg_class_to_reg_type[(int)BASE_REGS] = GPR_REG_TYPE; |
| reg_class_to_reg_type[(int)VSX_REGS] = VSX_REG_TYPE; |
| reg_class_to_reg_type[(int)VRSAVE_REGS] = SPR_REG_TYPE; |
| reg_class_to_reg_type[(int)VSCR_REGS] = SPR_REG_TYPE; |
| reg_class_to_reg_type[(int)LINK_REGS] = SPR_REG_TYPE; |
| reg_class_to_reg_type[(int)CTR_REGS] = SPR_REG_TYPE; |
| reg_class_to_reg_type[(int)LINK_OR_CTR_REGS] = SPR_REG_TYPE; |
| reg_class_to_reg_type[(int)CR_REGS] = CR_REG_TYPE; |
| reg_class_to_reg_type[(int)CR0_REGS] = CR_REG_TYPE; |
| reg_class_to_reg_type[(int)SPE_ACC_REGS] = SPE_ACC_TYPE; |
| reg_class_to_reg_type[(int)SPEFSCR_REGS] = SPEFSCR_REG_TYPE; |
| |
| if (TARGET_VSX) |
| { |
| reg_class_to_reg_type[(int)FLOAT_REGS] = VSX_REG_TYPE; |
| reg_class_to_reg_type[(int)ALTIVEC_REGS] = VSX_REG_TYPE; |
| } |
| else |
| { |
| reg_class_to_reg_type[(int)FLOAT_REGS] = FPR_REG_TYPE; |
| reg_class_to_reg_type[(int)ALTIVEC_REGS] = ALTIVEC_REG_TYPE; |
| } |
| |
| /* Precalculate the valid memory formats as well as the vector information, |
| this must be set up before the rs6000_hard_regno_nregs_internal calls |
| below. */ |
| gcc_assert ((int)VECTOR_NONE == 0); |
| memset ((void *) &rs6000_vector_unit[0], '\0', sizeof (rs6000_vector_unit)); |
| memset ((void *) &rs6000_vector_mem[0], '\0', sizeof (rs6000_vector_unit)); |
| |
| gcc_assert ((int)CODE_FOR_nothing == 0); |
| memset ((void *) ®_addr[0], '\0', sizeof (reg_addr)); |
| |
| gcc_assert ((int)NO_REGS == 0); |
| memset ((void *) &rs6000_constraints[0], '\0', sizeof (rs6000_constraints)); |
| |
| /* The VSX hardware allows native alignment for vectors, but control whether the compiler |
| believes it can use native alignment or still uses 128-bit alignment. */ |
| if (TARGET_VSX && !TARGET_VSX_ALIGN_128) |
| { |
| align64 = 64; |
| align32 = 32; |
| } |
| else |
| { |
| align64 = 128; |
| align32 = 128; |
| } |
| |
| /* V2DF mode, VSX only. */ |
| if (TARGET_VSX) |
| { |
| rs6000_vector_unit[V2DFmode] = VECTOR_VSX; |
| rs6000_vector_mem[V2DFmode] = VECTOR_VSX; |
| rs6000_vector_align[V2DFmode] = align64; |
| } |
| |
| /* V4SF mode, either VSX or Altivec. */ |
| if (TARGET_VSX) |
| { |
| rs6000_vector_unit[V4SFmode] = VECTOR_VSX; |
| rs6000_vector_mem[V4SFmode] = VECTOR_VSX; |
| rs6000_vector_align[V4SFmode] = align32; |
| } |
| else if (TARGET_ALTIVEC) |
| { |
| rs6000_vector_unit[V4SFmode] = VECTOR_ALTIVEC; |
| rs6000_vector_mem[V4SFmode] = VECTOR_ALTIVEC; |
| rs6000_vector_align[V4SFmode] = align32; |
| } |
| |
| /* V16QImode, V8HImode, V4SImode are Altivec only, but possibly do VSX loads |
| and stores. */ |
| if (TARGET_ALTIVEC) |
| { |
| rs6000_vector_unit[V4SImode] = VECTOR_ALTIVEC; |
| rs6000_vector_unit[V8HImode] = VECTOR_ALTIVEC; |
| rs6000_vector_unit[V16QImode] = VECTOR_ALTIVEC; |
| rs6000_vector_align[V4SImode] = align32; |
| rs6000_vector_align[V8HImode] = align32; |
| rs6000_vector_align[V16QImode] = align32; |
| |
| if (TARGET_VSX) |
| { |
| rs6000_vector_mem[V4SImode] = VECTOR_VSX; |
| rs6000_vector_mem[V8HImode] = VECTOR_VSX; |
| rs6000_vector_mem[V16QImode] = VECTOR_VSX; |
| } |
| else |
| { |
| rs6000_vector_mem[V4SImode] = VECTOR_ALTIVEC; |
| rs6000_vector_mem[V8HImode] = VECTOR_ALTIVEC; |
| rs6000_vector_mem[V16QImode] = VECTOR_ALTIVEC; |
| } |
| } |
| |
| /* V2DImode, full mode depends on ISA 2.07 vector mode. Allow under VSX to |
| do insert/splat/extract. Altivec doesn't have 64-bit integer support. */ |
| if (TARGET_VSX) |
| { |
| rs6000_vector_mem[V2DImode] = VECTOR_VSX; |
| rs6000_vector_unit[V2DImode] |
| = (TARGET_P8_VECTOR) ? VECTOR_P8_VECTOR : VECTOR_NONE; |
| rs6000_vector_align[V2DImode] = align64; |
| |
| rs6000_vector_mem[V1TImode] = VECTOR_VSX; |
| rs6000_vector_unit[V1TImode] |
| = (TARGET_P8_VECTOR) ? VECTOR_P8_VECTOR : VECTOR_NONE; |
| rs6000_vector_align[V1TImode] = 128; |
| } |
| |
| /* DFmode, see if we want to use the VSX unit. Memory is handled |
| differently, so don't set rs6000_vector_mem. */ |
| if (TARGET_VSX && TARGET_VSX_SCALAR_DOUBLE) |
| { |
| rs6000_vector_unit[DFmode] = VECTOR_VSX; |
| rs6000_vector_align[DFmode] = 64; |
| } |
| |
| /* SFmode, see if we want to use the VSX unit. */ |
| if (TARGET_P8_VECTOR && TARGET_VSX_SCALAR_FLOAT) |
| { |
| rs6000_vector_unit[SFmode] = VECTOR_VSX; |
| rs6000_vector_align[SFmode] = 32; |
| } |
| |
| /* Allow TImode in VSX register and set the VSX memory macros. */ |
| if (TARGET_VSX && TARGET_VSX_TIMODE) |
| { |
| rs6000_vector_mem[TImode] = VECTOR_VSX; |
| rs6000_vector_align[TImode] = align64; |
| } |
| |
| /* TODO add SPE and paired floating point vector support. */ |
| |
| /* Register class constraints for the constraints that depend on compile |
| switches. When the VSX code was added, different constraints were added |
| based on the type (DFmode, V2DFmode, V4SFmode). For the vector types, all |
| of the VSX registers are used. The register classes for scalar floating |
| point types is set, based on whether we allow that type into the upper |
| (Altivec) registers. GCC has register classes to target the Altivec |
| registers for load/store operations, to select using a VSX memory |
| operation instead of the traditional floating point operation. The |
| constraints are: |
| |
| d - Register class to use with traditional DFmode instructions. |
| f - Register class to use with traditional SFmode instructions. |
| v - Altivec register. |
| wa - Any VSX register. |
| wc - Reserved to represent individual CR bits (used in LLVM). |
| wd - Preferred register class for V2DFmode. |
| wf - Preferred register class for V4SFmode. |
| wg - Float register for power6x move insns. |
| wh - FP register for direct move instructions. |
| wi - FP or VSX register to hold 64-bit integers for VSX insns. |
| wj - FP or VSX register to hold 64-bit integers for direct moves. |
| wk - FP or VSX register to hold 64-bit doubles for direct moves. |
| wl - Float register if we can do 32-bit signed int loads. |
| wm - VSX register for ISA 2.07 direct move operations. |
| wn - always NO_REGS. |
| wr - GPR if 64-bit mode is permitted. |
| ws - Register class to do ISA 2.06 DF operations. |
| wt - VSX register for TImode in VSX registers. |
| wu - Altivec register for ISA 2.07 VSX SF/SI load/stores. |
| wv - Altivec register for ISA 2.06 VSX DF/DI load/stores. |
| ww - Register class to do SF conversions in with VSX operations. |
| wx - Float register if we can do 32-bit int stores. |
| wy - Register class to do ISA 2.07 SF operations. |
| wz - Float register if we can do 32-bit unsigned int loads. */ |
| |
| if (TARGET_HARD_FLOAT && TARGET_FPRS) |
| rs6000_constraints[RS6000_CONSTRAINT_f] = FLOAT_REGS; /* SFmode */ |
| |
| if (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) |
| rs6000_constraints[RS6000_CONSTRAINT_d] = FLOAT_REGS; /* DFmode */ |
| |
| if (TARGET_VSX) |
| { |
| rs6000_constraints[RS6000_CONSTRAINT_wa] = VSX_REGS; |
| rs6000_constraints[RS6000_CONSTRAINT_wd] = VSX_REGS; /* V2DFmode */ |
| rs6000_constraints[RS6000_CONSTRAINT_wf] = VSX_REGS; /* V4SFmode */ |
| rs6000_constraints[RS6000_CONSTRAINT_wi] = FLOAT_REGS; /* DImode */ |
| |
| if (TARGET_VSX_TIMODE) |
| rs6000_constraints[RS6000_CONSTRAINT_wt] = VSX_REGS; /* TImode */ |
| |
| if (TARGET_UPPER_REGS_DF) /* DFmode */ |
| { |
| rs6000_constraints[RS6000_CONSTRAINT_ws] = VSX_REGS; |
| rs6000_constraints[RS6000_CONSTRAINT_wv] = ALTIVEC_REGS; |
| } |
| else |
| rs6000_constraints[RS6000_CONSTRAINT_ws] = FLOAT_REGS; |
| } |
| |
| /* Add conditional constraints based on various options, to allow us to |
| collapse multiple insn patterns. */ |
| if (TARGET_ALTIVEC) |
| rs6000_constraints[RS6000_CONSTRAINT_v] = ALTIVEC_REGS; |
| |
| if (TARGET_MFPGPR) /* DFmode */ |
| rs6000_constraints[RS6000_CONSTRAINT_wg] = FLOAT_REGS; |
| |
| if (TARGET_LFIWAX) |
| rs6000_constraints[RS6000_CONSTRAINT_wl] = FLOAT_REGS; /* DImode */ |
| |
| if (TARGET_DIRECT_MOVE) |
| { |
| rs6000_constraints[RS6000_CONSTRAINT_wh] = FLOAT_REGS; |
| rs6000_constraints[RS6000_CONSTRAINT_wj] /* DImode */ |
| = rs6000_constraints[RS6000_CONSTRAINT_wi]; |
| rs6000_constraints[RS6000_CONSTRAINT_wk] /* DFmode */ |
| = rs6000_constraints[RS6000_CONSTRAINT_ws]; |
| rs6000_constraints[RS6000_CONSTRAINT_wm] = VSX_REGS; |
| } |
| |
| if (TARGET_POWERPC64) |
| rs6000_constraints[RS6000_CONSTRAINT_wr] = GENERAL_REGS; |
| |
| if (TARGET_P8_VECTOR && TARGET_UPPER_REGS_SF) /* SFmode */ |
| { |
| rs6000_constraints[RS6000_CONSTRAINT_wu] = ALTIVEC_REGS; |
| rs6000_constraints[RS6000_CONSTRAINT_wy] = VSX_REGS; |
| rs6000_constraints[RS6000_CONSTRAINT_ww] = VSX_REGS; |
| } |
| else if (TARGET_P8_VECTOR) |
| { |
| rs6000_constraints[RS6000_CONSTRAINT_wy] = FLOAT_REGS; |
| rs6000_constraints[RS6000_CONSTRAINT_ww] = FLOAT_REGS; |
| } |
| else if (TARGET_VSX) |
| rs6000_constraints[RS6000_CONSTRAINT_ww] = FLOAT_REGS; |
| |
| if (TARGET_STFIWX) |
| rs6000_constraints[RS6000_CONSTRAINT_wx] = FLOAT_REGS; /* DImode */ |
| |
| if (TARGET_LFIWZX) |
| rs6000_constraints[RS6000_CONSTRAINT_wz] = FLOAT_REGS; /* DImode */ |
| |
| /* Set up the reload helper and direct move functions. */ |
| if (TARGET_VSX || TARGET_ALTIVEC) |
| { |
| if (TARGET_64BIT) |
| { |
| reg_addr[V16QImode].reload_store = CODE_FOR_reload_v16qi_di_store; |
| reg_addr[V16QImode].reload_load = CODE_FOR_reload_v16qi_di_load; |
| reg_addr[V8HImode].reload_store = CODE_FOR_reload_v8hi_di_store; |
| reg_addr[V8HImode].reload_load = CODE_FOR_reload_v8hi_di_load; |
| reg_addr[V4SImode].reload_store = CODE_FOR_reload_v4si_di_store; |
| reg_addr[V4SImode].reload_load = CODE_FOR_reload_v4si_di_load; |
| reg_addr[V2DImode].reload_store = CODE_FOR_reload_v2di_di_store; |
| reg_addr[V2DImode].reload_load = CODE_FOR_reload_v2di_di_load; |
| reg_addr[V1TImode].reload_store = CODE_FOR_reload_v1ti_di_store; |
| reg_addr[V1TImode].reload_load = CODE_FOR_reload_v1ti_di_load; |
| reg_addr[V4SFmode].reload_store = CODE_FOR_reload_v4sf_di_store; |
| reg_addr[V4SFmode].reload_load = CODE_FOR_reload_v4sf_di_load; |
| reg_addr[V2DFmode].reload_store = CODE_FOR_reload_v2df_di_store; |
| reg_addr[V2DFmode].reload_load = CODE_FOR_reload_v2df_di_load; |
| reg_addr[DFmode].reload_store = CODE_FOR_reload_df_di_store; |
| reg_addr[DFmode].reload_load = CODE_FOR_reload_df_di_load; |
| reg_addr[DDmode].reload_store = CODE_FOR_reload_dd_di_store; |
| reg_addr[DDmode].reload_load = CODE_FOR_reload_dd_di_load; |
| reg_addr[SFmode].reload_store = CODE_FOR_reload_sf_di_store; |
| reg_addr[SFmode].reload_load = CODE_FOR_reload_sf_di_load; |
| |
| /* Only provide a reload handler for SDmode if lfiwzx/stfiwx are |
| available. */ |
| if (TARGET_NO_SDMODE_STACK) |
| { |
| reg_addr[SDmode].reload_store = CODE_FOR_reload_sd_di_store; |
| reg_addr[SDmode].reload_load = CODE_FOR_reload_sd_di_load; |
| } |
| |
| if (TARGET_VSX_TIMODE) |
| { |
| reg_addr[TImode].reload_store = CODE_FOR_reload_ti_di_store; |
| reg_addr[TImode].reload_load = CODE_FOR_reload_ti_di_load; |
| } |
| |
| if (TARGET_DIRECT_MOVE) |
| { |
| reg_addr[TImode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxti; |
| reg_addr[V1TImode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxv1ti; |
| reg_addr[V2DFmode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxv2df; |
| reg_addr[V2DImode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxv2di; |
| reg_addr[V4SFmode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxv4sf; |
| reg_addr[V4SImode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxv4si; |
| reg_addr[V8HImode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxv8hi; |
| reg_addr[V16QImode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxv16qi; |
| reg_addr[SFmode].reload_gpr_vsx = CODE_FOR_reload_gpr_from_vsxsf; |
| |
| reg_addr[TImode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprti; |
| reg_addr[V1TImode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprv1ti; |
| reg_addr[V2DFmode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprv2df; |
| reg_addr[V2DImode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprv2di; |
| reg_addr[V4SFmode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprv4sf; |
| reg_addr[V4SImode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprv4si; |
| reg_addr[V8HImode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprv8hi; |
| reg_addr[V16QImode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprv16qi; |
| reg_addr[SFmode].reload_vsx_gpr = CODE_FOR_reload_vsx_from_gprsf; |
| } |
| } |
| else |
| { |
| reg_addr[V16QImode].reload_store = CODE_FOR_reload_v16qi_si_store; |
| reg_addr[V16QImode].reload_load = CODE_FOR_reload_v16qi_si_load; |
| reg_addr[V8HImode].reload_store = CODE_FOR_reload_v8hi_si_store; |
| reg_addr[V8HImode].reload_load = CODE_FOR_reload_v8hi_si_load; |
| reg_addr[V4SImode].reload_store = CODE_FOR_reload_v4si_si_store; |
| reg_addr[V4SImode].reload_load = CODE_FOR_reload_v4si_si_load; |
| reg_addr[V2DImode].reload_store = CODE_FOR_reload_v2di_si_store; |
| reg_addr[V2DImode].reload_load = CODE_FOR_reload_v2di_si_load; |
| reg_addr[V1TImode].reload_store = CODE_FOR_reload_v1ti_si_store; |
| reg_addr[V1TImode].reload_load = CODE_FOR_reload_v1ti_si_load; |
| reg_addr[V4SFmode].reload_store = CODE_FOR_reload_v4sf_si_store; |
| reg_addr[V4SFmode].reload_load = CODE_FOR_reload_v4sf_si_load; |
| reg_addr[V2DFmode].reload_store = CODE_FOR_reload_v2df_si_store; |
| reg_addr[V2DFmode].reload_load = CODE_FOR_reload_v2df_si_load; |
| reg_addr[DFmode].reload_store = CODE_FOR_reload_df_si_store; |
| reg_addr[DFmode].reload_load = CODE_FOR_reload_df_si_load; |
| reg_addr[DDmode].reload_store = CODE_FOR_reload_dd_si_store; |
| reg_addr[DDmode].reload_load = CODE_FOR_reload_dd_si_load; |
| reg_addr[SFmode].reload_store = CODE_FOR_reload_sf_si_store; |
| reg_addr[SFmode].reload_load = CODE_FOR_reload_sf_si_load; |
| |
| /* Only provide a reload handler for SDmode if lfiwzx/stfiwx are |
| available. */ |
| if (TARGET_NO_SDMODE_STACK) |
| { |
| reg_addr[SDmode].reload_store = CODE_FOR_reload_sd_si_store; |
| reg_addr[SDmode].reload_load = CODE_FOR_reload_sd_si_load; |
| } |
| |
| if (TARGET_VSX_TIMODE) |
| { |
| reg_addr[TImode].reload_store = CODE_FOR_reload_ti_si_store; |
| reg_addr[TImode].reload_load = CODE_FOR_reload_ti_si_load; |
| } |
| |
| if (TARGET_DIRECT_MOVE) |
| { |
| reg_addr[DImode].reload_fpr_gpr = CODE_FOR_reload_fpr_from_gprdi; |
| reg_addr[DDmode].reload_fpr_gpr = CODE_FOR_reload_fpr_from_gprdd; |
| reg_addr[DFmode].reload_fpr_gpr = CODE_FOR_reload_fpr_from_gprdf; |
| } |
| } |
| |
| if (TARGET_UPPER_REGS_DF) |
| reg_addr[DFmode].scalar_in_vmx_p = true; |
| |
| if (TARGET_UPPER_REGS_SF) |
| reg_addr[SFmode].scalar_in_vmx_p = true; |
| } |
| |
| /* Precalculate HARD_REGNO_NREGS. */ |
| for (r = 0; r < FIRST_PSEUDO_REGISTER; ++r) |
| for (m = 0; m < NUM_MACHINE_MODES; ++m) |
| rs6000_hard_regno_nregs[m][r] |
| = rs6000_hard_regno_nregs_internal (r, (machine_mode)m); |
| |
| /* Precalculate HARD_REGNO_MODE_OK. */ |
| for (r = 0; r < FIRST_PSEUDO_REGISTER; ++r) |
| for (m = 0; m < NUM_MACHINE_MODES; ++m) |
| if (rs6000_hard_regno_mode_ok (r, (machine_mode)m)) |
| rs6000_hard_regno_mode_ok_p[m][r] = true; |
| |
| /* Precalculate CLASS_MAX_NREGS sizes. */ |
| for (c = 0; c < LIM_REG_CLASSES; ++c) |
| { |
| int reg_size; |
| |
| if (TARGET_VSX && VSX_REG_CLASS_P (c)) |
| reg_size = UNITS_PER_VSX_WORD; |
| |
| else if (c == ALTIVEC_REGS) |
| reg_size = UNITS_PER_ALTIVEC_WORD; |
| |
| else if (c == FLOAT_REGS) |
| reg_size = UNITS_PER_FP_WORD; |
| |
| else |
| reg_size = UNITS_PER_WORD; |
| |
| for (m = 0; m < NUM_MACHINE_MODES; ++m) |
| { |
| machine_mode m2 = (machine_mode)m; |
| int reg_size2 = reg_size; |
| |
| /* TFmode/TDmode always takes 2 registers, even in VSX. */ |
| if (TARGET_VSX && VSX_REG_CLASS_P (c) |
| && (m == TDmode || m == TFmode)) |
| reg_size2 = UNITS_PER_FP_WORD; |
| |
| rs6000_class_max_nregs[m][c] |
| = (GET_MODE_SIZE (m2) + reg_size2 - 1) / reg_size2; |
| } |
| } |
| |
| if (TARGET_E500_DOUBLE) |
| rs6000_class_max_nregs[DFmode][GENERAL_REGS] = 1; |
| |
| /* Calculate which modes to automatically generate code to use a the |
| reciprocal divide and square root instructions. In the future, possibly |
| automatically generate the instructions even if the user did not specify |
| -mrecip. The older machines double precision reciprocal sqrt estimate is |
| not accurate enough. */ |
| memset (rs6000_recip_bits, 0, sizeof (rs6000_recip_bits)); |
| if (TARGET_FRES) |
| rs6000_recip_bits[SFmode] = RS6000_RECIP_MASK_HAVE_RE; |
| if (TARGET_FRE) |
| rs6000_recip_bits[DFmode] = RS6000_RECIP_MASK_HAVE_RE; |
| if (VECTOR_UNIT_ALTIVEC_OR_VSX_P (V4SFmode)) |
| rs6000_recip_bits[V4SFmode] = RS6000_RECIP_MASK_HAVE_RE; |
| if (VECTOR_UNIT_VSX_P (V2DFmode)) |
| rs6000_recip_bits[V2DFmode] = RS6000_RECIP_MASK_HAVE_RE; |
| |
| if (TARGET_FRSQRTES) |
| rs6000_recip_bits[SFmode] |= RS6000_RECIP_MASK_HAVE_RSQRTE; |
| if (TARGET_FRSQRTE) |
| rs6000_recip_bits[DFmode] |= RS6000_RECIP_MASK_HAVE_RSQRTE; |
| if (VECTOR_UNIT_ALTIVEC_OR_VSX_P (V4SFmode)) |
| rs6000_recip_bits[V4SFmode] |= RS6000_RECIP_MASK_HAVE_RSQRTE; |
| if (VECTOR_UNIT_VSX_P (V2DFmode)) |
| rs6000_recip_bits[V2DFmode] |= RS6000_RECIP_MASK_HAVE_RSQRTE; |
| |
| if (rs6000_recip_control) |
| { |
| if (!flag_finite_math_only) |
| warning (0, "-mrecip requires -ffinite-math or -ffast-math"); |
| if (flag_trapping_math) |
| warning (0, "-mrecip requires -fno-trapping-math or -ffast-math"); |
| if (!flag_reciprocal_math) |
| warning (0, "-mrecip requires -freciprocal-math or -ffast-math"); |
| if (flag_finite_math_only && !flag_trapping_math && flag_reciprocal_math) |
| { |
| if (RS6000_RECIP_HAVE_RE_P (SFmode) |
| && (rs6000_recip_control & RECIP_SF_DIV) != 0) |
| rs6000_recip_bits[SFmode] |= RS6000_RECIP_MASK_AUTO_RE; |
| |
| if (RS6000_RECIP_HAVE_RE_P (DFmode) |
| && (rs6000_recip_control & RECIP_DF_DIV) != 0) |
| rs6000_recip_bits[DFmode] |= RS6000_RECIP_MASK_AUTO_RE; |
| |
| if (RS6000_RECIP_HAVE_RE_P (V4SFmode) |
| && (rs6000_recip_control & RECIP_V4SF_DIV) != 0) |
| rs6000_recip_bits[V4SFmode] |= RS6000_RECIP_MASK_AUTO_RE; |
| |
| if (RS6000_RECIP_HAVE_RE_P (V2DFmode) |
| && (rs6000_recip_control & RECIP_V2DF_DIV) != 0) |
| rs6000_recip_bits[V2DFmode] |= RS6000_RECIP_MASK_AUTO_RE; |
| |
| if (RS6000_RECIP_HAVE_RSQRTE_P (SFmode) |
| && (rs6000_recip_control & RECIP_SF_RSQRT) != 0) |
| rs6000_recip_bits[SFmode] |= RS6000_RECIP_MASK_AUTO_RSQRTE; |
| |
| if (RS6000_RECIP_HAVE_RSQRTE_P (DFmode) |
| && (rs6000_recip_control & RECIP_DF_RSQRT) != 0) |
| rs6000_recip_bits[DFmode] |= RS6000_RECIP_MASK_AUTO_RSQRTE; |
| |
| if (RS6000_RECIP_HAVE_RSQRTE_P (V4SFmode) |
| && (rs6000_recip_control & RECIP_V4SF_RSQRT) != 0) |
| rs6000_recip_bits[V4SFmode] |= RS6000_RECIP_MASK_AUTO_RSQRTE; |
| |
| if (RS6000_RECIP_HAVE_RSQRTE_P (V2DFmode) |
| && (rs6000_recip_control & RECIP_V2DF_RSQRT) != 0) |
| rs6000_recip_bits[V2DFmode] |= RS6000_RECIP_MASK_AUTO_RSQRTE; |
| } |
| } |
| |
| /* Update the addr mask bits in reg_addr to help secondary reload and go if |
| legitimate address support to figure out the appropriate addressing to |
| use. */ |
| rs6000_setup_reg_addr_masks (); |
| |
| if (global_init_p || TARGET_DEBUG_TARGET) |
| { |
| if (TARGET_DEBUG_REG) |
| rs6000_debug_reg_global (); |
| |
| if (TARGET_DEBUG_COST || TARGET_DEBUG_REG) |
| fprintf (stderr, |
| "SImode variable mult cost = %d\n" |
| "SImode constant mult cost = %d\n" |
| "SImode short constant mult cost = %d\n" |
| "DImode multipliciation cost = %d\n" |
| "SImode division cost = %d\n" |
| "DImode division cost = %d\n" |
| "Simple fp operation cost = %d\n" |
| "DFmode multiplication cost = %d\n" |
| "SFmode division cost = %d\n" |
| "DFmode division cost = %d\n" |
| "cache line size = %d\n" |
| "l1 cache size = %d\n" |
| "l2 cache size = %d\n" |
| "simultaneous prefetches = %d\n" |
| "\n", |
| rs6000_cost->mulsi, |
| rs6000_cost->mulsi_const, |
| rs6000_cost->mulsi_const9, |
| rs6000_cost->muldi, |
| rs6000_cost->divsi, |
| rs6000_cost->divdi, |
| rs6000_cost->fp, |
| rs6000_cost->dmul, |
| rs6000_cost->sdiv, |
| rs6000_cost->ddiv, |
| rs6000_cost->cache_line_size, |
| rs6000_cost->l1_cache_size, |
| rs6000_cost->l2_cache_size, |
| rs6000_cost->simultaneous_prefetches); |
| } |
| } |
| |
| #if TARGET_MACHO |
| /* 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; |
| rs6000_current_abi = ABI_DARWIN; |
| |
| if (DEFAULT_ABI == ABI_DARWIN |
| && TARGET_64BIT) |
| darwin_one_byte_bool = 1; |
| |
| if (TARGET_64BIT && ! TARGET_POWERPC64) |
| { |
| rs6000_isa_flags |= OPTION_MASK_POWERPC64; |
| warning (0, "-m64 requires PowerPC64 architecture, enabling"); |
| } |
| if (flag_mkernel) |
| { |
| rs6000_default_long_calls = 1; |
| rs6000_isa_flags |= OPTION_MASK_SOFT_FLOAT; |
| } |
| |
| /* Make -m64 imply -maltivec. Darwin's 64-bit ABI includes |
| Altivec. */ |
| if (!flag_mkernel && !flag_apple_kext |
| && TARGET_64BIT |
| && ! (rs6000_isa_flags_explicit & OPTION_MASK_ALTIVEC)) |
| rs6000_isa_flags |= OPTION_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 targeting the kernel. */ |
| if (!flag_mkernel |
| && !flag_apple_kext |
| && strverscmp (darwin_macosx_version_min, "10.5") >= 0 |
| && ! (rs6000_isa_flags_explicit & OPTION_MASK_ALTIVEC) |
| && ! global_options_set.x_rs6000_cpu_index) |
| { |
| rs6000_isa_flags |= OPTION_MASK_ALTIVEC; |
| } |
| } |
| #endif |
| |
| /* 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 |
| |
| /* Return the builtin mask of the various options used that could affect which |
| builtins were used. In the past we used target_flags, but we've run out of |
| bits, and some options like SPE and PAIRED are no longer in |
| target_flags. */ |
| |
| HOST_WIDE_INT |
| rs6000_builtin_mask_calculate (void) |
| { |
| return (((TARGET_ALTIVEC) ? RS6000_BTM_ALTIVEC : 0) |
| | ((TARGET_VSX) ? RS6000_BTM_VSX : 0) |
| | ((TARGET_SPE) ? RS6000_BTM_SPE : 0) |
| | ((TARGET_PAIRED_FLOAT) ? RS6000_BTM_PAIRED : 0) |
| | ((TARGET_FRE) ? RS6000_BTM_FRE : 0) |
| | ((TARGET_FRES) ? RS6000_BTM_FRES : 0) |
| | ((TARGET_FRSQRTE) ? RS6000_BTM_FRSQRTE : 0) |
| | ((TARGET_FRSQRTES) ? RS6000_BTM_FRSQRTES : 0) |
| | ((TARGET_POPCNTD) ? RS6000_BTM_POPCNTD : 0) |
| | ((rs6000_cpu == PROCESSOR_CELL) ? RS6000_BTM_CELL : 0) |
| | ((TARGET_P8_VECTOR) ? RS6000_BTM_P8_VECTOR : 0) |
| | ((TARGET_CRYPTO) ? RS6000_BTM_CRYPTO : 0) |
| | ((TARGET_HTM) ? RS6000_BTM_HTM : 0) |
| | ((TARGET_DFP) ? RS6000_BTM_DFP : 0) |
| | ((TARGET_HARD_FLOAT) ? RS6000_BTM_HARD_FLOAT : 0) |
| | ((TARGET_LONG_DOUBLE_128) ? RS6000_BTM_LDBL128 : 0)); |
| } |
| |
| /* Implement TARGET_MD_ASM_CLOBBERS. All asm statements are considered |
| to clobber the XER[CA] bit because clobbering that bit without telling |
| the compiler worked just fine with versions of GCC before GCC 5, and |
| breaking a lot of older code in ways that are hard to track down is |
| not such a great idea. */ |
| |
| static tree |
| rs6000_md_asm_clobbers (tree, tree, tree clobbers) |
| { |
| tree s = build_string (strlen (reg_names[CA_REGNO]), reg_names[CA_REGNO]); |
| return tree_cons (NULL_TREE, s, clobbers); |
| } |
| |
| /* Override command line options. Mostly we process the processor type and |
| sometimes adjust other TARGET_ options. */ |
| |
| static bool |
| rs6000_option_override_internal (bool global_init_p) |
| { |
| bool ret = true; |
| bool have_cpu = false; |
| |
| /* The default cpu requested at configure time, if any. */ |
| const char *implicit_cpu = OPTION_TARGET_CPU_DEFAULT; |
| |
| HOST_WIDE_INT set_masks; |
| int cpu_index; |
| int tune_index; |
| struct cl_target_option *main_target_opt |
| = ((global_init_p || target_option_default_node == NULL) |
| ? NULL : TREE_TARGET_OPTION (target_option_default_node)); |
| |
| /* Print defaults. */ |
| if ((TARGET_DEBUG_REG || TARGET_DEBUG_TARGET) && global_init_p) |
| rs6000_print_isa_options (stderr, 0, "TARGET_DEFAULT", TARGET_DEFAULT); |
| |
| /* Remember the explicit arguments. */ |
| if (global_init_p) |
| rs6000_isa_flags_explicit = global_options_set.x_rs6000_isa_flags; |
| |
| /* 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 (global_options_set.x_rs6000_alignment_flags |
| && rs6000_alignment_flags == MASK_ALIGN_POWER |
| && 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"); |
| |
| /* Numerous experiment shows that IRA based loop pressure |
| calculation works better for RTL loop invariant motion on targets |
| with enough (>= 32) registers. It is an expensive optimization. |
| So it is on only for peak performance. */ |
| if (optimize >= 3 && global_init_p |
| && !global_options_set.x_flag_ira_loop_pressure) |
| flag_ira_loop_pressure = 1; |
| |
| /* -fsanitize=address needs to turn on -fasynchronous-unwind-tables in order |
| for tracebacks to be complete but not if any -fasynchronous-unwind-tables |
| options were already specified. */ |
| if (flag_sanitize & SANITIZE_USER_ADDRESS |
| && !global_options_set.x_flag_asynchronous_unwind_tables) |
| flag_asynchronous_unwind_tables = 1; |
| |
| /* Set the pointer size. */ |
| if (TARGET_64BIT) |
| { |
| rs6000_pmode = (int)DImode; |
| rs6000_pointer_size = 64; |
| } |
| else |
| { |
| rs6000_pmode = (int)SImode; |
| rs6000_pointer_size = 32; |
| } |
| |
| /* 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 OPTION_MASK_POWERPC64 or OPTION_MASK_ALTIVEC settings; |
| if the user wants either, the user must explicitly specify them and we |
| won't interfere with the user's specification. */ |
| |
| set_masks = POWERPC_MASKS; |
| #ifdef OS_MISSING_POWERPC64 |
| if (OS_MISSING_POWERPC64) |
| set_masks &= ~OPTION_MASK_POWERPC64; |
| #endif |
| #ifdef OS_MISSING_ALTIVEC |
| if (OS_MISSING_ALTIVEC) |
| set_masks &= ~(OPTION_MASK_ALTIVEC | OPTION_MASK_VSX); |
| #endif |
| |
| /* Don't override by the processor default if given explicitly. */ |
| set_masks &= ~rs6000_isa_flags_explicit; |
| |
| /* Process the -mcpu=<xxx> and -mtune=<xxx> argument. If the user changed |
| the cpu in a target attribute or pragma, but did not specify a tuning |
| option, use the cpu for the tuning option rather than the option specified |
| with -mtune on the command line. Process a '--with-cpu' configuration |
| request as an implicit --cpu. */ |
| if (rs6000_cpu_index >= 0) |
| { |
| cpu_index = rs6000_cpu_index; |
| have_cpu = true; |
| } |
| else if (main_target_opt != NULL && main_target_opt->x_rs6000_cpu_index >= 0) |
| { |
| rs6000_cpu_index = cpu_index = main_target_opt->x_rs6000_cpu_index; |
| have_cpu = true; |
| } |
| else if (implicit_cpu) |
| { |
| rs6000_cpu_index = cpu_index = rs6000_cpu_name_lookup (implicit_cpu); |
| have_cpu = true; |
| } |
| else |
| { |
| /* PowerPC 64-bit LE requires at least ISA 2.07. */ |
| const char *default_cpu = ((!TARGET_POWERPC64) |
| ? "powerpc" |
| : ((BYTES_BIG_ENDIAN) |
| ? "powerpc64" |
| : "powerpc64le")); |
| |
| rs6000_cpu_index = cpu_index = rs6000_cpu_name_lookup (default_cpu); |
| have_cpu = false; |
| } |
| |
| gcc_assert (cpu_index >= 0); |
| |
| /* If we have a cpu, either through an explicit -mcpu=<xxx> or if the |
| compiler was configured with --with-cpu=<xxx>, replace all of the ISA bits |
| with those from the cpu, except for options that were explicitly set. If |
| we don't have a cpu, do not override the target bits set in |
| TARGET_DEFAULT. */ |
| if (have_cpu) |
| { |
| rs6000_isa_flags &= ~set_masks; |
| rs6000_isa_flags |= (processor_target_table[cpu_index].target_enable |
| & set_masks); |
| } |
| else |
| { |
| /* If no -mcpu=<xxx>, inherit any default options that were cleared via |
| POWERPC_MASKS. Originally, TARGET_DEFAULT was used to initialize |
| target_flags via the TARGET_DEFAULT_TARGET_FLAGS hook. When we switched |
| to using rs6000_isa_flags, we need to do the initialization here. |
| |
| If there is a TARGET_DEFAULT, use that. Otherwise fall back to using |
| -mcpu=powerpc, -mcpu=powerpc64, or -mcpu=powerpc64le defaults. */ |
| HOST_WIDE_INT flags = ((TARGET_DEFAULT) ? TARGET_DEFAULT |
| : processor_target_table[cpu_index].target_enable); |
| rs6000_isa_flags |= (flags & ~rs6000_isa_flags_explicit); |
| } |
| |
| if (rs6000_tune_index >= 0) |
| tune_index = rs6000_tune_index; |
| else if (have_cpu) |
| rs6000_tune_index = tune_index = cpu_index; |
| else |
| { |
| size_t i; |
| enum processor_type tune_proc |
| = (TARGET_POWERPC64 ? PROCESSOR_DEFAULT64 : PROCESSOR_DEFAULT); |
| |
| tune_index = -1; |
| for (i = 0; i < ARRAY_SIZE (processor_target_table); i++) |
| if (processor_target_table[i].processor == tune_proc) |
| { |
| rs6000_tune_index = tune_index = i; |
| break; |
| } |
| } |
| |
| gcc_assert (tune_index >= 0); |
| rs6000_cpu = processor_target_table[tune_index].processor; |
| |
| /* Pick defaults for SPE related control flags. Do this early to make sure |
| that the TARGET_ macros are representative ASAP. */ |
| { |
| int spe_capable_cpu = |
| (rs6000_cpu == PROCESSOR_PPC8540 |
| || rs6000_cpu == PROCESSOR_PPC8548); |
| |
| if (!global_options_set.x_rs6000_spe_abi) |
| rs6000_spe_abi = spe_capable_cpu; |
| |
| if (!global_options_set.x_rs6000_spe) |
| rs6000_spe = spe_capable_cpu; |
| |
| if (!global_options_set.x_rs6000_float_gprs) |
| rs6000_float_gprs = |
| (rs6000_cpu == PROCESSOR_PPC8540 ? 1 |
| : rs6000_cpu == PROCESSOR_PPC8548 ? 2 |
| : 0); |
| } |
| |
| if (global_options_set.x_rs6000_spe_abi |
| && rs6000_spe_abi |
| && !TARGET_SPE_ABI) |
| error ("not configured for SPE ABI"); |
| |
| if (global_options_set.x_rs6000_spe |
| && rs6000_spe |
| && !TARGET_SPE) |
| error ("not configured for SPE instruction set"); |
| |
| if (main_target_opt != NULL |
| && ((main_target_opt->x_rs6000_spe_abi != rs6000_spe_abi) |
| || (main_target_opt->x_rs6000_spe != rs6000_spe) |
| || (main_target_opt->x_rs6000_float_gprs != rs6000_float_gprs))) |
| error ("target attribute or pragma changes SPE ABI"); |
| |
| if (rs6000_cpu == PROCESSOR_PPCE300C2 || rs6000_cpu == PROCESSOR_PPCE300C3 |
| || rs6000_cpu == PROCESSOR_PPCE500MC || rs6000_cpu == PROCESSOR_PPCE500MC64 |
| || rs6000_cpu == PROCESSOR_PPCE5500) |
| { |
| if (TARGET_ALTIVEC) |
| error ("AltiVec not supported in this target"); |
| if (TARGET_SPE) |
| error ("SPE not supported in this target"); |
| } |
| if (rs6000_cpu == PROCESSOR_PPCE6500) |
| { |
| if (TARGET_SPE) |
| error ("SPE not supported in this target"); |
| } |
| |
| /* Disable Cell microcode if we are optimizing for the Cell |
| and not optimizing for size. */ |
| if (rs6000_gen_cell_microcode == -1) |
| rs6000_gen_cell_microcode = !(rs6000_cpu == PROCESSOR_CELL |
| && !optimize_size); |
| |
| /* If we are optimizing big endian systems for space and it's OK to |
| use instructions that would be microcoded on the Cell, use the |
| load/store multiple and string instructions. */ |
| if (BYTES_BIG_ENDIAN && optimize_size && rs6000_gen_cell_microcode) |
| rs6000_isa_flags |= ~rs6000_isa_flags_explicit & (OPTION_MASK_MULTIPLE |
| | OPTION_MASK_STRING); |
| |
| /* Don't allow -mmultiple or -mstring on little endian systems |
| unless the cpu is a 750, because the hardware doesn't support the |
| instructions used in little endian mode, and causes an alignment |
| trap. The 750 does not cause an alignment trap (except when the |
| target is unaligned). */ |
| |
| if (!BYTES_BIG_ENDIAN && rs6000_cpu != PROCESSOR_PPC750) |
| { |
| if (TARGET_MULTIPLE) |
| { |
| rs6000_isa_flags &= ~OPTION_MASK_MULTIPLE; |
| if ((rs6000_isa_flags_explicit & OPTION_MASK_MULTIPLE) != 0) |
| warning (0, "-mmultiple is not supported on little endian systems"); |
| } |
| |
| if (TARGET_STRING) |
| { |
| rs6000_isa_flags &= ~OPTION_MASK_STRING; |
| if ((rs6000_isa_flags_explicit & OPTION_MASK_STRING) != 0) |
| warning (0, "-mstring is not supported on little endian systems"); |
| } |
| } |
| |
| /* If little-endian, default to -mstrict-align on older processors. |
| Testing for htm matches power8 and later. */ |
| if (!BYTES_BIG_ENDIAN |
| && !(processor_target_table[tune_index].target_enable & OPTION_MASK_HTM)) |
| rs6000_isa_flags |= ~rs6000_isa_flags_explicit & OPTION_MASK_STRICT_ALIGN; |
| |
| /* -maltivec={le,be} implies -maltivec. */ |
| if (rs6000_altivec_element_order != 0) |
| rs6000_isa_flags |= OPTION_MASK_ALTIVEC; |
| |
| /* Disallow -maltivec=le in big endian mode for now. This is not |
| known to be useful for anyone. */ |
| if (BYTES_BIG_ENDIAN && rs6000_altivec_element_order == 1) |
| { |
| warning (0, N_("-maltivec=le not allowed for big-endian targets")); |
| rs6000_altivec_element_order = 0; |
| } |
| |
| /* Add some warnings for VSX. */ |
| if (TARGET_VSX) |
| { |
| const char *msg = NULL; |
| if (!TARGET_HARD_FLOAT || !TARGET_FPRS |
| || !TARGET_SINGLE_FLOAT || !TARGET_DOUBLE_FLOAT) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_VSX) |
| msg = N_("-mvsx requires hardware floating point"); |
| else |
| { |
| rs6000_isa_flags &= ~ OPTION_MASK_VSX; |
| rs6000_isa_flags_explicit |= OPTION_MASK_VSX; |
| } |
| } |
| else if (TARGET_PAIRED_FLOAT) |
| msg = N_("-mvsx and -mpaired are incompatible"); |
| else if (TARGET_AVOID_XFORM > 0) |
| msg = N_("-mvsx needs indexed addressing"); |
| else if (!TARGET_ALTIVEC && (rs6000_isa_flags_explicit |
| & OPTION_MASK_ALTIVEC)) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_VSX) |
| msg = N_("-mvsx and -mno-altivec are incompatible"); |
| else |
| msg = N_("-mno-altivec disables vsx"); |
| } |
| |
| if (msg) |
| { |
| warning (0, msg); |
| rs6000_isa_flags &= ~ OPTION_MASK_VSX; |
| rs6000_isa_flags_explicit |= OPTION_MASK_VSX; |
| } |
| } |
| |
| /* If hard-float/altivec/vsx were explicitly turned off then don't allow |
| the -mcpu setting to enable options that conflict. */ |
| if ((!TARGET_HARD_FLOAT || !TARGET_ALTIVEC || !TARGET_VSX) |
| && (rs6000_isa_flags_explicit & (OPTION_MASK_SOFT_FLOAT |
| | OPTION_MASK_ALTIVEC |
| | OPTION_MASK_VSX)) != 0) |
| rs6000_isa_flags &= ~((OPTION_MASK_P8_VECTOR | OPTION_MASK_CRYPTO |
| | OPTION_MASK_DIRECT_MOVE) |
| & ~rs6000_isa_flags_explicit); |
| |
| if (TARGET_DEBUG_REG || TARGET_DEBUG_TARGET) |
| rs6000_print_isa_options (stderr, 0, "before defaults", rs6000_isa_flags); |
| |
| /* For the newer switches (vsx, dfp, etc.) set some of the older options, |
| unless the user explicitly used the -mno-<option> to disable the code. */ |
| if (TARGET_P8_VECTOR || TARGET_DIRECT_MOVE || TARGET_CRYPTO) |
| rs6000_isa_flags |= (ISA_2_7_MASKS_SERVER & ~rs6000_isa_flags_explicit); |
| else if (TARGET_VSX) |
| rs6000_isa_flags |= (ISA_2_6_MASKS_SERVER & ~rs6000_isa_flags_explicit); |
| else if (TARGET_POPCNTD) |
| rs6000_isa_flags |= (ISA_2_6_MASKS_EMBEDDED & ~rs6000_isa_flags_explicit); |
| else if (TARGET_DFP) |
| rs6000_isa_flags |= (ISA_2_5_MASKS_SERVER & ~rs6000_isa_flags_explicit); |
| else if (TARGET_CMPB) |
| rs6000_isa_flags |= (ISA_2_5_MASKS_EMBEDDED & ~rs6000_isa_flags_explicit); |
| else if (TARGET_FPRND) |
| rs6000_isa_flags |= (ISA_2_4_MASKS & ~rs6000_isa_flags_explicit); |
| else if (TARGET_POPCNTB) |
| rs6000_isa_flags |= (ISA_2_2_MASKS & ~rs6000_isa_flags_explicit); |
| else if (TARGET_ALTIVEC) |
| rs6000_isa_flags |= (OPTION_MASK_PPC_GFXOPT & ~rs6000_isa_flags_explicit); |
| |
| if (TARGET_CRYPTO && !TARGET_ALTIVEC) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_CRYPTO) |
| error ("-mcrypto requires -maltivec"); |
| rs6000_isa_flags &= ~OPTION_MASK_CRYPTO; |
| } |
| |
| if (TARGET_DIRECT_MOVE && !TARGET_VSX) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_DIRECT_MOVE) |
| error ("-mdirect-move requires -mvsx"); |
| rs6000_isa_flags &= ~OPTION_MASK_DIRECT_MOVE; |
| } |
| |
| if (TARGET_P8_VECTOR && !TARGET_ALTIVEC) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_P8_VECTOR) |
| error ("-mpower8-vector requires -maltivec"); |
| rs6000_isa_flags &= ~OPTION_MASK_P8_VECTOR; |
| } |
| |
| if (TARGET_P8_VECTOR && !TARGET_VSX) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_P8_VECTOR) |
| error ("-mpower8-vector requires -mvsx"); |
| rs6000_isa_flags &= ~OPTION_MASK_P8_VECTOR; |
| } |
| |
| if (TARGET_VSX_TIMODE && !TARGET_VSX) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_VSX_TIMODE) |
| error ("-mvsx-timode requires -mvsx"); |
| rs6000_isa_flags &= ~OPTION_MASK_VSX_TIMODE; |
| } |
| |
| if (TARGET_DFP && !TARGET_HARD_FLOAT) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_DFP) |
| error ("-mhard-dfp requires -mhard-float"); |
| rs6000_isa_flags &= ~OPTION_MASK_DFP; |
| } |
| |
| /* Allow an explicit -mupper-regs to set both -mupper-regs-df and |
| -mupper-regs-sf, depending on the cpu, unless the user explicitly also set |
| the individual option. */ |
| if (TARGET_UPPER_REGS > 0) |
| { |
| if (TARGET_VSX |
| && !(rs6000_isa_flags_explicit & OPTION_MASK_UPPER_REGS_DF)) |
| { |
| rs6000_isa_flags |= OPTION_MASK_UPPER_REGS_DF; |
| rs6000_isa_flags_explicit |= OPTION_MASK_UPPER_REGS_DF; |
| } |
| if (TARGET_P8_VECTOR |
| && !(rs6000_isa_flags_explicit & OPTION_MASK_UPPER_REGS_SF)) |
| { |
| rs6000_isa_flags |= OPTION_MASK_UPPER_REGS_SF; |
| rs6000_isa_flags_explicit |= OPTION_MASK_UPPER_REGS_SF; |
| } |
| } |
| else if (TARGET_UPPER_REGS == 0) |
| { |
| if (TARGET_VSX |
| && !(rs6000_isa_flags_explicit & OPTION_MASK_UPPER_REGS_DF)) |
| { |
| rs6000_isa_flags &= ~OPTION_MASK_UPPER_REGS_DF; |
| rs6000_isa_flags_explicit |= OPTION_MASK_UPPER_REGS_DF; |
| } |
| if (TARGET_P8_VECTOR |
| && !(rs6000_isa_flags_explicit & OPTION_MASK_UPPER_REGS_SF)) |
| { |
| rs6000_isa_flags &= ~OPTION_MASK_UPPER_REGS_SF; |
| rs6000_isa_flags_explicit |= OPTION_MASK_UPPER_REGS_SF; |
| } |
| } |
| |
| if (TARGET_UPPER_REGS_DF && !TARGET_VSX) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_UPPER_REGS_DF) |
| error ("-mupper-regs-df requires -mvsx"); |
| rs6000_isa_flags &= ~OPTION_MASK_UPPER_REGS_DF; |
| } |
| |
| if (TARGET_UPPER_REGS_SF && !TARGET_P8_VECTOR) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_UPPER_REGS_SF) |
| error ("-mupper-regs-sf requires -mpower8-vector"); |
| rs6000_isa_flags &= ~OPTION_MASK_UPPER_REGS_SF; |
| } |
| |
| /* The quad memory instructions only works in 64-bit mode. In 32-bit mode, |
| silently turn off quad memory mode. */ |
| if ((TARGET_QUAD_MEMORY || TARGET_QUAD_MEMORY_ATOMIC) && !TARGET_POWERPC64) |
| { |
| if ((rs6000_isa_flags_explicit & OPTION_MASK_QUAD_MEMORY) != 0) |
| warning (0, N_("-mquad-memory requires 64-bit mode")); |
| |
| if ((rs6000_isa_flags_explicit & OPTION_MASK_QUAD_MEMORY_ATOMIC) != 0) |
| warning (0, N_("-mquad-memory-atomic requires 64-bit mode")); |
| |
| rs6000_isa_flags &= ~(OPTION_MASK_QUAD_MEMORY |
| | OPTION_MASK_QUAD_MEMORY_ATOMIC); |
| } |
| |
| /* Non-atomic quad memory load/store are disabled for little endian, since |
| the words are reversed, but atomic operations can still be done by |
| swapping the words. */ |
| if (TARGET_QUAD_MEMORY && !WORDS_BIG_ENDIAN) |
| { |
| if ((rs6000_isa_flags_explicit & OPTION_MASK_QUAD_MEMORY) != 0) |
| warning (0, N_("-mquad-memory is not available in little endian mode")); |
| |
| rs6000_isa_flags &= ~OPTION_MASK_QUAD_MEMORY; |
| } |
| |
| /* Assume if the user asked for normal quad memory instructions, they want |
| the atomic versions as well, unless they explicity told us not to use quad |
| word atomic instructions. */ |
| if (TARGET_QUAD_MEMORY |
| && !TARGET_QUAD_MEMORY_ATOMIC |
| && ((rs6000_isa_flags_explicit & OPTION_MASK_QUAD_MEMORY_ATOMIC) == 0)) |
| rs6000_isa_flags |= OPTION_MASK_QUAD_MEMORY_ATOMIC; |
| |
| /* Enable power8 fusion if we are tuning for power8, even if we aren't |
| generating power8 instructions. */ |
| if (!(rs6000_isa_flags_explicit & OPTION_MASK_P8_FUSION)) |
| rs6000_isa_flags |= (processor_target_table[tune_index].target_enable |
| & OPTION_MASK_P8_FUSION); |
| |
| /* Power8 does not fuse sign extended loads with the addis. If we are |
| optimizing at high levels for speed, convert a sign extended load into a |
| zero extending load, and an explicit sign extension. */ |
| if (TARGET_P8_FUSION |
| && !(rs6000_isa_flags_explicit & OPTION_MASK_P8_FUSION_SIGN) |
| && optimize_function_for_speed_p (cfun) |
| && optimize >= 3) |
| rs6000_isa_flags |= OPTION_MASK_P8_FUSION_SIGN; |
| |
| /* Set -mallow-movmisalign to explicitly on if we have full ISA 2.07 |
| support. If we only have ISA 2.06 support, and the user did not specify |
| the switch, leave it set to -1 so the movmisalign patterns are enabled, |
| but we don't enable the full vectorization support */ |
| if (TARGET_ALLOW_MOVMISALIGN == -1 && TARGET_P8_VECTOR && TARGET_DIRECT_MOVE) |
| TARGET_ALLOW_MOVMISALIGN = 1; |
| |
| else if (TARGET_ALLOW_MOVMISALIGN && !TARGET_VSX) |
| { |
| if (TARGET_ALLOW_MOVMISALIGN > 0 |
| && global_options_set.x_TARGET_ALLOW_MOVMISALIGN) |
| error ("-mallow-movmisalign requires -mvsx"); |
| |
| TARGET_ALLOW_MOVMISALIGN = 0; |
| } |
| |
| /* Determine when unaligned vector accesses are permitted, and when |
| they are preferred over masked Altivec loads. Note that if |
| TARGET_ALLOW_MOVMISALIGN has been disabled by the user, then |
| TARGET_EFFICIENT_UNALIGNED_VSX must be as well. The converse is |
| not true. */ |
| if (TARGET_EFFICIENT_UNALIGNED_VSX) |
| { |
| if (!TARGET_VSX) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_EFFICIENT_UNALIGNED_VSX) |
| error ("-mefficient-unaligned-vsx requires -mvsx"); |
| |
| rs6000_isa_flags &= ~OPTION_MASK_EFFICIENT_UNALIGNED_VSX; |
| } |
| |
| else if (!TARGET_ALLOW_MOVMISALIGN) |
| { |
| if (rs6000_isa_flags_explicit & OPTION_MASK_EFFICIENT_UNALIGNED_VSX) |
| error ("-mefficient-unaligned-vsx requires -mallow-movmisalign"); |
| |
| rs6000_isa_flags &= ~OPTION_MASK_EFFICIENT_UNALIGNED_VSX; |
| } |
| } |
| |
| if (TARGET_DEBUG_REG || TARGET_DEBUG_TARGET) |
| rs6000_print_isa_options (stderr, 0, "after defaults", rs6000_isa_flags); |
| |
| /* E500mc does "better" if we inline more aggressively. Respect the |
| user's opinion, though. */ |
| if (rs6000_block_move_inline_limit == 0 |
| && (rs6000_cpu == PROCESSOR_PPCE500MC |
| || rs6000_cpu == PROCESSOR_PPCE500MC64 |
| || rs6000_cpu == PROCESSOR_PPCE5500 |
| || rs6000_cpu == PROCESSOR_PPCE6500)) |
| rs6000_block_move_inline_limit = 128; |
| |
| /* store_one_arg depends on expand_block_move to handle at least the |
| size of reg_parm_stack_space. */ |
| if (rs6000_block_move_inline_limit < (TARGET_POWERPC64 ? 64 : 32)) |
| rs6000_block_move_inline_limit = (TARGET_POWERPC64 ? 64 : 32); |
| |
| if (global_init_p) |
| { |
| /* If the appropriate debug option is enabled, replace the target hooks |
| with debug versions that call the real version and then prints |
| debugging information. */ |
| if (TARGET_DEBUG_COST) |
| { |
| targetm.rtx_costs = rs6000_debug_rtx_costs; |
| targetm.address_cost = rs6000_debug_address_cost; |
| targetm.sched.adjust_cost = rs6000_debug_adjust_cost; |
| } |
| |
| if (TARGET_DEBUG_ADDR) |
| { |
| targetm.legitimate_address_p = rs6000_debug_legitimate_address_p; |
| targetm.legitimize_address = rs6000_debug_legitimize_address; |
| rs6000_secondary_reload_class_ptr |
| = rs6000_debug_secondary_reload_class; |
| rs6000_secondary_memory_needed_ptr |
| = rs6000_debug_secondary_memory_needed; |
| rs6000_cannot_change_mode_class_ptr |
| = rs6000_debug_cannot_change_mode_class; |
| rs6000_preferred_reload_class_ptr |
| = rs6000_debug_preferred_reload_class; |
| rs6000_legitimize_reload_address_ptr |
| = rs6000_debug_legitimize_reload_address; |
| rs6000_mode_dependent_address_ptr |
| = rs6000_debug_mode_dependent_address; |
| } |
| |
| if (rs6000_veclibabi_name) |
| { |
| if (strcmp (rs6000_veclibabi_name, "mass") == 0) |
| rs6000_veclib_handler = rs6000_builtin_vectorized_libmass; |
| else |
| { |
| error ("unknown vectorization library ABI type (%s) for " |
| "-mveclibabi= switch", rs6000_veclibabi_name); |
| ret = false; |
| } |
| } |
| } |
| |
| if (!global_options_set.x_rs6000_long_double_type_size) |
| { |
| if (main_target_opt != NULL |
| && (main_target_opt->x_rs6000_long_double_type_size |
| != RS6000_DEFAULT_LONG_DOUBLE_SIZE)) |
| error ("target attribute or pragma changes long double size"); |
| else |
| rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE; |
| } |
| |
| #if !defined (POWERPC_LINUX) && !defined (POWERPC_FREEBSD) |
| if (!global_options_set.x_rs6000_ieeequad) |
| rs6000_ieeequad = 1; |
| #endif |
| |
| /* Disable VSX and Altivec silently if the user switched cpus to power7 in a |
| target attribute or pragma which automatically enables both options, |
| unless the altivec ABI was set. This is set by default for 64-bit, but |
| not for 32-bit. */ |
| if (main_target_opt != NULL && !main_target_opt->x_rs6000_altivec_abi) |
| rs6000_isa_flags &= ~((OPTION_MASK_VSX | OPTION_MASK_ALTIVEC) |
| & ~rs6000_isa_flags_explicit); |
| |
| /* Enable Altivec ABI for AIX -maltivec. */ |
| if (TARGET_XCOFF && (TARGET_ALTIVEC || TARGET_VSX)) |
| { |
| if (main_target_opt != NULL && !main_target_opt->x_rs6000_altivec_abi) |
| error ("target attribute or pragma changes AltiVec ABI"); |
| else |
| rs6000_altivec_abi = 1; |
| } |
| |
| /* The AltiVec ABI is the default for PowerPC-64 GNU/Linux. For |
| PowerPC-32 GNU/Linux, -maltivec implies the AltiVec ABI. It can |
| be explicitly overridden in either case. */ |
| if (TARGET_ELF) |
| { |
| if (!global_options_set.x_rs6000_altivec_abi |
| && (TARGET_64BIT || TARGET_ALTIVEC || TARGET_VSX)) |
| { |
| if (main_target_opt != NULL && |
| !main_target_opt->x_rs6000_altivec_abi) |
| error ("target attribute or pragma changes AltiVec ABI"); |
| else |
| rs6000_altivec_abi = 1; |
| } |
| } |
| |
| /* Set the Darwin64 ABI as default for 64-bit Darwin. |
| So far, the only darwin64 targets are also MACH-O. */ |
| if (TARGET_MACHO |
| && DEFAULT_ABI == ABI_DARWIN |
| && TARGET_64BIT) |
| { |
| if (main_target_opt != NULL && !main_target_opt->x_rs6000_darwin64_abi) |
| error ("target attribute or pragma changes darwin64 ABI"); |
| else |
| { |
| rs6000_darwin64_abi = 1; |
| /* Default to natural alignment, for better performance. */ |
| rs6000_alignment_flags = MASK_ALIGN_NATURAL; |
| } |
| } |
| |
| /* Place FP constants in the constant pool instead of TOC |
| if section anchors enabled. */ |
| if (flag_section_anchors |
| && !global_options_set.x_TARGET_NO_FP_IN_TOC) |
| TARGET_NO_FP_IN_TOC = 1; |
| |
| if (TARGET_DEBUG_REG || TARGET_DEBUG_TARGET) |
| rs6000_print_isa_options (stderr, 0, "before subtarget", rs6000_isa_flags); |
| |
| #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_DEBUG_REG || TARGET_DEBUG_TARGET) |
| rs6000_print_isa_options (stderr, 0, "after subtarget", rs6000_isa_flags); |
| |
| /* For the E500 family of cores, reset the single/double FP flags to let us |
| check that they remain constant across attributes or pragmas. Also, |
| clear a possible request for string instructions, not supported and which |
| we might have silently queried above for -Os. |
| |
| For other families, clear ISEL in case it was set implicitly. |
| */ |
| |
| switch (rs6000_cpu) |
| { |
| case PROCESSOR_PPC8540: |
| case PROCESSOR_PPC8548: |
| case PROCESSOR_PPCE500MC: |
| case PROCESSOR_PPCE500MC64: |
| case PROCESSOR_PPCE5500: |
| case PROCESSOR_PPCE6500: |
| |
| rs6000_single_float = TARGET_E500_SINGLE || TARGET_E500_DOUBLE; |
| rs6000_double_float = TARGET_E500_DOUBLE; |
| |
| rs6000_isa_flags &= ~OPTION_MASK_STRING; |
| |
| break; |
| |
| default: |
| |
| if (have_cpu && !(rs6000_isa_flags_explicit & OPTION_MASK_ISEL)) |
| rs6000_isa_flags &= ~OPTION_MASK_ISEL; |
| |
| break; |
| } |
| |
| if (main_target_opt) |
| { |
| if (main_target_opt->x_rs6000_single_float != rs6000_single_float) |
| error ("target attribute or pragma changes single precision floating " |
| "point"); |
| if (main_target_opt->x_rs6000_double_float != rs6000_double_float) |
| error ("target attribute or pragma changes double precision floating " |
| "point"); |
| } |
| |
| /* Detect invalid option combinations with E500. */ |
| CHECK_E500_OPTIONS; |
| |
| rs6000_always_hint = (rs6000_cpu != PROCESSOR_POWER4 |
| && rs6000_cpu != PROCESSOR_POWER5 |
| && rs6000_cpu != PROCESSOR_POWER6 |
| && rs6000_cpu != PROCESSOR_POWER7 |
| && rs6000_cpu != PROCESSOR_POWER8 |
| && rs6000_cpu != PROCESSOR_PPCA2 |
| && rs6000_cpu != PROCESSOR_CELL |
| && rs6000_cpu != PROCESSOR_PPC476); |
| rs6000_sched_groups = (rs6000_cpu == PROCESSOR_POWER4 |
| || rs6000_cpu == PROCESSOR_POWER5 |
| || rs6000_cpu == PROCESSOR_POWER7 |
| || rs6000_cpu == PROCESSOR_POWER8); |
| rs6000_align_branch_targets = (rs6000_cpu == PROCESSOR_POWER4 |
| || rs6000_cpu == PROCESSOR_POWER5 |
| || rs6000_cpu == PROCESSOR_POWER6 |
| || rs6000_cpu == PROCESSOR_POWER7 |
| || rs6000_cpu == PROCESSOR_POWER8 |
| || rs6000_cpu == PROCESSOR_PPCE500MC |
| || rs6000_cpu == PROCESSOR_PPCE500MC64 |
| || rs6000_cpu == PROCESSOR_PPCE5500 |
| || rs6000_cpu == PROCESSOR_PPCE6500); |
| |
| /* Allow debug switches to override the above settings. These are set to -1 |
| in rs6000.opt to indicate the user hasn't directly set the switch. */ |
| if (TARGET_ALWAYS_HINT >= 0) |
| rs6000_always_hint = TARGET_ALWAYS_HINT; |
| |
| if (TARGET_SCHED_GROUPS >= 0) |
| rs6000_sched_groups = TARGET_SCHED_GROUPS; |
| |
| if (TARGET_ALIGN_BRANCH_TARGETS >= 0) |
| rs6000_align_branch_targets = TARGET_ALIGN_BRANCH_TARGETS; |
| |
| rs6000_sched_restricted_insns_priority |
| = (rs6000_sched_groups ? 1 : 0); |
| |
| /* Handle -msched-costly-dep option. */ |
| rs6000_sched_costly_dep |
| = (rs6000_sched_groups ? true_store_to_load_dep_costly : no_dep_costly); |
| |
| if (rs6000_sched_costly_dep_str) |
| { |
| if (! strcmp (rs6000_sched_costly_dep_str, "no")) |
| rs6000_sched_costly_dep = no_dep_costly; |
| else if (! strcmp (rs6000_sched_costly_dep_str, "all")) |
| rs6000_sched_costly_dep = all_deps_costly; |
| else if (! strcmp (rs6000_sched_costly_dep_str, "true_store_to_load")) |
| rs6000_sched_costly_dep = true_store_to_load_dep_costly; |
| else if (! strcmp (rs6000_sched_costly_dep_str, "store_to_load")) |
| rs6000_sched_costly_dep = store_to_load_dep_costly; |
| else |
| rs6000_sched_costly_dep = ((enum rs6000_dependence_cost) |
| 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 = ((enum rs6000_nop_insertion) |
| atoi (rs6000_sched_insert_nops_str)); |
| } |
| |
| if (global_init_p) |
| { |
| #ifdef TARGET_REGNAMES |
| /* If the user desires alternate register names, copy in the |
| alternate names now. */ |
| if (TARGET_REGNAMES) |
| memcpy (rs6000_reg_names, alt_reg_names, sizeof (rs6000_reg_names)); |
| #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 (!global_options_set.x_aix_struct_return) |
| aix_struct_return = (DEFAULT_ABI != ABI_V4 || DRAFT_V4_STRUCT_RET); |
| |
| #if 0 |
| /* IBM XL compiler defaults to unsigned bitfields. */ |
| if (TARGET_XL_COMPAT) |
| flag_signed_bitfields = 0; |
| #endif |
| |
| 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) |
| { |
| /* Cell wants to be aligned 8byte for dual issue. Titan wants to be |
| aligned 8byte to avoid misprediction by the branch predictor. */ |
| if (rs6000_cpu == PROCESSOR_TITAN |
| || rs6000_cpu == PROCESSOR_CELL) |
| { |
| if (align_functions <= 0) |
| align_functions = 8; |
| if (align_jumps <= 0) |
| align_jumps = 8; |
| if (align_loops <= 0) |
| align_loops = 8; |
| } |
| if (rs6000_align_branch_targets) |
| { |
| if (align_functions <= 0) |
| align_functions = 16; |
| if (align_jumps <= 0) |
| align_jumps = 16; |
| if (align_loops <= 0) |
| { |
| can_override_loop_align = 1; |
| align_loops = 16; |
| } |
| } |
| if (align_jumps_max_skip <= 0) |
| align_jumps_max_skip = 15; |
| if (align_loops_max_skip <= 0) |
| align_loops_max_skip = 15; |
| } |
| |
| /* 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_V4 || DEFAULT_ABI == ABI_DARWIN) |
| targetm.calls.split_complex_arg = NULL; |
| } |
| |
| /* 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_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_PPC476: |
| rs6000_cost = &ppc476_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_CELL: |
| rs6000_cost = &ppccell_cost; |
| break; |
| |
| case PROCESSOR_PPC750: |
| case PROCESSOR_PPC7400: |
| rs6000_cost = &ppc750_cost; |
| break; |
| |
| case PROCESSOR_PPC7450: |
| rs6000_cost = &ppc7450_cost; |
| break; |
| |
| case PROCESSOR_PPC8540: |
| case PROCESSOR_PPC8548: |
| rs6000_cost = &ppc8540_cost; |
| break; |
| |
| case PROCESSOR_PPCE300C2: |
| case PROCESSOR_PPCE300C3: |
| rs6000_cost = &ppce300c2c3_cost; |
| break; |
| |
| case PROCESSOR_PPCE500MC: |
| rs6000_cost = &ppce500mc_cost; |
| break; |
| |
| case PROCESSOR_PPCE500MC64: |
| rs6000_cost = &ppce500mc64_cost; |
| break; |
| |
| case PROCESSOR_PPCE5500: |
| rs6000_cost = &ppce5500_cost; |
| break; |
| |
| case PROCESSOR_PPCE6500: |
| rs6000_cost = &ppce6500_cost; |
| break; |
| |
| case PROCESSOR_TITAN: |
| rs6000_cost = &titan_cost; |
| break; |
| |
| case PROCESSOR_POWER4: |
| case PROCESSOR_POWER5: |
| rs6000_cost = &power4_cost; |
| break; |
| |
| case PROCESSOR_POWER6: |
| rs6000_cost = &power6_cost; |
| break; |
| |
| case PROCESSOR_POWER7: |
| rs6000_cost = &power7_cost; |
| break; |
| |
| case PROCESSOR_POWER8: |
| rs6000_cost = &power8_cost; |
| break; |
| |
| case PROCESSOR_PPCA2: |
| rs6000_cost = &ppca2_cost; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (global_init_p) |
| { |
| maybe_set_param_value (PARAM_SIMULTANEOUS_PREFETCHES, |
| rs6000_cost->simultaneous_prefetches, |
| global_options.x_param_values, |
| global_options_set.x_param_values); |
| maybe_set_param_value (PARAM_L1_CACHE_SIZE, rs6000_cost->l1_cache_size, |
| global_options.x_param_values, |
| global_options_set.x_param_values); |
| maybe_set_param_value (PARAM_L1_CACHE_LINE_SIZE, |
| rs6000_cost->cache_line_size, |
| global_options.x_param_values, |
| global_options_set.x_param_values); |
| maybe_set_param_value (PARAM_L2_CACHE_SIZE, rs6000_cost->l2_cache_size, |
| global_options.x_param_values, |
| global_options_set.x_param_values); |
| |
| /* Increase loop peeling limits based on performance analysis. */ |
| maybe_set_param_value (PARAM_MAX_PEELED_INSNS, 400, |
| global_options.x_param_values, |
| global_options_set.x_param_values); |
| maybe_set_param_value (PARAM_MAX_COMPLETELY_PEELED_INSNS, 400, |
| global_options.x_param_values, |
| global_options_set.x_param_values); |
| |
| /* If using typedef char *va_list, signal that |
| __builtin_va_start (&ap, 0) can be optimized to |
| ap = __builtin_next_arg (0). */ |
| if (DEFAULT_ABI != ABI_V4) |
| targetm.expand_builtin_va_start = NULL; |
| } |
| |
| /* Set up single/double float flags. |
| If TARGET_HARD_FLOAT is set, but neither single or double is set, |
| then set both flags. */ |
| if (TARGET_HARD_FLOAT && TARGET_FPRS |
| && rs6000_single_float == 0 && rs6000_double_float == 0) |
| rs6000_single_float = rs6000_double_float = 1; |
| |
| /* If not explicitly specified via option, decide whether to generate indexed |
| load/store instructions. */ |
| if (TARGET_AVOID_XFORM == -1) |
| /* Avoid indexed addressing when targeting Power6 in order to avoid the |
| DERAT mispredict penalty. However the LVE and STVE altivec instructions |
| need indexed accesses and the type used is the scalar type of the element |
| being loaded or stored. */ |
| TARGET_AVOID_XFORM = (rs6000_cpu == PROCESSOR_POWER6 && TARGET_CMPB |
| && !TARGET_ALTIVEC); |
| |
| /* Set the -mrecip options. */ |
| if (rs6000_recip_name) |
| { |
| char *p = ASTRDUP (rs6000_recip_name); |
| char *q; |
| unsigned int mask, i; |
| bool invert; |
| |
| while ((q = strtok (p, ",")) != NULL) |
| { |
| p = NULL; |
| if (*q == '!') |
| { |
| invert = true; |
| q++; |
| } |
| else |
| invert = false; |
| |
| if (!strcmp (q, "default")) |
| mask = ((TARGET_RECIP_PRECISION) |
| ? RECIP_HIGH_PRECISION : RECIP_LOW_PRECISION); |
| else |
| { |
| for (i = 0; i < ARRAY_SIZE (recip_options); i++) |
| if (!strcmp (q, recip_options[i].string)) |
| { |
| mask = recip_options[i].mask; |
| break; |
| } |
| |
| if (i == ARRAY_SIZE (recip_options)) |
| { |
| error ("unknown option for -mrecip=%s", q); |
| invert = false; |
| mask = 0; |
| ret = false; |
| } |
| } |
| |
| if (invert) |
| rs6000_recip_control &= ~mask; |
| else |
| rs6000_recip_control |= mask; |
| } |
| } |
| |
| /* Set the builtin mask of the various options used that could affect which |
| builtins were used. In the past we used target_flags, but we've run out |
| of bits, and some options like SPE and PAIRED are no longer in |
| target_flags. */ |
| rs6000_builtin_mask = rs6000_builtin_mask_calculate (); |
| if (TARGET_DEBUG_BUILTIN || TARGET_DEBUG_TARGET) |
| rs6000_print_builtin_options (stderr, 0, "builtin mask", |
| rs6000_builtin_mask); |
| |
| /* Initialize all of the registers. */ |
| rs6000_init_hard_regno_mode_ok (global_init_p); |
| |
| /* Save the initial options in case the user does function specific options */ |
| if (global_init_p) |
| target_option_default_node = target_option_current_node |
| = build_target_option_node (&global_options); |
| |
| /* If not explicitly specified via option, decide whether to generate the |
| extra blr's required to preserve the link stack on some cpus (eg, 476). */ |
| if (TARGET_LINK_STACK == -1) |
| SET_TARGET_LINK_STACK (rs6000_cpu == PROCESSOR_PPC476 && flag_pic); |
| |
| return ret; |
| } |
| |
| /* Implement TARGET_OPTION_OVERRIDE. On the RS/6000 this is used to |
| define the target cpu type. */ |
| |
| static void |
| rs6000_option_override (void) |
| { |
| (void) rs6000_option_override_internal (true); |
| |
| /* Register machine-specific passes. This needs to be done at start-up. |
| It's convenient to do it here (like i386 does). */ |
| opt_pass *pass_analyze_swaps = make_pass_analyze_swaps (g); |
| |
| struct register_pass_info analyze_swaps_info |
| = { pass_analyze_swaps, "cse1", 1, PASS_POS_INSERT_BEFORE }; |
| |
| register_pass (&analyze_swaps_info); |
| } |
| |
| |
| /* Implement targetm.vectorize.builtin_mask_for_load. */ |
| static tree |
| rs6000_builtin_mask_for_load (void) |
| { |
| /* Don't use lvsl/vperm for P8 and similarly efficient machines. */ |
| if ((TARGET_ALTIVEC && !TARGET_VSX) |
| || (TARGET_VSX && !TARGET_EFFICIENT_UNALIGNED_VSX)) |
| return altivec_builtin_mask_for_load; |
| else |
| return 0; |
| } |
| |
| /* Implement LOOP_ALIGN. */ |
| int |
| rs6000_loop_align (rtx label) |
| { |
| basic_block bb; |
| int ninsns; |
| |
| /* Don't override loop alignment if -falign-loops was specified. */ |
| if (!can_override_loop_align) |
| return align_loops_log; |
| |
| bb = BLOCK_FOR_INSN (label); |
| ninsns = num_loop_insns(bb->loop_father); |
| |
| /* Align small loops to 32 bytes to fit in an icache sector, otherwise return default. */ |
| if (ninsns > 4 && ninsns <= 8 |
| && (rs6000_cpu == PROCESSOR_POWER4 |
| || rs6000_cpu == PROCESSOR_POWER5 |
| || rs6000_cpu == PROCESSOR_POWER6 |
| || rs6000_cpu == PROCESSOR_POWER7 |
| || rs6000_cpu == PROCESSOR_POWER8)) |
| return 5; |
| else |
| return align_loops_log; |
| } |
| |
| /* Implement TARGET_LOOP_ALIGN_MAX_SKIP. */ |
| static int |
| rs6000_loop_align_max_skip (rtx_insn *label) |
| { |
| return (1 << rs6000_loop_align (label)) - 1; |
| } |
| |
| /* 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 (const_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) |
| return false; |
| |
| /* Assuming that all other types are naturally aligned. CHECKME! */ |
| return true; |
| } |
| } |
| |
| /* Return true if the vector misalignment factor is supported by the |
| target. */ |
| static bool |
| rs6000_builtin_support_vector_misalignment (machine_mode mode, |
| const_tree type, |
| int misalignment, |
| bool is_packed) |
| { |
| if (TARGET_VSX) |
| { |
| if (TARGET_EFFICIENT_UNALIGNED_VSX) |
| return true; |
| |
| /* Return if movmisalign pattern is not supported for this mode. */ |
| if (optab_handler (movmisalign_optab, mode) == CODE_FOR_nothing) |
| return false; |
| |
| if (misalignment == -1) |
| { |
| /* Misalignment factor is unknown at compile time but we know |
| it's word aligned. */ |
| if (rs6000_vector_alignment_reachable (type, is_packed)) |
| { |
| int element_size = TREE_INT_CST_LOW (TYPE_SIZE (type)); |
| |
| if (element_size == 64 || element_size == 32) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* VSX supports word-aligned vector. */ |
| if (misalignment % 4 == 0) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Implement targetm.vectorize.builtin_vectorization_cost. */ |
| static int |
| rs6000_builtin_vectorization_cost (enum vect_cost_for_stmt type_of_cost, |
| tree vectype, int misalign) |
| { |
| unsigned elements; |
| tree elem_type; |
| |
| switch (type_of_cost) |
| { |
| case scalar_stmt: |
| case scalar_load: |
| case scalar_store: |
| case vector_stmt: |
| case vector_load: |
| case vector_store: |
| case vec_to_scalar: |
| case scalar_to_vec: |
| case cond_branch_not_taken: |
| return 1; |
| |
| case vec_perm: |
| if (TARGET_VSX) |
| return 3; |
| else |
| return 1; |
| |
| case vec_promote_demote: |
| if (TARGET_VSX) |
| return 4; |
| else |
| return 1; |
| |
| case cond_branch_taken: |
| return 3; |
| |
| case unaligned_load: |
| if (TARGET_EFFICIENT_UNALIGNED_VSX) |
| return 1; |
| |
| if (TARGET_VSX && TARGET_ALLOW_MOVMISALIGN) |
| { |
| elements = TYPE_VECTOR_SUBPARTS (vectype); |
| if (elements == 2) |
| /* Double word aligned. */ |
| return 2; |
| |
| if (elements == 4) |
| { |
| switch (misalign) |
| { |
| case 8: |
| /* Double word aligned. */ |
| return 2; |
| |
| case -1: |
| /* Unknown misalignment. */ |
| case 4: |
| case 12: |
| /* Word aligned. */ |
| return 22; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| } |
| |
| if (TARGET_ALTIVEC) |
| /* Misaligned loads are not supported. */ |
| gcc_unreachable (); |
| |
| return 2; |
| |
| case unaligned_store: |
| if (TARGET_EFFICIENT_UNALIGNED_VSX) |
| return 1; |
| |
| if (TARGET_VSX && TARGET_ALLOW_MOVMISALIGN) |
| { |
| elements = TYPE_VECTOR_SUBPARTS (vectype); |
| if (elements == 2) |
| /* Double word aligned. */ |
| return 2; |
| |
| if (elements == 4) |
| { |
| switch (misalign) |
| { |
| case 8: |
| /* Double word aligned. */ |
| return 2; |
| |
| case -1: |
| /* Unknown misalignment. */ |
| case 4: |
| case 12: |
| /* Word aligned. */ |
| return 23; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| } |
| |
| if (TARGET_ALTIVEC) |
| /* Misaligned stores are not supported. */ |
| gcc_unreachable (); |
| |
| return 2; |
| |
| case vec_construct: |
| elements = TYPE_VECTOR_SUBPARTS (vectype); |
| elem_type = TREE_TYPE (vectype); |
| /* 32-bit vectors loaded into registers are stored as double |
| precision, so we need n/2 converts in addition to the usual |
| n/2 merges to construct a vector of short floats from them. */ |
| if (SCALAR_FLOAT_TYPE_P (elem_type) |
| && TYPE_PRECISION (elem_type) == 32) |
| return elements + 1; |
| else |
| return elements / 2 + 1; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Implement targetm.vectorize.preferred_simd_mode. */ |
| |
| static machine_mode |
| rs6000_preferred_simd_mode (machine_mode mode) |
| { |
| if (TARGET_VSX) |
| switch (mode) |
| { |
| case DFmode: |
| return V2DFmode; |
| default:; |
| } |
| if (TARGET_ALTIVEC || TARGET_VSX) |
| switch (mode) |
| { |
| case SFmode: |
| return V4SFmode; |
| case TImode: |
| return V1TImode; |
| case DImode: |
| return V2DImode; |
| case SImode: |
| return V4SImode; |
| case HImode: |
| return V8HImode; |
| case QImode: |
| return V16QImode; |
| default:; |
| } |
| if (TARGET_SPE) |
| switch (mode) |
| { |
| case SFmode: |
| return V2SFmode; |
| case SImode: |
| return V2SImode; |
| default:; |
| } |
| if (TARGET_PAIRED_FLOAT |
| && mode == SFmode) |
| return V2SFmode; |
| return word_mode; |
| } |
| |
| typedef struct _rs6000_cost_data |
| { |
| struct loop *loop_info; |
| unsigned cost[3]; |
| } rs6000_cost_data; |
| |
| /* Test for likely overcommitment of vector hardware resources. If a |
| loop iteration is relatively large, and too large a percentage of |
| instructions in the loop are vectorized, the cost model may not |
| adequately reflect delays from unavailable vector resources. |
| Penalize the loop body cost for this case. */ |
| |
| static void |
| rs6000_density_test (rs6000_cost_data *data) |
| { |
| const int DENSITY_PCT_THRESHOLD = 85; |
| const int DENSITY_SIZE_THRESHOLD = 70; |
| const int DENSITY_PENALTY = 10; |
| struct loop *loop = data->loop_info; |
| basic_block *bbs = get_loop_body (loop); |
| int nbbs = loop->num_nodes; |
| int vec_cost = data->cost[vect_body], not_vec_cost = 0; |
| int i, density_pct; |
| |
| for (i = 0; i < nbbs; i++) |
| { |
| basic_block bb = bbs[i]; |
| gimple_stmt_iterator gsi; |
| |
| for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple stmt = gsi_stmt (gsi); |
| stmt_vec_info stmt_info = vinfo_for_stmt (stmt); |
| |
| if (!STMT_VINFO_RELEVANT_P (stmt_info) |
| && !STMT_VINFO_IN_PATTERN_P (stmt_info)) |
| not_vec_cost++; |
| } |
| } |
| |
| free (bbs); |
| density_pct = (vec_cost * 100) / (vec_cost + not_vec_cost); |
| |
| if (density_pct > DENSITY_PCT_THRESHOLD |
| && vec_cost + not_vec_cost > DENSITY_SIZE_THRESHOLD) |
| { |
| data->cost[vect_body] = vec_cost * (100 + DENSITY_PENALTY) / 100; |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, vect_location, |
| "density %d%%, cost %d exceeds threshold, penalizing " |
| "loop body cost by %d%%", density_pct, |
| vec_cost + not_vec_cost, DENSITY_PENALTY); |
| } |
| } |
| |
| /* Implement targetm.vectorize.init_cost. */ |
| |
| static void * |
| rs6000_init_cost (struct loop *loop_info) |
| { |
| rs6000_cost_data *data = XNEW (struct _rs6000_cost_data); |
| data->loop_info = loop_info; |
| data->cost[vect_prologue] = 0; |
| data->cost[vect_body] = 0; |
| data->cost[vect_epilogue] = 0; |
| return data; |
| } |
| |
| /* Implement targetm.vectorize.add_stmt_cost. */ |
| |
| static unsigned |
| rs6000_add_stmt_cost (void *data, int count, enum vect_cost_for_stmt kind, |
| struct _stmt_vec_info *stmt_info, int misalign, |
| enum vect_cost_model_location where) |
| { |
| rs6000_cost_data *cost_data = (rs6000_cost_data*) data; |
| unsigned retval = 0; |
| |
| if (flag_vect_cost_model) |
| { |
| tree vectype = stmt_info ? stmt_vectype (stmt_info) : NULL_TREE; |
| int stmt_cost = rs6000_builtin_vectorization_cost (kind, vectype, |
| misalign); |
| /* Statements in an inner loop relative to the loop being |
| vectorized are weighted more heavily. The value here is |
| arbitrary and could potentially be improved with analysis. */ |
| if (where == vect_body && stmt_info && stmt_in_inner_loop_p (stmt_info)) |
| count *= 50; /* FIXME. */ |
| |
| retval = (unsigned) (count * stmt_cost); |
| cost_data->cost[where] += retval; |
| } |
| |
| return retval; |
| } |
| |
| /* Implement targetm.vectorize.finish_cost. */ |
| |
| static void |
| rs6000_finish_cost (void *data, unsigned *prologue_cost, |
| unsigned *body_cost, unsigned *epilogue_cost) |
| { |
| rs6000_cost_data *cost_data = (rs6000_cost_data*) data; |
| |
| if (cost_data->loop_info) |
| rs6000_density_test (cost_data); |
| |
| *prologue_cost = cost_data->cost[vect_prologue]; |
| *body_cost = cost_data->cost[vect_body]; |
| *epilogue_cost = cost_data->cost[vect_epilogue]; |
| } |
| |
| /* Implement targetm.vectorize.destroy_cost_data. */ |
| |
| static void |
| rs6000_destroy_cost_data (void *data) |
| { |
| free (data); |
| } |
| |
| /* Handler for the Mathematical Acceleration Subsystem (mass) interface to a |
| library with vectorized intrinsics. */ |
| |
| static tree |
| rs6000_builtin_vectorized_libmass (tree fndecl, tree type_out, tree type_in) |
| { |
| char name[32]; |
| const char *suffix = NULL; |
| tree fntype, new_fndecl, bdecl = NULL_TREE; |
| int n_args = 1; |
| const char *bname; |
| machine_mode el_mode, in_mode; |
| int n, in_n; |
| |
| /* Libmass is suitable for unsafe math only as it does not correctly support |
| parts of IEEE with the required precision such as denormals. Only support |
| it if we have VSX to use the simd d2 or f4 functions. |
| XXX: Add variable length support. */ |
| if (!flag_unsafe_math_optimizations || !TARGET_VSX) |
| return NULL_TREE; |
| |
| el_mode = TYPE_MODE (TREE_TYPE (type_out)); |
| n = TYPE_VECTOR_SUBPARTS (type_out); |
| in_mode = TYPE_MODE (TREE_TYPE (type_in)); |
| in_n = TYPE_VECTOR_SUBPARTS (type_in); |
| if (el_mode != in_mode |
| || n != in_n) |
| return NULL_TREE; |
| |
| if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) |
| { |
| enum built_in_function fn = DECL_FUNCTION_CODE (fndecl); |
| switch (fn) |
| { |
| case BUILT_IN_ATAN2: |
| case BUILT_IN_HYPOT: |
| case BUILT_IN_POW: |
| n_args = 2; |
| /* fall through */ |
| |
| case BUILT_IN_ACOS: |
| case BUILT_IN_ACOSH: |
| case BUILT_IN_ASIN: |
| case BUILT_IN_ASINH: |
| case BUILT_IN_ATAN: |
| case BUILT_IN_ATANH: |
| case BUILT_IN_CBRT: |
| case BUILT_IN_COS: |
| case BUILT_IN_COSH: |
| case BUILT_IN_ERF: |
| case BUILT_IN_ERFC: |
| case BUILT_IN_EXP2: |
| case BUILT_IN_EXP: |
| case BUILT_IN_EXPM1: |
| case BUILT_IN_LGAMMA: |
| case BUILT_IN_LOG10: |
| case BUILT_IN_LOG1P: |
| case BUILT_IN_LOG2: |
| case BUILT_IN_LOG: |
| case BUILT_IN_SIN: |
| case BUILT_IN_SINH: |
| case BUILT_IN_SQRT: |
| case BUILT_IN_TAN: |
| case BUILT_IN_TANH: |
| bdecl = builtin_decl_implicit (fn); |
| suffix = "d2"; /* pow -> powd2 */ |
| if (el_mode != DFmode |
| || n != 2 |
| || !bdecl) |
| return NULL_TREE; |
| break; |
| |
| case BUILT_IN_ATAN2F: |
| case BUILT_IN_HYPOTF: |
| case BUILT_IN_POWF: |
| n_args = 2; |
| /* fall through */ |
| |
| case BUILT_IN_ACOSF: |
| case BUILT_IN_ACOSHF: |
| case BUILT_IN_ASINF: |
| case BUILT_IN_ASINHF: |
| case BUILT_IN_ATANF: |
| case BUILT_IN_ATANHF: |
| case BUILT_IN_CBRTF: |
| case BUILT_IN_COSF: |
| case BUILT_IN_COSHF: |
| case BUILT_IN_ERFF: |
| case BUILT_IN_ERFCF: |
| case BUILT_IN_EXP2F: |
| case BUILT_IN_EXPF: |
| case BUILT_IN_EXPM1F: |
| case BUILT_IN_LGAMMAF: |
| case BUILT_IN_LOG10F: |
| case BUILT_IN_LOG1PF: |
| case BUILT_IN_LOG2F: |
| case BUILT_IN_LOGF: |
| case BUILT_IN_SINF: |
| case BUILT_IN_SINHF: |
| case BUILT_IN_SQRTF: |
| case BUILT_IN_TANF: |
| case BUILT_IN_TANHF: |
| bdecl = builtin_decl_implicit (fn); |
| suffix = "4"; /* powf -> powf4 */ |
| if (el_mode != SFmode |
| || n != 4 |
| || !bdecl) |
| return NULL_TREE; |
| break; |
| |
| default: |
| return NULL_TREE; |
| } |
| } |
| else |
| return NULL_TREE; |
| |
| gcc_assert (suffix != NULL); |
| bname = IDENTIFIER_POINTER (DECL_NAME (bdecl)); |
| if (!bname) |
| return NULL_TREE; |
| |
| strcpy (name, bname + sizeof ("__builtin_") - 1); |
| strcat (name, suffix); |
| |
| if (n_args == 1) |
| fntype = build_function_type_list (type_out, type_in, NULL); |
| else if (n_args == 2) |
| fntype = build_function_type_list (type_out, type_in, type_in, NULL); |
| else |
| gcc_unreachable (); |
| |
| /* Build a function declaration for the vectorized function. */ |
| new_fndecl = build_decl (BUILTINS_LOCATION, |
| FUNCTION_DECL, get_identifier (name), fntype); |
| TREE_PUBLIC (new_fndecl) = 1; |
| DECL_EXTERNAL (new_fndecl) = 1; |
| DECL_IS_NOVOPS (new_fndecl) = 1; |
| TREE_READONLY (new_fndecl) = 1; |
| |
| return new_fndecl; |
| } |
| |
| /* Returns a function decl for a vectorized version of the builtin function |
| with builtin function code FN and the result vector type TYPE, or NULL_TREE |
| if it is not available. */ |
| |
| static tree |
| rs6000_builtin_vectorized_function (tree fndecl, tree type_out, |
| tree type_in) |
| { |
| machine_mode in_mode, out_mode; |
| int in_n, out_n; |
| |
| if (TARGET_DEBUG_BUILTIN) |
| fprintf (stderr, "rs6000_builtin_vectorized_function (%s, %s, %s)\n", |
| IDENTIFIER_POINTER (DECL_NAME (fndecl)), |
| GET_MODE_NAME (TYPE_MODE (type_out)), |
| GET_MODE_NAME (TYPE_MODE (type_in))); |
| |
| if (TREE_CODE (type_out) != VECTOR_TYPE |
| || TREE_CODE (type_in) != VECTOR_TYPE |
| || !TARGET_VECTORIZE_BUILTINS) |
| return NULL_TREE; |
| |
| out_mode = TYPE_MODE (TREE_TYPE (type_out)); |
| out_n = TYPE_VECTOR_SUBPARTS (type_out); |
| in_mode = TYPE_MODE (TREE_TYPE (type_in)); |
| in_n = TYPE_VECTOR_SUBPARTS (type_in); |
| |
| if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) |
| { |
| enum built_in_function fn = DECL_FUNCTION_CODE (fndecl); |
| switch (fn) |
| { |
| case BUILT_IN_CLZIMAX: |
| case BUILT_IN_CLZLL: |
| case BUILT_IN_CLZL: |
| case BUILT_IN_CLZ: |
| if (TARGET_P8_VECTOR && in_mode == out_mode && out_n == in_n) |
| { |
| if (out_mode == QImode && out_n == 16) |
| return rs6000_builtin_decls[P8V_BUILTIN_VCLZB]; |
| else if (out_mode == HImode && out_n == 8) |
| return rs6000_builtin_decls[P8V_BUILTIN_VCLZH]; |
| else if (out_mode == SImode && out_n == 4) |
| return rs6000_builtin_decls[P8V_BUILTIN_VCLZW]; |
| else if (out_mode == DImode && out_n == 2) |
| return rs6000_builtin_decls[P8V_BUILTIN_VCLZD]; |
| } |
| break; |
| case BUILT_IN_COPYSIGN: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_CPSGNDP]; |
| break; |
| case BUILT_IN_COPYSIGNF: |
| if (out_mode != SFmode || out_n != 4 |
| || in_mode != SFmode || in_n != 4) |
| break; |
| if (VECTOR_UNIT_VSX_P (V4SFmode)) |
| return rs6000_builtin_decls[VSX_BUILTIN_CPSGNSP]; |
| if (VECTOR_UNIT_ALTIVEC_P (V4SFmode)) |
| return rs6000_builtin_decls[ALTIVEC_BUILTIN_COPYSIGN_V4SF]; |
| break; |
| case BUILT_IN_POPCOUNTIMAX: |
| case BUILT_IN_POPCOUNTLL: |
| case BUILT_IN_POPCOUNTL: |
| case BUILT_IN_POPCOUNT: |
| if (TARGET_P8_VECTOR && in_mode == out_mode && out_n == in_n) |
| { |
| if (out_mode == QImode && out_n == 16) |
| return rs6000_builtin_decls[P8V_BUILTIN_VPOPCNTB]; |
| else if (out_mode == HImode && out_n == 8) |
| return rs6000_builtin_decls[P8V_BUILTIN_VPOPCNTH]; |
| else if (out_mode == SImode && out_n == 4) |
| return rs6000_builtin_decls[P8V_BUILTIN_VPOPCNTW]; |
| else if (out_mode == DImode && out_n == 2) |
| return rs6000_builtin_decls[P8V_BUILTIN_VPOPCNTD]; |
| } |
| break; |
| case BUILT_IN_SQRT: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVSQRTDP]; |
| break; |
| case BUILT_IN_SQRTF: |
| if (VECTOR_UNIT_VSX_P (V4SFmode) |
| && out_mode == SFmode && out_n == 4 |
| && in_mode == SFmode && in_n == 4) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVSQRTSP]; |
| break; |
| case BUILT_IN_CEIL: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRDPIP]; |
| break; |
| case BUILT_IN_CEILF: |
| if (out_mode != SFmode || out_n != 4 |
| || in_mode != SFmode || in_n != 4) |
| break; |
| if (VECTOR_UNIT_VSX_P (V4SFmode)) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRSPIP]; |
| if (VECTOR_UNIT_ALTIVEC_P (V4SFmode)) |
| return rs6000_builtin_decls[ALTIVEC_BUILTIN_VRFIP]; |
| break; |
| case BUILT_IN_FLOOR: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRDPIM]; |
| break; |
| case BUILT_IN_FLOORF: |
| if (out_mode != SFmode || out_n != 4 |
| || in_mode != SFmode || in_n != 4) |
| break; |
| if (VECTOR_UNIT_VSX_P (V4SFmode)) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRSPIM]; |
| if (VECTOR_UNIT_ALTIVEC_P (V4SFmode)) |
| return rs6000_builtin_decls[ALTIVEC_BUILTIN_VRFIM]; |
| break; |
| case BUILT_IN_FMA: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVMADDDP]; |
| break; |
| case BUILT_IN_FMAF: |
| if (VECTOR_UNIT_VSX_P (V4SFmode) |
| && out_mode == SFmode && out_n == 4 |
| && in_mode == SFmode && in_n == 4) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVMADDSP]; |
| else if (VECTOR_UNIT_ALTIVEC_P (V4SFmode) |
| && out_mode == SFmode && out_n == 4 |
| && in_mode == SFmode && in_n == 4) |
| return rs6000_builtin_decls[ALTIVEC_BUILTIN_VMADDFP]; |
| break; |
| case BUILT_IN_TRUNC: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRDPIZ]; |
| break; |
| case BUILT_IN_TRUNCF: |
| if (out_mode != SFmode || out_n != 4 |
| || in_mode != SFmode || in_n != 4) |
| break; |
| if (VECTOR_UNIT_VSX_P (V4SFmode)) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRSPIZ]; |
| if (VECTOR_UNIT_ALTIVEC_P (V4SFmode)) |
| return rs6000_builtin_decls[ALTIVEC_BUILTIN_VRFIZ]; |
| break; |
| case BUILT_IN_NEARBYINT: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && flag_unsafe_math_optimizations |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRDPI]; |
| break; |
| case BUILT_IN_NEARBYINTF: |
| if (VECTOR_UNIT_VSX_P (V4SFmode) |
| && flag_unsafe_math_optimizations |
| && out_mode == SFmode && out_n == 4 |
| && in_mode == SFmode && in_n == 4) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRSPI]; |
| break; |
| case BUILT_IN_RINT: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && !flag_trapping_math |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRDPIC]; |
| break; |
| case BUILT_IN_RINTF: |
| if (VECTOR_UNIT_VSX_P (V4SFmode) |
| && !flag_trapping_math |
| && out_mode == SFmode && out_n == 4 |
| && in_mode == SFmode && in_n == 4) |
| return rs6000_builtin_decls[VSX_BUILTIN_XVRSPIC]; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| else if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) |
| { |
| enum rs6000_builtins fn |
| = (enum rs6000_builtins)DECL_FUNCTION_CODE (fndecl); |
| switch (fn) |
| { |
| case RS6000_BUILTIN_RSQRTF: |
| if (VECTOR_UNIT_ALTIVEC_OR_VSX_P (V4SFmode) |
| && out_mode == SFmode && out_n == 4 |
| && in_mode == SFmode && in_n == 4) |
| return rs6000_builtin_decls[ALTIVEC_BUILTIN_VRSQRTFP]; |
| break; |
| case RS6000_BUILTIN_RSQRT: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_RSQRT_2DF]; |
| break; |
| case RS6000_BUILTIN_RECIPF: |
| if (VECTOR_UNIT_ALTIVEC_OR_VSX_P (V4SFmode) |
| && out_mode == SFmode && out_n == 4 |
| && in_mode == SFmode && in_n == 4) |
| return rs6000_builtin_decls[ALTIVEC_BUILTIN_VRECIPFP]; |
| break; |
| case RS6000_BUILTIN_RECIP: |
| if (VECTOR_UNIT_VSX_P (V2DFmode) |
| && out_mode == DFmode && out_n == 2 |
| && in_mode == DFmode && in_n == 2) |
| return rs6000_builtin_decls[VSX_BUILTIN_RECIP_V2DF]; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* Generate calls to libmass if appropriate. */ |
| if (rs6000_veclib_handler) |
| return rs6000_veclib_handler (fndecl, type_out, type_in); |
| |
| return NULL_TREE; |
| } |
| |
| /* Default CPU string for rs6000*_file_start functions. */ |
| static const char *rs6000_default_cpu; |
| |
| /* Do anything needed at the start of the asm file. */ |
| |
| static void |
| rs6000_file_start (void) |
| { |
| char buffer[80]; |
| const char *start = buffer; |
| FILE *file = asm_out_file; |
| |
| rs6000_default_cpu = TARGET_CPU_DEFAULT; |
| |
| default_file_start (); |
| |
| if (flag_verbose_asm) |
| { |
| sprintf (buffer, "\n%s rs6000/powerpc options:", ASM_COMMENT_START); |
| |
| if (rs6000_default_cpu != 0 && rs6000_default_cpu[0] != '\0') |
| { |
| fprintf (file, "%s --with-cpu=%s", start, rs6000_default_cpu); |
| start = ""; |
| } |
| |
| if (global_options_set.x_rs6000_cpu_index) |
| { |
| fprintf (file, "%s -mcpu=%s", start, |
| processor_target_table[rs6000_cpu_index].name); |
| start = ""; |
| } |
| |
| if (global_options_set.x_rs6000_tune_index) |
| { |
| fprintf (file, "%s -mtune=%s", start, |
| processor_target_table[rs6000_tune_index].name); |
| 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 %d", start, |
| g_switch_value); |
| start = ""; |
| } |
| #endif |
| |
| if (*start == '\0') |
| putc ('\n', file); |
| } |
| |
| #ifdef USING_ELFOS_H |
| if (!(rs6000_default_cpu && rs6000_default_cpu[0]) |
| && !global_options_set.x_rs6000_cpu_index) |
| { |
| fputs ("\t.machine ", asm_out_file); |
| if ((rs6000_isa_flags & OPTION_MASK_DIRECT_MOVE) != 0) |
| fputs ("power8\n", asm_out_file); |
| else if ((rs6000_isa_flags & OPTION_MASK_POPCNTD) != 0) |
| fputs ("power7\n", asm_out_file); |
| else if ((rs6000_isa_flags & OPTION_MASK_CMPB) != 0) |
| fputs ("power6\n", asm_out_file); |
| else if ((rs6000_isa_flags & OPTION_MASK_POPCNTB) != 0) |
| fputs ("power5\n", asm_out_file); |
| else if ((rs6000_isa_flags & OPTION_MASK_MFCRF) != 0) |
| fputs ("power4\n", asm_out_file); |
| else if ((rs6000_isa_flags & OPTION_MASK_POWERPC64) != 0) |
| fputs ("ppc64\n", asm_out_file); |
| else |
| fputs ("ppc\n", asm_out_file); |
| } |
| #endif |
| |
| if (DEFAULT_ABI == ABI_ELFv2) |
| fprintf (file, "\t.abiversion 2\n"); |
| |
| if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2 |
| || (TARGET_ELF && flag_pic == 2)) |
| { |
| switch_to_section (toc_section); |
| switch_to_section (text_section); |
| } |
| } |
| |
| |
| /* 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 addi */ |
| if (((unsigned HOST_WIDE_INT) value + 0x8000) < 0x10000) |
| return 1; |
| |
| /* constant loadable with addis */ |
| else if ((value & 0xffff) == 0 |
| && (value >> 31 == -1 || value >> 31 == 0)) |
| return 1; |
| |
| 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 if (high == 0) |
| return num_insns_constant_wide (low) + 1; |
| else |
| return (num_insns_constant_wide (high) |
| + num_insns_constant_wide (low) + 1); |
| } |
| |
| else |
| return 2; |
| } |
| |
| int |
| num_insns_constant (rtx op, machine_mode mode) |
| { |
| HOST_WIDE_INT low, high; |
| |
| switch (GET_CODE (op)) |
| { |
| case CONST_INT: |
| if ((INTVAL (op) >> 31) != 0 && (INTVAL (op) >> 31) != -1 |
| && mask64_operand (op, mode)) |
| return 2; |
| else |
| return num_insns_constant_wide (INTVAL (op)); |
| |
| case CONST_WIDE_INT: |
| { |
| int i; |
| int ins = CONST_WIDE_INT_NUNITS (op) - 1; |
| for (i = 0; i < CONST_WIDE_INT_NUNITS (op); i++) |
| ins += num_insns_constant_wide (CONST_WIDE_INT_ELT (op, i)); |
| return ins; |
| } |
| |
| case CONST_DOUBLE: |
| if (mode == SFmode || mode == SDmode) |
| { |
| long l; |
| REAL_VALUE_TYPE rv; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, op); |
| if (DECIMAL_FLOAT_MODE_P (mode)) |
| REAL_VALUE_TO_TARGET_DECIMAL32 (rv, l); |
| else |
| REAL_VALUE_TO_TARGET_SINGLE (rv, l); |
| return num_insns_constant_wide ((HOST_WIDE_INT) l); |
| } |
| |
| long l[2]; |
| REAL_VALUE_TYPE rv; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, op); |
| if (DECIMAL_FLOAT_MODE_P (mode)) |
| REAL_VALUE_TO_TARGET_DECIMAL64 (rv, l); |
| else |
| 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. */ |
| |
| HOST_WIDE_INT |
| const_vector_elt_as_int (rtx op, unsigned int elt) |
| { |
| rtx tmp; |
| |
| /* We can't handle V2DImode and V2DFmode vector constants here yet. */ |
| gcc_assert (GET_MODE (op) != V2DImode |
| && GET_MODE (op) != V2DFmode); |
| |
| 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) |
| { |
| machine_mode mode = GET_MODE (op); |
| machine_mode inner = GET_MODE_INNER (mode); |
| |
| unsigned i; |
| unsigned nunits; |
| unsigned bitsize; |
| unsigned mask; |
| |
| HOST_WIDE_INT val; |
| HOST_WIDE_INT splat_val; |
| HOST_WIDE_INT msb_val; |
| |
| if (mode == V2DImode || mode == V2DFmode || mode == V1TImode) |
| return false; |
| |
| nunits = GET_MODE_NUNITS (mode); |
| bitsize = GET_MODE_BITSIZE (inner); |
| mask = GET_MODE_MASK (inner); |
| |
| val = const_vector_elt_as_int (op, BYTES_BIG_ENDIAN ? nunits - 1 : 0); |
| splat_val = val; |
| 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))) |
| ; |
| |
| /* Also check if are loading up the most significant bit which can be done by |
| loading up -1 and shifting the value left by -1. */ |
| else if (EASY_VECTOR_MSB (splat_val, inner)) |
| ; |
| |
| 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 = 1; i < nunits; ++i) |
| { |
| HOST_WIDE_INT desired_val; |
| unsigned elt = BYTES_BIG_ENDIAN ? nunits - 1 - i : i; |
| if ((i & (step - 1)) == 0) |
| desired_val = val; |
| else |
| desired_val = msb_val; |
| |
| if (desired_val != const_vector_elt_as_int (op, elt)) |
| 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, machine_mode mode) |
| { |
| unsigned step, copies; |
| |
| if (mode == VOIDmode) |
| mode = GET_MODE (op); |
| else if (mode != GET_MODE (op)) |
| return false; |
| |
| /* V2DI/V2DF was added with VSX. Only allow 0 and all 1's as easy |
| constants. */ |
| if (mode == V2DFmode) |
| return zero_constant (op, mode); |
| |
| else if (mode == V2DImode) |
| { |
| if (GET_CODE (CONST_VECTOR_ELT (op, 0)) != CONST_INT |
| || GET_CODE (CONST_VECTOR_ELT (op, 1)) != CONST_INT) |
| return false; |
| |
| if (zero_constant (op, mode)) |
| return true; |
| |
| if (INTVAL (CONST_VECTOR_ELT (op, 0)) == -1 |
| && INTVAL (CONST_VECTOR_ELT (op, 1)) == -1) |
| return true; |
| |
| return false; |
| } |
| |
| /* V1TImode is a special container for TImode. Ignore for now. */ |
| else if (mode == V1TImode) |
| 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) |
| { |
| machine_mode mode = GET_MODE (op); |
| int nunits = GET_MODE_NUNITS (mode); |
| rtx val = CONST_VECTOR_ELT (op, BYTES_BIG_ENDIAN ? nunits - 1 : 0); |
| 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, val)); |
| |
| /* 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, val)); |
| |
| /* 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, val)); |
| |
| gcc_unreachable (); |
| } |
| |
| const char * |
| output_vec_const_move (rtx *operands) |
| { |
| int cst, cst2; |
| machine_mode mode; |
| rtx dest, vec; |
| |
| dest = operands[0]; |
| vec = operands[1]; |
| mode = GET_MODE (dest); |
| |
| if (TARGET_VSX) |
| { |
| if (zero_constant (vec, mode)) |
| return "xxlxor %x0,%x0,%x0"; |
| |
| if ((mode == V2DImode || mode == V1TImode) |
| && INTVAL (CONST_VECTOR_ELT (vec, 0)) == -1 |
| && INTVAL (CONST_VECTOR_ELT (vec, 1)) == -1) |
| return "vspltisw %0,-1"; |
| } |
| |
| 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 if (WORDS_BIG_ENDIAN) |
| return "li %0,%1\n\tevmergelo %0,%0,%0\n\tli %0,%2"; |
| else |
| return "li %0,%2\n\tevmergelo %0,%0,%0\n\tli %0,%1"; |
| } |
| |
| /* Initialize TARGET of vector PAIRED to VALS. */ |
| |
| void |
| paired_expand_vector_init (rtx target, rtx vals) |
| { |
| machine_mode mode = GET_MODE (target); |
| int n_elts = GET_MODE_NUNITS (mode); |
| int n_var = 0; |
| rtx x, new_rtx, tmp, constant_op, op1, op2; |
| int i; |
| |
| for (i = 0; i < n_elts; ++i) |
| { |
| x = XVECEXP (vals, 0, i); |
| if (!(CONST_SCALAR_INT_P (x) || CONST_DOUBLE_P (x) || CONST_FIXED_P (x))) |
| ++n_var; |
| } |
| if (n_var == 0) |
| { |
| /* Load from constant pool. */ |
| emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0))); |
| return; |
| } |
| |
| if (n_var == 2) |
| { |
| /* The vector is initialized only with non-constants. */ |
| new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, XVECEXP (vals, 0, 0), |
| XVECEXP (vals, 0, 1)); |
| |
| emit_move_insn (target, new_rtx); |
| return; |
| } |
| |
| /* One field is non-constant and the other one is a constant. Load the |
| constant from the constant pool and use ps_merge instruction to |
| construct the whole vector. */ |
| op1 = XVECEXP (vals, 0, 0); |
| op2 = XVECEXP (vals, 0, 1); |
| |
| constant_op = (CONSTANT_P (op1)) ? op1 : op2; |
| |
| tmp = gen_reg_rtx (GET_MODE (constant_op)); |
| emit_move_insn (tmp, constant_op); |
| |
| if (CONSTANT_P (op1)) |
| new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, tmp, op2); |
| else |
| new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, op1, tmp); |
| |
| emit_move_insn (target, new_rtx); |
| } |
| |
| void |
| paired_expand_vector_move (rtx operands[]) |
| { |
| rtx op0 = operands[0], op1 = operands[1]; |
| |
| emit_move_insn (op0, op1); |
| } |
| |
| /* Emit vector compare for code RCODE. 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. This is a recursive |
| function. */ |
| |
| static void |
| paired_emit_vector_compare (enum rtx_code rcode, |
| rtx dest, rtx op0, rtx op1, |
| rtx cc_op0, rtx cc_op1) |
| { |
| rtx tmp = gen_reg_rtx (V2SFmode); |
| rtx tmp1, max, min; |
| |
| gcc_assert (TARGET_PAIRED_FLOAT); |
| gcc_assert (GET_MODE (op0) == GET_MODE (op1)); |
| |
| switch (rcode) |
| { |
| case LT: |
| case LTU: |
| paired_emit_vector_compare (GE, dest, op1, op0, cc_op0, cc_op1); |
| return; |
| case GE: |
| case GEU: |
| emit_insn (gen_subv2sf3 (tmp, cc_op0, cc_op1)); |
| emit_insn (gen_selv2sf4 (dest, tmp, op0, op1, CONST0_RTX (SFmode))); |
| return; |
| case LE: |
| case LEU: |
| paired_emit_vector_compare (GE, dest, op0, op1, cc_op1, cc_op0); |
| return; |
| case GT: |
| paired_emit_vector_compare (LE, dest, op1, op0, cc_op0, cc_op1); |
| return; |
| case EQ: |
| tmp1 = gen_reg_rtx (V2SFmode); |
| max = gen_reg_rtx (V2SFmode); |
| min = gen_reg_rtx (V2SFmode); |
| gen_reg_rtx (V2SFmode); |
| |
| emit_insn (gen_subv2sf3 (tmp, cc_op0, cc_op1)); |
| emit_insn (gen_selv2sf4 |
| (max, tmp, cc_op0, cc_op1, CONST0_RTX (SFmode))); |
| emit_insn (gen_subv2sf3 (tmp, cc_op1, cc_op0)); |
| emit_insn (gen_selv2sf4 |
| (min, tmp, cc_op0, cc_op1, CONST0_RTX (SFmode))); |
| emit_insn (gen_subv2sf3 (tmp1, min, max)); |
| emit_insn (gen_selv2sf4 (dest, tmp1, op0, op1, CONST0_RTX (SFmode))); |
| return; |
| case NE: |
| paired_emit_vector_compare (EQ, dest, op1, op0, cc_op0, cc_op1); |
| return; |
| case UNLE: |
| paired_emit_vector_compare (LE, dest, op1, op0, cc_op0, cc_op1); |
| return; |
| case UNLT: |
| paired_emit_vector_compare (LT, dest, op1, op0, cc_op0, cc_op1); |
| return; |
| case UNGE: |
| paired_emit_vector_compare (GE, dest, op1, op0, cc_op0, cc_op1); |
| return; |
| case UNGT: |
| paired_emit_vector_compare (GT, dest, op1, op0, cc_op0, cc_op1); |
| return; |
| default: |
| gcc_unreachable (); |
| } |
| |
| 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 |
| paired_emit_vector_cond_expr (rtx dest, rtx op1, rtx op2, |
| rtx cond, rtx cc_op0, rtx cc_op1) |
| { |
| enum rtx_code rcode = GET_CODE (cond); |
| |
| if (!TARGET_PAIRED_FLOAT) |
| return 0; |
| |
| paired_emit_vector_compare (rcode, dest, op1, op2, cc_op0, cc_op1); |
| |
| return 1; |
| } |
| |
| /* Initialize vector TARGET to VALS. */ |
| |
| void |
| rs6000_expand_vector_init (rtx target, rtx vals) |
| { |
| machine_mode mode = GET_MODE (target); |
| 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 (!(CONST_SCALAR_INT_P (x) || CONST_DOUBLE_P (x) || CONST_FIXED_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) |
| { |
| rtx const_vec = gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0)); |
| bool int_vector_p = (GET_MODE_CLASS (mode) == MODE_VECTOR_INT); |
| if ((int_vector_p || TARGET_VSX) && all_const_zero) |
| { |
| /* Zero register. */ |
| emit_insn (gen_rtx_SET (VOIDmode, target, |
| gen_rtx_XOR (mode, target, target))); |
| return; |
| } |
| else if (int_vector_p && easy_vector_constant (const_vec, mode)) |
| { |
| /* Splat immediate. */ |
| emit_insn (gen_rtx_SET (VOIDmode, target, const_vec)); |
| return; |
| } |
| else |
| { |
| /* Load from constant pool. */ |
| emit_move_insn (target, const_vec); |
| return; |
| } |
| } |
| |
| /* Double word values on VSX can use xxpermdi or lxvdsx. */ |
| if (VECTOR_MEM_VSX_P (mode) && (mode == V2DFmode || mode == V2DImode)) |
| { |
| rtx op0 = XVECEXP (vals, 0, 0); |
| rtx op1 = XVECEXP (vals, 0, 1); |
| if (all_same) |
| { |
| if (!MEM_P (op0) && !REG_P (op0)) |
| op0 = force_reg (inner_mode, op0); |
| if (mode == V2DFmode) |
| emit_insn (gen_vsx_splat_v2df (target, op0)); |
| else |
| emit_insn (gen_vsx_splat_v2di (target, op0)); |
| } |
| else |
| { |
| op0 = force_reg (inner_mode, op0); |
| op1 = force_reg (inner_mode, op1); |
| if (mode == V2DFmode) |
| emit_insn (gen_vsx_concat_v2df (target, op0, op1)); |
| else |
| emit_insn (gen_vsx_concat_v2di (target, op0, op1)); |
| } |
| return; |
| } |
| |
| /* With single precision floating point on VSX, know that internally single |
| precision is actually represented as a double, and either make 2 V2DF |
| vectors, and convert these vectors to single precision, or do one |
| conversion, and splat the result to the other elements. */ |
| if (mode == V4SFmode && VECTOR_MEM_VSX_P (mode)) |
| { |
| if (all_same) |
| { |
| rtx freg = gen_reg_rtx (V4SFmode); |
| rtx sreg = force_reg (SFmode, XVECEXP (vals, 0, 0)); |
| rtx cvt = ((TARGET_XSCVDPSPN) |
| ? gen_vsx_xscvdpspn_scalar (freg, sreg) |
| : gen_vsx_xscvdpsp_scalar (freg, sreg)); |
| |
| emit_insn (cvt); |
| emit_insn (gen_vsx_xxspltw_v4sf_direct (target, freg, const0_rtx)); |
| } |
| else |
| { |
| rtx dbl_even = gen_reg_rtx (V2DFmode); |
| rtx dbl_odd = gen_reg_rtx (V2DFmode); |
| rtx flt_even = gen_reg_rtx (V4SFmode); |
| rtx flt_odd = gen_reg_rtx (V4SFmode); |
| rtx op0 = force_reg (SFmode, XVECEXP (vals, 0, 0)); |
| rtx op1 = force_reg (SFmode, XVECEXP (vals, 0, 1)); |
| rtx op2 = force_reg (SFmode, XVECEXP (vals, 0, 2)); |
| rtx op3 = force_reg (SFmode, XVECEXP (vals, 0, 3)); |
| |
| emit_insn (gen_vsx_concat_v2sf (dbl_even, op0, op1)); |
| emit_insn (gen_vsx_concat_v2sf (dbl_odd, op2, op3)); |
| emit_insn (gen_vsx_xvcvdpsp (flt_even, dbl_even)); |
| emit_insn (gen_vsx_xvcvdpsp (flt_odd, dbl_odd)); |
| rs6000_expand_extract_even (target, flt_even, flt_odd); |
| } |
| return; |
| } |
| |
| /* Store value to stack temp. Load vector element. Splat. However, splat |
| of 64-bit items is not supported on Altivec. */ |
| if (all_same && GET_MODE_SIZE (inner_mode) <= 4) |
| { |
| mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode)); |
| emit_move_insn (adjust_address_nv (mem, inner_mode, 0), |
| 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, mem), |
| x))); |
| 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)); |
| 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) |
| { |
| machine_mode mode = GET_MODE (target); |
| machine_mode inner_mode = GET_MODE_INNER (mode); |
| rtx reg = gen_reg_rtx (mode); |
| rtx mask, mem, x; |
| int width = GET_MODE_SIZE (inner_mode); |
| int i; |
| |
| if (VECTOR_MEM_VSX_P (mode) && (mode == V2DFmode || mode == V2DImode)) |
| { |
| rtx (*set_func) (rtx, rtx, rtx, rtx) |
| = ((mode == V2DFmode) ? gen_vsx_set_v2df : gen_vsx_set_v2di); |
| emit_insn (set_func (target, target, val, GEN_INT (elt))); |
| return; |
| } |
| |
| /* Simplify setting single element vectors like V1TImode. */ |
| if (GET_MODE_SIZE (mode) == GET_MODE_SIZE (inner_mode) && elt == 0) |
| { |
| emit_move_insn (target, gen_lowpart (mode, val)); |
| return; |
| } |
| |
| /* Load single variable value. */ |
| mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode)); |
| emit_move_insn (adjust_address_nv (mem, inner_mode, 0), val); |
| 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, mem), |
| x))); |
| |
| /* 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)); |
| |
| if (BYTES_BIG_ENDIAN) |
| x = gen_rtx_UNSPEC (mode, |
| gen_rtvec (3, target, reg, |
| force_reg (V16QImode, x)), |
| UNSPEC_VPERM); |
| else |
| { |
| /* Invert selector. We prefer to generate VNAND on P8 so |
| that future fusion opportunities can kick in, but must |
| generate VNOR elsewhere. */ |
| rtx notx = gen_rtx_NOT (V16QImode, force_reg (V16QImode, x)); |
| rtx iorx = (TARGET_P8_VECTOR |
| ? gen_rtx_IOR (V16QImode, notx, notx) |
| : gen_rtx_AND (V16QImode, notx, notx)); |
| rtx tmp = gen_reg_rtx (V16QImode); |
| emit_insn (gen_rtx_SET (VOIDmode, tmp, iorx)); |
| |
| /* Permute with operands reversed and adjusted selector. */ |
| x = gen_rtx_UNSPEC (mode, gen_rtvec (3, reg, target, tmp), |
| 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) |
| { |
| machine_mode mode = GET_MODE (vec); |
| machine_mode inner_mode = GET_MODE_INNER (mode); |
| rtx mem; |
| |
| if (VECTOR_MEM_VSX_P (mode)) |
| { |
| switch (mode) |
| { |
| default: |
| break; |
| case V1TImode: |
| gcc_assert (elt == 0 && inner_mode == TImode); |
| emit_move_insn (target, gen_lowpart (TImode, vec)); |
| break; |
| case V2DFmode: |
| emit_insn (gen_vsx_extract_v2df (target, vec, GEN_INT (elt))); |
| return; |
| case V2DImode: |
| emit_insn (gen_vsx_extract_v2di (target, vec, GEN_INT (elt))); |
| return; |
| case V4SFmode: |
| emit_insn (gen_vsx_extract_v4sf (target, vec, GEN_INT (elt))); |
| return; |
| } |
| } |
| |
| /* Allocate mode-sized buffer. */ |
| mem = assign_stack_temp (mode, GET_MODE_SIZE (mode)); |
| |
| emit_move_insn (mem, vec); |
| |
| /* Add offset to field within buffer matching vector element. */ |
| mem = adjust_address_nv (mem, inner_mode, elt * GET_MODE_SIZE (inner_mode)); |
| |
| 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) |
| { |
| 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); |
| } |
| |
| /* Return TRUE if OP is an invalid SUBREG operation on the e500. */ |
| |
| bool |
| invalid_e500_subreg (rtx op, machine_mode mode) |
| { |
| if (TARGET_E500_DOUBLE) |
| { |
| /* Reject (subreg:SI (reg:DF)); likewise with subreg:DI or |
| subreg:TI and reg:TF. Decimal float modes are like integer |
| modes (only low part of each register used) for this |
| purpose. */ |
| if (GET_CODE (op) == SUBREG |
| && (mode == SImode || mode == DImode || mode == TImode |
| || mode == DDmode || mode == TDmode || mode == PTImode) |
| && REG_P (SUBREG_REG (op)) |
| && (GET_MODE (SUBREG_REG (op)) == DFmode |
| || GET_MODE (SUBREG_REG (op)) == TFmode)) |
| return true; |
| |
| /* Reject (subreg:DF (reg:DI)); likewise with subreg:TF and |
| reg:TI. */ |
| if (GET_CODE (op) == SUBREG |
| && (mode == DFmode || mode == TFmode) |
| && REG_P (SUBREG_REG (op)) |
| && (GET_MODE (SUBREG_REG (op)) == DImode |
| || GET_MODE (SUBREG_REG (op)) == TImode |
| || GET_MODE (SUBREG_REG (op)) == PTImode |
| || GET_MODE (SUBREG_REG (op)) == DDmode |
| || GET_MODE (SUBREG_REG (op)) == TDmode)) |
| 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; |
| } |
| |
| /* Return alignment of TYPE. Existing alignment is ALIGN. HOW |
| selects whether the alignment is abi mandated, optional, or |
| both abi and optional alignment. */ |
| |
| unsigned int |
| rs6000_data_alignment (tree type, unsigned int align, enum data_align how) |
| { |
| if (how != align_opt) |
| { |
| if (TREE_CODE (type) == VECTOR_TYPE) |
| { |
| if ((TARGET_SPE && SPE_VECTOR_MODE (TYPE_MODE (type))) |
| || (TARGET_PAIRED_FLOAT && PAIRED_VECTOR_MODE (TYPE_MODE (type)))) |
| { |
| if (align < 64) |
| align = 64; |
| } |
| else if (align < 128) |
| align = 128; |
| } |
| else if (TARGET_E500_DOUBLE |
| && TREE_CODE (type) == REAL_TYPE |
| && TYPE_MODE (type) == DFmode) |
| { |
| if (align < 64) |
| align = 64; |
| } |
| } |
| |
| if (how != align_abi) |
| { |
| if (TREE_CODE (type) == ARRAY_TYPE |
| && TYPE_MODE (TREE_TYPE (type)) == QImode) |
| { |
| if (align < BITS_PER_WORD) |
| align = BITS_PER_WORD; |
| } |
| } |
| |
| return align; |
| } |
| |
| /* Previous GCC releases forced all vector types to have 16-byte alignment. */ |
| |
| bool |
| rs6000_special_adjust_field_align_p (tree field, unsigned int computed) |
| { |
| if (TARGET_ALTIVEC && TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE) |
| { |
| if (computed != 128) |
| { |
| static bool warned; |
| if (!warned && warn_psabi) |
| { |
| warned = true; |
| inform (input_location, |
| "the layout of aggregates containing vectors with" |
| " %d-byte alignment has changed in GCC 5", |
| computed / BITS_PER_UNIT); |
| } |
| } |
| /* In current GCC there is no special case. */ |
| return false; |
| } |
| |
| return false; |
| } |
| |
| /* 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 = DECL_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; |
| } |
| |
| /* 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. */ |
| do { |
| tree field = TYPE_FIELDS (type); |
| /* Skip all non field decls */ |
| while (field != NULL && TREE_CODE (field) != FIELD_DECL) |
| field = DECL_CHAIN (field); |
| if (! field) |
| break; |
| /* A packed field does not contribute any extra alignment. */ |
| if (DECL_PACKED (field)) |
| return align; |
| type = TREE_TYPE (field); |
| while (TREE_CODE (type) == ARRAY_TYPE) |
| type = TREE_TYPE (type); |
| } while (AGGREGATE_TYPE_P (type)); |
| |
| if (! AGGREGATE_TYPE_P (type) && type != error_mark_node) |
| align = MAX (align, TYPE_ALIGN (type)); |
| |
| return align; |
| } |
| |
| /* Return 1 for an operand in small memory on V.4/eabi. */ |
| |
| int |
| small_data_operand (rtx op ATTRIBUTE_UNUSED, |
| 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; |
| |
| /* Vector and float memory instructions have a limited offset on the |
| SPE, so using a vector or float variable directly as an operand is |
| not useful. */ |
| if (TARGET_SPE |
| && (SPE_VECTOR_MODE (mode) || FLOAT_MODE_P (mode))) |
| 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 || 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)))); |
| } |
| |
| /* Return true if this is a move direct operation between GPR registers and |
| floating point/VSX registers. */ |
| |
| bool |
| direct_move_p (rtx op0, rtx op1) |
| { |
| int regno0, regno1; |
| |
| if (!REG_P (op0) || !REG_P (op1)) |
| return false; |
| |
| if (!TARGET_DIRECT_MOVE && !TARGET_MFPGPR) |
| return false; |
| |
| regno0 = REGNO (op0); |
| regno1 = REGNO (op1); |
| if (regno0 >= FIRST_PSEUDO_REGISTER || regno1 >= FIRST_PSEUDO_REGISTER) |
| return false; |
| |
| if (INT_REGNO_P (regno0)) |
| return (TARGET_DIRECT_MOVE) ? VSX_REGNO_P (regno1) : FP_REGNO_P (regno1); |
| |
| else if (INT_REGNO_P (regno1)) |
| { |
| if (TARGET_MFPGPR && FP_REGNO_P (regno0)) |
| return true; |
| |
| else if (TARGET_DIRECT_MOVE && VSX_REGNO_P (regno0)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Return true if this is a load or store quad operation. This function does |
| not handle the atomic quad memory instructions. */ |
| |
| bool |
| quad_load_store_p (rtx op0, rtx op1) |
| { |
| bool ret; |
| |
| if (!TARGET_QUAD_MEMORY) |
| ret = false; |
| |
| else if (REG_P (op0) && MEM_P (op1)) |
| ret = (quad_int_reg_operand (op0, GET_MODE (op0)) |
| && quad_memory_operand (op1, GET_MODE (op1)) |
| && !reg_overlap_mentioned_p (op0, op1)); |
| |
| else if (MEM_P (op0) && REG_P (op1)) |
| ret = (quad_memory_operand (op0, GET_MODE (op0)) |
| && quad_int_reg_operand (op1, GET_MODE (op1))); |
| |
| else |
| ret = false; |
| |
| if (TARGET_DEBUG_ADDR) |
| { |
| fprintf (stderr, "\n========== quad_load_store, return %s\n", |
| ret ? "true" : "false"); |
| debug_rtx (gen_rtx_SET (VOIDmode, op0, op1)); |
| } |
| |
| return ret; |
| } |
| |
| /* Given an address, return a constant offset term if one exists. */ |
| |
| static rtx |
| address_offset (rtx op) |
| { |
| if (GET_CODE (op) == PRE_INC |
| || GET_CODE (op) == PRE_DEC) |
| op = XEXP (op, 0); |
| else if (GET_CODE (op) == PRE_MODIFY |
| || GET_CODE (op) == LO_SUM) |
| op = XEXP (op, 1); |
| |
| if (GET_CODE (op) == CONST) |
| op = XEXP (op, 0); |
| |
| if (GET_CODE (op) == PLUS) |
| op = XEXP (op, 1); |
| |
| if (CONST_INT_P (op)) |
| return op; |
| |
| return NULL_RTX; |
| } |
| |
| /* Return true if the MEM operand is a memory operand suitable for use |
| with a (full width, possibly multiple) gpr load/store. On |
| powerpc64 this means the offset must be divisible by 4. |
| Implements 'Y' constraint. |
| |
| Accept direct, indexed, offset, lo_sum and tocref. Since this is |
| a constraint function we know the operand has satisfied a suitable |
| memory predicate. Also accept some odd rtl generated by reload |
| (see rs6000_legitimize_reload_address for various forms). It is |
| important that reload rtl be accepted by appropriate constraints |
| but not by the operand predicate. |
| |
| Offsetting a lo_sum should not be allowed, except where we know by |
| alignment that a 32k boundary is not crossed, but see the ??? |
| comment in rs6000_legitimize_reload_address. Note that by |
| "offsetting" here we mean a further offset to access parts of the |
| MEM. It's fine to have a lo_sum where the inner address is offset |
| from a sym, since the same sym+offset will appear in the high part |
| of the address calculation. */ |
| |
| bool |
| mem_operand_gpr (rtx op, machine_mode mode) |
| { |
| unsigned HOST_WIDE_INT offset; |
| int extra; |
| rtx addr = XEXP (op, 0); |
| |
| op = address_offset (addr); |
| if (op == NULL_RTX) |
| return true; |
| |
| offset = INTVAL (op); |
| if (TARGET_POWERPC64 && (offset & 3) != 0) |
| return false; |
| |
| extra = GET_MODE_SIZE (mode) - UNITS_PER_WORD; |
| if (extra < 0) |
| extra = 0; |
| |
| if (GET_CODE (addr) == LO_SUM) |
| /* For lo_sum addresses, we must allow any offset except one that |
| causes a wrap, so test only the low 16 bits. */ |
| offset = ((offset & 0xffff) ^ 0x8000) - 0x8000; |
| |
| return offset + 0x8000 < 0x10000u - extra; |
| } |
| |
| /* Subroutines of rs6000_legitimize_address and rs6000_legitimate_address_p. */ |
| |
| static bool |
| reg_offset_addressing_ok_p (machine_mode mode) |
| { |
| switch (mode) |
| { |
| case V16QImode: |
| case V8HImode: |
| case V4SFmode: |
| case V4SImode: |
| case V2DFmode: |
| case V2DImode: |
| case V1TImode: |
| case TImode: |
| /* AltiVec/VSX vector modes. Only reg+reg addressing is valid. While |
| TImode is not a vector mode, if we want to use the VSX registers to |
| move it around, we need to restrict ourselves to reg+reg |
| addressing. */ |
| if (VECTOR_MEM_ALTIVEC_OR_VSX_P (mode)) |
| return false; |
| break; |
| |
| case V4HImode: |
| case V2SImode: |
| case V1DImode: |
| case V2SFmode: |
| /* Paired vector modes. Only reg+reg addressing is valid. */ |
| if (TARGET_PAIRED_FLOAT) |
| return false; |
| break; |
| |
| case SDmode: |
| /* If we can do direct load/stores of SDmode, restrict it to reg+reg |
| addressing for the LFIWZX and STFIWX instructions. */ |
| if (TARGET_NO_SDMODE_STACK) |
| return false; |
| break; |
| |
| default: |
| break; |
| } |
| |
| return true; |
| } |
| |
| static bool |
| virtual_stack_registers_memory_p (rtx op) |
| { |
| int regnum; |
| |
| if (GET_CODE (op) == REG) |
| regnum = REGNO (op); |
| |
| else if (GET_CODE (op) == PLUS |
| && GET_CODE (XEXP (op, 0)) == REG |
| && GET_CODE (XEXP (op, 1)) == CONST_INT) |
| regnum = REGNO (XEXP (op, 0)); |
| |
| else |
| return false; |
| |
| return (regnum >= FIRST_VIRTUAL_REGISTER |
| && regnum <= LAST_VIRTUAL_POINTER_REGISTER); |
| } |
| |
| /* Return true if a MODE sized memory accesses to OP plus OFFSET |
| is known to not straddle a 32k boundary. */ |
| |
| static bool |
| offsettable_ok_by_alignment (rtx op, HOST_WIDE_INT offset, |
| machine_mode mode) |
| { |
| tree decl, type; |
| unsigned HOST_WIDE_INT dsize, dalign, lsb, mask; |
| |
| if (GET_CODE (op) != SYMBOL_REF) |
| return false; |
| |
| dsize = GET_MODE_SIZE (mode); |
| decl = SYMBOL_REF_DECL (op); |
| if (!decl) |
| { |
| if (dsize == 0) |
| return false; |
| |
| /* -fsection-anchors loses the original SYMBOL_REF_DECL when |
| replacing memory addresses with an anchor plus offset. We |
| could find the decl by rummaging around in the block->objects |
| VEC for the given offset but that seems like too much work. */ |
| dalign = BITS_PER_UNIT; |
| if (SYMBOL_REF_HAS_BLOCK_INFO_P (op) |
| && SYMBOL_REF_ANCHOR_P (op) |
| && SYMBOL_REF_BLOCK (op) != NULL) |
| { |
| struct object_block *block = SYMBOL_REF_BLOCK (op); |
| |
| dalign = block->alignment; |
| offset += SYMBOL_REF_BLOCK_OFFSET (op); |
| } |
| else if (CONSTANT_POOL_ADDRESS_P (op)) |
| { |
| /* It would be nice to have get_pool_align().. */ |
| machine_mode cmode = get_pool_mode (op); |
| |
| dalign = GET_MODE_ALIGNMENT (cmode); |
| } |
| } |
| else if (DECL_P (decl)) |
| { |
| dalign = DECL_ALIGN (decl); |
| |
| if (dsize == 0) |
| { |
| /* Allow BLKmode when the entire object is known to not |
| cross a 32k boundary. */ |
| if (!DECL_SIZE_UNIT (decl)) |
| return false; |
| |
| if (!tree_fits_uhwi_p (DECL_SIZE_UNIT (decl))) |
| return false; |
| |
| dsize = tree_to_uhwi (DECL_SIZE_UNIT (decl)); |
| if (dsize > 32768) |
| return false; |
| |
| return dalign / BITS_PER_UNIT >= dsize; |
| } |
| } |
| else |
| { |
| type = TREE_TYPE (decl); |
| |
| dalign = TYPE_ALIGN (type); |
| if (CONSTANT_CLASS_P (decl)) |
| dalign = CONSTANT_ALIGNMENT (decl, dalign); |
| else |
| dalign = DATA_ALIGNMENT (decl, dalign); |
| |
| if (dsize == 0) |
| { |
| /* BLKmode, check the entire object. */ |
| if (TREE_CODE (decl) == STRING_CST) |
| dsize = TREE_STRING_LENGTH (decl); |
| else if (TYPE_SIZE_UNIT (type) |
| && tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))) |
| dsize = tree_to_uhwi (TYPE_SIZE_UNIT (type)); |
| else |
| return false; |
| if (dsize > 32768) |
| return false; |
| |
| return dalign / BITS_PER_UNIT >= dsize; |
| } |
| } |
| |
| /* Find how many bits of the alignment we know for this access. */ |
| mask = dalign / BITS_PER_UNIT - 1; |
| lsb = offset & -offset; |
| mask &= lsb - 1; |
| dalign = mask + 1; |
| |
| return dalign >= dsize; |
| } |
| |
| static bool |
| constant_pool_expr_p (rtx op) |
| { |
| rtx base, offset; |
| |
| split_const (op, &base, &offset); |
| return (GET_CODE (base) == SYMBOL_REF |
| && CONSTANT_POOL_ADDRESS_P (base) |
| && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (base), Pmode)); |
| } |
| |
| static const_rtx tocrel_base, tocrel_offset; |
| |
| /* Return true if OP is a toc pointer relative address (the output |
| of create_TOC_reference). If STRICT, do not match high part or |
| non-split -mcmodel=large/medium toc pointer relative addresses. */ |
| |
| bool |
| toc_relative_expr_p (const_rtx op, bool strict) |
| { |
| if (!TARGET_TOC) |
| return false; |
| |
| if (TARGET_CMODEL != CMODEL_SMALL) |
| { |
| /* Only match the low part. */ |
| if (GET_CODE (op) == LO_SUM |
| && REG_P (XEXP (op, 0)) |
| && INT_REG_OK_FOR_BASE_P (XEXP (op, 0), strict)) |
| op = XEXP (op, 1); |
| else if (strict) |
| return false; |
| } |
| |
| tocrel_base = op; |
| tocrel_offset = const0_rtx; |
| if (GET_CODE (op) == PLUS && add_cint_operand (XEXP (op, 1), GET_MODE (op))) |
| { |
| tocrel_base = XEXP (op, 0); |
| tocrel_offset = XEXP (op, 1); |
| } |
| |
| return (GET_CODE (tocrel_base) == UNSPEC |
| && XINT (tocrel_base, 1) == UNSPEC_TOCREL); |
| } |
| |
| /* Return true if X is a constant pool address, and also for cmodel=medium |
| if X is a toc-relative address known to be offsettable within MODE. */ |
| |
| bool |
| legitimate_constant_pool_address_p (const_rtx x, machine_mode mode, |
| bool strict) |
| { |
| return (toc_relative_expr_p (x, strict) |
| && (TARGET_CMODEL != CMODEL_MEDIUM |
| || constant_pool_expr_p (XVECEXP (tocrel_base, 0, 0)) |
| || mode == QImode |
| || offsettable_ok_by_alignment (XVECEXP (tocrel_base, 0, 0), |
| INTVAL (tocrel_offset), mode))); |
| } |
| |
| static bool |
| legitimate_small_data_p (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 (machine_mode mode, rtx x, |
| bool strict, bool worst_case) |
| { |
| unsigned HOST_WIDE_INT offset; |
| unsigned int extra; |
| |
| if (GET_CODE (x) != PLUS) |
| return false; |
| if (!REG_P (XEXP (x, 0))) |
| return false; |
| if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict)) |
| return false; |
| if (!reg_offset_addressing_ok_p (mode)) |
| return virtual_stack_registers_memory_p (x); |
| if (legitimate_constant_pool_address_p (x, mode, strict || lra_in_progress)) |
| return true; |
| if (GET_CODE (XEXP (x, 1)) != CONST_INT) |
| return false; |
| |
| offset = INTVAL (XEXP (x, 1)); |
| extra = 0; |
| switch (mode) |
| { |
| case V4HImode: |
| case V2SImode: |
| case V1DImode: |
| case V2SFmode: |
| /* SPE vector modes. */ |
| return SPE_CONST_OFFSET_OK (offset); |
| |
| case DFmode: |
| case DDmode: |
| 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 we are using VSX scalar loads, restrict ourselves to reg+reg |
| addressing. */ |
| if (VECTOR_MEM_VSX_P (mode)) |
| return false; |
| |
| if (!worst_case) |
| break; |
| if (!TARGET_POWERPC64) |
| extra = 4; |
| else if (offset & 3) |
| return false; |
| break; |
| |
| case TFmode: |
| if (TARGET_E500_DOUBLE) |
| return (SPE_CONST_OFFSET_OK (offset) |
| && SPE_CONST_OFFSET_OK (offset + 8)); |
| /* fall through */ |
| |
| case TDmode: |
| case TImode: |
| case PTImode: |
| extra = 8; |
| if (!worst_case) |
| break; |
| if (!TARGET_POWERPC64) |
| extra = 12; |
| else if (offset & 3) |
| return false; |
| break; |
| |
| default: |
| break; |
| } |
| |
| offset += 0x8000; |
| return offset < 0x10000 - extra; |
| } |
| |
| 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)))); |
| } |
| |
| bool |
| avoiding_indexed_address_p (machine_mode mode) |
| { |
| /* Avoid indexed addressing for modes that have non-indexed |
| load/store instruction forms. */ |
| return (TARGET_AVOID_XFORM && VECTOR_MEM_NONE_P (mode)); |
| } |
| |
| 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, 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 (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 && GET_MODE_SIZE (mode) > UNITS_PER_WORD) |
| return false; |
| x = XEXP (x, 1); |
| |
| if (TARGET_ELF || TARGET_MACHO) |
| { |
| bool large_toc_ok; |
| |
| if (DEFAULT_ABI == ABI_V4 && flag_pic) |
| return false; |
| /* LRA don't use LEGITIMIZE_RELOAD_ADDRESS as it usually calls |
| push_reload from reload pass code. LEGITIMIZE_RELOAD_ADDRESS |
| recognizes some LO_SUM addresses as valid although this |
| function says opposite. In most cases, LRA through different |
| transformations can generate correct code for address reloads. |
| It can not manage only some LO_SUM cases. So we need to add |
| code analogous to one in rs6000_legitimize_reload_address for |
| LOW_SUM here saying that some addresses are still valid. */ |
| large_toc_ok = (lra_in_progress && TARGET_CMODEL != CMODEL_SMALL |
| && small_toc_ref (x, VOIDmode)); |
| if (TARGET_TOC && ! large_toc_ok) |
| return false; |
| if (GET_MODE_NUNITS (mode) != 1) |
| return false; |
| if (GET_MODE_SIZE (mode) > UNITS_PER_WORD |
| && !(/* ??? Assume floating point reg based on mode? */ |
| TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT |
| && (mode == DFmode || mode == DDmode))) |
| return false; |
| |
| return CONSTANT_P (x) || large_toc_ok; |
| } |
| |
| 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. |
| |
| 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. */ |
| |
| static rtx |
| rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, |
| machine_mode mode) |
| { |
| unsigned int extra; |
| |
| if (!reg_offset_addressing_ok_p (mode)) |
| { |
| if (virtual_stack_registers_memory_p (x)) |
| return x; |
| |
| /* In theory we should not be seeing addresses of the form reg+0, |
| but just in case it is generated, optimize it away. */ |
| if (GET_CODE (x) == PLUS && XEXP (x, 1) == const0_rtx) |
| return force_reg (Pmode, XEXP (x, 0)); |
| |
| /* For TImode with load/store quad, restrict addresses to just a single |
| pointer, so it works with both GPRs and VSX registers. */ |
| /* Make sure both operands are registers. */ |
| else if (GET_CODE (x) == PLUS |
| && (mode != TImode || !TARGET_QUAD_MEMORY)) |
| return gen_rtx_PLUS (Pmode, |
| force_reg (Pmode, XEXP (x, 0)), |
| force_reg (Pmode, XEXP (x, 1))); |
| else |
| return force_reg (Pmode, x); |
| } |
| 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); |
| } |
| |
| extra = 0; |
| switch (mode) |
| { |
| case TFmode: |
| case TDmode: |
| case TImode: |
| case PTImode: |
| /* As in legitimate_offset_address_p we do not assume |
| worst-case. The mode here is just a hint as to the registers |
| used. A TImode is usually in gprs, but may actually be in |
| fprs. Leave worst-case scenario for reload to handle via |
| insn constraints. PTImode is only GPRs. */ |
| extra = 8; |
| break; |
| default: |
| break; |
| } |
| |
| 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 - extra) |
| && !(SPE_VECTOR_MODE (mode) |
| || (TARGET_E500_DOUBLE && GET_MODE_SIZE (mode) > UNITS_PER_WORD))) |
| { |
| HOST_WIDE_INT high_int, low_int; |
| rtx sum; |
| low_int = ((INTVAL (XEXP (x, 1)) & 0xffff) ^ 0x8000) - 0x8000; |
| if (low_int >= 0x8000 - extra) |
| low_int = 0; |
| high_int = INTVAL (XEXP (x, 1)) - low_int; |
| sum = force_operand (gen_rtx_PLUS (Pmode, XEXP (x, 0), |
| GEN_INT (high_int)), 0); |
| return plus_constant (Pmode, sum, low_int); |
| } |
| else if (GET_CODE (x) == PLUS |
| && GET_CODE (XEXP (x, 0)) == REG |
| && GET_CODE (XEXP (x, 1)) != CONST_INT |
| && GET_MODE_NUNITS (mode) == 1 |
| && (GET_MODE_SIZE (mode) <= UNITS_PER_WORD |
| || (/* ??? Assume floating point reg based on mode? */ |
| (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) |
| && (mode == DFmode || mode == DDmode))) |
| && !avoiding_indexed_address_p (mode)) |
| { |
| return gen_rtx_PLUS (Pmode, XEXP (x, 0), |
| force_reg (Pmode, force_operand (XEXP (x, 1), 0))); |
| } |
| else if (SPE_VECTOR_MODE (mode) |
| || (TARGET_E500_DOUBLE && GET_MODE_SIZE (mode) > UNITS_PER_WORD)) |
| { |
| if (mode == DImode) |
| return x; |
| /* We accept [reg + reg] and [reg + OFFSET]. */ |
| |
| if (GET_CODE (x) == PLUS) |
| { |
| rtx op1 = XEXP (x, 0); |
| rtx op2 = XEXP (x, 1); |
| rtx y; |
| |
| op1 = force_reg (Pmode, op1); |
| |
| if (GET_CODE (op2) != REG |
| && (GET_CODE (op2) != CONST_INT |
| || !SPE_CONST_OFFSET_OK (INTVAL (op2)) |
| || (GET_MODE_SIZE (mode) > 8 |
| && !SPE_CONST_OFFSET_OK (INTVAL (op2) + 8)))) |
| op2 = force_reg (Pmode, op2); |
| |
| /* We can't always do [reg + reg] for these, because [reg + |
| reg + offset] is not a legitimate addressing mode. */ |
| y = gen_rtx_PLUS (Pmode, op1, op2); |
| |
| if ((GET_MODE_SIZE (mode) > 8 || mode == DDmode) && REG_P (op2)) |
| return force_reg (Pmode, y); |
| else |
| return y; |
| } |
| |
| return force_reg (Pmode, x); |
| } |
| else if ((TARGET_ELF |
| #if TARGET_MACHO |
| || !MACHO_DYNAMIC_NO_PIC_P |
| #endif |
| ) |
| && TARGET_32BIT |
| && TARGET_NO_TOC |
| && ! flag_pic |
| && GET_CODE (x) != CONST_INT |
| && GET_CODE (x) != CONST_WIDE_INT |
| && GET_CODE (x) != CONST_DOUBLE |
| && CONSTANT_P (x) |
| && GET_MODE_NUNITS (mode) == 1 |
| && (GET_MODE_SIZE (mode) <= UNITS_PER_WORD |
| || (/* ??? Assume floating point reg based on mode? */ |
| (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) |
| && (mode == DFmode || mode == DDmode)))) |
| { |
| rtx reg = gen_reg_rtx (Pmode); |
| if (TARGET_ELF) |
| emit_insn (gen_elf_high (reg, x)); |
| else |
| emit_insn (gen_macho_high (reg, x)); |
| return gen_rtx_LO_SUM (Pmode, reg, x); |
| } |
| else if (TARGET_TOC |
| && GET_CODE (x) == SYMBOL_REF |
| && constant_pool_expr_p (x) |
| && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), Pmode)) |
| return create_TOC_reference (x, NULL_RTX); |
| else |
| return x; |
| } |
| |
| /* Debug version of rs6000_legitimize_address. */ |
| static rtx |
| rs6000_debug_legitimize_address (rtx x, rtx oldx, machine_mode mode) |
| { |
| rtx ret; |
| rtx_insn *insns; |
| |
| start_sequence (); |
| ret = rs6000_legitimize_address (x, oldx, mode); |
| insns = get_insns (); |
| end_sequence (); |
| |
| if (ret != x) |
| { |
| fprintf (stderr, |
| "\nrs6000_legitimize_address: mode %s, old code %s, " |
| "new code %s, modified\n", |
| GET_MODE_NAME (mode), GET_RTX_NAME (GET_CODE (x)), |
| GET_RTX_NAME (GET_CODE (ret))); |
| |
| fprintf (stderr, "Original address:\n"); |
| debug_rtx (x); |
| |
| fprintf (stderr, "oldx:\n"); |
| debug_rtx (oldx); |
| |
| fprintf (stderr, "New address:\n"); |
| debug_rtx (ret); |
| |
| if (insns) |
| { |
| fprintf (stderr, "Insns added:\n"); |
| debug_rtx_list (insns, 20); |
| } |
| } |
| else |
| { |
| fprintf (stderr, |
| "\nrs6000_legitimize_address: mode %s, code %s, no change:\n", |
| GET_MODE_NAME (mode), GET_RTX_NAME (GET_CODE (x))); |
| |
| debug_rtx (x); |
| } |
| |
| if (insns) |
| emit_insn (insns); |
| |
| return ret; |
| } |
| |
| /* 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 *, int, rtx) ATTRIBUTE_UNUSED; |
| 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); |
| } |
| |
| /* Return true if X is a symbol that refers to real (rather than emulated) |
| TLS. */ |
| |
| static bool |
| rs6000_real_tls_symbol_ref_p (rtx x) |
| { |
| return (GET_CODE (x) == SYMBOL_REF |
| && SYMBOL_REF_TLS_MODEL (x) >= TLS_MODEL_REAL); |
| } |
| |
| /* In the name of slightly smaller debug output, and to cater to |
| general assembler lossage, recognize various UNSPEC sequences |
| and turn them back into a direct symbol reference. */ |
| |
| static rtx |
| rs6000_delegitimize_address (rtx orig_x) |
| { |
| rtx x, y, offset; |
| |
| orig_x = delegitimize_mem_from_attrs (orig_x); |
| x = orig_x; |
| if (MEM_P (x)) |
| x = XEXP (x, 0); |
| |
| y = x; |
| if (TARGET_CMODEL != CMODEL_SMALL |
| && GET_CODE (y) == LO_SUM) |
| y = XEXP (y, 1); |
| |
| offset = NULL_RTX; |
| if (GET_CODE (y) == PLUS |
| && GET_MODE (y) == Pmode |
| && CONST_INT_P (XEXP (y, 1))) |
| { |
| offset = XEXP (y, 1); |
| y = XEXP (y, 0); |
| } |
| |
| if (GET_CODE (y) == UNSPEC |
| && XINT (y, 1) == UNSPEC_TOCREL) |
| { |
| y = XVECEXP (y, 0, 0); |
| |
| #ifdef HAVE_AS_TLS |
| /* Do not associate thread-local symbols with the original |
| constant pool symbol. */ |
| if (TARGET_XCOFF |
| && GET_CODE (y) == SYMBOL_REF |
| && CONSTANT_POOL_ADDRESS_P (y) |
| && rs6000_real_tls_symbol_ref_p (get_pool_constant (y))) |
| return orig_x; |
| #endif |
| |
| if (offset != NULL_RTX) |
| y = gen_rtx_PLUS (Pmode, y, offset); |
| if (!MEM_P (orig_x)) |
| return y; |
| else |
| return replace_equiv_address_nv (orig_x, y); |
| } |
| |
| if (TARGET_MACHO |
| && GET_CODE (orig_x) == LO_SUM |
| && GET_CODE (XEXP (orig_x, 1)) == CONST) |
| { |
| y = XEXP (XEXP (orig_x, 1), 0); |
| if (GET_CODE (y) == UNSPEC |
| && XINT (y, 1) == UNSPEC_MACHOPIC_OFFSET) |
| return XVECEXP (y, 0, 0); |
| } |
| |
| return orig_x; |
| } |
| |
| /* Return true if X shouldn't be emitted into the debug info. |
| The linker doesn't like .toc section references from |
| .debug_* sections, so reject .toc section symbols. */ |
| |
| static bool |
| rs6000_const_not_ok_for_debug_p (rtx x) |
| { |
| if (GET_CODE (x) == SYMBOL_REF |
| && CONSTANT_POOL_ADDRESS_P (x)) |
| { |
| rtx c = get_pool_constant (x); |
| machine_mode cmode = get_pool_mode (x); |
| if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (c, cmode)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* 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; |
| } |
| |
| /* AIX Thread-Local Address support. */ |
| |
| static rtx |
| rs6000_legitimize_tls_address_aix (rtx addr, enum tls_model model) |
| { |
| rtx sym, mem, tocref, tlsreg, tmpreg, dest, tlsaddr; |
| const char *name; |
| char *tlsname; |
| |
| name = XSTR (addr, 0); |
| /* Append TLS CSECT qualifier, unless the symbol already is qualified |
| or the symbol will be in TLS private data section. */ |
| if (name[strlen (name) - 1] != ']' |
| && (TREE_PUBLIC (SYMBOL_REF_DECL (addr)) |
| || bss_initializer_p (SYMBOL_REF_DECL (addr)))) |
| { |
| tlsname = XALLOCAVEC (char, strlen (name) + 4); |
| strcpy (tlsname, name); |
| strcat (tlsname, |
| bss_initializer_p (SYMBOL_REF_DECL (addr)) ? "[UL]" : "[TL]"); |
| tlsaddr = copy_rtx (addr); |
| XSTR (tlsaddr, 0) = ggc_strdup (tlsname); |
| } |
| else |
| tlsaddr = addr; |
| |
| /* Place addr into TOC constant pool. */ |
| sym = force_const_mem (GET_MODE (tlsaddr), tlsaddr); |
| |
| /* Output the TOC entry and create the MEM referencing the value. */ |
| if (constant_pool_expr_p (XEXP (sym, 0)) |
| && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (XEXP (sym, 0)), Pmode)) |
| { |
| tocref = create_TOC_reference (XEXP (sym, 0), NULL_RTX); |
| mem = gen_const_mem (Pmode, tocref); |
| set_mem_alias_set (mem, get_TOC_alias_set ()); |
| } |
| else |
| return sym; |
| |
| /* Use global-dynamic for local-dynamic. */ |
| if (model == TLS_MODEL_GLOBAL_DYNAMIC |
| || model == TLS_MODEL_LOCAL_DYNAMIC) |
| { |
| /* Create new TOC reference for @m symbol. */ |
| name = XSTR (XVECEXP (XEXP (mem, 0), 0, 0), 0); |
| tlsname = XALLOCAVEC (char, strlen (name) + 1); |
| strcpy (tlsname, "*LCM"); |
| strcat (tlsname, name + 3); |
| rtx modaddr = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (tlsname)); |
| SYMBOL_REF_FLAGS (modaddr) |= SYMBOL_FLAG_LOCAL; |
| tocref = create_TOC_reference (modaddr, NULL_RTX); |
| rtx modmem = gen_const_mem (Pmode, tocref); |
| set_mem_alias_set (modmem, get_TOC_alias_set ()); |
| |
| rtx modreg = gen_reg_rtx (Pmode); |
| emit_insn (gen_rtx_SET (VOIDmode, modreg, modmem)); |
| |
| tmpreg = gen_reg_rtx (Pmode); |
| emit_insn (gen_rtx_SET (VOIDmode, tmpreg, mem)); |
| |
| dest = gen_reg_rtx (Pmode); |
| if (TARGET_32BIT) |
| emit_insn (gen_tls_get_addrsi (dest, modreg, tmpreg)); |
| else |
| emit_insn (gen_tls_get_addrdi (dest, modreg, tmpreg)); |
| return dest; |
| } |
| /* Obtain TLS pointer: 32 bit call or 64 bit GPR 13. */ |
| else if (TARGET_32BIT) |
| { |
| tlsreg = gen_reg_rtx (SImode); |
| emit_insn (gen_tls_get_tpointer (tlsreg)); |
| } |
| else |
| tlsreg = gen_rtx_REG (DImode, 13); |
| |
| /* Load the TOC value into temporary register. */ |
| tmpreg = gen_reg_rtx (Pmode); |
| emit_insn (gen_rtx_SET (VOIDmode, tmpreg, mem)); |
| set_unique_reg_note (get_last_insn (), REG_EQUAL, |
| gen_rtx_MINUS (Pmode, addr, tlsreg)); |
| |
| /* Add TOC symbol value to TLS pointer. */ |
| dest = force_reg (Pmode, gen_rtx_PLUS (Pmode, tmpreg, tlsreg)); |
| |
| return dest; |
| } |
| |
| /* 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; |
| |
| if (TARGET_XCOFF) |
| return rs6000_legitimize_tls_address_aix (addr, model); |
| |
| 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, call_insn; |
| |
| /* 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 mem, lab, last; |
| |
| tmp1 = gen_reg_rtx (Pmode); |
| tmp2 = gen_reg_rtx (Pmode); |
| mem = gen_const_mem (Pmode, tmp1); |
| lab = gen_label_rtx (); |
| emit_insn (gen_load_toc_v4_PIC_1b (gsym, lab)); |
| emit_move_insn (tmp1, gen_rtx_REG (Pmode, LR_REGNO)); |
| if (TARGET_LINK_STACK) |
| emit_insn (gen_addsi3 (tmp1, tmp1, GEN_INT (4))); |
| emit_move_insn (tmp2, mem); |
| last = emit_insn (gen_addsi3 (got, tmp1, tmp2)); |
| set_unique_reg_note (last, REG_EQUAL, gsym); |
| } |
| } |
| } |
| |
| if (model == TLS_MODEL_GLOBAL_DYNAMIC) |
| { |
| tga = rs6000_tls_get_addr (); |
| emit_library_call_value (tga, dest, LCT_CONST, Pmode, |
| 1, const0_rtx, Pmode); |
| |
| r3 = gen_rtx_REG (Pmode, 3); |
| if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) |
| { |
| if (TARGET_64BIT) |
| insn = gen_tls_gd_aix64 (r3, got, addr, tga, const0_rtx); |
| else |
| insn = gen_tls_gd_aix32 (r3, got, addr, tga, const0_rtx); |
| } |
| else if (DEFAULT_ABI == ABI_V4) |
| insn = gen_tls_gd_sysvsi (r3, got, addr, tga, const0_rtx); |
| else |
| gcc_unreachable (); |
| call_insn = last_call_insn (); |
| PATTERN (call_insn) = insn; |
| if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic) |
| use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), |
| pic_offset_table_rtx); |
| } |
| else if (model == TLS_MODEL_LOCAL_DYNAMIC) |
| { |
| tga = rs6000_tls_get_addr (); |
| tmp1 = gen_reg_rtx (Pmode); |
| emit_library_call_value (tga, tmp1, LCT_CONST, Pmode, |
| 1, const0_rtx, Pmode); |
| |
| r3 = gen_rtx_REG (Pmode, 3); |
| if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) |
| { |
| if (TARGET_64BIT) |
| insn = gen_tls_ld_aix64 (r3, got, tga, const0_rtx); |
| else |
| insn = gen_tls_ld_aix32 (r3, got, tga, const0_rtx); |
| } |
| else if (DEFAULT_ABI == ABI_V4) |
| insn = gen_tls_ld_sysvsi (r3, got, tga, const0_rtx); |
| else |
| gcc_unreachable (); |
| call_insn = last_call_insn (); |
| PATTERN (call_insn) = insn; |
| if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic) |
| use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), |
| pic_offset_table_rtx); |
| |
| 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; |
| } |
| |
| /* Implement TARGET_CANNOT_FORCE_CONST_MEM. */ |
| |
| static bool |
| rs6000_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, rtx x) |
| { |
| if (GET_CODE (x) == HIGH |
| && GET_CODE (XEXP (x, 0)) == UNSPEC) |
| return true; |
| |
| /* A TLS symbol in the TOC cannot contain a sum. */ |
| if (GET_CODE (x) == CONST |
| && GET_CODE (XEXP (x, 0)) == PLUS |
| && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF |
| && SYMBOL_REF_TLS_MODEL (XEXP (XEXP (x, 0), 0)) != 0) |
| return true; |
| |
| /* Do not place an ELF TLS symbol in the constant pool. */ |
| return TARGET_ELF && tls_referenced_p (x); |
| } |
| |
| /* Return true iff the given SYMBOL_REF refers to a constant pool entry |
| that we have put in the TOC, or for cmodel=medium, if the SYMBOL_REF |
| can be addressed relative to the toc pointer. */ |
| |
| static bool |
| use_toc_relative_ref (rtx sym) |
| { |
| return ((constant_pool_expr_p (sym) |
| && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (sym), |
| get_pool_mode (sym))) |
| || (TARGET_CMODEL == CMODEL_MEDIUM |
| && SYMBOL_REF_LOCAL_P (sym))); |
| } |
| |
| /* Our implementation of LEGITIMIZE_RELOAD_ADDRESS. Returns a value to |
| replace the input X, or the original X if no replacement is called for. |
| The output parameter *WIN is 1 if the calling macro should goto WIN, |
| 0 if it should not. |
| |
| For RS/6000, we wish to handle large displacements off a base |
| register by splitting the addend across an addiu/addis and the mem insn. |
| This cuts number of extra insns needed from 3 to 1. |
| |
| On Darwin, we use this to generate code for floating point constants. |
| A movsf_low is generated so we wind up with 2 instructions rather than 3. |
| The Darwin code is inside #if TARGET_MACHO because only then are the |
| machopic_* functions defined. */ |
| static rtx |
| rs6000_legitimize_reload_address (rtx x, machine_mode mode, |
| int opnum, int type, |
| int ind_levels ATTRIBUTE_UNUSED, int *win) |
| { |
| bool reg_offset_p = reg_offset_addressing_ok_p (mode); |
| |
| /* Nasty hack for vsx_splat_V2DF/V2DI load from mem, which takes a |
| DFmode/DImode MEM. */ |
| if (reg_offset_p |
| && opnum == 1 |
| && ((mode == DFmode && recog_data.operand_mode[0] == V2DFmode) |
| || (mode == DImode && recog_data.operand_mode[0] == V2DImode))) |
| reg_offset_p = false; |
| |
| /* We must recognize output that we have already generated ourselves. */ |
| if (GET_CODE (x) == PLUS |
| && GET_CODE (XEXP (x, 0)) == PLUS |
| && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG |
| && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT |
| && GET_CODE (XEXP (x, 1)) == CONST_INT) |
| { |
| push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, |
| BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0, |
| opnum, (enum reload_type) type); |
| *win = 1; |
| return x; |
| } |
| |
| /* Likewise for (lo_sum (high ...) ...) output we have generated. */ |
| if (GET_CODE (x) == LO_SUM |
| && GET_CODE (XEXP (x, 0)) == HIGH) |
| { |
| push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, |
| BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, |
| opnum, (enum reload_type) type); |
| *win = 1; |
| return x; |
| } |
| |
| #if TARGET_MACHO |
| if (DEFAULT_ABI == ABI_DARWIN && flag_pic |
| && GET_CODE (x) == LO_SUM |
| && GET_CODE (XEXP (x, 0)) == PLUS |
| && XEXP (XEXP (x, 0), 0) == pic_offset_table_rtx |
| && GET_CODE (XEXP (XEXP (x, 0), 1)) == HIGH |
| && XEXP (XEXP (XEXP (x, 0), 1), 0) == XEXP (x, 1) |
| && machopic_operand_p (XEXP (x, 1))) |
| { |
| /* Result of previous invocation of this function on Darwin |
| floating point constant. */ |
| push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, |
| BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, |
| opnum, (enum reload_type) type); |
| *win = 1; |
| return x; |
| } |
| #endif |
| |
| if (TARGET_CMODEL != CMODEL_SMALL |
| && reg_offset_p |
| && small_toc_ref (x, VOIDmode)) |
| { |
| rtx hi = gen_rtx_HIGH (Pmode, copy_rtx (x)); |
| x = gen_rtx_LO_SUM (Pmode, hi, x); |
| push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, |
| BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, |
| opnum, (enum reload_type) type); |
| *win = 1; |
| return x; |
| } |
| |
| if (GET_CODE (x) == PLUS |
| && GET_CODE (XEXP (x, 0)) == REG |
| && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER |
| && INT_REG_OK_FOR_BASE_P (XEXP (x, 0), 1) |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && reg_offset_p |
| && !SPE_VECTOR_MODE (mode) |
| && !(TARGET_E500_DOUBLE && GET_MODE_SIZE (mode) > UNITS_PER_WORD) |
| && (!VECTOR_MODE_P (mode) || VECTOR_MEM_NONE_P (mode))) |
| { |
| HOST_WIDE_INT val = INTVAL (XEXP (x, 1)); |
| HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000; |
| HOST_WIDE_INT high |
| = (((val - low) & 0xffffffff) ^ 0x80000000) - 0x80000000; |
| |
| /* Check for 32-bit overflow. */ |
| 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 |
| && reg_offset_p |
| && (!VECTOR_MODE_P (mode) || VECTOR_MEM_NONE_P (mode)) |
| && !SPE_VECTOR_MODE (mode) |
| #if TARGET_MACHO |
| && DEFAULT_ABI == ABI_DARWIN |
| && (flag_pic || MACHO_DYNAMIC_NO_PIC_P) |
| && machopic_symbol_defined_p (x) |
| #else |
| && DEFAULT_ABI == ABI_V4 |
| && !flag_pic |
| #endif |
| /* Don't do this for TFmode or TDmode, since the result isn't offsettable. |
| The same goes for DImode without 64-bit gprs and DFmode and DDmode |
| without fprs. |
| ??? Assume floating point reg based on mode? This assumption is |
| violated by eg. powerpc-linux -m32 compile of gcc.dg/pr28796-2.c |
| where reload ends up doing a DFmode load of a constant from |
| mem using two gprs. Unfortunately, at this point reload |
| hasn't yet selected regs so poking around in reload data |
| won't help and even if we could figure out the regs reliably, |
| we'd still want to allow this transformation when the mem is |
| naturally aligned. Since we say the address is good here, we |
| can't disable offsets from LO_SUMs in mem_operand_gpr. |
| FIXME: Allow offset from lo_sum for other modes too, when |
| mem is sufficiently aligned. |
| |
| Also disallow this if the type can go in VMX/Altivec registers, since |
| those registers do not have d-form (reg+offset) address modes. */ |
| && !reg_addr[mode].scalar_in_vmx_p |
| && mode != TFmode |
| && mode != TDmode |
| && (mode != TImode || !TARGET_VSX_TIMODE) |
| && mode != PTImode |
| && (mode != DImode || TARGET_POWERPC64) |
| && ((mode != DFmode && mode != DDmode) || TARGET_POWERPC64 |
| || (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT))) |
| { |
| #if TARGET_MACHO |
| if (flag_pic) |
| { |
| rtx offset = machopic_gen_offset (x); |
| x = gen_rtx_LO_SUM (GET_MODE (x), |
| gen_rtx_PLUS (Pmode, pic_offset_table_rtx, |
| gen_rtx_HIGH (Pmode, offset)), offset); |
| } |
| else |
| #endif |
| x = gen_rtx_LO_SUM (GET_MODE (x), |
| gen_rtx_HIGH (Pmode, x), x); |
| |
| push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, |
| BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, |
| opnum, (enum reload_type) type); |
| *win = 1; |
| return x; |
| } |
| |
| /* Reload an offset address wrapped by an AND that represents the |
| masking of the lower bits. Strip the outer AND and let reload |
| convert the offset address into an indirect address. For VSX, |
| force reload to create the address with an AND in a separate |
| register, because we can't guarantee an altivec register will |
| be used. */ |
| if (VECTOR_MEM_ALTIVEC_P (mode) |
| && GET_CODE (x) == AND |
| && GET_CODE (XEXP (x, 0)) == PLUS |
| && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG |
| && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && INTVAL (XEXP (x, 1)) == -16) |
| { |
| x = XEXP (x, 0); |
| *win = 1; |
| return x; |
| } |
| |
| if (TARGET_TOC |
| && reg_offset_p |
| && GET_CODE (x) == SYMBOL_REF |
| && use_toc_relative_ref (x)) |
| { |
| x = create_TOC_reference (x, NULL_RTX); |
| if (TARGET_CMODEL != CMODEL_SMALL) |
| push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL, |
| BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, |
| opnum, (enum reload_type) type); |
| *win = 1; |
| return x; |
| } |
| *win = 0; |
| return x; |
| } |
| |
| /* Debug version of rs6000_legitimize_reload_address. */ |
| static rtx |
| rs6000_debug_legitimize_reload_address (rtx x, machine_mode mode, |
| int opnum, int type, |
| int ind_levels, int *win) |
| { |
| rtx ret = rs6000_legitimize_reload_address (x, mode, opnum, type, |
| ind_levels, win); |
| fprintf (stderr, |
| "\nrs6000_legitimize_reload_address: mode = %s, opnum = %d, " |
| "type = %d, ind_levels = %d, win = %d, original addr:\n", |
| GET_MODE_NAME (mode), opnum, type, ind_levels, *win); |
| debug_rtx (x); |
| |
| if (x == ret) |
| fprintf (stderr, "Same address returned\n"); |
| else if (!ret) |
| fprintf (stderr, "NULL returned\n"); |
| else |
| { |
| fprintf (stderr, "New address:\n"); |
| debug_rtx (ret); |
| } |
| |
| return ret; |
| } |
| |
| /* TARGET_LEGITIMATE_ADDRESS_P recognizes an RTL expression |
| that is a valid memory address for an instruction. |
| The MODE argument is the machine mode for the MEM expression |
| 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, DDmode 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 and DDmode in 32-bit GPRs, |
| 32-bit DImode, TImode, TFmode, TDmode), indexed addressing cannot be used |
| because adjacent memory cells are accessed by adding word-sized offsets |
| during assembly output. */ |
| static bool |
| rs6000_legitimate_address_p (machine_mode mode, rtx x, bool reg_ok_strict) |
| { |
| bool reg_offset_p = reg_offset_addressing_ok_p (mode); |
| |
| /* If this is an unaligned stvx/ldvx type address, discard the outer AND. */ |
| if (VECTOR_MEM_ALTIVEC_P (mode) |
| && GET_CODE (x) == AND |
| && GET_CODE (XEXP (x, 1)) == CONST_INT |
| && INTVAL (XEXP (x, 1)) == -16) |
| x = XEXP (x, 0); |
| |
| if (TARGET_ELF && RS6000_SYMBOL_REF_TLS_P (x)) |
| return 0; |
| if (legitimate_indirect_address_p (x, reg_ok_strict)) |
| return 1; |
| if (TARGET_UPDATE |
| && (GET_CODE (x) == PRE_INC || GET_CODE (x) == PRE_DEC) |
| && mode_supports_pre_incdec_p (mode) |
| && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict)) |
| return 1; |
| if (virtual_stack_registers_memory_p (x)) |
| return 1; |
| if (reg_offset_p && legitimate_small_data_p (mode, x)) |
| return 1; |
| if (reg_offset_p |
| && legitimate_constant_pool_address_p (x, mode, |
| reg_ok_strict || lra_in_progress)) |
| return 1; |
| /* For TImode, if we have load/store quad and TImode in VSX registers, only |
| allow register indirect addresses. This will allow the values to go in |
| either GPRs or VSX registers without reloading. The vector types would |
| tend to go into VSX registers, so we allow REG+REG, while TImode seems |
| somewhat split, in that some uses are GPR based, and some VSX based. */ |
| if (mode == TImode && TARGET_QUAD_MEMORY && TARGET_VSX_TIMODE) |
| return 0; |
| /* If not REG_OK_STRICT (before reload) let pass any stack offset. */ |
| if (! reg_ok_strict |
| && reg_offset_p |
| && 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, false)) |
| return 1; |
| if (mode != TFmode |
| && mode != TDmode |
| && ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) |
| || TARGET_POWERPC64 |
| || (mode != DFmode && mode != DDmode) |
| || (TARGET_E500_DOUBLE && mode != DDmode)) |
| && (TARGET_POWERPC64 || mode != DImode) |
| && (mode != TImode || VECTOR_MEM_VSX_P (TImode)) |
| && mode != PTImode |
| && !avoiding_indexed_address_p (mode) |
| && legitimate_indexed_address_p (x, reg_ok_strict)) |
| return 1; |
| if (TARGET_UPDATE && GET_CODE (x) == PRE_MODIFY |
| && mode_supports_pre_modify_p (mode) |
| && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict) |
| && (rs6000_legitimate_offset_address_p (mode, XEXP (x, 1), |
| reg_ok_strict, false) |
| || (!avoiding_indexed_address_p (mode) |
| && legitimate_indexed_address_p (XEXP (x, 1), reg_ok_strict))) |
| && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0))) |
| return 1; |
| if (reg_offset_p && legitimate_lo_sum_address_p (mode, x, reg_ok_strict)) |
| return 1; |
| return 0; |
| } |
| |
| /* Debug version of rs6000_legitimate_address_p. */ |
| static bool |
| rs6000_debug_legitimate_address_p (machine_mode mode, rtx x, |
| bool reg_ok_strict) |
| { |
| bool ret = rs6000_legitimate_address_p (mode, x, reg_ok_strict); |
| fprintf (stderr, |
| "\nrs6000_legitimate_address_p: return = %s, mode = %s, " |
| "strict = %d, reload = %s, code = %s\n", |
| ret ? "true" : "false", |
| GET_MODE_NAME (mode), |
| reg_ok_strict, |
| (reload_completed |
| ? "after" |
| : (reload_in_progress ? "progress" : "before")), |
| GET_RTX_NAME (GET_CODE (x))); |
| debug_rtx (x); |
| |
| return ret; |
| } |
| |
| /* Implement TARGET_MODE_DEPENDENT_ADDRESS_P. */ |
| |
| static bool |
| rs6000_mode_dependent_address_p (const_rtx addr, |
| addr_space_t as ATTRIBUTE_UNUSED) |
| { |
| return rs6000_mode_dependent_address_ptr (addr); |
| } |
| |
| /* 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 |
| and VSX 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. */ |
| |
| static bool |
| rs6000_mode_dependent_address (const_rtx addr) |
| { |
| switch (GET_CODE (addr)) |
| { |
| case PLUS: |
| /* Any offset from virtual_stack_vars_rtx and arg_pointer_rtx |
| is considered a legitimate address before reload, so there |
| are no offset restrictions in that case. Note that this |
| condition is safe in strict mode because any address involving |
| virtual_stack_vars_rtx or arg_pointer_rtx would already have |
| been rejected as illegitimate. */ |
| if (XEXP (addr, 0) != virtual_stack_vars_rtx |
| && XEXP (addr, 0) != arg_pointer_rtx |
| && GET_CODE (XEXP (addr, 1)) == CONST_INT) |
| { |
| unsigned HOST_WIDE_INT val = INTVAL (XEXP (addr, 1)); |
| return val + 0x8000 >= 0x10000 - (TARGET_POWERPC64 ? 8 : 12); |
| } |
| break; |
| |
| case LO_SUM: |
| /* Anything in the constant pool is sufficiently aligned that |
| all bytes have the same high part address. */ |
| return !legitimate_constant_pool_address_p (addr, QImode, false); |
| |
| /* Auto-increment cases are now treated generically in recog.c. */ |
| case PRE_MODIFY: |
| return TARGET_UPDATE; |
| |
| /* AND is only allowed in Altivec loads. */ |
| case AND: |
| return true; |
| |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* Debug version of rs6000_mode_dependent_address. */ |
| static bool |
| rs6000_debug_mode_dependent_address (const_rtx addr) |
| { |
| bool ret = rs6000_mode_dependent_address (addr); |
| |
| fprintf (stderr, "\nrs6000_mode_dependent_address: ret = %s\n", |
| ret ? "true" : "false"); |
| debug_rtx (addr); |
| |
| return ret; |
| } |
| |
| /* Implement FIND_BASE_TERM. */ |
| |
| rtx |
| rs6000_find_base_term (rtx op) |
| { |
| rtx base; |
| |
| base = op; |
| if (GET_CODE (base) == CONST) |
| base = XEXP (base, 0); |
| if (GET_CODE (base) == PLUS) |
| base = XEXP (base, 0); |
| if (GET_CODE (base) == UNSPEC) |
| switch (XINT (base, 1)) |
| { |
| case UNSPEC_TOCREL: |
| case UNSPEC_MACHOPIC_OFFSET: |
| /* OP represents SYM [+ OFFSET] - ANCHOR. SYM is the base term |
| for aliasing purposes. */ |
| return XVECEXP (base, 0, 0); |
| } |
| |
| return op; |
| } |
| |
| /* 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. */ |
| |
| static bool |
| rs6000_offsettable_memref_p (rtx op, machine_mode reg_mode) |
| { |
| bool worst_case; |
| |
| if (!MEM_P (op)) |
| return false; |
| |
| /* First mimic offsettable_memref_p. */ |
| if (offsettable_address_p (true, 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, |
| at least with a little bit of help here given that we know the |
| actual registers used. */ |
| worst_case = ((TARGET_POWERPC64 && GET_MODE_CLASS (reg_mode) == MODE_INT) |
| || GET_MODE_SIZE (reg_mode) == 4); |
| return rs6000_legitimate_offset_address_p (GET_MODE (op), XEXP (op, 0), |
| true, worst_case); |
| } |
| |
| /* Change register usage conditional on target flags. */ |
| static void |
| rs6000_conditional_register_usage (void) |
| { |
| int i; |
| |
| if (TARGET_DEBUG_TARGET) |
| fprintf (stderr, "rs6000_conditional_register_usage called\n"); |
| |
| /* Set MQ register fixed (already call_used) so that it will not be |
| allocated. */ |
| fixed_regs[64] = 1; |
| |
| /* 64-bit AIX and Linux reserve GPR13 for thread-private data. */ |
| if (TARGET_64BIT) |
| fixed_regs[13] = call_used_regs[13] |
| = 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 || DEFAULT_ABI == ABI_ELFv2) |
| 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_SPE) |
| { |
| global_regs[SPEFSCR_REGNO] = 1; |
| /* We used to use r14 as FIXED_SCRATCH to address SPE 64-bit |
| registers in prologues and epilogues. We no longer use r14 |
| for FIXED_SCRATCH, but we're keeping r14 out of the allocation |
| pool for link-compatibility with older versions of GCC. Once |
| "old" code has died out, we can return r14 to the allocation |
| pool. */ |
| fixed_regs[14] |
| = call_used_regs[14] |
| = call_really_used_regs[14] = 1; |
| } |
| |
| if (!TARGET_ALTIVEC && !TARGET_VSX) |
| { |
| 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 || TARGET_VSX) |
| global_regs[VSCR_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; |
| |
| /* AIX reserves VR20:31 in non-extended ABI mode. */ |
| if (TARGET_XCOFF) |
| for (i = FIRST_ALTIVEC_REGNO + 20; i < FIRST_ALTIVEC_REGNO + 32; ++i) |
| fixed_regs[i] = call_used_regs[i] = call_really_used_regs[i] = 1; |
| } |
| } |
| |
| |
| /* Output insns to set DEST equal to the constant SOURCE as a series of |
| lis, ori and shl instructions and return TRUE. */ |
| |
| bool |
| rs6000_emit_set_const (rtx dest, rtx source) |
| { |
| machine_mode mode = GET_MODE (dest); |
| rtx temp, set; |
| rtx_insn *insn; |
| HOST_WIDE_INT c; |
| |
| gcc_checking_assert (CONST_INT_P (source)); |
| c = INTVAL (source); |
| switch (mode) |
| { |
| case QImode: |
| case HImode: |
| emit_insn (gen_rtx_SET (VOIDmode, dest, source)); |
| return true; |
| |
| case SImode: |
| temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (SImode); |
| |
| emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (temp), |
| GEN_INT (c & ~(HOST_WIDE_INT) 0xffff))); |
| emit_insn (gen_rtx_SET (VOIDmode, dest, |
| gen_rtx_IOR (SImode, copy_rtx (temp), |
| GEN_INT (c & 0xffff)))); |
| break; |
| |
| case DImode: |
| if (!TARGET_POWERPC64) |
| { |
| rtx hi, lo; |
| |
| hi = operand_subword_force (copy_rtx (dest), WORDS_BIG_ENDIAN == 0, |
| DImode); |
| lo = operand_subword_force (dest, WORDS_BIG_ENDIAN != 0, |
| DImode); |
| emit_move_insn (hi, GEN_INT (c >> 32)); |
| c = ((c & 0xffffffff) ^ 0x80000000) - 0x80000000; |
| emit_move_insn (lo, GEN_INT (c)); |
| } |
| else |
| rs6000_emit_set_long_const (dest, c); |
| 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, GEN_INT (c)); |
| |
| return true; |
| } |
| |
| /* Subroutine of rs6000_emit_set_const, handling PowerPC64 DImode. |
| Output insns to set DEST equal to the constant C as a series of |
| lis, ori and shl instructions. */ |
| |
| static void |
| rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c) |
| { |
| rtx temp; |
| HOST_WIDE_INT ud1, ud2, ud3, ud4; |
| |
| ud1 = c & 0xffff; |
| c = c >> 16; |
| ud2 = c & 0xffff; |
| c = c >> 16; |
| ud3 = c & 0xffff; |
| c = c >> 16; |
| ud4 = c & 0xffff; |
| |
| if ((ud4 == 0xffff && ud3 == 0xffff && ud2 == 0xffff && (ud1 & 0x8000)) |
| || (ud4 == 0 && ud3 == 0 && ud2 == 0 && ! (ud1 & 0x8000))) |
| emit_move_insn (dest, GEN_INT ((ud1 ^ 0x8000) - 0x8000)); |
| |
| else if ((ud4 == 0xffff && ud3 == 0xffff && (ud2 & 0x8000)) |
| || (ud4 == 0 && ud3 == 0 && ! (ud2 & 0x8000))) |
| { |
| temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode); |
| |
| emit_move_insn (ud1 != 0 ? copy_rtx (temp) : dest, |
| GEN_INT (((ud2 << 16) ^ 0x80000000) - 0x80000000)); |
| if (ud1 != 0) |
| emit_move_insn (dest, |
| gen_rtx_IOR (DImode, copy_rtx (temp), |
| GEN_INT (ud1))); |
| } |
| else if (ud3 == 0 && ud4 == 0) |
| { |
| temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode); |
| |
| gcc_assert (ud2 & 0x8000); |
| emit_move_insn (copy_rtx (temp), |
| GEN_INT (((ud2 << 16) ^ 0x80000000) - 0x80000000)); |
| if (ud1 != 0) |
| emit_move_insn (copy_rtx (temp), |
| gen_rtx_IOR (DImode, copy_rtx (temp), |
| GEN_INT (ud1))); |
| emit_move_insn (dest, |
| gen_rtx_ZERO_EXTEND (DImode, |
| gen_lowpart (SImode, |
| copy_rtx (temp)))); |
| } |
| else if ((ud4 == 0xffff && (ud3 & 0x8000)) |
| || (ud4 == 0 && ! (ud3 & 0x8000))) |
| { |
| temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode); |
| |
| emit_move_insn (copy_rtx (temp), |
| GEN_INT (((ud3 << 16) ^ 0x80000000) - 0x80000000)); |
| if (ud2 != 0) |
| emit_move_insn (copy_rtx (temp), |
| gen_rtx_IOR (DImode, copy_rtx (temp), |
| GEN_INT (ud2))); |
| emit_move_insn (ud1 != 0 ? copy_rtx (temp) : dest, |
| gen_rtx_ASHIFT (DImode, copy_rtx (temp), |
| GEN_INT (16))); |
| if (ud1 != 0) |
| emit_move_insn (dest, |
| gen_rtx_IOR (DImode, copy_rtx (temp), |
| GEN_INT (ud1))); |
| } |
| else |
| { |
| temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode); |
| |
| emit_move_insn (copy_rtx (temp), |
| GEN_INT (((ud4 << 16) ^ 0x80000000) - 0x80000000)); |
| if (ud3 != 0) |
| emit_move_insn (copy_rtx (temp), |
| gen_rtx_IOR (DImode, copy_rtx (temp), |
| GEN_INT (ud3))); |
| |
| emit_move_insn (ud2 != 0 || ud1 != 0 ? copy_rtx (temp) : dest, |
| gen_rtx_ASHIFT (DImode, copy_rtx (temp), |
| GEN_INT (32))); |
| if (ud2 != 0) |
| emit_move_insn (ud1 != 0 ? copy_rtx (temp) : dest, |
| gen_rtx_IOR (DImode, copy_rtx (temp), |
| GEN_INT (ud2 << 16))); |
| if (ud1 != 0) |
| emit_move_insn (dest, |
| gen_rtx_IOR (DImode, copy_rtx (temp), |
| GEN_INT (ud1))); |
| } |
| } |
| |
| /* Helper for the following. Get rid of [r+r] memory refs |
| in cases where it won't work (TImode, TFmode, TDmode, PTImode). */ |
| |
| static void |
| rs6000_eliminate_indexed_memrefs (rtx operands[2]) |
| { |
| if (reload_in_progress) |
| return; |
| |
| if (GET_CODE (operands[0]) == MEM |
| && GET_CODE (XEXP (operands[0], 0)) != REG |
| && ! legitimate_constant_pool_address_p (XEXP (operands[0], 0), |
| GET_MODE (operands[0]), false)) |
| operands[0] |
| = replace_equiv_address (operands[0], |
| copy_addr_to_reg (XEXP (operands[0], 0))); |
| |
| if (GET_CODE (operands[1]) == MEM |
| && GET_CODE (XEXP (operands[1], 0)) != REG |
| && ! legitimate_constant_pool_address_p (XEXP (operands[1], 0), |
| GET_MODE (operands[1]), false)) |
| operands[1] |
| = replace_equiv_address (operands[1], |
| copy_addr_to_reg (XEXP (operands[1], 0))); |
| } |
| |
| /* Generate a vector of constants to permute MODE for a little-endian |
| storage operation by swapping the two halves of a vector. */ |
| static rtvec |
| rs6000_const_vec (machine_mode mode) |
| { |
| int i, subparts; |
| rtvec v; |
| |
| switch (mode) |
| { |
| case V1TImode: |
| subparts = 1; |
| break; |
| case V2DFmode: |
| case V2DImode: |
| subparts = 2; |
| break; |
| case V4SFmode: |
| case V4SImode: |
| subparts = 4; |
| break; |
| case V8HImode: |
| subparts = 8; |
| break; |
| case V16QImode: |
| subparts = 16; |
| break; |
| default: |
| gcc_unreachable(); |
| } |
| |
| v = rtvec_alloc (subparts); |
| |
| for (i = 0; i < subparts / 2; ++i) |
| RTVEC_ELT (v, i) = gen_rtx_CONST_INT (DImode, i + subparts / 2); |
| for (i = subparts / 2; i < subparts; ++i) |
| RTVEC_ELT (v, i) = gen_rtx_CONST_INT (DImode, i - subparts / 2); |
| |
| return v; |
| } |
| |
| /* Generate a permute rtx that represents an lxvd2x, stxvd2x, or xxpermdi |
| for a VSX load or store operation. */ |
| rtx |
| rs6000_gen_le_vsx_permute (rtx source, machine_mode mode) |
| { |
| rtx par = gen_rtx_PARALLEL (VOIDmode, rs6000_const_vec (mode)); |
| return gen_rtx_VEC_SELECT (mode, source, par); |
| } |
| |
| /* Emit a little-endian load from vector memory location SOURCE to VSX |
| register DEST in mode MODE. The load is done with two permuting |
| insn's that represent an lxvd2x and xxpermdi. */ |
| void |
| rs6000_emit_le_vsx_load (rtx dest, rtx source, machine_mode mode) |
| { |
| rtx tmp, permute_mem, permute_reg; |
| |
| /* Use V2DImode to do swaps of types with 128-bit scalare parts (TImode, |
| V1TImode). */ |
| if (mode == TImode || mode == V1TImode) |
| { |
| mode = V2DImode; |
| dest = gen_lowpart (V2DImode, dest); |
| source = adjust_address (source, V2DImode, 0); |
| } |
| |
| tmp = can_create_pseudo_p () ? gen_reg_rtx_and_attrs (dest) : dest; |
| permute_mem = rs6000_gen_le_vsx_permute (source, mode); |
| permute_reg = rs6000_gen_le_vsx_permute (tmp, mode); |
| emit_insn (gen_rtx_SET (VOIDmode, tmp, permute_mem)); |
| emit_insn (gen_rtx_SET (VOIDmode, dest, permute_reg)); |
| } |
| |
| /* Emit a little-endian store to vector memory location DEST from VSX |
| register SOURCE in mode MODE. The store is done with two permuting |
| insn's that represent an xxpermdi and an stxvd2x. */ |
| void |
| rs6000_emit_le_vsx_store (rtx dest, rtx source, machine_mode mode) |
| { |
| rtx tmp, permute_src, permute_tmp; |
| |
| /* This should never be called during or after reload, because it does |
| not re-permute the source register. It is intended only for use |
| during expand. */ |
| gcc_assert (!reload_in_progress && !lra_in_progress && !reload_completed); |
| |
| /* Use V2DImode to do swaps of types with 128-bit scalare parts (TImode, |
| V1TImode). */ |
| if (mode == TImode || mode == V1TImode) |
| { |
| mode = V2DImode; |
| dest = adjust_address (dest, V2DImode, 0); |
| source = gen_lowpart (V2DImode, source); |
| } |
| |
| tmp = can_create_pseudo_p () ? gen_reg_rtx_and_attrs (source) : source; |
| permute_src = rs6000_gen_le_vsx_permute (source, mode); |
| permute_tmp = rs6000_gen_le_vsx_permute (tmp, mode); |
| emit_insn (gen_rtx_SET (VOIDmode, tmp, permute_src)); |
| emit_insn (gen_rtx_SET (VOIDmode, dest, permute_tmp)); |
| } |
| |
| /* Emit a sequence representing a little-endian VSX load or store, |
| moving data from SOURCE to DEST in mode MODE. This is done |
| separately from rs6000_emit_move to ensure it is called only |
| during expand. LE VSX loads and stores introduced later are |
| handled with a split. The expand-time RTL generation allows |
| us to optimize away redundant pairs of register-permutes. */ |
| void |
| rs6000_emit_le_vsx_move (rtx dest, rtx source, machine_mode mode) |
| { |
| gcc_assert (!BYTES_BIG_ENDIAN |
| && VECTOR_MEM_VSX_P (mode) |
| && !gpr_or_gpr_p (dest, source) |
| && (MEM_P (source) ^ MEM_P (dest))); |
| |
| if (MEM_P (source)) |
| { |
| gcc_assert (REG_P (dest) || GET_CODE (dest) == SUBREG); |
| rs6000_emit_le_vsx_load (dest, source, mode); |
| } |
| else |
| { |
| if (!REG_P (source)) |
| source = force_reg (mode, source); |
| rs6000_emit_le_vsx_store (dest, source, mode); |
| } |
| } |
| |
| /* Emit a move from SOURCE to DEST in mode MODE. */ |
| void |
| rs6000_emit_move (rtx dest, rtx source, machine_mode mode) |
| { |
| rtx operands[2]; |
| operands[0] = dest; |
| operands[1] = source; |
| |
| if (TARGET_DEBUG_ADDR) |
| { |
| fprintf (stderr, |
| "\nrs6000_emit_move: mode = %s, reload_in_progress = %d, " |
| "reload_completed = %d, can_create_pseudos = %d.\ndest:\n", |
| GET_MODE_NAME (mode), |
| reload_in_progress, |
| reload_completed, |
| can_create_pseudo_p ()); |
| debug_rtx (dest); |
| fprintf (stderr, "source:\n"); |
| debug_rtx (source); |
| } |
| |
| /* Sanity checks. Check that we get CONST_DOUBLE only when we should. */ |
| if (CONST_WIDE_INT_P (operands[1]) |
| && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) |
| { |
| /* This should be fixed with the introduction of CONST_WIDE_INT. */ |
| gcc_unreachable (); |
| } |
| |
| /* 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 (copy_rtx (operands[0]), SImode, 4), |
| adjust_address (copy_rtx (operands[1]), SImode, 4)); |
| return; |
| } |
| |
| if (can_create_pseudo_p () && GET_CODE (operands[0]) == MEM |
| && !gpc_reg_operand (operands[1], mode)) |
| operands[1] = force_reg (mode, operands[1]); |
| |
| /* Recognize the case where operand[1] is a reference to thread-local |
| data and load its address to a register. */ |
| if (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. However, this premature splitting is a problem when DFmode |
| values can go into Altivec registers. */ |
| if (!TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128 |
| && !reg_addr[DFmode].scalar_in_vmx_p |
| && mode == TFmode && GET_CODE (operands[1]) == CONST_DOUBLE) |
| { |
| rs6000_emit_move (simplify_gen_subreg (DFmode, operands[0], mode, 0), |
| simplify_gen_subreg (DFmode, operands[1], mode, 0), |
| DFmode); |
| rs6000_emit_move (simplify_gen_subreg (DFmode, operands[0], mode, |
| GET_MODE_SIZE (DFmode)), |
| simplify_gen_subreg (DFmode, operands[1], mode, |
| GET_MODE_SIZE (DFmode)), |
| DFmode); |
| return; |
| } |
| |
| if (reload_in_progress && cfun->machine->sdmode_stack_slot != NULL_RTX) |
| cfun->machine->sdmode_stack_slot = |
| eliminate_regs (cfun->machine->sdmode_stack_slot, VOIDmode, NULL_RTX); |
| |
| |
| /* Transform (p0:DD, (SUBREG:DD p1:SD)) to ((SUBREG:SD p0:DD), |
| p1:SD) if p1 is not of floating point class and p0 is spilled as |
| we can have no analogous movsd_store for this. */ |
| if (lra_in_progress && mode == DDmode |
| && REG_P (operands[0]) && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER |
| && reg_preferred_class (REGNO (operands[0])) == NO_REGS |
| && GET_CODE (operands[1]) == SUBREG && REG_P (SUBREG_REG (operands[1])) |
| && GET_MODE (SUBREG_REG (operands[1])) == SDmode) |
| { |
| enum reg_class cl; |
| int regno = REGNO (SUBREG_REG (operands[1])); |
| |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| { |
| cl = reg_preferred_class (regno); |
| regno = cl == NO_REGS ? -1 : ira_class_hard_regs[cl][1]; |
| } |
| if (regno >= 0 && ! FP_REGNO_P (regno)) |
| { |
| mode = SDmode; |
| operands[0] = gen_lowpart_SUBREG (SDmode, operands[0]); |
| operands[1] = SUBREG_REG (operands[1]); |
| } |
| } |
| if (lra_in_progress |
| && mode == SDmode |
| && REG_P (operands[0]) && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER |
| && reg_preferred_class (REGNO (operands[0])) == NO_REGS |
| && (REG_P (operands[1]) |
| || (GET_CODE (operands[1]) == SUBREG |
| && REG_P (SUBREG_REG (operands[1]))))) |
| { |
| int regno = REGNO (GET_CODE (operands[1]) == SUBREG |
| ? SUBREG_REG (operands[1]) : operands[1]); |
| enum reg_class cl; |
| |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| { |
| cl = reg_preferred_class (regno); |
| gcc_assert (cl != NO_REGS); |
| regno = ira_class_hard_regs[cl][0]; |
| } |
| if (FP_REGNO_P (regno)) |
| { |
| if (GET_MODE (operands[0]) != DDmode) |
| operands[0] = gen_rtx_SUBREG (DDmode, operands[0], 0); |
| emit_insn (gen_movsd_store (operands[0], operands[1])); |
| } |
| else if (INT_REGNO_P (regno)) |
| emit_insn (gen_movsd_hardfloat (operands[0], operands[1])); |
| else |
| gcc_unreachable(); |
| return; |
| } |
| /* Transform ((SUBREG:DD p0:SD), p1:DD) to (p0:SD, (SUBREG:SD |
| p:DD)) if p0 is not of floating point class and p1 is spilled as |
| we can have no analogous movsd_load for this. */ |
| if (lra_in_progress && mode == DDmode |
| && GET_CODE (operands[0]) == SUBREG && REG_P (SUBREG_REG (operands[0])) |
| && GET_MODE (SUBREG_REG (operands[0])) == SDmode |
| && REG_P (operands[1]) && REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER |
| && reg_preferred_class (REGNO (operands[1])) == NO_REGS) |
| { |
| enum reg_class cl; |
| int regno = REGNO (SUBREG_REG (operands[0])); |
| |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| { |
| cl = reg_preferred_class (regno); |
| regno = cl == NO_REGS ? -1 : ira_class_hard_regs[cl][0]; |
| } |
| if (regno >= 0 && ! FP_REGNO_P (regno)) |
| { |
| mode = SDmode; |
| operands[0] = SUBREG_REG (operands[0]); |
| operands[1] = gen_lowpart_SUBREG (SDmode, operands[1]); |
| } |
| } |
| if (lra_in_progress |
| && mode == SDmode |
| && (REG_P (operands[0]) |
| || (GET_CODE (operands[0]) == SUBREG |
| && REG_P (SUBREG_REG (operands[0])))) |
| && REG_P (operands[1]) && REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER |
| && reg_preferred_class (REGNO (operands[1])) == NO_REGS) |
| { |
| int regno = REGNO (GET_CODE (operands[0]) == SUBREG |
| ? SUBREG_REG (operands[0]) : operands[0]); |
| enum reg_class cl; |
| |
| if (regno >= FIRST_PSEUDO_REGISTER) |
| { |
| cl = reg_preferred_class (regno); |
| gcc_assert (cl != NO_REGS); |
| regno = ira_class_hard_regs[cl][0]; |
| } |
| if (FP_REGNO_P (regno)) |
| { |
| if (GET_MODE (operands[1]) != DDmode) |
| operands[1] = gen_rtx_SUBREG (DDmode, operands[1], 0); |
| emit_insn (gen_movsd_load (operands[0], operands[1])); |
| } |
| else if (INT_REGNO_P (regno)) |
| emit_insn (gen_movsd_hardfloat (operands[0], operands[1])); |
| else |
| gcc_unreachable(); |
| return; |
| } |
| |
| if (reload_in_progress |
| && mode == SDmode |
| && cfun->machine->sdmode_stack_slot != NULL_RTX |
| && MEM_P (operands[0]) |
| && rtx_equal_p (operands[0], cfun->machine->sdmode_stack_slot) |
| && REG_P (operands[1])) |
| { |
| if (FP_REGNO_P (REGNO (operands[1]))) |
| { |
| rtx mem = adjust_address_nv (operands[0], DDmode, 0); |
| mem = eliminate_regs (mem, VOIDmode, NULL_RTX); |
| emit_insn (gen_movsd_store (mem, operands[1])); |
| } |
| else if (INT_REGNO_P (REGNO (operands[1]))) |
| { |
| rtx mem = operands[0]; |
| if (BYTES_BIG_ENDIAN) |
| mem = adjust_address_nv (mem, mode, 4); |
| mem = eliminate_regs (mem, VOIDmode, NULL_RTX); |
| emit_insn (gen_movsd_hardfloat (mem, operands[1])); |
| } |
| else |
| gcc_unreachable(); |
| return; |
| } |
| if (reload_in_progress |
| && mode == SDmode |
| && REG_P (operands[0]) |
| && MEM_P (operands[1]) |
| && cfun->machine->sdmode_stack_slot != NULL_RTX |
| && rtx_equal_p (operands[1], cfun->machine->sdmode_stack_slot)) |
| { |
| if (FP_REGNO_P (REGNO (operands[0]))) |
| { |
| rtx mem = adjust_address_nv (operands[1], DDmode, 0); |
| mem = eliminate_regs (mem, VOIDmode, NULL_RTX); |
| emit_insn (gen_movsd_load (operands[0], mem)); |
| } |
| else if (INT_REGNO_P (REGNO (operands[0]))) |
| { |
| rtx mem = operands[1]; |
| if (BYTES_BIG_ENDIAN) |
| mem = adjust_address_nv (mem, mode, 4); |
| mem = eliminate_regs (mem, VOIDmode, NULL_RTX); |
| emit_insn (gen_movsd_hardfloat (operands[0], mem)); |
| } |
| else |
| gcc_unreachable(); |
| return; |
| } |
| |
| /* FIXME: In the long term, this switch statement should go away |
| and be replaced by a sequence of tests based on things like |
| mode == Pmode. */ |
| switch (mode) |
| { |
| case HImode: |
| case QImode: |
| if (CONSTANT_P (operands[1]) |
| && GET_CODE (operands[1]) != CONST_INT) |
| operands[1] = force_const_mem (mode, operands[1]); |
| break; |
| |
| case TFmode: |
| case TDmode: |
| rs6000_eliminate_indexed_memrefs (operands); |
| /* fall through */ |
| |
| case DFmode: |
| case DDmode: |
| case SFmode: |
| case SDmode: |
| 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: |
| case V2DFmode: |
| case V2DImode: |
| case V1TImode: |
| 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 = (!can_create_pseudo_p () |
| ? 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 |
| && use_toc_relative_ref (operands[1])) |
| operands[1] = create_TOC_reference (operands[1], operands[0]); |
| else if (mode == Pmode |
| && CONSTANT_P (operands[1]) |
| && GET_CODE (operands[1]) != HIGH |
| && ((GET_CODE (operands[1]) != CONST_INT |
| && ! easy_fp_constant (operands[1], mode)) |
| || (GET_CODE (operands[1]) == CONST_INT |
| && (num_insns_constant (operands[1], mode) |
| > (TARGET_CMODEL != CMODEL_SMALL ? 3 : 2))) |
| || (GET_CODE (operands[0]) == REG |
| && FP_REGNO_P (REGNO (operands[0])))) |
| && !toc_relative_expr_p (operands[1], false) |
| && (TARGET_CMODEL == CMODEL_SMALL |
| || can_create_pseudo_p () |
| || (REG_P (operands[0]) |
| && INT_REG_OK_FOR_BASE_P (operands[0], true)))) |
| { |
| |
| #if TARGET_MACHO |
| /* Darwin uses a special PIC legitimizer. */ |
| if (DEFAULT_ABI == ABI_DARWIN && MACHOPIC_INDIRECT) |
| { |
| operands[1] = |
| rs6000_machopic_legitimize_pic_address (operands[1], mode, |
| operands[0]); |
| if (operands[0] != operands[1]) |
| emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1])); |
| return; |
| } |
| #endif |
| |
| /* If we are to limit the number of things we put in the TOC and |
| this is a symbol plus a constant we can add in one insn, |
| just put the symbol in the TOC and add the constant. Don't do |
| this if reload is in progress. */ |
| if (GET_CODE (operands[1]) == CONST |
| && TARGET_NO_SUM_IN_TOC && ! reload_in_progress |
| && GET_CODE (XEXP (operands[1], 0)) == PLUS |
| && add_operand (XEXP (XEXP (operands[1], 0), 1), mode) |
| && (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == LABEL_REF |
| || GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == SYMBOL_REF) |
| && ! side_effects_p (operands[0])) |
| { |
| rtx sym = |
| force_const_mem (mode, XEXP (XEXP (operands[1], 0), 0)); |
| rtx other = XEXP (XEXP (operands[1], 0), 1); |
| |
| sym = force_reg (mode, sym); |
| emit_insn (gen_add3_insn (operands[0], sym, other)); |
| return; |
| } |
| |
| operands[1] = force_const_mem (mode, operands[1]); |
| |
| if (TARGET_TOC |
| && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF |
| && 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)))) |
| { |
| rtx tocref = create_TOC_reference (XEXP (operands[1], 0), |
| operands[0]); |
| operands[1] = gen_const_mem (mode, tocref); |
| set_mem_alias_set (operands[1], get_TOC_alias_set ()); |
| } |
| } |
| break; |
| |
| case TImode: |
| if (!VECTOR_MEM_VSX_P (TImode)) |
| rs6000_eliminate_indexed_memrefs (operands); |
| break; |
| |
| case PTImode: |
| rs6000_eliminate_indexed_memrefs (operands); |
| break; |
| |
| default: |
| fatal_insn ("bad move", gen_rtx_SET (VOIDmode, dest, source)); |
| } |
| |
| /* 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])); |
| } |
| |
| /* Return true if a structure, union or array containing FIELD should be |
| accessed using `BLKMODE'. |
| |
| For the SPE, simd types are V2SI, and gcc can be tempted to put the |
| entire thing in a DI and use subregs to access the internals. |
| store_bit_field() will force (subreg:DI (reg:V2SI x))'s to the |
| back-end. Because a single GPR can hold a V2SI, but not a DI, the |
| best thing to do is set structs to BLKmode and avoid Severe Tire |
| Damage. |
| |
| On e500 v2, DF and DI modes suffer from the same anomaly. DF can |
| fit into 1, whereas DI still needs two. */ |
| |
| static bool |
| rs6000_member_type_forces_blk (const_tree field, machine_mode mode) |
| { |
| return ((TARGET_SPE && TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE) |
| || (TARGET_E500_DOUBLE && mode == DFmode)); |
| } |
| |
| /* Nonzero if we can use a floating-point register to pass this arg. */ |
| #define USE_FP_FOR_ARG_P(CUM,MODE) \ |
| (SCALAR_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,NAMED) \ |
| (ALTIVEC_OR_VSX_VECTOR_MODE (MODE) \ |
| && (CUM)->vregno <= ALTIVEC_ARG_MAX_REG \ |
| && TARGET_ALTIVEC_ABI \ |
| && (NAMED)) |
| |
| /* Walk down the type tree of TYPE counting consecutive base elements. |
| If *MODEP is VOIDmode, then set it to the first valid floating point |
| or vector type. If a non-floating point or vector type is found, or |
| if a floating point or vector type that doesn't match a non-VOIDmode |
| *MODEP is found, then return -1, otherwise return the count in the |
| sub-tree. */ |
| |
| static int |
| rs6000_aggregate_candidate (const_tree type, machine_mode *modep) |
| { |
| machine_mode mode; |
| HOST_WIDE_INT size; |
| |
| switch (TREE_CODE (type)) |
| { |
| case REAL_TYPE: |
| mode = TYPE_MODE (type); |
| if (!SCALAR_FLOAT_MODE_P (mode)) |
| return -1; |
| |
| if (*modep == VOIDmode) |
| *modep = mode; |
| |
| if (*modep == mode) |
| return 1; |
| |
| break; |
| |
| case COMPLEX_TYPE: |
| mode = TYPE_MODE (TREE_TYPE (type)); |
| if (!SCALAR_FLOAT_MODE_P (mode)) |
| return -1; |
| |
| if (*modep == VOIDmode) |
| *modep = mode; |
| |
| if (*modep == mode) |
| return 2; |
| |
| break; |
| |
| case VECTOR_TYPE: |
| if (!TARGET_ALTIVEC_ABI || !TARGET_ALTIVEC) |
| return -1; |
| |
| /* Use V4SImode as representative of all 128-bit vector types. */ |
| size = int_size_in_bytes (type); |
| switch (size) |
| { |
| case 16: |
| mode = V4SImode; |
| break; |
| default: |
| return -1; |
| } |
| |
| if (*modep == VOIDmode) |
| *modep = mode; |
| |
| /* Vector modes are considered to be opaque: two vectors are |
| equivalent for the purposes of being homogeneous aggregates |
| if they are the same size. */ |
| if (*modep == mode) |
| return 1; |
| |
| break; |
| |
| case ARRAY_TYPE: |
| { |
| int count; |
| tree index = TYPE_DOMAIN (type); |
| |
| /* Can't handle incomplete types nor sizes that are not |
| fixed. */ |
| if (!COMPLETE_TYPE_P (type) |
| || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) |
| return -1; |
| |
| count = rs6000_aggregate_candidate (TREE_TYPE (type), modep); |
| if (count == -1 |
| || !index |
| || !TYPE_MAX_VALUE (index) |
| || !tree_fits_uhwi_p (TYPE_MAX_VALUE (index)) |
| || !TYPE_MIN_VALUE (index) |
| || !tree_fits_uhwi_p (TYPE_MIN_VALUE (index)) |
| || count < 0) |
| return -1; |
| |
| count *= (1 + tree_to_uhwi (TYPE_MAX_VALUE (index)) |
| - tree_to_uhwi (TYPE_MIN_VALUE (index))); |
| |
| /* There must be no padding. */ |
| if (wi::ne_p (TYPE_SIZE (type), count * GET_MODE_BITSIZE (*modep))) |
| return -1; |
| |
| return count; |
| } |
| |
| case RECORD_TYPE: |
| { |
| int count = 0; |
| int sub_count; |
| tree field; |
| |
| /* Can't handle incomplete types nor sizes that are not |
| fixed. */ |
| if (!COMPLETE_TYPE_P (type) |
| || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) |
| return -1; |
| |
| for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) |
| { |
| if (TREE_CODE (field) != FIELD_DECL) |
| continue; |
| |
| sub_count = rs6000_aggregate_candidate (TREE_TYPE (field), modep); |
| if (sub_count < 0) |
| return -1; |
| count += sub_count; |
| } |
| |
| /* There must be no padding. */ |
| if (wi::ne_p (TYPE_SIZE (type), count * GET_MODE_BITSIZE (*modep))) |
| return -1; |
| |
| return count; |
| } |
| |
| case UNION_TYPE: |
| case QUAL_UNION_TYPE: |
| { |
| /* These aren't very interesting except in a degenerate case. */ |
| int count = 0; |
| int sub_count; |
| tree field; |
| |
| /* Can't handle incomplete types nor sizes that are not |
| fixed. */ |
| if (!COMPLETE_TYPE_P (type) |
| || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST) |
| return -1; |
| |
| for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) |
| { |
| if (TREE_CODE (field) != FIELD_DECL) |
| continue; |
| |
| sub_count = rs6000_aggregate_candidate (TREE_TYPE (field), modep); |
| if (sub_count < 0) |
| return -1; |
| count = count > sub_count ? count : sub_count; |
| } |
| |
| /* There must be no padding. */ |
| if (wi::ne_p (TYPE_SIZE (type), count * GET_MODE_BITSIZE (*modep))) |
| return -1; |
| |
| return count; |
| } |
| |
| default: |
| break; |
| } |
| |
| return -1; |
| } |
| |
| /* If an argument, whose type is described by TYPE and MODE, is a homogeneous |
| float or vector aggregate that shall be passed in FP/vector registers |
| according to the ELFv2 ABI, return the homogeneous element mode in |
| *ELT_MODE and the number of elements in *N_ELTS, and return TRUE. |
| |
| Otherwise, set *ELT_MODE to MODE and *N_ELTS to 1, and return FALSE. */ |
| |
| static bool |
| rs6000_discover_homogeneous_aggregate (machine_mode mode, const_tree type, |
| machine_mode *elt_mode, |
| int *n_elts) |
| { |
| /* Note that we do not accept complex types at the top level as |
| homogeneous aggregates; these types are handled via the |
| targetm.calls.split_complex_arg mechanism. Complex types |
| can be elements of homogeneous aggregates, however. */ |
| if (DEFAULT_ABI == ABI_ELFv2 && type && AGGREGATE_TYPE_P (type)) |
| { |
| machine_mode field_mode = VOIDmode; |
| int field_count = rs6000_aggregate_candidate (type, &field_mode); |
| |
| if (field_count > 0) |
| { |
| int n_regs = (SCALAR_FLOAT_MODE_P (field_mode)? |
| (GET_MODE_SIZE (field_mode) + 7) >> 3 : 1); |
| |
| /* The ELFv2 ABI allows homogeneous aggregates to occupy |
| up to AGGR_ARG_NUM_REG registers. */ |
| if (field_count * n_regs <= AGGR_ARG_NUM_REG) |
| { |
| if (elt_mode) |
| *elt_mode = field_mode; |
| if (n_elts) |
| *n_elts = field_count; |
| return true; |
| } |
| } |
| } |
| |
| if (elt_mode) |
| *elt_mode = mode; |
| if (n_elts) |
| *n_elts = 1; |
| return false; |
| } |
| |
| /* 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. |
| |
| For the Darwin 64 Bit ABI, a function result can be returned in |
| registers or in memory, depending on the size of the return data |
| type. If it is returned in registers, the value occupies the same |
| registers as it would if it were the first and only function |
| argument. Otherwise, the function places its result in memory at |
| the location pointed to by GPR3. |
| |
| 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_option_override_internal 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 (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) |
| { |
| /* For the Darwin64 ABI, test if we can fit the return value in regs. */ |
| if (TARGET_MACHO |
| && 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, true, true); |
| if (valret) |
| return false; |
| /* Otherwise fall through to more conventional ABI rules. */ |
| } |
| |
| /* The ELFv2 ABI returns homogeneous VFP aggregates in registers */ |
| if (rs6000_discover_homogeneous_aggregate (TYPE_MODE (type), type, |
| NULL, NULL)) |
| return false; |
| |
| /* The ELFv2 ABI returns aggregates up to 16B in registers */ |
| if (DEFAULT_ABI == ABI_ELFv2 && AGGREGATE_TYPE_P (type) |
| && (unsigned HOST_WIDE_INT) int_size_in_bytes (type) <= 16) |
| return false; |
| |
| 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; |
| } |
| |
| /* Specify whether values returned in registers should be at the most |
| significant end of a register. We want aggregates returned by |
| value to match the way aggregates are passed to functions. */ |
| |
| static bool |
| rs6000_return_in_msb (const_tree valtype) |
| { |
| return (DEFAULT_ABI == ABI_ELFv2 |
| && BYTES_BIG_ENDIAN |
| && AGGREGATE_TYPE_P (valtype) |
| && FUNCTION_ARG_PADDING (TYPE_MODE (valtype), valtype) == upward); |
| } |
| |
| #ifdef HAVE_AS_GNU_ATTRIBUTE |
| /* Return TRUE if a call to function FNDECL may be one that |
| potentially affects the function calling ABI of the object file. */ |
| |
| static bool |
| call_ABI_of_interest (tree fndecl) |
| { |
| if (symtab->state == EXPANSION) |
| { |
| struct cgraph_node *c_node; |
| |
| /* Libcalls are always interesting. */ |
| if (fndecl == NULL_TREE) |
| return true; |
| |
| /* Any call to an external function is interesting. */ |
| if (DECL_EXTERNAL (fndecl)) |
| return true; |
| |
| /* Interesting functions that we are emitting in this object file. */ |
| c_node = cgraph_node::get (fndecl); |
| c_node = c_node->ultimate_alias_target (); |
| return !c_node->only_called_directly_p (); |
| } |
| return false; |
| } |
| #endif |
| |
| /* 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 and RETURN_MODE the return value mode. |
| |
| 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, |
| tree fndecl ATTRIBUTE_UNUSED, |
| machine_mode return_mode ATTRIBUTE_UNUSED) |
| { |
| 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 && prototype_p (fntype)); |
| cum->call_cookie = ((DEFAULT_ABI == ABI_V4 && libcall) |
| ? CALL_LIBCALL : CALL_NORMAL); |
| cum->sysv_gregno = GP_ARG_MIN_REG; |
| cum->stdarg = stdarg_p (fntype); |
| |
| 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,", |
| get_tree_code_name (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); |
| } |
| |
| #ifdef HAVE_AS_GNU_ATTRIBUTE |
| if (DEFAULT_ABI == ABI_V4) |
| { |
| cum->escapes = call_ABI_of_interest (fndecl); |
| if (cum->escapes) |
| { |
| tree return_type; |
| |
| if (fntype) |
| { |
| return_type = TREE_TYPE (fntype); |
| return_mode = TYPE_MODE (return_type); |
| } |
| else |
| return_type = lang_hooks.types.type_for_mode (return_mode, 0); |
| |
| if (return_type != NULL) |
| { |
| if (TREE_CODE (return_type) == RECORD_TYPE |
| && TYPE_TRANSPARENT_AGGR (return_type)) |
| { |
| return_type = TREE_TYPE (first_field (return_type)); |
| return_mode = TYPE_MODE (return_type); |
| } |
| if (AGGREGATE_TYPE_P (return_type) |
| && ((unsigned HOST_WIDE_INT) int_size_in_bytes (return_type) |
| <= 8)) |
| rs6000_returns_struct = true; |
| } |
| if (SCALAR_FLOAT_MODE_P (return_mode)) |
| rs6000_passes_float = true; |
| else if (ALTIVEC_OR_VSX_VECTOR_MODE (return_mode) |
| || SPE_VECTOR_MODE (return_mode)) |
| rs6000_passes_vector = true; |
| } |
| } |
| #endif |
| |
| 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"); |
| } |
| } |
| |
| /* The mode the ABI uses for a word. This is not the same as word_mode |
| for -m32 -mpowerpc64. This is used to implement various target hooks. */ |
| |
| static machine_mode |
| rs6000_abi_word_mode (void) |
| { |
| return TARGET_32BIT ? SImode : DImode; |
| } |
| |
| /* On rs6000, function arguments are promoted, as are function return |
| values. */ |
| |
| static machine_mode |
| rs6000_promote_function_mode (const_tree type ATTRIBUTE_UNUSED, |
| machine_mode mode, |
| int *punsignedp ATTRIBUTE_UNUSED, |
| const_tree, int) |
| { |
| PROMOTE_MODE (mode, *punsignedp, type); |
| |
| return mode; |
| } |
| |
| /* Return true if TYPE must be passed on the stack and not in registers. */ |
| |
| static bool |
| rs6000_must_pass_in_stack (machine_mode mode, const_tree type) |
| { |
| if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2 || 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 (machine_mode mode, const_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/VSX vectors. |
| Quadword align large synthetic vector types. */ |
| |
| static unsigned int |
| rs6000_function_arg_boundary (machine_mode mode, const_tree type) |
| { |
| machine_mode elt_mode; |
| int n_elts; |
| |
| rs6000_discover_homogeneous_aggregate (mode, type, &elt_mode, &n_elts); |
| |
| if (DEFAULT_ABI == ABI_V4 |
| && (GET_MODE_SIZE (mode) == 8 |
| || (TARGET_HARD_FLOAT |
| && TARGET_FPRS |
| && (mode == TFmode || mode == TDmode)))) |
| 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_OR_VSX_VECTOR_MODE (elt_mode) |
| || (type && TREE_CODE (type) == VECTOR_TYPE |
| && int_size_in_bytes (type) >= 16)) |
| return 128; |
| |
| /* Aggregate types that need > 8 byte alignment are quadword-aligned |
| in the parameter area in the ELFv2 ABI, and in the AIX ABI unless |
| -mcompat-align-parm is used. */ |
| if (((DEFAULT_ABI == ABI_AIX && !rs6000_compat_align_parm) |
| || DEFAULT_ABI == ABI_ELFv2) |
| && type && TYPE_ALIGN (type) > 64) |
| { |
| /* "Aggregate" means any AGGREGATE_TYPE except for single-element |
| or homogeneous float/vector aggregates here. We already handled |
| vector aggregates above, but still need to check for float here. */ |
| bool aggregate_p = (AGGREGATE_TYPE_P (type) |
| && !SCALAR_FLOAT_MODE_P (elt_mode)); |
| |
| /* We used to check for BLKmode instead of the above aggregate type |
| check. Warn when this results in any difference to the ABI. */ |
| if (aggregate_p != (mode == BLKmode)) |
| { |
| static bool warned; |
| if (!warned && warn_psabi) |
| { |
| warned = true; |
| inform (input_location, |
| "the ABI of passing aggregates with %d-byte alignment" |
| " has changed in GCC 5", |
| (int) TYPE_ALIGN (type) / BITS_PER_UNIT); |
| } |
| } |
| |
| if (aggregate_p) |
| return 128; |
| } |
| |
| /* Similar for the Darwin64 ABI. Note that for historical reasons we |
| implement the "aggregate type" check as a BLKmode check here; this |
| means certain aggregate types are in fact not aligned. */ |
| if (TARGET_MACHO && rs6000_darwin64_abi |
| && mode == BLKmode |
| && type && TYPE_ALIGN (type) > 64) |
| return 128; |
| |
| return PARM_BOUNDARY; |
| } |
| |
| /* The offset in words to the start of the parameter save area. */ |
| |
| static unsigned int |
| rs6000_parm_offset (void) |
| { |
| return (DEFAULT_ABI == ABI_V4 ? 2 |
| : DEFAULT_ABI == ABI_ELFv2 ? 4 |
| : 6); |
| } |
| |
| /* 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 (machine_mode mode, const_tree type, |
| unsigned int nwords) |
| { |
| unsigned int align; |
| |
| align = rs6000_function_arg_boundary (mode, type) / PARM_BOUNDARY - 1; |
| return nwords + (-(rs6000_parm_offset () + nwords) & align); |
| } |
| |
| /* Compute the size (in words) of a function argument. */ |
| |
| static unsigned long |
| rs6000_arg_size (machine_mode mode, const_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, |
| HOST_WIDE_INT bitpos, int final) |
| { |
| unsigned int startbit, endbit; |
| int intregs, intoffset; |
| machine_mode mode; |
| |
| /* 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; |
| } |
| |
| if (cum->intoffset == -1) |
| return; |
| |
| intoffset = cum->intoffset; |
| cum->intoffset = -1; |
| 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; |
| /* words should be unsigned. */ |
| if ((unsigned)cum->words < (endbit/BITS_PER_WORD)) |
| { |
| int pad = (endbit/BITS_PER_WORD) - cum->words; |
| cum->words += pad; |
| } |
| } |
| |
| /* 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, |
| const_tree type, |
| HOST_WIDE_INT startbitpos) |
| { |
| tree f; |
| |
| for (f = TYPE_FIELDS (type); f ; f = DECL_CHAIN (f)) |
| if (TREE_CODE (f) == FIELD_DECL) |
| { |
| HOST_WIDE_INT bitpos = startbitpos; |
| tree ftype = TREE_TYPE (f); |
| machine_mode mode; |
| if (ftype == error_mark_node) |
| continue; |
| mode = TYPE_MODE (ftype); |
| |
| if (DECL_SIZE (f) != 0 |
| && tree_fits_uhwi_p (bit_position (f))) |
| 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)) |
| { |
| unsigned n_fpregs = (GET_MODE_SIZE (mode) + 7) >> 3; |
| rs6000_darwin64_record_arg_advance_flush (cum, bitpos, 0); |
| cum->fregno += n_fpregs; |
| /* 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 |
| cum->words += n_fpregs; |
| } |
| else if (USE_ALTIVEC_FOR_ARG_P (cum, mode, 1)) |
| { |
| rs6000_darwin64_record_arg_advance_flush (cum, bitpos, 0); |
| cum->vregno++; |
| cum->words += 2; |
| } |
| else if (cum->intoffset == -1) |
| cum->intoffset = bitpos; |
| } |
| } |
| |
| /* Check for an item that needs to be considered specially under the darwin 64 |
| bit ABI. These are record types where the mode is BLK or the structure is |
| 8 bytes in size. */ |
| static int |
| rs6000_darwin64_struct_check_p (machine_mode mode, const_tree type) |
| { |
| return rs6000_darwin64_abi |
| && ((mode == BLKmode |
| && TREE_CODE (type) == RECORD_TYPE |
| && int_size_in_bytes (type) > 0) |
| || (type && TREE_CODE (type) == RECORD_TYPE |
| && int_size_in_bytes (type) == 8)) ? 1 : 0; |
| } |
| |
| /* 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. */ |
| |
| static void |
| rs6000_function_arg_advance_1 (CUMULATIVE_ARGS *cum, machine_mode mode, |
| const_tree type, bool named, int depth) |
| { |
| machine_mode elt_mode; |
| int n_elts; |
| |
| rs6000_discover_homogeneous_aggregate (mode, type, &elt_mode, &n_elts); |
| |
| /* Only tick off an argument if we're not recursing. */ |
| if (depth == 0) |
| cum->nargs_prototype--; |
| |
| #ifdef HAVE_AS_GNU_ATTRIBUTE |
| if (DEFAULT_ABI == ABI_V4 |
| && cum->escapes) |
| { |
| if (SCALAR_FLOAT_MODE_P (mode)) |
| rs6000_passes_float = true; |
| else if (named && ALTIVEC_OR_VSX_VECTOR_MODE (mode)) |
| rs6000_passes_vector = true; |
| else if (SPE_VECTOR_MODE (mode) |
| && !cum->stdarg |
| && cum->sysv_gregno <= GP_ARG_MAX_REG) |
| rs6000_passes_vector = true; |
| } |
| #endif |
| |
| if (TARGET_ALTIVEC_ABI |
| && (ALTIVEC_OR_VSX_VECTOR_MODE (elt_mode) |
| || (type && TREE_CODE (type) == VECTOR_TYPE |
| && int_size_in_bytes (type) == 16))) |
| { |
| bool stack = false; |
| |
| if (USE_ALTIVEC_FOR_ARG_P (cum, elt_mode, named)) |
| { |
| cum->vregno += n_elts; |
| |
| 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 || DEFAULT_ABI == ABI_ELFv2) |
| && TARGET_64BIT) |
| || (cum->stdarg && DEFAULT_ABI != ABI_V4)) |
| stack = true; |
| } |
| else |
| stack = true; |
| |
| if (stack) |
| { |
| int align; |
| |
| /* Vector parameters must be 16-byte aligned. In 32-bit |
| mode this means we need to take into account the offset |
| to the parameter save area. 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 = -(rs6000_parm_offset () + 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 (TARGET_MACHO && rs6000_darwin64_struct_check_p (mode, type)) |
| { |
| int size = int_size_in_bytes (type); |
| /* 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; |
| cum->floats_in_gpr = 0; |
| rs6000_darwin64_record_arg_advance_recurse (cum, type, 0); |
| rs6000_darwin64_record_arg_advance_flush (cum, |
| size * BITS_PER_UNIT, 1); |
| } |
| if (TARGET_DEBUG_ARG) |
| { |
| fprintf (stderr, "function_adv: words = %2d, align=%d, size=%d", |
| cum->words, TYPE_ALIGN (type), size); |
| fprintf (stderr, |
| "nargs = %4d, proto = %d, mode = %4s (darwin64 abi)\n", |
| cum->nargs_prototype, cum->prototype, |
| GET_MODE_NAME (mode)); |
| } |
| } |
| else if (DEFAULT_ABI == ABI_V4) |
| { |
| if (TARGET_HARD_FLOAT && TARGET_FPRS |
| && ((TARGET_SINGLE_FLOAT && mode == SFmode) |
| || (TARGET_DOUBLE_FLOAT && mode == DFmode) |
| || (mode == TFmode && !TARGET_IEEEQUAD) |
| || mode == SDmode || mode == DDmode || mode == TDmode)) |
| { |
| /* _Decimal128 must use an even/odd register pair. This assumes |
| that the register number is odd when fregno is odd. */ |
| if (mode == TDmode && (cum->fregno % 2) == 1) |
| cum->fregno++; |
| |
| if (cum->fregno + (mode == TFmode || mode == TDmode ? 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 |
| || mode == DDmode || mode == TDmode) |
| 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 (elt_mode) |
| && TARGET_HARD_FLOAT && TARGET_FPRS) |
| { |
| /* _Decimal128 must be passed in an even/odd float register pair. |
| This assumes that the register number is odd when fregno is |
| odd. */ |
| if (elt_mode == TDmode && (cum->fregno % 2) == 1) |
| cum->fregno++; |
| cum->fregno += n_elts * ((GET_MODE_SIZE (elt_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 void |
| rs6000_function_arg_advance (cumulative_args_t cum, machine_mode mode, |
| const_tree type, bool named) |
| { |
| rs6000_function_arg_advance_1 (get_cumulative_args (cum), mode, type, named, |
| 0); |
| } |
| |
| static rtx |
| spe_build_register_parallel (machine_mode mode, int gregno) |
| { |
| rtx r1, r3, r5, r7; |
| |
| 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: |
| case TFmode: |
| 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)); |
| |
| case TCmode: |
| 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)); |
| r5 = gen_rtx_REG (DImode, gregno + 4); |
| r5 = gen_rtx_EXPR_LIST (VOIDmode, r5, GEN_INT (16)); |
| r7 = gen_rtx_REG (DImode, gregno + 6); |
| r7 = gen_rtx_EXPR_LIST (VOIDmode, r7, GEN_INT (24)); |
| return gen_rtx_PARALLEL (mode, gen_rtvec (4, r1, r3, r5, r7)); |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Determine where to put a SIMD argument on the SPE. */ |
| static rtx |
| rs6000_spe_function_arg (const CUMULATIVE_ARGS *cum, machine_mode mode, |
| const_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 == TFmode |
| || mode == DCmode || mode == TCmode)) |
| { |
| 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; |
| 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) |
| { |
| 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, const_tree type, |
| HOST_WIDE_INT startbitpos, rtx rvec[], |
| int *k) |
| { |
| tree f; |
| |
| for (f = TYPE_FIELDS (type); f ; f = DECL_CHAIN (f)) |
| if (TREE_CODE (f) == FIELD_DECL) |
| { |
| HOST_WIDE_INT bitpos = startbitpos; |
| tree ftype = TREE_TYPE (f); |
| machine_mode mode; |
| if (ftype == error_mark_node) |
| continue; |
| mode = TYPE_MODE (ftype); |
| |
| if (DECL_SIZE (f) != 0 |
| && tree_fits_uhwi_p (bit_position (f))) |
| 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)) |
| { |
| unsigned n_fpreg = (GET_MODE_SIZE (mode) + 7) >> 3; |
| #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); |
| if (cum->fregno + n_fpreg > FP_ARG_MAX_REG + 1) |
| { |
| gcc_assert (cum->fregno == FP_ARG_MAX_REG |
| && (mode == TFmode || mode == TDmode)); |
| /* Long double or _Decimal128 split over regs and memory. */ |
| mode = DECIMAL_FLOAT_MODE_P (mode) ? DDmode : DFmode; |
| cum->use_stack=1; |
| } |
| rvec[(*k)++] |
| = gen_rtx_EXPR_LIST (VOIDmode, |
| gen_rtx_REG (mode, cum->fregno++), |
| GEN_INT (bitpos / BITS_PER_UNIT)); |
| if (mode == TFmode || mode == TDmode) |
| cum->fregno++; |
| } |
| else if (cum->named && USE_ALTIVEC_FOR_ARG_P (cum, mode, 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, const_tree type, |
| bool named, bool retval) |
| { |
| rtx rvec[FIRST_PSEUDO_REGISTER]; |
| int k = 1, kbase = 1; |
| HOST_WIDE_INT typesize = int_size_in_bytes (type); |
| /* This is a copy; modifications are not visible to our caller. */ |
| CUMULATIVE_ARGS copy_cum = *orig_cum; |
| CUMULATIVE_ARGS *cum = ©_cum; |
| |
| /* Pad to 16 byte boundary if needed. */ |
| if (!retval && TYPE_ALIGN (type) >= 2 * BITS_PER_WORD |
| && (cum->words % 2) != 0) |
| cum->words++; |
| |
| cum->intoffset = 0; |
| cum->use_stack = 0; |
| cum->named = named; |
| |
| /* Put entries into rvec[] for individual FP and vector fields, and |
| for the chunks of memory that go in int regs. Note we start at |
| element 1; 0 is reserved for an indication of using memory, and |
| may or may not be filled in below. */ |
| rs6000_darwin64_record_arg_recurse (cum, type, /* startbit pos= */ 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 (machine_mode mode, const_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)); |
| } |
| |
| /* We have an argument of MODE and TYPE that goes into FPRs or VRs, |
| but must also be copied into the parameter save area starting at |
| offset ALIGN_WORDS. Fill in RVEC with the elements corresponding |
| to the GPRs and/or memory. Return the number of elements used. */ |
| |
| static int |
| rs6000_psave_function_arg (machine_mode mode, const_tree type, |
| int align_words, rtx *rvec) |
| { |
| int k = 0; |
| |
| if (align_words < GP_ARG_NUM_REG) |
| { |
| int n_words = rs6000_arg_size (mode, type); |
| |
| if (align_words + n_words > GP_ARG_NUM_REG |
| || mode == BLKmode |
| || (TARGET_32BIT && TARGET_POWERPC64)) |
| { |
| /* If this is partially on the stack, then we only |
| include the portion actually in registers here. */ |
| machine_mode rmode = TARGET_32BIT ? SImode : DImode; |
| 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 |
| { |
| rtx r = gen_rtx_REG (rmode, GP_ARG_MIN_REG + align_words); |
| rtx 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. */ |
| rtx 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); |
| } |
| |
| return k; |
| } |
| |
| /* RVEC is a vector of K components of an argument of mode MODE. |
| Construct the final function_arg return value from it. */ |
| |
| static rtx |
| rs6000_finish_function_arg (machine_mode mode, rtx *rvec, int k) |
| { |
| gcc_assert (k >= 1); |
| |
| /* Avoid returning a PARALLEL in the trivial cases. */ |
| if (k == 1) |
| { |
| if (XEXP (rvec[0], 0) == NULL_RTX) |
| return NULL_RTX; |
| |
| if (GET_MODE (XEXP (rvec[0], 0)) == mode) |
| return XEXP (rvec[0], 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. */ |
| |
| static rtx |
| rs6000_function_arg (cumulative_args_t cum_v, machine_mode mode, |
| const_tree type, bool named) |
| { |
| CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
| enum rs6000_abi abi = DEFAULT_ABI; |
| machine_mode elt_mode; |
| int n_elts; |
| |
| /* 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 & ~CALL_LIBCALL); |
| } |
| |
| rs6000_discover_homogeneous_aggregate (mode, type, &elt_mode, &n_elts); |
| |
| if (TARGET_MACHO && rs6000_darwin64_struct_check_p (mode, type)) |
| { |
| rtx rslt = rs6000_darwin64_record_arg (cum, type, named, /*retval= */false); |
| if (rslt != NULL_RTX) |
| return rslt; |
| /* Else fall through to usual handling. */ |
| } |
| |
| if (USE_ALTIVEC_FOR_ARG_P (cum, elt_mode, named)) |
| { |
| rtx rvec[GP_ARG_NUM_REG + AGGR_ARG_NUM_REG + 1]; |
| rtx r, off; |
| int i, k = 0; |
| |
| /* Do we also need to pass this argument in the parameter |
| save area? */ |
| if (TARGET_64BIT && ! cum->prototype) |
| { |
| int align_words = (cum->words + 1) & ~1; |
| k = rs6000_psave_function_arg (mode, type, align_words, rvec); |
| } |
| |
| /* Describe where this argument goes in the vector registers. */ |
| for (i = 0; i < n_elts && cum->vregno + i <= ALTIVEC_ARG_MAX_REG; i++) |
| { |
| r = gen_rtx_REG (elt_mode, cum->vregno + i); |
| off = GEN_INT (i * GET_MODE_SIZE (elt_mode)); |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off); |
| } |
| |
| return rs6000_finish_function_arg (mode, rvec, k); |
| } |
| else if (TARGET_ALTIVEC_ABI |
| && (ALTIVEC_OR_VSX_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; |
| machine_mode part_mode; |
| |
| /* Vector parameters must be 16-byte aligned. In 32-bit |
| mode this means we need to take into account the offset |
| to the parameter save area. 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 = -(rs6000_parm_offset () + 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 |
| || mode == TFmode |
| || mode == TCmode)))) |
| return rs6000_spe_function_arg (cum, mode, type); |
| |
| else if (abi == ABI_V4) |
| { |
| if (TARGET_HARD_FLOAT && TARGET_FPRS |
| && ((TARGET_SINGLE_FLOAT && mode == SFmode) |
| || (TARGET_DOUBLE_FLOAT && mode == DFmode) |
| || (mode == TFmode && !TARGET_IEEEQUAD) |
| || mode == SDmode || mode == DDmode || mode == TDmode)) |
| { |
| /* _Decimal128 must use an even/odd register pair. This assumes |
| that the register number is odd when fregno is odd. */ |
| if (mode == TDmode && (cum->fregno % 2) == 1) |
| cum->fregno++; |
| |
| if (cum->fregno + (mode == TFmode || mode == TDmode ? 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); |
| |
| /* _Decimal128 must be passed in an even/odd float register pair. |
| This assumes that the register number is odd when fregno is odd. */ |
| if (elt_mode == TDmode && (cum->fregno % 2) == 1) |
| cum->fregno++; |
| |
| if (USE_FP_FOR_ARG_P (cum, elt_mode)) |
| { |
| rtx rvec[GP_ARG_NUM_REG + AGGR_ARG_NUM_REG + 1]; |
| rtx r, off; |
| int i, k = 0; |
| unsigned long n_fpreg = (GET_MODE_SIZE (elt_mode) + 7) >> 3; |
| int fpr_words; |
| |
| /* Do we also need to pass this argument in the parameter |
| save area? */ |
| if (type && (cum->nargs_prototype <= 0 |
| || ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) |
| && TARGET_XL_COMPAT |
| && align_words >= GP_ARG_NUM_REG))) |
| k = rs6000_psave_function_arg (mode, type, align_words, rvec); |
| |
| /* Describe where this argument goes in the fprs. */ |
| for (i = 0; i < n_elts |
| && cum->fregno + i * n_fpreg <= FP_ARG_MAX_REG; i++) |
| { |
| /* Check if the argument is split over registers and memory. |
| This can only ever happen for long double or _Decimal128; |
| complex types are handled via split_complex_arg. */ |
| machine_mode fmode = elt_mode; |
| if (cum->fregno + (i + 1) * n_fpreg > FP_ARG_MAX_REG + 1) |
| { |
| gcc_assert (fmode == TFmode || fmode == TDmode); |
| fmode = DECIMAL_FLOAT_MODE_P (fmode) ? DDmode : DFmode; |
| } |
| |
| r = gen_rtx_REG (fmode, cum->fregno + i * n_fpreg); |
| off = GEN_INT (i * GET_MODE_SIZE (elt_mode)); |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off); |
| } |
| |
| /* If there were not enough FPRs to hold the argument, the rest |
| usually goes into memory. However, if the current position |
| is still within the register parameter area, a portion may |
| actually have to go into GPRs. |
| |
| Note that it may happen that the portion of the argument |
| passed in the first "half" of the first GPR was already |
| passed in the last FPR as well. |
| |
| For unnamed arguments, we already set up GPRs to cover the |
| whole argument in rs6000_psave_function_arg, so there is |
| nothing further to do at this point. */ |
| fpr_words = (i * GET_MODE_SIZE (elt_mode)) / (TARGET_32BIT ? 4 : 8); |
| if (i < n_elts && align_words + fpr_words < GP_ARG_NUM_REG |
| && cum->nargs_prototype > 0) |
| { |
| static bool warned; |
| |
| machine_mode rmode = TARGET_32BIT ? SImode : DImode; |
| int n_words = rs6000_arg_size (mode, type); |
| |
| align_words += fpr_words; |
| n_words -= fpr_words; |
| |
| do |
| { |
| r = gen_rtx_REG (rmode, GP_ARG_MIN_REG + align_words); |
| off = GEN_INT (fpr_words++ * GET_MODE_SIZE (rmode)); |
| rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off); |
| } |
| while (++align_words < GP_ARG_NUM_REG && --n_words != 0); |
| |
| if (!warned && warn_psabi) |
| { |
| warned = true; |
| inform (input_location, |
| "the ABI of passing homogeneous float aggregates" |
| " has changed in GCC 5"); |
| } |
| } |
| |
| return rs6000_finish_function_arg (mode, rvec, k); |
| } |
| else if (align_words < GP_ARG_NUM_REG) |
| { |
| if (TARGET_32BIT && TARGET_POWERPC64) |
| return rs6000_mixed_function_arg (mode, type, align_words); |
| |
| 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_t cum_v, machine_mode mode, |
| tree type, bool named) |
| { |
| CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); |
| bool passed_in_gprs = true; |
| int ret = 0; |
| int align_words; |
| machine_mode elt_mode; |
| int n_elts; |
| |
| rs6000_discover_homogeneous_aggregate (mode, type, &elt_mode, &n_elts); |
| |
| if (DEFAULT_ABI == ABI_V4) |
| return 0; |
| |
| if (USE_ALTIVEC_FOR_ARG_P (cum, elt_mode, named)) |
| { |
| /* If we are passing this arg in the fixed parameter save area |
| (gprs or memory) as well as VRs, we do not use the partial |
| bytes mechanism; instead, rs6000_function_arg will return a |
| PARALLEL including a memory element as necessary. */ |
| if (TARGET_64BIT && ! cum->prototype) |
| return 0; |
| |
| /* Otherwise, we pass in VRs only. Check for partial copies. */ |
| passed_in_gprs = false; |
| if (cum->vregno + n_elts > ALTIVEC_ARG_MAX_REG + 1) |
| ret = (ALTIVEC_ARG_MAX_REG + 1 - cum->vregno) * 16; |
| } |
| |
| /* In this complicated case we just disable the partial_nregs code. */ |
| if (TARGET_MACHO && rs6000_darwin64_struct_check_p (mode, type)) |
| return 0; |
| |
| align_words = rs6000_parm_start (mode, type, cum->words); |
| |
| if (USE_FP_FOR_ARG_P (cum, elt_mode)) |
| { |
| unsigned long n_fpreg = (GET_MODE_SIZE (elt_mode) + 7) >> 3; |
| |
| /* If we are passing this arg in the fixed parameter save area |
| (gprs or memory) as well as FPRs, we do not use the partial |
| bytes mechanism; instead, rs6000_function_arg will return a |
| PARALLEL including a memory element as necessary. */ |
| if (type |
| && (cum->nargs_prototype <= 0 |
| || ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) |
| && TARGET_XL_COMPAT |
| && align_words >= GP_ARG_NUM_REG))) |
| return 0; |
| |
| /* Otherwise, we pass in FPRs only. Check for partial copies. */ |
| passed_in_gprs = false; |
| if (cum->fregno + n_elts * n_fpreg > FP_ARG_MAX_REG + 1) |
| { |
| /* Compute number of bytes / words passed in FPRs. If there |
| is still space available in the register parameter area |
| *after* that amount, a part of the argument will be passed |
| in GPRs. In that case, the total amount passed in any |
| registers is equal to the amount that would have been passed |
| in GPRs if everything were passed there, so we fall back to |
| the GPR code below to compute the appropriate value. */ |
| int fpr = ((FP_ARG_MAX_REG + 1 - cum->fregno) |
| * MIN (8, GET_MODE_SIZE (elt_mode))); |
| int fpr_words = fpr / (TARGET_32BIT ? 4 : 8); |
| |
| if (align_words + fpr_words < GP_ARG_NUM_REG) |
| passed_in_gprs = true; |
| else |
| ret = fpr; |
| } |
| } |
| |
| if (passed_in_gprs |
| && 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_t cum ATTRIBUTE_UNUSED, |
| machine_mode mode, const_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; |
| } |
| |
| /* Process parameter of type TYPE after ARGS_SO_FAR parameters were |
| already processes. Return true if the parameter must be passed |
| (fully or partially) on the stack. */ |
| |
| static bool |
| rs6000_parm_needs_stack (cumulative_args_t args_so_far, tree type) |
| { |
| machine_mode mode; |
| int unsignedp; |
| rtx entry_parm; |
| |
| /* Catch errors. */ |
| if (type == NULL || type == error_mark_node) |
| return true; |
| |
| /* Handle types with no storage requirement. */ |
| if (TYPE_MODE (type) == VOIDmode) |
| return false; |
| |
| /* Handle complex types. */ |
| if (TREE_CODE (type) == COMPLEX_TYPE) |
| return (rs6000_parm_needs_stack (args_so_far, TREE_TYPE (type)) |
| || rs6000_parm_needs_stack (args_so_far, TREE_TYPE (type))); |
| |
| /* Handle transparent aggregates. */ |
| if ((TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == RECORD_TYPE) |
| && TYPE_TRANSPARENT_AGGR (type)) |
| type = TREE_TYPE (first_field (type)); |
| |
| /* See if this arg was passed by invisible reference. */ |
| if (pass_by_reference (get_cumulative_args (args_so_far), |
| TYPE_MODE (type), type, true)) |
| type = build_pointer_type (type); |
| |
| /* Find mode as it is passed by the ABI. */ |
| unsignedp = TYPE_UNSIGNED (type); |
| mode = promote_mode (type, TYPE_MODE (type), &unsignedp); |
| |
| /* If we must pass in stack, we need a stack. */ |
| if (rs6000_must_pass_in_stack (mode, type)) |
| return true; |
| |
| /* If there is no incoming register, we need a stack. */ |
| entry_parm = rs6000_function_arg (args_so_far, mode, type, true); |
| if (entry_parm == NULL) |
| return true; |
| |
| /* Likewise if we need to pass both in registers and on the stack. */ |
| if (GET_CODE (entry_parm) == PARALLEL |
| && XEXP (XVECEXP (entry_parm, 0, 0), 0) == NULL_RTX) |
| return true; |
| |
| /* Also true if we're partially in registers and partially not. */ |
| if (rs6000_arg_partial_bytes (args_so_far, mode, type, true) != 0) |
| return true; |
| |
| /* Update info on where next arg arrives in registers. */ |
| rs6000_function_arg_advance (args_so_far, mode, type, true); |
| return false; |
| } |
| |
| /* Return true if FUN has no prototype, has a variable argument |
| list, or passes any parameter in memory. */ |
| |
| static bool |
| rs6000_function_parms_need_stack (tree fun, bool incoming) |
| { |
| tree fntype, result; |
| CUMULATIVE_ARGS args_so_far_v; |
| cumulative_args_t args_so_far; |
| |
| if (!fun) |
| /* Must be a libcall, all of which only use reg parms. */ |
| return false; |
| |
| fntype = fun; |
| if (!TYPE_P (fun)) |
| fntype = TREE_TYPE (fun); |
| |
| /* Varargs functions need the parameter save area. */ |
| if ((!incoming && !prototype_p (fntype)) || stdarg_p (fntype)) |
| return true; |
| |
| INIT_CUMULATIVE_INCOMING_ARGS (args_so_far_v, fntype, NULL_RTX); |
| args_so_far = pack_cumulative_args (&args_so_far_v); |
| |
| /* When incoming, we will have been passed the function decl. |
| It is necessary to use the decl to handle K&R style functions, |
| where TYPE_ARG_TYPES may not be available. */ |
| if (incoming) |
| { |
| gcc_assert (DECL_P (fun)); |
| result = DECL_RESULT (fun); |
| } |
| else |
| result = TREE_TYPE (fntype); |
| |
| if (result && aggregate_value_p (result, fntype)) |
| { |
| if (!TYPE_P (result)) |
| result = TREE_TYPE (result); |
| result = build_pointer_type (result); |
| rs6000_parm_needs_stack (args_so_far, result); |
| } |
| |
| if (incoming) |
| { |
| tree parm; |
| |
| for (parm = DECL_ARGUMENTS (fun); |
| parm && parm != void_list_node; |
| parm = TREE_CHAIN (parm)) |
| if (rs6000_parm_needs_stack (args_so_far, TREE_TYPE (parm))) |
| return true; |
| } |
| else |
| { |
| function_args_iterator args_iter; |
| tree arg_type; |
| |
| FOREACH_FUNCTION_ARGS (fntype, arg_type, args_iter) |
| if (rs6000_parm_needs_stack (args_so_far, arg_type)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Return the size of the REG_PARM_STACK_SPACE are for FUN. This is |
| usually a constant depending on the ABI. However, in the ELFv2 ABI |
| the register parameter area is optional when calling a function that |
| has a prototype is scope, has no variable argument list, and passes |
| all parameters in registers. */ |
| |
| int |
| rs6000_reg_parm_stack_space (tree fun, bool incoming) |
| { |
| int reg_parm_stack_space; |
| |
| switch (DEFAULT_ABI) |
| { |
| default: |
| reg_parm_stack_space = 0; |
| break; |
| |
| case ABI_AIX: |
| case ABI_DARWIN: |
| reg_parm_stack_space = TARGET_64BIT ? 64 : 32; |
| break; |
| |
| case ABI_ELFv2: |
| /* ??? Recomputing this every time is a bit expensive. Is there |
| a place to cache this information? */ |
| if (rs6000_function_parms_need_stack (fun, incoming)) |
| reg_parm_stack_space = TARGET_64BIT ? 64 : 32; |
| else |
| reg_parm_stack_space = 0; |
| break; |
| } |
| |
| return reg_parm_stack_space; |
| } |
| |
| static void |
| rs6000_move_block_from_reg (int regno, rtx x, int nregs) |
| { |
| int i; |
| 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_t cum, 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; |
| alias_set_type set; |
| |
| /* Skip the last named argument. */ |
| next_cum = *get_cumulative_args (cum); |
| rs6000_function_arg_advance_1 (&next_cum, mode, type, true, 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 (Pmode, 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 n_gpr, nregs = GP_ARG_NUM_REG - first_reg_offset; |
| |
| if (va_list_gpr_counter_field) |
| /* V4 va_list_gpr_size counts number of registers needed. */ |
| n_gpr = cfun->va_list_gpr_size; |
| else |
| /* char * va_list instead counts number of bytes needed. */ |
| n_gpr = (cfun->va_list_gpr_size + reg_size - 1) / reg_size; |
| |
| if (nregs > n_gpr) |
| nregs = n_gpr; |
| |
| mem = gen_rtx_MEM (BLKmode, |
| plus_constant (Pmode, 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 ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT) |
| ? DFmode : SFmode, |
| plus_constant (Pmode, save_area, off)); |
| MEM_NOTRAP_P (mem) = 1; |
| set_mem_alias_set (mem, set); |
| set_mem_align (mem, GET_MODE_ALIGNMENT ( |
| (TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT) |
| ? DFmode : SFmode)); |
| emit_move_insn (mem, gen_rtx_REG ( |
| (TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT) |
| ? DFmode : SFmode, fregno)); |
| } |
| |
| emit_label (lab); |
| } |
| } |
| |
| /* 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 (BUILTINS_LOCATION, TYPE_DECL, |
| get_identifier ("__va_list_tag"), record); |
| |
| f_gpr = build_decl (BUILTINS_LOCATION, FIELD_DECL, get_identifier ("gpr"), |
| unsigned_char_type_node); |
| f_fpr = build_decl (BUILTINS_LOCATION, 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 (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("reserved"), short_unsigned_type_node); |
| f_ovf = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
| get_identifier ("overflow_arg_area"), |
| ptr_type_node); |
| f_sav = build_decl (BUILTINS_LOCATION, 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; |
| |
| TYPE_STUB_DECL (record) = type_decl; |
| TYPE_NAME (record) = type_decl; |
| TYPE_FIELDS (record) = f_gpr; |
| DECL_CHAIN (f_gpr) = f_fpr; |
| DECL_CHAIN (f_fpr) = f_res; |
| DECL_CHAIN (f_res) = f_ovf; |
| DECL_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. */ |
| |
| static 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 = DECL_CHAIN (f_gpr); |
| f_res = DECL_CHAIN (f_fpr); |
| f_ovf = DECL_CHAIN (f_res); |
| f_sav = DECL_CHAIN (f_ovf); |
| |
| valist = build_simple_mem_ref (valist); |
| gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); |
| fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), unshare_expr (valist), |
| f_fpr, NULL_TREE); |
| ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), unshare_expr (valist), |
| f_ovf, NULL_TREE); |
| sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist), |
| f_sav, NULL_TREE); |
| |
| /* Count number of gp and fp argument registers used. */ |
| words = crtl->args.info.words; |
| n_gpr = MIN (crtl->args.info.sysv_gregno - GP_ARG_MIN_REG, |
| GP_ARG_NUM_REG); |
| n_fpr = MIN (crtl->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); |
| |
| #ifdef HAVE_AS_GNU_ATTRIBUTE |
| if (call_ABI_of_interest (cfun->decl)) |
| rs6000_passes_float = true; |
| #endif |
| } |
| |
| /* Find the overflow area. */ |
| t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); |
| if (words != 0) |
| t = fold_build_pointer_plus_hwi (t, words * MIN_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 = fold_build_pointer_plus_hwi (t, 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. */ |
| |
| static tree |
| rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, |
| gimple_seq *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_for_mode (type, ptr_mode, true); |
| int regalign = 0; |
| gimple stmt; |
| |
| 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); |
| } |
| |
| /* We need to deal with the fact that the darwin ppc64 ABI is defined by an |
| earlier version of gcc, with the property that it always applied alignment |
| adjustments to the va-args (even for zero-sized types). The cheapest way |
| to deal with this is to replicate the effect of the part of |
| std_gimplify_va_arg_expr that carries out the align adjust, for the case |
| of relevance. |
| We don't need to check for pass-by-reference because of the test above. |
| We can return a simplifed answer, since we know there's no offset to add. */ |
| |
| if (((TARGET_MACHO |
| && rs6000_darwin64_abi) |
| || DEFAULT_ABI == ABI_ELFv2 |
| || (DEFAULT_ABI == ABI_AIX && !rs6000_compat_align_parm)) |
| && integer_zerop (TYPE_SIZE (type))) |
| { |
| unsigned HOST_WIDE_INT align, boundary; |
| tree valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL); |
| align = PARM_BOUNDARY / BITS_PER_UNIT; |
| boundary = rs6000_function_arg_boundary (TYPE_MODE (type), type); |
| if (boundary > MAX_SUPPORTED_STACK_ALIGNMENT) |
| boundary = MAX_SUPPORTED_STACK_ALIGNMENT; |
| boundary /= BITS_PER_UNIT; |
| if (boundary > align) |
| { |
| tree t ; |
| /* This updates arg ptr by the amount that would be necessary |
| to align the zero-sized (but not zero-alignment) item. */ |
| t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, |
| fold_build_pointer_plus_hwi (valist_tmp, boundary - 1)); |
| gimplify_and_add (t, pre_p); |
| |
| t = fold_convert (sizetype, valist_tmp); |
| t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp, |
| fold_convert (TREE_TYPE (valist), |
| fold_build2 (BIT_AND_EXPR, sizetype, t, |
| size_int (-boundary)))); |
| t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, t); |
| gimplify_and_add (t, pre_p); |
| } |
| /* Since it is zero-sized there's no increment for the item itself. */ |
| valist_tmp = fold_convert (build_pointer_type (type), valist_tmp); |
| return build_va_arg_indirect_ref (valist_tmp); |
| } |
| |
| if (DEFAULT_ABI != ABI_V4) |
| { |
| if (targetm.calls.split_complex_arg && TREE_CODE (type) == COMPLEX_TYPE) |
| { |
| tree elem_type = TREE_TYPE (type); |
| 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; |
| gimple_seq post = NULL; |
| |
| 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); |
| gimple_seq_add_seq (pre_p, post); |
| |
| 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 = DECL_CHAIN (f_gpr); |
| f_res = DECL_CHAIN (f_fpr); |
| f_ovf = DECL_CHAIN (f_res); |
| f_sav = DECL_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), unshare_expr (valist), |
| f_fpr, NULL_TREE); |
| ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), unshare_expr (valist), |
| f_ovf, NULL_TREE); |
| sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist), |
| f_sav, NULL_TREE); |
| |
| size = int_size_in_bytes (type); |
| rsize = (size + 3) / 4; |
| int pad = 4 * rsize - size; |
| align = 1; |
| |
| if (TARGET_HARD_FLOAT && TARGET_FPRS |
| && ((TARGET_SINGLE_FLOAT && TYPE_MODE (type) == SFmode) |
| || (TARGET_DOUBLE_FLOAT |
| && (TYPE_MODE (type) == DFmode |
| || TYPE_MODE (type) == TFmode |
| || TYPE_MODE (type) == SDmode |
| || TYPE_MODE (type) == DDmode |
| || TYPE_MODE (type) == TDmode)))) |
| { |
| /* FP args go in FP registers, if present. */ |
| reg = fpr; |
| n_reg = (size + 7) / 8; |
| sav_ofs = ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT) ? 8 : 4) * 4; |
| sav_scale = ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT) ? 8 : 4); |
| if (TYPE_MODE (type) != SFmode && TYPE_MODE (type) != SDmode) |
| 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"); |
| |
| /* 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 (input_location); |
| lab_over = create_artificial_label (input_location); |
| |
| /* 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) |
| { |
| regalign = 1; |
| u = build2 (BIT_AND_EXPR, TREE_TYPE (reg), unshare_expr (reg), |
| build_int_cst (TREE_TYPE (reg), n_reg - 1)); |
| u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), |
| unshare_expr (reg), u); |
| } |
| /* _Decimal128 is passed in even/odd fpr pairs; the stored |
| reg number is 0 for f1, so we want to make it odd. */ |
| else if (reg == fpr && TYPE_MODE (type) == TDmode) |
| { |
| t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), unshare_expr (reg), |
| build_int_cst (TREE_TYPE (reg), 1)); |
| u = build2 (MODIFY_EXPR, void_type_node, unshare_expr (reg), t); |
| } |
| |
| 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 = fold_build_pointer_plus_hwi (sav, sav_ofs); |
| |
| u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), unshare_expr (reg), |
| build_int_cst (TREE_TYPE (reg), n_reg)); |
| u = fold_convert (sizetype, u); |
| u = build2 (MULT_EXPR, sizetype, u, size_int (sav_scale)); |
| t = fold_build_pointer_plus (t, u); |
| |
| /* _Decimal32 varargs are located in the second word of the 64-bit |
| FP register for 32-bit binaries. */ |
| if (TARGET_32BIT |
| && TARGET_HARD_FLOAT && TARGET_FPRS |
| && TYPE_MODE (type) == SDmode) |
| t = fold_build_pointer_plus_hwi (t, size); |
| |
| /* Args are right-aligned. */ |
| if (BYTES_BIG_ENDIAN) |
| t = fold_build_pointer_plus_hwi (t, pad); |
| |
| gimplify_assign (addr, t, pre_p); |
| |
| gimple_seq_add_stmt (pre_p, gimple_build_goto (lab_over)); |
| |
| stmt = gimple_build_label (lab_false); |
| gimple_seq_add_stmt (pre_p, stmt); |
| |
| if ((n_reg == 2 && !regalign) || n_reg > 2) |
| { |
| /* Ensure that we don't find any more args in regs. |
| Alignment has taken care of for special cases. */ |
| gimplify_assign (reg, build_int_cst (TREE_TYPE (reg), 8), pre_p); |
| } |
| } |
| |
| /* ... otherwise out of the overflow area. */ |
| |
| /* Care for on-stack alignment if needed. */ |
| t = ovf; |
| if (align != 1) |
| { |
| t = fold_build_pointer_plus_hwi (t, align - 1); |
| t = build2 (BIT_AND_EXPR, TREE_TYPE (t), t, |
| build_int_cst (TREE_TYPE (t), -align)); |
| } |
| |
| /* Args are right-aligned. */ |
| if (BYTES_BIG_ENDIAN) |
| t = fold_build_pointer_plus_hwi (t, pad); |
| |
| gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue); |
| |
| gimplify_assign (unshare_expr (addr), t, pre_p); |
| |
| t = fold_build_pointer_plus_hwi (t, size); |
| gimplify_assign (unshare_expr (ovf), t, pre_p); |
| |
| if (lab_over) |
| { |
| stmt = gimple_build_label (lab_over); |
| gimple_seq_add_stmt (pre_p, stmt); |
| } |
| |
| 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_call_expr (builtin_decl_implicit (BUILT_IN_MEMCPY), |
| 3, dest_addr, addr, size_int (rsize * 4)); |
| |
| 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 (const char *name, tree type, enum rs6000_builtins code) |
| { |
| tree t; |
| unsigned classify = rs6000_builtin_info[(int)code].attr; |
| const char *attr_string = ""; |
| |
| gcc_assert (name != NULL); |
| gcc_assert (IN_RANGE ((int)code, 0, (int)RS6000_BUILTIN_COUNT)); |
| |
| if (rs6000_builtin_decls[(int)code]) |
| fatal_error (input_location, |
| "internal error: builtin function %s already processed", name); |
| |
| rs6000_builtin_decls[(int)code] = t = |
| add_builtin_function (name, type, (int)code, BUILT_IN_MD, NULL, NULL_TREE); |
| |
| /* Set any special attributes. */ |
| if ((classify & RS6000_BTC_CONST) != 0) |
| { |
| /* const function, function only depends on the inputs. */ |
| TREE_READONLY (t) = 1; |
| TREE_NOTHROW (t) = 1; |
| attr_string = ", pure"; |
| } |
| else if ((classify & RS6000_BTC_PURE) != 0) |
| { |
| /* pure function, function can read global memory, but does not set any |
| external state. */ |
| DECL_PURE_P (t) = 1; |
| TREE_NOTHROW (t) = 1; |
| attr_string = ", const"; |
| } |
| else if ((classify & RS6000_BTC_FP) != 0) |
| { |
| /* Function is a math function. If rounding mode is on, then treat the |
| function as not reading global memory, but it can have arbitrary side |
| effects. If it is off, then assume the function is a const function. |
| This mimics the ATTR_MATHFN_FPROUNDING attribute in |
| builtin-attribute.def that is used for the math functions. */ |
| TREE_NOTHROW (t) = 1; |
| if (flag_rounding_math) |
| { |
| DECL_PURE_P (t) = 1; |
| DECL_IS_NOVOPS (t) = 1; |
| attr_string = ", fp, pure"; |
| } |
| else |
| { |
| TREE_READONLY (t) = 1; |
| attr_string = ", fp, const"; |
| } |
| } |
| else if ((classify & RS6000_BTC_ATTR_MASK) != 0) |
| gcc_unreachable (); |
| |
| if (TARGET_DEBUG_BUILTIN) |
| fprintf (stderr, "rs6000_builtin, code = %4d, %s%s\n", |
| (int)code, name, attr_string); |
| } |
| |
| /* Simple ternary operations: VECd = foo (VECa, VECb, VECc). */ |
| |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_3arg[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| /* DST operations: void foo (void *, const int, const char). */ |
| |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_dst[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| /* Simple binary operations: VECc = foo (VECa, VECb). */ |
| |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_2arg[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| /* AltiVec predicates. */ |
| |
| static const struct builtin_description bdesc_altivec_preds[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| /* SPE predicates. */ |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_spe_predicates[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| /* SPE evsel predicates. */ |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_spe_evsel[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| /* PAIRED predicates. */ |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_paired_preds[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| /* ABS* operations. */ |
| |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_abs[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| /* Simple unary operations: VECb = foo (unsigned literal) or VECb = |
| foo (VECa). */ |
| |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_1arg[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| /* HTM builtins. */ |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| #undef RS6000_BUILTIN_X |
| |
| #define RS6000_BUILTIN_1(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_2(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_3(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_A(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_D(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_E(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_H(ENUM, NAME, MASK, ATTR, ICODE) \ |
| { MASK, ICODE, NAME, ENUM }, |
| |
| #define RS6000_BUILTIN_P(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_Q(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_S(ENUM, NAME, MASK, ATTR, ICODE) |
| #define RS6000_BUILTIN_X(ENUM, NAME, MASK, ATTR, ICODE) |
| |
| static const struct builtin_description bdesc_htm[] = |
| { |
| #include "rs6000-builtin.def" |
| }; |
| |
| #undef RS6000_BUILTIN_1 |
| #undef RS6000_BUILTIN_2 |
| #undef RS6000_BUILTIN_3 |
| #undef RS6000_BUILTIN_A |
| #undef RS6000_BUILTIN_D |
| #undef RS6000_BUILTIN_E |
| #undef RS6000_BUILTIN_H |
| #undef RS6000_BUILTIN_P |
| #undef RS6000_BUILTIN_Q |
| #undef RS6000_BUILTIN_S |
| |
| /* Return true if a builtin function is overloaded. */ |
| bool |
| rs6000_overloaded_builtin_p (enum rs6000_builtins fncode) |
| { |
| return (rs6000_builtin_info[(int)fncode].attr & RS6000_BTC_OVERLOADED) != 0; |
| } |
| |
| /* Expand an expression EXP that calls a builtin without arguments. */ |
| static rtx |
| rs6000_expand_zeroop_builtin (enum insn_code icode, rtx target) |
| { |
| rtx pat; |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| |
| if (icode == CODE_FOR_nothing) |
| /* Builtin not supported on this processor. */ |
| return 0; |
| |
| 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; |
| } |
| |
| |
| static rtx |
| rs6000_expand_mtfsf_builtin (enum insn_code icode, tree exp) |
| { |
| rtx pat; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| machine_mode mode0 = insn_data[icode].operand[0].mode; |
| machine_mode mode1 = insn_data[icode].operand[1].mode; |
| |
| 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 (GET_CODE (op0) != CONST_INT |
| || INTVAL (op0) > 255 |
| || INTVAL (op0) < 0) |
| { |
| error ("argument 1 must be an 8-bit field value"); |
| return const0_rtx; |
| } |
| |
| if (! (*insn_data[icode].operand[0].predicate) (op0, mode0)) |
| op0 = copy_to_mode_reg (mode0, 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) |
| return const0_rtx; |
| emit_insn (pat); |
| |
| return NULL_RTX; |
| } |
| |
| |
| static rtx |
| rs6000_expand_unop_builtin (enum insn_code icode, tree exp, rtx target) |
| { |
| rtx pat; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| rtx op0 = expand_normal (arg0); |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| |
| 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 |
| || icode == CODE_FOR_altivec_vspltisw |
| || icode == CODE_FOR_spe_evsplatfi |
| || 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 (tmode); |
| } |
| } |
| |
| 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 exp, rtx target) |
| { |
| rtx pat, scratch1, scratch2; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| rtx op0 = expand_normal (arg0); |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| |
| /* 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 exp, rtx target) |
| { |
| rtx pat; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| machine_mode mode1 = insn_data[icode].operand[2].mode; |
| |
| 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 (tmode); |
| } |
| } |
| |
| 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, tree exp, rtx target) |
| { |
| rtx pat, scratch; |
| tree cr6_form = CALL_EXPR_ARG (exp, 0); |
| tree arg0 = CALL_EXPR_ARG (exp, 1); |
| tree arg1 = CALL_EXPR_ARG (exp, 2); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| machine_mode tmode = SImode; |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| machine_mode mode1 = insn_data[icode].operand[2].mode; |
| int cr6_form_int; |
| |
| 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); |
| 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 |
| paired_expand_lv_builtin (enum insn_code icode, tree exp, rtx target) |
| { |
| rtx pat, addr; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = Pmode; |
| machine_mode mode1 = Pmode; |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| |
| 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; |
| } |
| |
| /* Return a constant vector for use as a little-endian permute control vector |
| to reverse the order of elements of the given vector mode. */ |
| static rtx |
| swap_selector_for_mode (machine_mode mode) |
| { |
| /* These are little endian vectors, so their elements are reversed |
| from what you would normally expect for a permute control vector. */ |
| unsigned int swap2[16] = {7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8}; |
| unsigned int swap4[16] = {3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12}; |
| unsigned int swap8[16] = {1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14}; |
| unsigned int swap16[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; |
| unsigned int *swaparray, i; |
| rtx perm[16]; |
| |
| switch (mode) |
| { |
| case V2DFmode: |
| case V2DImode: |
| swaparray = swap2; |
| break; |
| case V4SFmode: |
| case V4SImode: |
| swaparray = swap4; |
| break; |
| case V8HImode: |
| swaparray = swap8; |
| break; |
| case V16QImode: |
| swaparray = swap16; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| for (i = 0; i < 16; ++i) |
| perm[i] = GEN_INT (swaparray[i]); |
| |
| return force_reg (V16QImode, gen_rtx_CONST_VECTOR (V16QImode, gen_rtvec_v (16, perm))); |
| } |
| |
| /* Generate code for an "lvx", "lvxl", or "lve*x" built-in for a little endian target |
| with -maltivec=be specified. Issue the load followed by an element-reversing |
| permute. */ |
| void |
| altivec_expand_lvx_be (rtx op0, rtx op1, machine_mode mode, unsigned unspec) |
| { |
| rtx tmp = gen_reg_rtx (mode); |
| rtx load = gen_rtx_SET (VOIDmode, tmp, op1); |
| rtx lvx = gen_rtx_UNSPEC (mode, gen_rtvec (1, const0_rtx), unspec); |
| rtx par = gen_rtx_PARALLEL (mode, gen_rtvec (2, load, lvx)); |
| rtx sel = swap_selector_for_mode (mode); |
| rtx vperm = gen_rtx_UNSPEC (mode, gen_rtvec (3, tmp, tmp, sel), UNSPEC_VPERM); |
| |
| gcc_assert (REG_P (op0)); |
| emit_insn (par); |
| emit_insn (gen_rtx_SET (VOIDmode, op0, vperm)); |
| } |
| |
| /* Generate code for a "stvx" or "stvxl" built-in for a little endian target |
| with -maltivec=be specified. Issue the store preceded by an element-reversing |
| permute. */ |
| void |
| altivec_expand_stvx_be (rtx op0, rtx op1, machine_mode mode, unsigned unspec) |
| { |
| rtx tmp = gen_reg_rtx (mode); |
| rtx store = gen_rtx_SET (VOIDmode, op0, tmp); |
| rtx stvx = gen_rtx_UNSPEC (mode, gen_rtvec (1, const0_rtx), unspec); |
| rtx par = gen_rtx_PARALLEL (mode, gen_rtvec (2, store, stvx)); |
| rtx sel = swap_selector_for_mode (mode); |
| rtx vperm; |
| |
| gcc_assert (REG_P (op1)); |
| vperm = gen_rtx_UNSPEC (mode, gen_rtvec (3, op1, op1, sel), UNSPEC_VPERM); |
| emit_insn (gen_rtx_SET (VOIDmode, tmp, vperm)); |
| emit_insn (par); |
| } |
| |
| /* Generate code for a "stve*x" built-in for a little endian target with -maltivec=be |
| specified. Issue the store preceded by an element-reversing permute. */ |
| void |
| altivec_expand_stvex_be (rtx op0, rtx op1, machine_mode mode, unsigned unspec) |
| { |
| machine_mode inner_mode = GET_MODE_INNER (mode); |
| rtx tmp = gen_reg_rtx (mode); |
| rtx stvx = gen_rtx_UNSPEC (inner_mode, gen_rtvec (1, tmp), unspec); |
| rtx sel = swap_selector_for_mode (mode); |
| rtx vperm; |
| |
| gcc_assert (REG_P (op1)); |
| vperm = gen_rtx_UNSPEC (mode, gen_rtvec (3, op1, op1, sel), UNSPEC_VPERM); |
| emit_insn (gen_rtx_SET (VOIDmode, tmp, vperm)); |
| emit_insn (gen_rtx_SET (VOIDmode, op0, stvx)); |
| } |
| |
| static rtx |
| altivec_expand_lv_builtin (enum insn_code icode, tree exp, rtx target, bool blk) |
| { |
| rtx pat, addr; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = Pmode; |
| machine_mode mode1 = Pmode; |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| |
| 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 (blk ? BLKmode : tmode, op1); |
| } |
| else |
| { |
| op0 = copy_to_mode_reg (mode0, op0); |
| addr = gen_rtx_MEM (blk ? BLKmode : 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 exp) |
| { |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| tree arg2 = CALL_EXPR_ARG (exp, 2); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| rtx op2 = expand_normal (arg2); |
| rtx pat; |
| machine_mode mode0 = insn_data[icode].operand[0].mode; |
| machine_mode mode1 = insn_data[icode].operand[1].mode; |
| 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 |
| paired_expand_stv_builtin (enum insn_code icode, tree exp) |
| { |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| tree arg2 = CALL_EXPR_ARG (exp, 2); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| rtx op2 = expand_normal (arg2); |
| rtx pat, addr; |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode1 = Pmode; |
| machine_mode mode2 = Pmode; |
| |
| /* 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 |
| altivec_expand_stv_builtin (enum insn_code icode, tree exp) |
| { |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| tree arg2 = CALL_EXPR_ARG (exp, 2); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| rtx op2 = expand_normal (arg2); |
| rtx pat, addr; |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode smode = insn_data[icode].operand[1].mode; |
| machine_mode mode1 = Pmode; |
| machine_mode mode2 = Pmode; |
| |
| /* 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, smode)) |
| op0 = copy_to_mode_reg (smode, 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; |
| } |
| |
| /* Return the appropriate SPR number associated with the given builtin. */ |
| static inline HOST_WIDE_INT |
| htm_spr_num (enum rs6000_builtins code) |
| { |
| if (code == HTM_BUILTIN_GET_TFHAR |
| || code == HTM_BUILTIN_SET_TFHAR) |
| return TFHAR_SPR; |
| else if (code == HTM_BUILTIN_GET_TFIAR |
| || code == HTM_BUILTIN_SET_TFIAR) |
| return TFIAR_SPR; |
| else if (code == HTM_BUILTIN_GET_TEXASR |
| || code == HTM_BUILTIN_SET_TEXASR) |
| return TEXASR_SPR; |
| gcc_assert (code == HTM_BUILTIN_GET_TEXASRU |
| || code == HTM_BUILTIN_SET_TEXASRU); |
| return TEXASRU_SPR; |
| } |
| |
| /* Return the appropriate SPR regno associated with the given builtin. */ |
| static inline HOST_WIDE_INT |
| htm_spr_regno (enum rs6000_builtins code) |
| { |
| if (code == HTM_BUILTIN_GET_TFHAR |
| || code == HTM_BUILTIN_SET_TFHAR) |
| return TFHAR_REGNO; |
| else if (code == HTM_BUILTIN_GET_TFIAR |
| || code == HTM_BUILTIN_SET_TFIAR) |
| return TFIAR_REGNO; |
| gcc_assert (code == HTM_BUILTIN_GET_TEXASR |
| || code == HTM_BUILTIN_SET_TEXASR |
| || code == HTM_BUILTIN_GET_TEXASRU |
| || code == HTM_BUILTIN_SET_TEXASRU); |
| return TEXASR_REGNO; |
| } |
| |
| /* Return the correct ICODE value depending on whether we are |
| setting or reading the HTM SPRs. */ |
| static inline enum insn_code |
| rs6000_htm_spr_icode (bool nonvoid) |
| { |
| if (nonvoid) |
| return (TARGET_POWERPC64) ? CODE_FOR_htm_mfspr_di : CODE_FOR_htm_mfspr_si; |
| else |
| return (TARGET_POWERPC64) ? CODE_FOR_htm_mtspr_di : CODE_FOR_htm_mtspr_si; |
| } |
| |
| /* Expand the HTM builtin in EXP and store the result in TARGET. |
| Store true in *EXPANDEDP if we found a builtin to expand. */ |
| static rtx |
| htm_expand_builtin (tree exp, rtx target, bool * expandedp) |
| { |
| tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); |
| bool nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; |
| enum rs6000_builtins fcode = (enum rs6000_builtins) DECL_FUNCTION_CODE (fndecl); |
| const struct builtin_description *d; |
| size_t i; |
| |
| *expandedp = true; |
| |
| if (!TARGET_POWERPC64 |
| && (fcode == HTM_BUILTIN_TABORTDC |
| || fcode == HTM_BUILTIN_TABORTDCI)) |
| { |
| size_t uns_fcode = (size_t)fcode; |
| const char *name = rs6000_builtin_info[uns_fcode].name; |
| error ("builtin %s is only valid in 64-bit mode", name); |
| return const0_rtx; |
| } |
| |
| /* Expand the HTM builtins. */ |
| d = bdesc_htm; |
| for (i = 0; i < ARRAY_SIZE (bdesc_htm); i++, d++) |
| if (d->code == fcode) |
| { |
| rtx op[MAX_HTM_OPERANDS], pat; |
| int nopnds = 0; |
| tree arg; |
| call_expr_arg_iterator iter; |
| unsigned attr = rs6000_builtin_info[fcode].attr; |
| enum insn_code icode = d->icode; |
| const struct insn_operand_data *insn_op; |
| bool uses_spr = (attr & RS6000_BTC_SPR); |
| rtx cr = NULL_RTX; |
| |
| if (uses_spr) |
| icode = rs6000_htm_spr_icode (nonvoid); |
| insn_op = &insn_data[icode].operand[0]; |
| |
| if (nonvoid) |
| { |
| machine_mode tmode = (uses_spr) ? insn_op->mode : SImode; |
| if (!target |
| || GET_MODE (target) != tmode |
| || (uses_spr && !(*insn_op->predicate) (target, tmode))) |
| target = gen_reg_rtx (tmode); |
| if (uses_spr) |
| op[nopnds++] = target; |
| } |
| |
| FOR_EACH_CALL_EXPR_ARG (arg, iter, exp) |
| { |
| if (arg == error_mark_node || nopnds >= MAX_HTM_OPERANDS) |
| return const0_rtx; |
| |
| insn_op = &insn_data[icode].operand[nopnds]; |
| |
| op[nopnds] = expand_normal (arg); |
| |
| if (!(*insn_op->predicate) (op[nopnds], insn_op->mode)) |
| { |
| if (!strcmp (insn_op->constraint, "n")) |
| { |
| int arg_num = (nonvoid) ? nopnds : nopnds + 1; |
| if (!CONST_INT_P (op[nopnds])) |
| error ("argument %d must be an unsigned literal", arg_num); |
| else |
| error ("argument %d is an unsigned literal that is " |
| "out of range", arg_num); |
| return const0_rtx; |
| } |
| op[nopnds] = copy_to_mode_reg (insn_op->mode, op[nopnds]); |
| } |
| |
| nopnds++; |
| } |
| |
| /* Handle the builtins for extended mnemonics. These accept |
| no arguments, but map to builtins that take arguments. */ |
| switch (fcode) |
| { |
| case HTM_BUILTIN_TENDALL: /* Alias for: tend. 1 */ |
| case HTM_BUILTIN_TRESUME: /* Alias for: tsr. 1 */ |
| op[nopnds++] = GEN_INT (1); |
| #ifdef ENABLE_CHECKING |
| attr |= RS6000_BTC_UNARY; |
| #endif |
| break; |
| case HTM_BUILTIN_TSUSPEND: /* Alias for: tsr. 0 */ |
| op[nopnds++] = GEN_INT (0); |
| #ifdef ENABLE_CHECKING |
| attr |= RS6000_BTC_UNARY; |
| #endif |
| break; |
| default: |
| break; |
| } |
| |
| /* If this builtin accesses SPRs, then pass in the appropriate |
| SPR number and SPR regno as the last two operands. */ |
| if (uses_spr) |
| { |
| machine_mode mode = (TARGET_POWERPC64) ? DImode : SImode; |
| op[nopnds++] = gen_rtx_CONST_INT (mode, htm_spr_num (fcode)); |
| op[nopnds++] = gen_rtx_REG (mode, htm_spr_regno (fcode)); |
| } |
| /* If this builtin accesses a CR, then pass in a scratch |
| CR as the last operand. */ |
| else if (attr & RS6000_BTC_CR) |
| { cr = gen_reg_rtx (CCmode); |
| op[nopnds++] = cr; |
| } |
| |
| #ifdef ENABLE_CHECKING |
| int expected_nopnds = 0; |
| if ((attr & RS6000_BTC_TYPE_MASK) == RS6000_BTC_UNARY) |
| expected_nopnds = 1; |
| else if ((attr & RS6000_BTC_TYPE_MASK) == RS6000_BTC_BINARY) |
| expected_nopnds = 2; |
| else if ((attr & RS6000_BTC_TYPE_MASK) == RS6000_BTC_TERNARY) |
| expected_nopnds = 3; |
| if (!(attr & RS6000_BTC_VOID)) |
| expected_nopnds += 1; |
| if (uses_spr) |
| expected_nopnds += 2; |
| |
| gcc_assert (nopnds == expected_nopnds && nopnds <= MAX_HTM_OPERANDS); |
| #endif |
| |
| switch (nopnds) |
| { |
| case 1: |
| pat = GEN_FCN (icode) (op[0]); |
| break; |
| case 2: |
| pat = GEN_FCN (icode) (op[0], op[1]); |
| break; |
| case 3: |
| pat = GEN_FCN (icode) (op[0], op[1], op[2]); |
| break; |
| case 4: |
| pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| if (!pat) |
| return NULL_RTX; |
| emit_insn (pat); |
| |
| if (attr & RS6000_BTC_CR) |
| { |
| if (fcode == HTM_BUILTIN_TBEGIN) |
| { |
| /* Emit code to set TARGET to true or false depending on |
| whether the tbegin. instruction successfully or failed |
| to start a transaction. We do this by placing the 1's |
| complement of CR's EQ bit into TARGET. */ |
| rtx scratch = gen_reg_rtx (SImode); |
| emit_insn (gen_rtx_SET (VOIDmode, scratch, |
| gen_rtx_EQ (SImode, cr, |
| const0_rtx))); |
| emit_insn (gen_rtx_SET (VOIDmode, target, |
| gen_rtx_XOR (SImode, scratch, |
| GEN_INT (1)))); |
| } |
| else |
| { |
| /* Emit code to copy the 4-bit condition register field |
| CR into the least significant end of register TARGET. */ |
| rtx scratch1 = gen_reg_rtx (SImode); |
| rtx scratch2 = gen_reg_rtx (SImode); |
| rtx subreg = simplify_gen_subreg (CCmode, scratch1, SImode, 0); |
| emit_insn (gen_movcc (subreg, cr)); |
| emit_insn (gen_lshrsi3 (scratch2, scratch1, GEN_INT (28))); |
| emit_insn (gen_andsi3 (target, scratch2, GEN_INT (0xf))); |
| } |
| } |
| |
| if (nonvoid) |
| return target; |
| return const0_rtx; |
| } |
| |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| static rtx |
| rs6000_expand_ternop_builtin (enum insn_code icode, tree exp, rtx target) |
| { |
| rtx pat; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| tree arg2 = CALL_EXPR_ARG (exp, 2); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| rtx op2 = expand_normal (arg2); |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| machine_mode mode1 = insn_data[icode].operand[2].mode; |
| machine_mode mode2 = insn_data[icode].operand[3].mode; |
| |
| 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; |
| |
| /* Check and prepare argument depending on the instruction code. |
| |
| Note that a switch statement instead of the sequence of tests |
| would be incorrect as many of the CODE_FOR values could be |
| CODE_FOR_nothing and that would yield multiple alternatives |
| with identical values. We'd never reach here at runtime in |
| this case. */ |
| 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 (tmode); |
| } |
| } |
| else if (icode == CODE_FOR_vsx_xxpermdi_v2df |
| || icode == CODE_FOR_vsx_xxpermdi_v2di |
| || icode == CODE_FOR_vsx_xxpermdi_v2df_be |
| || icode == CODE_FOR_vsx_xxpermdi_v2di_be |
| || icode == CODE_FOR_vsx_xxpermdi_v1ti |
| || icode == CODE_FOR_vsx_xxpermdi_v4sf |
| || icode == CODE_FOR_vsx_xxpermdi_v4si |
| || icode == CODE_FOR_vsx_xxpermdi_v8hi |
| || icode == CODE_FOR_vsx_xxpermdi_v16qi |
| || icode == CODE_FOR_vsx_xxsldwi_v16qi |
| || icode == CODE_FOR_vsx_xxsldwi_v8hi |
| || icode == CODE_FOR_vsx_xxsldwi_v4si |
| || icode == CODE_FOR_vsx_xxsldwi_v4sf |
| || icode == CODE_FOR_vsx_xxsldwi_v2di |
| || icode == CODE_FOR_vsx_xxsldwi_v2df) |
| { |
| /* Only allow 2-bit unsigned literals. */ |
| STRIP_NOPS (arg2); |
| if (TREE_CODE (arg2) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg2) & ~0x3) |
| { |
| error ("argument 3 must be a 2-bit unsigned literal"); |
| return CONST0_RTX (tmode); |
| } |
| } |
| else if (icode == CODE_FOR_vsx_set_v2df |
| || icode == CODE_FOR_vsx_set_v2di |
| || icode == CODE_FOR_bcdadd |
| || icode == CODE_FOR_bcdadd_lt |
| || icode == CODE_FOR_bcdadd_eq |
| || icode == CODE_FOR_bcdadd_gt |
| || icode == CODE_FOR_bcdsub |
| || icode == CODE_FOR_bcdsub_lt |
| || icode == CODE_FOR_bcdsub_eq |
| || icode == CODE_FOR_bcdsub_gt) |
| { |
| /* Only allow 1-bit unsigned literals. */ |
| STRIP_NOPS (arg2); |
| if (TREE_CODE (arg2) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg2) & ~0x1) |
| { |
| error ("argument 3 must be a 1-bit unsigned literal"); |
| return CONST0_RTX (tmode); |
| } |
| } |
| else if (icode == CODE_FOR_dfp_ddedpd_dd |
| || icode == CODE_FOR_dfp_ddedpd_td) |
| { |
| /* Only allow 2-bit unsigned literals where the value is 0 or 2. */ |
| STRIP_NOPS (arg0); |
| if (TREE_CODE (arg0) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg2) & ~0x3) |
| { |
| error ("argument 1 must be 0 or 2"); |
| return CONST0_RTX (tmode); |
| } |
| } |
| else if (icode == CODE_FOR_dfp_denbcd_dd |
| || icode == CODE_FOR_dfp_denbcd_td) |
| { |
| /* Only allow 1-bit unsigned literals. */ |
| STRIP_NOPS (arg0); |
| if (TREE_CODE (arg0) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg0) & ~0x1) |
| { |
| error ("argument 1 must be a 1-bit unsigned literal"); |
| return CONST0_RTX (tmode); |
| } |
| } |
| else if (icode == CODE_FOR_dfp_dscli_dd |
| || icode == CODE_FOR_dfp_dscli_td |
| || icode == CODE_FOR_dfp_dscri_dd |
| || icode == CODE_FOR_dfp_dscri_td) |
| { |
| /* Only allow 6-bit unsigned literals. */ |
| STRIP_NOPS (arg1); |
| if (TREE_CODE (arg1) != INTEGER_CST |
| || TREE_INT_CST_LOW (arg1) & ~0x3f) |
| { |
| error ("argument 2 must be a 6-bit unsigned literal"); |
| return CONST0_RTX (tmode); |
| } |
| } |
| else if (icode == CODE_FOR_crypto_vshasigmaw |
| || icode == CODE_FOR_crypto_vshasigmad) |
| { |
| /* Check whether the 2nd and 3rd arguments are integer constants and in |
| range and prepare arguments. */ |
| STRIP_NOPS (arg1); |
| if (TREE_CODE (arg1) != INTEGER_CST || wi::geu_p (arg1, 2)) |
| { |
| error ("argument 2 must be 0 or 1"); |
| return CONST0_RTX (tmode); |
| } |
| |
| STRIP_NOPS (arg2); |
| if (TREE_CODE (arg2) != INTEGER_CST || wi::geu_p (arg2, 16)) |
| { |
| error ("argument 3 must be in the range 0..15"); |
| return CONST0_RTX (tmode); |
| } |
| } |
| |
| 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); |
| |
| if (TARGET_PAIRED_FLOAT && icode == CODE_FOR_selv2sf4) |
| pat = GEN_FCN (icode) (target, op0, op1, op2, CONST0_RTX (SFmode)); |
| else |
| 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 (CALL_EXPR_FN (exp), 0); |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| tree arg0; |
| machine_mode tmode, mode0; |
| rtx pat, op0; |
| enum insn_code icode; |
| |
| switch (fcode) |
| { |
| case ALTIVEC_BUILTIN_LD_INTERNAL_16qi: |
| icode = CODE_FOR_vector_altivec_load_v16qi; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_8hi: |
| icode = CODE_FOR_vector_altivec_load_v8hi; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_4si: |
| icode = CODE_FOR_vector_altivec_load_v4si; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_4sf: |
| icode = CODE_FOR_vector_altivec_load_v4sf; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_2df: |
| icode = CODE_FOR_vector_altivec_load_v2df; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_2di: |
| icode = CODE_FOR_vector_altivec_load_v2di; |
| break; |
| case ALTIVEC_BUILTIN_LD_INTERNAL_1ti: |
| icode = CODE_FOR_vector_altivec_load_v1ti; |
| break; |
| default: |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| *expandedp = true; |
| |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| 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 (CALL_EXPR_FN (exp), 0); |
| unsigned int fcode = DECL_FUNCTION_CODE (fndecl); |
| tree arg0, arg1; |
| machine_mode mode0, mode1; |
| rtx pat, op0, op1; |
| enum insn_code icode; |
| |
| switch (fcode) |
| { |
| case ALTIVEC_BUILTIN_ST_INTERNAL_16qi: |
| icode = CODE_FOR_vector_altivec_store_v16qi; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_8hi: |
| icode = CODE_FOR_vector_altivec_store_v8hi; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_4si: |
| icode = CODE_FOR_vector_altivec_store_v4si; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_4sf: |
| icode = CODE_FOR_vector_altivec_store_v4sf; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_2df: |
| icode = CODE_FOR_vector_altivec_store_v2df; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_2di: |
| icode = CODE_FOR_vector_altivec_store_v2di; |
| break; |
| case ALTIVEC_BUILTIN_ST_INTERNAL_1ti: |
| icode = CODE_FOR_vector_altivec_store_v1ti; |
| break; |
| default: |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| 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 (CALL_EXPR_FN (exp), 0); |
| enum rs6000_builtins fcode = (enum rs6000_builtins) DECL_FUNCTION_CODE (fndecl); |
| tree arg0, arg1, arg2; |
| machine_mode mode0, mode1; |
| rtx pat, op0, op1, op2; |
| const struct builtin_description *d; |
| size_t i; |
| |
| *expandedp = false; |
| |
| /* Handle DST variants. */ |
| d = bdesc_dst; |
| for (i = 0; i < ARRAY_SIZE (bdesc_dst); i++, d++) |
| if (d->code == fcode) |
| { |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| arg2 = CALL_EXPR_ARG (exp, 2); |
| 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; |
| |
| /* 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 exp, rtx target) |
| { |
| machine_mode tmode = TYPE_MODE (type); |
| machine_mode inner_mode = GET_MODE_INNER (tmode); |
| int i, n_elt = GET_MODE_NUNITS (tmode); |
| |
| gcc_assert (VECTOR_MODE_P (tmode)); |
| gcc_assert (n_elt == call_expr_nargs (exp)); |
| |
| if (!target || !register_operand (target, tmode)) |
| target = gen_reg_rtx (tmode); |
| |
| /* If we have a vector compromised of a single element, such as V1TImode, do |
| the initialization directly. */ |
| if (n_elt == 1 && GET_MODE_SIZE (tmode) == GET_MODE_SIZE (inner_mode)) |
| { |
| rtx x = expand_normal (CALL_EXPR_ARG (exp, 0)); |
| emit_move_insn (target, gen_lowpart (tmode, x)); |
| } |
| else |
| { |
| rtvec v = rtvec_alloc (n_elt); |
| |
| for (i = 0; i < n_elt; ++i) |
| { |
| rtx x = expand_normal (CALL_EXPR_ARG (exp, i)); |
| RTVEC_ELT (v, i) = gen_lowpart (inner_mode, x); |
| } |
| |
| 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 (!tree_fits_uhwi_p (arg) |
| || (elt = tree_to_uhwi (arg), elt > max)) |
| { |
| error ("selector must be an integer constant in the range 0..%wi", max); |
| return 0; |
| } |
| |
| return elt; |
| } |
| |
| /* Expand vec_set builtin. */ |
| static rtx |
| altivec_expand_vec_set_builtin (tree exp) |
| { |
| machine_mode tmode, mode1; |
| tree arg0, arg1, arg2; |
| int elt; |
| rtx op0, op1; |
| |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| arg2 = CALL_EXPR_ARG (exp, 2); |
| |
| 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, EXPAND_NORMAL); |
| op1 = expand_expr (arg1, NULL_RTX, mode1, EXPAND_NORMAL); |
| 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 exp, rtx target) |
| { |
| machine_mode tmode, mode0; |
| tree arg0, arg1; |
| int elt; |
| rtx op0; |
| |
| arg0 = CALL_EXPR_ARG (exp, 0); |
| arg1 = CALL_EXPR_ARG (exp, 1); |
| |
| 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) |
| { |
| const struct builtin_description *d; |
| size_t i; |
| enum insn_code icode; |
| tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); |
| tree arg0; |
| rtx op0, pat; |
| machine_mode tmode, mode0; |
| enum rs6000_builtins fcode |
| = (enum rs6000_builtins) DECL_FUNCTION_CODE (fndecl); |
| |
| if (rs6000_overloaded_builtin_p (fcode)) |
| { |
| *expandedp = true; |
| error ("unresolved overload for Altivec builtin %qF", fndecl); |
| |
| /* Given it is invalid, just generate a normal call. */ |
| return expand_call (exp, target, false); |
| } |
| |
| 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_V2DF: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvx_v2df, exp); |
| case ALTIVEC_BUILTIN_STVX_V2DI: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvx_v2di, exp); |
| case ALTIVEC_BUILTIN_STVX_V4SF: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvx_v4sf, exp); |
| case ALTIVEC_BUILTIN_STVX: |
| case ALTIVEC_BUILTIN_STVX_V4SI: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvx_v4si, exp); |
| case ALTIVEC_BUILTIN_STVX_V8HI: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvx_v8hi, exp); |
| case ALTIVEC_BUILTIN_STVX_V16QI: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvx_v16qi, exp); |
| case ALTIVEC_BUILTIN_STVEBX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvebx, exp); |
| case ALTIVEC_BUILTIN_STVEHX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvehx, exp); |
| case ALTIVEC_BUILTIN_STVEWX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvewx, exp); |
| case ALTIVEC_BUILTIN_STVXL_V2DF: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvxl_v2df, exp); |
| case ALTIVEC_BUILTIN_STVXL_V2DI: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvxl_v2di, exp); |
| case ALTIVEC_BUILTIN_STVXL_V4SF: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvxl_v4sf, exp); |
| case ALTIVEC_BUILTIN_STVXL: |
| case ALTIVEC_BUILTIN_STVXL_V4SI: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvxl_v4si, exp); |
| case ALTIVEC_BUILTIN_STVXL_V8HI: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvxl_v8hi, exp); |
| case ALTIVEC_BUILTIN_STVXL_V16QI: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvxl_v16qi, exp); |
| |
| case ALTIVEC_BUILTIN_STVLX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvlx, exp); |
| case ALTIVEC_BUILTIN_STVLXL: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvlxl, exp); |
| case ALTIVEC_BUILTIN_STVRX: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvrx, exp); |
| case ALTIVEC_BUILTIN_STVRXL: |
| return altivec_expand_stv_builtin (CODE_FOR_altivec_stvrxl, exp); |
| |
| case VSX_BUILTIN_STXVD2X_V1TI: |
| return altivec_expand_stv_builtin (CODE_FOR_vsx_store_v1ti, exp); |
| case VSX_BUILTIN_STXVD2X_V2DF: |
| return altivec_expand_stv_builtin (CODE_FOR_vsx_store_v2df, exp); |
| case VSX_BUILTIN_STXVD2X_V2DI: |
| return altivec_expand_stv_builtin (CODE_FOR_vsx_store_v2di, exp); |
| case VSX_BUILTIN_STXVW4X_V4SF: |
| return altivec_expand_stv_builtin (CODE_FOR_vsx_store_v4sf, exp); |
| case VSX_BUILTIN_STXVW4X_V4SI: |
| return altivec_expand_stv_builtin (CODE_FOR_vsx_store_v4si, exp); |
| case VSX_BUILTIN_STXVW4X_V8HI: |
| return altivec_expand_stv_builtin (CODE_FOR_vsx_store_v8hi, exp); |
| case VSX_BUILTIN_STXVW4X_V16QI: |
| return altivec_expand_stv_builtin (CODE_FOR_vsx_store_v16qi, exp); |
| |
| 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 = CALL_EXPR_ARG (exp, 0); |
| 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 = CALL_EXPR_ARG (exp, 0); |
| 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: |
| case VSX_BUILTIN_VEC_INIT_V2DF: |
| case VSX_BUILTIN_VEC_INIT_V2DI: |
| case VSX_BUILTIN_VEC_INIT_V1TI: |
| return altivec_expand_vec_init_builtin (TREE_TYPE (exp), exp, 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: |
| case VSX_BUILTIN_VEC_SET_V2DF: |
| case VSX_BUILTIN_VEC_SET_V2DI: |
| case VSX_BUILTIN_VEC_SET_V1TI: |
| return altivec_expand_vec_set_builtin (exp); |
| |
| case ALTIVEC_BUILTIN_VEC_EXT_V4SI: |
| case ALTIVEC_BUILTIN_VEC_EXT_V8HI: |
| case ALTIVEC_BUILTIN_VEC_EXT_V16QI: |
| case ALTIVEC_BUILTIN_VEC_EXT_V4SF: |
| case VSX_BUILTIN_VEC_EXT_V2DF: |
| case VSX_BUILTIN_VEC_EXT_V2DI: |
| case VSX_BUILTIN_VEC_EXT_V1TI: |
| return altivec_expand_vec_ext_builtin (exp, target); |
| |
| default: |
| break; |
| /* Fall through. */ |
| } |
| |
| /* Expand abs* operations. */ |
| d = bdesc_abs; |
| for (i = 0; i < ARRAY_SIZE (bdesc_abs); i++, d++) |
| if (d->code == fcode) |
| return altivec_expand_abs_builtin (d->icode, exp, target); |
| |
| /* Expand the AltiVec predicates. */ |
| d = bdesc_altivec_preds; |
| for (i = 0; i < ARRAY_SIZE (bdesc_altivec_preds); i++, d++) |
| if (d->code == fcode) |
| return altivec_expand_predicate_builtin (d->icode, exp, target); |
| |
| /* LV* are funky. We initialized them differently. */ |
| switch (fcode) |
| { |
| case ALTIVEC_BUILTIN_LVSL: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsl, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVSR: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsr, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVEBX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvebx, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVEHX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvehx, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVEWX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvewx, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVXL_V2DF: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl_v2df, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVXL_V2DI: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl_v2di, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVXL_V4SF: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl_v4sf, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVXL: |
| case ALTIVEC_BUILTIN_LVXL_V4SI: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl_v4si, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVXL_V8HI: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl_v8hi, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVXL_V16QI: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl_v16qi, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVX_V2DF: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx_v2df, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVX_V2DI: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx_v2di, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVX_V4SF: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx_v4sf, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVX: |
| case ALTIVEC_BUILTIN_LVX_V4SI: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx_v4si, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVX_V8HI: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx_v8hi, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVX_V16QI: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx_v16qi, |
| exp, target, false); |
| case ALTIVEC_BUILTIN_LVLX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvlx, |
| exp, target, true); |
| case ALTIVEC_BUILTIN_LVLXL: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvlxl, |
| exp, target, true); |
| case ALTIVEC_BUILTIN_LVRX: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvrx, |
| exp, target, true); |
| case ALTIVEC_BUILTIN_LVRXL: |
| return altivec_expand_lv_builtin (CODE_FOR_altivec_lvrxl, |
| exp, target, true); |
| case VSX_BUILTIN_LXVD2X_V1TI: |
| return altivec_expand_lv_builtin (CODE_FOR_vsx_load_v1ti, |
| exp, target, false); |
| case VSX_BUILTIN_LXVD2X_V2DF: |
| return altivec_expand_lv_builtin (CODE_FOR_vsx_load_v2df, |
| exp, target, false); |
| case VSX_BUILTIN_LXVD2X_V2DI: |
| return altivec_expand_lv_builtin (CODE_FOR_vsx_load_v2di, |
| exp, target, false); |
| case VSX_BUILTIN_LXVW4X_V4SF: |
| return altivec_expand_lv_builtin (CODE_FOR_vsx_load_v4sf, |
| exp, target, false); |
| case VSX_BUILTIN_LXVW4X_V4SI: |
| return altivec_expand_lv_builtin (CODE_FOR_vsx_load_v4si, |
| exp, target, false); |
| case VSX_BUILTIN_LXVW4X_V8HI: |
| return altivec_expand_lv_builtin (CODE_FOR_vsx_load_v8hi, |
| exp, target, false); |
| case VSX_BUILTIN_LXVW4X_V16QI: |
| return altivec_expand_lv_builtin (CODE_FOR_vsx_load_v16qi, |
| exp, target, false); |
| break; |
| default: |
| break; |
| /* Fall through. */ |
| } |
| |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| /* Expand the builtin in EXP and store the result in TARGET. Store |
| true in *EXPANDEDP if we found a builtin to expand. */ |
| static rtx |
| paired_expand_builtin (tree exp, rtx target, bool * expandedp) |
| { |
| tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); |
| enum rs6000_builtins fcode = (enum rs6000_builtins) DECL_FUNCTION_CODE (fndecl); |
| const struct builtin_description *d; |
| size_t i; |
| |
| *expandedp = true; |
| |
| switch (fcode) |
| { |
| case PAIRED_BUILTIN_STX: |
| return paired_expand_stv_builtin (CODE_FOR_paired_stx, exp); |
| case PAIRED_BUILTIN_LX: |
| return paired_expand_lv_builtin (CODE_FOR_paired_lx, exp, target); |
| default: |
| break; |
| /* Fall through. */ |
| } |
| |
| /* Expand the paired predicates. */ |
| d = bdesc_paired_preds; |
| for (i = 0; i < ARRAY_SIZE (bdesc_paired_preds); i++, d++) |
| if (d->code == fcode) |
| return paired_expand_predicate_builtin (d->icode, exp, target); |
| |
| *expandedp = false; |
| return NULL_RTX; |
| } |
| |
| /* Binops that need to be initialized manually, but can be expanded |
| automagically by rs6000_expand_binop_builtin. */ |
| static const struct builtin_description bdesc_2arg_spe[] = |
| { |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlddx, "__builtin_spe_evlddx", SPE_BUILTIN_EVLDDX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evldwx, "__builtin_spe_evldwx", SPE_BUILTIN_EVLDWX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evldhx, "__builtin_spe_evldhx", SPE_BUILTIN_EVLDHX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwhex, "__builtin_spe_evlwhex", SPE_BUILTIN_EVLWHEX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwhoux, "__builtin_spe_evlwhoux", SPE_BUILTIN_EVLWHOUX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwhosx, "__builtin_spe_evlwhosx", SPE_BUILTIN_EVLWHOSX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwwsplatx, "__builtin_spe_evlwwsplatx", SPE_BUILTIN_EVLWWSPLATX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwhsplatx, "__builtin_spe_evlwhsplatx", SPE_BUILTIN_EVLWHSPLATX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlhhesplatx, "__builtin_spe_evlhhesplatx", SPE_BUILTIN_EVLHHESPLATX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlhhousplatx, "__builtin_spe_evlhhousplatx", SPE_BUILTIN_EVLHHOUSPLATX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlhhossplatx, "__builtin_spe_evlhhossplatx", SPE_BUILTIN_EVLHHOSSPLATX }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evldd, "__builtin_spe_evldd", SPE_BUILTIN_EVLDD }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evldw, "__builtin_spe_evldw", SPE_BUILTIN_EVLDW }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evldh, "__builtin_spe_evldh", SPE_BUILTIN_EVLDH }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwhe, "__builtin_spe_evlwhe", SPE_BUILTIN_EVLWHE }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwhou, "__builtin_spe_evlwhou", SPE_BUILTIN_EVLWHOU }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwhos, "__builtin_spe_evlwhos", SPE_BUILTIN_EVLWHOS }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwwsplat, "__builtin_spe_evlwwsplat", SPE_BUILTIN_EVLWWSPLAT }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlwhsplat, "__builtin_spe_evlwhsplat", SPE_BUILTIN_EVLWHSPLAT }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlhhesplat, "__builtin_spe_evlhhesplat", SPE_BUILTIN_EVLHHESPLAT }, |
| { RS6000_BTM_SPE, CODE_FOR_spe_evlhhousplat, "__builtin_spe_evlhhousplat", SPE_BUILTIN_EVLHHOUSPLAT }, |
| { RS6000_BTM_SPE, 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 (CALL_EXPR_FN (exp), 0); |
| tree arg1, arg0; |
| enum rs6000_builtins fcode = (enum rs6000_builtins) DECL_FUNCTION_CODE (fndecl); |
| enum insn_code icode; |
| machine_mode tmode, mode0; |
| rtx pat, op0; |
| const 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 = CALL_EXPR_ARG (exp, 2); |
| 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, |
| exp, target); |
| case SPE_BUILTIN_EVSPLATI: |
| return rs6000_expand_unop_builtin (CODE_FOR_spe_evsplati, |
| exp, target); |
| default: |
| break; |
| } |
| |
| d = 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, exp, target); |
| |
| d = 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, exp, target); |
| |
| d = 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, exp, target); |
| |
| switch (fcode) |
| { |
| case SPE_BUILTIN_EVSTDDX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstddx, exp); |
| case SPE_BUILTIN_EVSTDHX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdhx, exp); |
| case SPE_BUILTIN_EVSTDWX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdwx, exp); |
| case SPE_BUILTIN_EVSTWHEX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwhex, exp); |
| case SPE_BUILTIN_EVSTWHOX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwhox, exp); |
| case SPE_BUILTIN_EVSTWWEX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwwex, exp); |
| case SPE_BUILTIN_EVSTWWOX: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwwox, exp); |
| case SPE_BUILTIN_EVSTDD: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdd, exp); |
| case SPE_BUILTIN_EVSTDH: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdh, exp); |
| case SPE_BUILTIN_EVSTDW: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstdw, exp); |
| case SPE_BUILTIN_EVSTWHE: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwhe, exp); |
| case SPE_BUILTIN_EVSTWHO: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwho, exp); |
| case SPE_BUILTIN_EVSTWWE: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwwe, exp); |
| case SPE_BUILTIN_EVSTWWO: |
| return spe_expand_stv_builtin (CODE_FOR_spe_evstwwo, exp); |
| 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 = CALL_EXPR_ARG (exp, 0); |
| 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 |
| paired_expand_predicate_builtin (enum insn_code icode, tree exp, rtx target) |
| { |
| rtx pat, scratch, tmp; |
| tree form = CALL_EXPR_ARG (exp, 0); |
| tree arg0 = CALL_EXPR_ARG (exp, 1); |
| tree arg1 = CALL_EXPR_ARG (exp, 2); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| 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_paired_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 (CCFPmode); |
| |
| pat = GEN_FCN (icode) (scratch, op0, op1); |
| if (!pat) |
| return const0_rtx; |
| |
| emit_insn (pat); |
| |
| switch (form_int) |
| { |
| /* LT bit. */ |
| case 0: |
| code = LT; |
| break; |
| /* GT bit. */ |
| case 1: |
| code = GT; |
| break; |
| /* EQ bit. */ |
| case 2: |
| code = EQ; |
| break; |
| /* UN bit. */ |
| case 3: |
| emit_insn (gen_move_from_CR_ov_bit (target, scratch)); |
| return target; |
| default: |
| error ("argument 1 of __builtin_paired_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; |
| } |
| |
| static rtx |
| spe_expand_predicate_builtin (enum insn_code icode, tree exp, rtx target) |
| { |
| rtx pat, scratch, tmp; |
| tree form = CALL_EXPR_ARG (exp, 0); |
| tree arg0 = CALL_EXPR_ARG (exp, 1); |
| tree arg1 = CALL_EXPR_ARG (exp, 2); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| 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 exp, rtx target) |
| { |
| rtx pat, scratch; |
| tree arg0 = CALL_EXPR_ARG (exp, 0); |
| tree arg1 = CALL_EXPR_ARG (exp, 1); |
| tree arg2 = CALL_EXPR_ARG (exp, 2); |
| tree arg3 = CALL_EXPR_ARG (exp, 3); |
| rtx op0 = expand_normal (arg0); |
| rtx op1 = expand_normal (arg1); |
| rtx op2 = expand_normal (arg2); |
| rtx op3 = expand_normal (arg3); |
| machine_mode mode0 = insn_data[icode].operand[1].mode; |
| 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; |
| } |
| |
| /* Raise an error message for a builtin function that is called without the |
| appropriate target options being set. */ |
| |
| static void |
| rs6000_invalid_builtin (enum rs6000_builtins fncode) |
| { |
| size_t uns_fncode = (size_t)fncode; |
| const char *name = rs6000_builtin_info[uns_fncode].name; |
| HOST_WIDE_INT fnmask = rs6000_builtin_info[uns_fncode].mask; |
| |
| gcc_assert (name != NULL); |
| if ((fnmask & RS6000_BTM_CELL) != 0) |
| error ("Builtin function %s is only valid for the cell processor", name); |
| else if ((fnmask & RS6000_BTM_VSX) != 0) |
| error ("Builtin function %s requires the -mvsx option", name); |
| else if ((fnmask & RS6000_BTM_HTM) != 0) |
| error ("Builtin function %s requires the -mhtm option", name); |
| else if ((fnmask & RS6000_BTM_ALTIVEC) != 0) |
| error ("Builtin function %s requires the -maltivec option", name); |
| else if ((fnmask & RS6000_BTM_PAIRED) != 0) |
| error ("Builtin function %s requires the -mpaired option", name); |
| else if ((fnmask & RS6000_BTM_SPE) != 0) |
| error ("Builtin function %s requires the -mspe option", name); |
| else if ((fnmask & (RS6000_BTM_DFP | RS6000_BTM_P8_VECTOR)) |
| == (RS6000_BTM_DFP | RS6000_BTM_P8_VECTOR)) |
| error ("Builtin function %s requires the -mhard-dfp and" |
| " -mpower8-vector options", name); |
| else if ((fnmask & RS6000_BTM_DFP) != 0) |
| error ("Builtin function %s requires the -mhard-dfp option", name); |
| else if ((fnmask & RS6000_BTM_P8_VECTOR) != 0) |
| error ("Builtin function %s requires the -mpower8-vector option", name); |
| else if ((fnmask & (RS6000_BTM_HARD_FLOAT | RS6000_BTM_LDBL128)) |
| == (RS6000_BTM_HARD_FLOAT | RS6000_BTM_LDBL128)) |
| error ("Builtin function %s requires the -mhard-float and" |
| " -mlong-double-128 options", name); |
| else if ((fnmask & RS6000_BTM_HARD_FLOAT) != 0) |
| error ("Builtin function %s requires the -mhard-float option", name); |
| else |
| error ("Builtin function %s is not supported with the current options", |
| name); |
| } |
| |
| /* 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, |
| machine_mode mode ATTRIBUTE_UNUSED, |
| int ignore ATTRIBUTE_UNUSED) |
| { |
| tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); |
| enum rs6000_builtins fcode |
| = (enum rs6000_builtins)DECL_FUNCTION_CODE (fndecl); |
| size_t uns_fcode = (size_t)fcode; |
| const struct builtin_description *d; |
| size_t i; |
| rtx ret; |
| bool success; |
| HOST_WIDE_INT mask = rs6000_builtin_info[uns_fcode].mask; |
| bool func_valid_p = ((rs6000_builtin_mask & mask) == mask); |
| |
| if (TARGET_DEBUG_BUILTIN) |
| { |
| enum insn_code icode = rs6000_builtin_info[uns_fcode].icode; |
| const char *name1 = rs6000_builtin_info[uns_fcode].name; |
| const char *name2 = ((icode != CODE_FOR_nothing) |
| ? get_insn_name ((int)icode) |
| : "nothing"); |
| const char *name3; |
| |
| switch (rs6000_builtin_info[uns_fcode].attr & RS6000_BTC_TYPE_MASK) |
| { |
| default: name3 = "unknown"; break; |
| case RS6000_BTC_SPECIAL: name3 = "special"; break; |
| case RS6000_BTC_UNARY: name3 = "unary"; break; |
| case RS6000_BTC_BINARY: name3 = "binary"; break; |
| case RS6000_BTC_TERNARY: name3 = "ternary"; break; |
| case RS6000_BTC_PREDICATE: name3 = "predicate"; break; |
| case RS6000_BTC_ABS: name3 = "abs"; break; |
| case RS6000_BTC_EVSEL: name3 = "evsel"; break; |
| case RS6000_BTC_DST: name3 = "dst"; break; |
| } |
| |
| |
| fprintf (stderr, |
| "rs6000_expand_builtin, %s (%d), insn = %s (%d), type=%s%s\n", |
| (name1) ? name1 : "---", fcode, |
| (name2) ? name2 : "---", (int)icode, |
| name3, |
| func_valid_p ? "" : ", not valid"); |
| } |
| |
| if (!func_valid_p) |
| { |
| rs6000_invalid_builtin (fcode); |
| |
| /* Given it is invalid, just generate a normal call. */ |
| return expand_call (exp, target, ignore); |
| } |
| |
| switch (fcode) |
| { |
| case RS6000_BUILTIN_RECIP: |
| return rs6000_expand_binop_builtin (CODE_FOR_recipdf3, exp, target); |
| |
| case RS6000_BUILTIN_RECIPF: |
| return rs6000_expand_binop_builtin (CODE_FOR_recipsf3, exp, target); |
| |
| case RS6000_BUILTIN_RSQRTF: |
| return rs6000_expand_unop_builtin (CODE_FOR_rsqrtsf2, exp, target); |
| |
| case RS6000_BUILTIN_RSQRT: |
| return rs6000_expand_unop_builtin (CODE_FOR_rsqrtdf2, exp, target); |
| |
| case POWER7_BUILTIN_BPERMD: |
| return rs6000_expand_binop_builtin (((TARGET_64BIT) |
| ? CODE_FOR_bpermd_di |
| : CODE_FOR_bpermd_si), exp, target); |
| |
| case RS6000_BUILTIN_GET_TB: |
| return rs6000_expand_zeroop_builtin (CODE_FOR_rs6000_get_timebase, |
| target); |
| |
| case RS6000_BUILTIN_MFTB: |
| return rs6000_expand_zeroop_builtin (((TARGET_64BIT) |
| ? CODE_FOR_rs6000_mftb_di |
| : CODE_FOR_rs6000_mftb_si), |
| target); |
| |
| case RS6000_BUILTIN_MFFS: |
| return rs6000_expand_zeroop_builtin (CODE_FOR_rs6000_mffs, target); |
| |
| case RS6000_BUILTIN_MTFSF: |
| return rs6000_expand_mtfsf_builtin (CODE_FOR_rs6000_mtfsf, exp); |
| |
| case ALTIVEC_BUILTIN_MASK_FOR_LOAD: |
| case ALTIVEC_BUILTIN_MASK_FOR_STORE: |
| { |
| int icode = (BYTES_BIG_ENDIAN ? (int) CODE_FOR_altivec_lvsr_direct |
| : (int) CODE_FOR_altivec_lvsl_direct); |
| machine_mode tmode = insn_data[icode].operand[0].mode; |
| machine_mode mode = insn_data[icode].operand[1].mode; |
| tree arg; |
| rtx op, addr, pat; |
| |
| gcc_assert (TARGET_ALTIVEC); |
| |
| arg = CALL_EXPR_ARG (exp, 0); |
| gcc_assert (POINTER_TYPE_P (TREE_TYPE (arg))); |
| 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_FCN (icode) (target, op); |
| if (!pat) |
| return 0; |
| emit_insn (pat); |
| |
| return target; |
| } |
| |
| case ALTIVEC_BUILTIN_VCFUX: |
| case ALTIVEC_BUILTIN_VCFSX: |
| case ALTIVEC_BUILTIN_VCTUXS: |
| case ALTIVEC_BUILTIN_VCTSXS: |
| /* FIXME: There's got to be a nicer way to handle this case than |
| constructing a new CALL_EXPR. */ |
| if (call_expr_nargs (exp) == 1) |
| { |
| exp = build_call_nary (TREE_TYPE (exp), CALL_EXPR_FN (exp), |
| 2, CALL_EXPR_ARG (exp, 0), integer_zero_node); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| 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; |
| } |
| if (TARGET_PAIRED_FLOAT) |
| { |
| ret = paired_expand_builtin (exp, target, &success); |
| |
| if (success) |
| return ret; |
| } |
| if (TARGET_HTM) |
| { |
| ret = htm_expand_builtin (exp, target, &success); |
| |
| if (success) |
| return ret; |
| } |
| |
| unsigned attr = rs6000_builtin_info[uns_fcode].attr & RS6000_BTC_TYPE_MASK; |
| gcc_assert (attr == RS6000_BTC_UNARY |
| || attr == RS6000_BTC_BINARY |
| || attr == RS6000_BTC_TERNARY); |
| |
| /* Handle simple unary operations. */ |
| d = bdesc_1arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_1arg); i++, d++) |
| if (d->code == fcode) |
| return rs6000_expand_unop_builtin (d->icode, exp, target); |
| |
| /* Handle simple binary operations. */ |
| d = bdesc_2arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_2arg); i++, d++) |
| if (d->code == fcode) |
| return rs6000_expand_binop_builtin (d->icode, exp, target); |
| |
| /* Handle simple ternary operations. */ |
| d = bdesc_3arg; |
| for (i = 0; i < ARRAY_SIZE (bdesc_3arg); i++, d++) |
| if (d->code == fcode) |
| return rs6000_expand_ternop_builtin (d->icode, exp, target); |
| |
| gcc_unreachable (); |
| } |
| |
| static void |
| rs6000_init_builtins (void) |
| { |
| tree tdecl; |
| tree ftype; |
| machine_mode mode; |
| |
| if (TARGET_DEBUG_BUILTIN) |
| fprintf (stderr, "rs6000_init_builtins%s%s%s%s\n", |
| (TARGET_PAIRED_FLOAT) ? ", paired" : "", |
| (TARGET_SPE) ? ", spe" : "", |
| (TARGET_ALTIVEC) ? ", altivec" : "", |
| (TARGET_VSX) ? ", vsx" : ""); |
| |
| V2SI_type_node = build_vector_type (intSI_type_node, 2); |
| V2SF_type_node = build_vector_type (float_type_node, 2); |
| V2DI_type_node = build_vector_type (intDI_type_node, 2); |
| V2DF_type_node = build_vector_type (double_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); |
| unsigned_V2DI_type_node = build_vector_type (unsigned_intDI_type_node, 2); |
| |
| 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 = build_opaque_vector_type (intSI_type_node, 4); |
| |
| /* We use V1TI mode as a special container to hold __int128_t items that |
| must live in VSX registers. */ |
| if (intTI_type_node) |
| { |
| V1TI_type_node = build_vector_type (intTI_type_node, 1); |
| unsigned_V1TI_type_node = build_vector_type (unsigned_intTI_type_node, 1); |
| } |
| |
| /* 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); |
| bool_long_type_node = build_distinct_type_copy (unsigned_intDI_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; |
| long_long_integer_type_internal_node = long_long_integer_type_node; |
| long_long_unsigned_type_internal_node = long_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; |
| intDI_type_internal_node = intDI_type_node; |
| uintDI_type_internal_node = unsigned_intDI_type_node; |
| intTI_type_internal_node = intTI_type_node; |
| uintTI_type_internal_node = unsigned_intTI_type_node; |
| float_type_internal_node = float_type_node; |
| double_type_internal_node = double_type_node; |
| long_double_type_internal_node = long_double_type_node; |
| dfloat64_type_internal_node = dfloat64_type_node; |
| dfloat128_type_internal_node = dfloat128_type_node; |
| void_type_internal_node = void_type_node; |
| |
| /* Initialize the modes for builtin_function_type, mapping a machine mode to |
| tree type node. */ |
| builtin_mode_to_type[QImode][0] = integer_type_node; |
| builtin_mode_to_type[HImode][0] = integer_type_node; |
| builtin_mode_to_type[SImode][0] = intSI_type_node; |
| builtin_mode_to_type[SImode][1] = unsigned_intSI_type_node; |
| builtin_mode_to_type[DImode][0] = intDI_type_node; |
| builtin_mode_to_type[DImode][1] = unsigned_intDI_type_node; |
| builtin_mode_to_type[TImode][0] = intTI_type_node; |
| builtin_mode_to_type[TImode][1] = unsigned_intTI_type_node; |
| builtin_mode_to_type[SFmode][0] = float_type_node; |
| builtin_mode_to_type[DFmode][0] = double_type_node; |
| builtin_mode_to_type[TFmode][0] = long_double_type_node; |
| builtin_mode_to_type[DDmode][0] = dfloat64_type_node; |
| builtin_mode_to_type[TDmode][0] = dfloat128_type_node; |
| builtin_mode_to_type[V1TImode][0] = V1TI_type_node; |
| builtin_mode_to_type[V1TImode][1] = unsigned_V1TI_type_node; |
| builtin_mode_to_type[V2SImode][0] = V2SI_type_node; |
| builtin_mode_to_type[V2SFmode][0] = V2SF_type_node; |
| builtin_mode_to_type[V2DImode][0] = V2DI_type_node; |
| builtin_mode_to_type[V2DImode][1] = unsigned_V2DI_type_node; |
| builtin_mode_to_type[V2DFmode][0] = V2DF_type_node; |
| builtin_mode_to_type[V4HImode][0] = V4HI_type_node; |
| builtin_mode_to_type[V4SImode][0] = V4SI_type_node; |
| builtin_mode_to_type[V4SImode][1] = unsigned_V4SI_type_node; |
| builtin_mode_to_type[V4SFmode][0] = V4SF_type_node; |
| builtin_mode_to_type[V8HImode][0] = V8HI_type_node; |
| builtin_mode_to_type[V8HImode][1] = unsigned_V8HI_type_node; |
| builtin_mode_to_type[V16QImode][0] = V16QI_type_node; |
| builtin_mode_to_type[V16QImode][1] = unsigned_V16QI_type_node; |
| |
| tdecl = add_builtin_type ("__bool char", bool_char_type_node); |
| TYPE_NAME (bool_char_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__bool short", bool_short_type_node); |
| TYPE_NAME (bool_short_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__bool int", bool_int_type_node); |
| TYPE_NAME (bool_int_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__pixel", pixel_type_node); |
| TYPE_NAME (pixel_type_node) = tdecl; |
| |
| 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); |
| bool_V2DI_type_node = build_vector_type (bool_long_type_node, 2); |
| pixel_V8HI_type_node = build_vector_type (pixel_type_node, 8); |
| |
| tdecl = add_builtin_type ("__vector unsigned char", unsigned_V16QI_type_node); |
| TYPE_NAME (unsigned_V16QI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector signed char", V16QI_type_node); |
| TYPE_NAME (V16QI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector __bool char", bool_V16QI_type_node); |
| TYPE_NAME ( bool_V16QI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector unsigned short", unsigned_V8HI_type_node); |
| TYPE_NAME (unsigned_V8HI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector signed short", V8HI_type_node); |
| TYPE_NAME (V8HI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector __bool short", bool_V8HI_type_node); |
| TYPE_NAME (bool_V8HI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector unsigned int", unsigned_V4SI_type_node); |
| TYPE_NAME (unsigned_V4SI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector signed int", V4SI_type_node); |
| TYPE_NAME (V4SI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector __bool int", bool_V4SI_type_node); |
| TYPE_NAME (bool_V4SI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector float", V4SF_type_node); |
| TYPE_NAME (V4SF_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector __pixel", pixel_V8HI_type_node); |
| TYPE_NAME (pixel_V8HI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector double", V2DF_type_node); |
| TYPE_NAME (V2DF_type_node) = tdecl; |
| |
| if (TARGET_POWERPC64) |
| { |
| tdecl = add_builtin_type ("__vector long", V2DI_type_node); |
| TYPE_NAME (V2DI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector unsigned long", |
| unsigned_V2DI_type_node); |
| TYPE_NAME (unsigned_V2DI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector __bool long", bool_V2DI_type_node); |
| TYPE_NAME (bool_V2DI_type_node) = tdecl; |
| } |
| else |
| { |
| tdecl = add_builtin_type ("__vector long long", V2DI_type_node); |
| TYPE_NAME (V2DI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector unsigned long long", |
| unsigned_V2DI_type_node); |
| TYPE_NAME (unsigned_V2DI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector __bool long long", |
| bool_V2DI_type_node); |
| TYPE_NAME (bool_V2DI_type_node) = tdecl; |
| } |
| |
| if (V1TI_type_node) |
| { |
| tdecl = add_builtin_type ("__vector __int128", V1TI_type_node); |
| TYPE_NAME (V1TI_type_node) = tdecl; |
| |
| tdecl = add_builtin_type ("__vector unsigned __int128", |
| unsigned_V1TI_type_node); |
| TYPE_NAME (unsigned_V1TI_type_node) = tdecl; |
| } |
| |
| /* Paired and SPE builtins are only available if you build a compiler with |
| the appropriate options, so only create those builtins with the |
| appropriate compiler option. Create Altivec and VSX builtins on machines |
| with at least the general purpose extensions (970 and newer) to allow the |
| use of the target attribute. */ |
| if (TARGET_PAIRED_FLOAT) |
| paired_init_builtins (); |
| if (TARGET_SPE) |
| spe_init_builtins (); |
| if (TARGET_EXTRA_BUILTINS) |
| altivec_init_builtins (); |
| if (TARGET_HTM) |
| htm_init_builtins (); |
| |
| if (TARGET_EXTRA_BUILTINS || TARGET_SPE || TARGET_PAIRED_FLOAT) |
| rs6000_common_init_builtins (); |
| |
| ftype = builtin_function_type (DFmode, DFmode, DFmode, VOIDmode, |
| RS6000_BUILTIN_RECIP, "__builtin_recipdiv"); |
| def_builtin ("__builtin_recipdiv", ftype, RS6000_BUILTIN_RECIP); |
| |
| ftype = builtin_function_type (SFmode, SFmode, SFmode, VOIDmode, |
| RS6000_BUILTIN_RECIPF, "__builtin_recipdivf"); |
| def_builtin ("__builtin_recipdivf", ftype, RS6000_BUILTIN_RECIPF); |
| |
| ftype = builtin_function_type (DFmode, DFmode, VOIDmode, VOIDmode, |
| RS6000_BUILTIN_RSQRT, "__builtin_rsqrt"); |
| def_builtin ("__builtin_rsqrt", ftype, RS6000_BUILTIN_RSQRT); |
| |
| ftype = builtin_function_type (SFmode, SFmode, VOIDmode, VOIDmode, |
| RS6000_BUILTIN_RSQRTF, "__builtin_rsqrtf"); |
| def_builtin ("__builtin_rsqrtf", ftype, RS6000_BUILTIN_RSQRTF); |
| |
| mode = (TARGET_64BIT) ? DImode : SImode; |
| ftype = builtin_function_type (mode, mode, mode, VOIDmode, |
| POWER7_BUILTIN_BPERMD, "__builtin_bpermd"); |
| def_builtin ("__builtin_bpermd", ftype, POWER7_BUILTIN_BPERMD); |
| |
| ftype = build_function_type_list (unsigned_intDI_type_node, |
| NULL_TREE); |
| def_builtin ("__builtin_ppc_get_timebase", ftype, RS6000_BUILTIN_GET_TB); |
| |
| if (TARGET_64BIT) |
| ftype = build_function_type_list (unsigned_intDI_type_node, |
| NULL_TREE); |
| else |
| ftype = build_function_type_list (unsigned_intSI_type_node, |
| NULL_TREE); |
| def_builtin ("__builtin_ppc_mftb", ftype, RS6000_BUILTIN_MFTB); |
| |
| ftype = build_function_type_list (double_type_node, NULL_TREE); |
| def_builtin ("__builtin_mffs", ftype, RS6000_BUILTIN_MFFS); |
| |
| ftype = build_function_type_list (void_type_node, |
| intSI_type_node, double_type_node, |
| NULL_TREE); |
| def_builtin ("__builtin_mtfsf", ftype, RS6000_BUILTIN_MTFSF); |
| |
| #if TARGET_XCOFF |
| /* AIX libm provides clog as __clog. */ |
| if ((tdecl = builtin_decl_explicit (BUILT_IN_CLOG)) != NULL_TREE) |
| set_user_assembler_name (tdecl, "__clog"); |
| #endif |
| |
| #ifdef SUBTARGET_INIT_BUILTINS |
| SUBTARGET_INIT_BUILTINS; |
| #endif |
| } |
| |
| /* Returns the rs6000 builtin decl for CODE. */ |
| |
| static tree |
| rs6000_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED) |
| { |
| HOST_WIDE_INT fnmask; |
| |
| if (code >= RS6000_BUILTIN_COUNT) |
| return error_mark_node; |
| |
| fnmask = rs6000_builtin_info[code].mask; |
| if ((fnmask & rs6000_builtin_mask) != fnmask) |
| { |
| rs6000_invalid_builtin ((enum rs6000_builtins)code); |
| return error_mark_node; |
| } |
| |
| return rs6000_builtin_decls[code]; |
| } |
| |
| static void |
| spe_init_builtins (void) |
| { |
| tree puint_type_node = build_pointer_type (unsigned_type_node); |
| tree pushort_type_node = build_pointer_type (short_unsigned_type_node); |
| const struct builtin_description *d; |
| size_t i; |
| |
| tree v2si_ftype_4_v2si |
| = build_function_type_list (opaque_V2SI_type_node, |
| opaque_V2SI_type_node, |
| opaque_V2SI_type_node, |
| opaque_V2SI_type_node, |
| opaque_V2SI_type_node, |
| NULL_TREE); |
| |
| tree v2sf_ftype_4_v2sf |
| = build_function_type_list (opaque_V2SF_type_node, |
| opaque_V2SF_type_node, |
| opaque_V2SF_type_node, |
| opaque_V2SF_type_node, |
| opaque_V2SF_type_node, |
| NULL_TREE); |
| |
| tree int_ftype_int_v2si_v2si |
| = build_function_type_list (integer_type_node, |
| integer_type_node, |
| opaque_V2SI_type_node, |
| opaque_V2SI_type_node, |
| NULL_TREE); |
| |
| tree int_ftype_int_v2sf_v2sf |
| = build_function_type_list (integer_type_node, |
| integer_type_node, |
| opaque_V2SF_type_node, |
| opaque_V2SF_type_node, |
| NULL_TREE); |
| |
| tree void_ftype_v2si_puint_int |
| = build_function_type_list (void_type_node, |
| opaque_V2SI_type_node, |
| puint_type_node, |
| integer_type_node, |
| NULL_TREE); |
| |
| tree void_ftype_v2si_puint_char |
| = build_function_type_list (void_type_node, |
| opaque_V2SI_type_node, |
| puint_type_node, |
| char_type_node, |
| NULL_TREE); |
| |
| tree void_ftype_v2si_pv2si_int |
| = build_function_type_list (void_type_node, |
| opaque_V2SI_type_node, |
| opaque_p_V2SI_type_node, |
| integer_type_node, |
| NULL_TREE); |
| |
| tree void_ftype_v2si_pv2si_char |
| = build_function_type_list (void_type_node, |
| opaque_V2SI_type_node, |
| opaque_p_V2SI_type_node, |
| char_type_node, |
| NULL_TREE); |
| |
| tree void_ftype_int |
| = build_function_type_list (void_type_node, integer_type_node, NULL_TREE); |
| |
| tree int_ftype_void |
| = build_function_type_list (integer_type_node, NULL_TREE); |
| |
| tree v2si_ftype_pv2si_int |
| = build_function_type_list (opaque_V2SI_type_node, |
| opaque_p_V2SI_type_node, |
| integer_type_node, |
| NULL_TREE); |
| |
| tree v2si_ftype_puint_int |
| = build_function_type_list (opaque_V2SI_type_node, |
| puint_type_node, |
| integer_type_node, |
| NULL_TREE); |
| |
| tree v2si_ftype_pushort_int |
| = build_function_type_list (opaque_V2SI_type_node, |
| pushort_type_node, |
| integer_type_node, |
| NULL_TREE); |
| |
| tree v2si_ftype_signed_char |
| = build_function_type_list (opaque_V2SI_type_node, |
| signed_char_type_node, |
| NULL_TREE); |
| |
| add_builtin_type ("__ev64_opaque__", opaque_V2SI_type_node); |
| |
| /* Initialize irregular SPE builtins. */ |
| |
| def_builtin ("__builtin_spe_mtspefscr", void_ftype_int, SPE_BUILTIN_MTSPEFSCR); |
| def_builtin ("__builtin_spe_mfspefscr", int_ftype_void, SPE_BUILTIN_MFSPEFSCR); |
| def_builtin ("__builtin_spe_evstddx", void_ftype_v2si_pv2si_int, SPE_BUILTIN_EVSTDDX); |
| def_builtin ("__builtin_spe_evstdhx", void_ftype_v2si_pv2si_int, SPE_BUILTIN_EVSTDHX); |
| def_builtin ("__builtin_spe_evstdwx", void_ftype_v2si_pv2si_int, SPE_BUILTIN_EVSTDWX); |
| def_builtin ("__builtin_spe_evstwhex", void_ftype_v2si_puint_int, SPE_BUILTIN_EVSTWHEX); |
| def_builtin ("__builtin_spe_evstwhox", void_ftype_v2si_puint_int, SPE_BUILTIN_EVSTWHOX); |
| def_builtin ("__builtin_spe_evstwwex", void_ftype_v2si_puint_int, SPE_BUILTIN_EVSTWWEX); |
| def_builtin ("__builtin_spe_evstwwox", void_ftype_v2si_puint_int, SPE_BUILTIN_EVSTWWOX); |
| def_builtin ("__builtin_spe_evstdd", void_ftype_v2si_pv2si_char, SPE_BUILTIN_EVSTDD); |
| def_builtin ("__builtin_spe_evstdh", void_ftype_v2si_pv2si_char, SPE_BUILTIN_EVSTDH); |
| def_builtin ("__builtin_spe_evstdw", void_ftype_v2si_pv2si_char, SPE_BUILTIN_EVSTDW); |
| def_builtin ("__builtin_spe_evstwhe", void_ftype_v2si_puint_char, SPE_BUILTIN_EVSTWHE); |
| def_builtin ("__builtin_spe_evstwho", void_ftype_v2si_puint_char, SPE_BUILTIN_EVSTWHO); |
| def_builtin ("__builtin_spe_evstwwe", void_ftype_v2si_puint_char, SPE_BUILTIN_EVSTWWE); |
| def_builtin ("__builtin_spe_evstwwo", void_ftype_v2si_puint_char, SPE_BUILTIN_EVSTWWO); |
| def_builtin ("__builtin_spe_evsplatfi", v2si_ftype_signed_char, SPE_BUILTIN_EVSPLATFI); |
| def_builtin ("__builtin_spe_evsplati", v2si_ftype_signed_char, SPE_BUILTIN_EVSPLATI); |
| |
| /* Loads. */ |
| def_builtin ("__builtin_spe_evlddx", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDDX); |
| def_builtin ("__builtin_spe_evldwx", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDWX); |
| def_builtin ("__builtin_spe_evldhx", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDHX); |
| def_builtin ("__builtin_spe_evlwhex", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHEX); |
| def_builtin ("__builtin_spe_evlwhoux", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHOUX); |
| def_builtin ("__builtin_spe_evlwhosx", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHOSX); |
| def_builtin ("__builtin_spe_evlwwsplatx", v2si_ftype_puint_int, SPE_BUILTIN_EVLWWSPLATX); |
| def_builtin ("__builtin_spe_evlwhsplatx", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHSPLATX); |
| def_builtin ("__builtin_spe_evlhhesplatx", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHESPLATX); |
| def_builtin ("__builtin_spe_evlhhousplatx", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHOUSPLATX); |
| def_builtin ("__builtin_spe_evlhhossplatx", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHOSSPLATX); |
| def_builtin ("__builtin_spe_evldd", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDD); |
| def_builtin ("__builtin_spe_evldw", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDW); |
| def_builtin ("__builtin_spe_evldh", v2si_ftype_pv2si_int, SPE_BUILTIN_EVLDH); |
| def_builtin ("__builtin_spe_evlhhesplat", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHESPLAT); |
| def_builtin ("__builtin_spe_evlhhossplat", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHOSSPLAT); |
| def_builtin ("__builtin_spe_evlhhousplat", v2si_ftype_pushort_int, SPE_BUILTIN_EVLHHOUSPLAT); |
| def_builtin ("__builtin_spe_evlwhe", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHE); |
| def_builtin ("__builtin_spe_evlwhos", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHOS); |
| def_builtin ("__builtin_spe_evlwhou", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHOU); |
| def_builtin ("__builtin_spe_evlwhsplat", v2si_ftype_puint_int, SPE_BUILTIN_EVLWHSPLAT); |
| def_builtin ("__builtin_spe_evlwwsplat", v2si_ftype_puint_int, SPE_BUILTIN_EVLWWSPLAT); |
| |
| /* Predicates. */ |
| d = 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->name, type, d->code); |
| } |
| |
| /* Evsel predicates. */ |
| d = 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->name, type, d->code); |
| } |
| } |
| |
| static void |
| paired_init_builtins (void) |
| { |
| const struct builtin_description *d; |
| size_t i; |
| |
| tree int_ftype_int_v2sf_v2sf |
| = build_function_type_list (integer_type_node, |
| integer_type_node, |
| V2SF_type_node, |
| V2SF_type_node, |
| NULL_TREE); |
| tree pcfloat_type_node = |
| build_pointer_type (build_qualified_type |
| (float_type_node, TYPE_QUAL_CONST)); |
| |
| tree v2sf_ftype_long_pcfloat = build_function_type_list (V2SF_type_node, |
| long_integer_type_node, |
| pcfloat_type_node, |
| NULL_TREE); |
| tree void_ftype_v2sf_long_pcfloat = |
| build_function_type_list (void_type_node, |
| V2SF_type_node, |
| long_integer_type_node, |
| pcfloat_type_node, |
| NULL_TREE); |
| |
| |
| def_builtin ("__builtin_paired_lx", v2sf_ftype_long_pcfloat, |
| PAIRED_BUILTIN_LX); |
| |
| |
| def_builtin ("__builtin_paired_stx", void_ftype_v2sf_long_pcfloat, |
| PAIRED_BUILTIN_STX); |
| |
| /* Predicates. */ |
| d = bdesc_paired_preds; |
| for (i = 0; i < ARRAY_SIZE (bdesc_paired_preds); ++i, d++) |
| { |
| tree type; |
| |
| if (TARGET_DEBUG_BUILTIN) |
| fprintf (stderr, "paired pred #%d, insn = %s [%d], mode = %s\n", |
| (int)i, get_insn_name (d->icode), (int)d->icode, |
| GET_MODE_NAME (insn_data[d->icode].operand[1].mode)); |
| |
| switch (insn_data[d->icode].operand[1].mode) |
| { |
| case V2SFmode: |
| type = int_ftype_int_v2sf_v2sf; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| def_builtin (d->name, type, d->code); |
| } |
| } |
| |
| static void |
| altivec_init_builtins (void) |
| { |
| const struct builtin_description *d; |
| size_t i; |
| tree ftype; |
| tree decl; |
| |
| tree pvoid_type_node = build_pointer_type (void_type_node); |
| |
| 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 |
| = build_function_type_list (integer_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 int_ftype_int_v2di_v2di |
| = build_function_type_list (integer_type_node, |
| integer_type_node, V2DI_type_node, |
| V2DI_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_list (V8HI_type_node, NULL_TREE); |
| tree void_ftype_void |
| = build_function_type_list (void_type_node, NULL_TREE); |
| 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 v4sf_ftype_long_pcvoid |
| = build_function_type_list (V4SF_type_node, |
| long_integer_type_node, pcvoid_type_node, |
| NULL_TREE); |
| tree v2df_ftype_long_pcvoid |
| = build_function_type_list (V2DF_type_node, |
| long_integer_type_node, pcvoid_type_node, |
| NULL_TREE); |
| tree v2di_ftype_long_pcvoid |
| = build_function_type_list (V2DI_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 void_ftype_v4sf_long_pvoid |
| = build_function_type_list (void_type_node, |
| V4SF_type_node, long_integer_type_node, |
| pvoid_type_node, NULL_TREE); |
| tree void_ftype_v2df_long_pvoid |
| = build_function_type_list (void_type_node, |
| V2DF_type_node, long_integer_type_node, |
| pvoid_type_node, NULL_TREE); |
| tree void_ftype_v2di_long_pvoid |
| = build_function_type_list (void_type_node, |
| V2DI_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 int_ftype_int_v2df_v2df |
| = build_function_type_list (integer_type_node, |
| integer_type_node, V2DF_type_node, |
| V2DF_type_node, NULL_TREE); |
| tree v2di_ftype_v2di |
| = build_function_type_list (V2DI_type_node, V2DI_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 v2df_ftype_v2df |
| = build_function_type_list (V2DF_type_node, V2DF_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 ("__builtin_altivec_mtvscr", void_ftype_v4si, ALTIVEC_BUILTIN_MTVSCR); |
| def_builtin ("__builtin_altivec_mfvscr", v8hi_ftype_void, ALTIVEC_BUILTIN_MFVSCR); |
| def_builtin ("__builtin_altivec_dssall", void_ftype_void, ALTIVEC_BUILTIN_DSSALL); |
| def_builtin ("__builtin_altivec_dss", void_ftype_int, ALTIVEC_BUILTIN_DSS); |
| def_builtin ("__builtin_altivec_lvsl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVSL); |
| def_builtin ("__builtin_altivec_lvsr", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVSR); |
| def_builtin ("__builtin_altivec_lvebx", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEBX); |
| def_builtin ("__builtin_altivec_lvehx", v8hi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEHX); |
| def_builtin ("__builtin_altivec_lvewx", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEWX); |
| def_builtin ("__builtin_altivec_lvxl", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVXL); |
| def_builtin ("__builtin_altivec_lvxl_v2df", v2df_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVXL_V2DF); |
| def_builtin ("__builtin_altivec_lvxl_v2di", v2di_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVXL_V2DI); |
| def_builtin ("__builtin_altivec_lvxl_v4sf", v4sf_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVXL_V4SF); |
| def_builtin ("__builtin_altivec_lvxl_v4si", v4si_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVXL_V4SI); |
| def_builtin ("__builtin_altivec_lvxl_v8hi", v8hi_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVXL_V8HI); |
| def_builtin ("__builtin_altivec_lvxl_v16qi", v16qi_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVXL_V16QI); |
| def_builtin ("__builtin_altivec_lvx", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVX); |
| def_builtin ("__builtin_altivec_lvx_v2df", v2df_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVX_V2DF); |
| def_builtin ("__builtin_altivec_lvx_v2di", v2di_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVX_V2DI); |
| def_builtin ("__builtin_altivec_lvx_v4sf", v4sf_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVX_V4SF); |
| def_builtin ("__builtin_altivec_lvx_v4si", v4si_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVX_V4SI); |
| def_builtin ("__builtin_altivec_lvx_v8hi", v8hi_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVX_V8HI); |
| def_builtin ("__builtin_altivec_lvx_v16qi", v16qi_ftype_long_pcvoid, |
| ALTIVEC_BUILTIN_LVX_V16QI); |
| def_builtin ("__builtin_altivec_stvx", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVX); |
| def_builtin ("__builtin_altivec_stvx_v2df", void_ftype_v2df_long_pvoid, |
| ALTIVEC_BUILTIN_STVX_V2DF); |
| def_builtin ("__builtin_altivec_stvx_v2di", void_ftype_v2di_long_pvoid, |
| ALTIVEC_BUILTIN_STVX_V2DI); |
| def_builtin ("__builtin_altivec_stvx_v4sf", void_ftype_v4sf_long_pvoid, |
| ALTIVEC_BUILTIN_STVX_V4SF); |
| def_builtin ("__builtin_altivec_stvx_v4si", void_ftype_v4si_long_pvoid, |
| ALTIVEC_BUILTIN_STVX_V4SI); |
| def_builtin ("__builtin_altivec_stvx_v8hi", void_ftype_v8hi_long_pvoid, |
| ALTIVEC_BUILTIN_STVX_V8HI); |
| def_builtin ("__builtin_altivec_stvx_v16qi", void_ftype_v16qi_long_pvoid, |
| ALTIVEC_BUILTIN_STVX_V16QI); |
| def_builtin ("__builtin_altivec_stvewx", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVEWX); |
| def_builtin ("__builtin_altivec_stvxl", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVXL); |
| def_builtin ("__builtin_altivec_stvxl_v2df", void_ftype_v2df_long_pvoid, |
| ALTIVEC_BUILTIN_STVXL_V2DF); |
| def_builtin ("__builtin_altivec_stvxl_v2di", void_ftype_v2di_long_pvoid, |
| ALTIVEC_BUILTIN_STVXL_V2DI); |
| def_builtin ("__builtin_altivec_stvxl_v4sf", void_ftype_v4sf_long_pvoid, |
| ALTIVEC_BUILTIN_STVXL_V4SF); |
| def_builtin ("__builtin_altivec_stvxl_v4si", void_ftype_v4si_long_pvoid, |
| ALTIVEC_BUILTIN_STVXL_V4SI); |
| def_builtin ("__builtin_altivec_stvxl_v8hi", void_ftype_v8hi_long_pvoid, |
| ALTIVEC_BUILTIN_STVXL_V8HI); |
| def_builtin ("__builtin_altivec_stvxl_v16qi", void_ftype_v16qi_long_pvoid, |
| ALTIVEC_BUILTIN_STVXL_V16QI); |
| def_builtin ("__builtin_altivec_stvebx", void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_STVEBX); |
| def_builtin ("__builtin_altivec_stvehx", void_ftype_v8hi_long_pvoid, ALTIVEC_BUILTIN_STVEHX); |
| def_builtin ("__builtin_vec_ld", opaque_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LD); |
| def_builtin ("__builtin_vec_lde", opaque_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LDE); |
| def_builtin ("__builtin_vec_ldl", opaque_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LDL); |
| def_builtin ("__builtin_vec_lvsl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVSL); |
| def_builtin ("__builtin_vec_lvsr", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVSR); |
| def_builtin ("__builtin_vec_lvebx", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVEBX); |
| def_builtin ("__builtin_vec_lvehx", v8hi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVEHX); |
| def_builtin ("__builtin_vec_lvewx", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVEWX); |
| def_builtin ("__builtin_vec_st", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_ST); |
| def_builtin ("__builtin_vec_ste", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STE); |
| def_builtin ("__builtin_vec_stl", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STL); |
| def_builtin ("__builtin_vec_stvewx", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STVEWX); |
| def_builtin ("__builtin_vec_stvebx", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STVEBX); |
| def_builtin ("__builtin_vec_stvehx", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STVEHX); |
| |
| def_builtin ("__builtin_vsx_lxvd2x_v2df", v2df_ftype_long_pcvoid, |
| VSX_BUILTIN_LXVD2X_V2DF); |
| def_builtin ("__builtin_vsx_lxvd2x_v2di", v2di_ftype_long_pcvoid, |
| VSX_BUILTIN_LXVD2X_V2DI); |
| def_builtin ("__builtin_vsx_lxvw4x_v4sf", v4sf_ftype_long_pcvoid, |
| VSX_BUILTIN_LXVW4X_V4SF); |
| def_builtin ("__builtin_vsx_lxvw4x_v4si", v4si_ftype_long_pcvoid, |
| VSX_BUILTIN_LXVW4X_V4SI); |
| def_builtin ("__builtin_vsx_lxvw4x_v8hi", v8hi_ftype_long_pcvoid, |
| VSX_BUILTIN_LXVW4X_V8HI); |
| def_builtin ("__builtin_vsx_lxvw4x_v16qi", v16qi_ftype_long_pcvoid, |
| VSX_BUILTIN_LXVW4X_V16QI); |
| def_builtin ("__builtin_vsx_stxvd2x_v2df", void_ftype_v2df_long_pvoid, |
| VSX_BUILTIN_STXVD2X_V2DF); |
| def_builtin ("__builtin_vsx_stxvd2x_v2di", void_ftype_v2di_long_pvoid, |
| VSX_BUILTIN_STXVD2X_V2DI); |
| def_builtin ("__builtin_vsx_stxvw4x_v4sf", void_ftype_v4sf_long_pvoid, |
| VSX_BUILTIN_STXVW4X_V4SF); |
| def_builtin ("__builtin_vsx_stxvw4x_v4si", void_ftype_v4si_long_pvoid, |
| VSX_BUILTIN_STXVW4X_V4SI); |
| def_builtin ("__builtin_vsx_stxvw4x_v8hi", void_ftype_v8hi_long_pvoid, |
| VSX_BUILTIN_STXVW4X_V8HI); |
| def_builtin ("__builtin_vsx_stxvw4x_v16qi", void_ftype_v16qi_long_pvoid, |
| VSX_BUILTIN_STXVW4X_V16QI); |
| def_builtin ("__builtin_vec_vsx_ld", opaque_ftype_long_pcvoid, |
| VSX_BUILTIN_VEC_LD); |
| def_builtin ("__builtin_vec_vsx_st", void_ftype_opaque_long_pvoid, |
| VSX_BUILTIN_VEC_ST); |
| |
| def_builtin ("__builtin_vec_step", int_ftype_opaque, ALTIVEC_BUILTIN_VEC_STEP); |
| def_builtin ("__builtin_vec_splats", opaque_ftype_opaque, ALTIVEC_BUILTIN_VEC_SPLATS); |
| def_builtin ("__builtin_vec_promote", opaque_ftype_opaque, ALTIVEC_BUILTIN_VEC_PROMOTE); |
| |
|